/* ============================================================================
   Cerbero Console — COMPONENTES
   ----------------------------------------------------------------------------
   Camada construída SOBRE tokens.css. Junto com ele, é TODO o CSS do console: o
   style.css vendorizado do Typhon foi removido depois que as telas migraram.
   Regras:

   1. Nenhuma cor, tamanho, raio ou duração literal aqui — só var(--…). Se falta
      um valor, o token entra em tokens.css, não uma exceção local.
   2. Alvo de toque nunca menor que --touch-min, mesmo quando o controle é
      visualmente compacto (34px): a área efetiva cresce por padding/::after.
   3. Nada comunica estado SÓ por cor — sempre cor + texto (ou cor + ícone).
      O público é engenheiro de plantão, às vezes em tela ruim.
   4. Classe sem chamador sai (css_test.go reprova). Foi o acúmulo silencioso de
      classes órfãs que transformou o arquivo anterior em 64 KB intocáveis.
   ========================================================================= */

/* ---- Contêiner de página ------------------------------------------------- */
/* 1120px: linha de ~90 caracteres no corpo de 15px, e cabe uma tabela de 6
   colunas sem rolagem horizontal em notebook de 1366px. */
.stack     { display: flex; flex-direction: column; gap: var(--sp-6); }
.stack-sm  { display: flex; flex-direction: column; gap: var(--sp-3); }
.row       { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
.row-tight { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.spacer    { flex: 1 1 auto; }
.measure   { max-width: 72ch; }

/* ---- Painel (superfície de conteúdo) ------------------------------------ */
.panel {
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-md);
  overflow: hidden;
}
.panel-head {
  display: flex; align-items: baseline; gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--hairline);
}
.panel-title { font-size: var(--t-title-2); line-height: var(--t-title-2-lh); font-weight: var(--w-semibold); margin: 0; }
.panel-sub   { font-size: var(--t-subhead); line-height: var(--t-subhead-lh); color: var(--text-3); }
.panel-body  { padding: var(--sp-5); }
.panel-foot {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--overlay) 50%, transparent);
}
/* O <summary> é um controle de verdade (recebe foco, abre e fecha), e media
   18px de altura só com o texto — abaixo do piso de toque. O alvo cresce sem
   aumentar a fonte, então nada muda visualmente além da área clicável. */
.panel-foot > summary {
  display: flex; align-items: center;
  min-height: var(--touch-min);
  cursor: pointer;
}

/* ---- Botões -------------------------------------------------------------
   Uma escala de ênfase, não uma paleta: primary (ação da tela), secondary
   (alternativa), quiet (ação de linha), danger (destrutiva/irreversível). */
.btn {
  --btn-bg: transparent; --btn-fg: var(--text); --btn-bd: var(--hairline-strong);
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--sp-2);
  min-height: var(--touch-min);
  padding: 0 var(--sp-4);
  border: 1px solid var(--btn-bd);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-fg);
  font: inherit;
  font-size: var(--t-callout);
  font-weight: var(--w-medium);
  line-height: 1;
  cursor: pointer;
  white-space: nowrap;
  position: relative;
  transition: background var(--dur-1) var(--ease), border-color var(--dur-1) var(--ease);
}
/* Alvo de toque de 44px sem inflar o desenho (WCAG 2.5.5). */
.btn::after {
  content: ""; position: absolute; inset: 50% 0 auto 0;
  height: var(--touch-min); transform: translateY(-50%);
}
.btn:hover  { background: color-mix(in srgb, var(--text) 8%, transparent); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: 0.45; cursor: not-allowed; }

.btn-primary { --btn-bg: var(--accent); --btn-fg: var(--on-accent); --btn-bd: transparent; font-weight: var(--w-semibold); }
.btn-primary:hover { background: var(--accent-hover); }
.btn-primary:active { background: var(--accent-press); }

.btn-quiet { --btn-bd: transparent; color: var(--text-2); }
.btn-quiet:hover { color: var(--text); }

.btn-danger { --btn-fg: var(--danger); --btn-bd: color-mix(in srgb, var(--danger) 45%, transparent); }
.btn-danger:hover { background: var(--danger-quiet); }

/* "sm" é a densidade VISUAL (fonte e recuo menores), não a altura: o alvo
   continua sendo 44px, porque quem clica errado num console de segurança
   desliga uma proteção. */
.btn-sm { min-height: var(--touch-min); padding: 0 var(--sp-3); font-size: var(--t-subhead); }

/* ---- Campos ------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--sp-1); }
.field-label {
  font-size: var(--t-subhead); line-height: var(--t-subhead-lh);
  font-weight: var(--w-medium); color: var(--text-2);
}
.field-hint { font-size: var(--t-footnote); line-height: var(--t-footnote-lh); color: var(--text-3); }

.input, .select {
  min-height: var(--touch-min);
  padding: 0 var(--sp-3);
  background: var(--sunken);
  color: var(--text);
  border: 1px solid var(--hairline-strong);
  border-radius: var(--r-sm);
  font: inherit;
  font-size: var(--t-callout);
  width: 100%;
}
.input-mono { font-family: var(--font-mono); font-size: var(--t-subhead); }
.input::placeholder { color: var(--text-3); }
.input:hover, .select:hover { border-color: var(--text-3); }
.input[aria-invalid="true"] { border-color: var(--danger); }

/* ---- Tabela de dados ---------------------------------------------------- */
/* Sem zebra: separação por hairline. Zebra em tela escura cria duas superfícies
   que competem, e a linha de 1px já basta para o olho seguir a fileira. */
.table-scroll { overflow-x: auto; }
.data-table { width: 100%; border-collapse: collapse; font-size: var(--t-callout); }
.data-table th {
  text-align: left; font-size: var(--t-caption); line-height: var(--t-caption-lh);
  font-weight: var(--w-semibold); letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text-3);
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--hairline-strong);
  white-space: nowrap;
  position: sticky; top: 0; background: var(--surface);
}
.data-table td {
  padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: color-mix(in srgb, var(--text) 4%, transparent); }
.data-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table .cell-mono { font-family: var(--font-mono); font-size: var(--t-subhead); }

/* Coluna-chave fixa: ao rolar a tabela na horizontal, a coluna que IDENTIFICA a
   linha fica parada. Sem isso, quem rola para ver a última coluna perde de vista
   de qual provedor (ou usuário) está lendo o número — e um número sem sujeito,
   num console de segurança, é pior que nenhum número. */
.data-table .col-key { position: sticky; left: 0; z-index: 1; background: var(--surface); }
.data-table thead .col-key { z-index: 2; }
.data-table tbody tr:hover .col-key { background: color-mix(in srgb, var(--text) 4%, var(--surface)); }
/* A sombra só aparece quando há conteúdo rolado para debaixo da coluna. */
.table-scroll { scroll-timeline: --tsx x; }
.data-table .col-key::after {
  content: ""; position: absolute; top: 0; right: 0; bottom: 0; width: 12px;
  transform: translateX(100%);
  background: linear-gradient(to right, color-mix(in srgb, var(--bg) 55%, transparent), transparent);
  pointer-events: none;
}

