/* =====================================================================================
   EFICÁCIA CRM — mod-equipamentos.css · Extras do módulo Equipamentos (inventário OOH)
   Só o que o design-system.css NÃO cobre. Prefixo: meq-
   Depende de: design-system.css (variáveis --neutral-*, --primary-*, --shadow-*, …)
   Mobile-first: as regras base servem <768px; @media (min-width:…) acrescenta desktop.
   ===================================================================================== */

/* ---------- página / listagem ---------- */
.meq-page { min-width: 0; }

.meq-toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    padding: 0 0 12px;
    min-width: 0;
}

.meq-card-lista { min-width: 0; }

.meq-procurar-wrap { position: relative; }
.meq-procurar-wrap > svg {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--neutral-400);
    pointer-events: none;
}
.meq-procurar-wrap .ds-input { padding-left: 38px; }

/* Números de página só a partir de 640px (em mobile fica "x / y") */
.meq-pager-nums { display: none; }
@media (min-width: 640px) {
    .meq-pager-nums { display: inline-flex; gap: 4px; }
}

@media (min-width: 768px) {
    .meq-col-codigo { width: 130px; }
}

/* FAB mobile — criar sempre ao alcance do polegar */
.meq-fab {
    position: fixed;
    right: 16px;
    bottom: calc(76px + env(safe-area-inset-bottom, 0px));
    z-index: 60;
    width: 56px;
    height: 56px;
    min-width: 56px;
    border-radius: 50%;
    padding: 0;
    box-shadow: var(--shadow-lg);
}
@media (min-width: 768px) {
    .meq-fab { display: none; }
}

