/* ForensiQ — Sistema de componentes forense (Fase 3, server-rendered).
 *
 * Primitivas reutilizadas por TODAS as páginas internas: cabeçalho de página,
 * barra de ferramentas/filtros, grelha densa (role=grid), badges de prioridade
 * e estado, paginação, páginas de detalhe, mini-mapa.
 *
 * Estética: consola forense densa (Bloomberg/Cellebrite). Cor é SÓ semântica.
 * Identificadores/hashes/timestamps/coordenadas em IBM Plex Mono.
 * Tokens e casca em main.css / app-shell.css — NÃO redefinir aqui.
 */

/* ============================================================
   Utilidades
   ============================================================ */
/* .mono: utilitário canónico em main.css (auditoria D82) — sem redefinição aqui. */
/* Páginas densas usam a largura toda do main (anula o container só onde marcado) */
.app-main .container:has(> .fullbleed) { max-width: none; padding: 0; }

/* Receita ÚNICA de ícone outline (auditoria D90): stroke herda a cor do texto;
   cada consumidor declara só tamanho/posicionamento. */
:is(.btn-accent, .btn-ghost) svg,
.toolbar__search-icon,
.grid__geo {
    fill: none;
    stroke: currentColor;
    stroke-width: 2;
    stroke-linecap: round;
    stroke-linejoin: round;
}

/* Micro-rótulo mono uppercase (auditoria D83): receita ÚNICA dos rótulos
   técnicos (sidebar, lente, factos, formulários, stats). Cada sítio mantém só
   os seus deltas (peso, cor própria, margens; o thead da grelha fica fora —
   tem sticky/bg/padding próprios). */
.subhead,
.app-sidebar__head-title,
.app-sidebar__group-title,
.lens-bar__label,
.scope-badge,
.facts__row dt,
.intake-dest__tag,
.stat__label,
.form-field > label {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--text-subtle);
}

/* Moldura ÚNICA dos contentores de mapa Leaflet (auditoria D85): cada
   componente define só a altura via --map-h (e o overflow no picker). */
.map-box, .geo-field__map, .map-picker {
    height: var(--map-h, 240px);
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    background: var(--bg-inset);
    z-index: 0;
}

/* ============================================================
   Cabeçalho de página
   ============================================================ */
.page-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4);
    border-bottom: 1px solid var(--border);
}
.page-head__heading { display: flex; align-items: baseline; gap: var(--sp-3); min-width: 0; flex-wrap: wrap; }
.page-head__title {
    margin: 0;
    font-size: var(--fs-xl);
    font-weight: var(--fw-semi);
    letter-spacing: var(--tracking-tight);
    color: var(--text-emphasis);
}
.page-head__count {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-subtle);
    white-space: nowrap;
}
/* Em mobile o título e o contador deixam de competir na mesma linha — empilham. */
@media (max-width: 767px) {
    .page-head { flex-direction: column; align-items: stretch; }
}

/* Linha de orientação abaixo do cabeçalho (contexto curto da página). */
.page-lead {
    margin: var(--sp-3) var(--sp-4) 0;
    max-width: 64ch;
    font-size: var(--fs-sm);
    color: var(--text-muted);
    line-height: 1.5;
}
/* Link inline no lede: distinguido por SUBLINHADO, não só pela cor (regra
   link-in-text-block); mesmo padrão de .stats-link/.feed__target. */
.page-lead a {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--text-placeholder);
    text-underline-offset: 2px;
}
.page-lead a:hover { color: var(--accent); text-decoration-color: var(--accent); }
.page-lead a:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* Botão de ação primária (accent) */
.btn-accent {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-sm);
    font-weight: var(--fw-medium);
    color: var(--on-accent);
    background: var(--accent);
    border: 1px solid var(--accent);
    border-radius: var(--r-2);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--t-fast) var(--ease);
}
.btn-accent:hover { background: var(--accent-hover); }
.btn-accent:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-accent svg { width: 16px; height: 16px; }

/* Botão secundário/neutro.
   Auto-contido: declara todas as suas propriedades (background, color, border,
   padding...) para não herdar nada da .btn-ghost legada do main.css, que é uma
   modificadora de .btn. O hover usa :not(:disabled) para igualar a
   especificidade do .btn-ghost:hover:not(:disabled) do main.css e, carregando
   depois, vencer a cascata independentemente da ordem de import na página. */
.btn-ghost {
    display: inline-flex;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-2) var(--sp-3);
    font-size: var(--fs-sm);
    color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-2);
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.btn-ghost:hover:not(:disabled) { background: var(--surface-hover); border-color: var(--accent); }
.btn-ghost:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.btn-ghost svg { width: 15px; height: 15px; }
/* Alvo de toque >=44px nas ações de página em mobile (uso de campo, luvas) */
@media (max-width: 767px) {
    .btn-accent, .btn-ghost { min-height: var(--min-touch); }
}

/* ============================================================
   Barra de ferramentas / filtros (sticky)
   ============================================================ */
/* Controlo DENSO (auditoria D84): pele única dos inputs/selects de toolbar e
   filtros — fundo inset + borda + raio + altura parametrizados; cada contexto
   define só --ctl-h/--ctl-border/--ctl-r e a sua tipografia/cores próprias.
   (Os campos de formulário .form-field têm pele própria, sem altura fixa.) */
.grid-toolbar select,
.grid-toolbar input[type="search"],
.grid__filter-cell input,
.grid__filter-cell select,
.filter-pop__btn,
.filter-pop__panel input[type="date"],
.toolbar input[type="search"],
.toolbar input[type="text"],
.toolbar input[type="date"],
.toolbar select {
    height: var(--ctl-h, 30px);
    color: var(--text);
    background: var(--bg-inset);
    border: 1px solid var(--ctl-border, var(--border));
    border-radius: var(--ctl-r, var(--r-1));
}
:is(.toolbar, .grid-toolbar, .grid__filter-cell) input::placeholder { color: var(--text-placeholder); }
:is(.toolbar, .grid-toolbar, .grid__filter-cell) :is(input, select):focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: -1px;
    border-color: transparent;
}

.toolbar {
    display: flex;
    /* flex-direction:row e margin:0 explícitos (auto-contido). A antiga .toolbar
       legada do main.css, que colidia com esta, foi removida. */
    flex-direction: row;
    align-items: center;
    gap: var(--sp-2);
    margin: 0;
    padding: var(--sp-2) var(--sp-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-subtle);
    position: sticky;
    top: 0;
    z-index: 3;
    flex-wrap: wrap;
}

/* Barra fina sobre a grelha — busca transversal + ordenação (filtros por coluna
   vivem no <thead>; no telemóvel colapsam no botão "Filtros"). */
.grid-toolbar {
    display: flex;
    align-items: center;
    gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-4);
    border-bottom: 1px solid var(--border);
    background: var(--bg-subtle);
    flex-wrap: wrap;
}
.grid-toolbar__sort { display: inline-flex; align-items: center; gap: var(--sp-2); }
.grid-toolbar__sort-label { font-size: var(--fs-xs); color: var(--text-muted); }
.grid-toolbar select {
    padding: 0 var(--sp-2);
    font-family: var(--font-sans); font-size: var(--fs-sm);
    cursor: pointer;
}