/* Colapso para lista: abaixo de 720px uma tabela de 5 colunas não cabe em
   largura nenhuma sem virar rolagem horizontal aninhada, que é o pior gesto
   possível no toque. Cada linha vira um cartão e cada célula ganha o rótulo da
   coluna, que vem do atributo data-rotulo. */
@media (max-width: 720px) {
  .data-table--stack, .data-table--stack tbody, .data-table--stack tr, .data-table--stack td { display: block; width: auto; }
  .data-table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .data-table--stack tr {
    border: 1px solid var(--hairline); border-radius: var(--r-sm);
    margin-bottom: var(--sp-3); padding: var(--sp-2) var(--sp-3);
  }
  .data-table--stack td { border-bottom: 0; padding: var(--sp-1) 0; text-align: left; }
  .data-table--stack td.num { text-align: left; }
  .data-table--stack td[data-rotulo]::before {
    content: attr(data-rotulo);
    display: block;
    font-size: var(--t-caption); line-height: var(--t-caption-lh);
    letter-spacing: 0.04em; text-transform: uppercase; color: var(--text-3);
  }
  .data-table--stack .col-key { position: static; }
  .data-table--stack .col-key::after { display: none; }
  .table-scroll:has(.data-table--stack) { overflow-x: visible; }
}

/* ---- Interruptor --------------------------------------------------------
   Uma pílula "on/off" é um RÓTULO: não parece clicável e não diz para que lado
   o clique vai. O interruptor mostra o estado e a direção da mudança no mesmo
   desenho. É um <button role="switch">, não um checkbox, porque a mudança vai
   por POST e precisa poder ser recusada pelo servidor (confirmação de
   rebaixamento de postura) sem que o controle já tenha "virado" na tela. */
.switch {
  display: inline-flex; align-items: center; gap: var(--sp-3);
  min-height: var(--touch-min); padding: 0;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: var(--t-callout); color: var(--text-2);
}
.switch-track {
  position: relative; flex: 0 0 auto;
  width: 40px; height: 24px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--text) 14%, transparent);
  border: 1px solid var(--hairline-strong);
  transition: background var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.switch-thumb {
  position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px; border-radius: var(--r-full);
  background: var(--text-2);
  transition: transform var(--dur-2) var(--ease), background var(--dur-2) var(--ease);
}
.switch[aria-checked="true"] .switch-track { background: var(--success-quiet); border-color: var(--success); }
.switch[aria-checked="true"] .switch-thumb { transform: translateX(16px); background: var(--success); }
.switch[aria-checked="true"] { color: var(--text); }
/* O texto ao lado NÃO é decoração: cor sozinha não distingue ligado de desligado
   para quem não vê a diferença de matiz. */
.switch-state { font-weight: var(--w-semibold); font-size: var(--t-subhead); min-width: 3.5ch; }

.setting-label { display: block; font-weight: var(--w-medium); }

