/**
 * Sistema visual — Vinculação BRASÍNDICE / SIMPRO
 *
 * Estética Apple (HIG): pilha de fontes do sistema (SF em Apple, Segoe no Windows),
 * superfícies claras, controles pílula, materiais translúcidos e movimento curto.
 * Claro por padrão, com modo noturno por escolha do usuário (ver os tokens de
 * [data-tema="escuro"]). Sem dependência externa — o arquivo é servido pelo
 * próprio Apache, então funciona igual em dev e no cPanel.
 */

/* ----------------------------------------------------------------- tokens */

:root {
    /* Tema claro fixo: o fundo branco é decisão de marca, não preferência do SO. */
    color-scheme: light;

    /* Superfícies — fundo branco; os cartões se definem por separador de 1px,
       não por tom de fundo. */
    --bg: #ffffff;
    --surface: #ffffff;
    --surface-sunken: #f4f4f2;   /* trilho do segmented, fundo de <code> */
    --separator: rgba(0, 0, 0, .13);
    --separator-strong: rgba(0, 0, 0, .22);

    /* Texto — contrastes conferidos contra o branco */
    --ink: #1d1d1f;              /* 16.9:1 */
    --ink-muted: #545456;        /*  7.6:1 — não usar cinza mais claro em corpo de texto */

    /**
     * Amarelo: dois papéis distintos, e confundi-los quebra a legibilidade.
     *
     *   --accent      PREENCHIMENTO apenas (botão, item ativo, barra de progresso).
     *                 Nunca como cor de texto sobre branco: #ffcc00 sobre #fff dá
     *                 1.4:1, ilegível.
     *   --accent-ink  o texto que vai EM CIMA do amarelo (13.4:1 sobre #ffcc00).
     *   --accent-text o amarelo escurecido para TEXTO e bordas sobre branco
     *                 (6.1:1). É o que links e anel de foco usam.
     */
    --accent: #ffcc00;
    --accent-hover: #ffd633;
    --accent-active: #f0be00;
    --accent-ink: #1d1d1f;
    --accent-text: #7a5b00;
    --accent-soft: rgba(255, 204, 0, .22);

    /* Estados semânticos seguem verde/vermelho — sucesso e erro não são marca. */
    --success: #1a7f37;
    --success-soft: rgba(26, 127, 55, .1);
    --danger: #cf222e;
    --danger-soft: rgba(207, 34, 46, .09);

    /* Terminal do log */
    --log-bg: #1c1c1e;
    --log-ink: #e5e5ea;

    /* Material translúcido da barra de navegação */
    --chrome: rgba(255, 255, 255, .75);

    /* Raio — cartões 12, controles 8, botões pílula */
    --r-card: 12px;
    --r-control: 8px;
    --r-pill: 980px;
    /* Botões: cantos suaves, não pílula — pedido do usuário. Badge e chip
       continuam pílula, porque ali o formato é o que os distingue de botão. */
    --r-botao: 10px;

    /* Escala de espaço */
    --s1: 4px;
    --s2: 8px;
    --s3: 12px;
    --s4: 16px;
    --s5: 24px;
    --s6: 32px;
    --s7: 48px;

    /* Movimento — curva padrão da Apple, duração de produto */
    --ease: cubic-bezier(.25, .1, .25, 1);
    /* Altura da topbar. A gaveta e o veu do mobile comecam exatamente onde ela
       acaba, entao o numero precisa estar em um lugar so. */
    --topbar-h: 56px;
    --fast: 150ms;
    --base: 220ms;

    /* Camadas nomeadas — nada de 999 */
    --z-header: 100;
    --z-progress: 200;

    --azul: #0a66c2;
    --azul-soft: rgba(10, 102, 194, .12);
    --roxo: #7c3aed;
    --roxo-soft: rgba(124, 58, 237, .12);
    /* Divergente: nao e erro do arquivo, e cadastro pra conferir. */
    --ambar: #b45309;
    --ambar-soft: rgba(180, 83, 9, .12);
    --cinza-soft: rgba(120, 120, 128, .14);
    --aviso-bg: #fff8e1;
    --aviso-borda: #ffe08a;
    --login-bg: #f5f5f7;
}

/**
 * Modo noturno. Escolha explícita do usuário (botão na topbar, guardado no
 * localStorage), não preferência do SO: o atributo entra no <html> antes da
 * primeira pintura, senão a tela pisca branca. Só os tokens mudam — nenhum
 * componente tem cor própria.
 */
:root[data-tema="escuro"] {
    color-scheme: dark;

    --bg: #161618;
    --surface: #1f1f22;
    --surface-sunken: #2a2a2e;
    --separator: rgba(255, 255, 255, .14);
    --separator-strong: rgba(255, 255, 255, .26);

    --ink: #f2f2f5;
    --ink-muted: #a3a3ab;

    /* O amarelo de preenchimento continua igual (é a marca); o de TEXTO precisa
       clarear: o #7a5b00 sobre fundo escuro fica ilegível. */
    --accent-text: #ffd60a;
    --accent-soft: rgba(255, 214, 10, .22);

    --success: #4ade80;
    --success-soft: rgba(74, 222, 128, .14);
    --danger: #ff6b6b;
    --danger-soft: rgba(255, 107, 107, .14);

    --chrome: rgba(22, 22, 24, .75);

    --azul: #6cb0f5;
    --azul-soft: rgba(108, 176, 245, .16);
    --roxo: #b794f6;
    --roxo-soft: rgba(183, 148, 246, .18);
    --ambar: #fbbf24;
    --ambar-soft: rgba(251, 191, 36, .16);
    --cinza-soft: rgba(160, 160, 170, .16);
    --aviso-bg: rgba(255, 204, 0, .08);
    --aviso-borda: rgba(255, 204, 0, .28);
    --login-bg: #101012;
}


/* ------------------------------------------------------------------- base */

*, *::before, *::after { box-sizing: border-box; }

/* O [hidden] do HTML vale display:none pela folha do NAVEGADOR, que perde para
   qualquer regra de classe daqui (".modal__formatos { display: flex }" ganhava,
   e o elemento marcado como hidden continuava na tela). Com !important o
   atributo volta a significar o que promete, em qualquer componente. */
[hidden] { display: none !important; }

html {
    -webkit-text-size-adjust: 100%;
    /* Interface 20% menor, como o zoom do navegador: encolhe layout e tipografia
       juntos. O Chrome resolve vh/dvh contra o viewport zoomado, então as telas de
       altura fixa (shell, login) continuam preenchendo a janela. */
    zoom: 0.8;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-family: -apple-system, BlinkMacSystemFont, "SF Pro Text", "Segoe UI",
                 Roboto, Helvetica, Arial, sans-serif;
    font-size: 15px;
    line-height: 1.47;
    letter-spacing: -.01em;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
}

h1, h2, h3 { margin: 0; text-wrap: balance; letter-spacing: -.022em; }
h1 { font-size: 28px; line-height: 1.14; font-weight: 600; }
h2 { font-size: 19px; line-height: 1.26; font-weight: 600; }
h3 { font-size: 15px; line-height: 1.33; font-weight: 600; }

p { margin: 0; }

a {
    color: var(--accent-text);
    text-decoration: none;
    transition: opacity var(--fast) var(--ease);
}
a:hover { text-decoration: underline; }
a:active { opacity: .7; }

code {
    font-family: ui-monospace, "SF Mono", SFMono-Regular, Consolas, monospace;
    font-size: .92em;
    background: var(--surface-sunken);
    border: 1px solid var(--separator);
    border-radius: 5px;
    padding: 1px 5px;
}

/* O anel de foco usa o amarelo escurecido: o #ffcc00 puro sobre branco quase
   não aparece, e foco invisível é o mesmo que não ter foco. */
:focus-visible {
    outline: 3px solid var(--accent-text);
    outline-offset: 2px;
    border-radius: 4px;
}

/* --------------------------------------------------------------- estrutura */

.app-header {
    position: sticky;
    top: 0;
    z-index: var(--z-header);
    background: var(--chrome);
    border-bottom: 1px solid var(--separator);
}

/* O material translúcido só entra onde o navegador realmente desfoca;
   sem suporte, a barra fica opaca em vez de transparente e ilegível. */
@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .app-header {
        -webkit-backdrop-filter: saturate(180%) blur(20px);
        backdrop-filter: saturate(180%) blur(20px);
    }
}

.app-header__inner {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--s3) var(--s5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4);
    flex-wrap: wrap;
}

.wrap {
    max-width: 1200px;
    margin: 0 auto;
    padding: var(--s6) var(--s5) var(--s7);
}