/* Cabeçalho em 2 linhas COLADAS (nomes _col_names.html + filtros _filter_row.html):
   no desktop parecem uma só célula por coluna. Linha de nomes (th) = cabeçalho
   sticky; linha de filtros (td) cola-se por baixo (mesmo fundo, sem borda entre as
   duas; a borda inferior do cabeçalho fica na linha de filtros). No telemóvel a
   linha de filtros vira o painel "Filtros". */
.grid thead tr.grid__head-row > th { border-bottom: none; padding-bottom: 2px; }
.grid thead td.grid__filter-cell {
    padding: 0 var(--sp-3) var(--sp-2);
    vertical-align: top;
    background: var(--bg-subtle);
    border-bottom: 1px solid var(--border-strong);
}
.grid__col-label { display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.grid__filter-cell input,
.grid__filter-cell select {
    width: 100%;
    min-width: 0;
    --ctl-h: 28px;
    padding: 0 var(--sp-1);
    font-family: var(--font-sans);
    font-size: var(--fs-xs);
}
.grid__filter-cell select { cursor: pointer; }
.grid__filter-daterange { display: flex; flex-direction: column; gap: 3px; }
.grid__empty-cell { padding: var(--sp-6) var(--sp-4); text-align: center; color: var(--text-muted); }
@media (pointer: coarse) {
    .grid__filter-cell input, .grid__filter-cell select, .grid-toolbar select { height: var(--min-touch); }
}

/* Tabela de dados: LARGURA FIXA + reticências (acaba com a tabela "gigante" do
   full-width). Larguras por coluna em CSS (sem estilo inline → CSP-safe). */
.grid--filterable { table-layout: fixed; width: 100%; }
.grid--filterable > tbody > tr > td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Larguras por coluna (CSP-safe; sem estilo inline). A GridColumn.width (int %)
   escolhe a classe no <th> da 1.ª linha do <thead> — que table-layout:fixed lê.
   Somam 100% por grelha → não há overflow inicial; a barra horizontal só aparece
   (overflow-x:auto) depois de o utilizador alargar uma coluna. */
.grid__col--w6  { width: 6%; }
.grid__col--w8  { width: 8%; }
.grid__col--w9  { width: 9%; }
.grid__col--w10 { width: 10%; }
.grid__col--w11 { width: 11%; }
.grid__col--w12 { width: 12%; }
.grid__col--w13 { width: 13%; }
.grid__col--w14 { width: 14%; }
.grid__col--w15 { width: 15%; }
.grid__col--w16 { width: 16%; }
.grid__col--w18 { width: 18%; }
.grid__col--w20 { width: 20%; }
.grid__col--w21 { width: 21%; }
.grid__col--w22 { width: 22%; }
.grid__col--w23 { width: 23%; }
.grid__col--w24 { width: 24%; }
.grid__col--w27 { width: 27%; }
.grid__col--w34 { width: 34%; }

/* Coluna redimensionável: pega arrastável (column-resize.js mexe em style.width
   por JS — CSSOM, permitido pelo CSP; não é estilo inline no HTML). */
.grid--resizable thead tr:first-child th { position: relative; }
.col-resize { position: absolute; top: 0; right: -3px; width: 7px; height: 100%; cursor: col-resize; user-select: none; touch-action: none; z-index: 2; }
.col-resize:hover, .col-resize--active { background: var(--accent); opacity: 0.5; }

/* ============================================================
   Telemóvel: grelha de ocorrências reduzida a 4 colunas + painel de filtros
   ============================================================ */
@media (max-width: 767px) {
    /* Prioridade, Tipo de crime e Local escondem-se (col-reduce-hide); Agente
       (col-hide-sm) também. Sobram Código · NUIPC · Data. SEM filtros por coluna no
       telemóvel — só a busca global; a linha de filtros do <thead> fica escondida.
       SEM scroll lateral: a tabela passa a auto/100%, anulando as larguras % do desktop. */
    .grid--mobile-reduce { table-layout: auto; min-width: 0; }
    .grid--mobile-reduce .col-reduce-hide { display: none; }
    .grid--mobile-reduce .grid__col { width: auto; }
    .grid--mobile-reduce .grid__cell-time { display: none; }   /* data sem hora (vai p/ gaveta) */
    .grid--mobile-reduce .grid__filter-row { display: none; }  /* sem filtros por coluna no telemóvel */
    /* busca global ocupa a largura toda da barra */
    .grid-toolbar .toolbar__search { flex: 1 1 100%; max-width: none; }
}

/* Filtro de DATA como botão + popover (elemento nativo <details>, CSP-safe). */
.filter-pop { position: relative; }
.filter-pop__btn {
    list-style: none; cursor: pointer; --ctl-h: 28px; line-height: 26px;
    padding: 0 var(--sp-1); font-size: var(--fs-xs); color: var(--text-muted);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.filter-pop__btn::-webkit-details-marker { display: none; }
.filter-pop[open] .filter-pop__btn { border-color: var(--accent); }
.filter-pop--active .filter-pop__btn { color: var(--accent-on-tint); border-color: var(--accent-tint); background: var(--accent-tint); }
.filter-pop__panel {
    position: absolute; top: calc(100% + 4px); left: 0; z-index: 1000;
    display: flex; flex-direction: column; gap: var(--sp-2);
    padding: var(--sp-3); min-width: 190px;
    background: var(--surface-elevated); border: 1px solid var(--border); border-radius: var(--r-2); box-shadow: var(--shadow-lg);
}
.filter-pop__panel label { display: flex; flex-direction: column; gap: 2px; font-size: var(--fs-xs); color: var(--text-muted); }
.filter-pop__panel input[type="date"] { padding: 0 var(--sp-1); font-family: var(--font-mono); font-size: var(--fs-xs); }
.toolbar__search { position: relative; flex: 1 1 auto; min-width: 180px; max-width: 440px; }
.toolbar__search-icon {
    position: absolute;
    left: 10px; top: 50%;
    width: 15px; height: 15px;
    transform: translateY(-50%);
    color: var(--text-subtle);
    pointer-events: none;
}
.toolbar input[type="search"],
.toolbar input[type="text"],
.grid-toolbar input[type="search"] {
    width: 100%;
    --ctl-h: 34px;
    --ctl-border: var(--border-input);
    --ctl-r: var(--r-2);
    padding: 0 var(--sp-3) 0 32px;
    font-family: var(--font-sans);
    font-size: var(--fs-sm);
}
.toolbar select {
    --ctl-h: 34px;
    --ctl-border: var(--border-input);
    --ctl-r: var(--r-2);
    /* Cap de largura: o Chrome dimensiona o <select> à opção mais longa, e os
       rótulos de categoria de crime são extensos — sem isto o dropdown estica e
       desfigura a toolbar. O texto fechado trunca; a lista aberta mostra tudo. */
    max-width: 220px;
    padding: 0 var(--sp-2);
    font-family: var(--font-sans);
    font-size: var(--fs-xs);
    color: var(--text-muted);
    cursor: pointer;
    text-overflow: ellipsis;
}
.toolbar input[type="date"] {
    --ctl-h: 34px;
    --ctl-border: var(--border-input);
    --ctl-r: var(--r-2);
    padding: 0 var(--sp-2);
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    color: var(--text-muted);
    cursor: pointer;
}
.toolbar__date { max-width: 150px; }
/* Controlos do toolbar a >=44px em mobile (pesquisa, prioridade, ordenação) */
@media (max-width: 767px) {
    .toolbar input[type="search"],
    .toolbar input[type="text"],
    .toolbar input[type="date"],
    .toolbar select,
    .grid-toolbar input[type="search"] { height: var(--min-touch); }
}
.toolbar__busy {
    flex-shrink: 0;
    width: 14px; height: 14px;
    border: 2px solid var(--border-strong);
    border-top-color: var(--accent);
    border-radius: 50%;
    opacity: 0;
    transition: opacity var(--t-fast);
}
/* @keyframes `spin` único em main.css (auditoria D86); o bloco global de
   prefers-reduced-motion de main.css cobre a exceção que vivia aqui. */
.toolbar__busy.htmx-request { opacity: 1; animation: spin 0.6s linear infinite; }

/* Disclosure "Filtros" (custody_list) — em desktop os filtros ficam sempre
   inline na toolbar (o summary é escondido e o <details> comporta-se como um
   contentor flex transparente); em mobile colapsam atrás de um botão "Filtros"
   para a toolbar sticky não ocupar 6-7 linhas antes da grelha. */
.toolbar__filters { display: contents; }
.toolbar__filters-summary { display: none; }
/* content-visibility:visible anula a regra UA que esconde os filhos de um
   <details> fechado, para os filtros ficarem sempre inline em desktop mesmo
   quando o <details> não está [open]. */
.toolbar__filters-body { display: contents; content-visibility: visible; }
@media (max-width: 767px) {
    .toolbar__filters {
        display: block;
        flex: 1 1 100%;
        margin: 0;
        border: 1px solid var(--border-input);
        border-radius: var(--r-2);
        background: var(--bg-inset);
    }
    .toolbar__filters-summary {
        display: flex;
        align-items: center;
        gap: var(--sp-2);
        min-height: var(--min-touch);
        padding: 0 var(--sp-3);
        font-family: var(--font-sans);
        font-size: var(--fs-sm);
        color: var(--text-muted);
        cursor: pointer;
        list-style: none;
    }
    .toolbar__filters-summary::-webkit-details-marker { display: none; }
    .toolbar__filters-summary::after {
        content: "+";
        margin-left: auto;
        font-family: var(--font-mono);
        color: var(--text-subtle);
    }
    .toolbar__filters[open] > .toolbar__filters-summary::after { content: "−"; }
    .toolbar__filters-summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
    .toolbar__filters-body {
        display: flex;
        flex-direction: column;
        gap: var(--sp-2);
        padding: var(--sp-2) var(--sp-3) var(--sp-3);
    }
    .toolbar__filters-body .toolbar__date,
    .toolbar__filters-body select { max-width: none; width: 100%; }
}

/* ============================================================
   Grelha densa (role=grid)
   ============================================================ */
/* Scroll horizontal nas grelhas densas (replica .data-table-wrap): em ecrãs
   estreitos as colunas mono nowrap não cabem — deslizar em vez de cortar. */
.gridwrap { width: 100%; overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* Variante de painel: altura fixa com scroll interno — a lista mostra sempre
   várias linhas sem truncar (o thead sticky cola ao topo deste contentor). */
.gridwrap--scroll { max-height: 396px; overflow-y: auto; }
.grid { width: 100%; min-width: max-content; border-collapse: collapse; font-size: var(--fs-sm); }
.grid thead th {
    position: sticky;
    /* O cabeçalho cola ao TOPO do seu contentor de scroll — que é a .gridwrap,
       não a página: `.gridwrap { overflow-x: auto }` promove o overflow-y a
       `auto` (regra do CSS), tornando-a um contentor de scroll nos dois eixos.
       Logo o offset é 0. Um offset pela altura da toolbar (a antiga
       --toolbar-h) era medido no espaço errado e empurrava o cabeçalho para
       DENTRO da gridwrap, por cima da 1.ª linha (cabeçalhos descolados). */
    top: 0;
    z-index: 1;
    text-align: left;
    padding: var(--sp-2) var(--sp-3);
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    font-weight: var(--fw-semi);
    letter-spacing: var(--tracking-wide);
    text-transform: uppercase;
    color: var(--text-subtle);
    background: var(--bg-subtle);
    border-bottom: 1px solid var(--border-strong);
    white-space: nowrap;
}
.grid__th--pri { width: 52px; text-align: center; }
/* Sem linha de filtros no DOM (painéis com hide_filter_row), a linha de nomes
   é a última do thead e recupera a borda forte que por contrato vivia na
   linha de filtros (ver o bloco .grid__head-row mais acima). */
.grid thead tr.grid__head-row:last-child > th {
    border-bottom: 1px solid var(--border-strong);
    padding-bottom: var(--sp-2);
}
.grid tbody tr {
    border-bottom: 1px solid var(--divider);
    border-left: 2px solid transparent;
    transition: background var(--t-fast) var(--ease);
}
.grid--clickable tbody tr { cursor: pointer; }
/* Ligação dentro de uma célula de grelha (ex.: código → detalhe nas Guias de transporte; _grid_cell.html via link_key). */
.grid__link { color: var(--accent); text-decoration: none; }
.grid__link:hover { text-decoration: underline; }

/* Barra de distribuição (stats): <meter> nativo estilizado para o tema forense
   (sem isto rende o widget verde do user-agent, fora da paleta âmbar/escura). */
.grid__bar { width: 160px; }
meter.bar {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 8px;
    vertical-align: middle;
    background: var(--bg-inset);
    border: 1px solid var(--border);
    border-radius: var(--r-1);
}
meter.bar::-webkit-meter-bar {
    background: var(--bg-inset);
    border: 0;
    height: 8px;
    border-radius: var(--r-1);
}
meter.bar::-webkit-meter-optimum-value { background: var(--accent); border-radius: var(--r-1); }
meter.bar::-moz-meter-bar { background: var(--accent); border-radius: var(--r-1); }
.grid tbody tr:hover { background: var(--surface-hover); }
.grid tbody tr:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.grid td {
    padding: var(--sp-2) var(--sp-3);
    color: var(--text);
    vertical-align: middle;
    white-space: nowrap;
}
.grid__code { color: var(--text-emphasis); font-weight: var(--fw-medium); }
.grid--clickable tbody tr:hover .grid__code { color: var(--accent); }
.grid__ellipsis { max-width: 280px; overflow: hidden; text-overflow: ellipsis; }
.grid__muted { color: var(--text-muted); }
/* Recuo progressivo da árvore de sub-componentes (nível 2 = «└» encostado;
   nível 3 = neto recuado — sem isto o neto era indistinguível do filho). */
.tree-lvl-3 { padding-left: 1.1em; }
.grid__pri { text-align: center; }
.grid__geo {
    width: 12px; height: 12px;
    margin-right: 4px;
    color: var(--accent);
    vertical-align: -1px;
}

/* ============================================================
   Badges
   ============================================================ */
/* Prioridade — único ponto de cor da linha (P1 lei / P2 manual) */
.pri {
    display: inline-flex; align-items: center; justify-content: center;
    min-width: 26px; padding: 1px 6px;
    font-family: var(--font-mono); font-size: var(--fs-micro);
    font-weight: var(--fw-semi); letter-spacing: 0.02em;
    border-radius: var(--r-1); border: 1px solid transparent;
}
/* Prioridade por PESO, não por cor — o hue é da família ESTADO. P1 = chip
   cheio (tinta forte invertida), P2 = chip de contorno, Normal = nada.
   O texto «P1»/«P2» já identifica; o preenchimento dá a hierarquia. */
.pri--p1 { color: var(--text-inverse); background: var(--text-emphasis); border-color: var(--text-emphasis); }
.pri--p2 { color: var(--text); background: transparent; border-color: var(--border-strong); }
.pri--none { color: var(--text-placeholder); background: transparent; }

/* Bolinha de urgência (componente REUTILIZÁVEL — todas as tabelas). Nas vistas
   mobile reduzidas substitui a coluna própria escondida (aqui: prioridade das
   ocorrências). A PRIORIDADE distingue-se por peso (cheio/anel), não por cor;
   os tons de ESTADO (ok/warn/…) é que reaproveitam o hue, mais abaixo. */
.urgency-dot { display: inline-block; flex: none; width: 8px; height: 8px; border-radius: 50%; vertical-align: middle; box-sizing: border-box; }
.urgency-dot--p1 { background: var(--text-emphasis); }
.urgency-dot--p2 { background: transparent; border: 1.5px solid var(--text-muted); }
.urgency-dot--none { background: var(--border-strong); }
/* Mapa tom→cor ÚNICO (auditoria D88): a bolinha de urgência e o ponto do badge
   .state consomem a mesma regra (os NOMES de tom vêm de core/labels.py). */
.urgency-dot--ok, .state--ok::before { background: var(--success); }
.urgency-dot--warn, .state--warn::before { background: var(--warning); }
.urgency-dot--info, .state--info::before { background: var(--info); }
.urgency-dot--danger, .state--danger::before { background: var(--danger); }
.urgency-dot--muted, .state--muted::before { background: var(--text-subtle); }
/* Na grelha a bolinha prefixa a coluna identificadora (QUALQUER tipo de célula,
   via .grid__dot-host) e SÓ aparece na vista mobile reduzida. */
.grid__dot-host > .urgency-dot { display: none; margin-right: var(--sp-2); }
@media (max-width: 767px) { .grid--mobile-reduce .grid__dot-host > .urgency-dot { display: inline-block; } }
/* Marcador de VALIDAÇÃO PENDENTE (eixo próprio da apreensão, CPP 178.º/6):
   bolinha standalone SUFIXA o conteúdo da célula (warn = por validar,
   danger = em atraso) e está SEMPRE visível — escapa à regra do dot-host,
   que só governa a bolinha de urgência prefixada. */
.val-flag { margin-left: var(--sp-2); }
.grid__dot-host > .val-flag { display: inline-block; margin-right: 0; }
/* Legenda (discreta; a de urgência só no telemóvel — no desktop há coluna.
   O modificador --always fica visível em qualquer largura: é o caso da legenda
   de pendência, cujos marcadores não têm coluna própria). */
.urgency-legend {
    display: none; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
    margin: 0; padding: var(--sp-3) var(--sp-4);
    list-style: none; font-size: var(--fs-xs); color: var(--text-muted);
    border-top: 1px solid var(--border);
}
.urgency-legend__item { display: inline-flex; align-items: center; gap: var(--sp-2); }
.urgency-legend--always { display: flex; }
@media (max-width: 767px) {
    .urgency-legend { display: flex; }
}

/* Estado (custódia legal / estado atual da evidência) — pill neutro mono */
.state {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 1px 8px;
    font-family: var(--font-mono); font-size: var(--fs-micro);
    color: var(--text-muted);
    background: var(--surface);
    border: 1px solid var(--border-strong);
    border-radius: var(--r-pill);
    white-space: nowrap;
}
.state::before {
    content: ""; width: 6px; height: 6px; border-radius: 50%;
    background: var(--text-subtle); flex-shrink: 0;
}
/* Variantes .state--*::before: no mapa tom→cor único, junto ao .urgency-dot (D88). */

/* Badges dos ATOS clicáveis (_act_badges.html → consulta read-only no modal):
   grupo numa só âncora. Sublinhado pontilhado distingue o link sem ser só
   pela cor (regra link-in-text-block — mesmo padrão de .page-lead a); no
   hover/teclado a borda dos pills acende a accent. */
.state-acts {
    display: inline-flex; align-items: center; gap: 4px;
    text-decoration: underline dotted;
    text-decoration-color: var(--text-placeholder);
    text-underline-offset: 4px;
}
.state-acts:hover { text-decoration-color: var(--accent); }
.state-acts:hover .state, .state-acts:focus-visible .state {
    border-color: var(--accent);
    color: var(--text);
}
.state-acts:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ============================================================
   Paginação
   ============================================================ */
.pager {
    display: flex; align-items: center; justify-content: center;
    gap: var(--sp-3); padding: var(--sp-3);
    border-top: 1px solid var(--border);
}
.pager__btn {
    padding: var(--sp-1) var(--sp-3);
    font-size: var(--fs-xs); color: var(--text);
    background: var(--surface);
    border: 1px solid var(--border-strong); border-radius: var(--r-2);
    text-decoration: none; cursor: pointer;
}
.pager__btn:hover { background: var(--surface-hover); border-color: var(--accent); }
.pager__btn--off { color: var(--text-placeholder); background: transparent; border-color: var(--border); pointer-events: none; }
.pager__status { font-size: var(--fs-xs); color: var(--text-muted); }
/* Navegação entre páginas fiável ao toque (>=44px) em ecrãs estreitos */
@media (max-width: 767px) {
    .pager__btn { min-height: var(--min-touch); display: inline-flex; align-items: center; }
}

/* ============================================================
   Estado vazio
   ============================================================ */
.empty { padding: var(--sp-12) var(--sp-4); text-align: center; }
.empty__title { font-size: var(--fs-lg); color: var(--text-muted); margin: 0 0 var(--sp-1); }
.empty__hint { font-size: var(--fs-sm); color: var(--text-subtle); margin: 0; }
/* Variante "ledger" (listas probatórias, ex.: feed de auditoria): voz de
   terminal — à esquerda, mono, contida — em vez do placeholder centrado. */
.empty--ledger { padding: var(--sp-4) var(--sp-3); text-align: left; }
.empty--ledger .empty__hint {
    font-family: var(--font-mono);
    font-size: var(--fs-xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--text-muted);
}

/* ============================================================
   Factos (lista de pares chave/valor) — páginas de detalhe
   ============================================================ */
.facts { margin: 0; }
.facts__row {
    display: grid;
    grid-template-columns: 96px 1fr;
    gap: var(--sp-3);
    padding: var(--sp-2) 0;
    border-bottom: 1px solid var(--divider);
    align-items: baseline;
}
.facts__row dd { margin: 0; font-size: var(--fs-sm); color: var(--text); }
.facts__row dd.mono { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
/* Controlo embutido numa linha de factos (ex.: select do tema em /settings/):
   a ficha .form-input dá fundo/tinta dos tokens — o fundo UA do <select> sem
   classe varia por plataforma e reprovava contraste AA no axe (CI Linux).
   Largura ao conteúdo, não à coluna inteira. */
.facts__row dd > .form-input { width: auto; }
.facts__src { color: var(--text-subtle); font-size: var(--fs-xs); }

/* ============================================================
   Destino de receção (read-only) — a instituição de destino e a sua
   coordenada vêm da ficha; a receção confirma-as, não as captura.
   ============================================================ */
.intake-dest { display: flex; flex-direction: column; gap: var(--sp-2); margin: 0 0 var(--sp-4); }
.intake-dest__card {
    border: 1px solid var(--border-strong);
    border-left: 3px solid var(--accent);
    background: var(--accent-tint);
    border-radius: var(--r-2);
    padding: var(--sp-3) var(--sp-4);
}
.intake-dest__tag {
    display: block;
    color: var(--accent); font-weight: var(--fw-semi);
    margin-bottom: 2px;
}
.intake-dest__name {
    margin: 0; font-size: var(--fs-base); font-weight: var(--fw-semi);
    color: var(--text-emphasis); line-height: var(--lh-tight);
}
.intake-dest__sigla {
    font-size: var(--fs-xs); color: var(--text-muted);
    padding: 1px 6px; border: 1px solid var(--border-strong);
    border-radius: var(--r-1); margin-left: var(--sp-1);
}
.intake-dest__addr { margin: var(--sp-1) 0 0; font-size: var(--fs-sm); color: var(--text-muted); }
.intake-dest__gps {
    margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--text);
    font-feature-settings: var(--ffs-forensic);
    display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap;
}
.intake-dest__src { font-family: var(--font-sans); font-size: var(--fs-xs); color: var(--text-subtle); }
.intake-dest__gps--missing { color: var(--text-muted); }

/* ============================================================
   Secção (blocos em páginas de detalhe)
   ============================================================ */
.section { border: 1px solid var(--border); border-radius: var(--r-3); margin-bottom: var(--sp-4); overflow: hidden; }
.section__head {
    /* wrap: chips/navegação (filtro ativo, janela do fluxo) quebram linha em
       vez de serem cortados pelo overflow:hidden da .section. */
    display: flex; align-items: center; justify-content: space-between; gap: var(--sp-2);
    flex-wrap: wrap;
    padding: var(--sp-2) var(--sp-3);
    background: var(--bg-subtle);
    border-bottom: 1px solid var(--border);
}
.section__title {
    /* Mesma receita do .geo-hero__title (cabeçalho de cartão ÚNICO): antes a
       mesma página tinha títulos de secção a 10px e 11px. */
    margin: 0; font-family: var(--font-mono); font-size: var(--fs-2xs);
    text-transform: uppercase; letter-spacing: var(--tracking-wide);
    color: var(--text-muted); font-weight: var(--fw-semi);
}
/* Link de cauda do cabeçalho de secção ("Ver todas →"). */
.section__more {
    font-family: var(--font-mono); font-size: var(--fs-2xs);
    text-transform: uppercase; letter-spacing: var(--tracking-wide);
    color: var(--accent); text-decoration: none; white-space: nowrap;
}
.section__more:hover { text-decoration: underline; }
.section__body { padding: var(--sp-3); }
.section__body--flush { padding: 0; }

/* Páginas de detalhe (hub do caso / item) */
.detail { padding: var(--sp-4); }
.detail__head {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: var(--sp-3); margin-bottom: var(--sp-4); flex-wrap: wrap;
}
.detail__title {
    display: flex; align-items: center; gap: var(--sp-2); margin: 0;
    font-family: var(--font-mono); font-size: var(--fs-2xl); font-weight: var(--fw-bold);
    letter-spacing: var(--tracking-tight); color: var(--text-emphasis); word-break: break-all;
}
.detail__sub { font-size: var(--fs-sm); color: var(--text-muted); margin: 2px 0 0; }
.detail__actions { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.cols-2 { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: var(--sp-4); align-items: start; }
@media (max-width: 1023px) { .cols-2 { grid-template-columns: 1fr; } }
.map-box { --map-h: 280px; }
.map-box--tall { --map-h: 380px; }

/* Marcadores da trajetória de custódia (forensic-list.js, modo Cadeia).
   Carregado DEPOIS do leaflet.css → o reset sobrepõe-se ao .leaflet-div-icon
   default (caixa branca com borda). Sem estilo inline (CSP estrita). */
.fq-chain-pin { background: transparent; border: 0; display: flex; align-items: center; justify-content: center; }
/* Base ÚNICA do pino numerado — mapa da cadeia E legenda (auditoria D89):
   a receita (pill âmbar + número mono) vive aqui; cada lado só dimensiona. */
.fq-chain-pin__num, .map-legend__pin {
    display: flex; align-items: center; justify-content: center;
    border-radius: var(--r-pill);
    background: var(--accent); color: var(--on-accent);
    font-family: var(--font-mono); font-weight: var(--fw-semi); line-height: 1;
}
.fq-chain-pin__num {
    width: 24px; height: 24px; font-size: 11px;
    border: 2px solid var(--bg-subtle); box-shadow: 0 1px 5px rgba(0, 0, 0, 0.5);
}
/* Origem (1.º ponto): contorno âmbar, fundo sóbrio. */
.fq-chain-pin--first .fq-chain-pin__num { background: var(--bg-subtle); color: var(--accent); border-color: var(--accent); }
/* Localização atual (último ponto): maior + halo de destaque. */
.fq-chain-pin--current .fq-chain-pin__num {
    width: 30px; height: 30px; font-size: 12px;
    box-shadow: 0 0 0 3px var(--accent-tint), 0 1px 6px rgba(0, 0, 0, 0.55);
}
/* Pin ATIVO (linha selecionada na lista do detalhe): anel âmbar cheio. */
.fq-chain-pin--active .fq-chain-pin__num {
    box-shadow: 0 0 0 3px var(--accent), 0 1px 6px rgba(0, 0, 0, 0.55);
}

/* Lista COMPACTA de eventos da cadeia (detalhe do item, sob o mapa) — consulta
   interativa [data-chain-evt]; o ledger oficial (hash + âncoras) é /custody/. */
.chain-list { margin: 0; padding: 0; list-style: none; border-top: 1px solid var(--border); }
.chain-row { display: flex; align-items: center; border-bottom: 1px solid var(--border); }
.chain-row__btn {
    flex: 1; min-width: 0; display: flex; align-items: baseline; gap: var(--sp-2);
    flex-wrap: wrap; padding: var(--sp-2) var(--sp-4);
    background: none; border: 0; text-align: left; cursor: pointer;
    font-size: var(--fs-sm); color: var(--text);
}
.chain-row__btn:hover, .chain-row__btn:focus-visible { background: var(--bg-subtle); }
.chain-row__btn.is-active { background: var(--accent-tint); }
.chain-row__seq { font-size: var(--fs-xs); color: var(--text-subtle); }
.chain-row__time, .chain-row__loc { font-size: var(--fs-xs); color: var(--text-muted); }
.chain-row__nogps { font-size: var(--fs-xs); color: var(--text-subtle); font-style: italic; }
.chain-row__link { padding: 0 var(--sp-4) 0 var(--sp-2); font-size: var(--fs-xs); white-space: nowrap; }

/* Legenda do trajeto (sob o mapa da cadeia). */
.map-legend { display: flex; flex-wrap: wrap; gap: var(--sp-3); align-items: center; margin: 0; padding: var(--sp-2) var(--sp-4); list-style: none; font-size: var(--fs-xs); color: var(--text-muted); }
.map-legend__item { display: inline-flex; align-items: center; gap: 6px; }
.map-legend__pin { display: inline-flex; width: 18px; height: 18px; font-size: 10px; }
.map-legend__pin--first { background: var(--bg-subtle); color: var(--accent); border: 1.5px solid var(--accent); }
.map-legend__pin--current { box-shadow: 0 0 0 2px var(--accent-tint); }
.map-legend__dash { width: 22px; height: 0; border-top: 3px dashed var(--accent); }

/* Campo de localização auto-localizado (geo-field.js): mapa no topo + barra de
   estado + inputs. Reutilizável em qualquer formulário com localização. */
.geo-field { margin-bottom: var(--sp-5); }
.geo-field__title { margin: 0 0 var(--sp-2); }
.geo-field__map { --map-h: 300px; }
.geo-field__map[hidden] { display: none; }
.geo-field__bar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin: var(--sp-2) 0 var(--sp-3); }
.geo-field__status { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); }
.geo-field__status--flag { color: var(--warning); }
.geo-field__status--error { color: var(--danger); }
/* Coordenadas bloqueadas (vindas de GPS/mapa): legíveis mas claramente
   não-editáveis — o ajuste fino é clicar no mapa. A morada fica editável.
   Texto muted + fundo subtil sinalizam "bloqueado" SEM opacity (que derrubava o
   contraste abaixo de AA — axe). --text-muted cumpre AA nos dois temas. */
.geo-field input[readonly] { color: var(--text-muted); background: var(--bg-subtle); cursor: default; }
/* A atribuição do Leaflet é agora estilizada na fonte única map-theme.css
   (tokens com variante por tema); a antiga regra dark daqui (#08436a, escrita
   para o fundo BRANCO default) vencia por especificidade e ficava ilegível
   (~1.7:1) sobre o novo fundo --surface escuro. */

/* Foto da prova — hero no detalhe do item (object-fit: contain p/ não recortar prova) */
.photo-hero { margin: 0 0 var(--sp-4); border: 1px solid var(--border); border-radius: var(--r-2); overflow: hidden; background: var(--bg-inset); }
.photo-hero__img { display: block; width: 100%; max-height: 340px; object-fit: contain; background: var(--bg-inset); }

/* Documento público (verificação por QR, standalone sem casca) */
.pubdoc { max-width: 760px; margin: 0 auto; padding: var(--sp-10) var(--sp-4); }
.pubdoc__brand { display: flex; align-items: center; gap: var(--sp-2); color: var(--text-emphasis); font-weight: var(--fw-semi); margin-bottom: var(--sp-6); }
.pubdoc__brand svg { width: 28px; height: 24px; }
.pubdoc__title { font-family: var(--font-mono); font-size: var(--fs-2xl); color: var(--text-emphasis); margin: var(--sp-2) 0 var(--sp-1); word-break: break-all; }
.pubdoc__sub { color: var(--text-muted); margin: 0 0 var(--sp-6); font-size: var(--fs-sm); }
.pubdoc__note { font-size: var(--fs-xs); color: var(--text-subtle); margin-top: var(--sp-4); line-height: var(--lh-base); }

/* Dashboard — linha de regie + faixa de métricas */
.dash { padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-5); }
/* Linha de regie: título + carimbo de âmbito/momento numa só banda mono densa
   (substitui o h1 de 28px que repetia a sidebar e o breadcrumb sem acrescentar
   nada). O h1 mantém-se para a estrutura do documento, em tamanho de carimbo. */
.dash__regie {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--sp-2) var(--sp-3);
}
.dash__regie-title {
    margin: 0;
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    font-weight: var(--fw-semi);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--text-emphasis);
}
.dash__regie-meta {
    font-family: var(--font-mono);
    font-size: var(--fs-micro);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--text-muted);
}
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-3); }
.stat { border: 1px solid var(--border); border-radius: var(--r-3); padding: var(--sp-3) var(--sp-4); background: var(--bg-subtle); }
.stat__num { font-family: var(--font-mono); font-size: var(--fs-2xl); font-weight: var(--fw-semi); color: var(--text-emphasis); font-feature-settings: var(--ffs-forensic); }
.stat__label { margin-top: 2px; }
@media (max-width: 767px) { .stats { grid-template-columns: repeat(2, 1fr); } }
.stats--3 { grid-template-columns: repeat(3, 1fr); }
@media (max-width: 767px) { .stats--3 { grid-template-columns: repeat(2, 1fr); } }
.detail__desc { margin: var(--sp-2) 0 0; font-size: var(--fs-sm); color: var(--text-muted); line-height: var(--lh-base); }