/* ---- Busca em tabela ---------------------------------------------------- */
.table-filter { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.table-filter .input { max-width: 22rem; }
.table-filter-count { font-size: var(--t-subhead); color: var(--text-3); white-space: nowrap; }

/* Linha de ajuste PENDENTE (A3): fundo + pílula. Só o fundo não bastaria — cor
   sozinha não comunica estado (e a pílula "pendente" é o que se lê num print). */
.linha-pendente > td { background: color-mix(in srgb, var(--warning) 9%, transparent); }
.linha-pendente > td:first-child { box-shadow: inset 3px 0 0 var(--warning); }

/* Linha alcançada por navegação (paleta, dashboard, "salvo e voltou"): pisca uma
   vez para o olho encontrá-la. Sem isto a página rola até o lugar certo e nada
   distingue a linha das vizinhas. */
.linha-alvo > td { background: var(--accent-quiet); }

/* ---- Sombra de conteúdo cortado (B7) ------------------------------------
   Rolagem horizontal de tabela é INVISÍVEL: sem indicação de borda ninguém
   procura o que está fora da vista — quem via só as três primeiras colunas de
   /providers concluía que o upstream não estava configurado. Os dois degradês
   de máscara são `local` (rolam com o conteúdo) e as duas sombras são `scroll`
   (ficam paradas): o resultado é que a sombra aparece só do lado em que há
   conteúdo escondido. CSS puro, sem JS e sem listener de scroll. */
.table-scroll--sombra {
  background:
    linear-gradient(to right, var(--surface) 30%, transparent) left center,
    linear-gradient(to left, var(--surface) 30%, transparent) right center,
    radial-gradient(farthest-side at 0 50%, color-mix(in srgb, var(--text) 22%, transparent), transparent) left center,
    radial-gradient(farthest-side at 100% 50%, color-mix(in srgb, var(--text) 22%, transparent), transparent) right center;
  background-repeat: no-repeat;
  background-size: 34px 100%, 34px 100%, 14px 100%, 14px 100%;
  background-attachment: local, local, scroll, scroll;
}
/* (o antigo `tr[hidden]{display:none}` saiu daqui: era este mesmo bug remendado
   só para linha de tabela. A regra `[hidden]` do tokens.css cobre todos os
   casos, inclusive os que ainda não existem.) */

/* ---- Pílula de estado ---------------------------------------------------
   Cor + TEXTO sempre. Um daltônico e uma impressão em preto-e-branco leem o
   mesmo estado — requisito de acessibilidade e de relatório de auditoria. */
/* Fundo OPACO, e isto é uma decisão de contraste, não de estética. Com um tint
   translúcido (rgba .../0.10) o fundo real da pílula depende da camada embaixo
   dela: a mesma pílula media 4.65:1 dentro de um painel `surface` e 4.36:1
   quando pousava no `overlay` — reprovando AA só em algumas telas, o tipo de
   defeito que aparece numa e não na outra e some quando se vai investigar.
   Compondo contra `--surface` na própria regra, a razão passa a ser a MESMA em
   qualquer contexto, e é a razão que o teste calcula. */
.pill {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 2px var(--sp-2);
  border-radius: var(--r-xs);
  font-size: var(--t-caption); line-height: var(--t-caption-lh);
  font-weight: var(--w-semibold); letter-spacing: 0.02em;
  border: 1px solid transparent;
  white-space: nowrap;
  --pill-cor: var(--text-2);
  background: color-mix(in srgb, var(--pill-cor) 10%, var(--surface));
  color: var(--pill-cor);
  border-color: color-mix(in srgb, var(--pill-cor) 30%, transparent);
}
.pill-ok      { --pill-cor: var(--success); }
.pill-warn    { --pill-cor: var(--warning); }
.pill-danger  { --pill-cor: var(--danger); }
.pill-info    { --pill-cor: var(--info); }
.pill-neutral { background: color-mix(in srgb, var(--text) 8%, var(--surface)); color: var(--text-2); border-color: var(--hairline-strong); }

/* ---- Barra de ação fixa (pendência) -------------------------------------
   No rodapé e fixa. A tela de Configurações passa de 5.000px: um aviso no topo
   do conteúdo simplesmente não existe para quem está mexendo num ajuste lá
   embaixo — e o preço de não vê-lo é achar que a mudança já vale. */
.action-dock {
  position: fixed; left: 0; right: 0; bottom: 0;
  z-index: var(--z-action-bar);
  padding: var(--sp-3) var(--sp-6) calc(var(--sp-3) + env(safe-area-inset-bottom));
  background: color-mix(in srgb, var(--overlay) 92%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-top: 2px solid var(--warning);
  box-shadow: var(--shadow-2);
}
.action-dock-inner {
  max-width: 1120px; margin: 0 auto;
  display: flex; align-items: center; gap: var(--sp-4);
}
.action-dock .icon { color: var(--warning); flex: 0 0 auto; }
.action-dock-text { display: flex; flex-direction: column; gap: 2px; flex: 1 1 auto; min-width: 0; }
.action-dock-text code {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  padding: 0 4px; border-radius: 4px;
}
/* Sem isto a barra tapa o último painel da página. */
body:has(.action-dock) .page-content { padding-bottom: var(--sp-12); }

/* ---- Diálogo (C5) -------------------------------------------------------
   <dialog> nativo: foco preso, Esc fecha, fundo escurecido — tudo sem
   biblioteca, que é requisito num binário único que roda em rede isolada. */
.dialog {
  width: min(34rem, calc(100vw - 2 * var(--sp-5)));
  padding: 0; border: 1px solid var(--hairline-strong);
  border-radius: var(--r-lg);
  background: var(--overlay); color: var(--text);
  box-shadow: var(--shadow-2);
}
.dialog::backdrop { background: var(--scrim); backdrop-filter: blur(2px); }
.dialog-form { display: flex; flex-direction: column; gap: var(--sp-4); padding: var(--sp-6); }
.dialog-title {
  margin: 0; font-size: var(--t-title-2); line-height: var(--t-title-2-lh);
  font-weight: var(--w-semibold);
}
.dialog-body { margin: 0; font-size: var(--t-body); line-height: var(--t-body-lh); color: var(--text-2); }
.dialog-body strong { color: var(--text); }
.dialog-actions { display: flex; justify-content: flex-end; gap: var(--sp-3); }
.dialog-actions .btn { min-height: var(--control-lg); }
.field-error { font-size: var(--t-footnote); line-height: var(--t-footnote-lh); color: var(--danger); }

/* ---- KPI ---------------------------------------------------------------- */
.kpi-grid { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.kpi {
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--r-md); padding: var(--sp-4) var(--sp-5);
  display: flex; flex-direction: column; gap: var(--sp-1);
}
.kpi-label { font-size: var(--t-subhead); line-height: var(--t-subhead-lh); color: var(--text-3); }
.kpi-value {
  font-size: var(--t-large-title); line-height: var(--t-large-title-lh);
  font-weight: var(--w-semibold); letter-spacing: var(--tracking-tight);
}
.kpi-note { font-size: var(--t-footnote); line-height: var(--t-footnote-lh); color: var(--text-3); }
.kpi-accent .kpi-value  { color: var(--accent); }
.kpi-danger .kpi-value  { color: var(--danger); }
.kpi-warning .kpi-value { color: var(--warning); }
/* M2b · ZERO não usa cor de alarme. "0 segredos bloqueados" em vermelho ensina o
   operador a ignorar vermelho — que é justamente a cor que precisa funcionar
   quando o número deixar de ser zero. */
.kpi-quiet .kpi-value { color: var(--text-3); }
/* M2a · O contador é um LINK para o feed filtrado: um número que diz "3 segredos
   bloqueados" e não deixa ver quais três não permite investigar nada. Herda a cor
   do texto para não parecer um link de prosa no meio dos números. */
a.kpi { text-decoration: none; color: inherit; transition: border-color var(--dur-1) var(--ease); }
a.kpi:hover { border-color: var(--accent); }
a.kpi:hover .kpi-note { color: var(--accent); }

/* ---- Estado vazio -------------------------------------------------------
   Um vazio precisa dizer POR QUE está vazio e o que fazer — "nenhum dado" é
   indistinguível de "quebrou". */
.empty {
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-5);
  text-align: center; color: var(--text-3);
}
.empty-title { font-size: var(--t-headline); line-height: var(--t-headline-lh); font-weight: var(--w-semibold); color: var(--text-2); }
.empty-body  { font-size: var(--t-subhead); line-height: var(--t-subhead-lh); max-width: 48ch; }

/* ---- Cabeçalho de seção ------------------------------------------------- */
.section-head { display: flex; align-items: baseline; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.section-title { font-size: var(--t-title-2); line-height: var(--t-title-2-lh); font-weight: var(--w-semibold); margin: 0; }
.section-sub { font-size: var(--t-subhead); line-height: var(--t-subhead-lh); color: var(--text-3); }

/* ---- Ícones -------------------------------------------------------------
   Um ícone é traço em currentColor, dimensionado em relação ao texto (1em ⇒
   acompanha a escala tipográfica em vez de brigar com ela) e nunca encolhe num
   flex. `vertical-align: -0.15em` alinha o desenho à base da letra: sem isso o
   ícone "flutua" acima do texto ao lado dele. */
.icon {
  width: 1.25em; height: 1.25em;
  flex: 0 0 auto;
  vertical-align: -0.15em;
  fill: none;
  stroke: currentColor;
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.icon-sm { width: 1em;    height: 1em; }
.icon-warning { color: var(--warning); }

/* ---- Grade de painéis ---------------------------------------------------
   `auto-fit` + `minmax` em vez de media query: as colunas se resolvem sozinhas
   pela largura DISPONÍVEL, então a grade também funciona quando o rail está
   expandido — que é o caso em que um breakpoint por viewport erra. */
.grid-2 { display: grid; gap: var(--sp-4); grid-template-columns: repeat(auto-fit, minmax(min(100%, 22rem), 1fr)); }

/* ---- Lista de atributo (rótulo → valor) ---------------------------------
   Para o painel de "estado de X": à esquerda o nome, à direita o valor. É uma
   tabela de verdade porque é dado tabular — e assim herda o <caption> e o
   comportamento de leitor de tela da .data-table. */
.kv {
  width: 100%; border-collapse: collapse; font-size: var(--t-callout);
  /* `fixed` + largura na primeira coluna: sem isso o navegador dimensiona pelo
     CONTEÚDO, e um valor longo (fingerprint, caminho de arquivo) empurrava a
     tabela para fora do painel — 143px de overflow que cortavam a coluna de
     valores inteira, justamente a que interessa. */
  table-layout: fixed;
}
.kv td { padding: var(--sp-2) 0; border-bottom: 1px solid var(--hairline); vertical-align: baseline; }
.kv tr:last-child td { border-bottom: 0; }
.kv td:first-child {
  width: 40%; color: var(--text-3); padding-right: var(--sp-4);
  overflow-wrap: break-word;
}
.kv td:last-child { text-align: right; overflow-wrap: break-word; }
.kv .cell-mono { font-family: var(--font-mono); font-size: var(--t-subhead); word-break: break-all; }
@media (max-width: 480px) {
  /* Em tela estreita, rótulo e valor em duas linhas leem melhor que duas
     colunas de 8 caracteres cada. */
  .kv td, .kv td:first-child, .kv td:last-child { display: block; width: auto; text-align: left; }
  .kv td:first-child { padding-bottom: 0; border-bottom: 0; }
}

/* ---- Barra de proporção -------------------------------------------------
   O número fica SEMPRE ao lado da barra: comprimento sozinho não é leitura
   precisa, e quem usa leitor de tela não recebe barra nenhuma. */
.meter-row { display: flex; align-items: center; gap: var(--sp-3); font-size: var(--t-subhead); }
.meter-name { width: 9rem; flex: 0 0 auto; color: var(--text-2); }
.meter { flex: 1 1 auto; height: 8px; background: color-mix(in srgb, var(--text) 10%, transparent); border-radius: var(--r-full); overflow: hidden; }
.meter-fill { height: 100%; background: var(--accent); border-radius: var(--r-full); }
.meter-value { width: 4rem; flex: 0 0 auto; text-align: right; font-variant-numeric: tabular-nums; }

/* ---- Valor copiável (C11) ----------------------------------------------- */
.copiavel { display: inline-flex; align-items: center; gap: var(--sp-2); max-width: 100%; }
.copiavel-valor { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
/* Discrição pela COR, nunca por opacity: `opacity` compõe o texto com o fundo
   depois que a cor já foi escolhida, então ela desfaz o contraste que o token
   garante — aqui --text-2 (8.01:1) caía para 3.29:1 sob 0.65. --text-3 é o
   token de texto quieto que ainda passa AA (5.38:1) por construção. */
.copiar { flex: 0 0 auto; color: var(--text-3); }
.copiar:hover, .copiar:focus-visible { color: var(--text); }
.copiar[data-copiado="sim"] { color: var(--success); }

/* ---- Bloco de código / JSON --------------------------------------------- */
.code-block {
  margin: 0; padding: var(--sp-4);
  background: var(--sunken);
  border-top: 1px solid var(--hairline);
  font-family: var(--font-mono); font-size: var(--t-subhead); line-height: 1.55;
  color: var(--text-2);
  overflow-x: auto;
  max-height: 26rem;
}

/* ---- Nota (contexto e consequência) -------------------------------------
   Diferente da .action-dock: a barra é uma PENDÊNCIA com ação; a nota é
   explicação fixa. A borda esquerda dá a gravidade sem colorir o texto todo,
   que prejudicaria a leitura. */
.note {
  display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--text-3);
  border-radius: var(--r-sm);
  font-size: var(--t-subhead); line-height: var(--t-subhead-lh);
  color: var(--text-2);
  background: color-mix(in srgb, var(--overlay) 55%, transparent);
}
.note-info   { border-left-color: var(--info); }
.note-warn   { border-left-color: var(--warning); }
.note-danger { border-left-color: var(--danger); }
/* .note-ok existia como lacuna: havia acento para informação, aviso e perigo, e
   nenhum para "conferido, está certo" — então o veredito ÍNTEGRA da auditoria saía
   com a cor de informação, indistinguível de uma explicação qualquer. */
.note-ok     { border-left-color: var(--success); }
.note .icon  { color: var(--text-3); margin-top: 1px; }
.note-warn .icon   { color: var(--warning); }
.note-danger .icon { color: var(--danger); }
.note-info .icon   { color: var(--info); }
.note-ok .icon     { color: var(--success); }
.note strong { color: var(--text); }

/* ---- Veredito (M1/M2) ---------------------------------------------------
   O resultado de uma verificação de integridade é lido por quem NÃO escreveu o
   código — jurídico, auditor externo, diretoria — e precisa ser entendido em dois
   segundos. Por isso é um cabeçalho escaneável (pílula + uma linha de resumo) e
   não um parágrafo: a versão anterior explicava segmentos, cadeias e HMAC em sete
   linhas corretas e ilegíveis. A explicação inteira continua, recolhida. */
.veredito {
  display: flex; flex-direction: column; gap: var(--sp-2);
  padding: var(--sp-4);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--text-3);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--overlay) 55%, transparent);
}
.veredito-topo { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; }
/* A lista de provedores ao lado do selo é uma string longa e sem espaço em branco
   (nome da rota, seta e URL do upstream, repetidos por provedor): em 375px ela
   empurrava a página 38px além da largura do documento — rolagem horizontal no
   dashboard, medida. `anywhere` deixa quebrar em qualquer ponto; `min-width: 0` é
   o que permite ao item de flex encolher abaixo do próprio conteúdo. */