.page-head { margin-bottom: var(--s5); }
.page-head p { color: var(--ink-muted); margin-top: var(--s2); max-width: 68ch; }

.stack > * + * { margin-top: var(--s4); }

/* A marca é texto, não imagem: um logo em PNG seria mais um arquivo para servir
   e mais uma coisa para desalinhar em tela de alta densidade. */
.marca {
    font-size: 17px;
    font-weight: 640;
    letter-spacing: -.02em;
    color: var(--ink);
}
.marca:hover { text-decoration: none; opacity: .7; }

/* ---------------------------------------------- segmented control (navegação) */

.segmented {
    display: inline-flex;
    background: var(--surface-sunken);
    border: 1px solid var(--separator);
    border-radius: var(--r-control);
    padding: 2px;
    gap: 2px;
}

.segmented a {
    padding: 5px 14px;
    border-radius: 6px;
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-muted);
    white-space: nowrap;
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.segmented a:hover { color: var(--ink); text-decoration: none; }

/* A seção ativa é o lugar onde o amarelo trabalha como sinal, não decoração. */
.segmented a[aria-current="page"] {
    background: var(--accent);
    color: var(--accent-ink);
    font-weight: 590;
}

.link-quiet { color: var(--ink-muted); font-size: 13px; font-weight: 500; }
.link-quiet:hover { color: var(--ink); }

/* --------------------------------------------------------------- progresso */

/* Fica colada abaixo da barra de navegação, como o indicador de carregamento
   do Safari. Só aparece durante upload ou troca de tela. */
.progress {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    z-index: var(--z-progress);
    background: var(--separator);
    overflow: hidden;
}

.progress[hidden] { display: none; }

.progress__bar {
    height: 100%;
    width: 0;
    background: var(--accent);
    transition: width var(--base) var(--ease);
}

/* Upload terminou, o servidor ainda processa: sem percentual para mostrar. */
.progress--indeterminado .progress__bar {
    animation: deslizar 1.4s ease-in-out infinite;
    transform-origin: left center;
}

@keyframes deslizar {
    0%   { transform: translateX(-100%) scaleX(.4); }
    100% { transform: translateX(100%)  scaleX(.4); }
}

/* ---------------------------------------------------------------- cartões */

.card {
    background: var(--surface);
    border: 1px solid var(--separator);
    border-radius: 0;   /* cards quadrados (pedido do usuário) */
    padding: var(--s5);
}

.card__title { margin-bottom: var(--s4); }

/* ----------------------------------------------------------------- avisos */

.banner {
    border-radius: var(--r-card);
    padding: var(--s3) var(--s4);
    font-size: 14px;
}
.banner--error   { background: var(--danger-soft);  color: var(--danger); }
.banner--success { background: var(--success-soft); color: var(--success); font-weight: 590; }
/* Nota de contexto, não alerta: preenchimento neutro no estilo iOS. O amarelo
   translúcido sobre superfície escura vira um verde-oliva sujo. */
.banner--info    { background: rgba(120, 120, 128, .14); color: var(--ink); }
/* Achado que pede conferencia no cadastro: nao e erro da importacao, mas nao
   pode se perder dentro do verde de "concluida". */
.banner--aviso   { background: var(--aviso-bg); border: 1px solid var(--aviso-borda); color: var(--ink); }

/* Nota dentro de um formulário precisa do próprio respiro: .stack não alcança aqui. */
form > .banner { margin-top: var(--s5); }

/* ---------------------------------------------------------------- formulário */

.field + .field { margin-top: var(--s5); }

.field__label {
    display: block;
    font-size: 14px;
    font-weight: 590;
    margin-bottom: var(--s2);
}

.field__hint {
    color: var(--ink-muted);
    font-size: 13px;
    margin-top: var(--s2);
    max-width: 68ch;
}

input[type="file"],
input[type="text"],
input[type="password"],
input[type="number"] {
    width: 100%;
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-control);
    padding: 9px 12px;
    transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}

input[type="number"] { max-width: 160px; }

/* TODO dropdown do sistema usa a mesma pele: seta chevron propria a direita (a
   nativa muda de desenho a cada SO), hover igual ao do input, e texto que corta
   com reticencias em vez de esticar o controle.
 *
 * Vale para o <select> nativo E para o .combo__campo, que e o input que o
 * combobox com busca poe no lugar do select. Sem isso o sistema tinha dois
 * dropdowns de aparencia diferente: uns com seta, os com busca sem nenhuma. */
/* Todo dropdown do sistema usa a mesma pele: seta chevron propria a direita (a
 * nativa muda de desenho a cada SO), hover igual ao do input, e texto que corta
 * com reticencias em vez de esticar o controle.
 *
 * Vale para o <select> nativo E para o .combo__campo, que e o input que o
 * combobox com busca poe no lugar do select — os dois sao o mesmo dropdown para
 * quem usa.
 *
 * O seletor e `input.combo__campo`, com o elemento junto, por especificidade:
 * `input[type="text"]` acima vale (0,1,1) e ganharia de `.combo__campo` (0,1,0).
 * Ganhando, o `background:` de la zerava size/repeat/position, e o SVG (que nao
 * tem tamanho proprio, so viewBox) esticava ate o tamanho do campo e se repetia:
 * a seta virava um risco diagonal atravessando o texto.
 *
 * O width/height no proprio SVG e a segunda trava: mesmo sem background-size ele
 * nao estica. */
select,
input.combo__campo {
    width: 100%;
    font: inherit;
    color: var(--ink);
    background: var(--surface);
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-control);
    padding: 9px 34px 9px 12px;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23787880' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 10px center;
    background-size: 16px 16px;
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
select:hover:not(:disabled),
input.combo__campo:hover:not(:disabled) { border-color: var(--ink-muted); }
select:focus,
input.combo__campo:focus { outline: none; border-color: var(--accent-text); box-shadow: 0 0 0 3.5px var(--accent-soft); }
select:disabled { color: var(--ink-muted); cursor: default; }

/* Aberto, a seta vira para cima — mesmo gesto do menu do usuario na topbar. */
input.combo__campo[aria-expanded="true"] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23787880' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m18 15-6-6-6 6'/%3E%3C/svg%3E");
}

/* A seta e data URI e nao herda currentColor: o modo noturno precisa da sua. */
:root[data-tema="escuro"] select,
:root[data-tema="escuro"] input.combo__campo {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a3a3ab' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
}
:root[data-tema="escuro"] input.combo__campo[aria-expanded="true"] {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%23a3a3ab' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m18 15-6-6-6 6'/%3E%3C/svg%3E");
}

/* A lista aberta e desenhada pelo SO: so da para pedir as cores do tema. */
select option { background: var(--surface); color: var(--ink); }

input:hover:not(:disabled) { border-color: var(--ink-muted); }

input:focus {
    outline: none;
    border-color: var(--accent-text);
    box-shadow: 0 0 0 3.5px var(--accent-soft);
}

input::placeholder { color: var(--ink-muted); }

/* Mantém o seletor de arquivo nativo — só veste o botão que ele já expõe. */
input[type="file"] { padding: 7px 10px; cursor: pointer; }

input[type="file"]::file-selector-button {
    font: inherit;
    font-weight: 500;
    color: var(--ink);
    background: var(--surface-sunken);
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-botao);
    padding: 5px 14px;
    margin-right: var(--s3);
    cursor: pointer;
    transition: background var(--fast) var(--ease);
}
input[type="file"]::file-selector-button:hover { background: var(--separator); }

/* ----------------------------------------------------------------- botões */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--s2);
    font: inherit;
    letter-spacing: -.01em;
    padding: 10px 22px;
    border: 0;
    border-radius: var(--r-botao);
    background: var(--accent);
    color: var(--accent-ink);
    font-weight: 590;
    cursor: pointer;
    transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}

.btn:hover:not(:disabled) { background: var(--accent-hover); }
.btn:active:not(:disabled) { background: var(--accent-active); transform: scale(.97); }

.btn:disabled { opacity: .55; cursor: default; }
.btn[data-busy] { cursor: progress; }

.btn--block { width: 100%; }

