/* IBM Plex Sans servida do próprio domínio: a CSP (middleware/seguranca.js)
   bloqueia fonte de CDN, e uma fonte que não carrega é pior que nenhuma.
   Subconjunto latino, três pesos — 59KB no total. */
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/vendor/fonts/ibm-plex-sans-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/vendor/fonts/ibm-plex-sans-600.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/vendor/fonts/ibm-plex-sans-700.woff2") format("woff2");
}

/* ===== Tokens de identidade (docs/design.md) =====
   Cinco papéis fixos. O matiz institucional é o azul profundo da marca, e os
   cinzas de superfície são contaminados por ele — cinza neutro deixa a tela
   com cara de template genérico, que é justamente o que o documento recusa.

   Os nomes antigos (--bg, --panel, --primary...) continuam existindo e agora
   derivam dos papéis: 22KB de CSS e as telas com estilo embutido dependem
   deles, e trocar todos de uma vez seria refatoração, não identidade. */
:root {
  color-scheme: light;
  --fonte: "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --fonte-mono: "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;

  --institucional: #0b1440;
  --apoio: #6069e9;
  --acento: #ffbf28; /* só no logo — nunca botão, badge ou link */
  --superficie-a: #f2f5fb;
  --superficie-b: #e9eef4;
  --conteudo: #f9fbfd;

  --bg: var(--conteudo);
  --panel: #ffffff;
  /* Superficie de segundo nivel (coluna de quadro kanban): um degrau abaixo
     do cartao, para o cartao flutuar sobre ela. */
  --panel-alt: #f1f5f9;
  --text: #1c2545;
  --muted: #667089;
  --border: #e2e7f0;
  --primary: #4f46e5;
  --primary-soft: #eef1ff;
  --danger: #dc2626;
  --success: #16a34a;
  --aviso: #b45309;
  --shadow: 0 10px 30px rgba(11, 20, 64, 0.07);

  /* Navegação lateral: no tema claro ela recebe o gradiente de superfície, e a
     área de conteúdo (quase-branca) flutua sobre ela por diferença de tom. */
  --sidebar-bg: linear-gradient(87deg, var(--superficie-a), var(--superficie-b));
  --sidebar-text: var(--institucional);
  --sidebar-muted: #5d6787;
  --sidebar-link: #384063;
  --sidebar-hover: rgba(11, 20, 64, 0.07);
  --sidebar-ativo-bg: #ffffff;
  --sidebar-ativo-text: var(--institucional);
  /* Marca do item selecionado. Tom institucional, não cor saturada: quem tem
     cor é o dado (§1 do docs/design.md). No claro, o cartão do item ativo é
     branco sobre uma lateral quase branca — sem esta marca e sem a borda, a
     seleção dependia só de uma sombra difusa de 7%. */
  --sidebar-ativo-marca: var(--institucional);
  --sidebar-borda: rgba(11, 20, 64, 0.1);
  --logo-fundo: #ffffff;
  /* Largura da navegação: a appbar começa onde ela termina, então as duas
     precisam ler o mesmo número. */
  --nav-w: 260px;
}
/* Tema escuro: a lateral escura que o sistema já tinha, agora coerente com o
   conteúdo (antes só a barra era escura e o resto continuava claro). */
:root[data-theme="escuro"] {
  color-scheme: dark;
  --institucional: #c9d2f5;
  --apoio: #8b93f0;
  --superficie-a: #131c34;
  --superficie-b: #0f1729;
  --conteudo: #0f172a;

  --bg: var(--conteudo);
  --panel: #1a2438;
  --panel-alt: #131c30;
  --text: #e2e8f0;
  --muted: #97a2ba;
  --border: #2c3a54;
  --primary: #7c85f2;
  --primary-soft: #262f57;
  --danger: #f87171;
  --success: #4ade80;
  --aviso: #fbbf24;
  --shadow: 0 10px 30px rgba(0, 0, 0, 0.35);

  --sidebar-bg: linear-gradient(87deg, #131c34, #0d1425);
  --sidebar-text: #f1f5ff;
  --sidebar-muted: #8792a2;
  --sidebar-link: #cbd5e1;
  --sidebar-hover: rgba(255, 255, 255, 0.09);
  --sidebar-ativo-bg: rgba(255, 255, 255, 0.14);
  --sidebar-ativo-text: #ffffff;
  --sidebar-ativo-marca: #ffffff;
  --sidebar-borda: rgba(255, 255, 255, 0.12);
  --logo-fundo: rgba(255, 255, 255, 0.92);
}
/* Tema rosa: escolha PESSOAL, guardada no navegador de quem clicou duas vezes
   no botão de tema — não existe no seletor das Configurações de propósito, que
   é o padrão da empresa e pintaria a tela dos colegas. Estrutura idêntica à do
   claro: o que muda é o matiz dos papéis, e a cor continua sendo do dado
   (docs/design.md §1), não da decoração. */
:root[data-theme="rosa"] {
  color-scheme: light;
  --institucional: #6b1440;
  --apoio: #e0669e;
  --superficie-a: #fdeff6;
  --superficie-b: #f7e0ec;
  --conteudo: #fff7fb;

  --bg: var(--conteudo);
  --panel: #ffffff;
  --panel-alt: #fdeff6;
  --text: #3d1029;
  --muted: #8a6076;
  --border: #f2d9e6;
  /* Rosa mais fechado que o da superfície: --primary também é cor de link e de
     texto de botão sobre branco, e o rosa claro não passaria em contraste. */
  --primary: #c62b74;
  --primary-soft: #fde7f1;
  --danger: #dc2626;
  --success: #16a34a;
  --aviso: #b45309;
  --shadow: 0 10px 30px rgba(107, 20, 64, 0.1);

  --sidebar-bg: linear-gradient(87deg, var(--superficie-a), var(--superficie-b));
  --sidebar-text: var(--institucional);
  --sidebar-muted: #8a6076;
  --sidebar-link: #5c2342;
  --sidebar-hover: rgba(107, 20, 64, 0.07);
  --sidebar-ativo-bg: #ffffff;
  --sidebar-ativo-text: var(--institucional);
  --sidebar-ativo-marca: var(--institucional);
  --sidebar-borda: rgba(107, 20, 64, 0.12);
  --logo-fundo: #ffffff;
}
* { box-sizing: border-box; }
/* Uma família só, como manda o documento — exceto onde monoespaçado é
   informação (código, caminho de arquivo), e não decoração. */
body { margin: 0; font-family: var(--fonte); background: var(--bg); color: var(--text); font-size: 15px; }
input, select, textarea, button, table { font-family: var(--fonte); }
code, pre, kbd, samp { font-family: var(--fonte-mono); }
a { color: inherit; text-decoration: none; }
.app-shell { display: grid; grid-template-columns: var(--nav-w) 1fr; min-height: 100vh; }
/* Era `#111827` fixo: a lateral ficava escura mesmo no tema claro, e o tema
   escuro deixava só ela coerente. Agora a pele vem dos tokens e os dois temas
   são de verdade — a estrutura (sanfona, agrupamento, item ativo) não muda. */
.sidebar { background: var(--sidebar-bg); color: var(--sidebar-text); padding: 24px 18px; border-right: 1px solid var(--sidebar-borda); }
/* Cabeçalho enxuto: só a marca e o nome curto, para sobrar altura para a
   navegação e o bloco do usuário. */
.brand { display: flex; align-items: center; gap: 10px; margin-bottom: 16px; flex-shrink: 0; }
.brand-logo { width: 32px; height: 32px; object-fit: contain; border-radius: 8px; background: var(--logo-fundo); padding: 3px; }
.brand h1 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: 0.01em; }
.brand-icon { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; background: linear-gradient(135deg, var(--primary), #818cf8); font-size: 22px; }
.sidebar nav { display: flex; flex-direction: column; gap: 6px; }
/* Rótulo longo ("Componentes/Consumíveis") corta com reticências em vez de
   empurrar a lateral e criar rolagem horizontal. */
/* A borda transparente já nasce em todo link: o item ativo só troca a cor
   dela, e assim a caixa tem exatamente o mesmo tamanho nos dois estados —
   ganhar 1px ao selecionar empurraria o texto e faria a navegação tremer. */
.sidebar nav a { display: flex; align-items: center; gap: 9px; padding: 9px 12px; border: 1px solid transparent; border-radius: 10px; color: var(--sidebar-link); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: 14px; }
.sidebar nav a span { overflow: hidden; text-overflow: ellipsis; }
.sidebar nav a:hover { background: var(--sidebar-hover); color: var(--sidebar-text); }
/* Três sinais somando um só estado: a marca vertical à esquerda (onde você
   está), a borda que recorta o cartão do fundo e a sombra que o levanta. A
   marca é `inset`, e não `border-left`, para não deslocar o texto em 3px. */
.sidebar nav a.active { background: var(--sidebar-ativo-bg); color: var(--sidebar-ativo-text); font-weight: 600; border-color: var(--sidebar-borda); box-shadow: inset 3px 0 0 var(--sidebar-ativo-marca), var(--shadow); }
/* Ícone de módulo: 16px na navegação, traço fino, herdando a cor do texto —
   é o mesmo desenho em toda tela (ver vocabulário em public/js/app.js). */
.icone { flex-shrink: 0; display: inline-block; vertical-align: -0.15em; }
.nav-section { display: flex; flex-direction: column; gap: 2px; }
/* Link de primeiro nível (Dashboard): herda a caixa dos demais itens, mas sem o
   recuo e sem a linha-guia — é justamente o não pertencer a grupo nenhum que
   ele precisa mostrar. O respiro embaixo separa-o do primeiro rótulo de seção,
   que sem isto encostava nele e os dois liam como um par. */
.nav-solo { margin-bottom: 4px; }
/* O rótulo virou botão: abre/fecha a seção, então precisa parecer clicável e
   perder a aparência padrão de <button>. */
.nav-section-label { display: flex; align-items: center; justify-content: space-between; gap: 8px; width: 100%; padding: 14px 12px 4px; background: none; border: 0; text-align: left; cursor: pointer; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--sidebar-muted); font-family: var(--fonte); }
.nav-section-label:hover { color: var(--sidebar-text); }
/* A seção que contém a página aberta sai do cinza dos rótulos: sem isto, o
   grupo em que você está lia exatamente igual aos outros seis, e a única
   pista de contexto era o item selecionado lá dentro. */
.nav-section.atual > .nav-section-label { color: var(--sidebar-text); }
.nav-section-chevron { font-size: 12px; transition: transform 0.15s ease; }
.nav-section.open .nav-section-chevron { transform: rotate(90deg); }
/* Fechada: só o rótulo da categoria aparece; as subcategorias somem. */
.nav-section:not(.open) .nav-links { display: none; }
/* Linha-guia da seção: uma hairline nascendo sob o ícone da categoria e
   descendo ao lado dos itens. É ela que separa os DOIS NÍVEIS — antes a
   diferença entre categoria e item era só tamanho e caixa da fonte, e num
   relance os dois liam como a mesma coisa. */
.nav-links { display: flex; flex-direction: column; gap: 2px; margin-left: 19px; padding-left: 4px; border-left: 1px solid var(--sidebar-borda); }
.nav-section-icone { display: inline-flex; }
.nav-section-label { gap: 8px; }
.nav-section-nome { flex: 1; }
.nav-section:first-child .nav-section-label { padding-top: 0; }
/* 19 (guia, sob o centro do ícone da categoria) + 4 + 5 = 28px de recuo. O
   rótulo mais longo do menu ("Componentes/Consumíveis") já cortava com
   reticências antes disto; recuar mais só encurtaria o que dá para ler. */
.sidebar nav .nav-section a { padding-left: 5px; }
/* ===== Subseção (o grupo "Empresas", que só o admin recebe) =====
   Um terceiro nível dentro da barra. Ele se distingue por RECUO e por PESO,
   não por uma cor nova: o rótulo já nasce em --sidebar-muted como o da seção
   de raiz, e o que o afasta é o recuo e um apagado leve. Cor própria aqui
   obrigaria a inventar um token por tema (claro, escuro e rosa).
   A 10px e a 0.75 de opacidade o rótulo sumia: "Estoque", dentro de Comércio
   e Serviços, não era lido de relance. Subiu para a altura do rótulo de seção
   — quem separa os dois níveis passa a ser o recuo, que já bastava. */
.nav-subsection { display: flex; flex-direction: column; gap: 2px; }
.nav-subsection-label { display: flex; align-items: center; justify-content: space-between; gap: 6px; width: 100%; padding: 8px 6px 3px; background: none; border: 0; text-align: left; cursor: pointer; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em; color: var(--sidebar-muted); opacity: 0.9; font-family: var(--fonte); }
.nav-subsection-label:hover { color: var(--sidebar-text); opacity: 1; }
.nav-subsection-label:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; border-radius: 6px; }
/* A subseção que contém a página aberta sai do apagado, como a seção de raiz
   já faz: com dois níveis, perder essa pista custa dois cliques para achar
   de novo onde você está. */