.veredito-topo > * { min-width: 0; overflow-wrap: anywhere; }
.veredito-selo { font-size: var(--t-subhead); padding: 3px var(--sp-3); letter-spacing: 0.04em; }
.veredito-resumo {
  font-size: var(--t-headline); line-height: var(--t-headline-lh);
  font-weight: var(--w-semibold); color: var(--text);
}
.veredito-detalhe { padding-top: var(--sp-2); }
.veredito > details > summary {
  display: flex; align-items: center; min-height: var(--touch-min);
  cursor: pointer; color: var(--text-3);
}
.veredito.note-ok .icon     { color: var(--success); }
.veredito.note-warn .icon   { color: var(--warning); }
.veredito.note-danger .icon { color: var(--danger); }
/* Lista do veredito do dashboard: cada item é um link para o ajuste que o
   governa, então o veredito não é só informativo — é o caminho. */
.veredito-itens {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-5);
  font-size: var(--t-subhead);
}
.veredito-itens li { display: flex; align-items: center; gap: var(--sp-2); }
/* Link que NAVEGA (não link dentro de frase) precisa de alvo de ponteiro: o texto
   de 13px dava 16px de altura clicável. A exceção "inline" da WCAG vale para link
   no meio de uma sentença, e estes são controles sozinhos numa célula — 24px é o
   piso do SC 2.5.8. Medido em 4 larguras: 16px → 24px. */
.veredito-itens a, .kv td a {
  display: inline-flex; align-items: center; min-height: 24px;
}
/* Lista de pendências na barra fixa: rótulo humano + os dois valores. */
.pending-list {
  list-style: none; margin: 0; padding: 0;
  font-size: var(--t-subhead); line-height: var(--t-subhead-lh); color: var(--text-2);
}
.pending-list code {
  background: color-mix(in srgb, var(--text) 8%, transparent);
  padding: 0 4px; border-radius: 4px;
}