/* Estatísticas (UX 2026-06) — seletor de janela, meta do cabeçalho, alertas de SLA. */
.section__meta { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-subtle); }
.stats-window { display: flex; align-items: center; flex-wrap: wrap; gap: var(--sp-2); }
.stats-window__label { font-family: var(--font-mono); font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--text-subtle); }
.stats-window__opt {
    font-size: var(--fs-xs); font-family: var(--font-mono); color: var(--text-muted);
    text-decoration: none; padding: 2px var(--sp-2); border: 1px solid var(--border);
    border-radius: var(--r-pill); transition: color var(--t-fast) var(--ease), border-color var(--t-fast) var(--ease);
}
.stats-window__opt:hover { color: var(--text-emphasis); border-color: var(--border-strong); }
.stats-window__opt.is-active { color: var(--accent-on-tint); background: var(--accent-tint); border-color: transparent; }
.stats-flow__table { margin-top: var(--sp-4); }
.stats-alerts { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-2); }
.stats-alert {
    display: flex; align-items: baseline; gap: var(--sp-3);
    padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong); border-radius: var(--r-1); background: var(--bg-subtle);
}
.stats-alert--warn { border-left-color: var(--warning); }
.stats-alert--info { border-left-color: var(--info); }
.stats-alert__num { font-family: var(--font-mono); font-size: var(--fs-lg); font-weight: var(--fw-semi); color: var(--text-emphasis); font-feature-settings: "tnum" 1; min-width: 3ch; }
.stats-alert__label { font-size: var(--fs-sm); color: var(--text-muted); line-height: 1.4; }
/* Rótulo acionável de /stats/ (alertas E linhas do stock): âncora de
   drill-down re-derivável com sublinhado DISCRETO persistente (padrão
   .feed__target — affordance visível em repouso, não só no hover). */