.nav-subsection.atual > .nav-subsection-label { color: var(--sidebar-text); opacity: 1; }
.nav-subsection-nome { flex: 1; }
.nav-subsection-chevron { font-size: 11px; transition: transform 0.15s ease; }
.nav-subsection.open .nav-subsection-chevron { transform: rotate(90deg); }
.nav-subsection:not(.open) .nav-sublinks { display: none; }
/* Classe PRÓPRIA e não outro `.nav-links`: recolhida, `.nav-links` vira painel
   flutuante (position: absolute logo abaixo), e um segundo `.nav-links`
   aninhado abriria um painel dentro do painel, ancorado no canto errado. */
.nav-sublinks { display: flex; flex-direction: column; gap: 2px; margin-left: 8px; padding-left: 4px; border-left: 1px solid var(--sidebar-borda); }
.main-content { padding: 24px; min-width: 0; }
.topbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-bottom: 20px; }
.topbar h2 { margin: 0; }
.topbar p { margin: 4px 0 0; color: var(--muted); }
.topbar-actions { display: flex; gap: 10px; }
input, select, textarea, button { font: inherit; }
input, select, textarea { width: 100%; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); }
input[type="checkbox"] { width: auto; padding: 0; margin-right: 6px; vertical-align: middle; }
.card { background: var(--panel); border-radius: 18px; padding: 20px; box-shadow: var(--shadow); margin-bottom: 20px; }
.cards-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 20px; }
.stat-card strong { font-size: 28px; color: var(--primary); display: block; margin-top: 6px; }
/* Assinatura estrutural do sistema (docs/design.md, §4): ícone + título curto +
   régua logo abaixo. A régua vem por CSS para valer em toda seção já existente
   sem espalhar <hr> por dezoito views; o ícone entra pelo `data-icone`. */
.section-title { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 14px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.section-title > :first-child, .titulo-secao { display: flex; align-items: center; gap: 9px; margin: 0; }
.titulo-secao { font-size: 1.05rem; font-weight: 600; color: var(--institucional); }
.section-title h2, .section-title h3 { font-size: 1.05rem; font-weight: 600; color: var(--institucional); margin: 0; }
/* Botão em outline, nunca preenchido com cor sólida (docs/design.md, §1): a
   interface é acromática e quem tem cor é o dado. A hierarquia entre as ações
   passa a vir do PESO do contorno e da cor dele, não de um bloco chapado.
   `background` transparente e não `var(--panel)`: o botão precisa funcionar
   tanto sobre o cartão branco quanto sobre a superfície cinza. */
.btn-primary, .btn-secondary { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 10px 14px; border-radius: 10px; border: 1px solid var(--border); background: transparent; cursor: pointer; font: inherit; font-weight: 600; text-decoration: none; }
.btn-primary { color: var(--institucional); border-color: var(--institucional); border-width: 1.5px; }
.btn-primary:hover { background: var(--primary-soft); }
.btn-secondary { color: var(--text); }
.btn-secondary:hover { background: var(--primary-soft); border-color: var(--primary); }
/* Saída (baixar, exportar, imprimir) em verde; destrutiva em vermelho. São as
   duas famílias que o documento separa da ação comum, e as únicas em que a cor
   do contorno carrega significado. Os dois são MODIFICADORES: vão sempre com
   `btn-secondary` junto (ex.: `class="btn-secondary btn-perigo"`). Sozinhos o
   botão nasce sem padding e sem contorno — `border-width` sem `border-style`
   não pinta nada. */
.btn-saida { color: var(--success); border-color: var(--success); border-width: 1.5px; }
.btn-saida:hover { background: color-mix(in srgb, var(--success) 10%, transparent); }
.btn-perigo { color: var(--danger); border-color: var(--danger); border-width: 1.5px; }
.btn-perigo:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); }
/* As telas antigas usam as classes do Bootstrap direto no HTML. Em vez de
   reescrever dezoito views, as três variantes usadas aqui passam a desenhar
   igual às de cima — senão metade do sistema ficaria com botão chapado. */
.btn.btn-primary, .btn.btn-success, .btn.btn-danger, .btn.btn-outline-primary {
  background: transparent;
  font-weight: 600;
  border-width: 1.5px;
}
.btn.btn-primary, .btn.btn-outline-primary { color: var(--institucional); border-color: var(--institucional); }
.btn.btn-success { color: var(--success); border-color: var(--success); }
.btn.btn-danger { color: var(--danger); border-color: var(--danger); }
.btn.btn-primary:hover, .btn.btn-outline-primary:hover { background: var(--primary-soft); color: var(--institucional); }
.btn.btn-success:hover { background: color-mix(in srgb, var(--success) 10%, transparent); color: var(--success); }
.btn.btn-danger:hover { background: color-mix(in srgb, var(--danger) 10%, transparent); color: var(--danger); }
.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; }
th, td { padding: 12px; border-bottom: 1px solid var(--border); text-align: left; }
/* DRE mês a mês (dashboard). Números à direita e em largura fixa: a coluna só
   se compara de relance quando as casas decimais ficam alinhadas. O nome do
   mês, à esquerda, é a única coluna de texto. */