/* Badge "dark" — estado Anulado do contrato (paridade badge-dark do CoreUI) */
.meq-badge--dark {
    background: var(--neutral-700, #33383d);
    color: #fff;
    border-color: var(--neutral-700, #33383d);
}

.meq-aviso-inline {
    display: inline-flex;
    vertical-align: -2px;
    color: var(--warning-600, #b26a00);
}

.meq-link-btn {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    cursor: pointer;
    color: var(--primary-600);
    text-decoration: underline;
    text-underline-offset: 2px;
    min-height: 44px;          /* alvo de toque */
}
@media (min-width: 768px) {
    .meq-link-btn { min-height: 0; }
}

/* ---------- mapa ---------- */
.meq-card-mapa { min-width: 0; overflow: hidden; }

.meq-mapa-barra {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding: 12px 16px;
    border-bottom: 1px solid var(--neutral-100);
}

.meq-mapa {
    width: 100%;
    /* §11.17: altura fluida (o original tinha 650px fixos) */
    min-height: 320px;
    background: var(--neutral-50);
    position: relative;
}
.meq-mapa--global { height: clamp(320px, 62vh, 760px); }
.meq-mapa--ficha { height: clamp(240px, 42vh, 460px); }
.meq-mapa--previa { height: clamp(200px, 34vh, 360px); border-radius: var(--radius-md, 10px); overflow: hidden; margin-top: 10px; }

/* Paisagem em telemóvel (ex.: 844x390): o mapa não pode comer o ecrã todo */
@media (max-height: 480px) {
    .meq-mapa--global { height: 260px; }
    .meq-mapa--ficha,
    .meq-mapa--previa { height: 200px; }
}

.meq-mapa-skel { padding: 24px; }

.meq-card-mapa.is-fullscreen {
    position: fixed;
    inset: 0;
    z-index: 200;
    border-radius: 0;
    margin: 0;
    display: flex;
    flex-direction: column;
}
.meq-card-mapa.is-fullscreen .meq-mapa { flex: 1; height: auto; }
body.meq-sem-scroll { overflow: hidden; }

/* Marcadores (divIcon do Leaflet) */
.meq-pin { background: none; border: 0; }
.meq-pin-dot {
    display: block;
    width: 18px;
    height: 18px;
    border-radius: 50%;
    border: 2px solid #fff;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .45);
}
.meq-pin:focus-visible .meq-pin-dot,
.meq-cluster:focus-visible {
    outline: 3px solid var(--primary-500);
    outline-offset: 2px;
}

.meq-cluster {
    background: var(--primary-600);
    color: #fff;
    border: 2px solid #fff;
    border-radius: 50%;
    display: flex !important;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 12px;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .35);
}
.meq-cluster--m { font-size: 13px; }
.meq-cluster--g { font-size: 14px; }

/* Popup */
.meq-popup { min-width: 180px; }
.meq-popup-link { font-weight: 600; display: block; margin-bottom: 4px; }
.meq-popup-meta { color: var(--neutral-500); font-size: 12px; margin-bottom: 8px; }
.meq-popup-accoes { display: flex; gap: 6px; flex-wrap: wrap; }

/* Legenda (obrigatória — §11.16) */
.meq-legenda { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; min-width: 0; }
.meq-legenda-titulo {
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--neutral-500);
    font-weight: 700;
}
.meq-legenda-lista {
    display: flex;
    gap: 12px;
    flex-wrap: wrap;
    list-style: none;
    margin: 0;
    padding: 0;
}
.meq-legenda-item {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    font-size: 12px;
    color: var(--neutral-600);
    white-space: nowrap;
}
.meq-legenda-cor {
    width: 12px;
    height: 12px;
    border-radius: 50%;
    border: 1.5px solid #fff;
    box-shadow: 0 0 0 1px var(--neutral-200);
    flex: none;
}

/* Mapa esquemático de reserva (Leaflet indisponível) */
.meq-mapa-fallback { padding: 12px; overflow: auto; }
.meq-mapa-fallback--alto { min-height: 320px; }
.meq-mapa-aviso {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0 0 8px;
    font-size: 12px;
    color: var(--neutral-500);
}
.meq-fb-svg { width: 100%; height: auto; max-height: 60vh; display: block; }
.meq-fb-bg { fill: var(--neutral-50); }
.meq-fb-dot { cursor: pointer; }
.meq-fb-dot:hover { r: 7; }
.meq-fb-dot:focus-visible { outline: 3px solid var(--primary-500); }

/* ---------- formulário ---------- */
.meq-form-card { margin-bottom: 16px; }
.meq-form-card:last-of-type { margin-bottom: 0; }

.meq-coord-accoes { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.meq-precario-nota { margin: 0 0 12px; }
.meq-precario-aviso { margin: 4px 0 12px; }

.meq-foto-editor { display: grid; gap: 14px; }
.meq-foto-atual {
    display: grid;
    gap: 12px;
    align-items: center;
    padding: 12px;
    border: 1px solid var(--neutral-200);
    border-radius: var(--radius-md, 10px);
    background: var(--neutral-50);
}
.meq-foto-atual img {
    width: 100%;
    max-width: 260px;
    height: auto;
    border-radius: var(--radius-sm, 6px);
    background: var(--neutral-100);
    display: block;
}
.meq-foto-atual-accoes { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.meq-foto-atual-nome {
    font-size: 12px;
    color: var(--neutral-500);
    overflow-wrap: anywhere;
    flex: 1 1 140px;
    min-width: 0;
}
@media (min-width: 640px) {
    .meq-foto-atual { grid-template-columns: auto 1fr; }
}

/* Barra de ações do formulário fixa no fundo em mobile (critério de aceitação) */
.meq-form-actions {
    position: sticky;
    bottom: 0;
    z-index: 30;
    background: var(--surface, #fff);
    border-top: 1px solid var(--neutral-200);
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    margin: 12px -12px 0;
    display: flex;
    gap: 8px;
    align-items: center;
    box-shadow: 0 -4px 14px rgba(0, 0, 0, .06);
}
.meq-form-actions .ds-btn { min-height: 44px; }
@media (min-width: 768px) {
    .meq-form-actions {
        position: static;
        margin: 16px 0 0;
        padding: 12px 0 0;
        box-shadow: none;
        border-top: 1px solid var(--neutral-100);
    }
}

/* ---------- dropzone ---------- */
.meq-dropzone {
    border: 1.5px dashed var(--neutral-300);
    border-radius: var(--radius-md, 10px);
    background: var(--neutral-50);
    padding: 18px 14px;
    min-height: 96px;
    display: flex;
    align-items: center;
    justify-content: center;
    text-align: center;
    cursor: pointer;
    transition: border-color .15s, background .15s;
}
.meq-dropzone:hover,
.meq-dropzone:focus-visible { border-color: var(--primary-400); background: var(--primary-50); }
.meq-dropzone.is-drag { border-color: var(--primary-500); background: var(--primary-50); }
.meq-dropzone.is-cheia { border-style: solid; background: var(--surface, #fff); cursor: default; }

.meq-dz-estado {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 4px;
    color: var(--neutral-500);
}
.meq-dz-titulo { font-size: 13px; font-weight: 600; color: var(--neutral-700); }
.meq-dz-sub { font-size: 11px; color: var(--neutral-500); }

.meq-dz-ficheiro {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    min-width: 0;
    color: var(--success-700, #2f7a4a);
}
.meq-dz-nome {
    flex: 1;
    min-width: 0;
    overflow-wrap: anywhere;
    font-size: 13px;
    font-weight: 600;
    text-align: left;
}
.meq-dz-tam { font-size: 11px; color: var(--neutral-500); white-space: nowrap; }
.meq-dz-thumb {
    width: 44px;
    height: 44px;
    object-fit: cover;
    border-radius: var(--radius-sm, 6px);
    flex: none;
}
.meq-dz-remover { flex: none; }

/* ---------- detalhe ---------- */
.meq-skel-hero { margin-top: 12px; }

/* Índice de secções: chips scrolláveis no topo, sticky */
.meq-indice {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding: 8px 0;
    margin-bottom: 12px;
    position: sticky;
    top: 0;
    z-index: 25;
    background: var(--bg, #f7f8fa);
    scrollbar-width: thin;
    -webkit-overflow-scrolling: touch;
}
.meq-indice-chip {
    white-space: nowrap;
    cursor: pointer;
    border: 1px solid var(--neutral-200);
    background: var(--surface, #fff);
    min-height: 36px;
}
.meq-indice-chip:hover { border-color: var(--primary-400); }
@media (min-width: 768px) {
    .meq-indice { flex-wrap: wrap; overflow: visible; }
}

.meq-hero { margin-bottom: 16px; }
.meq-hero-corpo { display: grid; gap: 16px; }
@media (min-width: 900px) {
    .meq-hero-corpo { grid-template-columns: 260px 1fr; align-items: start; }
}

.meq-hero-img {
    width: 100%;
    height: auto;
    border-radius: var(--radius-md, 10px);
    background: var(--neutral-100);
    display: block;
}
.meq-hero-sem-foto {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 6px;
    height: 160px;
    border: 1px dashed var(--neutral-300);
    border-radius: var(--radius-md, 10px);
    color: var(--neutral-400);
    font-size: 12px;
    background: var(--neutral-50);
}

.meq-hero-topo {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 2px;
}
.meq-hero-titulo {
    margin: 0;
    font-size: 18px;
    line-height: 1.3;
    overflow-wrap: anywhere;
}
@media (min-width: 768px) {
    .meq-hero-titulo { font-size: 20px; }
}
.meq-hero-sub { margin: 0 0 14px; color: var(--neutral-500); font-size: 13px; }
.meq-hero-campos { margin-top: 6px; }

/* Preçário em cartões de escalão */
.meq-card-precos { margin-bottom: 16px; }
.meq-escaloes {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
    gap: 10px;
    margin-bottom: 14px;
}
.meq-escalao {
    border: 1px solid var(--neutral-200);
    border-radius: var(--radius-md, 10px);
    padding: 12px;
    text-align: center;
    background: var(--neutral-50);
    min-width: 0;
}
.meq-escalao-meses {
    display: block;
    font-size: 11px;
    text-transform: uppercase;
    letter-spacing: .04em;
    color: var(--neutral-500);
    font-weight: 700;
}
.meq-escalao-valor {
    display: block;
    font-size: 22px;
    font-weight: 700;
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
}
.meq-escalao-uni { display: block; font-size: 11px; color: var(--neutral-500); }
.meq-precos-extra { margin-top: 4px; }

/* Campo apagado por dependência de outro (Data de Pagamento enquanto a coima não está
   «Paga» — pedido 535de65f). O `input:disabled` já se apaga sozinho; o que faltava era
   o RÓTULO e a ajuda, que sem isto ficavam a preto por cima de um campo morto. */
.meq-campo-dependente.is-desativado :where(.ds-label, .ds-help) { opacity: .5; }

.meq-card-obs { margin-bottom: 16px; }
.meq-obs { border: 0; padding: 0; min-height: 0; }
.meq-obs :where(h1, h2, h3) { margin: .6em 0 .3em; }
.meq-obs img { max-width: 100%; height: auto; }

.meq-seccao { margin-bottom: 16px; }
.meq-seccao-header { flex-wrap: wrap; gap: 8px; }
.meq-seccao-acoes { display: flex; gap: 8px; flex-wrap: wrap; }
.meq-contagem { margin-left: 8px; }
.meq-sub-paginacao { padding: 10px 16px; border-top: 1px solid var(--neutral-100); }

/* Linhas de contrato agrupadas sob o contrato (elimina a aparência de duplicação) */
.meq-linhas-contrato {
    list-style: none;
    margin: 0;
    padding: 0 0 0 4px;
    display: grid;
    gap: 6px;
}
.meq-linhas-contrato li {
    display: flex;
    gap: 10px;
    flex-wrap: wrap;
    align-items: baseline;
    font-size: 12px;
    color: var(--neutral-600);
    padding: 6px 8px;
    background: var(--neutral-50);
    border-radius: var(--radius-sm, 6px);
}
.meq-linha-prod { flex: 1 1 160px; min-width: 0; overflow-wrap: anywhere; }
@media (max-width: 767px) {
    /* Em cartões, a linha-mãe já mostra tudo — as sub-linhas ficam por baixo */
    .meq-sublinhas td { padding: 0 !important; }
    .meq-sublinhas td::before { content: none !important; }
}

.meq-col-sel { width: 44px; }

.meq-barra-selecao {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px 16px;
    background: var(--primary-50);
    border-top: 1px solid var(--primary-200, var(--neutral-200));
    font-size: 13px;
}
.meq-barra-selecao.is-excede {
    background: var(--danger-50, #fdeceb);
    border-top-color: var(--danger-200, #f5c2c0);
    color: var(--danger-700, #a3302c);
}

.meq-anexo-atual {
    display: flex;
    align-items: center;
    gap: 8px;
    flex-wrap: wrap;
    padding: 10px;
    border: 1px solid var(--neutral-200);
    border-radius: var(--radius-md, 10px);
    background: var(--neutral-50);
}
.meq-anexo-nome { flex: 1 1 140px; min-width: 0; overflow-wrap: anywhere; font-size: 13px; }

/* Barra fixa do detalhe em mobile (o botão Editar tem de estar sempre ao alcance) */
.meq-barra-fixa {
    position: sticky;
    bottom: 0;
    z-index: 30;
    background: var(--surface, #fff);
    border-top: 1px solid var(--neutral-200);
    padding: 10px 12px calc(10px + env(safe-area-inset-bottom, 0px));
    margin: 12px -12px 0;
    box-shadow: 0 -4px 14px rgba(0, 0, 0, .06);
}
.meq-barra-fixa .ds-btn { min-height: 46px; }
@media (min-width: 768px) {
    .meq-barra-fixa { display: none; }
}

/* ---------- editor de texto rico ---------- */
.meq-rt-toolbar { flex-wrap: wrap; gap: 4px; }
.meq-rt-sel {
    height: 30px;
    padding: 0 6px;
    font-size: 12px;
    max-width: 118px;
}

/* ---------- entrada de emails ---------- */
.meq-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    align-items: center;
    min-height: 44px;
    padding: 6px 8px;
    border: 1px solid var(--neutral-300);
    border-radius: var(--radius-md, 10px);
    background: var(--surface, #fff);
    cursor: text;
}
.meq-tags:focus-within { border-color: var(--primary-500); box-shadow: 0 0 0 3px var(--primary-100); }
.meq-tags-input {
    flex: 1 1 160px;
    min-width: 120px;
    border: 0;
    outline: none;
    font: inherit;
    background: transparent;
    padding: 4px;
}
.meq-tag {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 3px 6px 3px 8px;
    background: var(--primary-50);
    color: var(--primary-700);
    border-radius: 999px;
    font-size: 12px;
    max-width: 100%;
}
.meq-tag-txt { overflow-wrap: anywhere; }
.meq-tag-x {
    border: 0;
    background: none;
    cursor: pointer;
    color: inherit;
    display: inline-flex;
    padding: 2px;
    border-radius: 50%;
}
.meq-tag-x:hover { background: rgba(0, 0, 0, .08); }

/* ---------- modais ---------- */
body.meq-modal-aberto { overflow: hidden; }
.meq-modal-head-txt { min-width: 0; }

/* Paisagem baixa (844x390): o modal cabe e faz scroll interno */
@media (max-height: 480px) {
    .meq-modal-overlay .ds-modal { max-height: 96vh; }
    .meq-modal-overlay .ds-modal-body { max-height: calc(96vh - 118px); overflow-y: auto; }
}

/* ---------- garantias transversais ---------- */
.meq-page :where(img, svg, video) { max-width: 100%; }
.meq-page { overflow-x: clip; }

/* Alvos de toque >= 44x44px (WCAG 2.5.5) — critério de aceitação.
   Aplica-se em mobile retrato (<768px) E em mobile paisagem (ex.: 844x390), onde a
   largura já não é "mobile" mas o dedo continua a ser o apontador.
   Cobre os controlos do design system e do editor de texto, que não podem ser
   alterados na origem (design-system.css é partilhado). */
@media (max-width: 767px), (max-height: 500px) and (max-width: 1024px) {
    /* fecho dos modais e checkboxes dentro deles */
    .meq-modal-overlay .ds-modal-close { min-width: 44px; min-height: 44px; }

    /* controlos pequenos partilhados (também dentro dos modais) */
    .meq-page .ds-combobox-clear,
    .meq-modal-overlay .ds-combobox-clear,
    .meq-page .ds-richtext-btn,
    .meq-modal-overlay .ds-richtext-btn,
    .meq-page .meq-dz-remover,
    .meq-modal-overlay .meq-dz-remover { min-width: 44px; min-height: 44px; }
    .meq-page .meq-rt-sel,
    .meq-modal-overlay .meq-rt-sel { height: 44px; min-height: 44px; }
    .meq-modal-overlay .ds-btn { min-height: 44px; }
    .meq-modal-overlay .ds-check { align-items: center; }
    .meq-modal-overlay .ds-check input[type="checkbox"] {
        appearance: none;
        -webkit-appearance: none;
        width: 44px;
        height: 44px;
        margin: 0 -8px 0 -10px;
        padding: 0;
        border: 0;
        background: transparent;
        display: grid;
        place-content: center;
        cursor: pointer;
        flex-shrink: 0;
    }
    .meq-modal-overlay .ds-check input[type="checkbox"]::before {
        content: "";
        width: 22px;
        height: 22px;
        border-radius: 5px;
        border: 2px solid var(--neutral-400, #9aa1ab);
        background: var(--surface, #fff) no-repeat center / 15px 15px;
    }
    .meq-modal-overlay .ds-check input[type="checkbox"]:checked::before {
        border-color: var(--primary-600, #2b6cb0);
        background-color: var(--primary-600, #2b6cb0);
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m20 6-11 11-5-5'/%3E%3C/svg%3E");
    }
}

@media (max-width: 767px) {
    .meq-page :where(.ds-btn, .ds-icon-btn, .ds-page-btn, .ds-chip),
    .meq-modal-overlay :where(.ds-btn, .ds-icon-btn, .ds-page-btn) { min-height: 44px; }
    .meq-page :where(.ds-btn, .ds-icon-btn, .ds-page-btn),
    .meq-modal-overlay :where(.ds-btn, .ds-icon-btn) { min-width: 44px; }
    .meq-page .ds-row-actions { gap: 6px; }

    /* Breadcrumb: links pequenos viram alvos tocáveis */
    .meq-page .ds-breadcrumb { align-items: center; row-gap: 2px; }
    .meq-page .ds-breadcrumb a {
        display: inline-flex;
        align-items: center;
        justify-content: flex-start;
        min-height: 44px;
        min-width: 44px;
        padding-right: 2px;
    }

    /* Checkboxes de seleção (Lista de Ficheiros): alvo real de 44x44.
       O Chromium ignora `padding` em checkboxes nativos, por isso desenha-se a caixa
       (22px) dentro de um alvo de 44px com `appearance:none`. */
    .meq-page .meq-col-sel .ds-check { display: inline-flex; align-items: center; justify-content: center; margin: 0; }
    .meq-page .meq-col-sel .ds-check input[type="checkbox"] {
        appearance: none;
        -webkit-appearance: none;
        width: 44px;
        height: 44px;
        margin: 0;
        padding: 0;
        border: 0;
        background: transparent;
        display: grid;
        place-content: center;
        cursor: pointer;
    }
    .meq-page .meq-col-sel .ds-check input[type="checkbox"]::before {
        content: "";
        width: 22px;
        height: 22px;
        border-radius: 5px;
        border: 2px solid var(--neutral-400, #9aa1ab);
        background: var(--surface, #fff);
        background-repeat: no-repeat;
        background-position: center;
        background-size: 15px 15px;
    }
    .meq-page .meq-col-sel .ds-check input[type="checkbox"]:checked::before {
        border-color: var(--primary-600, #2b6cb0);
        background-color: var(--primary-600, #2b6cb0);
        background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m20 6-11 11-5-5'/%3E%3C/svg%3E");
    }
    .meq-page .meq-col-sel .ds-check input[type="checkbox"]:focus-visible {
        outline: 2px solid var(--primary-500);
        outline-offset: -6px;
        border-radius: 8px;
    }

    /* Contadores dos chips do índice de secções */
    .meq-page .ds-chip-count { font-size: 12px; }

    /* Seletor de tamanho de página (lista e sub-tabelas) */
    .meq-page .ds-pagesize { align-items: center; }
    .meq-page .ds-pagesize label { display: inline-flex; align-items: center; min-height: 44px; }
    .meq-page .ds-pagesize .ds-select,
    .meq-page .ds-select--sm { min-height: 44px; }

    /* Botão "×" de limpar dos comboboxes */
    .meq-page .ds-combobox-clear,
    .meq-modal-overlay .ds-combobox-clear { min-width: 44px; min-height: 44px; }

    /* Barra do editor de texto rico */
    .meq-page .ds-richtext-btn,
    .meq-modal-overlay .ds-richtext-btn { min-width: 44px; min-height: 44px; }
    .meq-page .meq-rt-sel,
    .meq-modal-overlay .meq-rt-sel { height: 44px; min-height: 44px; }

    /* Links dentro dos campos de leitura da ficha (coordenadas → Google Maps) */
    .meq-page .ds-field-ro-value a,
    .meq-page .meq-popup-link { display: inline-flex; align-items: center; min-height: 44px; }

    /* Texto mínimo legível (check 8) */
    .meq-legenda-titulo,
    .meq-legenda-item,
    .meq-dz-sub,
    .meq-dz-tam,
    .meq-escalao-meses,
    .meq-escalao-uni,
    .meq-foto-atual-nome,
    .meq-linhas-contrato li { font-size: 12px; }
}

/* Modo escuro (o original não tinha) */
@media (prefers-color-scheme: dark) {
    .meq-escalao,
    .meq-linhas-contrato li,
    .meq-anexo-atual,
    .meq-foto-atual { background: rgba(255, 255, 255, .04); }
    .meq-fb-bg { fill: rgba(255, 255, 255, .04); }
    .meq-pin-dot { border-color: rgba(255, 255, 255, .85); }
}
:root[data-theme="dark"] .meq-escalao,
:root[data-theme="dark"] .meq-linhas-contrato li,
:root[data-theme="dark"] .meq-anexo-atual,
:root[data-theme="dark"] .meq-foto-atual { background: rgba(255, 255, 255, .04); }
:root[data-theme="light"] .meq-escalao,
:root[data-theme="light"] .meq-linhas-contrato li,
:root[data-theme="light"] .meq-anexo-atual,
:root[data-theme="light"] .meq-foto-atual { background: var(--neutral-50); }

/* ---------- sub-tabelas ordenáveis (item 111) ---------- */
.meq-th-sort {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: none;
    border: 0;
    padding: 4px 2px;
    margin: -4px -2px;
    font: inherit;
    color: inherit;
    cursor: pointer;
    min-height: 32px;
}
.meq-th-sort:hover { color: var(--primary-600, #2563eb); }
.meq-th-sort.is-active { color: var(--primary-600, #2563eb); font-weight: 600; }
.meq-th-seta { font-size: 11px; opacity: .7; line-height: 1; }
.meq-th-sort.is-active .meq-th-seta { opacity: 1; }
.meq-th-sort:focus-visible { outline: 2px solid var(--primary-500, #3b82f6); outline-offset: 2px; border-radius: 4px; }

/* ---------- rodapé da Ficha Técnica com Enviar Email (item 133) ---------- */
.meq-ficha-rodape {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding-top: 12px;
}

/* Alvo de toque confortável no cabeçalho ordenável em mobile */
@media (max-width: 767px) {
    .meq-th-sort { min-height: 40px; }
}

/* =====================================================================================
   FASE 2 — O DETALHE DENTRO DA FICHA-MODAL (mod-ficha.js + mod-ficha-equipamento.js)
   -------------------------------------------------------------------------------------
   Só o que a moldura do motor não sabe desenhar: a foto em tamanho útil, o envelope
   das sub-colecções e os retoques do preçário quando ele vive numa secção `ds-secao`
   em vez do card `meq-card-precos` da ficha em página.
   Mobile-first: só `min-width` daqui para baixo.
   ===================================================================================== */

/* Envelope de sub-colecção. Existe para o `redesenharSub()` continuar a poder repintar
   UMA secção (paginação, ordenação, seleção) sem repintar a ficha inteira. Não impõe
   caixa nenhuma — a caixa é da `ds-secao`. */
.meq-sub { display: block; min-width: 0; }

/* =====================================================================================
   A CAPA DO SUPORTE — a foto passa a ser o elemento principal do herói da ficha
   -------------------------------------------------------------------------------------
   Palavras do cliente: «os colaboradores percebem logo que equipamento é de forma
   visual». A variante `foto` do motor (ds-ficha.css §2.1) desenhava um selo de 96x54 px
   ao lado do texto — isso identifica um REGISTO, não um SUPORTE. Aqui a mesma variante
   passa a CAPA: uma banda que sangra até aos bordos do cartão-herói (por isso o
   `--fh-pad`), com o código, o nome e o estado logo por baixo.

   PORQUÊ AQUI E NÃO NA FOLHA DO MOTOR. A outra ficha com `variante:'foto'` são as
   Ativações de Marca, e o `mod-ativacoes.js` só entrega ao motor a miniatura de 192 px
   em `data:` — esticada a 1200 px seria uma mancha. O equipamento entrega a miniatura
   (primeiro fotograma, sem salto) E troca-a pela foto inteira em `aposPintarFicha()`.
   No dia em que as ativações fizerem o mesmo, acrescenta-se o seletor e mais nada.

   ZERO SALTOS DE LAYOUT: a altura da capa NUNCA depende da imagem — é `aspect-ratio`
   com a largura fixada por `calc()` abaixo dos 1024 px e altura declarada acima deles.
   O espaço está reservado antes do primeiro byte da fotografia chegar.
   ===================================================================================== */

/* A faixa de 3 px na cor do estado é a marca de assinatura nº 1 do desenho (§2.8) e
   vive no topo do cartão-herói — exatamente onde a capa agora começa. MEDIDO: sem
   `z-index` a fotografia ficava por cima e a ficha do equipamento era a única das dez
   sem o fio de cor do estado. */
.ds-ficha[data-entidade="equipamento"] .ds-ficha-faixa { z-index: 1; }

/* Mobile-first: capa em cima, identidade por baixo. */
.ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 12px;
    align-items: start;
}

/* Serve a <img> e o <span> do estado "sem foto" — a mesma caixa, a mesma reserva de
   espaço. `width` explícita + `margin-left` negativa: com as duas margens negativas e
   `width:100%` o box ficava com a largura da área e só sangrava para a esquerda. */
.ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-simbolo {
    width: calc(100% + var(--fh-pad) * 2);
    /* MEDIDO: sem isto a banda ficava 40 px curta à direita. O `img{max-width:100%}`
       do design-system.css travava a largura na da área da grelha e a sangria só
       acontecia do lado esquerdo (o da margem negativa). */
    max-width: none;
    height: auto;
    aspect-ratio: 16 / 9;
    max-height: 42vh;
    margin-top: calc(var(--fh-pad) * -1);
    margin-left: calc(var(--fh-pad) * -1);
    margin-bottom: 0;
    border-radius: 0;
    object-fit: cover;
    object-position: center;
    background: var(--surface-sunken);
    color: var(--text-3);
}

/* 13 dos 269 suportes não têm fotografia. O vazio diz o que é e porque está vazio —
   um retângulo cinzento seria indistinguível de uma imagem que não carregou.
   E é MAIS BAIXO do que a capa: guardar 171 px para dizer «não há» seria dar ao vazio
   o destaque que o pedido do cliente quer dar à fotografia. */
.ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-simbolo--sem-foto {
    gap: 6px;
    aspect-ratio: 3 / 1;
    max-height: 132px;
    background:
        repeating-linear-gradient(135deg,
            transparent 0 9px,
            rgba(163, 156, 157, .16) 9px 18px),
        var(--surface-sunken);
    box-shadow: inset 0 -1px 0 var(--border);
}
.ds-ficha[data-entidade="equipamento"] .ds-ficha-simbolo--sem-foto::after {
    content: "Sem fotografia";
    font-size: var(--txt-xs);
    font-weight: 600;
    letter-spacing: .01em;
}
.ds-ficha[data-entidade="equipamento"] .ds-ficha-simbolo--sem-foto .ds-ficha-ico {
    width: 26px;
    height: 26px;
}

/* MEDIDO num tablet de 768x1024: em 16:9 a banda dava 383 px de altura e empurrava os
   mosaicos e as abas para fora do ecrã. A partir dos 640 px a proporção deixa de mandar
   sozinha — o recorte é do `object-fit`, e o suporte continua perfeitamente
   reconhecível em 240 px. */
@media (min-width: 640px) {
    .ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-simbolo {
        max-height: 240px;
    }
}

/* A capa a 1254 px de largura pedia 705 px de altura em 16:9 e empurrava as abas para
   fora do ecrã — «a foto valoriza, não esmaga». Acima dos 1024 px a banda passa a ter
   ALTURA declarada (o recorte é do `object-fit`) e atravessa o herói inteiro.

   `display: contents` na identidade promove a fotografia e o texto a itens da grelha do
   herói: a banda ocupa a linha 1 de bordo a bordo, e o texto e os mosaicos partilham a
   linha 2. MEDIDO a 1440 px: os mosaicos mantêm os mesmos 715 px de sempre (continuam
   a caber os quatro em linha, com o mesmo corpo de 28 px) e a identidade ainda ganha
   15 px. Ou seja: a foto passou a protagonista sem tirar nada aos números. */
@media (min-width: 1024px) {
    .ds-ficha[data-entidade="equipamento"] .ds-ficha-hero-topo {
        grid-template-rows: auto auto;
    }

    .ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto {
        display: contents;
    }

    .ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-simbolo {
        grid-column: 1 / -1;
        grid-row: 1;
        height: clamp(150px, 19vh, 208px);
        aspect-ratio: auto;
        max-height: none;
    }

    .ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-simbolo--sem-foto {
        height: 104px;
        max-height: none;
    }

    .ds-ficha[data-entidade="equipamento"] .ds-ficha-ident--foto>.ds-ficha-ident-txt {
        grid-column: 1;
        grid-row: 2;
        align-self: center;
    }

    .ds-ficha[data-entidade="equipamento"] .ds-ficha-hero-topo>.ds-heroi {
        grid-column: 2;
        grid-row: 2;
        align-self: center;
    }
}

/* A foto do suporte, na aba Geral. `aspect-ratio` reserva o espaço antes de a imagem
   chegar: sem ela a secção crescia de repente e empurrava o resto para baixo depois do
   primeiro `ficha:pronta`.

   `object-fit: contain` e não `cover` DESDE QUE EXISTE A CAPA: a capa mostra o suporte
   recortado (é identidade, quer preencher a banda); esta secção mostra a fotografia
   INTEIRA, que é o que o comercial abre para ver o enquadramento. Com as duas a
   recortar igual, a segunda era a primeira outra vez. */
.meq-ficha-foto {
    margin: 0;
    display: flex;
    flex-direction: column;
    gap: 8px;
    min-width: 0;
}
/* MEDIDO nas fotografias reais do inventário: 0,97:1 a 1,61:1, com a maioria em 3:2 e
   4:3. Numa caixa de 16:9 à largura toda do modal (1214x414) uma foto 3:2 deixava ~300
   px de fundo de cada lado. Com 3:2 e um teto de largura, a moldura passa a ter a forma
   do que lá vai dentro e as barras do `contain` ficam residuais. */
.meq-ficha-foto img {
    display: block;
    width: 100%;
    max-width: 760px;
    aspect-ratio: 3 / 2;
    max-height: 62vh;
    object-fit: contain;
    background: var(--surface-sunken, var(--neutral-100));
    border: 1px solid var(--neutral-200);
    border-radius: var(--radius-lg, 14px);
}
.meq-ficha-foto-cap {
    font-size: 12px;
    color: var(--neutral-500);
    word-break: break-word;
}

/* Escalão sem valor: continua visível (regra de ouro dos dados — nada desaparece),
   mas em surdina, para o olho ir aos que têm preço. */
.meq-escalao.is-vazio {
    background: transparent;
    border-style: dashed;
}
.meq-escalao.is-vazio .meq-escalao-valor { color: var(--neutral-400); font-weight: 600; }

/* Dentro da ficha o mapa não precisa de tanta altura como na página: divide o ecrã
   com as abas e a barra de ações fixa. */
.ds-ficha .meq-mapa--ficha,
.ds-fichamod .meq-mapa--ficha {
    height: clamp(220px, 34vh, 380px);
}

@media (min-width: 1024px) {
    .meq-ficha-foto img { max-height: 56vh; }
}

/* =====================================================================================
   LISTAGEM — as colunas próprias de EQUIPAMENTOS na moldura `ds-listagem.css`
   -------------------------------------------------------------------------------------
   A folha genérica traz os slots do contrato (--codigo, --cliente, --marca…). As
   colunas deste módulo são outras — Nome, Categoria, SubCategoria — e a colocação no
   CARTÃO tem de ser EXPLÍCITA: a `.ds-lst-linha` é um grid com colocação célula a
   célula e uma célula sem `grid-row` cairia em auto-placement, sobrepondo-se às
   outras. Só isso é que aqui se declara; tudo o resto (faixa de estado, cartão,
   hover, tabela a partir dos 1024 px) vem da moldura.
   Mobile-first: a base é o cartão, o `min-width` é que traz a tabela.
   ===================================================================================== */

/* O respiro do cartão passa a variável porque a célula da FOTOGRAFIA sangra até aos
   bordos com margens negativas — e uma margem negativa que não bata certo com o
   `padding` deixa um risco de fundo à volta da imagem. Os valores são exatamente os
   que a `.ds-lst-linha` da moldura já usava. */
[data-lista="equipamentos"] .ds-lst-linha {
    --meq-cardpad-y: var(--sp-3);
    --meq-cardpad-x: var(--sp-4);
    padding: var(--meq-cardpad-y) var(--meq-cardpad-x);
    padding-left: calc(var(--meq-cardpad-x) + 3px);
}

/* --- cartão (< 1024 px): FOTO · Código | Estado · Nome · Categoria · SubCategoria --- */
.ds-lst-c--eqfoto { grid-column: 1 / -1; grid-row: 1; }
/* ⚠️ `--codigo` e `--estado` são da MOLDURA e vivem em TODAS as listagens da app
   (contratos, propostas, folhas…). Esta folha é global e SEM camada, por isso um
   `.ds-lst-c--codigo { grid-row: 2 }` solto empurrava o código de todos os outros
   módulos para cima do que lá está. O escopo `[data-lista="equipamentos"]` é
   obrigatório sempre que se toca numa classe que não é nossa. */
[data-lista="equipamentos"] .ds-lst-c--codigo { grid-row: 2; }
[data-lista="equipamentos"] .ds-lst-c--estado { grid-row: 2; }
.ds-lst-c--eqnome { grid-column: 1 / -1; grid-row: 3; }
.ds-lst-c--eqcategoria { grid-column: 1 / -1; grid-row: 4; }
.ds-lst-c--eqsub { grid-column: 1 / -1; grid-row: 5; }

/* O nome é o herói do cartão logo a seguir ao código: dispensa eyebrow. */
.ds-lst-c--eqnome[data-rot]::before { content: none; }
.ds-lst-c--eqfoto[data-rot]::before { content: none; }

/* A fotografia ocupa a largura toda do cartão e encosta aos bordos — é a primeira
   coisa que se vê, antes do código. A margem negativa anula o respiro do cartão
   (a faixa de 3 px do estado continua por cima, é `position:absolute` na linha). */
.ds-lst-c--eqfoto {
    margin: calc(var(--meq-cardpad-y) * -1) calc(var(--meq-cardpad-x) * -1) var(--sp-2)
        calc((var(--meq-cardpad-x) + 3px) * -1);
}

/* A CAIXA da fotografia. `aspect-ratio` aqui e não na imagem: assim o espaço está
   reservado mesmo antes de existir `<img>` com `src` — e a foto, que chega segundos
   depois e por `fetch` autenticado, entra sem empurrar nada. */
.meq-cel-foto {
    display: block;
    position: relative;
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    background: var(--surface-sunken, var(--neutral-100));
}

.meq-cel-foto-img {
    display: block;
    width: 100%;
    height: 100%;
    max-width: none;
    object-fit: cover;
    object-position: center;
    opacity: 0;
    transition: opacity .18s ease;
}

/* Só depois de a imagem ter mesmo fonte: sem isto via-se o retângulo do `alt` vazio
   a piscar entre a pintura da linha e a chegada do blob. */
.meq-cel-foto-img[data-carregada="1"] { opacity: 1; }

/* 13 dos 269 suportes não têm fotografia. Diz-se, com a mesma caixa: um retângulo
   cinzento liso lê-se como "está a carregar" ou "partiu-se". */
.meq-cel-foto-vazia {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 4px;
    color: var(--text-3);
    background: repeating-linear-gradient(135deg,
            transparent 0 9px,
            rgba(163, 156, 157, .12) 9px 18px);
}
.meq-cel-foto-vazia-txt {
    font-size: var(--txt-2xs);
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: var(--track-caps, .04em);
}

@media (prefers-reduced-motion: reduce) {
    .meq-cel-foto-img { transition: none; }
}

@media (min-width: 640px) and (max-width: 1023.98px) {
    /* Em 2 colunas de cartões a categoria e a subcategoria partilham a linha. */
    .ds-lst-c--eqcategoria { grid-column: 1; grid-row: 4; }
    .ds-lst-c--eqsub { grid-column: 2; grid-row: 4; text-align: right; }
}

/* --- tabela (>= 1024 px): larguras declaradas, `table-layout: fixed` da moldura --- */
@media (min-width: 1024px) {
    .ds-lst-h--eqnome { width: auto; }
    .ds-lst-h--eqcategoria { width: 190px; }
    .ds-lst-h--eqsub { width: 150px; }
    .ds-lst-c--eqnome,
    .ds-lst-c--eqcategoria,
    .ds-lst-c--eqsub { overflow: hidden; text-overflow: ellipsis; }

    /* MEDIDO a 1280 px: a coluna da miniatura tirou largura ao Nome e os nomes longos
       («EN113 Lagarinho (Sentido Ourém/Tomar)») passaram a ser CORTADOS a seco. As
       reticências da célula não os apanhavam porque o valor vem dentro de um
       `.ds-lst-forte` em `display:block` — e `text-overflow` só trata do conteúdo
       em linha do próprio bloco. Aplicadas onde o texto está, o corte volta a
       anunciar-se. */
    .meq-page[data-meq-modo="tabela"] .ds-lst-c--eqnome .ds-lst-forte {
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    /* Mesmo na tabela a linha ganha miniatura: reconhecer o suporte não pode depender
       do modo de apresentação escolhido. */
    .ds-lst-h--eqfoto { width: 84px; }
    .meq-page[data-meq-modo="tabela"] .ds-lst-c--eqfoto {
        margin: 0;
        padding: 6px var(--sp-3);
        padding-left: calc(var(--sp-3) - 3px);
        /* A faixa do estado era a borda esquerda da célula do CÓDIGO (ds-listagem.css
           §6). Agora a primeira célula é a foto — se não se mudasse, o fio de cor
           aparecia a meio da linha, que é onde ele deixa de querer dizer alguma coisa. */
        border-left: 3px solid var(--lst-estado);
    }
    .meq-page[data-meq-modo="tabela"] .ds-lst-c--codigo {
        border-left: 0;
        padding-left: var(--sp-3);
    }
    .meq-page[data-meq-modo="tabela"] .meq-cel-foto {
        width: 68px;
        aspect-ratio: 16 / 10;
        border-radius: var(--r-md, 10px);
    }
}

/* =====================================================================================
   MODO GRELHA (≥1024 px) — o inventário visual
   -------------------------------------------------------------------------------------
   A moldura transforma cartões em TABELA aos 1024 px. Em `data-meq-modo="grelha"`
   desfaz-se essa transformação e os cartões voltam — agora em várias colunas, com a
   fotografia a mandar. É o mesmo markup e as mesmas linhas: trocar de modo não
   repinta nada nem volta a pedir uma única imagem.
   ===================================================================================== */
@media (min-width: 1024px) {
    .meq-page[data-meq-modo="grelha"] .ds-lst-tabwrap {
        border: 0;
        background: none;
        box-shadow: none;
        overflow: visible;
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-tab { display: block; }
    .meq-page[data-meq-modo="grelha"] .ds-lst-tab thead { display: none; }

    .meq-page[data-meq-modo="grelha"] .ds-lst-tab tbody {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
        align-items: stretch;
        gap: var(--sp-4);
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-linha {
        display: grid;
        grid-template-columns: minmax(0, 1fr) auto;
        align-content: start;
        gap: var(--sp-1) var(--sp-3);
        position: relative;
        border: 1px solid var(--border);
        border-radius: var(--r-xl);
        background: var(--surface);
        box-shadow: var(--sh-1);
        overflow: hidden;
        transition: box-shadow .16s ease, border-color .16s ease;
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-linha::before {
        content: "";
        position: absolute;
        inset: 0 auto 0 0;
        width: 3px;
        background: var(--lst-estado);
        z-index: 1;
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-tab tbody tr.ds-lst-linha:hover {
        background: var(--surface);
        border-color: var(--border-strong);
        box-shadow: var(--sh-2);
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-c {
        display: block;
        padding: 0;
        border: 0;
        overflow: visible;
        text-overflow: clip;
        white-space: normal;
    }

    /* Os eyebrows voltam — em cartão o rótulo da coluna é a única pista do que é
       aquele texto (na tabela era o cabeçalho). */
    .meq-page[data-meq-modo="grelha"] .ds-lst-c[data-rot]::before {
        content: attr(data-rot);
        display: block;
        font-size: var(--txt-2xs);
        font-weight: var(--fw-bold);
        text-transform: uppercase;
        letter-spacing: var(--track-caps);
        color: var(--text-3);
    }

    .meq-page[data-meq-modo="grelha"] .ds-lst-c--codigo[data-rot]::before,
    .meq-page[data-meq-modo="grelha"] .ds-lst-c--estado[data-rot]::before,
    .meq-page[data-meq-modo="grelha"] .ds-lst-c--eqnome[data-rot]::before,
    .meq-page[data-meq-modo="grelha"] .ds-lst-c--eqfoto[data-rot]::before { content: none; }

    .meq-page[data-meq-modo="grelha"] .ds-lst-cod { font-size: var(--txt-lg); }
    .meq-page[data-meq-modo="grelha"] .ds-lst-c--estado { text-align: right; }

    .meq-page[data-meq-modo="grelha"] .ds-lst-c--eqfoto {
        margin: calc(var(--meq-cardpad-y) * -1) calc(var(--meq-cardpad-x) * -1) var(--sp-2)
            calc((var(--meq-cardpad-x) + 3px) * -1);
    }

    /* 3:2 e não 16:9 — MEDIDO num cartão de 264 px: em 16:9 a fotografia dava 149 px
       e o bloco de texto por baixo 160 px, ou seja, o texto ganhava ao suporte. Em 3:2
       são 176 px e a imagem volta a mandar no cartão. Em mobile mantém-se 16:9: aí a
       largura é o ecrã inteiro e 3:2 custava 40 px de scroll por linha da lista. */
    .meq-page[data-meq-modo="grelha"] .meq-cel-foto { aspect-ratio: 3 / 2; }
}

/* =====================================================================================
   BARRA DE RESULTADOS — ordenar (sempre) e escolher o modo (onde há dois)
   ===================================================================================== */
[data-lista="equipamentos"] .ds-lst-barra {
    align-items: center;
    row-gap: var(--sp-2);
}

.meq-barra-dir {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    flex-wrap: wrap;
    margin-left: auto;
}

.meq-ordenar {
    display: flex;
    align-items: center;
    gap: 4px;
}

.meq-ordenar-sel { min-width: 132px; }

/* MEDIDO pelo gate a 320/375 px: 27x34 px. Duas causas — a caixa nascia com 34 px e
   ainda encolhia num contentor `flex`. Mobile-first, o alvo é 44x44 e `flex:0 0 auto`
   tira-lhe a licença para encolher; só em ecrã grande COM RATO é que aperta. */
.meq-ord-dir {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    width: 44px;
    height: 44px;
    padding: 0;
    color: var(--text-2);
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-md, 10px);
    cursor: pointer;
}
.meq-ord-dir:hover { border-color: var(--border-strong); color: var(--text-1); }

@media (min-width: 768px) and (pointer: fine) {
    .meq-ord-dir { width: 34px; height: 34px; }
}

/* Dois modos, um só grupo — a pílula segmentada diz que são alternativas do mesmo
   conteúdo e não duas ações diferentes. */
.meq-modos {
    display: none;
    padding: 2px;
    background: var(--surface-sunken, var(--neutral-100));
    border: 1px solid var(--border);
    border-radius: var(--r-full, 999px);
}

.meq-modo-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    height: 30px;
    padding: 0 12px;
    font-size: var(--txt-sm);
    font-weight: 600;
    color: var(--text-3);
    background: none;
    border: 0;
    border-radius: var(--r-full, 999px);
    cursor: pointer;
}
.meq-modo-btn:hover { color: var(--text-1); }
.meq-modo-btn[aria-pressed="true"] {
    color: var(--text-1);
    background: var(--surface);
    box-shadow: var(--sh-1);
}

/* A escolha só existe onde existe tabela. Abaixo dos 1024 px há UMA apresentação
   possível (cartão), e um interruptor com uma posição só é um botão morto. */
@media (min-width: 1024px) {
    .meq-modos { display: inline-flex; }

    /* Em tabela quem ordena é o cabeçalho, e ter as duas portas ao mesmo tempo era
       ter duas verdades no ecrã. */
    .meq-page[data-meq-modo="tabela"] .meq-ordenar { display: none; }
}

/* Tablets de 1024 px com toque: o dedo é o mesmo de um telemóvel. */
@media (pointer: coarse) {
    .meq-modo-btn { height: 44px; }
}

/* O cabeçalho ordenável é um botão dentro do <th> da moldura: sem fundo próprio e
   com o mesmo alvo de toque dos restantes controlos. */
.ds-lst-h .ds-th-sortable {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    color: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    cursor: pointer;
}

/* KPI de aviso («Sem GPS»): é uma falha de dados, não uma contagem neutra. */
.ds-lst-kpi--aviso .ds-lst-kpi-n { color: var(--warning-700, #B45309); }

/* =====================================================================================
   PAINEL DO PARQUE (spec-frontend/dashboards-equipamentos.md)
   Acrescenta-se ao TOPO da listagem — não substitui a faixa `ds-lst-kpis` de estados
   nem nada do que já lá estava. Mobile-first: base <768px, `min-width` acrescenta.
   Três camadas: mosaicos (sempre visíveis) · gráficos + listas (colapsáveis).
   Em mobile o detalhe arranca FECHADO — a lista é o produto, o painel é contexto.
   ===================================================================================== */
/* A caixa própria (borda + sombra + cabeçalho com título e botão «Ver
   detalhe») foi APAGADA: era mais um desenho para a mesma coisa. Ficam a
   faixa de KPIs, sempre visível, e o painel «Análise» partilhado. */
.meq-painel {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2, 8px);
    min-width: 0;
}

/* ---------- mosaicos ---------- */
/* `auto-fit` em vez de um número fixo de colunas: os cartões variam com os dados
   (o «Por rentabilizar» desaparece quando não há nada por rentabilizar) e uma
   grelha de 6 fixas deixaria buracos. 148px é o mínimo em que "42 446 €" ainda
   cabe sem partir a linha a 320px. */
.meq-painel-kpis {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(148px, 1fr));
    /* `1fr` iguala TODAS as linhas à mais alta. Sem isto, o mosaico da ocupação (o
       único com linha de tendência) fazia a primeira linha ficar 18px mais alta do
       que as outras a 320px — o gate responsivo apanha-o como botões assimétricos,
       e tem razão: mosaicos de tamanhos diferentes leem-se como importâncias
       diferentes. */
    grid-auto-rows: 1fr;
    gap: var(--sp-2, 8px);
}

/* `--r-lg`: mesmo objeto que o `.ds-lst-kpi` da moldura, mesmo canto.
   Ver a nota igual em mod-ativacoes.css. */
.meq-kpi {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-height: 88px;
    padding: var(--sp-2, 8px) var(--sp-3, 12px);
    border: 1px solid var(--border, #E4E0E0);
    border-radius: var(--r-lg, 14px);
    background: var(--surface-alt, #FCFBFB);
    color: var(--text-2, #423B3D);
    text-align: left;
    cursor: pointer;
    transition: border-color .16s ease, box-shadow .16s ease, background .16s ease;

    --kpi-cor: var(--ink-400, #A39C9D);
}

.meq-kpi--estatico { cursor: default; }
.meq-kpi:hover { border-color: var(--border-strong, #CFC9CA); box-shadow: var(--sh-2, 0 2px 4px -1px rgba(43, 6, 7, .06)); }
.meq-kpi--estatico:hover { border-color: var(--border, #E4E0E0); box-shadow: none; }

.meq-kpi[aria-pressed="true"] {
    border-color: var(--kpi-cor);
    background: var(--surface, #fff);
    box-shadow: inset 0 -2px 0 var(--kpi-cor);
}

/* `min-height` de duas linhas: «Fichas incompletas» e «Contratado / mês» partem em
   duas num mosaico de 148px e, sem a reserva, os números da mesma linha ficavam a
   alturas diferentes. Reservar sempre duas linhas alinha-os — e continua a funcionar
   se um rótulo crescer. O ponto de cor alinha com a PRIMEIRA linha, não com o meio. */
.meq-kpi-rot {
    display: flex;
    align-items: flex-start;
    gap: 6px;
    min-height: 2.5em;
    font-size: var(--txt-xs, 0.75rem);
    font-weight: var(--fw-bold, 700);
    line-height: 1.25;
    text-transform: uppercase;
    letter-spacing: var(--track-caps, 0.06em);
    color: var(--text-3, #6E6668);
}

.meq-kpi-rot::before {
    content: "";
    width: 7px;
    height: 7px;
    margin-top: 0.35em;
    border-radius: var(--r-full, 9999px);
    background: var(--kpi-cor);
    flex-shrink: 0;
}

.meq-kpi-n {
    font-size: var(--txt-2xl, 1.375rem);
    font-weight: var(--fw-extra, 800);
    line-height: 1.05;
    letter-spacing: var(--track-tight, -0.02em);
    font-variant-numeric: var(--num-tabular, tabular-nums);
    color: var(--text-1, #1B1618);
}

.meq-kpi-sub,
.meq-kpi-delta {
    font-size: var(--txt-xs, 0.75rem);
    font-variant-numeric: var(--num-tabular, tabular-nums);
    line-height: 1.3;
}

.meq-kpi-sub { color: var(--text-3, #6E6668); }
.meq-kpi-delta { font-weight: var(--fw-semibold, 600); }
.meq-kpi-delta[data-tend="sobe"] { color: var(--success-700, #047857); }
.meq-kpi-delta[data-tend="desce"] { color: var(--danger-700, #A00F33); }
.meq-kpi-delta[data-tend="igual"] { color: var(--text-3, #6E6668); }

/* A paleta é a mesma dos KPIs da listagem — o vocabulário de cor não muda de bloco
   para bloco. `brand` é o nível «ação» do MOD.alertas: não é um problema, é dinheiro
   em cima da mesa. */
.meq-kpi[data-cor="green"] { --kpi-cor: var(--success-500, #10B981); }
.meq-kpi[data-cor="red"] { --kpi-cor: var(--danger-500, #E11D48); }
.meq-kpi[data-cor="amber"] { --kpi-cor: var(--warning-500, #F59E0B); }
.meq-kpi[data-cor="brand"] { --kpi-cor: var(--brand-500, #DA0A0D); }
.meq-kpi[data-cor="indigo"] { --kpi-cor: #6366f1; }
.meq-kpi[data-cor="gray"] { --kpi-cor: var(--ink-400, #A39C9D); }
.meq-kpi[data-cor="red"] .meq-kpi-n { color: var(--danger-700, #A00F33); }

/* ---------- corpo colapsável (`.ds-analise-corpo`, partilhado) ---------- */
.meq-painel .ds-analise-corpo { gap: var(--sp-4, 16px); }

/* ---------- gráficos ---------- */
.meq-painel-graficos {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4, 16px);
}

.meq-painel-graficos:empty { display: none; }

@media (min-width: 1024px) {
    .meq-painel-graficos { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.meq-painel-graf { margin: 0; min-width: 0; }

.meq-painel-graf-tit {
    display: flex;
    flex-direction: column;
    gap: 1px;
    margin-bottom: var(--sp-2, 8px);
    font-size: var(--txt-sm, 0.8125rem);
    font-weight: var(--fw-bold, 700);
    color: var(--text-1, #1B1618);
}

.meq-painel-graf-sub,
.meq-painel-graf-nota {
    font-size: var(--txt-xs, 0.75rem);
    font-weight: var(--fw-semibold, 600);
    color: var(--text-3, #6E6668);
}

/* O canvas do Chart.js só se comporta dentro de uma caixa com altura definida: com
   `maintainAspectRatio:false` e um pai sem altura, cresce sem parar a cada resize.
   `position:relative` + altura em clamp resolve nos dois sentidos. */
.meq-painel-graf-caixa {
    position: relative;
    height: clamp(210px, 34vh, 280px);
    min-width: 0;
}

.meq-painel-graf-caixa canvas { max-width: 100%; }

/* Sem Chart.js (CDN em baixo) o painel mostra os mesmos números em tabela. */
.meq-painel-graf-fallback { min-width: 0; overflow-x: auto; }
.meq-painel-graf-fallback + .meq-painel-graf-fallback { margin-top: var(--sp-4, 16px); }
.meq-painel-tabela { width: 100%; margin-top: var(--sp-2, 8px); }

/* ---------- listas acionáveis ---------- */
.meq-painel-listas {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: var(--sp-4, 16px);
}

.meq-painel-listas:empty { display: none; }

@media (min-width: 1024px) {
    .meq-painel-listas { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .meq-painel-cobertura { grid-column: 1 / -1; }
}

.meq-plista {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2, 8px);
    min-width: 0;
}

.meq-plista-cab {
    display: flex;
    align-items: center;
    gap: var(--sp-2, 8px);
}

.meq-plista-tit {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    min-width: 0;
    font-size: var(--txt-sm, 0.8125rem);
    font-weight: var(--fw-bold, 700);
    color: var(--text-1, #1B1618);
}

.meq-plista-tit svg { flex-shrink: 0; color: var(--text-3, #6E6668); }
.meq-plista-tit span { overflow: hidden; text-overflow: ellipsis; }
.meq-plista-n { margin-left: auto; flex-shrink: 0; font-variant-numeric: var(--num-tabular, tabular-nums); }

/* As linhas são o rail de alertas do MOD.alertas (mesmas classes, mesmos níveis,
   mesmas cores da ficha). Aqui só se acrescenta o que faz delas uma LISTA: a linha
   inteira é clicável e o botão de ação encolhe para caber ao lado em ecrã largo. */
.meq-plista-rail .ds-alerta { cursor: pointer; }
.meq-plista-rail .ds-alerta:hover { filter: brightness(0.985); }
.meq-plista-rail .ds-alerta-texto { overflow-wrap: anywhere; }

@media (min-width: 480px) {
    .meq-plista-rail .ds-alerta-accao { flex: 0 0 auto; }
}

.meq-plista-simples {
    display: flex;
    flex-direction: column;
    gap: 1px;
    padding: var(--sp-2, 8px);
    border-left: 3px solid var(--border-strong, #CFC9CA);
    font-size: var(--txt-sm, 0.8125rem);
}

.meq-plista-rodape { display: flex; }
.meq-plista-rodape .ds-btn { flex: 1 1 auto; }

@media (min-width: 640px) {
    .meq-plista-rodape .ds-btn { flex: 0 0 auto; }
}

/* ---------- cobertura: o que a base ainda não sabe ---------- */
/* Não é um resultado — é o aviso de que o resultado ainda não existe. Fica em tom
   informativo (azul), nunca em vermelho: dados por registar não são uma avaria. */
.meq-painel-cobertura {
    display: flex;
    flex-direction: column;
    gap: var(--sp-2, 8px);
    padding: var(--sp-3, 12px);
    border: 1px dashed var(--info-500, #3B82F6);
    border-radius: var(--r-md, 10px);
    background: var(--info-50, #EFF6FF);
    color: var(--info-700, #1D4ED8);
}

.meq-painel-cobertura .meq-plista-tit,
.meq-painel-cobertura .meq-plista-tit svg { color: inherit; }

.meq-painel-cobertura ul {
    display: flex;
    flex-direction: column;
    gap: 6px;
    margin: 0;
    padding-left: 18px;
    font-size: var(--txt-sm, 0.8125rem);
}

.meq-painel-cobertura em { font-style: normal; font-weight: var(--fw-bold, 700); }

/* ---------- estado vazio (o de erro é `.ds-analise-erro`, partilhado) ---------- */
.meq-painel-vazio {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--sp-2, 8px);
    margin: 0;
    grid-column: 1 / -1;
    font-size: var(--txt-sm, 0.8125rem);
    color: var(--text-3, #6E6668);
}

.meq-painel-vazio svg { color: var(--success-500, #10B981); flex-shrink: 0; }

/* Impressão: 6 mosaicos e 2 gráficos não vão para o papel. */
@media print {
    .meq-painel .ds-analise { display: none !important; }
}

/* --------------------------------------------------------------------------
   Controlos próprios (MOD.campos) — geometria dentro deste módulo

   O `.cp` nasce `width:100%` porque serve um campo de formulário, que ocupa a
   coluna toda. Os dois controlos da BARRA da lista — «Ordenar por» e «Por
   página» — mediam-se pelo conteúdo; esticados, empurravam o botão de inverter
   a ordem e o pager. Devolvem-se-lhes as larguras que tinham
   (`.meq-ordenar-sel` era `min-width:132px`; o «Por página», 74px).

   Presas às classes `meq-cp-ordenar` / `meq-cp-pagina`, que é este módulo que
   as pede ao montar — nenhuma outra página as apanha.
   -------------------------------------------------------------------------- */
.cp.meq-cp-ordenar {
    width: auto;
    min-width: 132px;
}

.cp.meq-cp-pagina {
    width: auto;
    min-width: 0;
}

.cp.meq-cp-pagina .cp-txt {
    flex: 0 1 auto;
    width: 3.5ch;
    min-width: 3.5ch;
    font-size: var(--text-xs);
}

/* Em toque o alvo volta aos 44px, como o botão de inverter a ordem ao lado. */
/* Largura E ponteiro: a auditoria corre a 320-414px SEM emular toque, e um
   telemóvel a sério cumpre as duas. Basta uma para o alvo subir. */
@media (max-width: 768px), (pointer: coarse) {
    .cp.meq-cp-pagina .cp-gatilho,
    .cp.meq-cp-ordenar .cp-gatilho { min-height: 44px; }
    /* 44px de largura, e não 3.5ch: em toque o alvo é o elemento, e uma
       caixa de 28px falhava o mínimo mesmo com o gatilho a 79px. */
    .cp.meq-cp-pagina .cp-txt {
        font-size: max(16px, var(--text-xs));
        width: var(--ds-touch, 44px);
        min-width: var(--ds-touch, 44px);
    }
}

/* =====================================================================================
   MAPA DE QUANTIDADES na ficha do suporte  (pedido 19ca2d6c, 2ª ronda — 27/08/2026)

   A tabela das sete folhas do Excel do cliente: ARTIGOS · QUANTIDADE · VALOR UNITÁRIO ·
   VALOR TOTAL, a fechar num total. Assenta na `.ds-table` do design system — aqui só se
   acrescenta o que esta tabela tem de próprio: a coluna do número de ordem, a segunda
   linha de cada peça (referência e observações), a linha de PASSO DE TRABALHO e o rodapé
   do total.

   ⚠️ O `overflow-x: auto` do `.meq-mapa-wrap` não é decoração. Com as cinco colunas e
   uma designação como «PARAFUSO M12 x 30 C/PORCA», a tabela não cabe a 390 px — e uma
   tabela que não cabe empurra a PÁGINA para o lado, o que parte o ecrã inteiro em vez de
   só a tabela. O scroll fica DENTRO da caixa.
   ===================================================================================== */
.meq-mapa-cab {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    flex-wrap: wrap;
    margin-bottom: var(--sp-3);
}

.meq-mapa-ident {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}

.meq-mapa-nome {
    font-weight: var(--fw-bold);
    color: var(--text-1);
    text-decoration: none;
}

.meq-mapa-nome:hover { text-decoration: underline; }

.meq-mapa-sub {
    font-size: var(--text-xs);
    color: var(--text-3);
}

.meq-mapa-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.meq-mapa-tabela { width: 100%; }

/* Coluna do número de ordem: estreita e discreta. A ordem das linhas é a ordem de
   MONTAGEM — numerá-las é metade da utilidade da lista para quem está no andaime. */
.meq-mapa-tabela .meq-mapa-n {
    width: 2.5rem;
    text-align: right;
    color: var(--text-3);
    font-variant-numeric: tabular-nums;
}

.meq-mapa-desig { display: block; }

.meq-mapa-ref,
.meq-mapa-obs {
    display: block;
    font-size: var(--txt-2xs, var(--text-xs));
    color: var(--text-3);
}

.meq-mapa-un {
    color: var(--text-3);
    font-size: var(--text-xs);
}

/* Preço por preencher. «—» e não «0,00 €»: zero é uma afirmação («é grátis») e o que
   ali está é uma ausência — o Excel do cliente tem linhas assim (PASSADEIRA a 0). */
.meq-mapa-sem { color: var(--text-3); }

/* PASSO DE TRABALHO («CORTE A LASER CANTONEIRAS», «PINTURA VIGAS + SOLDA…»): não é uma
   peça, é uma operação, e o cliente escreve-a no meio da lista porque é aí que ela
   acontece. Sem colunas de número e com fundo próprio, para se distinguir à distância de
   uma linha de peça sem dados. */
.meq-mapa-passo > td {
    background: var(--surface-sunken);
    color: var(--text-2);
}

.meq-mapa-passo-txt {
    font-style: italic;
    text-transform: uppercase;
    letter-spacing: var(--track-caps, .02em);
    font-size: var(--text-xs);
}

.meq-mapa-tabela .meq-mapa-tot { font-weight: var(--fw-bold); }

.meq-mapa-tabela tfoot td {
    border-top: 2px solid var(--border-strong);
    font-weight: var(--fw-bold);
}

.meq-mapa-total {
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* =====================================================================================
   MAPA CONGELADO — o aviso de «este suporte está a preços de então» (diretiva do dono,
   27/08/2026). O alerta usa o `.ds-alert--info` do design system; o que se acrescenta
   aqui é só a linha do botão por baixo do texto, para ele não ficar colado à frase.
   ===================================================================================== */
.meq-mapa-desatual .meq-mapa-acao {
    margin-top: var(--sp-2);
}

/* =====================================================================================
   PEDIDOS DE MANUTENÇÃO (ticket abcab273)
   Quatro regras e mais nada: tudo o resto (tabela, badges, campos, modal) é do
   design-system e é assim que fica igual às secções irmãs desta ficha.
   ===================================================================================== */

/* A tira do custo agregado, por cima da tabela. É a resposta à frase do pedido —
   «apurar os custos de manutenção de cada equipamento» — e por isso o número final
   é o único elemento com peso. Em telemóvel empilha, para o total não ser esmagado
   contra a decomposição. */
.meq-manut-custo {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-1) var(--sp-3);
    padding: var(--sp-2) var(--sp-3);
    margin-bottom: var(--sp-3);
    border: 1px solid var(--border);
    border-radius: var(--r-xl);
    background: var(--neutral-50);
}

.meq-manut-custo strong {
    margin-left: auto;
    font-weight: var(--fw-bold);
    font-size: var(--text-lg, 1.125rem);
}

@media (max-width: 640px) {
    .meq-manut-custo strong { margin-left: 0; }
}

/* Separador «Execução» dentro da modal do pedido: a metade de baixo do formulário só
   aparece a editar, e sem uma linha a dividir lê-se como continuação da de cima. */
.meq-pedman-sep { display: grid; gap: var(--sp-2); }
.meq-pedman-sep h3 { margin: 0; font-size: var(--text-sm, 0.875rem); font-weight: var(--fw-bold); }

.meq-pedman-viagem { display: grid; gap: var(--sp-2); }
.meq-pedman-vazio { margin: 0; }