/* ---- Valor sensível (Fase 4 — segurança como UX) ------------------------
   Um dado re-identificado não pode simplesmente aparecer na tela: quem opera
   costuma estar em sala compartilhada, chamada com tela compartilhada ou
   gravação. Ele nasce OCULTO e só é exposto por um gesto explícito, que a
   pessoa faz sabendo que está expondo. */
.secret-value {
  display: flex; align-items: center; gap: var(--sp-3);
  font-family: var(--font-mono); font-size: var(--t-body);
}
.secret-value[data-oculto="sim"] .secret-text {
  filter: blur(7px);
  user-select: none;
}
.secret-text { color: var(--accent); word-break: break-all; }

/* ---- Utilitários de fluxo -----------------------------------------------
   Existem para que nenhum template precise de style="" inline: um valor inline
   é invisível para o sistema de design (não aparece em auditoria de CSS, não
   responde a tema e não pode ser mudado em um lugar só). */
.flush      { margin: 0; }
/* (`.row-bottom` saiu: era `.row` + alinhamento pelo BASE DA CAIXA, que punha a
   fileira em degraus quando um campo tinha dica de duas linhas. O `.field-row`
   abaixo alinha os controles e reserva a altura da dica — ver B1.) */
.grow-md    { flex: 1 1 14rem; }
.grow-sm    { flex: 0 1 12rem; }
/* 64 caracteres hexadecimais em fonte mono (M7): o campo de hash mostrava um
   terço do valor, e conferir se o que foi colado é o certo exigia rolar DENTRO
   do input. */
.grow-hash  { flex: 1 1 44rem; }
.input-hash { min-width: 0; }

/* ---- Linha de formulário (B1) -------------------------------------------
   `align-items: end` alinha os CONTROLES na mesma linha de base, não as caixas:
   sem isso, um campo com dica de duas linhas empurrava o próprio input para
   cima e a fileira ficava em degraus. E a dica tem altura RESERVADA
   (`min-height`), então digitar algo que faz a dica aparecer não empurra o
   formulário inteiro para baixo — o layout pulando sob o cursor é o que faz
   alguém clicar no botão errado. */
.field-row {
  display: flex; align-items: start; gap: var(--sp-3); flex-wrap: wrap;
}
/* Alinhado pelo TOPO do controle, e não pelo fim da caixa. Com `end`, o que manda
   é a altura do que vem DEPOIS do controle: um campo sem dica subia 20px e um com
   duas dicas descia outros 20px — medido, a fileira de /policies saía com o select
   "Ação" 20px fora e a de /users com 24px. Com o rótulo de altura fixa (uma linha)
   e o controle logo abaixo, a posição do controle não depende da dica: escrever,
   apagar ou trocar a dica não mexe no formulário. */
.field-row > .field > .field-label { min-height: var(--t-subhead-lh); }
.field-row .field-hint { min-height: var(--t-footnote-lh); }
/* O botão não tem rótulo em cima, então desce até a linha dos controles. */
.field-row > .btn { margin-top: calc(var(--t-subhead-lh) + var(--sp-1)); }
.field-row .field { min-width: 0; }
/* Estado do texto de apoio: erro e requisito atendido em TEXTO, nunca só cor. */
.field-erro { color: var(--danger); }
.field-ok   { color: var(--success); }
.req {
  font-size: var(--t-caption); font-weight: var(--w-semibold);
  color: var(--danger); text-transform: uppercase; letter-spacing: 0.04em;
}

/* Campo de senha com botão de revelar: 12+ caracteres digitados às cegas erram,
   e o erro só apareceria no login seguinte — de outra pessoa. */
.senha-campo { display: flex; align-items: center; gap: var(--sp-2); }
.senha-campo .input { min-width: 0; }

/* Painel que exibe dado pessoal real (reveal): a moldura diz o que é, sem
   depender de o operador lembrar. */
.panel-alerta {
  border-color: color-mix(in srgb, var(--warning) 45%, transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--warning) 18%, transparent);
}

/* ---- Paleta de comandos (B6) -------------------------------------------- */
.dialog-paleta { width: min(46rem, calc(100vw - 2 * var(--sp-5))); }
.paleta { display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-4); }
.paleta-campo { display: flex; align-items: center; gap: var(--sp-3); }
.paleta-campo .icon { color: var(--text-3); flex: 0 0 auto; }
.paleta-lista {
  list-style: none; margin: 0; padding: 0;
  max-height: 60vh; overflow-y: auto;
  display: flex; flex-direction: column; gap: 2px;
}
.paleta-item {
  display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap;
  min-height: var(--touch-min);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  cursor: pointer;
}
.paleta-item:hover { background: color-mix(in srgb, var(--text) 6%, transparent); }
/* O item corrente é destacado por FUNDO e por aria-selected: navegação por
   teclado sem marca visível é navegação às cegas. */
.paleta-item[aria-selected="true"] { background: var(--accent-quiet); }
.paleta-nome { font-weight: var(--w-medium); }
.paleta-chave { font-family: var(--font-mono); font-size: var(--t-subhead); color: var(--text-3); }
.paleta-nota { font-size: var(--t-subhead); color: var(--text-3); flex: 1 1 14rem; min-width: 0; }
.paleta-vazio { padding: var(--sp-5); text-align: center; color: var(--text-3); font-size: var(--t-subhead); }
.paleta-dica { margin: 0; }

/* ============================================================================
   SHELL — rail, topo, conteúdo, rodapé, snackbar
   ----------------------------------------------------------------------------
   Portado do style.css legado para os tokens, mantendo os MESMOS nomes de
   classe. É de propósito: assim o layout e o app.js não mudam, e o arquivo
   legado inteiro pode ser apagado sem que a moldura do console pisque.
   ========================================================================= */
body { display: flex; min-height: 100vh; overflow-x: hidden; }

/* ---- Rail de navegação ---------------------------------------------------
   Colapsado é a forma padrão: 80px devolvem largura ao conteúdo, que é onde
   está o trabalho. Expandido mostra os rótulos para quem ainda não decorou os
   ícones. */
.nav-rail {
  position: fixed; inset: 0 auto 0 0;
  width: 80px; height: 100vh;
  display: flex; flex-direction: column; align-items: center;
  padding: var(--sp-1) 0;
  background: var(--surface);
  border-right: 1px solid var(--hairline);
  z-index: var(--z-rail);
  transition: width var(--dur-3) var(--ease);
}
.nav-rail.expanded { width: 240px; }
/* Botão só-ícone do rail: sem moldura, porque borda de botão de formulário no
   meio da navegação compete com os itens. O alvo de toque continua sendo os
   44px que o .btn garante por ::after. */
.rail-toggle, .rail-logout {
  --btn-bd: transparent;
  width: 44px; padding: 0; color: var(--text-3);
}
.rail-toggle:hover { color: var(--text); background: color-mix(in srgb, var(--text) 8%, transparent); }
.rail-logout:hover { color: var(--danger); background: var(--danger-quiet); }