.dre-tabela { font-variant-numeric: tabular-nums; }
.dre-tabela th, .dre-tabela td { padding: 8px 10px; font-size: 13px; text-align: right; }
.dre-tabela th:first-child, .dre-tabela td:first-child { text-align: left; }
.dre-tabela th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--muted); }
.dre-tabela tr.dre-total td { font-weight: 700; border-top: 2px solid var(--border); }
/* Cor SÓ nas linhas de lucro, e só como reforço: o sinal do número já diz
   prejuízo. Quem não separa verde de vermelho lê o "-" do mesmo jeito. */
.dre-positivo { color: var(--success); }
.dre-negativo { color: var(--danger); }
/* O aviso da trava do acumulado. Tem cor de alerta, e não de nota de rodapé,
   porque ele existe para NÃO ser esquecido: é uma decisão provisória sobre o
   número que a empresa usa para decidir. Some sozinho quando a trava sair. */
.dre-aviso-marco { margin-top: 12px; padding: 12px 14px; border-radius: 8px; font-size: 13px; line-height: 1.5;
  color: var(--text); background: color-mix(in srgb, var(--aviso) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--aviso) 40%, transparent); }
.dre-aviso-marco strong { color: var(--aviso); }
.thumb { width: 48px; height: 48px; object-fit: cover; border-radius: 6px; display: block; margin: 0 auto; }
.data-table td:first-child { width: 60px; text-align: center; }
.form-grid { display: grid; grid-template-columns: repeat(2, minmax(220px, 1fr)); gap: 16px; }
.form-group { display: flex; flex-direction: column; gap: 6px; }
.form-group.full { grid-column: 1 / -1; }
/* `.full` valia só para .form-group, mas os títulos e campos largos das telas
   de formulário já usavam `class="full"` direto no h3/label dentro do grid — e
   ficavam presos a uma coluna. Vale para qualquer filho do grid. */
.form-grid > .full { grid-column: 1 / -1; }
.location-selector { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
.location-selector select { flex: 1; min-width: 120px; max-width: 200px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); color: var(--text); cursor: pointer; }
.location-selector select:focus { border-color: var(--primary); outline: none; box-shadow: 0 0 0 3px rgba(79,70,229,0.1); }
.location-preview { margin-top: 8px; padding: 8px 12px; background: var(--primary-soft); border-radius: 10px; color: var(--primary); font-weight: 700; font-size: 14px; }
.form-actions { display: flex; gap: 10px; align-items: center; }
.filters-grid { display: grid; grid-template-columns: repeat(5, minmax(150px, 1fr)); gap: 12px; margin-bottom: 16px; }
.filters { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 16px; }
.filters select, .filters input { width: auto; min-width: 140px; }
.muted { color: var(--muted); }
.money { color: var(--success); }
.stat-card.money strong { color: var(--success); }
.stat-card.alert strong { color: var(--danger); }
td.right, th.right { text-align: right; }
.image-suggestions { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; }
.image-suggestion { display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 8px; border: 1px solid var(--border); border-radius: 10px; background: var(--panel); }
.image-suggestion img { width: 80px; height: 80px; object-fit: cover; border-radius: 8px; }
/* ==========================================================================
   Ambar do Bootstrap (orcamento, certidoes, expedicao)
   ==========================================================================
   O #ffc107 do Bootstrap sobre branco da 1,6:1 — ilegivel, e em TODOS os temas,
   nao so no escuro. Aqui ele passa a ser o --aviso do sistema: a mesma cor que
   o resto das telas ja usa para "atencao", e que tem par claro e escuro.
   `.btn-outline-warning` precisa de todas as variaveis porque o Bootstrap
   escreve o #ffc107 direto em cada uma delas, e nao via var(--bs-warning).
   `alert-warning` fica de fora de proposito: o Bootstrap 5.3 ja troca os tons
   dele sozinho pelo data-bs-theme. */
.text-warning { color: var(--aviso) !important; }
.btn-outline-warning {
  --bs-btn-color: var(--aviso);
  --bs-btn-border-color: var(--aviso);
  --bs-btn-hover-color: var(--panel);
  --bs-btn-hover-bg: var(--aviso);
  --bs-btn-hover-border-color: var(--aviso);
  --bs-btn-active-color: var(--panel);
  --bs-btn-active-bg: var(--aviso);
  --bs-btn-active-border-color: var(--aviso);
  --bs-btn-disabled-color: var(--aviso);
  --bs-btn-disabled-border-color: var(--aviso);
}
.btn-warning {
  --bs-btn-bg: var(--aviso);
  --bs-btn-border-color: var(--aviso);
  --bs-btn-color: var(--panel);
  --bs-btn-hover-bg: color-mix(in srgb, var(--aviso) 84%, black);
  --bs-btn-hover-border-color: color-mix(in srgb, var(--aviso) 84%, black);
  --bs-btn-hover-color: var(--panel);
  --bs-btn-active-bg: color-mix(in srgb, var(--aviso) 78%, black);
  --bs-btn-active-border-color: color-mix(in srgb, var(--aviso) 78%, black);
  --bs-btn-active-color: var(--panel);
}
/* O `.text-dark` que acompanha o `.bg-warning` no orcamento vira preto sobre um
   fundo que agora e escuro: a pastilha leva a cor da superficie no lugar. */
.bg-warning { background-color: var(--aviso) !important; }
.bg-warning.text-dark { color: var(--panel) !important; }

.badge { display: inline-flex; padding: 6px 10px; border-radius: 999px; font-size: 12px; }
/* Fundo tingido a partir da propria cor semantica, e nao um verde/vermelho
   claro fixo: no tema escuro o #dcfce7 era uma pastilha quase branca com texto
   verde-claro por cima (1,6:1 de contraste, ilegivel). Com color-mix o fundo
   nasce do tom do tema e assenta sobre a superficie de cada um. */
.badge.low { background: color-mix(in srgb, var(--danger) 16%, transparent); color: var(--danger); }
.badge.ok { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }
/* O amarelo morava só no <style> de algumas views (chamados, certificados…),
   cada uma com a MESMA regra copiada — quem não copiava (disputas, compras)
   mostrava o selo neutro, sem cor de aviso. Promovida para cá; as cópias
   locais idênticas continuam válidas e vencem por ordem, sem mudar nada. */
.badge.alerta { background: color-mix(in srgb, var(--aviso) 16%, transparent); color: var(--aviso); }
:root[data-theme="escuro"] .badge.alerta { background: #78350f; color: #fde68a; }

.backup-actions { display: flex; flex-wrap: wrap; gap: 12px; }
.detail-card { display: grid; grid-template-columns: 280px 1fr; gap: 20px; }
.detail-card img { width: 100%; max-height: 280px; object-fit: cover; border-radius: 14px; }

/* Gruda a barra lateral na viewport: em telas com muitos dados a página fica
   longa e o bloco do usuário (com o "Sair") ficava lá no fim do scroll. */
.sidebar { display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; }
/* Rola por dentro quando a navegação não cabe, mas sem barra visível (ela
   polui a lateral escura) — a roda do mouse continua funcionando. */
.sidebar nav { flex: 1; min-height: 0; overflow-y: auto; overflow-x: hidden; scrollbar-width: none; -ms-overflow-style: none; }
.sidebar nav::-webkit-scrollbar { width: 0; height: 0; }
.sidebar-user { flex-shrink: 0; margin-top: 24px; padding-top: 16px; border-top: 1px solid var(--sidebar-borda); display: flex; flex-direction: column; gap: 10px; }
.sidebar-user-info { display: flex; flex-direction: column; gap: 6px; color: var(--sidebar-text); }
/* Perfil do usuário deixa de ser bloco de cor sólida: vira contorno, como as
   demais marcações de identidade (docs/design.md, §1). */
.sidebar-user-info .badge { align-self: flex-start; background: transparent; border: 1px solid var(--sidebar-borda); color: var(--sidebar-muted); }
.sidebar-user-info .badge.ok { background: transparent; border-color: var(--primary); color: var(--primary); font-weight: 600; }
.sidebar-logout { width: 100%; }

.login-body { display: grid; place-items: center; min-height: 100vh; background: var(--bg); }
.login-shell { width: 100%; max-width: 380px; padding: 16px; }
.login-card { display: flex; flex-direction: column; gap: 14px; text-align: center; }
.login-logo { width: 64px; height: 64px; object-fit: contain; margin: 0 auto; }
.login-card h1 { margin: 0; }
.login-card .form-group { text-align: left; }
.login-btn { width: 100%; margin-top: 4px; }
.login-erro { color: var(--danger); font-size: 14px; margin: 0; }

.production-preview { border: 1px solid var(--border); border-radius: 12px; padding: 12px; margin-top: 8px; }
.production-preview .shortage { color: var(--danger); font-weight: 700; }
.production-summary { display: flex; flex-wrap: wrap; gap: 16px; margin-top: 10px; }
.production-summary div { background: var(--primary-soft); color: var(--primary); padding: 8px 12px; border-radius: 10px; font-weight: 600; }
.mov-item { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 10px 12px; border-bottom: 1px solid var(--border); flex-wrap: wrap; }
.mov-item:last-child { border-bottom: none; }
.mov-item small { color: var(--muted); }
.mov-item span { color: var(--muted); font-size: 13px; }
.kv-list { display: flex; flex-direction: column; gap: 8px; }
.kv-item { display: flex; justify-content: space-between; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 10px; }
.kv-item small { color: var(--muted); display: block; }
tr.expandable { cursor: pointer; }
tr.expand-row td { background: var(--primary-soft); padding: 12px 16px; }
/* Empilhado no celular a lateral vira cabeçalho: sem sticky nem 100vh, senão
   ela ocupa a tela inteira antes do conteúdo. */
@media (max-width: 900px) { .app-shell { grid-template-columns: 1fr; } .sidebar { padding-bottom: 12px; position: static; height: auto; } .sidebar nav { overflow-y: visible; } .cards-grid, .form-grid, .filters-grid, .detail-card { grid-template-columns: 1fr; } }
@media (max-width: 640px) { .main-content { padding: 12px; } .topbar { flex-direction: column; align-items: flex-start; } .section-title { flex-direction: column; align-items: flex-start; gap: 8px; } }

.pendencias-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.65); display: flex; align-items: center; justify-content: center; z-index: 9999; padding: 20px; }
.pendencias-modal { width: 100%; max-width: 560px; max-height: 80vh; overflow-y: auto; }
.pendencia-item { margin-bottom: 12px; }
.pendencia-item:last-child { margin-bottom: 0; }