/* Indicador de trabalho em andamento — o envio leva minutos nos arquivos grandes. */
.btn[data-busy]::before {
    content: "";
    width: 14px;
    height: 14px;
    border-radius: 50%;
    border: 2px solid rgba(29, 29, 31, .3);
    border-top-color: var(--accent-ink);
    animation: spin 700ms linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* O "Cancelar envio" nasce no JS ao lado do submit enquanto o upload sobe. */
.form-actions { margin-top: var(--s5); display: flex; gap: var(--s3); flex-wrap: wrap; align-items: center; }

/* Formularios de importacao e comparacao: largura contida e centralizada.
   Sem isso os campos de arquivo esticam nos 960px da coluna.
   O :not(.filtros) tira dessa regra a barra de filtros da diferenca entre
   edicoes, que precisa da largura toda para alinhar com os cards abaixo. */
main.wrap > form.card:not(.filtros) {
    max-width: 480px;
    margin-inline: auto;
}

/* --------------------------------------------------- nova importacao (passos) */

.import-nova { max-width: 600px; margin-inline: auto; }

/* Bloco de orientacao: amarelo claro. */
.orientacao {
    background: var(--aviso-bg);
    border: 1px solid var(--aviso-borda);
    border-radius: var(--r-card);
    padding: var(--s4) var(--s5);
    font-size: 14px;
    color: var(--ink);
}
.orientacao ol { margin: var(--s2) 0 0; padding-left: 1.3em; }
.orientacao li + li { margin-top: var(--s2); }
.orientacao__nota { margin: var(--s3) 0 0; font-size: 13px; color: var(--ink); }

/* Nova importacao: instrucoes ao lado do bloco de upload. */
.import-split {
    display: grid;
    grid-template-columns: minmax(240px, 320px) 1fr;
    gap: var(--s5);
    align-items: start;
}
/* `minmax(0, 1fr)` e nao `1fr`: o minimo automatico de uma track de grid e o
   min-content do filho, que aqui passa da largura da tela e vazava 8px para
   fora do .wrap. Com minimo 0 a coluna encolhe de verdade. */
@media (max-width: 860px) { .import-split { grid-template-columns: minmax(0, 1fr); } }

.select-uf { max-width: 320px; }

/* Select com busca. O <select> real fica escondido (mas continua no form, com o
   required do navegador); o que aparece é o campo de filtro + a lista. */
.combo { position: relative; max-width: 320px; }
/* `left: 0` nao e enfeite. Sem ele o absoluto cai na POSICAO ESTATICA — logo
   depois do botao do combo — e ai `width: 100%` conta a partir dali, sobrando
   para fora do container. Como o <select> guarda o valor do formulario, ele
   existe em quase toda tela, e o resultado era a pagina inteira rolando na
   horizontal no celular (medido: 493px de conteudo numa viewport de 375). */
.combo__nativo { position: absolute; left: 0; top: 0; opacity: 0; width: 100%; height: 0; pointer-events: none; }
/* A pele do .combo__campo (seta, padding, reticencias) fica junto com a do
   <select>, no bloco de formularios: os dois sao o mesmo dropdown para quem usa. */
/* O lado é decidido no JS pelo espaço disponível: para baixo por padrão, para
   cima quando o campo está no fim da tela (é o caso da UF, último do formulário
   de importação). Abrir sempre para cima cobria os campos da linha de cima. */
.combo__lista {
    position: absolute;
    z-index: 20;
    top: calc(100% + 4px);
    left: 0;
    right: 0;
    max-height: 240px;
    overflow-y: auto;
    margin: 0;
    padding: var(--s1);
    list-style: none;
    background: var(--surface);
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-control);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .14);
}
.combo__item {
    padding: 7px 10px;
    border-radius: 6px;
    font-size: 14px;
    cursor: pointer;
}
.combo__lista--acima { top: auto; bottom: calc(100% + 4px); }
.combo__item:hover, .combo__item--marcado { background: var(--surface-sunken); }
.combo__item--atual { color: var(--accent-text); font-weight: 600; }
.combo__vazio { padding: 7px 10px; font-size: 14px; color: var(--ink-muted); }

/* Area de drag-and-drop com icone de arquivo no centro. */
.dropzone {
    position: relative;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    gap: 3px; text-align: center;
    padding: 12px var(--s4);
    border: 2px dashed var(--separator-strong);
    border-radius: var(--r-card);
    background: var(--surface-sunken);
    color: var(--ink-muted);
    cursor: pointer;
    transition: border-color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.dropzone:hover { border-color: var(--accent); }
.dropzone--over { border-color: var(--accent); background: var(--accent-soft); color: var(--ink); }
.dropzone__ico { width: 26px; height: 26px; color: var(--ink-muted); }
.dropzone__txt { font-size: 13px; }
.dropzone__sub { font-size: 11px; }
/* input nativo escondido, mas o <label> continua abrindo o seletor no clique. */
.dropzone__input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; clip: rect(0 0 0 0); }

/* Etapas sequenciais. */
.passos { list-style: none; margin: 0; padding: 0; }
.passo { display: flex; gap: var(--s4); padding: var(--s4) 0; }
.passo:first-child { padding-top: 0; }
.passo + .passo { border-top: 1px solid var(--separator); }

.passo__num {
    flex-shrink: 0;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    background: var(--accent);
    color: var(--accent-ink);
    display: grid;
    place-items: center;
    font-size: 14px;
    font-weight: 600;
}
.passo__corpo { flex: 1; min-width: 0; }

.passo--desabilitado { opacity: .55; }
.passo--desabilitado .passo__num { background: var(--surface-sunken); color: var(--ink-muted); }

.passo--ok .passo__num { background: var(--success); color: #fff; }

/* Lista dos arquivos de publicação selecionados. */
/* Altura fixa com rolagem: subir 15 .txt de uma vez esticava a lista pra fora
   da viewport e escondia os botoes do modal. Dentro do modal (flex column) ela e
   a UNICA que rola: min-height 0 deixa encolher abaixo do conteudo em tela baixa,
   que e o que tira a barra de rolagem do modal inteiro. */
.arquivos-lista {
    list-style: none;
    margin: var(--s3) 0 0;
    padding: 0;
    max-height: 220px;
    /* Piso de 2 arquivos: encolhida a uma linha so, a lista deixa de mostrar que
       ha mais coisa ali. Piso maior fazia o modal passar de 90vh num notebook de
       730px de altura — e ai quem rolava era o modal, o oposto do que se quer. */
    min-height: 88px;
    overflow-y: auto;
}
.arquivos-lista:empty { display: none; }
.arquivos-lista li {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 6px 10px;
    font-size: 13px;
    color: var(--ink);
    background: var(--surface-sunken);
    border: 1px solid var(--separator);
    border-radius: var(--r-control);
}
.arquivos-lista li + li { margin-top: var(--s2); }
.arquivos-lista__nome { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.arquivos-lista__remover {
    flex-shrink: 0;
    display: grid;
    place-items: center;
    background: transparent;
    border: 0;
    border-radius: 5px;
    padding: 3px;
    cursor: pointer;
    color: var(--ink-muted);
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.arquivos-lista__remover:hover { background: var(--danger-soft); color: var(--danger); }
.arquivos-lista__remover svg { width: 14px; height: 14px; }
.arquivos-lista li::before {
    content: "";
    width: 14px;
    height: 14px;
    flex-shrink: 0;
    background: var(--accent);
    border-radius: 3px;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 2v6h6'/%3E%3C/svg%3E") center/contain no-repeat;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2'%3E%3Cpath d='M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z'/%3E%3Cpath d='M14 2v6h6'/%3E%3C/svg%3E") center/contain no-repeat;
}

/* ---------------------------------------------------------------- métricas */

.metrics {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
    gap: 1px;
    background: var(--separator);
    border: 1px solid var(--separator);
    border-radius: var(--r-card);
    overflow: hidden;
}

.metric { background: var(--surface); padding: var(--s4); }

.metric__value {
    font-size: 24px;
    font-weight: 600;
    letter-spacing: -.02em;
    font-variant-numeric: tabular-nums;
}

.metric__label { color: var(--ink-muted); font-size: 13px; margin-top: var(--s1); }

/* -------------------------------------------------------------- downloads */

.downloads { list-style: none; margin: var(--s4) 0 0; padding: 0; }

.downloads li + li { border-top: 1px solid var(--separator); }

.downloads a {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 10px 0;
    font-size: 14px;
    font-weight: 500;
}
.downloads a:hover { text-decoration: none; opacity: .75; }
.downloads a::after { content: "↓"; margin-left: auto; opacity: .55; }

/* -------------------------------------------------------------------- log */

.log {
    background: var(--log-bg);
    color: var(--log-ink);
    padding: var(--s4);
    border-radius: var(--r-control);
    overflow-x: auto;
    margin: 0;
    font: 13px/1.55 ui-monospace, "SF Mono", SFMono-Regular, Consolas, monospace;
    letter-spacing: 0;
}

/* ------------------------------------------------------------------- login */

.login-shell {
    /* 125vh e não 100vh: o zoom de 0.8 no <html> não encolhe vh (o Chrome resolve
       vh contra o viewport real), então 100vh renderizava a 80% e sobrava faixa
       embaixo. 100 / 0.8 = 125 devolve a altura cheia. Mesma razão no .app-body. */
    min-height: 125vh;
    min-height: 125dvh;
    display: grid;
    place-items: center;
    padding: var(--s5);
    background:
        radial-gradient(760px 420px at 12% -10%, var(--accent-soft), transparent 65%),
        radial-gradient(620px 380px at 100% 110%, var(--azul-soft), transparent 60%),
        var(--login-bg);
}

.login-card { width: 100%; max-width: 460px; }

/* Cartão elevado: na tela de login não existe nada em volta para dar hierarquia,
   então a moldura precisa vir da própria superfície. */
.login-card > form.card {
    max-width: none;
    padding: var(--s6);
    border-radius: 18px;
    box-shadow: 0 24px 60px rgba(0, 0, 0, .1), 0 2px 6px rgba(0, 0, 0, .04);
}

.login-card .field + .field { margin-top: var(--s4); }
.login-card .form-actions { margin-top: var(--s5); }
.login-card .btn { padding: 12px 18px; font-size: 16px; }

/* A marca é o único elemento em escala de display do sistema. É ela que carrega
   a identidade agora que não há cartão nem cor de fundo para isso. */
/* Mesma marca da topbar: texto + ponto amarelo, em escala de display. O ponto
   cresce junto, senão vira um respingo perdido ao lado de 36px de texto. */
.login-card__marca {
    display: flex;
    align-items: flex-end;
    font-size: 36px;
    line-height: 1;
    font-weight: 660;
    letter-spacing: -.03em;
    color: var(--ink);
}
.login-card__marca .topbar__brand-dot {
    width: 10px;
    height: 10px;
    margin-left: 5px;
    margin-bottom: 5px;
}

/* Espaço entre a marca e o cartão do formulário. */
.login-card__marca { margin-bottom: var(--s5); }

/* Versão do sistema, abaixo do formulário. */
.login-card__versao {
    text-align: center;
    color: var(--ink-muted);
    font-size: 13px;
    margin-top: var(--s4);
}

.login-card__sub {
    color: var(--ink-muted);
    font-size: 15px;
    margin-top: var(--s4);
    margin-bottom: var(--s6);
}

.login-card .banner { margin-top: var(--s5); margin-bottom: var(--s5); }

/* Sem cartão, o formulário precisa de um alvo visual próprio: os campos passam
   a ser a única moldura da tela. */
.login-card input { padding: 11px 12px; }

/* --------------------------------------------------------------- movimento */

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
    }
    /* Os indicadores de progresso não podem sumir — são a única prova de que
       algo está acontecendo num upload de minutos. Viram pulso de opacidade. */
    .btn[data-busy]::before,
    .spinner,
    .progress--indeterminado .progress__bar {
        animation: pulse 1.4s ease-in-out infinite !important;
        animation-duration: 1.4s !important;
    }
    .progress--indeterminado .progress__bar { transform: none; }
    @keyframes pulse { 50% { opacity: .3; } }
}

/* ------------------------------------------------------------ shell/sidebar */

/* Topbar full-width no topo, ACIMA do sidebar (a logo mora aqui). */
.topbar {
    position: sticky;
    top: 0;
    z-index: 200;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--s4);
    height: var(--topbar-h);
    padding: 0 var(--s5) 0 0;
    background: var(--surface);
    border-bottom: 1px solid var(--separator);
}
/* Coluna da logo com a MESMA largura da sidebar + borda alinhada: a logo fica
   exatamente por cima da sidebar. */