.stats-link {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--text-placeholder);
    text-underline-offset: 2px;
}
.stats-link:hover { color: var(--accent); text-decoration-color: var(--accent); }
.stats-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Valor numérico visível ao lado da barra semanal (o title não chega em
   mobile/varrimento). A célula vira flex: o <meter> é width:100% e, sozinho,
   empurrava o número para fora dos 160px da coluna. */
.stats-flow__n { font-size: var(--fs-xs); color: var(--text-muted); font-feature-settings: "tnum" 1; }
.stats-flow__table .grid__bar { display: flex; align-items: center; gap: var(--sp-2); }
.stats-flow__table meter.bar { flex: 1; }

/* Chip do filtro ativo no cabeçalho (clicar limpa) — partilhado pelo painel e
   pelas páginas de lista (grid_page; veio de geo-hero.css na auditoria UX B9). */
.attn-filter-chip {
    font-family: var(--font-mono);
    font-size: var(--fs-2xs);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
    color: var(--accent-on-tint);
    background: var(--accent-tint);
    border: 1px solid var(--accent);
    border-radius: var(--r-pill);
    padding: 2px var(--sp-2);
    text-decoration: none;
    white-space: nowrap;
}
.attn-filter-chip:hover { background: var(--accent-tint-hover); }

/* Auditoria & Integridade — banner de estado da cadeia de hash. */
.integrity-banner {
    display: flex; align-items: center; gap: var(--sp-3);
    padding: var(--sp-3) var(--sp-4); border: 1px solid var(--border);
    border-left: 3px solid var(--border-strong); border-radius: var(--r-2); background: var(--bg-subtle);
}
.integrity-banner--ok { border-left-color: var(--success); }
.integrity-banner--broken { border-left-color: var(--danger); }
.integrity-banner__title { margin: 0; font-size: var(--fs-md); font-weight: var(--fw-semi); color: var(--text-emphasis); }
.integrity-banner__meta { margin: 2px 0 0; font-size: var(--fs-xs); font-family: var(--font-mono); color: var(--text-subtle); }
.integrity-breaks { margin-top: var(--sp-4); }