/* Catálogo de Produtos: fichas técnicas, comparativo e lista de materiais. */
.catalogo-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 16px; margin-bottom: 20px; }
/* `display: grid` daqui vence o `[hidden] { display: none }` do navegador: sem
   esta linha, trocar para Comparativo/Materiais/Site mantém as fichas na tela. */
.catalogo-grid[hidden] { display: none; }
/* .cards-grid é fixo em 3 colunas e deixaria o 4º indicador sozinho na linha. */
#catResumo, #midResumo, #catcResumo { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.catalogo-card { background: var(--panel); border-radius: 18px; box-shadow: var(--shadow); overflow: hidden; display: flex; flex-direction: column; transition: transform 0.15s, box-shadow 0.15s; }
.catalogo-card:hover { transform: translateY(-2px); box-shadow: 0 16px 36px rgba(15, 23, 42, 0.14); }
/* Fundo claro fixo (não var(--bg)): as fotos são renders CAD de traço escuro
   com fundo transparente — no tema escuro elas sumiriam. */
.catalogo-foto { height: 200px; flex-shrink: 0; padding: 12px; display: flex; align-items: center; justify-content: center; overflow: hidden; background: var(--panel-alt); border-bottom: 1px solid var(--border); }
/* Limite em px, não 100%: dentro do card (flex em coluna) o percentual não
   resolveu contra a caixa da foto, e a imagem era pintada em cima do texto. */