.topbar__left {
    display: flex;
    align-items: center;
    gap: var(--s2);
    width: 240px;
    height: 100%;
    box-sizing: border-box;
    padding-left: var(--s4);
    border-right: 1px solid var(--separator);
    transition: width var(--base) var(--ease);
}
:root[data-sidebar="recolhida"] .topbar__left { width: 64px; padding-left: 0; justify-content: center; }

/* Toggle na topbar: escondido no desktop (usa o do rodapé), aparece <=1024. */
.topbar__toggle {
    display: none;
    place-items: center;
    background: transparent;
    border: 0;
    border-radius: var(--r-control);
    padding: 6px;
    cursor: pointer;
    color: var(--ink-muted);
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.topbar__toggle:hover { background: var(--surface-sunken); color: var(--ink); }
.topbar__toggle svg { width: 22px; height: 22px; }

.topbar__brand {
    display: inline-flex;
    align-items: flex-end;
    font-size: 20px;
    font-weight: 700;
    letter-spacing: -.02em;
    line-height: 1;
    color: var(--ink);
}
.topbar__brand:hover { text-decoration: none; opacity: .8; }
.topbar__brand-dot {
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: var(--accent);
    margin-left: 3px;
    margin-bottom: 3px;
}

/* Corpo: sidebar + main, abaixo da topbar. Altura fixa (viewport menos a
   topbar) + overflow hidden: a topbar não rola, só o conteúdo do main. */
.app-body {
    display: flex;
    /* 125vh compensa o zoom de 0.8 do <html> (ver .login-shell); o -56px da
       topbar é literal e não muda. */
    height: calc(125vh - 56px);
    height: calc(125dvh - 56px);
    overflow: hidden;
}

.sidebar {
    width: 240px;
    flex-shrink: 0;
    border-right: 1px solid var(--separator);
    padding: var(--s4) var(--s3);
    height: 100%;
    overflow-x: hidden;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    transition: width var(--base) var(--ease), padding var(--base) var(--ease);
}
.sidebar__nav { flex: 1 1 auto; }

/* Botão de recolher no rodapé do sidebar (desktop). */
.sidebar__toggle {
    margin-top: var(--s2);
    padding-top: var(--s3);
    border-top: 1px solid var(--separator);
    display: flex;
    align-items: center;
    gap: 12px;
    padding-left: 12px;
    padding-bottom: 4px;
    background: transparent;
    border-left: 0;
    border-right: 0;
    border-bottom: 0;
    color: var(--ink-muted);
    font: inherit;
    font-size: 14px;
    font-weight: 500;
    cursor: pointer;
    white-space: nowrap;
    overflow: hidden;
}
.sidebar__toggle:hover { color: var(--ink); }
.sidebar__toggle span { transition: opacity var(--fast) var(--ease); }
.sidebar__toggle-ico { width: 18px; height: 18px; flex-shrink: 0; transition: transform var(--base) var(--ease); }

:root[data-sidebar="recolhida"] .sidebar__toggle { justify-content: center; gap: 0; padding-left: 0; }
:root[data-sidebar="recolhida"] .sidebar__toggle span { opacity: 0; max-width: 0; }

.sidebar__nav a {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 9px 12px;
    border-radius: var(--r-control);
    color: var(--ink-muted);
    font-size: 14px;
    font-weight: 500;
    margin-bottom: 2px;
    white-space: nowrap;
    overflow: hidden;
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.sidebar__nav a span { transition: opacity var(--fast) var(--ease); }
.sidebar__nav a:hover { background: var(--surface-sunken); color: var(--ink); text-decoration: none; }
.sidebar__nav a[aria-current="page"] {
    background: var(--accent-soft);
    color: var(--accent-text);
    font-weight: 600;
}
.nav-ico { width: 18px; height: 18px; flex-shrink: 0; }

/* Recolhido: rail só com ícones, animação smooth. */
:root[data-sidebar="recolhida"] .sidebar { width: 64px; padding-left: var(--s2); padding-right: var(--s2); }
:root[data-sidebar="recolhida"] .sidebar__nav a { justify-content: center; gap: 0; }
:root[data-sidebar="recolhida"] .sidebar__nav a span { opacity: 0; max-width: 0; }

.app-main { flex: 1; min-width: 0; height: 100%; overflow-y: auto; display: flex; flex-direction: column; }

.topbar__user { position: relative; display: flex; align-items: center; gap: var(--s2); }

/* Alternador de tema: mostra o ícone do modo para onde o clique leva. */
.btn-tema {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid var(--separator);
    border-radius: 50%;
    background: transparent;
    color: var(--ink-muted);
    cursor: pointer;
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn-tema:hover { background: var(--surface-sunken); color: var(--ink); }
/* Na tela de login não há barra onde encaixar: fica solto no canto. */
.btn-tema--solto { position: fixed; top: var(--s4); right: var(--s4); z-index: var(--z-header); background: var(--surface); }
.btn-tema svg { width: 17px; height: 17px; grid-area: 1 / 1; }
.btn-tema__sol { display: none; }
:root[data-tema="escuro"] .btn-tema__sol { display: block; }
:root[data-tema="escuro"] .btn-tema__lua { display: none; }

.avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    background: var(--ink);
    /* A inicial usa a cor do FUNDO, não branco fixo: no modo escuro o círculo
       fica claro e o branco sumia. */
    color: var(--bg);
    display: grid;
    place-items: center;
    font-size: 13px;
    font-weight: 600;
    flex-shrink: 0;
}

/* Botão do usuário: abre o dropdown com "Sair". */
.user-btn {
    display: flex;
    align-items: center;
    gap: var(--s2);
    padding: 4px 8px 4px 4px;
    border: 1px solid transparent;
    border-radius: var(--r-botao);
    background: transparent;
    font: inherit;
    color: var(--ink);
    cursor: pointer;
    transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.user-btn:hover { background: var(--surface-sunken); }
.user-btn[aria-expanded="true"] { background: var(--surface-sunken); border-color: var(--separator); }
.user-btn__login { font-size: 14px; font-weight: 500; }
.user-btn__chevron { width: 16px; height: 16px; color: var(--ink-muted); transition: transform var(--fast) var(--ease); }
.user-btn[aria-expanded="true"] .user-btn__chevron { transform: rotate(180deg); }

.user-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 160px;
    background: var(--surface);
    border: 1px solid var(--separator);
    border-radius: var(--r-control);
    box-shadow: 0 8px 24px rgba(0, 0, 0, .12);
    padding: 4px;
    z-index: var(--z-header);
}
.user-menu[hidden] { display: none; }
.user-menu a {
    display: block;
    padding: 8px 12px;
    border-radius: 6px;
    font-size: 14px;
    font-weight: 500;
    color: var(--ink);
}
.user-menu a:hover { background: var(--surface-sunken); text-decoration: none; }

/* ------------------------------------------------------------- tabela dados */

.page-head--row { display: flex; align-items: center; justify-content: space-between; gap: var(--s4); }
.page-head__acoes { display: flex; gap: var(--s3); flex-wrap: wrap; }

/* Rodapé da tabela: paginação à esquerda, total à direita. O margin-right auto
   na paginação mantém o total encostado na direita mesmo com página única, em
   que o pager não renderiza nada. */
.table-rodape { display: flex; align-items: center; gap: var(--s4); flex-wrap: wrap; }
.table-rodape .pagination { margin-right: auto; }
.tabela-total { color: var(--ink-muted); font-size: 14px; margin-left: auto; }

.col-id { color: var(--ink-muted); font-variant-numeric: tabular-nums; width: 1%; white-space: nowrap; }

/* Ficha do item: rótulo em cima, valor embaixo, em grade que se ajusta sozinha. */
.dados { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: var(--s4); margin: var(--s4) 0 0; }
.dados dt { color: var(--ink-muted); font-size: 12px; text-transform: uppercase; letter-spacing: .03em; }
.dados dd { margin: 2px 0 0; font-size: 15px; }

/* A linha crua tem 363 colunas: rola na horizontal em vez de esticar o cartão. */
.linha-crua {
    margin: var(--s3) 0 0;
    padding: var(--s3);
    background: var(--surface-sunken);
    border: 1px solid var(--separator);
    border-radius: var(--r-control);
    overflow-x: auto;
    font-size: 12px;
    white-space: pre;
}
.linha-crua code { background: none; border: 0; padding: 0; }

/* Modal mais largo para a linha crua de 363 colunas caber com folga. Dupla classe
   porque `.modal` (max-width 440px) e definido DEPOIS: com uma classe so, empate
   de especificidade, e quem vem depois ganha. */
.modal.modal--largo { max-width: min(920px, 92vw); }

details > summary { cursor: pointer; color: var(--accent-text); font-size: 14px; }

/* Legenda acima da tabela: diz o que aquela lista significa, sem virar parágrafo. */
.tabela-legenda {
    caption-side: top;
    text-align: left;
    padding: var(--s3) var(--s4) 0;
    color: var(--ink-muted);
    font-size: 13px;
}

.obrigatorio { color: var(--danger); font-weight: 700; }
.tabela-total strong { color: var(--ink); }

.card--table { padding: 0; overflow: hidden; }
.table-scroll { overflow: auto; min-height: 0; }
.table-scroll .data-table thead th { position: sticky; top: 0; z-index: 1; background: var(--surface); }

/* No desktop a lista ocupa a altura disponível e rola POR DENTRO: a tabela nunca
   passa da viewport e a paginação fica sempre visível, sem scroll da página.
   Só aqui: no tablet/mobile o layout é vertical e rola normal (ver media 1024). */
@media (min-width: 1025px) {
    .app-main:has(.card--table) { overflow: hidden; }
    /* A cadeia é .app-main > #spa > main.wrap: todos precisam propagar a altura. */
    #spa:has(.card--table) { flex: 1; min-height: 0; display: flex; flex-direction: column; }
    /* width fixo: o main.wrap virou flex + tem margin:0 auto, e auto-margin
       desliga o stretch -> sem width o item encolhe pro conteudo (tabela estreita
       fica menor que a larga da relatorio). Fixa em 1200 pra todas as listas. */
    main.wrap:has(.card--table) { flex: 1; min-height: 0; display: flex; flex-direction: column; width: min(1200px, 100%); }
    /* card vira flex-col e a table-scroll preenche + rola por dentro (min-height:0
       deixa encolher). Sem isso o conteudo vaza do card (overflow:hidden) e some
       sem barra de scroll. */
    main.wrap:has(.card--table) .card--table { flex: 1; min-height: 0; display: flex; flex-direction: column; }
    main.wrap:has(.card--table) .table-scroll { flex: 1; min-height: 0; }
}

.data-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.data-table th {
    text-align: left;
    padding: 12px 16px;
    font-size: 12px;
    font-weight: 600;
    color: var(--ink-muted);
    white-space: nowrap;
    border-bottom: 1px solid var(--separator);
}
.data-table td { padding: 12px 16px; border-bottom: 1px solid var(--separator); white-space: nowrap; }
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: var(--surface-sunken); }
.data-table__empty { text-align: center; color: var(--ink-muted); padding: var(--s6) !important; white-space: normal; }

/* Produto é a coluna que pode ser longa: deixa quebrar. */
.data-table td.col-produto { white-space: normal; min-width: 220px; }

/* Coluna de ações fixa na lateral direita: some no scroll horizontal, mas os
   botões continuam alcançáveis. A sombra separa do conteúdo que rola por baixo. */
.data-table th.col-acoes, .data-table td.col-acoes {
    position: sticky; right: 0;
    background: var(--surface);
    box-shadow: -6px 0 8px -6px rgba(0, 0, 0, .18);
}
.data-table tbody tr:hover td.col-acoes { background: var(--surface-sunken); }

/* Linha inteira clicável (resumo -> detalhe). */
.data-table tbody tr[data-href] { cursor: pointer; }

/* ------------------------------------------------------------------ badges */

.badge {
    display: inline-block;
    padding: 3px 10px;
    border-radius: var(--r-pill);
    font-size: 12px;
    font-weight: 590;
    line-height: 1.4;
}
.badge--ok, .badge--atualizado { background: var(--success-soft); color: var(--success); }
.badge--erro { background: var(--danger-soft); color: var(--danger); }
/* Nao lido = codigo da base fora do arquivo lido. Neutro, nao alarme. */
.badge--proc, .badge--vigente, .badge--descontinuado { background: var(--cinza-soft); color: var(--ink-muted); }
/* Substituído: reaponte por EAN — cor propria (azul) pra nao confundir. */
.badge--substituido { background: var(--azul-soft); color: var(--azul); }
/* Divergente: o TUSS do cadastro nunca existiu pra aquele TISS. Cadastro, nao arquivo. */
.badge--divergente, .badge--unidade, .badge--ambar { background: var(--ambar-soft); color: var(--ambar); }
.badge--roxo { background: var(--roxo-soft); color: var(--roxo); }

/* Arquivo selecionado acima do limite de upload do servidor. */
.arquivos-lista__erro {
    display: block;
    margin-top: var(--s2);
    padding: var(--s2) var(--s3);
    border-radius: var(--r-control);
    background: var(--danger-soft);
    color: var(--danger);
    font-size: 13px;
    line-height: 1.45;
}

/* ------------------------------------------------------- esqueleto de carga */

/* Desenhado no clique, antes da resposta chegar (ver mostrarEsqueleto no app.js).
   A tela de diferenca entre edicoes leva segundos no servidor; sem isto o clique
   nao mudava nada e parecia perdido. */
.sk {
    display: block;
    border-radius: var(--r-control);
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 25%,
        var(--separator) 37%,
        var(--surface-sunken) 63%
    );
    background-size: 400% 100%;
    animation: sk-brilho 1.4s ease infinite;
}