/* Subtítulo curto (mono uppercase) reutilizável */
.subhead { margin: 0 0 var(--sp-1); }

/* ============================================================
   Botão copiar (valores forenses: código, hash, coordenadas)
   ============================================================ */
.copy-btn {
    flex-shrink: 0;
    width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 13px; color: var(--text-subtle);
    background: transparent; border: 1px solid var(--border); border-radius: var(--r-1);
    cursor: pointer;
    transition: color var(--t-fast), border-color var(--t-fast);
}
.copy-btn:hover { color: var(--text); border-color: var(--border-strong); }
.copy-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.copy-btn.is-copied { color: var(--success); border-color: var(--success); }
/* Alvo de toque >=44px no terreno (com luvas/stress); glifo mantém-se a ~24px */
@media (pointer: coarse) {
    .copy-btn { min-width: var(--min-touch); min-height: var(--min-touch); }
}

/* ============================================================
   Detalhe — utilitários partilhados (fichas, hashes, sem-geo)
   ============================================================ */
.dd__nogeo {
    font-size: var(--fs-xs); color: var(--text-subtle); font-family: var(--font-mono);
    padding: var(--sp-3); border: 1px dashed var(--border); border-radius: var(--r-2); text-align: center;
}
.dd__full { align-self: flex-start; font-size: var(--fs-sm); color: var(--accent); text-decoration: none; font-weight: var(--fw-medium); }
.dd__full:hover { text-decoration: underline; }
.dd__hash { word-break: break-all; font-size: var(--fs-xs); color: var(--text-muted); }