.nav-rail-header { display: flex; justify-content: center; padding: var(--sp-1) 0 var(--sp-2); }
/* Com 11 destinos, o rail precisa caber em 950px de altura SEM rolar — o item
   corrente estar fora da vista é pior do que qualquer economia de pixel. Por
   isso o item colapsado ficou compacto (28px de ícone + rótulo de 11px = 50px),
   o que dá ~750px com os cabeçalhos de seção, o topo e o rodapé.
   Quando ainda assim não couber (janela baixa), a rolagem é EXPLÍCITA: barra
   visível e degradê nas bordas. A versão anterior escondia a barra
   (`scrollbar-width: none`) e ficavam 342px de navegação invisível — o usuário
   não tinha como saber que havia mais. */
.nav-rail-items {
  flex: 1; width: 100%; min-height: 0;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 0 var(--sp-1);
  overflow-y: auto; overflow-x: hidden;
  scrollbar-width: thin;
  scrollbar-color: var(--hairline-strong) transparent;
  /* Degradê de 12px nas duas pontas: diz "há mais aqui" sem ocupar espaço. */
  mask-image: linear-gradient(to bottom, transparent 0, var(--mask-solid) 12px, var(--mask-solid) calc(100% - 12px), transparent 100%);
}
.nav-rail-items::-webkit-scrollbar { width: 6px; }
.nav-rail-items::-webkit-scrollbar-thumb { background: var(--hairline-strong); border-radius: var(--r-full); }
.nav-rail-items::-webkit-scrollbar-track { background: transparent; }

.nav-rail-item {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  width: 72px; padding: 2px 0 3px;
  border-radius: var(--r-sm);
  color: var(--text-3);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease), background var(--dur-1) var(--ease);
}
.nav-rail-item .nav-icon {
  width: 48px; height: 24px;
  display: flex; align-items: center; justify-content: center;
  border-radius: var(--r-full);
  transition: background var(--dur-1) var(--ease);
}
.nav-rail-item > span:not(.nav-icon) {
  margin-top: 1px;
  font-size: var(--t-caption); line-height: var(--t-caption-lh);
  font-weight: var(--w-medium); text-align: center;
  max-width: 68px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nav-rail-item:hover { color: var(--text); }
.nav-rail-item:hover .nav-icon { background: color-mix(in srgb, var(--text) 8%, transparent); }
/* O item corrente ganha PESO e FUNDO, não só um tom de cor: matiz sozinho não
   sobrevive a monitor ruim, a daltonismo nem a print em preto e branco. */
.nav-rail-item.active { color: var(--accent); font-weight: var(--w-semibold); }
.nav-rail-item.active .nav-icon { background: var(--accent-quiet); }

.nav-rail-section {
  position: sticky; top: 0; z-index: 1;
  width: 100%; padding: 6px 0 1px; flex-shrink: 0;
  display: flex; flex-direction: column; align-items: center;
  background: var(--surface);
}
.nav-rail-section .sec-label {
  font-size: var(--t-caption); line-height: 1.1;
  font-weight: var(--w-semibold); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-3);
  transform: scale(0.82); transform-origin: center; /* encolhe o DESENHO, não o
      tamanho computado: o piso de 11px continua valendo para quem amplia. */
}
.nav-rail-section .sec-rule { display: none; }

.nav-rail-footer {
  width: 100%;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--sp-1) 0 0;
  border-top: 1px solid var(--hairline);
}
.nav-user-label {
  max-width: 72px;
  font-size: var(--t-caption); font-weight: var(--w-medium); color: var(--text-3);
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* B2 · A versão vive no rodapé do rail. Antes era `position: fixed` no canto
   inferior direito da janela: tapava a última linha de tabela no desktop e
   pousava sobre a barra de abas no celular. Aqui é o que ela é — informação de
   rodapé de navegação, junto da conta. */
.rail-version {
  max-width: 72px;
  font-family: var(--font-mono); font-size: var(--t-caption); color: var(--text-3);
  text-align: center; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* B4 · MENU RECOLHIDO: só ícones. O rótulo de 11px cortado em 68px
   ("Configuraçõ…", "Identidade…") não é leitura — é ruído que ainda consome
   altura, e com onze destinos a altura é o recurso escasso. O texto continua no
   DOM como nome acessível (as mesmas propriedades do .sr-only, aplicadas por
   estado): sumir com ele deixaria cada item anunciado apenas como "link".
   O nome visível para quem usa mouse vem do `title`, e o rótulo inteiro volta
   quando o menu é expandido. */
.nav-rail:not(.expanded) .nav-rail-item > span:not(.nav-icon) {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.nav-rail:not(.expanded) .nav-rail-item { padding: var(--sp-2) 0; }
.nav-rail:not(.expanded) .nav-rail-item .nav-icon { height: 28px; }

/* Expandido: os itens viram lista com rótulo ao lado do ícone. */
.nav-rail.expanded .nav-rail-items { align-items: stretch; padding: 0 var(--sp-3); }
.nav-rail.expanded .nav-rail-section { flex-direction: row; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4) var(--sp-1); }
.nav-rail.expanded .nav-rail-section .sec-label { transform: none; letter-spacing: 0.14em; }
.nav-rail.expanded .nav-rail-section .sec-rule { display: block; flex: 1; height: 1px; background: var(--hairline); }
.nav-rail.expanded .nav-rail-item {
  flex-direction: row; justify-content: flex-start; align-items: center;
  width: auto; height: 38px; padding: 0 var(--sp-4); gap: var(--sp-3);
  border-radius: var(--r-full);
}
.nav-rail.expanded .nav-rail-item > span:not(.nav-icon) { max-width: none; }
.nav-rail.expanded .nav-rail-item .nav-icon { width: 24px; height: 24px; background: none; border-radius: 0; }
.nav-rail.expanded .nav-rail-item.active { background: var(--accent-quiet); }
.nav-rail.expanded .nav-rail-item > span:not(.nav-icon) { margin-top: 0; font-size: var(--t-callout); }
.nav-rail.expanded .nav-rail-footer { flex-direction: row; align-items: center; gap: var(--sp-3); padding: var(--sp-2) var(--sp-4); }
.nav-rail.expanded .nav-user-label { max-width: none; font-size: var(--t-subhead); }

/* Botão "Mais" da barra do celular: não existe no desktop, onde o rail vertical
   comporta os onze destinos. */
.nav-more { display: none; }

/* ---- Área principal ------------------------------------------------------ */
main.with-rail { margin-left: 80px; width: calc(100% - 80px); }
.nav-rail.expanded ~ main.with-rail { margin-left: 240px; width: calc(100% - 240px); }
main { flex: 1 1 auto; min-width: 0; }

.top-bar {
  position: sticky; top: 0; z-index: var(--z-header);
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  min-height: 60px; padding: var(--sp-3) var(--sp-6);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--hairline);
  isolation: isolate;
}
/* B3 · Marca e título formam UM bloco, à esquerda. Antes o `space-between`
   empurrava o logo para o meio da barra, entre o título e o papel — solto, ele
   parecia um elemento de interface em vez da marca do produto. */
.top-bar-brand { display: flex; align-items: center; gap: var(--sp-3); min-width: 0; }
.top-bar h1 {
  margin: 0;
  font-size: var(--t-title-1); line-height: var(--t-title-1-lh);
  font-weight: var(--w-semibold); letter-spacing: var(--tracking-snug);
}
/* O arquivo do logo é branco com transparência: perfeito no tema escuro e
   invisível no claro (branco sobre #FBFBFD). Sem uma variante escura do asset,
   a inversão por filtro resolve sem baixar nada e sem perder nitidez — a imagem
   tem 3333px de largura para 22px de altura na tela. A opacidade sobe para 1: o
   logo é a marca, não uma marca-d'água. */
.top-bar-logo {
  height: 22px; opacity: 1;
  pointer-events: none; user-select: none;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .top-bar-logo { filter: invert(1) brightness(0.35); }
}
:root[data-theme="light"] .top-bar-logo { filter: invert(1) brightness(0.35); }
:root[data-theme="dark"] .top-bar-logo { filter: none; }
.top-bar-actions { display: flex; align-items: center; gap: var(--sp-3); }
/* Atalho da paleta (B6): o botão existe porque um atalho invisível não é
   descoberto por quem abre o console duas vezes por mês. */
.cmdk-abrir { color: var(--text-3); }
.cmdk-abrir:hover { color: var(--text); }
.cmdk-atalho {
  font-family: var(--font-mono); font-size: var(--t-caption);
  padding: 1px var(--sp-2); border-radius: var(--r-xs);
  border: 1px solid var(--hairline-strong);
}
.role-chip {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 3px var(--sp-3);
  border-radius: var(--r-full);
  background: color-mix(in srgb, var(--text) 8%, transparent);
  border: 1px solid var(--hairline-strong);
  color: var(--text-2);
  font-size: var(--t-caption); font-weight: var(--w-semibold);
  text-transform: uppercase; letter-spacing: 0.04em;
}

/* 1120px: uma linha de ~90 caracteres no corpo de 15px, e uma tabela de seis
   colunas cabe sem rolagem horizontal num notebook de 1366px. */
.page-content { padding: var(--sp-7) var(--sp-6) var(--sp-10); max-width: 1120px; }

/* ---- Snackbar ------------------------------------------------------------ */
#snackbar-container {
  position: fixed; left: 50%; bottom: var(--sp-6); transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex; flex-direction: column; gap: var(--sp-2);
  pointer-events: none;
}
.snackbar {
  display: flex; align-items: center; gap: var(--sp-3);
  min-width: 20rem; max-width: 34rem;
  padding: var(--sp-3) var(--sp-4);
  background: var(--overlay); color: var(--text);
  border: 1px solid var(--hairline-strong);
  border-left: 3px solid var(--accent);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-2);
  font-size: var(--t-callout);
  pointer-events: auto;
  animation: snackbar-entra var(--dur-3) var(--ease);
}
.snackbar-error { border-left-color: var(--danger); }
/* Três níveis, não dois: confirmar que a anonimização foi DESLIGADA com a mesma
   cor de "deu tudo certo" ensinaria que rebaixar a postura é um desfecho bom. */