@keyframes sk-brilho {
    0%   { background-position: 100% 50%; }
    100% { background-position: 0 50%; }
}

.sk--titulo  { width: 260px; height: 30px; }
/* Seção sem forma conhecida: um bloco só, em vez de prometer cards e tabela. */
.sk--bloco   { width: 100%; height: 220px; }
.sk--num     { width: 60%; height: 22px; margin-bottom: 8px; }
.sk--rot     { width: 85%; height: 12px; }
.sk--celula  { flex: 1; height: 13px; }

.sk-card { pointer-events: none; }

.sk-tabela { padding: var(--s4) var(--s5); }
.sk-linha {
    display: flex;
    gap: var(--s4);
    align-items: center;
    padding: var(--s4) 0;
}
.sk-linha + .sk-linha { border-top: 1px solid var(--separator); }
/* Larguras diferentes por coluna: barra toda igual parece tabela de verdade
   vazia, nao carregando. */
.sk-linha .sk--celula:nth-child(1) { max-width: 90px; }
.sk-linha .sk--celula:nth-child(2) { max-width: 110px; }
.sk-linha .sk--celula:nth-child(4) { flex: 2.5; }
.sk-linha .sk--celula:nth-child(5) { max-width: 120px; }
.sk-linha .sk--celula:nth-child(6) { max-width: 80px; }