/* ============================================================
   Timeline do ledger de custódia (vertical)
   ============================================================ */
.timeline { list-style: none; margin: 0; padding: 0; }
.timeline__item {
    position: relative;
    padding: 0 0 var(--sp-4) var(--sp-6);
    border-left: 2px solid var(--border-strong);
    margin-left: 6px;
}
.timeline__item:last-child { border-left-color: transparent; padding-bottom: 0; }
.timeline__dot {
    position: absolute; left: -7px; top: 2px;
    width: 12px; height: 12px; border-radius: 50%;
    background: var(--surface); border: 2px solid var(--accent);
}
.timeline__head { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.timeline__seq { font-family: var(--font-mono); font-size: var(--fs-micro); color: var(--text-subtle); }
.timeline__event { font-weight: var(--fw-semi); color: var(--text-emphasis); font-size: var(--fs-sm); }
.timeline__time { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); margin-left: auto; }
.timeline__meta { display: flex; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-1); font-size: var(--fs-xs); color: var(--text-muted); }
.timeline__acc--flag { color: var(--danger); }
/* Evento ancorado (#evt-<seq>, vindo da lista de custódias): realce âmbar
   discreto no marcador e fundo — a cor CLASSIFICA o evento visado; o
   scroll-margin desencosta a âncora do cabeçalho sticky. */
