/* ============================================================================
   QualityControl — direção visual "instrumento de inspeção".

   O domínio é controle de qualidade em metalurgia: precisão, medição, documentos
   autênticos. A linguagem reflete isso — superfícies de aço/grafite, hairlines de
   1px (não sombras flutuantes), MONO em todo dado técnico (marcas, revisões,
   medidas, e-mails), e dois acentos com papéis distintos:
     - azul  (--accent)  = AÇÃO (botões, links, foco)
     - âmbar (--amber)   = MEDIÇÃO / dado em destaque / atenção (eyebrows, marcas)

   Dois temas pelos MESMOS tokens. Mobile-first: o layout base é uma coluna; o
   shell de duas colunas (aside + main) entra a partir de 880px.

   REGRA: zero estilo inline no HTML. Toda aparência mora aqui, em classes
   semânticas. Faltou classe? Crie — não use style="".
   ============================================================================ */

:root,
[data-theme="dark"] {
    --bg: #0d1117;
    --bg-2: #0a0e14;        /* recuado: campos de input, poços */
    --panel: #11161f;       /* aside, header, superfície base */
    --panel-2: #161d28;
    --fg: #e6edf3;
    --muted: #8b97a7;
    --faint: #5b6675;
    --accent: #3b82f6;      /* AÇÃO */
    --accent-2: #5b9cff;
    --amber: #e1a751;       /* MEDIÇÃO / atenção (giz de solda industrial) */
    --amber-2: #f0b964;
    --danger: #f0544c;
    --danger-bg: #2a1416;
    --success: #3fb950;
    --warning: #e0a13a;
    --border: #20262f;      /* divisória SUTIL (contorno de superfície, fundo) */
    --border-2: #313b48;    /* traço ESTRUTURAL visível (campos, separadores, grades) */
    --hover: #273140;       /* estado hover/ativo: nitidamente acima de panel/elevated */
    --elevated: #1a212c;    /* popovers/menus acima do panel */
    /* header da tabela: no ESCURO o panel-2 já contrasta. */
    --thead-bg: var(--panel-2);
    --thead-fg: var(--muted);
    --row-stripe: rgba(255, 255, 255, .025); /* zebra: linha alternada (escuro) */
    /* "ilha escura": toolbar + rodapé da tabela. No ESCURO acompanham o painel. */
    --island-bg: var(--panel-2);
    --island-fg: var(--muted);
    --island-line: var(--border-2);
    --island-field: var(--bg-2);     /* fundo de input/botão na ilha */
    --radius: 8px;
    --shadow: 0 12px 32px rgba(0, 0, 0, .45);
    --glow: radial-gradient(1100px 520px at 82% -12%, #16202f 0%, transparent 62%);
    --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
}

[data-theme="light"] {
    --bg: #eceef2;
    --bg-2: #f6f7f9;
    --panel: #ffffff;
    --panel-2: #f1f3f6;
    --fg: #161b22;
    --muted: #59616d;
    --faint: #98a2b1;
    --accent: #1f6feb;
    --accent-2: #3b82f6;
    --amber: #e1a751;
    --amber-2: #d08f28;
    --danger: #cf222e;
    --danger-bg: #fff0ef;
    --success: #1a7f37;
    --warning: #9a6700;
    --border: #dde1e8;      /* divisória SUTIL */
    --border-2: #b9c2d1;    /* traço ESTRUTURAL visível (mais escuro p/ aparecer no branco) */
    --hover: #e2e7ef;       /* hover nitidamente visível sobre branco */
    --elevated: #ffffff;
    /* header da tabela no CLARO: faixa grafite escura com texto claro — o
       panel-2 claro ficava quase igual ao fundo e o header sumia. */
    --thead-bg: #323b48;
    --thead-fg: #cfd6e0;
    --row-stripe: #f3f5f8; /* zebra: cinza bem leve sobre o branco do painel */
    /* "ilha escura" no CLARO: toolbar + rodapé em grafite (como o header), com
       input/paginação em estilo escuro local p/ harmonizar com o grafite. */
    --island-bg: #2b333e;
    --island-fg: #cfd6e0;
    --island-line: #3d4757;
    --island-field: #232a33;
    --radius: 8px;
    --shadow: 0 12px 32px rgba(20, 30, 50, .14);
    --glow: radial-gradient(1100px 520px at 82% -12%, #e2e9f6 0%, transparent 62%);
    --mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
    margin: 0;
    font: 15px/1.55 system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
    background: var(--glow), var(--bg);
    color: var(--fg);
}
[hidden] { display: none !important; }
.muted { color: var(--muted); }

/* ---- tipografia ---- */
h1 { font-size: 22px; font-weight: 650; margin: 0; letter-spacing: -.015em; }
h2 { font-size: 17px; font-weight: 600; margin: 0; letter-spacing: -.01em; }
.lead { color: var(--muted); font-size: 14px; margin: 4px 0 0; max-width: 62ch; }
.mono, .data { font-family: var(--mono); font-feature-settings: "tnum" 1; }

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-2); }

/* eyebrow: rótulo técnico mono que abre uma seção/ficha. A régua hairline ocupa
   o resto da linha — leitura de "campo de formulário de inspeção". */
.eyebrow {
    display: flex; align-items: center; gap: .75rem;
    font-family: var(--mono); font-size: 11px; font-weight: 600;
    letter-spacing: .12em; text-transform: uppercase; color: var(--amber);
    margin: 0 0 1rem;
}
/* a régua é estrutural (tem que SE VER): tom do próprio âmbar do label, esmaecido
   mas presente nos dois temas — não usa --border (sutil demais, somia no branco). */
.eyebrow::after { content: ""; flex: 1; height: 1px; background: color-mix(in srgb, var(--amber) 45%, transparent); }
/* cabeçalho de seção: eyebrow (com a régua) à esquerda + ação à direita, na mesma
   linha base. A régua do eyebrow corre até o botão; o botão fica alinhado. */
.section-head { display: flex; align-items: center; gap: .9rem; margin-bottom: 1rem; }
.section-head .eyebrow { flex: 1; margin: 0; }
.section-head .btn { flex: none; }
/* link discreto no cabeçalho de seção (ex.: "mostrar revogados") */
.link-toggle { flex: none; font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.link-toggle:hover { color: var(--accent); }

/* ============================ Botões ============================ */
button, .btn {
    font: inherit; cursor: pointer; border-radius: var(--radius);
    border: 1px solid transparent; padding: 7px 14px; color: var(--fg);
    transition: filter .12s, background .12s, border-color .12s, transform .04s;
}
button:active, .btn:active { transform: translateY(1px); }
button:disabled, .btn:disabled { opacity: .45; cursor: not-allowed; }
/* botão compacto (ex.: "+ Nova área" no cabeçalho de seção) */
.btn-sm { padding: 5px 11px; font-size: 13px; }

/* ação principal em ÂMBAR (cor de destaque do sistema). Texto no MESMO matiz do
   fundo, bem escuro (cor relativa: deriva do âmbar e acompanha qualquer ajuste);
   hover escurece o próprio âmbar. (Só o .primary — demais acentos seguem azul.) */
.primary { background: var(--amber); color: hsl(from var(--amber) h s 20%); font-weight: 600; }
.primary:hover:not(:disabled) { background: hsl(from var(--amber) h s calc(l - 8%)); }
.secondary { background: transparent; border-color: var(--border-2); color: var(--fg); }
.secondary:hover { border-color: var(--accent); color: var(--accent); }
.ghost { background: transparent; border-color: transparent; color: var(--muted); }
.ghost:hover { color: var(--fg); background: var(--hover); }
.danger-ghost { background: transparent; border-color: transparent; color: var(--faint); }
.danger-ghost:hover { color: var(--danger); background: var(--danger-bg); }
.link { background: none; border: 0; color: var(--accent); padding: 0; text-decoration: underline; font: inherit; cursor: pointer; }
.link:hover { color: var(--accent-2); }

/* botão-ícone: inline-flex centrando o SVG (tamanho FIXO). NÃO impõe altura — só
   o SVG (18px) + padding define a caixa, sempre menor que a altura da linha de
   texto. Assim a célula de ações nunca dita a altura da linha (some o desalinho de
   sub-pixel). Largura mínima preserva a área de clique. */
.icon-btn { flex: none; display: inline-flex; align-items: center; justify-content: center;
    min-width: 30px; padding: 3px 6px; border: 0; border-radius: var(--radius); background: transparent; color: inherit; }
.icon-btn svg { width: 17px; height: 17px; display: block; }

/* ============================ Inputs ============================ */
/* dentro de .form-row cada .field ocupa uma fração igual (flex-basis 0) — sem
   grid md-N; o select (Tom Select) e o input nativo herdam a MESMA largura. */
.field { display: flex; flex-direction: column; gap: .3rem; }
.form-row .field { flex: 1 1 0; min-width: 220px; }
/* campo SOZINHO na linha que deve ocupar só MEIA linha (o "md-6"): metade descontado
   meio gap. No celular o min-width de 220 vence e ele volta a ocupar a linha. */
.form-row .field.field-half { flex: 0 1 calc(50% - .375rem); }
.field-label {
    font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted);
}
/* altura de campo padronizada via --field-h: input nativo e Tom Select usam a
   MESMA altura fixa, garantindo alinhamento exato (border-box). */
:root { --field-h: 40px; }
input[type="text"], input[type="password"], input[type="email"], input[type="search"], input[type="date"], input[type="number"], select {
    height: var(--field-h); padding: 0 12px; border: 1px solid var(--border-2); border-radius: var(--radius);
    background: var(--bg-2); color: var(--fg); font: inherit;
}
select { cursor: pointer; }
/* Campos NÃO editáveis (readonly = valor derivado; disabled = travado pela regra):
   fundo mais afundado, texto esmaecido e cursor de "não-editável" — diferenciam de
   relance o que o inspetor pode digitar do que o sistema calcula/bloqueia. */
input[type="text"]:read-only, input[type="number"]:read-only, input[type="date"]:read-only,
input[type="text"]:disabled, input[type="number"]:disabled, input[type="date"]:disabled, select:disabled {
    background: var(--bg-1); color: var(--muted); cursor: not-allowed; border-style: dashed;
}
/* input[type=number] SEM o spinner nativo (setinhas): o controle do browser tem
   fundo claro e não casa com o tema. Escondemos o stepper e tratamos como input de
   texto numérico (o backend valida a faixa). Onde quiser stepper, usar .stepper. */
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
/* input[type=date]: o controle nativo recebe o MESMO visual dos demais campos
   (acima). O ícone do calendário (webkit) é escuro por padrão → some no tema
   escuro; invertemos só no dark e empurramos pra direita. */
input[type="date"] { cursor: pointer; }
input[type="date"]::-webkit-calendar-picker-indicator { cursor: pointer; opacity: .65; }
input[type="date"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(1); }
/* REGRA ÚNICA DE LARGURA: todo controle dentro de .field preenche 100% do campo.
   É o que mantém inputs, selects, pills, stepper e dropzone ALINHADOS entre si —
   eles compartilham a mesma caixa (a do .field), em vez de cada um inventar a sua
   largura. Para campos lado a lado, usar .form-row (frações iguais). Sem isto, cada
   form vira um quebra-cabeça de larguras inconsistentes. */
.field > input, .field > select, .field > .ts-wrapper,
.field > .pill-group, .field > .stepper, .field > .filedrop { width: 100%; }
select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }

/* textarea: MESMO visual dos inputs (borda/raio/fundo/cor), mas altura livre
   (multi-linha) — não usa --field-h. Redimensiona só na vertical. */
textarea {
    padding: 8px 12px; border: 1px solid var(--border-2); border-radius: var(--radius);
    background: var(--bg-2); color: var(--fg); font: inherit; resize: vertical; min-height: calc(var(--field-h) * 1.8);
}
textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
textarea:read-only, textarea:disabled {
    background: var(--bg-1); color: var(--muted); cursor: not-allowed; border-style: dashed; resize: none;
}
.field > textarea { width: 100%; }

/* ---- Tom Select: tema casado com nossos tokens (combo com busca). A lib é
   vendorizada (tom-select.min.css/js); aqui sobrescrevemos o visual default
   para ficar idêntico aos nossos inputs (campo grafite, dropdown elevado,
   item ativo em âmbar). Usado onde há "escolher entre muitos" com busca. ---- */
/* sem min-width próprio: a largura vem do .field (flex). Assim os estados
   colapsado e .full do Tom Select têm a MESMA largura do input nativo. */
.ts-wrapper { width: 100%; }
/* control: prefixado com .ts-wrapper p/ VENCER os estados do default (.full
   .ts-control{background:#fff}, .disabled, etc. — todos especificidade 0,2,0). */
.ts-wrapper .ts-control {
    height: var(--field-h); min-height: var(--field-h); padding: 0 12px;
    border: 1px solid var(--border-2); border-radius: var(--radius);
    background: var(--bg-2); color: var(--fg); font: inherit; box-shadow: none;
    align-items: center;  /* centra o input/item na altura fixa */
}
/* o <input> interno não soma altura; herda a centralização do control flex.
   nowrap + min-width:0 no input impedem que um item longo + o input de busca
   passem pra 2ª linha (o que estourava a altura fixa de 40px). No single, o
   item ocupa a linha e o input de busca encolhe a ~0 até você digitar. */