/* Cards ja na tela enquanto o filtro recarrega: o numero some e o brilho fica no
   lugar dele, mantendo o card do mesmo tamanho (a tela nao pula). */
.stat-cards--carregando .stat-cards__num,
.stat-cards--carregando .stat-cards__rot {
    color: transparent;
    border-radius: var(--r-control);
    background: linear-gradient(
        90deg,
        var(--surface-sunken) 25%,
        var(--separator) 37%,
        var(--surface-sunken) 63%
    );
    background-size: 400% 100%;
    animation: sk-brilho 1.4s ease infinite;
}
.stat-cards--carregando .stat-cards__card { pointer-events: none; }

/* Quem pediu menos movimento nao ganha brilho correndo na tela. */
@media (prefers-reduced-motion: reduce) {
    .sk,
    .stat-cards--carregando .stat-cards__num,
    .stat-cards--carregando .stat-cards__rot { animation: none; background: var(--surface-sunken); }
}

/* ------------------------------------------- detalhe do item entre edicoes */

/* Campo que nao existe / nao foi publicado. Cinza e em texto, nao "0": zero e um
   valor, ausencia nao. */
.sem-valor { color: var(--ink-muted); font-style: italic; font-size: 13px; }

.item-situacao { margin: var(--s3) 0 var(--s4); }

/* Vinculo do item no cadastro do Tasy. Mais de um codigo acontece: duplicidade
   no cadastro do cliente (dois codigos Tasy para o mesmo produto da publicacao). */
.col-tasy { white-space: nowrap; }
.vinculo-tasy {
    display: inline-block;
    padding: 1px 7px;
    margin-right: 4px;
    border-radius: var(--r-pill);
    background: var(--surface-sunken);
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

/* So a linha que MUDOU ganha fundo. Destacar tudo e o mesmo que nao destacar. */
.data-table tbody tr.linha-mudou > td { background: var(--aviso-bg); }
.data-table tbody tr.linha-mudou > td.col-destaque { font-weight: 600; }

/* Marca o que o preco e: PMC (maximo ao consumidor) ou PFB (fabrica). Sem isso
   dois numeros de bases diferentes aparecem lado a lado como comparaveis. */
.col-preco { white-space: nowrap; }
.preco-tipo {
    display: inline-block;
    margin-left: 6px;
    padding: 1px 5px;
    border-radius: var(--r-pill);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .02em;
    vertical-align: 1px;
}
.preco-tipo--pmc { background: var(--azul-soft); color: var(--azul); }
.preco-tipo--pf  { background: var(--cinza-soft); color: var(--ink-muted); }

/* Coluna TUSS quando o codigo mudou: de -> para, e em que edicao mudou. */
.tuss-de-para { display: block; white-space: nowrap; }
.tuss-edicao { display: block; font-size: 11px; color: var(--ink-muted); }
/* Substituicao que o cadastro ja acompanha: aparece para a conta bater com a
   tela de diferenca entre edicoes, mas nao pede acao. */
.selo-aplicado {
    display: inline-block;
    margin-top: 3px;
    padding: 1px 7px;
    border-radius: var(--r-pill);
    background: var(--success-soft);
    color: var(--success);
    font-size: 10px;
    font-weight: 700;
}

/* ------------------------------------------------------------------ filtros */

.filters { display: flex; align-items: center; gap: var(--s3); flex-wrap: wrap; }

/* Filtros da diferenca entre edicoes: ocupam a largura toda, alinhados com os
   cards. Grid e nao flex para as colunas nascerem do mesmo tamanho; o botao
   fica na ultima e encolhe ao seu conteudo. */
.filtros {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr)) auto;
    align-items: end;
    gap: var(--s4);
    width: 100%;
    max-width: none;
    margin-inline: 0;
}
.filtros__campo { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.filtros__campo label { font-size: 12px; font-weight: 590; color: var(--ink-muted); }
.filtros__campo select { width: 100%; min-width: 0; }
.filtros__campo--acao .btn { white-space: nowrap; }

/* Seletor de importacao na barra de filtros do relatorio. Largura fixa para o
   rotulo longo ("#46 · ed. 1097 · PI · BRAS · 30/07 14:22") caber. */
.filters__importacao { flex: 0 1 300px; min-width: 200px; }
.filters__importacao .combo { max-width: none; }

/* Filtro recolhivel. <details> nativo: abre/fecha sem JS e ja vem acessivel. */
.filtros-caixa__cabecalho {
    display: flex;
    align-items: center;
    gap: var(--s3);
    padding: var(--s3) var(--s4);
    background: var(--surface);
    border: 1px solid var(--separator);
    border-radius: var(--r-card);
    cursor: pointer;
    list-style: none;
    user-select: none;
}
.filtros-caixa__cabecalho::-webkit-details-marker { display: none; }
.filtros-caixa__cabecalho:hover { border-color: var(--separator-strong); }
.filtros-caixa__cabecalho:focus-visible {
    outline: none;
    border-color: var(--accent-text);
    box-shadow: 0 0 0 3.5px var(--accent-soft);
}
.filtros-caixa__ico { width: 18px; height: 18px; color: var(--ink-muted); flex: none; }
.filtros-caixa__titulo { font-weight: 590; font-size: 14px; }
/* O resumo cede espaco antes do titulo quando a tela aperta. */
.filtros-caixa__resumo {
    flex: 1;
    min-width: 0;
    color: var(--ink-muted);
    font-size: 13px;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.filtros-caixa__seta {
    width: 18px; height: 18px; flex: none;
    color: var(--ink-muted);
    transition: transform var(--fast) var(--ease);
}
.filtros-caixa[open] .filtros-caixa__seta { transform: rotate(180deg); }
.filtros-caixa[open] .filtros-caixa__cabecalho {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}
.filtros-caixa[open] .filtros {
    border-top: 0;
    border-top-left-radius: 0;
    border-top-right-radius: 0;
}

/* O combo tem largura propria de 320px, pensada para o formulario de importacao.
   Dentro do grid de filtros quem manda e a coluna. */
.filtros .combo { max-width: none; }
.filtros .search-box { max-width: none; min-width: 0; flex: none; }

/* A busca de produto ocupa a linha inteira, abaixo dos seletores: e um campo de
   digitar, nao de escolher, e no meio dos dropdowns competia com eles. */
.filtros__campo--busca { grid-column: 1 / -2; }

@media (max-width: 860px) {
    .filtros { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .filtros__campo--busca,
    .filtros__campo--acao { grid-column: 1 / -1; }
    .filtros__campo--acao .btn { width: 100%; }
}

/* Campo de busca com ícone embutido — maior e mais quadrado. */
.search-box { position: relative; flex: 1; min-width: 260px; max-width: 480px; }
.search-box__ico {
    position: absolute;
    left: 16px;
    top: 50%;
    transform: translateY(-50%);
    width: 18px;
    height: 18px;
    color: var(--ink-muted);
    pointer-events: none;
}
.search-box .filters__search {
    width: 100%;
    max-width: none;
    padding: 13px 16px 13px 46px;
    border-radius: var(--r-control);
    font-size: 15px;
    border: 0;
    background: var(--surface-sunken);
}
.search-box .filters__search:hover,
.search-box .filters__search:focus {
    border: 0;
    outline: none;
    box-shadow: none;
}
.chips { display: flex; gap: var(--s2); flex-wrap: wrap; }
.chip {
    padding: 5px 12px;
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-botao);
    font-size: 13px;
    font-weight: 500;
    color: var(--ink-muted);
}
.chip:hover { text-decoration: none; color: var(--ink); border-color: var(--ink-muted); }
.chip--ativa { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 590; }
.chip--ativa:hover { color: var(--accent-ink); }

.btn--sm { padding: 7px 14px; font-size: 13px; }
.filtro-run { color: var(--ink-muted); font-size: 13px; }

/* ---------------------------------------------------------------- paginação */

.pagination { display: flex; gap: 4px; list-style: none; padding: 0; margin: 0; flex-wrap: wrap; }
.pagination li a, .pagination li.active a {
    display: inline-block;
    padding: 6px 12px;
    border: 1px solid var(--separator);
    border-radius: var(--r-control);
    font-size: 13px;
    color: var(--ink);
}
.pagination li a:hover { background: var(--surface-sunken); text-decoration: none; }
.pagination li.active a { background: var(--accent); color: var(--accent-ink); border-color: var(--accent); font-weight: 590; }
.pagination li.disabled a { opacity: .4; pointer-events: none; }

/* -------------------------------------------------------------- responsivo */

/* iPad pra baixo: o toggle vai pra topbar (o do rodapé some). */
@media (max-width: 1024px) {
    .topbar__toggle { display: grid; }
    .sidebar__toggle { display: none; }
    .topbar__left, :root[data-sidebar="recolhida"] .topbar__left { width: auto; padding-left: var(--s5); border-right: 0; justify-content: flex-start; }
}

/* O véu só existe no celular; no desktop o menu é fixo e não precisa dele. */
.scrim { display: none; }

@media (max-width: 760px) {
    /* Mobile volta ao scroll normal da página (topbar sticky continua fixa). */
    .app-body { flex-direction: column; height: auto; overflow: visible; }
    .app-main { height: auto; overflow: visible; }

    /* MENU EM GAVETA, como app de celular.
       Antes o menu virava uma faixa horizontal acima do conteúdo, e os sete itens
       dividiam a largura da tela: sobravam ~50px por item e os rótulos saíam
       cortados em "Rela", "Dife", "Ediç". Fora da tela por padrão resolve os dois
       problemas de uma vez — o conteúdo ganha a tela inteira e o item cabe
       inteiro quando aparece. */
    .sidebar, :root[data-sidebar="recolhida"] .sidebar {
        position: fixed;
        top: var(--topbar-h);
        bottom: 0;
        left: 0;
        z-index: 250;
        width: min(80vw, 300px);
        height: auto;
        /* No desktop o menu e opaco por tabela: fica dentro do shell e herda o
           fundo dele. Flutuando por cima do conteudo precisa do proprio, senao
           a tabela atras aparece atravessando os itens. */
        background: var(--surface);
        border-right: 1px solid var(--separator);
        padding: var(--s4) var(--s3);
        overflow-y: auto;
        transform: translateX(-100%);
        transition: transform 220ms var(--ease);
    }
    :root[data-menu="aberto"] .sidebar { transform: none; }

    .scrim {
        display: block;
        position: fixed;
        inset: var(--topbar-h) 0 0 0;
        z-index: 240;
        background: rgba(0, 0, 0, .45);
    }
    /* `hidden` sai pelo JS ao abrir. Sem o véu bloqueando o toque, o dedo que
       tenta fechar a gaveta acerta um botão do conteúdo atrás dela. */
    .scrim[hidden] { display: none; }

    /* Empilhado e com rótulo inteiro: é uma gaveta, não um rail de ícones. */
    .sidebar__nav { display: flex; flex-direction: column; gap: 2px; overflow: visible; }
    .sidebar__nav a { margin-bottom: 0; justify-content: flex-start; white-space: nowrap; min-height: 44px; }
    /* No mobile o rail some — mantém os rótulos mesmo com o estado "recolhida"
       lembrado do desktop. */
    :root[data-sidebar="recolhida"] .sidebar__nav a { justify-content: flex-start; }
    :root[data-sidebar="recolhida"] .sidebar__nav a span { opacity: 1; }
}

@media (max-width: 640px) {
    .wrap { padding: var(--s5) var(--s4) var(--s6); }
    h1 { font-size: 24px; }
}

/* ---------------------------------------------------------------- relatorio */

.relatorio__botoes { display: flex; gap: var(--s3); flex-wrap: wrap; }
.relatorio table { width: 100%; border-collapse: collapse; margin: var(--s4) 0; }
.relatorio th, .relatorio td { text-align: left; padding: 6px 8px; border-bottom: 1px solid var(--surface-sunken); }
.resumo td:last-child { font-weight: 660; text-align: right; }

/* ------------------------------------------------------------------- modais */

.modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .4);
    display: grid;
    place-items: center;
    padding: var(--s4);
    z-index: 300;
    /* O overlay costuma ser filho de .stack, e o margin-top de la empurraria
       o fixed pra baixo, deixando uma faixa do fundo sem veu. */
    margin: 0;
}
.modal-overlay[hidden] { display: none; }