.timeline__item { scroll-margin-top: calc(var(--app-top-h, 52px) + var(--sp-4)); }
.timeline__item:target { background: var(--accent-tint, rgba(245, 158, 11, 0.08)); border-radius: var(--r-1); }
.timeline__item:target > .timeline__dot { background: var(--accent); }
.timeline__item:target > .timeline__head .timeline__event { color: var(--accent); }

/* ============================================================
   Form (fichas densas) — para os formulários server-rendered
   ============================================================ */
.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-3) var(--sp-4); }
.form-field { display: flex; flex-direction: column; gap: var(--sp-1); min-width: 0; }
.form-field--full { grid-column: 1 / -1; }
.form-field input, .form-field select, .form-field textarea {
    width: 100%; padding: var(--sp-2) var(--sp-3);
    font-family: var(--font-sans); font-size: var(--fs-sm); color: var(--text);
    background: var(--bg-inset); border: 1px solid var(--border-input); border-radius: var(--r-2);
}
.form-field input.mono { font-family: var(--font-mono); }
.form-field textarea { min-height: 88px; resize: vertical; }
.form-field input:focus-visible, .form-field select:focus-visible, .form-field textarea:focus-visible {
    outline: 2px solid var(--accent); outline-offset: -1px; border-color: transparent;
}
/* Dono ÚNICO de .form-error/.visible, .form-hint e .form-actions (auditoria
   D81): as definições homónimas de main.css foram removidas — os comentários-
   -armadura sobre a ordem de carregamento deixaram de ser necessários. O erro
   está escondido por omissão e só aparece com .visible (os spans
   server-rendered emitem ambas as classes). */
.form-error { font-size: var(--fs-xs); color: var(--danger); min-height: 0; display: none; }
.form-error.visible { display: block; }
.form-hint { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-subtle); margin-top: 2px; }
/* Aviso de FORMULÁRIO (crime-cascade.js): a tipologia torna a ocorrência
   prioritária por lei — alerta, não badge de prioridade, daí cor de estado. */
.pri-hint--p1 { color: var(--danger); }
.form-actions { display: flex; gap: var(--sp-2); justify-content: flex-end; margin-top: var(--sp-4); }
/* Modo terreno: a barra de ações acompanha o teclado virtual no telemóvel
   (queixa do João: o teclado tapava os botões). --kb é medido por
   field-keyboard.js (Visual Viewport). No desktop mantém o fluxo normal. */