.catalogo-foto img { max-width: 100%; max-height: 176px; width: auto; height: auto; display: block; }
.catalogo-foto .sem-foto { color: var(--muted); font-size: 13px; }
.catalogo-card-body { padding: 16px; display: flex; flex-direction: column; gap: 10px; flex: 1; }
.catalogo-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
.catalogo-card-head h3 { margin: 0; font-size: 16px; }
.catalogo-card-head small { display: block; margin-top: 2px; font-size: 12px; }
.catalogo-sem-ficha { margin: 0; font-size: 13px; }
.catalogo-acoes { margin-top: auto; padding-top: 4px; display: flex; flex-wrap: wrap; gap: 8px; }
.catalogo-acoes .btn-primary, .catalogo-acoes .btn-secondary { flex: 1 1 auto; font-size: 13px; padding: 8px 12px; }
.ficha-fotos { display: flex; flex-direction: column; gap: 12px; }
.ficha-fotos figure { margin: 0; }
.ficha-fotos figcaption { font-size: 12px; text-align: center; margin-top: 4px; }
.catalogo-card-head .badge { flex-shrink: 0; }
.catalogo-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.catalogo-specs { margin: 0; display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.catalogo-specs div { min-width: 0; }
.catalogo-specs dt { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.catalogo-specs dd { margin: 2px 0 0; font-size: 14px; font-weight: 600; }
.catalogo-card .btn-secondary { margin-top: auto; }
.badge.info { background: var(--primary-soft); color: var(--primary); }
/* O primeiro campo destas tabelas é texto (produto/material), não a miniatura
   de 60px que a regra geral de .data-table assume. */
.table-text-first td:first-child { width: auto; text-align: left; }
.data-table.compact th, .data-table.compact td { padding: 8px 10px; font-size: 13px; }
.table-sticky th:first-child, .table-sticky td:first-child { position: sticky; left: 0; background: var(--panel); z-index: 1; }
.modal-overlay { position: fixed; inset: 0; background: rgba(15, 23, 42, 0.65); display: flex; align-items: center; justify-content: center; z-index: 9999; padding: 20px; }
/* Terceira aparição da mesma armadilha (ver .catalogo-grid e #assistenteGaveta):
   o `display: flex` da linha de cima vence o `[hidden] { display: none }` do
   navegador. As telas da imobiliária marcam o modal com o atributo `hidden` em
   vez de mexer em style.display — sem esta regra, o formulário de cadastro
   nasceria aberto por cima da lista em /imoveis, /pessoas e
   /contratos-locacao, e o botão Fechar não fecharia nada. */
.modal-overlay[hidden] { display: none; }
.modal-card { width: 100%; max-width: 900px; max-height: 88vh; overflow-y: auto; margin: 0; }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; margin-bottom: 16px; }
.modal-head h3 { margin: 0 0 8px; }
.ficha-grid { display: grid; grid-template-columns: 280px 1fr; gap: 24px; align-items: start; }
.ficha-foto { display: block; margin: 0 auto; max-width: 100%; max-height: 260px; width: auto; height: auto; border-radius: 14px; background: #f1f5f9; padding: 8px; }
.ficha-grupo { margin-top: 18px; }
.ficha-grupo:first-child { margin-top: 0; }
.ficha-grupo h4 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
@media (max-width: 900px) { .ficha-grid { grid-template-columns: 1fr; } }

/* Edição do catálogo (só Produção vê). O formulário é longo por natureza — a
   ficha tem 19 campos técnicos, mais BOM — então cada bloco reusa .ficha-grupo
   e .form-grid, e só o que é próprio da edição ganha regra aqui. */
.cat-ed-dica { margin: 0 0 8px; font-size: 12px; }
.cat-ed-foto { display: grid; grid-template-columns: 96px 1fr; gap: 12px; align-items: center; padding: 10px; border: 1px solid var(--border); border-radius: 12px; margin-bottom: 10px; }
/* Fundo claro fixo pelo mesmo motivo de .catalogo-foto: render CAD de traço
   escuro em fundo transparente sumiria no tema escuro. */
.cat-ed-foto img { width: 96px; height: 96px; object-fit: contain; background: var(--panel-alt); border-radius: 8px; }
.cat-ed-foto-info { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.cat-ed-foto-acoes { grid-column: 2; display: flex; flex-wrap: wrap; gap: 6px; }
.cat-ed-foto-acoes .btn-secondary { font-size: 12px; padding: 5px 10px; }
.cat-ed-envio { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-top: 10px; }
.cat-ed-envio input[type="text"] { flex: 1 1 200px; }
.cat-ed-envio input[type="file"] { flex: 1 1 220px; }
.cat-ed-linha { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.cat-ed-linha input { flex: 1 1 140px; }
.cat-ed-linha .btn-secondary { font-size: 12px; padding: 6px 12px; }
.cat-ed-add { font-size: 13px; padding: 6px 12px; }
.cat-ed-rodape { margin-top: 22px; }
@media (max-width: 600px) {
  .cat-ed-foto { grid-template-columns: 1fr; }
  .cat-ed-foto-acoes { grid-column: 1; }
}

/* Páginas de conteúdo: mídia, catálogos comerciais, serviços e marca. */
.conteudo-lista { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; }
.conteudo-lista li { line-height: 1.45; }
.conteudo-resumo { margin: 0; font-size: 13px; line-height: 1.45; }
.conteudo-caminho { margin: 16px 0 0; font-size: 12px; }
.conteudo-caminho code { background: var(--primary-soft); color: var(--primary); padding: 2px 6px; border-radius: 6px; }
.avisos-lista { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 10px; }
.avisos-lista li { line-height: 1.45; }
.ficha-subtitulo { margin: 16px 0 6px; font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--muted); }
/* Mosaico de 4 miniaturas no card da pasta: dá para reconhecer a pasta sem abrir. */
.midia-mosaico { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; height: 200px; flex-shrink: 0; background: #f1f5f9; border-bottom: 1px solid var(--border); overflow: hidden; }
.midia-mosaico img { width: 100%; height: 100%; object-fit: cover; display: block; }
.midia-mosaico img:only-child { grid-column: 1 / -1; object-fit: contain; padding: 8px; }
.midia-mosaico.vazio { display: grid; place-items: center; grid-template-columns: 1fr; }
.midia-galeria { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 14px; margin-bottom: 8px; }
.midia-galeria figure { margin: 0; display: flex; flex-direction: column; gap: 6px; }
.midia-galeria img { width: 100%; height: 130px; object-fit: contain; background: #f1f5f9; border-radius: 10px; padding: 6px; }
.midia-galeria figcaption { display: flex; flex-direction: column; gap: 2px; font-size: 12px; word-break: break-word; }
.midia-galeria figcaption strong { font-weight: 600; }
/* O card do serviço é uma foto quadrada pensada para preencher o espaço —
   diferente do render de produto, que precisa de respiro em volta. */
.servico-foto { height: 220px; padding: 0; }
.servico-foto img { width: 100%; height: 100%; max-height: none; object-fit: cover; }
.btn-primary:disabled, .btn-secondary:disabled { opacity: 0.45; cursor: not-allowed; filter: none; }
/* Desabilitado não pinta no hover: antes ele voltava ao fundo sólido do
   primário e o botão parecia clicável justamente quando não era. */
.btn-primary:disabled:hover, .btn-secondary:disabled:hover { background: transparent; }
.servico-banner { width: 100%; border-radius: 12px; display: block; margin-bottom: 8px; }
.servico-hero { max-width: 100%; max-height: 320px; display: block; margin: 0 auto; }
/* O logo é branco puro sobre transparente: em fundo claro ele some. */
.marca-logos { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 16px; }
.marca-logo { margin: 0; background: #111827; border-radius: 14px; padding: 16px; display: flex; flex-direction: column; align-items: center; gap: 10px; }
.marca-logo img { max-width: 100%; max-height: 160px; }
.marca-logo figcaption { color: #d1d5db; font-size: 12px; text-align: center; word-break: break-word; }
.marca-banners { display: flex; flex-direction: column; gap: 16px; }
.marca-banners figure { margin: 0; }
.marca-banners img { width: 100%; border-radius: 12px; display: block; }
.marca-banners figcaption { font-size: 12px; margin-top: 6px; }
.marca-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; }
.marca-card { margin: 0; display: flex; flex-direction: column; gap: 8px; }
.marca-card img { width: 100%; border-radius: 12px; background: #f1f5f9; display: block; }
.marca-card figcaption { display: flex; flex-direction: column; gap: 2px; font-size: 12px; word-break: break-word; }
.marca-card .sem-produto { color: var(--danger); }

/* ==========================================================================
   Sino de notificações
   O botão vive na barra do topo de toda página (montado por app.js). O painel
   é ancorado nele e não num canto fixo da tela: aberto, o olho continua no
   mesmo lugar onde clicou.
   ========================================================================== */
/* Envelope e sino ficam na mesma caixa (e não soltos na barra) para não se
   separarem quando a tela ganha outros botões de ação. O painel é ancorado
   nesta caixa e o envelope está à esquerda do sino: assim a lista continua
   alinhada pela direita, embaixo do sino, como antes. */
.sino-caixa {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.4rem;
}
/* Botão de ícone da barra superior (sino, mensagem, tema): outline, ícone SVG
   herdando a cor do texto. Era emoji — que muda de desenho e de cor a cada
   sistema operacional, e não acompanha o tema. */
.sino-botao {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: transparent;
  color: var(--text);
  border: 1px solid var(--border, #d6dae2);
  border-radius: 10px;
  padding: 0.4rem 0.55rem;
  cursor: pointer;
  line-height: 1;
}
.sino-botao:hover {
  border-color: var(--primary, #2563eb);
}
/* O contador é o único elemento que precisa gritar: é ele que faz alguém
   abrir o sino. Vermelho e sobreposto, como em qualquer app de mensagem. */
.sino-contador {
  position: absolute;
  top: -6px;
  right: -6px;
  background: #dc2626;
  color: #fff;
  border-radius: 999px;
  font-size: 0.68rem;
  font-weight: 700;
  min-width: 1.15rem;
  padding: 0.05rem 0.28rem;
  text-align: center;
}
.sino-painel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: min(380px, 92vw);
  max-height: 70vh;
  overflow-y: auto;
  /* --panel, e não a antiga --card-bg: essa variável nunca existiu em nenhum
     tema, então o painel caía sempre no #fff do fallback — no tema escuro isso
     era texto claro sobre fundo branco. */
  background: var(--panel, #fff);
  border: 1px solid var(--border, #d6dae2);
  border-radius: 12px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.16);
  z-index: 50;
}
.sino-topo {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.5rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border, #d6dae2);
  position: sticky;
  top: 0;
  /* Cor explícita, não `inherit`: o valor computado até era branco, mas fundo
     de barra fixa não pode depender do que o pai resolveu. */
  background: var(--panel, #fff);
  /* O z-index é o que segura a barra. Sem ele o texto dos avisos era pintado
     POR CIMA do fundo opaco do cabeçalho ao rolar a lista, e "Notificações"
     ficava embolado com a linha do aviso. */
  z-index: 1;
}
.sino-item {
  display: block;
  padding: 0.65rem 0.9rem;
  border-bottom: 1px solid var(--border, #eceff3);
  color: inherit;
  text-decoration: none;
}
a.sino-item:hover,
.sino-item-abrivel:hover {
  background: rgba(37, 99, 235, 0.06);
}
/* Recado sem link abre a janela grande: o cursor precisa dizer isso antes do
   clique, e o foco por teclado precisa ser visível (o item é role="button"). */
.sino-item-abrivel {
  cursor: pointer;
}
.sino-item-abrivel:focus-visible {
  outline: 2px solid var(--primary, #2563eb);
  outline-offset: -2px;
}
/* Caixa de sugestões: botão flutuante, presente em toda tela. Fica ABAIXO do
   modal (z-index 9999) e da gaveta do assistente (1200) de propósito — ele
   nunca pode cobrir o que abriu por cima dele. */
.sugestao-fab {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 900;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  display: grid;
  place-items: center;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  box-shadow: var(--shadow);
  cursor: pointer;
}
.sugestao-fab:hover {
  color: var(--primary);
  border-color: var(--primary);
}
.sugestao-fab:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}
/* Em tela estreita ele sobe: no rodapé do celular disputaria espaço com a
   barra do navegador. */
@media (max-width: 700px) {
  .sugestao-fab {
    bottom: 72px;
  }
}

/* Na janela o texto vem do banco com as quebras que quem escreveu digitou —
   sem isto a resposta do assistente vira um parágrafo só. */
.sino-modal-corpo {
  white-space: pre-wrap;
  /* anywhere e não break-word: mensagem colada sem espaço (código, URL,
     teste de teclado) estourava o modal com rolagem horizontal. */
  overflow-wrap: anywhere;
  line-height: 1.55;
  margin-top: 0.75rem;
}
/* Não lida ganha uma barra à esquerda em vez de fundo colorido: com dez
   avisos na lista, dez fundos coloridos deixam de distinguir qualquer coisa. */
.sino-item.nao-lida {
  border-left: 3px solid var(--primary, #2563eb);
  padding-left: calc(0.9rem - 3px);
}
.sino-cabeca {
  display: flex;
  gap: 0.45rem;
  align-items: baseline;
  font-size: 0.92rem;
}
.sino-corpo {
  font-size: 0.85rem;
  opacity: 0.85;
  margin-top: 0.2rem;
  overflow-wrap: anywhere;
}
.sino-meta {
  font-size: 0.74rem;
  opacity: 0.6;
  margin-top: 0.25rem;
}
.sino-modal-fundo {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 1rem;
}
.sino-modal {
  width: min(520px, 100%);
  display: grid;
  gap: 0.7rem;
}

/* Acessibilidade: quem pediu menos movimento no sistema operacional recebe a
   interface sem transição nem animação. As transições daqui são curtas e
   funcionais (chevron da sanfona, card do catálogo), mas para quem tem
   sensibilidade vestibular movimento nenhum é a única régua que serve. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* Marca do item no sino: a cor diz o que é (urgente, atenção, recado) — este é
   um dos lugares onde cor é informação, e não enfeite de interface. */
.sino-marca { display: inline-flex; align-items: center; }
.sino-marca.urgente { color: var(--danger); }
.sino-marca.atencao { color: var(--aviso); }
.sino-marca.recado { color: var(--primary); }
.sino-cabeca { display: flex; align-items: center; gap: 7px; }

/* ===================================================================
   Barra superior fixa (appbar) — o que existe em TODA tela
   ===================================================================
   Não confundir com `.topbar`, que é o cabeçalho de cada página (título,
   subtítulo e os botões daquela tela) e rola junto com o conteúdo. A appbar
   fica parada no topo e carrega só o que independe de tela: recolher a
   navegação, tema, mensagem, sino e o usuário.

   O gradiente é o mesmo da lateral em outro ângulo (274° contra 87°), como
   pede o documento: as duas superfícies se encaixam pelo tom, sem divisória
   pesada, e o conteúdo quase-branco flutua sobre elas. */
:root { --appbar-h: 52px; }
/* Fixa e recuada: a lateral sobe até o topo da janela e a barra ocupa só a
   largura restante. `fixed` (e não `sticky`) porque ela precisa sair do fluxo
   para a lateral poder começar em y=0 — o espaço dela é devolvido pelo
   padding-top do conteúdo, logo abaixo. */
.appbar {
  position: fixed;
  top: 0;
  left: var(--nav-w);
  right: 0;
  z-index: 60;
  height: var(--appbar-h);
  display: flex;
  align-items: center;
  /* Só as ações moram aqui agora (a marca voltou para a lateral): elas ficam
     encostadas à direita, longe do conteúdo da tela. */
  justify-content: flex-end;
  gap: 12px;
  padding: 0 14px;
  background: linear-gradient(274deg, var(--superficie-a), var(--superficie-b));
  border-bottom: 1px solid var(--sidebar-borda);
}
/* `flex: 0 0 auto` explícito: com a frase do tema rosa ao lado, é o que
   garante que quem encolhe é o texto, nunca a fileira de botões. */
.appbar-acoes { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }

/* Frase da barra: existe em toda tela, mas SÓ aparece no tema rosa. O gate é
   aqui, e não no JS, porque o `data-theme` já está no <html> antes da primeira
   pintura — decidir isso depois faria a frase piscar na tela de quem não a
   escolheu. */
.appbar-frase {
  display: none;
  /* Empurra as ações para a direita e cede espaço primeiro: min-width 0 é o
     que permite o texto encolher dentro do flex em vez de espremer os botões. */
  margin-right: auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Quem impede a quebra de linha é o `nowrap` — a barra tem altura fixa
     (--appbar-h), de onde saem o padding do conteúdo e o topo da gaveta do
     assistente, então a frase não a estica de qualquer forma.
     `line-height: 1` era estrago: a IBM Plex Sans ocupa 1,3em de área de
     conteúdo, então a caixa de 13px ficava menor que a letra e o `overflow:
     hidden` raspava o pé do "j", do "p" e do cedilha — em itálico, pior. */
  white-space: nowrap;
  line-height: 1.4;
  font-size: 13px;
  font-style: italic;
  color: var(--institucional);
  opacity: 0.75;
}
:root[data-theme="rosa"] .appbar-frase { display: block; }
/* A rosa não pega cor daqui: ela é ilustração e carrega o próprio vermelho em
   cada pétala (ver ICONES_ILUSTRADOS em public/js/app.js). Pintar o botão com
   `color` não teria efeito nenhum sobre ela. */
/* O botão perde a moldura: a flor cheia é desenho, e o retângulo em volta competia
   com ela. `transparent`, e não `border: none` — tirar a borda encolheria o
   botão em 2px e desalinharia a fileira inteira, que é o que o teste guarda.
   O :hover precisa vir junto, senão a moldura reaparece ao passar o mouse. */
:root[data-theme="rosa"] #temaBotao,
:root[data-theme="rosa"] #temaBotao:hover { border-color: transparent; }
/* Tela estreita: a barra continua recuada pela lateral e sobram cinco botões.
   A frase é a primeira a sair — ela é o que menos custa perder. */
@media (max-width: 760px) {
  .appbar-frase { display: none !important; }
}
.appbar-recolher {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  background: transparent;
  color: var(--institucional);
  border: 1px solid transparent;
  border-radius: 9px;
  cursor: pointer;
}
.appbar-recolher:hover { background: var(--sidebar-hover); }

/* A lateral vai do topo ao rodapé da janela, com a marca no alto — é ela que
   recolhe e expande a navegação. */
/* O z-index é da LATERAL, não do painel que abre dentro dela. `position:
   sticky` já cria contexto de empilhamento, e sem um valor explícito ele entra
   como `auto`: o painel da navegação recolhida ficava preso nesse contexto e o
   conteúdo da página (o rótulo dos cartões do dashboard, por exemplo) era
   desenhado POR CIMA dele — o painel parecia transparente. 65 fica acima do
   conteúdo e abaixo dos modais (9999).

   Só o z-index mora aqui: `top`/`height` ficam na regra base (~355), porque
   esta linha vem DEPOIS da media query de 900px e com a mesma especificidade
   — repeti-los aqui anulava o `height: auto` que empilha a lateral em tela
   estreita. */
.sidebar { z-index: 65; }
/* O conteúdo devolve a altura que a barra fixa tirou do fluxo. */
.main-content { padding-top: calc(var(--appbar-h) + 24px); }
/* A marca virou o botão de recolher: precisa parecer clicável e ter alvo de
   toque decente, sem virar um <button> (o HTML dela está nas dezoito views). */
.brand { cursor: pointer; border-radius: 12px; padding: 6px; margin: -6px -6px 10px; }
.brand:hover { background: var(--sidebar-hover); }
.brand:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* Botão de comprimir/ampliar a navegação, logo abaixo dos grupos. Fica na
   discrição dos rótulos de seção (mesma cor apagada) porque é comando de
   moldura, não destino: só ganha contraste quando a pessoa passa por cima. */
.nav-recolher {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  margin-top: 4px;
  padding: 9px 12px;
  background: none;
  border: 1px solid transparent;
  border-radius: 10px;
  cursor: pointer;
  color: var(--sidebar-muted);
  font-family: var(--fonte);
  font-size: 12px;
  font-weight: 600;
  text-align: left;
  white-space: nowrap;
  overflow: hidden;
}
.nav-recolher:hover { background: var(--sidebar-hover); color: var(--sidebar-text); }
.nav-recolher:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* A seta aponta para onde o clique leva: para dentro quando dá para comprimir,
   para fora quando dá para ampliar. */
.nav-recolher-seta { font-size: 13px; line-height: 1; transition: transform 0.15s ease; }

/* ===== Navegação recolhida =====
   64px, só os ícones de categoria. Os links de dentro passam a aparecer num
   painel ao lado, ao passar o mouse OU ao receber foco pelo teclado — sem o
   `:focus-within` a navegação ficaria inalcançável para quem não usa mouse. */
:root.nav-recolhida { --nav-w: 64px; }
:root.nav-recolhida .sidebar { padding: 16px 10px; }
:root.nav-recolhida .brand { justify-content: center; }
:root.nav-recolhida .brand h1,
:root.nav-recolhida .nav-section-nome,
:root.nav-recolhida .nav-section-chevron,
:root.nav-recolhida .sidebar nav a span,
:root.nav-recolhida .sidebar-user-info { display: none; }
:root.nav-recolhida .nav-section-label { justify-content: center; padding: 10px 0; }
/* Recolhida, o link solto vira ícone centralizado como os rótulos de seção — o
   nome dele passa a vir da tooltip (data-dica). */
:root.nav-recolhida .nav-solo { justify-content: center; padding: 10px 0; }
:root.nav-recolhida .nav-section { position: relative; }
:root.nav-recolhida .nav-links {
  display: none;
  position: absolute;
  left: calc(100% + 6px);
  top: 0;
  z-index: 70;
  min-width: 210px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow);
  /* Recolhida, os links viram um painel flutuante: a linha-guia e o recuo que
     aninham os itens na barra aberta empurrariam esse painel para o lado. */
  margin-left: 0;
}
:root.nav-recolhida .nav-section:hover .nav-links,
:root.nav-recolhida .nav-section:focus-within .nav-links { display: block; }
/* Só UM painel por vez. O `:focus-within` acima existe para o teclado, mas ele
   segurava aberto o painel da seção que tinha o foco enquanto o mouse abria o
   da seção seguinte — e como os dois nascem no `top: 0` da própria seção, um
   caía por cima do outro (o rótulo de uma categoria aparecia dentro do painel
   da outra). Com o mouse dentro da navegação, quem manda é o hover. */
:root.nav-recolhida .sidebar nav:hover .nav-section:not(:hover) .nav-links { display: none; }
/* A navegação rola por dentro (overflow), e isso RECORTAVA o painel: ele nascia
   fora da caixa e simplesmente não aparecia. Recolhida são sete ícones, que
   cabem sem rolagem — então o overflow pode ser liberado justamente aqui. */
:root.nav-recolhida .sidebar nav { overflow: visible; }
/* Dentro do painel o texto volta: é justamente ele que a barra estreita tirou.
   O seletor precisa ser mais específico que o que esconde os rótulos acima
   (`.sidebar nav a span`), senão o painel abre só com ícones — que é
   exatamente o problema que ele existe para resolver. */
:root.nav-recolhida .sidebar nav .nav-links a span { display: inline; }
/* Sem o rótulo da categoria, o painel não diz de onde saiu. */
:root.nav-recolhida .nav-links::before {
  content: attr(data-secao);
  display: block;
  padding: 4px 10px 8px;
  margin-bottom: 4px;
  border-bottom: 1px solid var(--border);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--muted);
}
:root.nav-recolhida .sidebar nav .nav-section a { padding-left: 10px; }
:root.nav-recolhida .sidebar-logout { padding: 8px 0; }
/* Recolhida, sobra a seta — virada para fora, porque agora o clique amplia. */
:root.nav-recolhida .nav-recolher { justify-content: center; padding: 10px 0; }
:root.nav-recolhida .nav-recolher-texto { display: none; }
:root.nav-recolhida .nav-recolher-seta { transform: rotate(180deg); }
/* Recolhida, a sanfona não manda mais: quem mostra os links é o painel. */
:root:not(.nav-recolhida) .nav-section:not(.open) .nav-links { display: none; }
/* Recolhida, o painel flutuante já É o conteúdo aberto da seção: manter a
   sanfona de dentro fechada obrigaria a clicar dentro de um painel que só
   existe enquanto o mouse está em cima. Sem chevron, porque não há o que
   abrir. */
:root.nav-recolhida .nav-subsection .nav-sublinks { display: flex; }
:root.nav-recolhida .nav-subsection-chevron { display: none; }
:root.nav-recolhida .nav-subsection-label { cursor: default; padding: 6px 10px 2px; }
/* Dentro do painel o fundo é --panel, e não o da barra: os tons de sidebar
   ficariam fora de tom aí (o ::before do painel já usa --muted por isso). */
:root.nav-recolhida .nav-subsection-label { color: var(--muted); }
:root.nav-recolhida .nav-subsection-label:hover { color: var(--muted); opacity: 0.9; }
:root.nav-recolhida .nav-subsection.atual > .nav-subsection-label { color: var(--text); opacity: 1; }
:root.nav-recolhida .nav-sublinks { border-left-color: var(--border); }
/* O nível a mais come largura do rótulo: sem esta folga, "Equipamentos e
   certificados" cortava com reticências dentro do painel. */
:root.nav-recolhida .nav-section-grupo .nav-links { min-width: 244px; }

/* ===== Assistente (gaveta lateral) =====
   SOBREPOSTA, nunca empurrando o conteúdo: as telas deste sistema são densas
   (o quadro do Jurídico rola na horizontal) e uma gaveta que espreme a página
   quebraria exatamente onde ela mais serve. Esc fecha, como os modais. */
#assistenteGaveta {
  position: fixed;
  top: var(--appbar-h);
  right: 0;
  bottom: 0;
  width: min(400px, 92vw);
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-left: 1px solid var(--border);
  box-shadow: -12px 0 30px rgba(11, 20, 64, 0.12);
  z-index: 1200;
}
/* O display:flex de cima vence o [hidden] do navegador (mesma armadilha do
   .catalogo-grid, linha ~310): sem esta regra a gaveta NUNCA fechava — o ✕ e
   o Esc mudavam o estado, e ela continuava pintada na tela. */
#assistenteGaveta[hidden] { display: none; }
.assistente-topo {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--border);
}
/* A âncora diz a que a conversa está presa — sem ela, uma pergunta geral com
   resposta contextual pareceria o assistente respondendo errado. */
.assistente-ancora {
  flex: 1;
  font-size: 11px;
  color: var(--muted);
  font-family: var(--fonte-mono);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* Seletor de modelo, compacto para caber no topo estreito da gaveta. */
.assistente-modelo {
  font-size: 12px;
  padding: 3px 4px;
  max-width: 108px;
  background: var(--bg);
  color: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
}
.assistente-corpo { flex: 1; overflow-y: auto; padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.assistente-vazio { color: var(--muted); font-size: 13px; padding: 8px 4px; }
.assistente-fala { max-width: 92%; border-radius: 12px; padding: 8px 11px; font-size: 13.5px; line-height: 1.45; }
.assistente-fala p { margin: 0 0 6px; }
.assistente-fala p:last-child { margin-bottom: 0; }
.assistente-fala.usuario { align-self: flex-end; background: var(--primary-soft); }
.assistente-fala.bot { align-self: flex-start; background: var(--bg); border: 1px solid var(--border); }
/* Palpite pintado diferente do fato: a dedução do modelo nunca pode se passar
   por registro do sistema — é a regra nº 2 do services/assistente.js na tela. */
.assistente-palpite {
  color: var(--aviso);
  border-left: 3px solid var(--aviso);
  padding-left: 8px;
  font-style: italic;
}
.assistente-consultas { margin-top: 6px; font-size: 11px; color: var(--muted); font-family: var(--fonte-mono); }
.assistente-espera { color: var(--muted); font-style: italic; }
/* A resposta pode trazer código (nome de kit, SQL citado) e lista — o texto
   plano continua sendo a regra, mas quando vem, tem que ler bem no escuro
   da gaveta estreita. */
.assistente-fala code {
  font-family: var(--fonte-mono);
  font-size: 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 4px;
}
.assistente-fala ul { margin: 6px 0; padding-left: 18px; }
.assistente-fala li { margin: 2px 0; }
.assistente-envio { display: flex; gap: 8px; padding: 10px 12px; border-top: 1px solid var(--border); }
.assistente-envio textarea { resize: none; font-size: 13.5px; }
.assistente-envio .btn { align-self: flex-end; }

/* ===== Usuário na barra superior ===== */
.usuario-caixa { position: relative; display: flex; align-items: center; }
.usuario-botao {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  padding: 0;
  overflow: hidden;
  background: transparent;
  color: var(--institucional);
  /* Círculo em outline com a inicial (docs/design.md, §6). A foto, quando
     existe, preenche o mesmo círculo — o contorno permanece. */
  border: 1.5px solid var(--institucional);
  border-radius: 999px;
  cursor: pointer;
  font-weight: 600;
  font-size: 13px;
  line-height: 1;
}
.usuario-botao img { width: 100%; height: 100%; object-fit: cover; display: block; }
.usuario-menu {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 80;
  min-width: 210px;
  padding: 6px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 12px;
  box-shadow: var(--shadow);
}
.usuario-menu-cabeca { padding: 8px 10px 10px; border-bottom: 1px solid var(--border); margin-bottom: 6px; }
.usuario-menu-cabeca strong { display: block; }
.usuario-menu-cabeca span { color: var(--muted); font-size: 0.85em; }
.usuario-menu a, .usuario-menu button {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 9px 10px;
  background: none;
  border: 0;
  border-radius: 9px;
  text-align: left;
  color: var(--text);
  cursor: pointer;
}
.usuario-menu a:hover, .usuario-menu button:hover { background: var(--primary-soft); }

/* ===== Meu perfil ===== */
.perfil-grid { display: grid; grid-template-columns: minmax(240px, 320px) 1fr; gap: 20px; align-items: start; }
.perfil-foto { display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center; }
.perfil-foto-circulo {
  width: 132px;
  height: 132px;
  border-radius: 999px;
  border: 1.5px solid var(--institucional);
  display: grid;
  place-items: center;
  overflow: hidden;
  font-size: 44px;
  font-weight: 600;
  color: var(--institucional);
}
.perfil-foto-circulo img { width: 100%; height: 100%; object-fit: cover; }
@media (max-width: 760px) { .perfil-grid { grid-template-columns: 1fr; } }
/* Aviso de erro em formulário: existia só como cor solta nas telas, cada uma
   escrevendo a sua. Agora é uma classe, e ela segue o tema. */
.danger { color: var(--danger); }

/* ===================================================================
   Espera visível (docs/design.md, §7)
   ===================================================================
   Nenhum processo demorado sem sinal. As três peças abaixo são as mesmas em
   toda tela: o rótulo diz O QUE está acontecendo, e não só que "algo" está. */

/* Spinner com rótulo, no lugar exato onde o resultado vai nascer. */
.aguardando { display: inline-flex; align-items: center; gap: 9px; color: var(--muted); }
.aguardando::before {
  content: "";
  width: 15px;
  height: 15px;
  flex-shrink: 0;
  border: 2px solid var(--border);
  border-top-color: var(--primary);
  border-radius: 999px;
  animation: girar 0.7s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }
/* Bloco de espera onde o conteúdo ainda vai aparecer (lista, tabela, gráfico). */
.aguardando-bloco { display: flex; align-items: center; justify-content: center; gap: 10px; padding: 28px 12px; color: var(--muted); }

/* Aviso de que vai demorar, exibido JUNTO do botão — antes da espera, não
   depois. É o que evita o segundo clique e a impressão de travamento. */
.aviso-espera { display: block; margin-top: 6px; color: var(--muted); font-size: 0.86em; }

/* Progresso de operação longa. Sem valor conhecido, vira barra indeterminada:
   ainda diz "está andando", que é a informação que falta. */
.progresso { height: 6px; border-radius: 999px; background: var(--border); overflow: hidden; margin-top: 8px; }
.progresso > span { display: block; height: 100%; width: 0; border-radius: 999px; background: var(--primary); transition: width 0.25s ease; }
.progresso.indeterminado > span { width: 35%; animation: correr 1.1s ease-in-out infinite; }
@keyframes correr {
  0% { margin-left: -35%; }
  100% { margin-left: 100%; }
}

/* Tooltip com 300ms de atraso: sem o atraso ela pisca quando o mouse só passa
   por cima a caminho de outra coisa. O atraso vive no JS (dica()), porque o
   elemento só entra no DOM quando a espera termina. */
.dica {
  position: fixed;
  z-index: 200;
  max-width: 260px;
  padding: 6px 9px;
  border-radius: 8px;
  border: 1px solid var(--institucional);
  background: var(--panel);
  color: var(--text);
  font-size: 0.82rem;
  line-height: 1.35;
  box-shadow: var(--shadow);
  pointer-events: none;
}

/* ===== Cofre de senhas =====
   O segredo revelado é o único texto do sistema em fonte monoespaçada por
   motivo de LEITURA: quem digita a senha em outro lugar precisa distinguir
   O de 0 e l de 1. */
.input-com-botao { display: flex; gap: 8px; align-items: center; }
.input-com-botao input { flex: 1; min-width: 0; }
.cofre-item { border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; margin-bottom: 10px; }
.cofre-item header { display: flex; flex-wrap: wrap; gap: 8px; align-items: baseline; justify-content: space-between; }
.cofre-item h4 { margin: 0; font-size: 1rem; }
.cofre-item .cofre-meta { color: var(--muted); font-size: 0.85rem; display: flex; flex-wrap: wrap; gap: 4px 12px; margin: 6px 0 0; }
.cofre-acoes { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.cofre-segredo {
  margin-top: 10px;
  padding: 10px 12px;
  border: 1px dashed var(--institucional);
  border-radius: 8px;
  font-family: ui-monospace, "IBM Plex Mono", Consolas, monospace;
  word-break: break-all;
  user-select: all;
}
.cofre-segredo .cofre-obs { display: block; margin-top: 8px; font-family: inherit; color: var(--muted); white-space: pre-wrap; word-break: break-word; }

/* ===== Índice de seções de uma tela comprida =====
   Fileira de atalhos para as âncoras da própria página. Fica GRUDADA logo
   abaixo da appbar (que é fixa, --appbar-h) porque índice que rola junto só
   serve na primeira tela — e o problema que ele resolve aparece justamente
   depois de rolar. O fundo é opaco de propósito: o conteúdo passa por baixo.
   O alvo da âncora precisa de scroll-margin-top pelo mesmo motivo: sem ele o
   salto para o título da seção embaixo da barra, e ela some. */
.indice-secoes {
  position: sticky;
  top: var(--appbar-h);
  /* Acima do conteúdo da página e abaixo da lateral (65) e dos modais. */
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0 0 18px;
  padding: 10px 0;
  background: var(--bg);
}
.indice-secoes a {
  padding: 6px 13px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  text-decoration: none;
  white-space: nowrap;
}
.indice-secoes a:hover { border-color: var(--primary); color: var(--primary); }
.indice-secoes a:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }
/* Seção em que a rolagem está. Quem marca é o JS (ativarIndiceSecoes). */
.indice-secoes a.ativo { background: var(--primary); border-color: var(--primary); color: var(--panel); }
/* appbar + a altura da própria barra de índice, com folga para o título não
   encostar nela. O valor conversa com a linha de corte do JS
   (ativarIndiceSecoes): o alvo tem de parar ACIMA do corte, senão clicar no
   atalho leva à seção e acende a anterior. */
.indice-alvo { scroll-margin-top: calc(var(--appbar-h) + 60px); }
/* Em tela estreita os quatro atalhos passam para duas linhas e a barra
   dobra de altura — o alvo tem de descer junto, senão o título da seção
   para embaixo dela. */
@media (max-width: 640px) {
  .indice-alvo { scroll-margin-top: calc(var(--appbar-h) + 104px); }
}

/* ===== Dispositivos: o conjunto (master + slaves) =====
   A planilha que a tela substituiu mantinha o conjunto numa faixa de cor só. A
   primeira versão daqui tentou o mesmo dentro de uma tabela, com borda e recuo
   — e em 146 aparelhos a divisão de 2px entre um conjunto e o próximo some.
   Por isso o conjunto virou CARTÃO: a caixa é a fronteira, e não uma linha.
   A cor continua sendo REFORÇO, não a informação: quem não a distingue lê o
   selo "Master", o ID em caixa própria e a contagem de slaves no topo. */
/* Grade de ladrilhos, ~5 por linha em tela cheia (o minmax decide sozinho
   quantos cabem, então em tela estreita vira 4, 3, 2 e por fim 1). Fechado o
   cartão é um ladrilho; ABERTO ele toma a linha inteira, porque a tabela de
   slaves tem até 12 colunas e não cabe num quinto da largura. É a grade que
   faz isso, sem JS: `grid-column: 1 / -1`. */
.disp-lista { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px; align-items: start; }
.disp-cartao[open] { grid-column: 1 / -1; }
.disp-cartao { border: 1px solid var(--border); border-left: 4px solid var(--primary); border-radius: 12px; background: var(--panel); overflow: hidden; }
/* O cartão é um <details> fechado. `list-style: none` + o pseudo do WebKit
   tiram o triângulo padrão do <summary>, que aparece FORA do flex e desalinha
   a linha inteira; a seta própria (.disp-seta) entra no lugar, dentro do flex. */
/* Fechado o cabeçalho é uma COLUNA: em 260px de ladrilho, o
   `justify-content: space-between` de uma linha só empurraria cliente e
   entrega para longe do título e deixaria a quebra ragged. Aberto, o cartão
   tem a largura toda e o cabeçalho volta a ser uma linha. */
.disp-cartao-topo { display: flex; flex-direction: column; align-items: flex-start; gap: 6px; padding: 12px 14px; background: var(--primary-soft); border-bottom: 1px solid var(--border); cursor: pointer; list-style: none; user-select: none; }
.disp-cartao[open] .disp-cartao-topo { flex-direction: row; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px; padding: 12px 16px; }
.disp-cartao-topo::-webkit-details-marker { display: none; }
.disp-cartao-topo:hover { background: color-mix(in srgb, var(--primary) 14%, transparent); }
.disp-cartao-topo:focus-visible { outline: 2px solid var(--primary); outline-offset: -2px; }
/* Fechado o cartão não tem borda embaixo do cabeçalho: ela desenharia uma
   segunda linha logo acima da borda do próprio cartão. */
.disp-cartao:not([open]) .disp-cartao-topo { border-bottom: none; }
.disp-seta { width: 0; height: 0; border-left: 6px solid currentColor; border-top: 5px solid transparent; border-bottom: 5px solid transparent; opacity: 0.55; transition: transform 0.15s ease; }
.disp-cartao[open] .disp-seta { transform: rotate(90deg); }
.disp-cartao-titulo { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; font-size: 14px; }
/* A série é o que se procura no ladrilho: ganha a linha inteira dele para não
   quebrar no meio do número. */
.disp-cartao:not([open]) .disp-cartao-titulo > strong { flex-basis: 100%; font-size: 15px; }
.disp-cartao-selos { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; font-size: 13px; }
/* O ID é o 4º octeto do MAC do master, e é ele que amarra o conjunto: ganha
   peso de identificador (mono, caixa própria) e não de mais um texto. */
/* Texto em --panel, e não #fff: no tema escuro o --primary clareia, e branco
   sobre ele fica sem contraste. --panel acompanha o tema nos dois sentidos. */
.disp-id { font-family: var(--fonte-mono); font-weight: 700; padding: 2px 8px; border-radius: 6px; background: var(--primary); color: var(--panel); font-size: 12px; }
.disp-cliente { font-weight: 600; }
.disp-cartao-master { padding: 12px 16px; border-bottom: 1px solid var(--border); }
.disp-cartao-chaves { display: flex; flex-wrap: wrap; align-items: baseline; gap: 12px; margin-bottom: 8px; }
.disp-cartao-nota { padding: 0 16px 12px; }
/* Rótulo em cima do valor: os rótulos têm larguras muito diferentes ("Nº" ×
   "Comando de pareamento") e em duas colunas o valor ficava longe do nome.
   O par é UM filho do grid (o <div> que o JS emite): cada dt/dd solto viraria
   uma célula, e o rótulo terminaria numa coluna com o valor na seguinte. */
.disp-campos { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 12px 16px; margin: 0; }
.disp-campos dt { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }
.disp-campos dd { margin: 2px 0 0; word-break: break-word; }
.disp-slaves-wrap { overflow-x: auto; }
.disp-slaves { margin: 0; }
.disp-slaves th { font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; }
.disp-slaves td, .disp-slaves th { white-space: nowrap; }
/* .data-table reserva 60px centrados na primeira coluna para a miniatura do
   produto; aqui a primeira coluna é a série do aparelho, texto. */
.disp-slaves td:first-child { width: auto; text-align: left; font-family: var(--fonte-mono); }
.disp-slave-divergente td { background: color-mix(in srgb, var(--aviso) 10%, transparent); }
.disp-sem-slave { padding: 12px 16px; margin: 0; }
/* Conjunto cujo master não veio na consulta (filtro por tipo, busca ou teto),
   e os avulsos sem master: tracejado e apagado, para não passar por remessa. */
.disp-cartao-orfao { border-left-style: dashed; border-left-color: var(--border); }
.disp-cartao-orfao .disp-cartao-topo { background: var(--panel-alt); color: var(--muted); }
.conjunto-chip { display: inline-block; font-size: 11px; font-weight: 400; color: var(--muted); }