.snackbar-warn { border-left-color: var(--warning); }
.snackbar-action {
  margin-left: auto; padding: 0; border: 0; background: none;
  color: var(--accent); font: inherit; font-weight: var(--w-semibold); cursor: pointer;
  white-space: nowrap;
}
.snackbar.snackbar-exit { animation: snackbar-sai var(--dur-2) var(--ease) forwards; }
@keyframes snackbar-entra { from { opacity: 0; transform: translateY(12px); } }
@keyframes snackbar-sai   { to   { opacity: 0; transform: translateY(8px); } }

/* ---- Indicadores de carregamento ---------------------------------------- */
.htmx-indicator { visibility: hidden; }
.htmx-request .htmx-indicator, .htmx-request.htmx-indicator { visibility: visible; }
.progress { width: 100%; height: 2px; background: transparent; overflow: hidden; }
.progress-bar { height: 100%; width: 40%; background: var(--accent); animation: progress-indet 1.4s var(--ease) infinite; }
@keyframes progress-indet { 0% { transform: translateX(-100%); } 100% { transform: translateX(350%); } }

.loading-overlay { display: none; position: fixed; inset: 0; z-index: var(--z-overlay); cursor: wait; }
.loading-overlay.active { display: flex; align-items: center; justify-content: center; background: var(--scrim); }
.loading-spinner {
  width: 34px; height: 34px; border-radius: 50%;
  border: 3px solid var(--hairline-strong); border-top-color: var(--accent);
  animation: girar 0.7s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* ---- Login ---------------------------------------------------------------
   Tela sem rail e sem topo: a única do console em que a pessoa ainda não é
   ninguém. Centralizada e curta, para não parecer que há algo a explorar. */
.login-shell { display: flex; align-items: center; justify-content: center;
  min-height: 100vh; width: 100%; padding: var(--sp-5); }
.login-card {
  width: 100%; max-width: 24rem;
  padding: var(--sp-8);
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-2);
  text-align: center;
}
.login-brand { display: block; height: 26px; margin: 0 auto var(--sp-5); opacity: 0.8; }
.login-mark { color: var(--accent); margin-bottom: var(--sp-2); }
.login-mark .icon { width: 36px; height: 36px; stroke-width: 1.5; }
.login-title { margin: 0; font-size: var(--t-title-1); line-height: var(--t-title-1-lh);
  font-weight: var(--w-semibold); letter-spacing: var(--tracking-snug); }
.login-sub { margin: var(--sp-1) 0 var(--sp-6); font-size: var(--t-subhead); color: var(--text-3); }
.login-card .field { text-align: left; }
.login-erro { text-align: left; margin-bottom: var(--sp-4); }
.login-submit { width: 100%; margin-top: var(--sp-2); min-height: var(--control-lg); }
.login-foot { margin: var(--sp-6) 0 0; text-align: left; }

/* ---- Responsivo ---------------------------------------------------------
   Um único ponto de virada, a 720px, onde o rail deixa de fazer sentido como
   coluna e vira barra inferior — o polegar alcança a base da tela, não a
   lateral esquerda. Acima disso, as grades já se resolvem por auto-fit. */