.modal {
    background: var(--surface);
    border-radius: var(--r-card);
    padding: var(--s5);
    width: 100%;
    max-width: 440px;
    /* Quem rola e a lista de arquivos, nao o modal: coluna flex + max-height faz
       o excedente sobrar para ela (ver .arquivos-lista), entao o titulo e os
       botoes ficam sempre na tela. O overflow-y: auto e so rede de seguranca
       para tela MUITO baixa, onde nem o resto cabe — sem ele os botoes ficariam
       inalcancaveis. O eixo X precisa ser declarado junto: `visible` com um
       `auto` do outro lado vira `auto` pela spec, e qualquer transbordo de 1px
       punha uma barra horizontal no modal. */
    display: flex;
    flex-direction: column;
    /* Toda a altura menos o respiro do overlay. Nao da para usar `100%`: o item
       de grid mora numa track `auto`, que cresce com o proprio conteudo, entao a
       porcentagem resolveria contra ela e nao limitaria nada (medido: modal de
       775px numa viewport de 730). */
    max-height: calc(100vh - 2 * var(--s4));
    overflow-y: auto;
    overflow-x: hidden;
    box-shadow: 0 20px 60px rgba(0, 0, 0, .28);
}
/* No escuro a sombra vira um halo preto sobre fundo preto — feio e inútil.
   A borda já separa o cartão do fundo. */
:root[data-tema="escuro"] .modal { box-shadow: none; border: 1px solid var(--separator); }
:root[data-tema="escuro"] .combo__lista,
:root[data-tema="escuro"] .user-menu { box-shadow: none; }

/* O form fica entre o modal e a lista de arquivos: sem ser flex tambem, o aperto
   da altura para nele e a lista nunca encolhe. */
.modal > form { display: flex; flex-direction: column; min-height: 0; }

.modal h2 { margin-bottom: var(--s3); }
.modal p { color: var(--ink-muted); font-size: 14px; }
.modal__acoes { display: flex; justify-content: flex-end; gap: var(--s3); margin-top: var(--s5); flex-wrap: wrap; }
.modal__formatos { display: flex; flex-direction: column; gap: var(--s2); margin-top: var(--s4); }
.modal__passo { color: var(--ink-muted); font-size: 13px; margin-top: var(--s3); }
.modal__passo strong { color: var(--ink); }
.modal__formatos .btn { justify-content: flex-start; }

.modal--carregando { max-width: 400px; }
.modal--carregando__linha { display: flex; align-items: center; gap: var(--s4); }
.modal--carregando p { margin: 0; }
.spinner {
    width: 22px;
    height: 22px;
    flex-shrink: 0;
    border-radius: 50%;
    border: 3px solid var(--separator);
    border-top-color: var(--accent);
    animation: spin 700ms linear infinite;
}