.ts-wrapper .ts-control { flex-wrap: nowrap; overflow: hidden; }
.ts-wrapper .ts-control > input { height: auto; line-height: normal; min-width: 0; }
/* item selecionado (single): não empurra o input; trunca com reticências */
.ts-wrapper.single .ts-control > .item {
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* o default pinta branco em vários estados, alguns com especificidade 0,3,0
   (.ts-wrapper.single.input-active .ts-control{background:#fff}). Cobrimos todos
   com a MESMA especificidade (3 classes) p/ vencer por ordem de carga. */
.ts-wrapper.full .ts-control,
.ts-wrapper.focus.full .ts-control,
.ts-wrapper.single.input-active .ts-control,
.ts-wrapper.single.dropdown-active .ts-control { background: var(--bg-2); }
.ts-wrapper .ts-control > input { color: var(--fg); }
/* o Tom Select zera o placeholder nativo e desenha o seu via .ts-control>input;
   pinta o texto digitado em --fg (acima). O placeholder visível é o data-placeholder. */
.ts-wrapper.focus .ts-control { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.ts-wrapper .ts-control .item { color: var(--fg); }
/* combo TRAVADO: o default da lib só esmaece (opacity), o que destoava dos inputs
   disabled ao lado (fundo afundado + borda tracejada). Aqui o combo recebe o MESMO
   tratamento — de relance, campo travado é campo travado, seja select ou input. */
.ts-wrapper.disabled .ts-control {
    opacity: 1; background: var(--bg-1); color: var(--muted);
    border-style: dashed; cursor: not-allowed;
}
.ts-wrapper.disabled .ts-control .item { color: var(--muted); }
/* dropdown: superfície elevada, hairline, itens em hover/ativo destacados */
.ts-wrapper .ts-dropdown, .ts-dropdown {
    margin-top: 4px; background: var(--elevated); border: 1px solid var(--border-2);
    border-radius: 10px; border-top: 1px solid var(--border-2);
    box-shadow: var(--shadow); color: var(--fg); overflow: hidden;
}
.ts-dropdown .ts-dropdown-content { padding: 4px; }
.ts-dropdown .option { padding: .5rem .7rem; border-radius: 6px; color: var(--fg); }
.ts-dropdown .option:hover, .ts-dropdown .active { background: var(--hover); color: var(--fg); }
/* item já selecionado em âmbar (cor de destaque do sistema) */
.ts-dropdown .selected, .ts-dropdown .active.selected { color: var(--amber); }
.ts-dropdown [data-selectable] .highlight { background: color-mix(in srgb, var(--amber) 28%, transparent); border-radius: 2px; color: inherit; }
.ts-dropdown .no-results, .ts-dropdown .optgroup-header { color: var(--muted); padding: .5rem .7rem; background: transparent; }
/* dados técnicos digitados (e-mail é credencial/identificador) em mono */
input[type="email"] { font-family: var(--mono); font-size: 14px; }
input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
input::placeholder { color: var(--faint); }
/* dica abaixo de um campo (ex.: pré-visualização do código da área) */
.field-hint { font-size: 12px; color: var(--muted); }
/* dica FANTASMA: reserva a MESMA altura da .field-hint sem texto, para alinhar campos
   pareados num .form-row (align-items:flex-end) quando só um deles tem dica real. */
.field-hint-ghost { font-size: 12px; visibility: hidden; }
.field-hint-ghost::before { content: "\00a0"; }
/* LINHA COM DICA ALINHA PELO TOPO — regra GERAL, não remendo por tela. O .form-row alinha
   pela BASE (flex-end), então qualquer .field-hint real (1 ou 2 linhas, aviso condicional
   que aparece só às vezes) empurrava o input do SEU campo para cima e desnivelava a linha
   — aconteceu a cada tela nova com dica. Os rótulos têm a mesma altura, então pelo topo os
   inputs ficam alinhados e a dica pende embaixo do seu campo. A .field-hint-ghost continua
   inofensiva (vira só espaço embaixo). */
.form-row:has(.field-hint:not(.field-hint-ghost)) { align-items: flex-start; }
/* texto de instrução de uma section (parágrafo abaixo do eyebrow): quebra na coluna de
   conteúdo (620) em vez de esticar até 1040 na tela larga. */
.section-hint { font-size: 12.5px; color: var(--muted); max-width: 620px; margin: -.25rem 0 1rem; }

/* SWITCH (CLAUDE.md: checkbox → switch). Linha com texto à esquerda + trilho à
   direita; checkbox nativo escondido, o :checked move o "thumb" e pinta âmbar. */
.switch-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: .35rem 0; }
.switch-text { display: flex; flex-direction: column; gap: .15rem; min-width: 0; }

/* .switch-line — o switch como LINHA CLICÁVEL INTEIRA (lista de flags: fabricação,
   superfície). O <label> envolve rótulo e switch, então qualquer ponto da linha alterna,
   inclusive o vão entre os dois. O hover destaca a linha toda: é o que anuncia que o alvo
   é a linha, e não só o botãozinho da ponta. */
/* user-select: arrastar sobre a lista é o PINCEL (ver app.js), e sem isto o navegador
   seleciona o texto das linhas no meio do gesto — fica azul e o arrasto parece bug. */
.switch-list { user-select: none; -webkit-user-select: none; }
/* a borda é TRANSPARENTE no repouso, não ausente: aparecer uma borda no hover mudaria a
   altura da linha e a lista inteira tremeria ao passar o mouse. */
.switch-line {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .5rem .75rem; margin: 0 -.75rem; border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer; transition: background .12s, border-color .12s;
}
.switch-line:hover { background: var(--bg-2); border-color: var(--border-2); }
/* o foco do teclado tem que ser visível na LINHA: o input real é opaco/zero-size (ver
   .switch input), então sem isto navegar por Tab não mostraria onde se está. */
.switch-line:focus-within { background: var(--bg-2); border-color: var(--border-2); }
.switch-title { font-size: 14px; }
.switch-sub { font-size: 12.5px; color: var(--muted); }
.switch { position: relative; flex: none; cursor: pointer; }
.switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.switch-track {
    display: block; width: 42px; height: 24px; border-radius: 999px;
    background: var(--bg-2); border: 1px solid var(--border-2); transition: background .15s, border-color .15s;
}
.switch-track::after {
    content: ""; position: absolute; top: 3px; left: 3px; width: 18px; height: 18px;
    border-radius: 50%; background: var(--muted); transition: transform .15s, background .15s;
}
.switch input:checked + .switch-track { background: var(--amber); border-color: var(--amber); }
.switch input:checked + .switch-track::after { transform: translateX(18px); background: #fff; }
.switch input:focus-visible + .switch-track { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
/* switch SOMENTE LEITURA (disabled): sem cursor de ponteiro, track esmaecido e borda
   tracejada — sinaliza que o estado é DERIVADO/travado (ex.: resultado da amostragem),
   não um toggle que o inspetor mexe. :has() coloca o cursor no wrapper (o input é 0×0). */
.switch:has(input:disabled) { cursor: not-allowed; }
.switch input:disabled + .switch-track { opacity: .5; border-style: dashed; }

/* ============================ Login (gate) ============================ */
.auth-layout { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.gate-card {
    width: min(400px, 100%); background: var(--panel); border: 1px solid var(--border);
    border-radius: 12px; padding: 32px 28px; box-shadow: var(--shadow);
    display: flex; flex-direction: column; gap: 14px;
}
.gate-head { text-align: center; margin-bottom: 6px; }
.gate-icon { width: 48px; height: 48px; margin: 0 auto 12px; color: var(--accent); display: block; }
.gate-card h1 { text-align: center; }
.gate-card .primary { width: 100%; margin-top: 6px; padding: 10px; }

/* ============================ Shell (mobile-first) ============================ */
/* Base (mobile): uma coluna. O aside é um drawer fora da tela; o topbar mobile
   o abre. Em ≥880px o aside fixa à esquerda e o topbar some. */
.app-grid { min-height: 100vh; display: flex; flex-direction: column; }
/* Numa página de tabela, a coluna trava na altura do dispositivo (não cresce):
   o main vira flex-1 de altura definida e só a tabela rola lá dentro. */
.app-grid:has(.area-main.is-table) { height: 100dvh; min-height: 0; }

/* topbar só-mobile: voltar + hambúrguer */
.topbar {
    display: flex; align-items: center; gap: .5rem;
    height: 52px; padding: 0 .75rem;
    border-bottom: 1px solid var(--border); background: var(--panel);
    position: sticky; top: 0; z-index: 20;
}
.topbar .spacer { flex: 1; }

.area-aside {
    position: fixed; inset: 0 auto 0 0; z-index: 40;
    width: 264px; transform: translateX(-100%);
    transition: transform .22s ease;
    display: flex; flex-direction: column;
    padding: 1rem .85rem; border-right: 1px solid var(--border);
    background: var(--panel);
}
/* drawer aberto via :target (#menu) — zero JS */
.area-aside:target { transform: translateX(0); }
.scrim {
    position: fixed; inset: 0; z-index: 30; background: rgba(0, 0, 0, .5);
    opacity: 0; pointer-events: none; transition: opacity .22s;
}
.area-aside:target + .scrim { opacity: 1; pointer-events: auto; }

.area-main { flex: 1; min-width: 0; padding: 1.25rem; }
/* mobile: página de tabela vira coluna de altura definida (só a tabela rola). */
.area-main.is-table { display: flex; flex-direction: column; min-height: 0; overflow: hidden; }
.area-footer {
    display: flex; align-items: center; gap: .5rem;
    padding: .75rem 1.25rem; border-top: 1px solid var(--border);
    color: var(--faint); font-size: .78rem; font-family: var(--mono);
}

@media (min-width: 880px) {
    .topbar { display: none; }
    .app-grid {
        /* largura do aside via variável definida no <html> (ver abaixo): o rail
           só troca --aside-w lá, e a coluna do grid acompanha. */
        display: grid; height: 100vh;
        grid-template-columns: var(--aside-w, 264px) 1fr;
        grid-template-rows: 1fr auto;
        grid-template-areas: "aside main" "aside footer";
    }
    /* no desktop o aside ocupa a CÉLULA do grid (264px ou 68px no rail); zera a
       largura fixa de 264px que vem da regra base do drawer mobile. NÃO usa
       overflow:hidden (cortaria o popover do usuário no rail); os textos são
       escondidos por display:none, não por clip. */
    .area-aside { position: static; transform: none; grid-area: aside; width: auto; min-width: 0; }
    .scrim { display: none; }
    .area-main { grid-area: main; overflow-y: auto; padding: 2rem 2.25rem; }
    .area-footer { grid-area: footer; }
    /* página de tabela: o main NÃO rola; o grid já lhe dá 100vh − footer.
       (o comportamento flex-column está na regra base de .area-main.is-table) */
}

/* Laptop+ (≥1200px): o conteúdo das páginas de FICHA ganha uma largura máxima
   confortável e centraliza, aproveitando o espaço sem espremer num canto nem
   esticar texto até a borda. Páginas de tabela (.is-table) NÃO entram — elas já
   usam toda a largura/altura. */
@media (min-width: 1200px) {
    .area-main:not(.is-table) { padding-left: 3rem; padding-right: 3rem; }
    .area-main:not(.is-table) > * { max-width: 1040px; margin-left: auto; margin-right: auto; }

    /* botão de colapsar aparece quando o aside é fixo */
    .aside-collapse { display: inline-grid; }

    /* ---- ASIDE COLAPSADO (rail): faixa de ícones, libera espaço ao conteúdo ----
       a largura é trocada NO <html> (data-aside mora lá), eliminando qualquer
       questão de especificidade/descendência com o .app-grid. */
    [data-aside="rail"] { --aside-w: 68px; }
    [data-aside="rail"] .area-aside { padding: 1rem .6rem; align-items: stretch; }
    /* esconde os rótulos textuais da BARRA (nav principal, marca, usuário),
       mantendo só os ícones. Os rótulos DENTRO do dropup (.user-menu-panel)
       seguem visíveis — o painel abre para fora, com largura própria. */
    [data-aside="rail"] .brand-text,
    [data-aside="rail"] .nav .nav-text,
    [data-aside="rail"] .aside-user-meta,
    [data-aside="rail"] .user-menu-caret { display: none; }
    /* ACCORDION no rail: não há espaço para cabeçalho/chevron; achatamos os
       grupos numa faixa contínua de ícones. Os cabeçalhos de grupo e o rótulo
       "Em breve" somem; os itens dos grupos aparecem SEMPRE (aberto ou não) e
       perdem a indentação/hairline que só faz sentido no aside largo. */
    [data-aside="rail"] .nav-group-head,
    [data-aside="rail"] .nav-group-empty { display: none; }
    /* um <details> fechado esconde o conteúdo; no rail forçamos os itens a
       aparecer sempre (não há gatilho de abrir/fechar visível). */
    [data-aside="rail"] .nav-group-items {
        display: flex; margin: 0; padding: 0; border-left: 0; gap: 2px;
    }
    /* ícones centralizados nos itens estreitos da barra */
    [data-aside="rail"] .brand { justify-content: center; padding: .25rem 0 1.1rem; }
    [data-aside="rail"] .nav .nav-link,
    [data-aside="rail"] .aside-user { justify-content: center; gap: 0; padding-left: 0; padding-right: 0; }
    [data-aside="rail"] .brand-mark { display: none; }
    /* no rail o botão centraliza (a marca some); o ícone de painel não gira */
    [data-aside="rail"] .aside-collapse { margin: 0; }
    /* dropup do usuário no RAIL: o painel não cabe nos 68px → solta para a
       direita do rail (sobre o conteúdo), com largura própria, ancorado embaixo. */
    [data-aside="rail"] .user-menu-panel {
        left: calc(100% + 8px); right: auto; bottom: 0; width: 220px;
    }
}

/* marca no topo do aside */
.brand { display: flex; align-items: center; gap: .6rem; padding: .25rem .5rem 1.1rem; }
/* botão de colapsar o aside: só no aside fixo (≥880px). No drawer mobile some.
   A visibilidade vem da media ≥880px (.aside-collapse { display: inline-grid });
   aqui escondemos no mobile sem depender de ordem de cascata. */
@media (max-width: 879.98px) { .aside-collapse { display: none; } }
.aside-collapse { flex: none; margin-left: auto; color: var(--muted); }
.brand-mark { width: 28px; height: 28px; flex: none; color: var(--accent); }
.brand-text { display: flex; flex-direction: column; line-height: 1.2; }
.brand-name { font-weight: 650; letter-spacing: -.01em; }
.brand-sub { font-size: 10.5px; color: var(--faint); font-family: var(--mono); letter-spacing: .04em; }

/* navegação */
/* O nav é quem ROLA quando os itens excedem a altura do aside (são muitos: modelos-base
   + as 4 famílias internas). flex:1 + min-height:0 deixa o brand fixo no topo e o nav
   ocupar o resto; overflow-y:auto rola só ele. Sem isto os itens transbordam o aside. */
.nav {
    display: flex; flex-direction: column; gap: 2px;
    flex: 1; min-height: 0; overflow-y: auto;
    /* scrollbar discreta, some quando não há overflow */
    scrollbar-width: thin; scrollbar-color: var(--border-2) transparent;
    margin: 0 -.25rem; padding: 0 .25rem; /* respiro p/ a scrollbar não colar no texto */
}
.nav::-webkit-scrollbar { width: 6px; }
.nav::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 3px; }
.nav::-webkit-scrollbar-thumb:hover { background: var(--muted); }
.nav-link {
    display: flex; align-items: center; gap: .65rem;
    padding: .55rem .7rem; border-radius: var(--radius); color: var(--muted);
    font-size: 14px; font-weight: 500;
}
.nav-link:hover, .nav-link.active { background: var(--hover); color: var(--fg); }
.nav-link.active { box-shadow: inset 2px 0 0 var(--accent); }
.nav-icon { width: 18px; height: 18px; flex: none; }

/* ---- Grupos do aside (accordion via <details> nativo) --------------------
   O cabeçalho é o gatilho (ícone + rótulo + chevron); abre/fecha sem JS. Os
   itens ficam levemente indentados para ler como subordinados ao grupo. */
.nav-group > summary { list-style: none; cursor: pointer; }
.nav-group > summary::-webkit-details-marker { display: none; }
.nav-group-head {
    display: flex; align-items: center; gap: .65rem;
    padding: .55rem .7rem; border-radius: var(--radius);
    color: var(--muted); font-size: 14px; font-weight: 500;
}
.nav-group-head:hover { background: var(--hover); color: var(--fg); }
.nav-group-caret { width: 16px; height: 16px; flex: none; margin-left: auto; color: var(--faint); transition: transform .2s; }
.nav-group[open] > summary .nav-group-caret { transform: rotate(180deg); }
/* itens indentados; a hairline à esquerda amarra o grupo visualmente. */
.nav-group-items {
    display: flex; flex-direction: column; gap: 2px;
    margin: 2px 0 2px .95rem; padding-left: .55rem;
    border-left: 1px solid var(--border);
}
.nav-group-empty { padding: .5rem .7rem; color: var(--faint); font-size: 13px; font-style: italic; }

/* ---- disclosure de FORMULÁRIO ("ver detalhes"/ajuda recolhível) ----
   Some do aside: aqui o gatilho precisa PARECER clicável (chevron próprio, que gira
   ao abrir) e ter respiro em volta, senão lê como um título solto colado no campo
   seguinte. NÃO reusar .nav-group para isso — lá o marcador nativo é escondido e o
   chevron vem de um SVG do menu. */
/* --panel (não --panel-2): no tema CLARO o panel-2 é quase o fundo da página e o
   bloco inteiro somia. O texto é --fg (não --muted): é um gatilho, precisa ler. */
.disclosure { border: 1px solid var(--border); border-radius: var(--radius); background: var(--panel); }
.disclosure > summary {
    list-style: none; cursor: pointer;
    display: flex; align-items: center; gap: .5rem;
    padding: .6rem .8rem; color: var(--fg);
    font-size: 13px; font-weight: 500;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary:hover { background: var(--hover); border-radius: var(--radius); }
/* chevron em CSS puro (sem SVG): triângulo que gira 90° quando aberto. */
.disclosure > summary::before {
    content: ""; flex: none; width: 0; height: 0;
    border-left: 5px solid currentColor;
    border-top: 4px solid transparent; border-bottom: 4px solid transparent;
    transition: transform .18s; transform-origin: 25% 50%;
}
.disclosure[open] > summary::before { transform: rotate(90deg); }
.disclosure > :not(summary) { padding: 0 .8rem; }
.disclosure > :last-child { padding-bottom: .7rem; }

/* ============================ Menu do usuário (dropup) ============================ */
.user-menu { margin-top: auto; }
.user-menu > summary { list-style: none; cursor: pointer; }
.user-menu > summary::-webkit-details-marker { display: none; }
.aside-user {
    display: flex; align-items: center; gap: .6rem;
    padding: .5rem; border-radius: var(--radius);
    border-top: 1px solid var(--border); padding-top: .8rem; margin-top: .4rem;
    transition: background .12s;
}
.user-menu .aside-user { border-top: 1px solid var(--border); }
.user-menu[open] .aside-user, .user-menu .aside-user:hover { background: var(--hover); }
.aside-user-meta { display: flex; flex-direction: column; line-height: 1.25; min-width: 0; }
.aside-user-name { font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.aside-user-role { font-size: 10.5px; color: var(--faint); font-family: var(--mono); letter-spacing: .05em; text-transform: uppercase; }
.user-menu-caret { margin-left: auto; color: var(--faint); transition: transform .2s; }
.user-menu[open] .user-menu-caret { transform: rotate(180deg); }

/* o <details> é a âncora; o painel FLUTUA acima do card com respiro (gap),
   sombra e cantos próprios — popover destacado, não colado no gatilho. */
.user-menu { position: relative; }
.user-menu-panel {
    position: absolute; left: 0; right: 0; bottom: calc(100% + 8px);
    background: var(--elevated); border: 1px solid var(--border-2);
    border-radius: 10px; box-shadow: var(--shadow); padding: 5px;
    display: flex; flex-direction: column; gap: 2px;
    opacity: 0; transform: translateY(6px); pointer-events: none;
    transition: opacity .16s ease, transform .16s ease;
}
.user-menu[open] .user-menu-panel { opacity: 1; transform: none; pointer-events: auto; }
.user-menu-panel .nav-link { width: 100%; background: none; border: 0; font: inherit; text-align: left; cursor: pointer; color: var(--fg); }
.user-menu-panel .nav-link:hover { background: var(--hover); color: var(--fg); }
.user-menu-panel .nav-link:hover svg { color: var(--fg); }
.user-menu-panel .nav-link svg { color: var(--muted); }
.user-menu-sep { height: 1px; background: var(--border-2); margin: 4px 2px; }

/* toggle de tema = item de menu inteiro. Mostra o ícone do tema ATUAL (lua no
   claro, sol no escuro) — clicar leva ao oposto. */
[data-theme="dark"] .icon-moon { display: none; }
[data-theme="light"] .icon-sun { display: none; }

/* avatar (inicial OU foto) */
.avatar { width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid; place-items: center; font-weight: 700; font-size: 13px; color: #fff; background: linear-gradient(180deg, var(--accent-2), var(--accent)); object-fit: cover; }
.avatar-img { width: 88px; height: 88px; flex: none; border-radius: 50%; object-fit: cover; border: 1px solid var(--border-2); }
.avatar.avatar-img { width: 34px; height: 34px; border: 0; }

/* seletor de tema (no menu do usuário) */

/* ============================ Página / fichas ============================ */
.page-head { margin-bottom: 1.75rem; }
/* cabeçalho com ações à direita; empilha no mobile */
.page-head-row { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; flex-wrap: wrap; }
.head-actions { display: flex; gap: .5rem; flex: none; }
a.btn { display: inline-flex; align-items: center; text-decoration: none; }
/* separação pelo eyebrow (régua no topo) + respiro; sem border-bottom (seria
   linha dobrada com a régua da próxima seção). */
.section { padding: 0 0 2rem; max-width: 620px; }
.section + .section { padding-top: .5rem; }
/* seção que contém TABELA/grade: sem o cap de leitura (620px espremia e cortava
   colunas). Usa a largura do container. */
.section-wide { max-width: none; }

/* foto do perfil: clicável (abre troca) + lixeira para remover */
.photo-field { display: flex; align-items: center; gap: 1.25rem; }
.photo-pick { position: relative; border: 0; background: none; padding: 0; cursor: pointer; border-radius: 50%; }
.photo-pick .avatar-img { display: block; }
.photo-pick::after {
    content: "Trocar"; position: absolute; inset: 0; border-radius: 50%;
    display: grid; place-items: center; font-size: 11px; font-weight: 600; color: #fff;
    background: rgba(0, 0, 0, .55); opacity: 0; transition: opacity .15s;
}
.photo-pick:hover::after, .photo-pick:focus-visible::after { opacity: 1; }
.photo-meta { display: flex; flex-direction: column; gap: .5rem; }
.photo-hint { font-size: 12.5px; color: var(--muted); }

.form-stack { display: flex; flex-direction: column; gap: 1rem; max-width: 420px; }
/* A RÉGUA DO EYEBROW VAI SEMPRE ATÉ O FIM, esteja ele solto na .section ou dentro de um
   form. Não adianta alargar o eyebrow: ele é filho do .form-stack (420px) e um filho não
   ultrapassa o pai. Então quem solta a largura é o PRÓPRIO FORM, e o cap de 420px desce
   para os campos — que é onde ele sempre pertenceu (o .form-row já faz assim, com 620).
   Sem isto, o eyebrow que nasce fora do form atravessa e o de dentro para no meio: duas
   larguras de régua na mesma tela. */
.form-stack:has(> .eyebrow) { max-width: none; }
/* o cap volta em TUDO que não é a régua: campos, linhas, textos de apoio e o rodapé de
   botões. O .form-row tem 620px próprio, mas aqui ele acompanha os demais (o form é de
   coluna estreita — a régua é a única coisa que atravessa). */
.form-stack:has(> .eyebrow) > .field,
.form-stack:has(> .eyebrow) > .form-row,
.form-stack:has(> .eyebrow) > .alert,
.form-stack:has(> .eyebrow) > p:not(.eyebrow),
.form-stack:has(> .eyebrow) > .form-foot,
.form-stack:has(> .eyebrow) > .field-group > .field,
.form-stack:has(> .eyebrow) > .field-group > .form-row { max-width: 420px; }
/* form em linha (campo cresce + botão ao lado); empilha no mobile */
.form-row { display: flex; align-items: flex-end; gap: .75rem; flex-wrap: wrap; max-width: 620px; }

/* CONTAINER de formulário (estilo framework): coluna única centralizada com largura
   máxima, TODO o conteúdo do form dentro dela (eyebrow, inputs, switches, texto). Ao
   envolver o conteúdo, ele define o limite de largura de uma vez — em vez de cada
   elemento inventar o seu. Genérica: reutilizável em qualquer form (POC no diluente).
   A regra abaixo (área principal) precisa da especificidade para vencer o override
   .area-main:not(.is-table) > * { max-width:1040 } em telas largas. */
.area-main:not(.is-table) > .form-container { max-width: 680px; margin-left: auto; margin-right: auto; }
.form-container { display: flex; flex-direction: column; gap: 1.75rem; }
/* dentro do container, os blocos de largura própria cedem ao container (não estouram
   nem ficam mais estreitos que ele): inputs pareados e switches ocupam a coluna toda. */
.form-container .form-row,
.form-container .amostra-panel,
.form-container .form-stack { max-width: none; }
/* o gap do container cuida do respiro entre seções: zera o padding-bottom próprio da
   .section (senão soma e fica espaçado demais). */
.form-container .section { padding-bottom: 0; max-width: none; }
/* quando o <form> ENVOLVE as seções (upload precisa do form de verdade em volta, ex.:
   recebimento de metal de base), ele é o único filho direto do container — o gap do
   container não alcançaria as .section dentro dele, e o form ficaria sem respiro entre
   as linhas. Repassa o mesmo empilhamento ao form. (No diluente/tinta as .section são
   filhas diretas e o <form> fica de lado; ambos os arranjos ficam iguais na tela.) */
.form-container > form { display: flex; flex-direction: column; gap: 1.75rem; }
/* dentro de uma .section, os campos empilhados precisam do respiro do .form-stack — o
   gap acima separa SEÇÕES, não os campos irmãos de uma mesma seção. */
.form-container .section > .form-row + .form-row,
.form-container .section > .field + .field,
.form-container .section > .form-row + .field,
.form-container .section > .field + .form-row,
.form-container .section > .disclosure + .field,
.form-container .section > .field + .disclosure,
.form-container .section > .disclosure + .form-row { margin-top: 1rem; }
/* o disclosure de ajuda vem depois de um .field-hint solto: separa do texto acima. */
.section > .field-hint + .disclosure { margin-top: .75rem; }
/* painel de amostragem (recebimento de diluente): empilha campos/rows/switches com o
   MESMO respiro do .form-stack, mas sem o max-width de 420px (as rows são de 620px).
   Os subtítulos das sub-amostras ganham margem própria acima. */
.amostra-panel { display: flex; flex-direction: column; gap: 1rem; max-width: 620px; }
.amostra-panel .eyebrow { margin-top: .5rem; }
/* bloco de campos que o htmx troca inteiro (ex.: #origens da suspensão): é UM filho do
   .form-stack, então o gap do pai não alcança os campos DE DENTRO — eles ficariam
   colados. Repete o empilhamento do .form-stack para o grupo respirar igual aos campos
   soltos, e o eyebrow do grupo ganha o mesmo respiro acima dos demais. */
.field-group { display: flex; flex-direction: column; gap: 1rem; max-width: none; }
.field-group > .eyebrow { margin: .5rem 0 0; }
.field-grow { flex: 1; min-width: 220px; }

/* token recém-criado: banner de atenção com o valor + copiar (mostrado 1x) */
.token-new {
    border: 1px solid color-mix(in srgb, var(--amber) 45%, transparent);
    background: color-mix(in srgb, var(--amber) 10%, transparent);
    border-radius: 10px; padding: 1rem 1.1rem; margin-bottom: 1.75rem;
}
.token-new-head { display: flex; align-items: center; gap: .5rem; margin: 0 0 .7rem; font-size: 13.5px; }
.token-new-head svg { width: 18px; height: 18px; flex: none; color: var(--amber); }
.token-copy { display: flex; align-items: stretch; gap: .5rem; }
.token-value {
    flex: 1; min-width: 0; font-family: var(--mono); font-size: 13px; color: var(--fg);
    background: var(--bg-2); border: 1px solid var(--border-2); border-radius: var(--radius);
    padding: .55rem .7rem; overflow-x: auto; white-space: nowrap;
}
/* célula de ações: os icon-btn são display:grid (block), então precisam de um
   contêiner flex para ficarem lado a lado — sem isso empilham e incham a linha. */
/* célula de ações: o td continua sendo table-cell NORMAL (mesma resolução de
   altura/borda das células de texto — sem flex no td, que causava diferença de
   sub-pixel na linha). Os ícones vão num .actions flex DENTRO. */
.td-action { white-space: nowrap; }
.actions { display: flex; align-items: center; gap: .25rem; }
/* ações centradas numa .td-center (ex.: "não gerado" + Regenerar no modal de documentos) */
.actions-center { justify-content: center; gap: .5rem; }
/* coluna centrada para um único ícone/valor (ex.: Anexo na lista de procedimentos) */
.th-center, .td-center { text-align: center; white-space: nowrap; }
/* input que mostra o texto em MAIÚSCULAS enquanto digita (ex.: prefixo do
   equipamento). É só UX — o backend normaliza com ToUpper. */
input.upper { text-transform: uppercase; }
/* alerta inline (erro de validação no topo do form) */
.alert { margin: 0; padding: .7rem .9rem; border-radius: var(--radius); font-size: 13.5px; }
.alert-danger { background: var(--danger-bg); border: 1px solid color-mix(in srgb, var(--danger) 45%, transparent); color: var(--danger); }
/* o .alert nasce sem margem (fica solto no topo do form, onde o gap do .form-stack
   já resolve). Quando ele ENCABEÇA um conteúdo irmão — a lista de problemas da
   importação, p.ex. —, o gap não existe e os dois se colam: damos o respiro aqui. */
.alert + * { margin-top: .9rem; }
/* painel FIXO "o que falta para finalizar" (relatórios de recebimento em rascunho). Âmbar
   de atenção, alinhado ao toast-warning; permanece na tela (o toast some em 5s). Só
   margin-BOTTOM: o shorthand `margin: 0 0 1rem` zerava o topo e, vindo depois, vencia o
   `.alert + *` (mesma especificidade) — o erro do save colava no painel. */
.pending-panel { margin-bottom: 1rem; padding: .7rem .9rem; border-radius: var(--radius);
    background: color-mix(in srgb, var(--warning) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--warning) 45%, transparent); font-size: 13.5px; }
.pending-head { display: flex; align-items: center; gap: .45rem; font-weight: 600;
    color: var(--warning); }
.pending-icon { width: 18px; height: 18px; flex: none; }
.pending-list { margin: .35rem 0 0; padding-left: 1.9rem; }
.pending-list li { margin: .1rem 0; }
/* rodapé do form: ação primária à esquerda, Cancelar à direita (na borda), com
   gap — mesma regra dos modais (o gesto fácil sempre cancela). */
.form-foot { display: flex; justify-content: flex-end; gap: .6rem; padding-top: .25rem; }

/* dashboard de início: fichas em grade */
.tiles { display: grid; gap: 1px; background: var(--border-2); border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden; }
@media (min-width: 560px) { .tiles { grid-template-columns: repeat(3, 1fr); } }
.tile { background: var(--panel); padding: 1.25rem; }
.tile h2 { margin-bottom: .35rem; }
.tile p { margin: 0; color: var(--muted); font-size: 13.5px; }

/* ============================ Navegação de páginas ============================ */
.back-link { font-family: var(--mono); font-size: 12px; color: var(--muted); display: inline-block; margin-bottom: .6rem; }
.back-link:hover { color: var(--accent); }
.page-head h1 .data { color: var(--amber); }

/* ============================ Tabela de dados ============================ */
/* PAINEL DE TABELA (reutilizável em projetos, áreas, assemblies, …).
   Caixa única emoldurada, em grid de 3 linhas: toolbar (busca/filtros) /
   tabela (1fr, ÚNICO que rola) / footer (faixa "1–20 de N" + paginação).
   A página inteira trava na altura do dispositivo; só a área da tabela scrolla,
   com thead grudado (sticky) e o rodapé sempre visível. */
.table-panel {
    flex: 1; min-height: 0;
    display: grid; grid-template-rows: auto 1fr auto;
    border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden;
    background: var(--panel);
}

/* Toolbar SLIM: só ícones (lupa/filtro) + chips do que está ativo. Abrem modais
   (busca/filtro). Faz parte da "ilha escura" (grafite nos dois temas). */
.table-toolbar {
    display: flex; align-items: center; gap: .5rem;
    padding: .45rem .6rem; border-bottom: 1px solid var(--island-line);
    background: var(--island-bg);
}
.toolbar-spacer { flex: 1; }
/* botão-ícone da toolbar (sobre o grafite). padding/line-height zerados p/ o SVG
   ficar perfeitamente centrado (sem o jogo das margens do <button> nativo). */
.tool-btn {
    width: 32px; height: 32px; flex: none; padding: 0; margin: 0; line-height: 0;
    display: inline-grid; place-items: center;
    border: 1px solid transparent; border-radius: var(--radius);
    background: none; color: var(--island-fg); cursor: pointer;
}
.tool-btn svg { display: block; width: 16px; height: 16px; }
.tool-btn:hover { background: var(--island-field); border-color: var(--island-line); }
.tool-btn.on { color: var(--amber); }  /* critério ativo nesta ferramenta */
/* o botão de busca traz o badge de atalho: vira auto-width com ícone + kbd */
.tool-search {
    width: auto; gap: .5rem; padding: 0 .5rem 0 .55rem;
    grid-auto-flow: column; line-height: normal;
    border-color: var(--island-line); background: var(--island-field);
}
.tool-search:hover { border-color: color-mix(in srgb, var(--accent) 55%, var(--island-line)); }
.kbd {
    display: inline-flex; align-items: center; gap: 1px; font-family: var(--mono);
    font-size: 10.5px; color: var(--island-fg); opacity: .7;
    padding: 1px 5px; border-radius: 4px; border: 1px solid var(--island-line);
    background: color-mix(in srgb, var(--island-fg) 8%, transparent);
}
@media (max-width: 560px) { .kbd { display: none; } }  /* sem teclado físico: esconde */
/* chip de critério ativo (termo de busca / valor de filtro): rótulo + ✕ */
.chip {
    display: inline-flex; align-items: center; gap: .25rem; height: 26px; padding: 0 .25rem 0 .6rem;
    border: 1px solid var(--island-line); border-radius: 999px;
    background: var(--island-field); color: var(--island-fg); font-size: 12.5px;
}
.chip-label { font-family: var(--mono); max-width: 240px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.chip-x { display: inline-grid; place-items: center; width: 18px; height: 18px; border-radius: 50%; color: var(--island-fg); opacity: .65; cursor: pointer; }
.chip-x:hover { opacity: 1; color: var(--danger); background: color-mix(in srgb, var(--danger) 18%, transparent); }
.chip-x svg { width: 11px; height: 11px; }
/* a lista é trocada inteira pelo htmx; o wrapper preenche o painel */
.lista-wrap { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* linha única de "sem resultados" dentro da tabela */
.table-empty { text-align: center; padding: 2rem 1rem; }

/* campo de busca DENTRO do modal (tema normal, não a ilha escura) */
.field-search { position: relative; display: block; margin-top: .9rem; }
.field-search svg { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); width: 16px; height: 16px; color: var(--faint); pointer-events: none; }
.field-search input { width: 100%; padding-left: 34px; }

/* Área que rola: o thead vira sticky para o cabeçalho não sumir no scroll. */
.table-scroll { overflow: auto; min-height: 0; }
.table-scroll thead th { position: sticky; top: 0; z-index: 1; }

/* Footer da tabela: faixa de contagem à esquerda, paginação à direita.
   Faz parte da "ilha escura" (grafite nos dois temas). */
.table-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 1rem;
    padding: .5rem .75rem; border-top: 1px solid var(--island-line); background: var(--island-bg);
}
.table-foot .range { font-family: var(--mono); font-size: 12px; color: var(--island-fg); opacity: .85; white-space: nowrap; }
.table-foot .pager { margin: 0; }
/* paginação sobre a ilha: botões em estilo escuro local (não o claro padrão) */
.table-foot .pager-btn {
    background: var(--island-field); border-color: var(--island-line); color: var(--island-fg);
}
.table-foot .pager-btn:hover { border-color: var(--accent); color: var(--accent-2); }
.table-foot .pager-btn.current { background: var(--accent); border-color: var(--accent); color: #fff; }
.table-foot .pager-btn.disabled { color: var(--island-fg); opacity: .4; border-color: var(--island-line); }
.table-foot .pager-gap { color: var(--island-fg); opacity: .55; }

/* Tabela como leitura de instrumento: header mono, hairlines, dados em mono.
   No mobile, rola horizontalmente dentro do scroll (quem rola é a tabela). */
.table-wrap { border: 1px solid var(--border-2); border-radius: 10px; overflow: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.data-table th {
    text-align: left; font-family: var(--mono); font-size: 10.5px; font-weight: 600;
    letter-spacing: .08em; text-transform: uppercase; color: var(--thead-fg);
    padding: 0; background: var(--thead-bg); border-bottom: 1px solid var(--border-2);
    white-space: nowrap;
}
/* th sem ordenação (texto puro, ex.: nos modais de histórico) não tem .th-sort para
   carregar o padding — sem isto os rótulos se colam ("POSIÇÕESDIÂMETRO"). Mas quando o
   próprio conteúdo já traz o padding (.th-static tem o seu), NÃO dobramos: senão o header
   fica com padding duplo e cresce demais (visível ao misturar colunas ordenáveis e
   estáticas na mesma tabela). */
.data-table th:not(:has(.th-sort)):not(:has(.th-static)) { padding: .7rem 1rem; }
/* Célula de header: link de ordenação + "x" irmão, AGRUPADOS à esquerda (o "x"
   cola ao número/seta da SUA coluna — não é empurrado para a borda, senão pareceria
   o "x" da próxima coluna). hover leve sobre o thead-bg (vale em grafite e claro). */
.th-cell { display: flex; align-items: center; }
/* header de coluna de ENSAIO (col-test): funil + rótulo CENTRADOS na coluna
   estreita. O funil fica colado ao rótulo, sem padding lateral do th-sort. */
.th-cell-center { justify-content: center; gap: .15rem; padding: .7rem .25rem; }
.th-cell-center .th-filter { width: 18px; height: 18px; }
.th-cell-center .th-filter svg { width: 11px; height: 11px; }
.th-cell-center .th-label { font-size: inherit; }
.th-sort { display: flex; align-items: center; gap: .35rem; padding: .7rem 1rem; color: inherit; min-width: 0; }
/* quando há "x" ao lado, encurta o respiro à direita do link p/ o "x" colar */
.th-hasx .th-sort { padding-right: .4rem; }
/* funil de filtro NA coluna (antes do rótulo). Abre o modal central da coluna.
   Colado ao rótulo: o th-sort à direita perde o padding-left p/ não dobrar. */
.th-filter {
    display: inline-grid; place-items: center; flex: none; padding: 0; margin: 0; line-height: 0;
    width: 22px; height: 22px; border: 0; border-radius: 4px; background: none;
    color: var(--thead-fg); opacity: .55; cursor: pointer;
}
.th-filter svg { display: block; width: 12px; height: 12px; }
.th-filter:hover { opacity: 1; color: #fff; background: color-mix(in srgb, var(--thead-fg) 12%, transparent); }
.th-filter.on { opacity: 1; color: var(--amber); }  /* filtro aplicado nesta coluna */
/* com funil à esquerda, o link de sort não precisa do padding-left cheio */
.th-filter + .th-sort { padding-left: .35rem; }
/* hover clareia o texto: o header é sempre escuro (grafite no claro, panel-2 no
   escuro), então clareia para quase-branco — NÃO para --fg (que é preto no tema
   claro e sumiria no grafite). */
.th-sort:hover { color: #fff; background: color-mix(in srgb, var(--thead-fg) 12%, transparent); }
.th-sort.active { color: var(--amber); }
.th-sort.active:hover { color: var(--amber-2); }
.th-caret { width: 13px; height: 13px; flex: none; }
/* número de prioridade (multi-coluna): pílula âmbar pequena após a seta */
.th-prio {
    font-family: var(--mono); font-size: 9px; font-weight: 700; line-height: 1;
    color: var(--amber); background: color-mix(in srgb, var(--amber) 18%, transparent);
    border-radius: 4px; padding: 2px 3px; min-width: 13px; text-align: center; flex: none;
}
/* "x" remover da ordenação: pequeno e colado à coluna (sem borda/separador),
   como no protótipo. Sempre visível (touch). */
.th-x {
    display: inline-grid; place-items: center; flex: none;
    width: 22px; height: 22px; margin-right: .85rem;
    border-radius: 4px; color: var(--faint);
}
.th-x:hover { color: var(--danger); background: color-mix(in srgb, var(--danger) 16%, transparent); }
.th-x svg { display: block; width: 12px; height: 12px; }
/* nowrap: a célula nunca quebra → todas as linhas com a MESMA altura (ritmo
   constante, mobile-first). Texto longo alarga a coluna e a .table-scroll rola
   horizontal (mesmo gesto do header sticky). Sem reticências: nada de texto
   cortado, o valor fica sempre legível rolando. */
.data-table td { padding: .7rem 1rem; border-bottom: 1px solid var(--border); white-space: nowrap; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table .data { font-family: var(--mono); }
.row-link { cursor: pointer; transition: background .1s; }
.row-link:hover { background: var(--hover); }

/* zebra striping (opt-in: <table class="data-table striped">). Linhas pares
   ganham um tom alternado, ajudando a varredura na horizontal (a tabela rola
   lateral no mobile). O hover continua vencendo a faixa. Tokens por tema. */
.data-table.striped tbody tr:nth-child(even) { background: var(--row-stripe); }
.data-table.striped tbody tr:hover { background: var(--hover); }

/* tag/chip (ex.: "com pintura") */
.tag { font-family: var(--mono); font-size: 11px; padding: 2px 8px; border-radius: 999px; border: 1px solid var(--border-2); color: var(--muted); }

/* chanfro: desenho + nome, sempre juntos (o desenho é o que distingue chanfros
   homônimos). inline-flex: vive dentro de <td> e de <label>. */
.bevel { display: inline-flex; align-items: center; gap: 8px; }
.bevel svg { display: block; width: 24px; height: 24px; flex: none; color: var(--accent); }
.bevel-text { min-width: 0; }
/* no dropdown, o Tom Select funde a div do render no .option: sem o flex aqui, o
   inline-flex acima escorre as opções lado a lado. */
.ts-dropdown .option.bevel { display: flex; }
.ts-dropdown { scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }
/* separador dos grupos (Topo/Ângulo) no filtro de chanfro */
.bevel-group { margin: 14px 0 8px; }
.bevel-group:first-of-type { margin-top: 6px; }
.tag-amber { color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, transparent); background: color-mix(in srgb, var(--amber) 10%, transparent); }
.tag-ok { color: var(--success); border-color: color-mix(in srgb, var(--success) 45%, transparent); background: color-mix(in srgb, var(--success) 10%, transparent); }
.tag-danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, transparent); background: color-mix(in srgb, var(--danger) 10%, transparent); }
/* Linha de tags numa célula (ex.: usos do procedimento): UMA linha, sem quebrar.
   Quebrar (o que fazia antes) engorda a <tr>: um procedimento com 6 usos virava uma
   linha 4× mais alta que as vizinhas e a tabela perdia o ritmo de leitura. Sem quebra,
   a coluna cresce em LARGURA e o scroll horizontal do .table-wrap (que já existe)
   absorve — a altura da linha não depende mais de quantos usos o registro tem. */
.tag-row { display: inline-flex; flex-wrap: nowrap; gap: 4px; }
/* grade de checkboxes (.ck) dos usos do procedimento: colunas responsivas */
.check-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: .5rem .9rem; margin-top: .4rem; }
.check-item { display: flex; align-items: center; gap: .55rem; cursor: pointer; font-size: 13.5px; }
.req { color: var(--danger); }

/* linha de registro inativo: esmaecida, mas ainda legível e clicável. As tags de
   status (Ativo/Inativo) ficam em opacidade plena para o estado seguir nítido,
   qualquer que seja a coluna onde a tabela põe o status. */
.row-inactive td { opacity: .5; }
.row-inactive td .tag { opacity: 1; }

/* ---- CJE (assemblies/sequenciais) ----
   col-test: colunas estreitas dos ensaios (VS/LP/PM/US/PT, As Built), centradas.
   test-flag: indicador read-only — check âmbar quando tem, traço esmaecido quando
   não (é exibição, não entrada; a edição mora na tela de sequenciais). */
.col-test { text-align: center; width: 48px; white-space: nowrap; }
/* contagem EDITÁVEL numa célula (ex.: juntas do sequencial → modal). Chip com
   contorno âmbar + lápis: tem cara de "clique aqui", não de dado solto. */
.count-chip { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 8px 0 10px;
    border: 1px solid color-mix(in srgb, var(--amber) 55%, transparent); border-radius: 999px;
    background: color-mix(in srgb, var(--amber) 10%, transparent); color: var(--amber);
    font: inherit; cursor: pointer; transition: background .12s, border-color .12s; }
.count-chip svg { width: 13px; height: 13px; flex: none; }
.count-chip:hover, .count-chip:focus-visible { background: color-mix(in srgb, var(--amber) 20%, transparent);
    border-color: var(--amber); outline: none; }
.col-test .th-static { text-align: center; }
.test-flag { width: 16px; height: 16px; display: inline-block; vertical-align: middle; }
.test-flag.on { color: var(--amber); }
.test-flag.off { color: var(--faint); font-family: var(--mono); }
/* botão-ícone desabilitado (ação indisponível, ex.: sem desenho) */
.icon-btn.is-disabled { opacity: .35; cursor: default; pointer-events: none; }
/* combobox de troca de área no cabeçalho: largura confortável, não estica */
.switch-area .field { min-width: 240px; }
/* leitor de tela: rótulo acessível invisível visualmente */
.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* coluna de seleção (sequenciais): estreita, centrada */
/* coluna de seleção: th tem padding:0 (o padding mora no .th-static), td tem
   padding 1rem — isso desalinhava o checkbox do header em relação aos das linhas.
   Padding simétrico em AMBOS centra os checkboxes na mesma vertical. */
/* coluna de seleção: padding lateral SIMÉTRICO e text-align:center em ambos (th e
   td) → o checkbox (inline-block) centra no MESMO eixo. O bug era o padding
   assimétrico herdado (td tinha 1rem à esquerda, th tinha 0). */
.col-check { width: 44px; text-align: center; }
td.col-check, th.col-check { padding: .55rem 0.75rem !important; }
.col-check .ck { vertical-align: middle; }

/* Checkbox customizado (.ck): caixa quadrada com hairline, check âmbar quando
   marcado, traço quando indeterminado (selecionar-todos parcial). Casa com a
   direção visual do site (nada de checkbox cinza padrão do browser). */
.ck {
    appearance: none; -webkit-appearance: none; margin: 0;
    width: 18px; height: 18px; border: 1px solid var(--border-2); border-radius: 5px;
    background: var(--bg-2); cursor: pointer; vertical-align: middle; position: relative;
    display: inline-block; transition: border-color .12s, background .12s;
}
.ck:hover { border-color: var(--amber); }
.ck:checked, .ck:indeterminate { background: var(--amber); border-color: var(--amber); }
.ck:checked::after {
    content: ""; position: absolute; left: 5px; top: 1.5px; width: 5px; height: 9px;
    border: solid #1a1206; border-width: 0 2px 2px 0; transform: rotate(42deg);
}
.ck:indeterminate::after {
    content: ""; position: absolute; left: 3px; right: 3px; top: 7.5px; height: 2px;
    background: #1a1206; border-radius: 1px;
}
.ck:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber) 30%, transparent); }
.ck:disabled { opacity: .35; cursor: default; }

/* Dropzone de arquivo (.filedrop): substitui o input[type=file] cru. O input fica
   transparente sobre a área; o rótulo mostra ícone + texto e, via JS, o nome do
   arquivo escolhido. */
.filedrop {
    position: relative; display: flex; align-items: center; gap: .7rem;
    padding: .9rem 1rem; border: 1px dashed var(--border-2); border-radius: var(--radius);
    background: var(--bg-2); color: var(--muted); cursor: pointer; transition: border-color .12s, color .12s;
}
.filedrop:hover, .filedrop.dragover { border-color: var(--amber); color: var(--fg); }
.filedrop input[type="file"] { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.filedrop .fd-icon { flex: none; width: 22px; height: 22px; color: var(--amber); }
.filedrop .fd-icon svg { width: 100%; height: 100%; }
.filedrop .fd-text { font-size: 13.5px; min-width: 0; }
.filedrop .fd-name { color: var(--fg); font-family: var(--mono); font-size: 12.5px; word-break: break-all; }
.filedrop.has-file { border-style: solid; color: var(--fg); }

/* Stepper de número customizado (.stepper): botões −/+ próprios; esconde as setas
   nativas do browser. O input fica centrado entre os dois botões, em mono. */
.stepper { display: flex; align-items: stretch; height: var(--field-h);
    border: 1px solid var(--border-2); border-radius: var(--radius); background: var(--bg-2); overflow: hidden; }
.stepper input[type="number"] {
    -moz-appearance: textfield; appearance: textfield;
    flex: 1; min-width: 0; height: 100%; border: 0; background: transparent; color: var(--fg);
    text-align: center; font-family: var(--mono); font-size: 14px; padding: 0;
}
.stepper input[type="number"]::-webkit-outer-spin-button,
.stepper input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stepper input[type="number"]:focus { outline: none; }
.stepper:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
.stepper button {
    flex: none; width: 36px; border: 0; background: transparent; color: var(--muted);
    font-size: 16px; line-height: 1; cursor: pointer; transition: color .12s, background .12s;
}
.stepper button:hover:not(:disabled) { color: var(--amber); background: color-mix(in srgb, var(--amber) 10%, transparent); }
.stepper button:disabled { opacity: .3; cursor: default; }
.stepper .step-down { border-right: 1px solid var(--border-2); }
.stepper .step-up { border-left: 1px solid var(--border-2); }
/* barra de lote (sequenciais): atribuição/remoção de ensaios em massa. As ações
   coletam os seqs marcados via htmx (hx-include="#batch-form"). O toggle "Remover"
   alterna entre o grupo de adicionar e o de remover (revelados por CSS). */
.batch-bar { display: flex; align-items: center; gap: .75rem; flex-wrap: wrap; padding: .55rem 1rem; }
.batch-count { font-family: var(--mono); font-size: 11px; letter-spacing: .06em; text-transform: uppercase; color: var(--muted); }
.batch-count .batch-n { color: var(--fg); }
.batch-switch { display: inline-flex; align-items: center; gap: .5rem; margin-right: .25rem; }
.batch-switch .switch-label { font-size: 12px; color: var(--muted); }
.batch-tests { display: inline-flex; gap: .35rem; }
.batch-test {
    font-family: var(--mono); font-size: 12px; padding: 4px 10px; border-radius: var(--radius);
    border: 1px solid var(--border-2); color: var(--fg); background: var(--bg-2); cursor: pointer;
}
.batch-test:hover:not(:disabled) { border-color: var(--amber); }
.batch-test:disabled { opacity: .4; cursor: default; }
.batch-test.danger { color: var(--danger); }
.batch-test.danger:hover:not(:disabled) { border-color: var(--danger); }
/* o toggle "Remover ensaio" revela o grupo certo: por padrão mostra ADICIONAR;
   marcado, mostra REMOVER. Usa :has() no contêiner da barra. */
.batch-remove { display: none; }
.batch-bar:has(#batch-remove-toggle:checked) .batch-add { display: none; }
.batch-bar:has(#batch-remove-toggle:checked) .batch-remove { display: inline-flex; }

/* pill DESLIZANTE (segmented control): escolha curta e exclusiva entre 2–4 opções
   lado a lado (ex.: Chapa|Tubo). Radios reais (form nativo serializa), escondidos;
   o "polegar" âmbar é UM elemento que desliza até a opção marcada. --n (nº de
   opções) vem inline no template; a posição sai do :has() — sem JS. */
.seg { position: relative; display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
    height: var(--field-h); padding: 3px; border: 1px solid var(--border-2);
    border-radius: 999px; background: var(--bg-2); --i: 0; }
.seg-thumb { position: absolute; top: 3px; bottom: 3px; left: 3px;
    width: calc((100% - 6px) / var(--n, 2)); border-radius: 999px; background: var(--amber);
    transform: translateX(calc(var(--i) * 100%)); transition: transform .22s cubic-bezier(.3, .7, .3, 1); }
.seg:has(label:nth-of-type(2) input:checked) { --i: 1; }
.seg:has(label:nth-of-type(3) input:checked) { --i: 2; }
.seg:has(label:nth-of-type(4) input:checked) { --i: 3; }
.seg label { position: relative; z-index: 1; display: grid; place-items: center; cursor: pointer;
    font-size: 13.5px; color: var(--muted); border-radius: 999px; transition: color .15s; user-select: none; }
.seg label:hover { color: var(--fg); }
.seg label:has(input:checked) { color: hsl(from var(--amber) h s 20%); font-weight: 600; }
.seg input { position: absolute; opacity: 0; pointer-events: none; }
.seg:has(input:focus-visible) { box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber) 30%, transparent); }
@media (prefers-reduced-motion: reduce) { .seg-thumb, .seg label { transition: none; } }

/* grupo de pílulas (radio estilizado) — usado no modal de US (LP|PM) */
/* grupo de opções (radio) em coluna: cada opção é uma linha de largura uniforme,
   com um radio CUSTOMIZADO (ponto âmbar) — nada de radio nativo de tamanho
   inconsistente. Sem margem própria: o gap do .modal-body cuida do respiro. */
.pill-group { display: flex; flex-direction: column; gap: .5rem; }
.pill { display: flex; align-items: center; gap: .6rem; padding: .55rem .8rem;
    border: 1px solid var(--border-2); border-radius: var(--radius); cursor: pointer; font-size: 13.5px;
    transition: border-color .12s, background .12s; }
.pill:hover { border-color: var(--amber); }
.pill:has(input:checked) { border-color: var(--amber); background: color-mix(in srgb, var(--amber) 8%, transparent); }
/* radio customizado: anel + ponto âmbar quando marcado */
.pill input[type="radio"] {
    appearance: none; -webkit-appearance: none; margin: 0; flex: none;
    width: 16px; height: 16px; border: 1px solid var(--border-2); border-radius: 50%;
    background: var(--bg-2); position: relative; cursor: pointer;
}
.pill input[type="radio"]:checked { border-color: var(--amber); }
.pill input[type="radio"]:checked::after {
    content: ""; position: absolute; inset: 3px; border-radius: 50%; background: var(--amber);
}
.pill input[type="radio"]:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber) 30%, transparent); }
.small { font-size: 12px; }

/* cabeçalho de coluna não-ordenável (mesma tipografia, sem link/hover) */
.th-static { display: block; padding: .7rem 1rem; }

/* lista de especificações (rótulo mono / valor) */
.spec { margin: 0; display: flex; flex-direction: column; }
.spec-row { display: grid; grid-template-columns: 140px 1fr; gap: 1rem; padding: .55rem 0; border-bottom: 1px solid var(--border); }
.spec-row:last-child { border-bottom: 0; }
.spec dt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); align-self: center; }
.spec dd { margin: 0; }

/* estado vazio */
.empty-state { border: 1px dashed var(--border-2); border-radius: 10px; padding: 2.5rem 1.5rem; text-align: center; }
.empty-state p { margin: 0 0 .35rem; }
.empty-state p:last-child { margin: 0; }

/* paginação com elipse */
.pager { display: flex; align-items: center; gap: 4px; margin-top: 1.25rem; }
.pager-btn {
    min-width: 32px; height: 32px; padding: 0 8px; display: inline-grid; place-items: center;
    font-family: var(--mono); font-size: 13px; border-radius: var(--radius);
    border: 1px solid var(--border-2); color: var(--fg); background: var(--panel);
}
.pager-btn:hover { border-color: var(--accent); color: var(--accent); }
.pager-btn.current { background: var(--accent); border-color: var(--accent); color: #fff; }
.pager-btn.disabled { color: var(--faint); border-color: var(--border); pointer-events: none; }
/* elipse = BOTÃO de salto de página (reset do <button>): some a borda/fundo, mas
   ganha cursor/hover p/ sinalizar que é clicável (vira input ao clicar). */
.pager-gap {
    min-width: 22px; height: 32px; padding: 0 4px; text-align: center;
    border: 0; background: none; cursor: pointer;
    color: var(--faint); font-family: var(--mono); font-size: 13px;
}
.pager-gap:hover { color: var(--accent); }
/* input que substitui a elipse ao saltar: estreito, casa a altura dos botões.
   Sem as setinhas (stepper) nativas do number — só o campo de digitar a página. */
.pager-jump-input {
    width: 56px; height: 32px; padding: 0 6px; text-align: center;
    font-family: var(--mono); font-size: 13px; border-radius: var(--radius);
    border: 1px solid var(--accent); color: var(--fg); background: var(--panel);
    -moz-appearance: textfield; appearance: textfield;
}
.pager-jump-input::-webkit-outer-spin-button,
.pager-jump-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* na "ilha escura" do rodapé da tabela, o botão/realce seguem o tom local */
.table-foot .pager-gap:hover { color: var(--accent-2); }

/* ============================ Seletor de arquivo ============================ */
.picker {
    display: flex; align-items: center; gap: 12px; cursor: pointer;
    background: var(--bg-2); border: 1px dashed var(--border-2);
    border-radius: var(--radius); padding: 9px 12px; transition: border-color .12s;
}
.picker:hover { border-color: var(--accent); }
.picker-icon { color: var(--muted); flex: none; }
.picker-path { flex: 1; min-width: 0; font-size: 13px; font-family: var(--mono); color: var(--fg); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.picker-path[data-empty="true"] { color: var(--faint); font-style: italic; font-family: inherit; }
.picker-btn { flex: none; padding: 4px 12px; pointer-events: none; }

.crop-stage { margin-top: .75rem; max-height: 320px; }
.crop-stage img { max-width: 100%; display: block; }

/* ============================ Modal ============================ */
/* O OVERLAY É QUEM ROLA quando o modal passa da altura da tela (celular, formulário longo):
   sem overflow aqui o modal era cortado e a roda/dedo rolava a PÁGINA DE TRÁS, deixando o
   fim do modal (e o Salvar) inalcançável. overscroll-behavior impede o rolar "vazar" para
   trás. place-items start + margin-block:auto no .modal: centraliza quando cabe e começa
   no topo quando não cabe (center puro cortaria o topo, fora do alcance da rolagem). */
.modal-overlay { position: fixed; inset: 0; z-index: 60; display: grid; place-items: start center; padding: 1rem; overflow-y: auto; overscroll-behavior: contain; background: rgba(0, 0, 0, .55); backdrop-filter: blur(2px); }
/* com modal aberto a página de trás NÃO rola (o conteúdo do #modal só existe enquanto
   aberto — fechar é esvaziá-lo). */
html:has(#modal .modal-overlay), body:has(#modal .modal-overlay),
body:has(#modal .modal-overlay) .area-main { overflow: hidden; }
.modal { margin-block: auto; width: min(400px, 100%); background: var(--panel); color: var(--fg); border: 1px solid var(--border-2); border-radius: 12px; box-shadow: var(--shadow); }
.modal-wide { width: min(460px, 100%); }
/* modal de tabela (ex.: histórico de calibrações): mais largo e com a tabela
   rolando se passar de ~50vh, sem estourar a tela. */
.modal-table { width: min(680px, 100%); }
/* modal de tabela com muitas colunas (ex.: qualificações do soldador). Cabe na
   tela; quem rola é a tabela (células nowrap).
   min-width:0 é obrigatório: o modal é item de grid (.modal-overlay), e item de
   grid nasce com min-width:auto — sem isto ele estica até o conteúdo e ignora o
   100%, estourando a tela no celular (e a tabela nunca rola). */
.modal-cols { width: min(1040px, 100%); min-width: 0; }
.modal-cols .modal-body { min-width: 0; }
/* área de linhas do modal de juntas (CJE): ALTURA FIXA. A prévia do −/+ muda o nº de
   linhas; se o modal crescesse, o overlay o recentralizaria e o stepper fugiria do
   cursor a cada clique. Tamanho constante → o stepper fica parado; rola a tabela. */
.joint-rows { display: flex; flex-direction: column; gap: .5rem; height: min(340px, 45dvh); }
.joint-rows > p { margin: 0; flex: none; }
.joint-rows > .modal-scroll { flex: 1; min-height: 0; max-height: none; }
.modal-scroll {
    max-height: 50vh; overflow: auto;
    scrollbar-width: thin; scrollbar-color: var(--border-2) transparent;
}

/* Modais altos: limitam-se à tela, título e ações sempre à vista. O 2rem desconta o
   padding do overlay. Duas variantes, porque o que rola é diferente:
     .modal-list — UMA lista longa (ex.: filtrar chanfro): rola a lista.
     .modal-form — um FORM (ex.: nova qualificação): rola o CORPO INTEIRO. Aqui o
       scroll não pode ir nos blocos: cada fieldset viraria um scroller próprio e
       cortaria os campos dentro dele. */
.modal-list,
.modal-form { display: flex; flex-direction: column; max-height: calc(100dvh - 2rem); }
/* o form tem pares de campos (mín/máx): 400px os empilha e alonga o scroll à toa */
.modal-form { width: min(560px, 100%); min-width: 0; }
.modal-list .modal-body,
.modal-form .modal-body { min-height: 0; }
.modal-list .modal-body { overflow: hidden; }
.modal-list .opt-field { overflow-y: auto; min-height: 0; scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }
.modal-form .modal-body { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }
/* link só-ícone (ex.: baixar/abrir anexo numa célula). */
.icon-link { display: inline-flex; width: 30px; height: 30px; align-items: center; justify-content: center; border-radius: 8px; color: var(--muted); }
.icon-link:hover { color: var(--amber); background: var(--hover); }
.icon-link svg { width: 18px; height: 18px; }
/* grade de evidências (fotos da aferição) no modal de calibração da trena */
.evidence-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(84px, 1fr)); gap: 8px; }
.evidence-thumb { display: block; aspect-ratio: 1; border-radius: var(--radius); overflow: hidden; border: 1px solid var(--border-2); }
.evidence-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.evidence-thumb:hover { border-color: var(--amber); }
.modal.pulse { animation: modal-pulse .22s ease; }
@keyframes modal-pulse { 0% { transform: scale(1); } 40% { transform: scale(1.025); } 100% { transform: scale(1); } }
/* modal-body como flex-column com gap: cada bloco do form (título, parágrafo,
   pills, campo, hint) ganha respiro vertical consistente, sem depender de margens
   ad-hoc por elemento (que deixavam tudo condensado). */
.modal-body { padding: 1.4rem 1.4rem 1rem; display: flex; flex-direction: column; gap: .9rem; }
.modal-body h2 { margin: 0; }
.modal-body p { margin: 0; color: var(--muted); font-size: 14px; }
/* o título cola no parágrafo logo abaixo (menos gap entre eles que entre blocos) */
.modal-body h2 + p { margin-top: -.5rem; }
/* destrutiva à ESQUERDA (interna, exige intenção); Cancelar à DIREITA (borda). */
.modal-actions { display: flex; justify-content: flex-end; gap: .6rem; padding: 1rem 1.4rem 1.2rem; }
.modal-actions button { padding: 6px 16px; min-width: 96px; }

/* MODAL DE ESCOLHA com lista filtrável (ex.: "Trocar área"): input de busca em
   cima + lista ABERTA abaixo (não dropdown). A lista rola se passar da altura;
   cada item é uma linha clicável (código mono + descrição). O JS (data-area-
   filter) esconde os <li> que não casam e revela .pick-empty. */
.modal-pick { width: min(460px, 100%); }
.pick-list {
    list-style: none; margin: .2rem 0 0; padding: 0;
    max-height: min(48vh, 360px); overflow-y: auto;
    border: 1px solid var(--border); border-radius: 8px;
}
.pick-list li + li .pick-item { border-top: 1px solid var(--border); }
.pick-item {
    display: flex; align-items: baseline; gap: .7rem;
    padding: .6rem .8rem; color: var(--fg); text-decoration: none;
    /* <button> no modal de NOVO relatório (POSTa em vez de navegar): reset para
       ficar idêntico ao <a> do "Trocar área". */
    width: 100%; border: 0; background: none; font: inherit; text-align: left; cursor: pointer;
}
.pick-item:hover, .pick-item:focus-visible { background: var(--hover); outline: none; }
.pick-code { font-family: var(--mono); font-weight: 600; color: var(--amber); flex: none; }
.pick-desc { color: var(--muted); font-size: 14px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pick-empty { padding: .2rem .2rem 0; }

/* grupo de opções (rádio) — usado no modal de filtro */
/* LISTA DE OPÇÕES EM CARTÃO (radio repaginado) — usado no modal de filtro. Cada
   opção é uma linha-retângulo; a selecionada (:checked) ganha borda âmbar, fundo
   sutil e o "dot" preenchido. Radio nativo escondido; o :checked estiliza o
   label irmão (zero JS). Continua radio de verdade (acessível, teclado). */
.opt-field { border: 0; margin: .9rem 0 0; padding: 0; }
.opt-legend { font-family: var(--mono); font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); margin-bottom: .55rem; padding: 0; }
.opt-list { display: flex; flex-direction: column; gap: 8px; }
.opt-list input { position: absolute; opacity: 0; width: 0; height: 0; }
.opt-card {
    display: flex; align-items: center; gap: .7rem;
    padding: .7rem .8rem; border-radius: 10px; cursor: pointer;
    border: 1px solid var(--border-2); background: var(--bg-2);
    color: var(--fg); font-size: 14px; transition: border-color .12s, background .12s;
}
.opt-card:hover { border-color: color-mix(in srgb, var(--amber) 55%, var(--border-2)); }
/* o "dot" do radio, desenhado (anel → preenchido âmbar quando marcado) */
.opt-dot { width: 16px; height: 16px; flex: none; border-radius: 50%; border: 2px solid var(--border-2); transition: border-color .12s; position: relative; }
/* selecionado: cartão realçado + dot âmbar */
.opt-list input:checked + .opt-card {
    border-color: var(--amber); background: color-mix(in srgb, var(--amber) 10%, transparent);
}
.opt-list input:checked + .opt-card .opt-dot { border-color: var(--amber); }
.opt-list input:checked + .opt-card .opt-dot::after {
    content: ""; position: absolute; inset: 2px; border-radius: 50%; background: var(--amber);
}
.opt-list input:checked + .opt-card .opt-text { font-weight: 600; }
/* foco por teclado: anel no cartão do radio focado */
.opt-list input:focus-visible + .opt-card { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }

/* CROQUI do ultrassom: o rádio de cartão (mesma linguagem do .opt-card) com o DESENHO
   como opção. Grid que quebra no celular; o SVG escala pelo viewBox e herda a cor
   (currentColor), então acompanha o tema. */
.croqui-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; }
.croqui-grid input { position: absolute; opacity: 0; width: 0; height: 0; }
.croqui-card {
    display: flex; flex-direction: column; align-items: center; gap: .6rem;
    padding: 1rem .8rem .7rem; border-radius: 10px; cursor: pointer;
    border: 1px solid var(--border-2); background: var(--bg-2); color: var(--fg);
    transition: border-color .12s, background .12s;
}
.croqui-card:hover { border-color: color-mix(in srgb, var(--amber) 55%, var(--border-2)); }
.croqui-svg { width: 100%; max-width: 240px; height: 120px; }
.croqui-label { font-family: var(--mono); font-size: 12px; color: var(--muted); }
.croqui-grid input:checked + .croqui-card { border-color: var(--amber); background: color-mix(in srgb, var(--amber) 10%, transparent); }
.croqui-grid input:checked + .croqui-card .croqui-label { color: var(--amber); font-weight: 600; }
.croqui-grid input:focus-visible + .croqui-card { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
.croqui-summary .croqui-svg { max-width: 260px; }

/* MODAIS da junta e da indicação do ultrassom: mais largos que o .modal-wide (a junta
   carrega a tabela de indicações; a indicação, 11 números). A grade dos números quebra
   sozinha até uma coluna no celular. */
.modal-us { width: min(820px, 100%); }
/* grades de COLUNAS FIXAS (o layout do legado: "linha de 2", "grade 3×3"); no celular
   caem para uma coluna. */
/* align-items:end — o rótulo que QUEBRA em duas linhas (coluna estreita) não empurra o
   seu input para baixo dos vizinhos: os inputs ficam na mesma linha. */
.form-grid-1, .form-grid-2, .form-grid-3 { display: grid; gap: .75rem; margin-top: .75rem; align-items: end; }
.form-grid-1 { grid-template-columns: minmax(0, 1fr); }
.form-grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.form-grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 560px) { .form-grid-2, .form-grid-3 { grid-template-columns: minmax(0, 1fr); } }

/* ============================ Toast ============================ */
.toast-wrap { position: fixed; top: 16px; right: 16px; z-index: 1000; display: flex; flex-direction: column; gap: 10px; pointer-events: none; }
.toast {
    pointer-events: auto; display: flex; align-items: center; gap: 12px;
    min-width: 270px; max-width: 380px;
    background: var(--elevated); border: 1px solid var(--border-2);
    border-left: 3px solid var(--accent); border-radius: 10px;
    padding: 11px 13px; box-shadow: var(--shadow);
    animation: toast-in .22s cubic-bezier(.2, .8, .2, 1);
}
.toast.leaving { animation: toast-out .2s ease forwards; }
.toast-success { border-left-color: var(--success); }
.toast-danger  { border-left-color: var(--danger); }
.toast-warning { border-left-color: var(--warning); }
.toast .t-icon { width: 20px; height: 20px; flex: none; color: var(--accent); }
.toast-success .t-icon { color: var(--success); }
.toast-danger  .t-icon { color: var(--danger); }
.toast-warning .t-icon { color: var(--warning); }
.toast .t-body { flex: 1; min-width: 0; }
.toast .t-title { display: block; font-size: 13px; font-weight: 600; line-height: 1.3; }
.toast .t-msg { display: block; font-size: 12.5px; color: var(--muted); line-height: 1.4; word-break: break-word; }
.toast .t-pie { width: 16px; height: 16px; flex: none; transform: rotate(-90deg); }
.toast .t-pie circle { fill: none; stroke: var(--faint); stroke-width: 3; stroke-dasharray: 44; stroke-dashoffset: 0; animation: pie var(--toast-ms, 4500ms) linear forwards; }
.toast-success .t-pie circle { stroke: var(--success); }
.toast-danger  .t-pie circle { stroke: var(--danger); }
.toast:hover .t-pie circle { animation-play-state: paused; }
@keyframes pie { to { stroke-dashoffset: 44; } }
@keyframes toast-in { from { opacity: 0; transform: translateX(24px); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateX(24px); } }


/* ==================== Wizard (relatórios: dimensional e visual) ==================== */
/* Barra de ETAPAS em TRILHO: marcadores redondos ligados por uma régua contínua, o
   rótulo ABAIXO de cada marcador.
   TRÊS estados, e é isso que a distingue de uma barra de abas:
     • concluída (.is-done)   — visto âmbar, a etapa tem o que precisa;
     • atual (.is-current)    — realçada, com aria-current="step" no HTML;
     • pendente               — número apagado, ainda falta.

   O RÓTULO ABAIXO é o que torna a régua possível: com ele à direita do marcador (como
   era antes) a linha horizontal atravessaria o texto do passo seguinte. Empilhado, a
   régua corre na altura dos marcadores e o texto fica livre embaixo.

   A régua é desenhada em DOIS pedaços por passo (::before à esquerda, ::after à direita)
   em vez de uma linha só atrás de tudo: assim cada metade acende sozinha, e o trilho
   âmbar pode parar exatamente no passo atual — é ele que mostra o progresso. Os cotos
   das pontas (antes do 1º, depois do último) somem com :first-child/:last-child. */
.wizard-steps { margin: 0 0 1.5rem; }
.wizard-steps ol {
    display: flex; list-style: none; margin: 0; padding: 0;
    /* NÃO envolve: numa barra de 8 passos, quebrar a linha partiria o trilho no meio e
       o progresso deixaria de ser legível. Falta espaço ⇒ rola na horizontal. */
    overflow-x: auto; scrollbar-width: thin;
}
/* cada passo divide a largura por igual — é o que mantém o espaçamento do trilho
   regular. min-width segura o rótulo em telas estreitas (aí a barra rola). */
.wizard-steps li { flex: 1 0 0; min-width: 5.5rem; position: relative; }
/* as duas metades da régua, na altura do centro do marcador (26px/2 + padding do topo) */
.wizard-steps li::before,
.wizard-steps li::after {
    content: ""; position: absolute; top: calc(.5rem + 13px);
    height: 2px; background: var(--border-2);
}
.wizard-steps li::before { left: 0; right: 50%; margin-right: 13px; }
.wizard-steps li::after  { right: 0; left: 50%; margin-left: 13px; }
/* cotos das pontas: não há passo antes do primeiro nem depois do último */
.wizard-steps li:first-child::before,
.wizard-steps li:last-child::after { display: none; }
/* TRILHO PERCORRIDO: âmbar até o passo atual. A metade DIREITA do atual continua
   apagada — dali em diante é o que ainda falta. */
.wizard-steps li.is-done::before,
.wizard-steps li.is-done::after,
.wizard-steps li.is-current::before { background: var(--amber); }
/* o atual encerra o trilho: o que vem depois dele é futuro, mesmo que já esteja
   concluído (dá para navegar para trás e voltar) */
.wizard-steps li.is-current::after { background: var(--border-2); }

/* o passo é <a> nas etapas sem form e <button> nas com (salvar ao sair): mesmo
   visual nos dois — o reset do button tira aparência nativa. */
.wizard-steps a,
.wizard-steps button {
    display: flex; flex-direction: column; align-items: center; gap: .4rem;
    padding: .5rem .25rem .45rem;
    border: 0; border-radius: var(--radius);
    color: var(--muted); font-size: 12.5px;
    width: 100%; background: none; font-family: inherit; text-align: center; cursor: pointer;
}
/* o marcador fica ACIMA da régua (que é irmã, no li) e cobre o traço embaixo dele */
.wizard-num {
    position: relative; z-index: 1;
    flex: none; display: grid; place-items: center; width: 26px; height: 26px;
    border-radius: 50%; border: 1px solid var(--border-2);
    background: var(--bg-2); color: var(--muted);
    font-family: var(--mono); font-size: 11.5px;
    transition: background .12s, border-color .12s, color .12s;
}
.wizard-num svg { width: 14px; height: 14px; }
.wizard-label {
    min-width: 0; max-width: 100%;
    /* o rótulo não pode empurrar a largura do passo: corta com reticências */
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* hover: só o marcador e o texto reagem — pintar o fundo do bloco brigaria com a régua */
.wizard-steps a:hover, .wizard-steps button:hover { color: var(--fg); }
.wizard-steps a:hover .wizard-num,
.wizard-steps button:hover .wizard-num { border-color: var(--amber); color: var(--fg); }
/* CONCLUÍDA: visto âmbar preenchido — "esta etapa está vencida" */
.wizard-steps li.is-done .wizard-num { background: var(--amber); border-color: var(--amber); color: #1b1206; }
.wizard-steps li.is-done a, .wizard-steps li.is-done button { color: var(--fg); }
/* ATUAL: marcador em anel âmbar (halo) e rótulo forte — o passo onde estou */
.wizard-steps li.is-current .wizard-num {
    border-color: var(--amber); color: var(--amber);
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--amber) 22%, transparent);
}
.wizard-steps li.is-current a,
.wizard-steps li.is-current button { color: var(--fg); font-weight: 600; }
.wizard-steps li.is-current.is-done .wizard-num { background: var(--amber); color: #1b1206; }

/* ==================== Transferência de dois painéis ====================
   Portado do SelectFilter2 do admin do Django (widgets.css), com as cores do tema:
     • título do painel: padding 8px, cantos superiores arredondados; o de DESTINO usa
       a cor de destaque (lá é --secondary, aqui o âmbar);
     • filtro logo abaixo, colado ao título (bordas laterais apenas);
     • as setas vivem numa PÍLULA (.selector-chooser: 30px de largura, fundo colorido,
       raio 10px) entre os painéis — não são dois botões soltos;
     • cada seta 24×24 com opacity .55, subindo a 1 no hover;
     • "Escolher/Remover todas": padding 4px 5px, raio 4px, mesma lógica de opacidade.
   A linha é selecionável por clique/arrasto (sem checkbox, sem <select> nativo). */
.transfer { display: grid; gap: .7rem; align-items: start; margin-bottom: 1.1rem; }
@media (min-width: 900px) { .transfer { grid-template-columns: 1fr auto 1fr; } }
.transfer-panel { display: flex; flex-direction: column; min-width: 0; }

/* título do painel */
.transfer-head {
    display: flex; align-items: baseline; gap: .5rem; flex-wrap: wrap;
    padding: 8px; border: 1px solid var(--border-2); border-radius: 4px 4px 0 0;
    background: var(--panel-2); color: var(--muted);
}
.transfer-head h2 { font-size: 12.5px; margin: 0; color: var(--fg); }
.transfer-hint { margin: 0; font-size: 11px; }
.transfer-panel.is-target .transfer-head {
    background: color-mix(in srgb, var(--amber) 20%, var(--panel-2));
    border-color: color-mix(in srgb, var(--amber) 45%, var(--border-2));
}

/* filtro: colado ao título, só bordas laterais (como o .selector-filter) */
.transfer-search {
    position: relative; display: block; padding: 8px;
    border: 1px solid var(--border-2); border-width: 0 1px;
    background: var(--panel);
}
.transfer-search svg {
    position: absolute; left: 1.05rem; top: 50%; transform: translateY(-50%);
    width: 14px; height: 14px; color: var(--faint); pointer-events: none;
}
.transfer-search input { width: 100%; padding-left: 1.9rem; height: 30px; font-size: 12.5px; }

.transfer-list {
    padding: 0 8px 8px; border: 1px solid var(--border-2); border-width: 0 1px 1px;
    border-radius: 0 0 4px 4px; background: var(--panel);
}
.transfer-items {
    list-style: none; margin: 0; padding: 0; height: 17.2em; overflow-y: auto;
    border: 1px solid var(--border-2); border-radius: 4px; background: var(--bg-2);
}
.transfer-items:focus-visible { outline: none; border-color: var(--amber); }
/* a LINHA é o alvo: clique/arrasto selecionam (o gesto do Django). */
.transfer-items [data-item] {
    display: flex; align-items: center; justify-content: space-between; gap: .5rem;
    padding: .28rem .6rem; font-family: var(--mono); font-size: 12px; line-height: 1.5;
    cursor: pointer; user-select: none;
}
.transfer-items [data-item]:hover { background: var(--hover); }
.transfer-items [data-item][aria-selected="true"] {
    background: color-mix(in srgb, var(--amber) 25%, transparent); color: var(--fg);
}
.transfer-empty { padding: 1.1rem .9rem; color: var(--muted); font-size: 12.5px; text-align: center; line-height: 1.5; }

/* ação em massa: rodapé do painel, arredondando a caixa embaixo */
.transfer-bulk {
    display: flex; align-items: center; justify-content: center; gap: .35rem;
    width: 100%; margin-top: 8px; padding: 6px 8px; opacity: .55;
    border: 1px solid var(--border-2); border-radius: 4px;
    background: var(--panel-2); color: var(--fg);
    font: inherit; font-size: 12px; cursor: pointer;
}
.transfer-bulk:hover:not(:disabled) { opacity: 1; background: var(--hover); }
.transfer-bulk:disabled { opacity: .3; cursor: default; }
.transfer-bulk-arrow { font-size: 13px; line-height: 1; color: var(--amber); }

/* PÍLULA das setas entre os painéis (.selector-chooser do Django) */
.transfer-actions {
    display: flex; gap: 3px; padding: 3px; align-self: center;
    background: var(--panel-2); border: 1px solid var(--border-2); border-radius: 10px;
}
@media (min-width: 900px) { .transfer-actions { flex-direction: column; } }
.transfer-actions .btn {
    display: grid; place-items: center; width: 24px; height: 24px; min-width: 0;
    padding: 0; border: 0; border-radius: 7px; background: none;
    color: var(--fg); font-size: 14px; line-height: 1; opacity: .55;
    transition: opacity .12s, background .12s;
}
.transfer-actions .btn:hover { opacity: 1; background: var(--hover); }

/* BADGE no cabeçalho do painel: ação em lote que só aparece COM seleção (data-selonly
   + o JS). Tem borda — é um alvo de clique, não um rótulo solto. */
.head-badge {
    margin-left: auto; flex: none; padding: .2rem .55rem;
    border: 1px solid var(--amber); border-radius: 999px;
    background: color-mix(in srgb, var(--amber) 15%, transparent);
    color: var(--amber); font: inherit; font-size: 11.5px; cursor: pointer;
}
.head-badge:hover { background: var(--amber); color: #1b1206; }
/* link textual dentro do cabeçalho ("tudo") */
.link-inline {
    padding: 0 .15rem; border: 0; background: none; color: var(--accent);
    font: inherit; font-size: 11.5px; text-decoration: underline; cursor: pointer;
}
.link-inline:hover { color: var(--accent-2); }
/* ícones de ação na linha: um por modal (montador+retrabalho / as-built). Acendem
   quando a peça tem aquele estado — é a resposta visual da linha. */
.row-acts { display: flex; gap: .15rem; flex: none; }
.row-act {
    display: grid; place-items: center; width: 22px; height: 22px;
    padding: 0; border: 0; border-radius: 5px; background: none;
    color: var(--faint); cursor: pointer; opacity: .55;
}
.row-act svg { width: 15px; height: 15px; }
.row-act:hover { opacity: 1; color: var(--fg); background: var(--hover); }
.row-act.on-rework { opacity: 1; color: var(--warning); }
.row-act.on-asbuilt { opacity: 1; color: var(--accent); }
/* modal mais largo para o bloco de retrabalho (8 causas em grade) */
.modal-wide { width: min(560px, 100%); }
/* dentro do fieldset do modal os filhos precisam respirar: sem isto a grade de causas
   cola na textarea de observações. Escopado aos modais para não mexer no .opt-field
   das outras telas. */
.modal .opt-field { display: flex; flex-direction: column; gap: .8rem; }

/* RESUMO: a tabela de peças pode ter centenas de linhas — teto de altura com rolagem
   PRÓPRIA (o thead já é sticky pelo .table-scroll), senão ela empurra instrumentos e
   anexo para muito abaixo e é preciso rolar a página inteira para ver o resto. */
.summary-table { max-height: min(50vh, 420px); }

/* lista de pares rótulo/valor do RESUMO (dados do relatório) */
.kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: .9rem 1.4rem; margin: 0; }
.kv > div { min-width: 0; }
.kv dt { font-family: var(--mono); font-size: 10.5px; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted); margin-bottom: .2rem; }
.kv dd { margin: 0; font-size: 14px; }
.notes { margin: 1rem 0 0; padding: .7rem .9rem; border-left: 2px solid var(--border-2);
    color: var(--muted); font-size: 13.5px; white-space: pre-wrap; }
.plain-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .35rem; }
/* idem para os instrumentos: muitos deles não podem alongar a página sem limite */
.summary-list { max-height: min(30vh, 260px); overflow-y: auto; }
/* o PDF do relatório (prévia / aguardando assinaturas) visto na própria página */
.pdf-frame { display: block; width: 100%; height: min(85vh, 1100px);
    border: 1px solid var(--border); border-radius: var(--radius); background: var(--bg-2); }
.plain-list li { font-size: 13.5px; }

/* instrumento/peça: identificação em cima, complemento embaixo */
.ti-label { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.ti-tag { font-family: var(--mono); font-size: 12px; }
.ti-holder { color: var(--muted); font-size: 11px; }
.transfer-panel .table-wrap { max-height: 22rem; overflow-y: auto; }

/* lista de escolha por CHECKBOX (etapa Locação: instrumentos com portador). Nome
   próprio para não colidir com a .pick-list dos modais de escolha de área, que é
   uma lista de LINKS/ações e veio antes. */
.check-list { list-style: none; margin: 0; padding: 0;
    border: 1px solid var(--border-2); border-radius: var(--radius); background: var(--bg-2); }
.check-list li + li { border-top: 1px solid var(--border); }
.check-list .check-item { display: flex; align-items: center; gap: .6rem; padding: .5rem .7rem; cursor: pointer; }
.check-list .check-item:hover { background: var(--hover); }
.check-list .muted { font-size: 12.5px; }
.empty-note { color: var(--muted); font-size: 13.5px; padding: .8rem 0; }

/* WIDGET DE TRANSFERÊNCIA DENTRO DE UM MODAL (escolha dos soldadores da peça).
   O widget foi desenhado para uma página inteira; no modal ele precisa de um teto de
   altura, senão os dois painéis empurram os botões para fora da tela. */
.modal-transfer { width: min(920px, 100%); }
.modal-transfer .transfer { margin-top: .8rem; }
.modal-transfer .transfer-items { max-height: min(38vh, 320px); }

/* aviso de campo (ex.: procedimento limpo pela troca de data) */
.field-hint-warn { color: var(--warning); }

/* ==================== Abas (cadastro da área) ====================
   Barra de seções de uma MESMA entidade — Dados, Instruções, Materiais, IEIS,
   Pintura. Não confundir com .wizard-steps: lá há uma sequência com três estados
   (concluída/atual/pendente); aqui as seções são independentes e só interessa ONDE
   ESTOU, marcado pela sublinha âmbar (a cor de "dado em destaque", coerente com as
   eyebrows) e pelo texto acendendo.

   A régua inferior (border-bottom da barra) é o trilho: a aba atual a cobre com um
   traço mais grosso da cor de destaque, o desenho clássico de aba sem "caixinhas". */
.tabs {
    display: flex; flex-wrap: wrap; gap: .15rem;
    border-bottom: 1px solid var(--border-2);
    margin: 0 0 1.4rem;
}
.tab {
    padding: .55rem .85rem; color: var(--muted); font-size: 13.5px;
    border-bottom: 2px solid transparent; margin-bottom: -1px; white-space: nowrap;
}
.tab:hover { color: var(--fg); }
.tab.is-current { color: var(--fg); font-weight: 600; border-bottom-color: var(--amber); }

/* CONTAGEM ao lado de um título: "Materiais (12)", "Peças disponíveis (128)".
   Número mono e discreto, sem o peso de uma pílula colorida — é informação de apoio,
   não status (status é .tag).

   ⚠️ A classe já era usada em 8 lugares (os painéis de transferência do dimensional e
   o resumo do relatório) SEM nunca ter sido definida no CSS: o número saía com a
   fonte e a cor do título, sem os parênteses. Definida aqui, os oito passam a
   concordar. Os parênteses vêm do CSS para que o template escreva só o número. */
.badge { font-family: var(--mono); font-size: 11.5px; font-weight: 400; color: var(--muted); letter-spacing: 0; }
.badge::before { content: "("; }
.badge::after  { content: ")"; }

/* Lista de rótulos vinculados (resumo da área: materiais e IEIS).
   NÃO usa .chip: chip aqui significa "critério de filtro ativo" (o da toolbar, com
   ✕) — a mesma forma para conteúdo faria a lista parecer um filtro aplicado. Isto é
   DADO TÉCNICO, então segue a regra da casa: mono, hairline, sem pílula.

   UMA COLUNA, empilhada — exatamente como a .spec logo acima. Tentei grid de várias
   colunas para economizar altura e a régua nunca fechou: a hairline é border-bottom
   do item, então ela termina onde a CÉLULA termina. Em duas colunas isso deixa a
   linha interrompida no meio da seção, encurtada na última coluna (pelo respiro
   entre colunas) e cobrindo só metade da largura na última fileira quando o número
   de itens é ímpar — e não há seletor que acerte "a última fileira" com a contagem
   de colunas variando conforme a largura. Empilhado, a régua atravessa a seção
   inteira em qualquer contagem, igual à dos vizinhos.

   SEM CAIXA e SEM padding lateral, também para casar com a .spec (`padding: .55rem
   0`): uma moldura jogaria estes itens para dentro em relação aos vizinhos. A da
   .tree se justifica porque agrupa uma hierarquia; lista plana não precisa.

   A altura que as colunas economizariam vem da ROLAGEM: a partir de ~13rem, 8 ou 50
   vinculados ocupam o mesmo espaço e as seções seguintes não são empurradas. */
.tag-list {
    list-style: none; margin: 0; padding: 0;
    max-height: 13rem; overflow-y: auto;
}
.tag-list li {
    padding: .45rem 0; font-family: var(--mono); font-size: 12.5px;
    border-bottom: 1px solid var(--border);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* último item sem régua, como .spec-row:last-child — o fim da lista é o fim da
   seção, e a linha solta ali pareceria um item cortado pela rolagem. */
.tag-list li:last-child { border-bottom: 0; }

/* ==================== Treeview (esquemas de pintura → demãos) ====================
   Duas hierarquias, e ver as duas juntas é o ponto: a sequência de demãos só faz
   sentido sob o esquema. Cada esquema é um <details> (abre/fecha sem JS), e as demãos
   penduram numa régua vertical à esquerda — o guia que liga os filhos ao pai. */
.tree { border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden; }
.tree-node + .tree-node { border-top: 1px solid var(--border); }

.tree-head {
    display: flex; align-items: center; gap: .7rem; flex-wrap: wrap;
    padding: .7rem .85rem; cursor: pointer; background: var(--panel-2);
    list-style: none; /* some com o marcador nativo; o ▸ é nosso, abaixo */
}
.tree-head::-webkit-details-marker { display: none; }
.tree-head::before {
    content: "›"; flex: none; width: 1em; color: var(--muted);
    transition: transform .12s ease; transform-origin: center;
}
.tree-node[open] > .tree-head::before { transform: rotate(90deg); }
.tree-head:hover { background: var(--hover); }

.tree-name { flex: none; font-weight: 600; }
.tree-tags { display: flex; flex-wrap: wrap; gap: .3rem; min-width: 0; }
/* empurra as ações para a direita nos dois níveis */
.tree-actions { margin-left: auto; flex: none; }

.tree-children { list-style: none; margin: 0; padding: .2rem 0 .4rem 1.9rem; }
.tree-leaf {
    display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
    padding: .5rem .85rem .5rem 1rem; font-size: 13px;
    border-left: 1px solid var(--border-2); /* a régua que liga as demãos ao esquema */
}
.tree-leaf + .tree-leaf { margin-top: .1rem; }
.tree-detail { color: var(--muted); min-width: 0; }
.tree-empty { color: var(--muted); font-style: italic; }

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---- PALETA DE NAVEGAÇÃO (Ctrl+/) ----------------------------------------
   Vai a qualquer seção sem caçar no aside. Reusa .modal-overlay/.modal para
   herdar superfície, sombra e o véu; o que muda é a ANCORAGEM: a paleta abre
   no ALTO (align-items:start), não centrada — a lista cresce para baixo e o
   campo não dança conforme o número de resultados.

   ⚠️ O botão que a abre é um <button> com .nav-link: precisa do reset (a regra
   do projeto rejeita a aparência nativa dos controles). */
.nav-search {
    width: 100%; background: none; border: 0; font: inherit; text-align: left;
    cursor: pointer;
}
/* o badge da tecla vai para a direita, longe do rótulo.

   ⚠️ REPINTADO: o .kbd base foi desenhado para a ILHA da toolbar, que é grafite
   nos DOIS temas (--island-fg é um cinza CLARO). Aqui ele vive no aside, que no
   tema branco é claro — herdar aquela cor deixa o badge ilegível. Então as cores
   saem dos tokens do próprio aside (--muted/--border-2), que acompanham o tema. */
.nav-kbd {
    margin-left: auto;
    color: var(--muted); border-color: var(--border-2);
    background: color-mix(in srgb, var(--muted) 10%, transparent);
    opacity: 1;
}
[data-aside="rail"] .nav-kbd { display: none; }

.palette-overlay { align-items: start; padding-top: 12vh; }
.palette { width: min(520px, 100%); overflow: hidden; }

/* o campo encosta nas bordas do cartão: é o "topo" da paleta, não um campo de
   formulário solto — daí zerar a margem que .field-search traz. */
.palette-search { margin: 0; border-bottom: 1px solid var(--border-2); }
.palette-search input {
    border: 0; border-radius: 0; background: none; height: 52px; font-size: 15px;
}
.palette-search input:focus { outline: none; box-shadow: none; }

.palette-list {
    list-style: none; margin: 0; padding: .4rem; max-height: 46vh; overflow-y: auto;
}
/* o item é um <a> DE VERDADE (botão do meio abre em nova aba), então precisa do
   display:flex e da cor próprios — o sublinhado e o azul de link não servem aqui. */
.palette-item {
    display: flex; align-items: center; gap: .6rem;
    padding: .5rem .6rem; border-radius: var(--radius);
    color: var(--muted); font-size: 14px; text-decoration: none;
}
/* is-active é a seleção do TECLADO; o hover do mouse pinta igual, para os dois
   modos de uso terem a mesma leitura. */
.palette-item:hover, .palette-item.is-active { background: var(--hover); color: var(--fg); }
.palette-item.is-active { box-shadow: inset 2px 0 0 var(--accent); }
.palette-label { flex: none; }

/* o GRUPO desempata os homônimos ("Líquido penetrante" existe em Recebimento e
   em Relatórios): secundário na hierarquia, mas sempre visível. */
.palette-group {
    color: var(--faint); font-size: 12px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.palette-group::before { content: "· "; }

/* "aqui" marca a página atual — evita o clique que não leva a lugar nenhum. */
.palette-here {
    margin-left: auto; flex: none; font-size: 10.5px; letter-spacing: .04em;
    text-transform: uppercase; color: var(--accent);
}

.palette-empty { margin: 0; padding: 1.2rem .9rem; color: var(--muted); font-size: 14px; }

.palette-foot {
    display: flex; gap: 1rem; padding: .5rem .8rem;
    border-top: 1px solid var(--border-2); background: var(--panel-2);
    color: var(--faint); font-size: 12px;
}
/* mesmo motivo do .nav-kbd: o .kbd base assume a ilha grafite, e aqui o fundo é
   o do painel — que no tema claro é claro. */
.palette-foot .kbd {
    color: var(--muted); border-color: var(--border-2);
    background: color-mix(in srgb, var(--muted) 10%, transparent);
    opacity: 1;
}
@media (max-width: 560px) { .palette-foot { display: none; } } /* sem teclado físico */