@media (max-width: 720px) {
  .panel-head, .panel-body, .panel-foot { padding-left: var(--sp-4); padding-right: var(--sp-4); }
  .action-dock-inner { flex-wrap: wrap; }
  .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); }

  .nav-rail, .nav-rail.expanded {
    inset: auto 0 0 0; width: 100%; height: auto;
    flex-direction: row; padding: 0;
    border-right: 0; border-top: 1px solid var(--hairline);
  }
  .nav-rail-header, .nav-rail-footer, .nav-rail-section { display: none; }
  .nav-rail-items, .nav-rail.expanded .nav-rail-items {
    flex-direction: row; align-items: center; justify-content: space-around;
    overflow-x: auto; overflow-y: hidden;
    /* env(): no iPhone a barra inferior fica sob o indicador do sistema, e o
       último item vira inalcançável. Zero em tudo que não tem entalhe. */
    padding: var(--sp-1) var(--sp-2) calc(var(--sp-1) + env(safe-area-inset-bottom));
    gap: 0;
    mask-image: none; /* o degradê vertical não faz sentido na barra horizontal */
    scrollbar-width: thin;
  }
  .nav-rail-item, .nav-rail.expanded .nav-rail-item {
    flex-direction: column; width: auto; min-width: 0; flex: 1 1 0; height: auto;
    padding: var(--sp-2) 2px var(--sp-2); gap: 0; border-radius: var(--r-sm);
  }
  /* Cinco destinos + "Mais". Onze rótulos numa barra de 390px viravam
     "AtividadeOrtro / NERReveal" com rolagem lateral — e rolagem lateral numa
     barra de navegação é invisível: ninguém procura. Os secundários abrem numa
     folha ACIMA da barra, onde há espaço para o nome inteiro. */
  .nav-rail-item:not([data-primario]) { display: none; }
  .nav-rail.mostrar-todos { flex-direction: column; }
  .nav-rail.mostrar-todos .nav-rail-item:not([data-primario]) { display: flex; }
  .nav-rail.mostrar-todos .nav-rail-items {
    flex-wrap: wrap; justify-content: flex-start;
    max-height: 60vh; overflow-y: auto;
  }
  .nav-rail.mostrar-todos .nav-rail-item { flex: 0 0 25%; }
  .nav-more { display: flex; }
  /* B5 · Na barra do celular o rótulo VOLTA (o recolhimento só-ícone é do rail
     vertical) e pode ocupar DUAS linhas: "Identidades" e "Configurações" não
     cabem em ~70px numa linha, e cortar com reticências deixava "Identidad…".
     Duas linhas curtas leem melhor que uma cortada. */
  .nav-rail:not(.expanded) .nav-rail-item > span:not(.nav-icon),
  .nav-rail-item > span:not(.nav-icon) {
    position: static; width: auto; height: auto; margin: 0; padding: 0;
    overflow: visible; clip: auto; border: 0;
    max-width: 100%; font-size: var(--t-caption); line-height: 1.15;
    white-space: normal; text-overflow: clip;
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  }
  .nav-rail:not(.expanded) .nav-rail-item { padding: var(--sp-2) 2px; }
  .nav-rail-item .nav-icon, .nav-rail.expanded .nav-rail-item .nav-icon,
  .nav-rail:not(.expanded) .nav-rail-item .nav-icon { width: 100%; height: 24px; }
  .nav-rail.expanded .nav-rail-item > span:not(.nav-icon) { margin-top: var(--sp-1); font-size: var(--t-caption); }

  main.with-rail, .nav-rail.expanded ~ main.with-rail {
    margin-left: 0; width: 100%;
    padding-bottom: calc(84px + env(safe-area-inset-bottom));
  }
  .page-content { padding: var(--sp-5) var(--sp-4) var(--sp-8); }
  .top-bar { padding: var(--sp-3) var(--sp-4); }
  .top-bar h1 { font-size: var(--t-title-2); line-height: var(--t-title-2-lh);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* B3 · O logo CONTINUA no celular (antes era `display: none`): a marca é o que
     identifica o produto justamente na tela em que o resto foi cortado. Reduz
     para 18px, que é o piso em que o traço da marca ainda lê. */
  .top-bar-logo { height: 18px; flex: 0 0 auto; }
  /* O botão da paleta sai inteiro no celular: não há Ctrl+K num telefone, e os
     56px que ele ocupava faziam o título ser cortado — medido em 375px, o h1
     precisava de 135px e tinha 109px, então "Configurações" virava
     "Configur…". A navegação no telefone é a barra inferior. */
  .cmdk-abrir { display: none; }
  #snackbar-container { left: var(--sp-3); right: var(--sp-3); bottom: 92px; transform: none; }
  .snackbar { min-width: 0; max-width: none; }
  .login-card { padding: var(--sp-6); }
  .meter-name { width: 6rem; }
  .veredito-itens { flex-direction: column; gap: var(--sp-2); }
}

/* ---- A8 · /settings em tela estreita ------------------------------------
   Abaixo de 640px a tabela de três colunas comprimia a coluna do meio para
   ~60px: o input de um endereço, de um timeout ou de uma chave ficava com espaço
   para três caracteres — o valor era ilegível E não editável, que é o defeito
   mais grave de uma tela de configuração no telefone (é justamente o cenário do
   plantão). Cada linha passa a ser um CARTÃO, com o mesmo padrão `data-rotulo`
   que /policies e /ratelimit já usavam: identificação em bloco, controle em
   largura total, selo de aplicação embaixo. */
@media (max-width: 640px) {
  .data-table--cartao, .data-table--cartao tbody,
  .data-table--cartao tr, .data-table--cartao td { display: block; width: auto; }
  .data-table--cartao thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
  .data-table--cartao tr {
    border: 1px solid var(--hairline); border-radius: var(--r-sm);
    margin-bottom: var(--sp-3); padding: var(--sp-3);
  }
  .data-table--cartao td { border-bottom: 0; padding: var(--sp-1) 0; text-align: left; }
  .data-table--cartao td.num { text-align: left; }
  /* O rótulo da COLUNA é ruído aqui: cada célula já se explica pelo conteúdo
     (nome do ajuste, controle, selo). Só "Valor" ganha marca, para separar a
     identificação do que é editável. */
  .data-table--cartao td[data-rotulo="Valor"] {
    margin-top: var(--sp-2); padding-top: var(--sp-2);
    border-top: 1px dashed var(--hairline);
  }
  /* Controle em largura TOTAL: é a correção do campo de 60px. O botão desce para
     a linha de baixo em vez de dividir a largura com o campo — muitos valores aqui
     são caminho de arquivo e URL, e cada pixel de campo é caractere visível
     (medido: 240px com o botão ao lado → 311px com ele embaixo). */
  .data-table--cartao .row-tight { flex-wrap: wrap; width: 100%; }
  .data-table--cartao .row-tight .input { flex: 1 1 100%; min-width: 0; }
  .data-table--cartao .row-tight .btn { margin-left: auto; }
  .data-table--cartao .setting-label { font-size: var(--t-headline); }
  .table-scroll:has(.data-table--cartao) { overflow-x: visible; }
  /* A barra de pendência empilha: três blocos lado a lado em 375px não cabem. */
  .action-dock-inner { flex-direction: column; align-items: stretch; }
  .action-dock-inner .btn { width: 100%; }
}