@media (max-width: 767px) {
    .form-actions--sticky {
        position: sticky;
        bottom: var(--kb, 0px);
        margin-top: var(--sp-4);
        padding: var(--sp-3) var(--sp-4);
        padding-bottom: calc(var(--sp-3) + env(safe-area-inset-bottom));
        background: var(--surface);
        border-top: 1px solid var(--border);
        z-index: 5;
    }
    .form-actions--sticky .btn-accent,
    .form-actions--sticky .btn-ghost { flex: 1; min-height: var(--min-touch); }
}
.form-inline { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.form-check { display: flex; align-items: center; gap: var(--sp-2); font-family: var(--font-sans); font-size: var(--fs-sm); color: var(--text); text-transform: none; letter-spacing: 0; cursor: pointer; }
.form-check input { width: auto; }
/* O atributo `hidden` tem de vencer o display:flex acima (regra de autor bate o
   [hidden] da UA stylesheet) — usado p.ex. na elevação manual de prioridade,
   que o crime-cascade.js mostra/esconde conforme o tipo de crime. */
.form-check[hidden] { display: none; }
/* Elevação de prioridade encostada ao texto-guia da prioridade (sem o salto do
   form-grid), para se ler como continuação do que a lei deriva. */
.form-check--inline-hint { margin-top: var(--sp-2); }

/* Coluna de seleção por linha (occurrence_intake) — toda a célula é tocável,
   para marcar/desmarcar itens com luvas em operação de campo em lote. */
.grid__check { padding: 0; text-align: center; }
.grid__check-label {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: var(--min-touch);
    min-width: var(--min-touch);
    margin: 0;
    padding: 0 var(--sp-2);
}
.geo-acc--flag { color: var(--danger); }
.geo-acc--error { color: var(--danger); }
.intake-meta { margin-top: var(--sp-4); }

/* Picker de POIs próximos — lista de botões selecionáveis (fiável em mobile,
   ao contrário do <datalist>). A escolha escreve no input location_name. */
.poi-picker {
    display: flex;
    flex-direction: column;
    gap: 2px;
    margin-top: var(--sp-2);
    max-height: 220px;
    overflow-y: auto;
    border: 1px solid var(--border);
    border-radius: var(--r-2);
    background: var(--bg-subtle);
}
.poi-chip {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--sp-3);
    width: 100%;
    min-height: var(--min-touch);
    padding: var(--sp-2) var(--sp-3);
    text-align: left;
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    color: var(--text);
    font-family: var(--font-sans);
    font-size: var(--fs-sm);
    cursor: pointer;
}
.poi-chip:last-child { border-bottom: none; }
.poi-chip:hover { background: var(--surface-hover); }
.poi-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.poi-chip[aria-pressed="true"] {
    background: var(--accent-tint);
    color: var(--accent);
}
.poi-chip__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.poi-chip__meta {
    flex-shrink: 0;
    font-size: var(--fs-xs);
    color: var(--text-subtle);
    text-transform: uppercase;
    letter-spacing: var(--tracking-wide);
}

/* Identificadores específicos do tipo (evidences_new) — visibilidade por tipo */
.id-section { display: none; }
.id-section.is-on { display: block; }
.id-field { display: none; }
.id-field.is-on { display: flex; }
.upper { text-transform: uppercase; }

/* Resultado de lookup externo (IMEI/VIN) abaixo do campo */
.lookup-result { margin-top: var(--sp-2); padding: var(--sp-2) var(--sp-3); border: 1px solid var(--border); border-radius: var(--r-2); background: var(--bg-subtle); }
.lookup-result .facts__row { grid-template-columns: 120px 1fr; }
.lookup-pending { margin: 0; font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); }
.lookup-error { margin: 0; font-size: var(--fs-xs); color: var(--danger); }

/* Registo de evento de custódia (disclosure inline na timeline) */
.custody-register > summary { cursor: pointer; list-style: none; }
.custody-register > summary::-webkit-details-marker { display: none; }
.custody-register > summary:hover { background: var(--surface-hover); }
.custody-register__hint { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--accent); }

/* Registo de atividade (feed do AuditLog) */
.feed { list-style: none; margin: 0; padding: 0; }
/* Uma linha por evento, em ritmo de log: seq + tempo + ação curta (nowrap, a
   coluna de 104px partia rótulos em 3-4 linhas) + alvo limitado a ~56ch para o
   autor ficar COLADO ao evento (a 1920 o 1fr empurrava-o ~1400px para a direita). */
.feed__item {
    display: grid;
    /* Larguras FIXAS nas 3 primeiras colunas: cada item é a sua própria grid,
       e colunas auto desalinhavam o log entre linhas. 112px cabe o rótulo
       curto mais longo (VISUALIZAÇÃO) a --fs-micro mono. */
    grid-template-columns: 56px 132px 112px minmax(0, 56ch) max-content;
    justify-content: start;
    gap: var(--sp-3);
    align-items: baseline;
    padding: var(--sp-2) var(--sp-3);
    border-bottom: 1px solid var(--divider);
    font-size: var(--fs-sm);
}
.feed__item:last-child { border-bottom: 0; }
.feed__item:hover { background: var(--surface-hover); }
/* Nº de sequência do AuditLog: o append-only tornado visível — a prova de que
   nada foi removido é estética E probatória. Esbatido, não compete. */
.feed__seq { font-size: var(--fs-xs); color: var(--text-subtle); white-space: nowrap; }
.feed__time { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-subtle); white-space: nowrap; }
.feed__action {
    font-family: var(--font-mono); font-size: var(--fs-micro); white-space: nowrap;
    text-transform: uppercase; letter-spacing: var(--tracking-wide); color: var(--text-muted);
}
/* Cor por TOM semântico (auditoria D97): a tradução ação→tom vive em
   core/labels.py (ACTION_CSS), não em strings do enum coladas no CSS. */
.feed__action--ok { color: var(--success); }
.feed__action--info { color: var(--info); }
.feed__action--danger { color: var(--danger); }
.feed__what { color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Link do alvo: sublinhado discreto em repouso — no mesmo feed coexistem
   alvos com e sem link, e "clicável" é informação (WCAG 1.4.1). */
.feed__target {
    color: inherit;
    text-decoration: underline;
    text-decoration-color: var(--text-placeholder);
    text-underline-offset: 2px;
}
.feed__target:hover { text-decoration-color: var(--accent); }
.feed__who { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-subtle); white-space: nowrap; }
/* Em mobile reorganiza em duas linhas em vez de ocultar: o timestamp e o tipo
   de evento são probatórios (AuditLog append-only) e não podem desaparecer.
   Linha 1: timestamp + ação. Linha 2: o-quê + quem. (O nº de sequência é
   redundante com o timestamp+desempate; cede o espaço em ecrã estreito.) */
@media (max-width: 767px) {
    .feed__item {
        grid-template-columns: auto 1fr;
        row-gap: 2px;
    }
    .feed__seq { display: none; }
    .feed__time { grid-column: 1; }
    .feed__action { grid-column: 2; justify-self: end; }
    .feed__what { grid-column: 1 / -1; }
    .feed__who { grid-column: 1 / -1; }
}

@media (max-width: 767px) {
    .form-grid { grid-template-columns: 1fr; }
    /* Alvo de toque >=44px nos campos dos formulários de terreno (uso com luvas,
       exterior). Exclui checkbox/radio (alvo aplicado ao .form-check); a textarea
       já tem min-height:88px, acima do mínimo. */
    .form-field input:not([type="checkbox"]):not([type="radio"]),
    .form-field select,
    .form-field textarea { min-height: var(--min-touch); }
}

/* ============================================================
   Colunas responsivas (esconder colunas menos críticas em ecrãs estreitos).
   Marcar TH e TD com a mesma classe.
   ============================================================ */
@media (max-width: 1023px) { .col-hide-md { display: none; } }
@media (max-width: 767px)  { .col-hide-sm { display: none; } }