.btn--perigo { background: var(--danger); color: #fff; }
.btn--perigo:hover:not(:disabled) { background: #b31d29; }
.btn--ghost { background: var(--surface-sunken); color: var(--ink); }
.btn--ghost:hover:not(:disabled) { background: var(--separator); }

.btn-icone {
    background: transparent;
    border: 1px solid var(--separator-strong);
    border-radius: var(--r-control);
    padding: 5px 9px;
    cursor: pointer;
    color: var(--danger);
    line-height: 1;
    transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.btn-icone:hover { background: var(--danger-soft); border-color: var(--danger); }
.btn-icone svg { width: 16px; height: 16px; display: block; }

/* ------------------------------------------------------------- impressão/PDF */

@media print {
    .sidebar, .topbar, .progress, .btn, .chips, .filters, .filtro-run,
    .pagination, .modal-overlay, .page-head p, .no-print { display: none !important; }
    .app-body, .app-main, .wrap { display: block; max-width: none; margin: 0; padding: 0; }
    .card, .card--table { border: 0; box-shadow: none; padding: 0; }
    .data-table { font-size: 10px; }
    .data-table td, .data-table th { white-space: normal; padding: 4px 6px; }
    .badge { border: 1px solid #999; }
}

/* ------------------------------------------------------------- cadastro */

/* Formulário de duas colunas que vira uma sozinho: dado de pessoa é campo curto,
   e uma coluna só deixaria a tela comprida à toa. */
/* O formulario de cadastro escapa dos 480px do card padrao: sao muitos
   campos curtos, e uma coluna so deixaria a tela comprida a toa. */
main.wrap > form.card--largo { max-width: 900px; }

.campos { display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: var(--s4) var(--s5); }
.campos .field--largo { grid-column: 1 / -1; }
/* Na grade quem separa os campos e o gap. A margem de formulario empilhado
   (.field + .field) empurraria todos menos o primeiro da linha, deixando o
   primeiro campo desalinhado dos vizinhos. */
.campos .field + .field { margin-top: 0; }
.campos .field__hint { margin-top: var(--s1); }

.opcoes { display: flex; flex-direction: column; gap: var(--s3); }
.opcao { display: flex; gap: var(--s3); align-items: flex-start; cursor: pointer; }
.opcao input { margin-top: 3px; width: 16px; height: 16px; flex-shrink: 0; }
.opcao em { display: block; font-style: normal; color: var(--ink-muted); font-size: 13px; margin-top: 2px; }

.btn--sm { padding: 5px 12px; font-size: 13px; }

/* ------------------------------------------------------- cards de resumo */

/* Retrato do run acima da tabela. Os que são resultado (atualizado etc.) são
   clicáveis e filtram; os informativos (descontinuado, não vinculado) não. */
.stat-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: var(--s3); }
.stat-cards__card {
    display: flex; flex-direction: column; gap: 2px;
    padding: var(--s4);
    background: var(--surface);
    border: 1px solid var(--separator);
    border-left: 3px solid var(--separator-strong);
    border-radius: 0;
    color: var(--ink);
    transition: background var(--fast) var(--ease), transform var(--fast) var(--ease);
}
a.stat-cards__card:hover { text-decoration: none; background: var(--surface-sunken); }
a.stat-cards__card:active { transform: scale(.98); }
.stat-cards__card.is-ativo { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.stat-cards__num { font-size: 26px; font-weight: 640; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.stat-cards__rot { font-size: 13px; color: var(--ink-muted); }

.stat-cards__card--ok     { border-left-color: var(--success); }
.stat-cards__card--azul   { border-left-color: var(--azul); }
.stat-cards__card--erro   { border-left-color: var(--danger); }
.stat-cards__card--roxo   { border-left-color: var(--roxo); }
.stat-cards__card--ambar  { border-left-color: var(--ambar); }
.stat-cards__card--neutro { border-left-color: var(--separator-strong); }

/* ====================================================================
   MOBILE — ajustes que precisam vir DEPOIS de todos os componentes.

   Este bloco fica no fim de proposito. A secao "responsivo" mora no meio do
   arquivo, antes de .modal, .stat-cards e .data-table; qualquer override
   escrito la com a mesma especificidade PERDE para a definicao de baixo, e o
   ajuste some sem erro nenhum. Regra: ajuste de tela pequena entra aqui.
   ==================================================================== */

@media (max-width: 640px) {

    /* ------------------------------------------------------------ toque */

    /* 44px e o alvo minimo confortavel para o dedo (as duas plataformas usam
       esse numero). No desktop os controles sao menores porque o mouse acerta
       o que a ponta do cursor mira. */
    .btn { min-height: 44px; }
    .btn--sm { min-height: 38px; }
    .pagination li a { min-height: 40px; display: inline-flex; align-items: center; }

    /* Botoes de icone: no desktop o tamanho e o do icone, porque o mouse acerta
       27px sem esforco. No dedo nao acerta — e o pior deles era justamente o
       hamburguer, unica porta de entrada do menu no celular. A area cresce sem
       o icone crescer junto. */
    .topbar__toggle, .btn-tema, .user-btn { min-width: 44px; min-height: 44px; }
    .btn-icone { min-width: 40px; min-height: 40px; }
    .arquivos-lista__remover { min-width: 36px; min-height: 36px; }

    /* ------------------------------------------------------- cabecalho */

    .page-head--row { flex-direction: column; align-items: stretch; gap: var(--s3); }
    .page-head__acoes { display: flex; gap: var(--s3); }
    .page-head__acoes .btn { flex: 1; justify-content: center; }

    /* --------------------------------------------------------- filtros */

    /* Lado a lado sobra ~150px por campo, que nao serve nem para o dropdown de
       edicao ("Ed. 1097 - PI - BRAS - 30/07/2026 16:..."). */
    .filters { flex-direction: column; align-items: stretch; }
    /* Virar a coluna vira o EIXO PRINCIPAL junto: `flex: 0 1 300px`, escrito
       para largura, passou a valer como altura e abriu 240px de buraco entre o
       dropdown e o botao Buscar. Base automatica devolve a altura ao conteudo. */
    .filters > * { flex: 0 0 auto; width: 100%; min-width: 0; }
    .search-box { min-width: 0; max-width: none; }
    .filters .combo, .filters .combo__campo, .filters select { max-width: none; width: 100%; }
    .filtros { grid-template-columns: minmax(0, 1fr); }
    .filtros__campo--busca, .filtros__campo--acao { grid-column: auto; }

    /* ----------------------------------------------------------- modal */

    /* Folha subindo de baixo: e como app de celular pede escolha, e encosta os
       botoes na parte da tela que o polegar alcanca. */
    .modal-overlay { padding: 0; place-items: end stretch; }
    .modal {
        max-width: none;
        max-height: 88vh;
        border-radius: var(--r-card) var(--r-card) 0 0;
    }
    .modal__acoes { flex-direction: column-reverse; }
    .modal__acoes .btn { width: 100%; }

    /* --------------------------------------------------------- tabelas */

    /* CADA LINHA VIRA UM CARTAO.
       Sete colunas em 375px davam ~50px por coluna dentro de um scroll
       horizontal: para ler uma linha inteira o usuario arrastava a tabela de
       ponta a ponta e perdia o cabecalho no caminho. Empilhado, o rotulo anda
       junto com o valor. O rotulo vem do <th> correspondente, carimbado em
       data-rotulo pelo JS — assim vale para toda tabela do sistema sem mexer
       em nenhuma view. */
    .data-table thead { display: none; }
    .data-table, .data-table tbody, .data-table tr, .data-table td { display: block; width: 100%; }
    .table-scroll { overflow: visible; }

    .data-table tbody tr {
        padding: var(--s3) 0;
        border-bottom: 1px solid var(--separator);
    }
    .data-table tbody tr:last-child { border-bottom: 0; }

    .data-table td {
        display: flex;
        justify-content: space-between;
        align-items: baseline;
        gap: var(--s4);
        padding: 5px var(--s4);
        border-bottom: 0;
        /* nowrap e do modo tabela; aqui a largura e a da tela. */
        white-space: normal;
        text-align: right;
    }
    .data-table td::before {
        content: attr(data-rotulo);
        flex: 0 0 auto;
        max-width: 45%;
        font-size: 12px;
        font-weight: 600;
        color: var(--ink-muted);
        text-align: left;
    }
    /* Celula vazia sem rotulo nao vira linha fantasma no cartao. */
    .data-table td:empty { display: none; }

    /* Celula com mais de um bloco — o par "TUSS antigo -> novo" mais a nota da
       edicao, por exemplo. Os dois sao `display: block`, mas dentro do flex
       viravam itens lado a lado e a nota espremia o valor. Do segundo filho em
       diante cada um leva a propria linha. */
    .data-table td { flex-wrap: wrap; }
    .data-table td > *:not(:first-child) { flex: 1 0 100%; }

    /* A coluna grudada na direita so faz sentido com scroll horizontal. */
    .data-table td.col-acoes, .data-table th.col-acoes {
        position: static;
        box-shadow: none;
        justify-content: flex-end;
    }
    .data-table td.col-produto { min-width: 0; }

    /* "Nenhum item" e um recado, nao um par rotulo/valor. */
    .data-table__empty { display: block !important; text-align: center; }
    .data-table__empty::before { content: none; }
}
