/* ═══════════════════════════════════════════════════════════════
   Reset & base
   ═══════════════════════════════════════════════════════════════ */
* { box-sizing: border-box; }
html, body {
  margin: 0; padding: 0;
  background: var(--bg-base); color: var(--text-primary);
  font-family: var(--font-sans);
  font-size: var(--fs-body); line-height: var(--lh-body);
  font-feature-settings: 'cv11', 'ss01', 'ss03';
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

a { color: var(--text-primary); text-decoration: none; }
a:hover { text-decoration: underline; }

::selection { background: var(--accent-muted); color: var(--text-primary); }

/* FOCO VISÍVEL — a única cor de marca que entra no conteúdo, e ela entra
   porque é transitória: aparece um anel por vez, só por teclado, e some.
   Não compete com o âmbar de dado (`--p-google`, `--conf-fallback`) porque
   nunca está parada na tela ao lado deles.

   `outline`, e não `box-shadow`: sombra é RECORTADA por qualquer ancestral com
   `overflow: hidden` — e a tabela, o painel da conversa e o seletor de cliente
   todos têm. O anel sumiria justamente onde mais se navega por teclado. */
:focus { outline: none; }
:focus-visible {
  outline: 2px solid var(--marca);
  outline-offset: 2px;
  border-radius: var(--raio-sm);
}

/* NÚMERO SEMPRE TABULAR — em tabela, cartão, campo e etiqueta. Antes dependia
   da classe `.num` estar lembrada em cada lugar; digito que não alinha entre
   linhas é o que faz uma coluna de valores parecer desarrumada. */
.num, td, th, input, .valor, .badge, .card-stat, .conv-hora, .venda-valor {
  font-variant-numeric: tabular-nums;
}

/* ═══════════════════════════════════════════════════════════════
   Marca — a troca de arquivo por tema
   ═══════════════════════════════════════════════════════════════
   O logo completo tem a palavra "trakzo" desenhada, e as letras são de UMA
   cor só dentro do arquivo: `#ffffff` no branco, `#000000` no preto. Não há
   como fazê-las herdar `currentColor`, porque o logo entra como <img> e não
   inline — e entrar inline foi descartado (6 KB em toda página e ids de
   exportador que colidem).

   Então os DOIS arquivos vão no HTML e aqui se esconde um. Sem isto, um
   clique no #theme-toggle apaga a palavra: branco sobre `--bg-base` claro
   (#FAFAFA) some, e sobra o "o" laranja sozinho.

   O padrão sem `data-theme` é o ESCURO, igual ao script inline do layout —
   as duas regras precisam concordar, ou a marca pisca na primeira pintura.

   O `display` das duas mora AQUI, e não num `style=` no <img>: estilo inline
   vence folha de estilo, então um `display:block` no atributo anularia o
   `display:none` desta seção e os dois logos apareceriam lado a lado. Foi o
   que aconteceu na primeira tentativa, visto no navegador antes de subir. */
.marca-trakzo { display: block; width: auto; max-width: 100%; }
.marca-trakzo-claro  { display: none; }
[data-theme="light"] .marca-trakzo-escuro { display: none; }
[data-theme="light"] .marca-trakzo-claro  { display: block; }

/* ═══════════════════════════════════════════════════════════════
   Legacy layout (login, index) — retrocompatibilidade
   ═══════════════════════════════════════════════════════════════ */
.container { max-width: 1200px; margin: 0 auto; padding: var(--sp-5); }


/* ═══════════════════════════════════════════════════════════════
   Tipografia
   ═══════════════════════════════════════════════════════════════ */
h1, h2, h3, h4 { color: var(--text-primary); margin: 0; }
h1 { font-size: var(--fs-h1);      line-height: var(--lh-h1);      font-weight: 600; letter-spacing: var(--track-h1); }
h2 { font-size: var(--fs-h2);      line-height: var(--lh-h2);      font-weight: 600; }
h3 { font-size: var(--fs-body);    line-height: var(--lh-body);    font-weight: 600; }

code { font-family: var(--font-mono); font-size: 0.92em; }

/* ═══════════════════════════════════════════════════════════════
   Formulários
   ═══════════════════════════════════════════════════════════════ */
form { display: flex; flex-direction: column; gap: var(--sp-3); }
label {
  display: flex; flex-direction: column; gap: 6px;
  font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: 500; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-tertiary);
}
input, select, textarea {
  padding: var(--sp-2) var(--sp-3); height: 36px;
  border: 1px solid var(--border); border-radius: var(--raio-sm);
  /* O campo AFUNDA. Ele era `--bg-elevated`, o mesmo tom do hover de linha:
     um campo mais claro que o painel que o contém lê como algo saliente, e
     campo é onde se escreve, não algo que se destaca. */
  background: var(--bg-afundado); color: var(--text-primary);
  font-family: var(--font-sans); font-size: var(--fs-body); line-height: var(--lh-body);
  transition: border-color var(--trans);
}
textarea { height: auto; padding: var(--sp-3); line-height: 1.5; }
input:hover, select:hover, textarea:hover { border-color: var(--border-hover); }
input:focus-visible, select:focus-visible, textarea:focus-visible {
  border-color: var(--border-hover);
}
select {
  appearance: none; -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23A1A1AA' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'><polyline points='6 9 12 15 18 9'/></svg>");
  background-repeat: no-repeat; background-position: right 10px center;
  padding-right: var(--sp-6); cursor: pointer;
}
::placeholder { color: var(--text-tertiary); }

/* ═══════════════════════════════════════════════════════════════
   Botões
   ═══════════════════════════════════════════════════════════════ */
/* TRÊS PESOS, e o quarto é o destrutivo.
   O que estava invertido (apontado pelo dono em 19/08): `button[type=submit]`
   sem classe cai na regra do primário, então "Remover acesso" — ação
   SECUNDÁRIA e DESTRUTIVA — virava o elemento mais pesado da tela de Usuários,
   e "Sair" dominava a barra lateral pelo mesmo motivo. Os dois ganharam classe
   própria; a regra do primário não mudou, porque ela está certa. */
button {
  padding: var(--sp-2) 14px; height: 36px;
  border: 1px solid var(--border); border-radius: var(--raio);
  background: var(--bg-surface); color: var(--text-primary);
  font-family: var(--font-sans); font-size: var(--fs-body); font-weight: 500;
  cursor: pointer; transition: background var(--trans), border-color var(--trans), color var(--trans);
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  white-space: nowrap;
}
button:hover { background: var(--bg-elevated); border-color: var(--border-hover); }
button:disabled { opacity: 0.4; cursor: not-allowed; }

/* A LISTA DE EXCEÇÕES do primário precisa cobrir TODA classe que já significa
   "não é a ação principal". `.perigo` e `.destrutiva` faltavam, e o resultado
   era invisível na leitura do CSS: `.zona-perigo .perigo` pesa 0,2,0 contra os
   0,3,1 desta regra, então "Apagar" e "Ignorar" — as duas ações mais graves do
   sistema — renderizavam como botão primário cheio, exatamente ao contrário do
   que a classe delas pedia. */
button.primario,
button[type=submit]:not(.secundario):not(.link-btn):not(.critico):not(.perigo):not(.destrutiva) {
  background: var(--accent); color: var(--accent-fg); border-color: var(--accent);
  font-weight: 600;
}
button.primario:hover,
button[type=submit]:not(.secundario):not(.link-btn):not(.critico):not(.perigo):not(.destrutiva):hover {
  background: var(--accent-strong); border-color: var(--accent-strong);
}
button.secundario {
  background: var(--bg-surface); color: var(--text-primary); border-color: var(--border-hover);
}
button.secundario:hover { background: var(--bg-elevated); border-color: var(--border-hover); }

/* DESTRUTIVA: aparência de texto. Ela não pede para ser clicada — pede para ser
   encontrada por quem a procura. A cor só aparece no hover, quando já não há
   dúvida de que o cursor está ali. Distinta de `.critico`, que é o botão cheio
   de confirmação dentro de um diálogo, onde apagar já foi decidido. */
button.destrutiva {
  background: transparent; border-color: transparent;
  color: var(--text-secondary); font-weight: 500;
}
button.destrutiva:hover {
  background: var(--danger-muted); border-color: transparent; color: var(--critico);
}
button.critico {
  background: var(--critico); color: #fff; border-color: var(--critico); font-weight: 600;
}
button.critico:hover { background: #dc2626; border-color: #dc2626; }
button.link-btn {
  background: transparent; border: none; color: var(--text-primary); padding: 0; height: auto;
  font-weight: 500;
}
button.link-btn:hover { color: var(--accent); background: transparent; }
button.ghost {
  background: transparent; border: 1px solid transparent; color: var(--text-secondary);
}
button.ghost:hover { background: var(--bg-surface); color: var(--text-primary); }

/* ═══════════════════════════════════════════════════════════════
   Tabelas
   ═══════════════════════════════════════════════════════════════ */
table {
  width: 100%; border-collapse: collapse; margin-top: var(--sp-4);
}
th, td {
  /* Mais apertado: 14px de respiro vertical numa ferramenta que se opera o dia
     inteiro é altura de site, não de tabela de trabalho. */
  text-align: left; padding: var(--sp-3) var(--sp-4);
  border-bottom: 1px solid var(--border);
  font-size: var(--fs-body); line-height: var(--lh-body);
  color: var(--text-primary);
}
th {
  font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: 600; text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-tertiary); background: transparent;
  padding: var(--sp-2) var(--sp-4);
}
tbody tr { transition: background var(--trans); }
tbody tr:hover { background: var(--bg-elevated); }
tbody tr:last-child td { border-bottom: none; }
td .num, td.num, th.num { font-variant-numeric: tabular-nums; text-align: right; }
td a { color: var(--text-primary); text-decoration: none; }
td a:hover { text-decoration: underline; }
tr.linha-sem-origem td { color: var(--text-tertiary); font-style: italic; }
tr.linha-total td { font-weight: 600; border-top: 2px solid var(--border); }
.legenda-oculta { font-size: var(--fs-label); font-weight: 400; color: var(--text-tertiary); margin-top: 2px; }

/* ═══════════════════════════════════════════════════════════════
   Erro (login e forms genéricos)
   ═══════════════════════════════════════════════════════════════ */
.erro {
  color: var(--critico); padding: var(--sp-3) var(--sp-4);
  border: 1px solid transparent; border-left: 3px solid var(--critico);
  border-radius: var(--raio); background: var(--danger-muted);
  font-size: var(--fs-body);
}

/* ═══════════════════════════════════════════════════════════════
   Cards de resumo — o número antes da tabela crua
   Quem abre o painel quer saber "como está" antes de "o que aconteceu".
   ═══════════════════════════════════════════════════════════════ */
/* .cards-resumo e .cards (dashboard) compartilham a mesma aparência de
   propósito: dois sistemas de card no mesmo painel seria justamente a
   inconsistência que esta fase existe pra eliminar. A diferença é só a
   densidade da grade — a de leads cabe mais colunas. */
.cards-resumo {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-3);
  margin-bottom: var(--sp-5);
}
.card-stat {
  background: var(--bg-surface);
  border: var(--bord);
  border-radius: var(--raio-md);
  padding: var(--sp-4);
  display: flex; flex-direction: column; gap: 2px;
  box-shadow: var(--sombra-sm);
  transition: box-shadow var(--trans), border-color var(--trans);
}
/* O NÚMERO É O PONTO ALTO DA TELA e precisa parecer isso. "43,1%" e "4 min"
   são o que a pessoa veio ver; o rótulo acima só diz do que se trata. */
.card-stat .valor {
  font-size: var(--fs-metric); line-height: var(--lh-metric);
  font-weight: 600; letter-spacing: var(--track-metric);
  font-variant-numeric: tabular-nums;
}
.card-stat .rotulo {
  font-size: var(--fs-label); line-height: var(--lh-label);
  text-transform: uppercase; letter-spacing: var(--track-label); font-weight: 600;
  color: var(--text-tertiary);
}
/* A cor do número segue o estado que ele mede — mesma escala das badges,
   pra leitura bater o olho entre card e tabela. */
.card-stat.codigo   .valor { color: var(--conf-codigo-fg); }
.card-stat.manual   .valor { color: var(--conf-manual-fg); }
.card-stat.fallback .valor { color: var(--conf-fallback-fg); }
.card-stat.sem      .valor { color: var(--conf-sem-fg); }
/* Card que pede ação fica com uma faixa lateral — forma, não só cor,
   pra não depender de enxergar matiz. */
.card-stat.acao {
  border-left: 3px solid var(--conf-manual-fg);
}

/* ═══════════════════════════════════════════════════════════════
   Badges — pill sem borda, muted + cor semântica
   ═══════════════════════════════════════════════════════════════ */
.badge {
  display: inline-block; padding: 3px var(--sp-2);
  border-radius: var(--raio-sm); border: none;
  font-size: var(--fs-label); line-height: var(--lh-label);
  font-weight: 500; letter-spacing: 0; text-transform: none;
  background: var(--bg-elevated); color: var(--text-secondary);
}
/* ── Os 4 estados de confiança ────────────────────────────────
   Únicos lugares do painel onde matiz carrega significado. As classes
   vêm de services/confianca.js — não escreva o ternário na view. */
.badge.codigo   { background: var(--conf-codigo-bg);   color: var(--conf-codigo-fg); }
.badge.manual   { background: var(--conf-manual-bg);   color: var(--conf-manual-fg); }
.badge.fallback { background: var(--conf-fallback-bg); color: var(--conf-fallback-fg); }
.badge.sem      { background: var(--conf-sem-bg);      color: var(--conf-sem-fg); }

/* ── Semânticos, fora da escala de confiança ──────────────────
   Erro não é nível de confiança: misturar os dois é como o painel perde
   significado. Ficam separados de propósito. */
.badge.info    { background: var(--info-muted);    color: var(--info); }
.badge.critico { background: var(--danger-muted);  color: var(--critico); }
.badge.neutro  { background: var(--bg-elevated);   color: var(--text-tertiary); }
/* Aliases legados — status de conexão usa a marca WhatsApp, não confiança */
.badge.sucesso      { background: var(--conf-codigo-bg);   color: var(--conf-codigo-fg); }
.badge.aviso        { background: var(--conf-fallback-bg); color: var(--conf-fallback-fg); }
.badge.conectado    { background: var(--conf-codigo-bg);   color: var(--conf-codigo-fg); }
.badge.desconectado { background: var(--danger-muted);     color: var(--critico); }

/* ═══════════════════════════════════════════════════════════════
   Login (usa .container.login)
   ═══════════════════════════════════════════════════════════════ */
/* ═══════════ CONECTAR — a tela que o CLIENTE vê ═══════════
   Ela é a primeira coisa que o dono de um número vê do Trakzo, e é por ela que
   se reconecta um número que caiu. Até 19/08 tinha DUAS linhas de estilo:
   `text-align: center` e um teto na imagem. O resto era HTML nu.

   O defeito que o dono viu: `text-align: center` numa `<ul>` centraliza o TEXTO
   e não os marcadores — os bullets ficavam colados na margem esquerda com as
   frases soltas no meio. Centralizar é decisão de bloco, não de parágrafo.

   Ela NÃO usa o app-shell, e isso é certo: quem abre não faz login e não
   navega. Segue a família do `.login` — coluna estreita, cartão, marca em cima.
   O `data-theme` é fixo em `dark` no próprio HTML, então aqui não há par claro
   a manter. */
.conectar { max-width: 480px; margin: 0 auto;
            padding: var(--sp-7) var(--sp-4) var(--sp-8);
            display: flex; flex-direction: column; gap: var(--sp-4); }
.conectar .marca { display: flex; justify-content: center; }
.conectar h1 { text-align: center; margin: 0; }
/* A abertura é a única frase centralizada, e por ser uma linha só. */
.conectar .abertura { text-align: center; color: var(--text-secondary); margin: 0; }

.conectar .cartao { background: var(--bg-surface); border: var(--bord);
                    border-radius: var(--raio-md); padding: var(--sp-5);
                    box-shadow: var(--sombra-md); }
.conectar .cartao > :first-child { margin-top: 0; }
.conectar .cartao > :last-child { margin-bottom: 0; }
.conectar .cartao h2 { font-size: var(--fs-h2); line-height: var(--lh-h2);
                       margin: 0 0 var(--sp-3); }
.conectar .cartao h2 small { color: var(--text-tertiary); font-weight: 400;
                             font-size: var(--fs-caption); }
.conectar .cartao p { margin: 0 0 var(--sp-3); color: var(--text-secondary); }
/* A lista alinhada à esquerda, que é a correção do defeito. */
.conectar ul { margin: 0 0 var(--sp-4); padding-left: var(--sp-5);
               color: var(--text-primary); }
.conectar ul li + li { margin-top: var(--sp-2); }

/* O ACEITE é um alvo de clique, e o `<label>` inteiro faz parte dele: caixa
   pequena sozinha é o clique mais fácil de errar no celular.
   `flex-direction: row` EXPLÍCITO — a regra base `label { flex-direction:
   column }` é a mesma armadilha que `.filtros` e `.form-link-novo` já
   documentam, e eu caí nela de novo em 19/08: a caixa ficou EMPILHADA sobre o
   texto. Declarar `display: flex` sem declarar a direção é herdar a direção
   errada de alguém.
   E o resto desta regra desfaz o estilo de RÓTULO DE CAMPO que o `label` base
   aplica — caixa alta, 12px, cor terciária. Isto aqui não é rótulo de campo, é
   uma frase que a pessoa lê e aceita; em caixa alta ela lia como etiqueta. */
.conectar .aceite { display: flex; flex-direction: row; align-items: flex-start;
                    gap: var(--sp-3); cursor: pointer;
                    text-transform: none; letter-spacing: normal;
                    font-size: var(--fs-body); line-height: var(--lh-body);
                    font-weight: 400; color: var(--text-primary); }
/* A caixa não é campo de texto: sem a borda, o fundo afundado e a altura de
   36px que a regra base dá a todo `input`. Quem pinta é `accent-color`. */
.conectar .aceite input[type=checkbox] { flex: none; width: 18px; height: 18px;
                                         margin: 1px 0 0; padding: 0;
                                         border: 0; background: none;
                                         cursor: pointer;
                                         accent-color: var(--accent); }
.conectar form { gap: var(--sp-4); }
.conectar form button { width: 100%; }

/* O QR PRECISA DE FUNDO CLARO PARA SER LIDO — isto é função, não enfeite. A
   imagem chega com fundo transparente, e sobre o #0E1116 do tema escuro o
   leitor do celular perde o contraste entre os módulos.
   O `color` escuro acompanha o fundo branco porque o JavaScript escreve texto
   DENTRO desta caixa ("Gerando QR…", "Conectado com sucesso"): sem ele, herda
   o quase-branco do tema e some no papel. */
/* QUADRADO E CENTRADO. Sem o teto ele virava uma lousa branca da largura do
   cartão com um código pequeno no meio — o papel tem que ter o tamanho do que
   está impresso nele. `aspect-ratio` em vez de altura fixa para o quadrado
   sobreviver ao encolher no celular. */
.conectar #qr-box { background: #FFFFFF; color: #14181F;
                    border-radius: var(--raio-md); padding: var(--sp-4);
                    display: flex; align-items: center; justify-content: center;
                    max-width: 272px; width: 100%; aspect-ratio: 1;
                    margin: 0 auto; text-align: center; }
.conectar #qr-box p { margin: 0; color: inherit; }
.conectar #qr-img { max-width: 240px; width: 100%; height: auto; display: block; }
/* O teto de 300px era global e pegava TODA imagem da tela — inclusive a marca,
   que passou a existir aqui em 19/08. Escopado ao QR. */
.conectar .estado { text-align: center; color: var(--text-secondary);
                    font-size: var(--fs-caption); margin: 0; }
.conectar .estado strong { color: var(--text-primary); font-weight: 600; }
.login {
  max-width: 380px; margin: 10vh auto 0;
  padding: var(--sp-6);
  background: var(--bg-surface); border: var(--bord); border-radius: var(--raio-lg);
}
.login h1 { margin-bottom: var(--sp-4); }

/* ═══════════════════════════════════════════════════════════════
   Shell — app-shell (sidebar + topbar + main)
   ═══════════════════════════════════════════════════════════════ */
/* A MOLDURA FICA PARADA, e só o conteúdo rola.
   Era `min-height: 100vh` com `overflow: visible`: o shell crescia com o
   conteúdo (2405px numa lista de 30 linhas), a PÁGINA inteira rolava, e a barra
   lateral subia junto — o botão "Sair" simplesmente saía da vista. O
   `overflow-y: auto` do `.main` nunca entrava em ação, porque não havia altura
   nenhuma para ele respeitar.
   `100dvh` antes de `100vh`: no celular a barra de endereço entra e sai, e
   `100vh` é a altura COM ela escondida — o rodapé ficaria cortado. Navegador
   que não conhece `dvh` ignora a linha e fica com a anterior. */
.app-shell {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: var(--altura-topo) 1fr;
  grid-template-areas: "sidebar topbar" "sidebar main";
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}

/* ─── Sidebar ─────────────────────────────────────────── */
.sidebar {
  grid-area: sidebar;
  background: var(--bg-base);
  border-right: 1px solid var(--border);
  display: flex; flex-direction: column;
  padding: 0;
  /* `min-height: 0` pelo mesmo motivo que o `.main` tem `min-width: 0`: item de
     grid não encolhe abaixo do próprio conteúdo por padrão, e sem isto um menu
     longo empurraria o rodapé para fora em vez de rolar. */
  min-height: 0;
}
/* O bloco do logo termina EXATAMENTE onde a barra de topo termina — as duas
   bordas horizontais são a mesma linha atravessando a tela. A altura vem do
   token, e não de `padding` mais a altura da imagem, que era o que produzia os
   13px de desencontro. */
/* `flex-direction: row` EXPLICITO. Este bloco virou um `<label>` em 19/08 (a
   barra da gaveta), e a regra base `label { flex-direction: column }` passou a
   se aplicar a ele -- sem esta linha o hamburguer EMPILHA sobre a marca. Quem
   achou foi o guarda `form-em-linha`, minutos depois de eu tê-lo ensinado a
   olhar `<label>`. A mesma armadilha, pela terceira vez neste arquivo. */
.sidebar .logo {
  display: flex; flex-direction: row; align-items: center; gap: 10px;
  height: var(--altura-topo); flex: none; box-sizing: border-box;
  padding: 0 var(--sp-4); border-bottom: 1px solid var(--border);
  color: var(--text-primary); font-weight: 600; font-size: var(--fs-lg);
}
.sidebar .logo .marca-trakzo { max-height: 24px; width: auto; }
.sidebar .logo svg { flex-shrink: 0; }

/* ─── A GAVETA DO CELULAR (19/08) ─────────────────────────
   Caixa de seleção, e NÃO `<details>` — a primeira versão usou `<details>` e
   custou dois defeitos graves, os dois medidos numa revisão:

   1. `nav` e `.rodape` dentro do `<details>` deixam de ser itens flex de
      `.sidebar`, então `nav { flex: 1 }` (abaixo) virava letra morta: o menu
      caiu de 746px para 173px e o "Sair" desgrudou do rodapé.
   2. O desktop dependia de forçar `display` em filho de `<details>` FECHADO,
      que é comportamento de navegador e não de especificação. Em Safari < 18.4
      e Firefox < 139 a barra lateral renderizava só o logo.

   Com a caixa, `nav` e `.rodape` continuam filhos DIRETOS de `.sidebar` e o
   desktop é exatamente o que sempre foi — não há uma regra sequer aqui que o
   altere. Tudo que existe abaixo é ou esconder a caixa, ou valer só no celular.

   Continua zero JavaScript. */

/* ESCONDIDA MAS FOCÁVEL. `display: none` ou `hidden` a tirariam da ordem de
   tabulação, e a gaveta ficaria impossível de abrir pelo teclado. */
.gaveta-check {
  position: absolute; width: 1px; height: 1px;
  opacity: 0; margin: 0; padding: 0; border: 0;
  overflow: hidden; clip-path: inset(50%);
}
/* O anel de foco aparece na BARRA, que é o que a pessoa enxerga. */
.gaveta-check:focus-visible + .gaveta-barra {
  outline: 2px solid var(--marca); outline-offset: -2px;
}
/* No desktop a barra é a faixa da marca: sem hambúrguer e sem promessa de
   clique. Um cursor de mão anunciaria uma ação que não acontece. */
.sidebar .gaveta-abre, .sidebar .gaveta-fecha { display: none; }
.gaveta-barra { cursor: default; }

/* O menu rola por dentro se for longo; o rodapé com "Sair" fica ancorado. */
.sidebar nav {
  display: flex; flex-direction: column; flex: 1;
  padding: var(--sp-4) var(--sp-3); gap: 2px;
  overflow-y: auto; min-height: 0;
}
.sidebar .secao-label {
  font-size: var(--fs-label); line-height: var(--lh-label);
  text-transform: uppercase; letter-spacing: var(--track-label);
  color: var(--text-tertiary); font-weight: 600;
  padding: 0 var(--sp-3); margin: var(--sp-2) 0 var(--sp-1);
}
.sidebar nav a {
  display: flex; align-items: center; gap: 10px;
  padding: 0 var(--sp-3); height: 36px; border-radius: var(--raio-sm);
  color: var(--text-secondary); font-size: var(--fs-body); font-weight: 500;
  position: relative; transition: background var(--trans), color var(--trans);
  border-left: none;
}
.sidebar nav a svg { flex-shrink: 0; opacity: 0.8; }
.sidebar nav a:hover {
  background: var(--bg-elevated); color: var(--text-primary); text-decoration: none;
}
.sidebar nav a:hover svg { opacity: 1; }

/* O ITEM ATIVO é um dos três lugares onde o laranja da marca aparece.
   A moldura (barra lateral e topo) nunca carrega dado, então aqui a cor não
   disputa significado com nada — é o que torna a regra por zona possível.
   Trilho de 2px E fundo: dois canais para a mesma informação, porque um item
   de menu ativo é o que ancora a pessoa na tela em que ela está. */
.sidebar nav a.ativo {
  background: var(--marca-fraca); color: var(--text-primary); font-weight: 600;
}
.sidebar nav a.ativo::before {
  content: ''; position: absolute; left: -12px; top: 6px; bottom: 6px;
  width: 2px; background: var(--marca); border-radius: 0 2px 2px 0;
}
.sidebar nav a.ativo svg { opacity: 1; color: var(--marca); }
.sidebar nav a .badge {
  margin-left: auto; background: var(--bg-elevated); color: var(--text-tertiary);
  font-size: var(--fs-label); padding: 1px 6px;
}
.sidebar nav a.ativo .badge { background: var(--bg-base); color: var(--text-secondary); }

.sidebar .rodape {
  flex: none;
  padding: var(--sp-3) var(--sp-3);
  border-top: 1px solid var(--border);
}
.sidebar .rodape form { margin: 0; }
/* "SAIR" NUNCA PREENCHIDO. Ele já era transparente aqui, mas a regra do
   primário (`button[type=submit]:not(...)`, peso 0,3,1) vencia esta (0,2,1) e o
   pintava de branco sólido — o elemento mais pesado da barra lateral, para a
   ação menos importante dela. `.destrutiva` no markup resolve na origem; esta
   linha continua como a definição de forma. */
.sidebar .rodape button {
  width: 100%; background: transparent; border: 1px solid transparent;
  color: var(--text-secondary); text-align: left; padding: 0 var(--sp-3); height: 36px;
  font-weight: 500; font-size: var(--fs-body);
  display: flex; align-items: center; gap: 10px; justify-content: flex-start;
}
.sidebar .rodape button:hover {
  color: var(--text-primary); background: var(--bg-elevated); border-radius: var(--raio-sm);
}

/* ─── Topbar ─────────────────────────────────────────── */
.topbar {
  grid-area: topbar;
  background: var(--bg-base);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 var(--sp-6);
}
.topbar .breadcrumb {
  color: var(--text-tertiary); font-size: var(--fs-caption);
  display: flex; align-items: center; gap: 6px;
}
.topbar .breadcrumb a { color: var(--text-tertiary); }
.topbar .breadcrumb a:hover { color: var(--text-primary); text-decoration: none; }
.topbar .direita { display: flex; align-items: center; gap: var(--sp-3); }
.topbar .theme-toggle {
  background: transparent; border: 1px solid transparent;
  border-radius: var(--raio-sm); width: 32px; height: 32px; padding: 0;
  color: var(--text-secondary); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--trans), color var(--trans);
}
.topbar .theme-toggle:hover { background: var(--bg-surface); color: var(--text-primary); }
.topbar .theme-toggle .icone-tema-dark,
.topbar .theme-toggle .icone-tema-light { display: none; }
[data-theme="dark"] .topbar .theme-toggle .icone-tema-dark { display: inline-flex; }
[data-theme="light"] .topbar .theme-toggle .icone-tema-light { display: inline-flex; }

/* ─── Main ─────────────────────────────────────────── */
.main {
  grid-area: main; padding-block: var(--sp-6);
  background: var(--bg-base); overflow-y: auto;
  /* `min-height: 0` para o `overflow-y` acima valer: sem ele o item de grid
     cresce com o conteúdo e quem rola volta a ser a página. */
  min-height: 0;
  /* `min-width: 0` NÃO é decoração. Como item de grid na coluna `1fr`, o padrão
     é `min-width: auto`, que significa "nunca fique menor que meu conteúdo".
     Um único elemento largo — o textarea do pixel exige 1358px — então empurra
     a coluna e a página inteira vaza para a direita, cortando todo o resto.
     Medido em 18/08, depois que o dono mandou a captura da tela cortada. */
  min-width: 0;
}
/* LARGURA DE LEITURA.
   Sem teto, um formulário de dois campos se espalha por 1600px e abre um vazio
   no meio — foi o "mal dimensionado" que o dono descreveu. A referência que ele
   deu (Itaú, Nubank) limita a coluna de conteúdo e deixa o resto respirar.
   Tabelas largas não sofrem: elas rolam dentro de `.tabela-rolante`. */
/* A COLUNA é centralizada, e NÃO cada filho.
   Primeira tentativa: `.main > * { max-width: 1440px; margin-inline: auto }`.
   Ela centraliza cada filho DENTRO dos 1440 — então um bloco estreito
   (`.form-estreito`, 480px) flutua no meio enquanto a tabela ao lado começa na
   borda. Medido na tela de Usuários: três bordas esquerdas diferentes, 272,
   334 e 814. Teste verde, layout errado.
   O `padding-inline` centraliza a coluna inteira e deixa TODOS os filhos
   alinhados na mesma borda, que é o que se quer. O `max()` garante o respiro
   mínimo quando a janela é menor que 1440. */
.main { padding-inline: max(var(--sp-6), (100% - 1440px) / 2); }
.main > * { max-width: 1440px; }
/* ...MENOS o que sai do fluxo e cobre a tela.
   `.sobreposicao` é `position: fixed; inset: 0`, ou seja, a largura dela É a da
   janela -- e um teto de 1440px a corta. Numa tela de 1900px, medido: o fundo
   escuro ia de 0 a 1440 e o painel da conversa parava em 980-1440, no MEIO da
   tela, com um lado escurecido e o outro não. Foi assim que o dono viu, em
   20/08, e o defeito entrou junto com a largura de leitura (417a997, 18/08):
   ele só aparece em janela MAIOR que 1440, por isso passou.
   `.main > .sobreposicao` (0,2,0) vence `.main > *` (0,1,0) sem `!important`. */
.main > .sobreposicao { max-width: none; }

.main h1 { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 600; margin: 0 0 var(--sp-1); letter-spacing: var(--track-h1); }
.main h1 + .subtitulo {
  color: var(--text-secondary); margin: 0 0 var(--sp-5);
  font-size: var(--fs-body); line-height: var(--lh-body);
}
.main .cabecalho {
  display: flex; align-items: start; justify-content: space-between;
  gap: var(--sp-4); margin-bottom: var(--sp-5);
}
.main .cabecalho .acoes { display: flex; gap: var(--sp-2); align-items: center; }

/* ═══════════════════════════════════════════════════════════════
   Cards de KPI (dashboard)
   ═══════════════════════════════════════════════════════════════ */
.cards {
  display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: var(--sp-3) 0 var(--sp-5);
}
@media (max-width: 1024px) { .cards { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 640px)  { .cards { grid-template-columns: 1fr; } }

/* ═══════════════════════════════════════════════════════════════
   Responsivo — o suficiente pra conferir no celular
   ═══════════════════════════════════════════════════════════════ */
@media (max-width: 900px) {
  /* Sidebar vira faixa no topo; o conteúdo ocupa a largura toda. */
  .app-shell {
    grid-template-columns: 1fr;
    grid-template-areas: "sidebar" "topbar" "main";
    grid-template-rows: auto auto 1fr;
  }
  /* A BARRA LATERAL VIRA GAVETA, e não uma fileira de itens.
     O que havia antes era `flex-direction: row` com `overflow-x: auto` — e ele
     nunca chegava a rolar: os itens ENCOLHIAM e quebravam o texto, então
     "Painel geral" virava duas linhas e o terceiro item sobrava só com o ícone.
     Medido a 390px em 19/08: `scrollWidth === clientWidth`, ou seja, nada
     transbordava; estava tudo espremido. */
  .sidebar { border-right: 0; border-bottom: 1px solid var(--border); }
  .gaveta-barra { cursor: pointer; }
  .sidebar .gaveta-abre { display: inline-flex; }
  .gaveta-check:checked ~ .gaveta-barra .gaveta-abre { display: none; }
  .gaveta-check:checked ~ .gaveta-barra .gaveta-fecha { display: inline-flex; }
  /* Fechada, some tudo menos a barra. Aberta, o menu desce EM COLUNA e empurra
     o conteúdo — nunca em linha, que é o defeito que estamos consertando. */
  .gaveta-check:not(:checked) ~ nav,
  .gaveta-check:not(:checked) ~ .rodape { display: none; }
  .sidebar nav { flex-direction: column; }

  /* O MENU ROLA POR DENTRO, e o teto é o que garante o "Sair" alcançável.
     Sem isto, medido a 740×360 (celular deitado): a gaveta aberta media 289px
     numa barra de 283px, o `.app-shell` (que é `100dvh; overflow: hidden`)
     cortava, a página não rolava, e o único botão de sair ficava fora da tela.
     Com teto e rolagem própria, o menu rola e o rodapé fica sempre visível. */
  .sidebar nav { max-height: 50dvh; overflow-y: auto; min-height: 0; }

  /* Na gaveta há espaço, então o rótulo da seção e o "Sair" voltam a existir —
     eles só tinham sumido porque não cabiam na fileira. */
  .sidebar .secao-label { display: block; }
  .sidebar .rodape { display: block; }

  /* ─── ALVO DE TOQUE ───────────────────────────────────
     Medido a 390px em 19/08: 4 de 4 alvos no Painel geral e 18 de 18 no
     Rastreamento tinham MENOS de 44px de altura. A interface inteira estava
     dimensionada para ponteiro de mouse. 44px é o mínimo de toque, não gosto
     pessoal — e `min-height` para não encolher nada que já seja maior. */
  .sidebar nav a { min-height: 44px; height: auto; }
  .sidebar .rodape button { min-height: 44px; height: auto; }
  button, .link-btn { min-height: 44px; }
  input:not([type=checkbox]):not([type=radio]), select { min-height: 44px; }
  /* O BOTÃO DE TEMA É QUADRADO, e `min-height` sozinho o deformava: ele tem
     `height: 32px` fixo, e `min-height` vence `height` — virava 32 de largura
     por 44 de altura, uma pílula. Cresce nas DUAS medidas, que aliás é o que
     um alvo de toque precisa. */
  .topbar .theme-toggle { width: 44px; height: 44px; min-height: 44px; }

  /* A tabela rola DENTRO dela mesma. Sem isto, uma tabela de 8 colunas
     empurra a página inteira pro lado e o resto do layout quebra junto. */
  table { display: block; overflow-x: auto; white-space: nowrap; max-width: 100%; }

  .filtros { flex-wrap: wrap; }
  .cabecalho { flex-direction: column; align-items: flex-start; gap: var(--sp-3); }
}


/* ═══════════════════════════════════════════════════════════════
   Barra de filtros
   ═══════════════════════════════════════════════════════════════ */
.filtros {
  /* `flex-direction: row` EXPLICITO, e nao por omissao: a regra base
     `form { display:flex; flex-direction: column }` (linha ~60) se aplica aqui
     tambem, e sem esta linha os cinco filtros empilhavam na vertical -- com o
     `align-items: end` abaixo jogando todos para a direita da tela. Encontrado
     em 14/08 renderizando a tela; e defeito antigo, nao da tela de Conversas. */
  display: flex; flex-direction: row; flex-wrap: wrap; align-items: end; gap: var(--sp-3);
  padding: var(--sp-3); margin-bottom: var(--sp-4);
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg);
}
.filtros label { flex: 0 0 auto; min-width: 140px; }
.filtros button[type=submit] {
  height: 36px;
}
.filtros a.limpar {
  color: var(--text-tertiary); font-size: var(--fs-caption); padding: 0 var(--sp-2);
  align-self: center;
}
.filtros a.limpar:hover { color: var(--text-primary); text-decoration: none; }

/* ═══════════════════════════════════════════════════════════════
   Banner topo (avisos)
   ═══════════════════════════════════════════════════════════════ */
.banner {
  padding: var(--sp-3) var(--sp-4); border-radius: var(--raio);
  margin-bottom: var(--sp-4);
  border: 1px solid transparent; border-left-width: 3px;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  font-size: var(--fs-caption); line-height: 1.5;
}
.banner.aviso   { background: var(--warning-muted); color: var(--aviso);   border-left-color: var(--aviso); }
.banner.critico { background: var(--danger-muted);  color: var(--critico); border-left-color: var(--critico); }
.banner.sucesso { background: var(--accent-muted);  color: var(--accent);  border-left-color: var(--accent); }
.banner.info    { background: var(--info-muted);    color: var(--info);    border-left-color: var(--info); }

/* ═══════════════════════════════════════════════════════════════
   Modais
   ═══════════════════════════════════════════════════════════════ */
dialog.modal {
  border: 1px solid var(--border); border-radius: var(--raio-lg);
  background: var(--bg-surface); color: var(--text-primary);
  padding: var(--sp-5); box-shadow: var(--sombra-lg);
  max-width: 480px; width: 90%;
}
dialog.modal::backdrop { background: rgba(0,0,0,0.6); backdrop-filter: blur(2px); }
dialog.modal form { gap: var(--sp-3); }
dialog.modal .modal-acoes {
  display: flex; justify-content: flex-end; gap: var(--sp-2); margin-top: var(--sp-3);
}

/* ═══════════════════════════════════════════════════════════════
   Empty state
   ═══════════════════════════════════════════════════════════════ */
/* SEM BORDA TRACEJADA. Tracejado é a convenção de PLACEHOLDER — de coisa que
   deveria estar ali e não está —, e a lista vazia de um cliente novo não é uma
   falha, é o começo. O que sustenta o bloco agora é o respiro e o ícone. */
.tabela-vazia {
  padding: var(--sp-7) var(--sp-5); text-align: center;
  color: var(--text-secondary);
  border: none; background: transparent;
  display: flex; flex-direction: column; align-items: center; gap: var(--sp-1);
}
.vazio-icone { color: var(--text-tertiary); opacity: .45; margin-bottom: var(--sp-1); }
.tabela-vazia .titulo {
  color: var(--text-primary); font-weight: 600; font-size: var(--fs-h2);
  line-height: var(--lh-h2); letter-spacing: var(--track-h2); margin: 0;
}
.tabela-vazia .desc {
  color: var(--text-secondary); font-size: var(--fs-body); max-width: 46ch; margin: 0;
}

/* ═══════════════════════════════════════════════════════════════
   Item de fila (fila de confirmação de venda) — NÃO é `.passo-card`.
   `.passo-card`/`.passo-cabecalho` (abaixo) só existem escopados sob
   `.gerador-v3`, que é outro fluxo; reusar o nome por conveniência aqui
   deixaria a linha do comprovante sem borda, padding ou espaçamento nenhum.
   Nome próprio, não escopado, tokens já existentes.
   ═══════════════════════════════════════════════════════════════ */
.item-fila {
  padding: var(--sp-4); background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg); display: flex; flex-direction: column; gap: var(--sp-3);
  margin-bottom: var(--sp-4);
}
.item-fila-cabecalho {
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
}
.item-fila-cabecalho h3 {
  margin: 0; font-size: var(--fs-body); font-weight: 600; color: var(--text-primary); flex: 1;
}
.item-fila .peca {
  display: block; max-width: 100%; height: auto;
  border: 1px solid var(--border); border-radius: var(--raio-lg);
}
/* Popup do comprovante: por cima da tela, menor que ela, fundo escurecido. A
   lista continua atrás, na mesma posição de rolagem — decisão do dono:
   comprovante embutido destrói o ritmo visual da lista. */
/* `sobreposicao`, e NÃO `modal`: três telas do admin já têm
   `<dialog class="modal">` (clientes, detalhe do cliente, detalhe do lead), e
   uma regra de autor com `display: flex` vence o `display: none` que o
   navegador dá a `dialog` fechado — a classe compartilhada pintaria um overlay
   preto por cima daquelas telas, engolindo todo clique. Nome próprio resolve
   por construção; brigar por especificidade só adiaria o problema. */
.sobreposicao { position: fixed; inset: 0; background: rgba(0,0,0,.6); z-index: 50;
  display: flex; align-items: center; justify-content: center; padding: var(--sp-4); }
.sobreposicao[hidden] { display: none; }
.sobreposicao-caixa { background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg); padding: var(--sp-4); max-width: min(680px, 92vw);
  max-height: 88vh; overflow: auto; display: flex; flex-direction: column; gap: var(--sp-3); }
.sobreposicao-caixa img { display: block; max-width: 100%; height: auto; border-radius: var(--raio-lg); }
/* `[hidden]` esconde via `display: none` do navegador, e a regra acima tem mais
   especificidade que isso — sem esta linha, a imagem QUEBRADA continua à vista
   ao lado do aviso de "mídia não disponível". Defeito que nenhum teste pegou:
   eles verificam texto, não pintura. Achado olhando a tela. */
.sobreposicao-caixa img[hidden] { display: none; }
.sobreposicao-fechar { background: none; border: 1px solid var(--border); border-radius: var(--raio-lg);
  color: var(--text-secondary); padding: var(--sp-1) var(--sp-3); cursor: pointer;
  align-self: flex-end; }

/* A MOLDURA DA CONVERSA, no meio da tela e com o resto escurecido em volta.
   Era uma gaveta colada na borda direita até 20/08. O dono pediu a moldura
   ("tipo um retângulo no meio da tela, todo o resto, dos lados, opaco") e ela
   diz melhor o que a coisa É: a tela do celular de onde a conversa veio.
   Herda `justify-content: center` de `.sobreposicao` -- não sobrescreve mais.
   O `padding` no fundo é o que garante escuro dos QUATRO lados: sem ele a
   moldura encosta em cima e embaixo e volta a parecer gaveta. */
.painel { padding: var(--sp-5); }
/* 420x760 é proporção de telefone, e ela não é enfeite: é a largura em que os
   balões caem em duas ou três palavras por linha, que é como a conversa foi
   escrita. `min(..., 100%)` para caber em janela baixa ou estreita sem cortar
   nada -- é o `.sobreposicao` (com o padding acima) que define esse 100%. */
.painel-caixa { background: var(--bg-surface); border: var(--bord);
  border-radius: var(--raio-xl); box-shadow: var(--sombra-lg);
  width: min(420px, 100%); height: min(760px, 100%); overflow: hidden;
  padding: var(--sp-4); display: flex; flex-direction: column; gap: var(--sp-3); }
.painel-topo { display: flex; align-items: center; justify-content: space-between; flex: none; }
/* DENTRO DA MOLDURA SÓ AS MENSAGENS ROLAM. Antes rolava a caixa inteira: o
   "Fechar" subia junto e sumia, e na fila de comprovantes os botões de decisão
   iam junto com ele -- justamente os dois que precisam estar sempre à mão.
   `min-height: 0` em cada nível porque item de flex não encolhe abaixo do
   próprio conteúdo por padrão, e sem isso `overflow-y` é decorativo (o mesmo
   motivo que `.main` e `.sidebar` já documentam). */
.painel-corpo { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.painel-corpo > .conversa { flex: 1; min-height: 0; overflow-y: auto; }
.painel-decisao { border-top: 1px solid var(--border); padding-top: var(--sp-3); margin-top: var(--sp-2); flex: none; }
/* Na tela do celular a moldura é a tela toda menos uma folga: manter 24px de
   escuro de cada lado deixaria 342px de conversa num aparelho de 390px. */
@media (max-width: 640px) { .painel { padding: var(--sp-3); } }
body.com-sobreposicao { overflow: hidden; }

/* Balões: lead à esquerda, empresa à direita — a convenção do WhatsApp, onde
   quem olha vê o outro à esquerda e a si mesmo à direita. */
.conversa { display: flex; flex-direction: column; gap: var(--sp-2); }
.balao { max-width: 82%; padding: var(--sp-2) var(--sp-3); border-radius: var(--raio-lg);
  display: flex; flex-direction: column; gap: 2px; }
.balao p { margin: 0; overflow-wrap: anywhere; }
.balao-lead { align-self: flex-start; background: var(--bg-elevated); }
.balao-empresa { align-self: flex-end; background: var(--conf-codigo-bg); color: var(--conf-codigo-fg); }
.balao-hora { font-size: var(--fs-label); opacity: .7; align-self: flex-end; }
.balao-midia { font-size: var(--fs-sm); font-style: italic; opacity: .8; }

/* A ETIQUETA DE DIA, entre os balões (20/08, pedido da equipe do dono).
   Centralizada e discreta: ela orienta, não compete com a mensagem. É o mesmo
   papel que a data cumpre no WhatsApp, e a forma segue o que o sistema já tem
   -- fundo de superfície elevada, tipo de etiqueta, sem cor de estado (dia não
   é bom nem ruim, então não empresta a paleta de confiança nem a de alerta). */
.conversa-dia { display: flex; justify-content: center; margin: var(--sp-2) 0; }
/* CONTORNO, E NAO PREENCHIMENTO. A primeira versão usava `--bg-elevated`, que
   é EXATAMENTE o fundo do balão do lead -- medido no navegador: as duas cores
   deram `rgb(238,240,243)`, então a etiqueta de dia parecia mais uma mensagem
   curta no meio da conversa. Vazada, ela lê como marcador. */
.conversa-dia span {
  background: transparent; color: var(--text-tertiary);
  border: 1px solid var(--border);
  font-size: var(--fs-label); line-height: var(--lh-label);
  padding: 1px var(--sp-3); border-radius: var(--raio-lg);
  white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════════
   A LINHA da fila de confirmação — desenho fechado em 13/08.

   Três colunas: o que aconteceu (fluida), quanto (fixa), e a decisão (fixa).
   As duas larguras fixas são o que faz as bordas caírem no MESMO lugar em
   toda linha, e é isso que põe os botões numa coluna reta descendo a tela.

   A altura igual vem do conteúdo, não de um `height`: o arco reserva duas
   linhas e a frase reserva uma, cheias ou vazias (ver o comentário na view).
   Um `height` fixo cortaria texto; a reserva só impede o encolhimento.
   ═══════════════════════════════════════════════════════════════ */
.item-fila.linha {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--sp-4); align-items: center;
}
.linha-quem { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.linha-nome { font-size: var(--fs-lg); }
.linha-sub { font-size: var(--fs-sm); color: var(--text-secondary); }
/* O SLOT DE TEXTO da linha da fila (direção A, 14/08).
   A reserva de altura mora AQUI, e não nos dois filhos. É ela que mantém toda
   linha com a mesma altura -- e é a altura igual que faz os botões caírem numa
   coluna reta descendo a tela; o que desalinha um botão não é o botão, é a
   linha ao lado mudar de tamanho.
   Eram 60px (40 do arco + 20 da frase), reservados mesmo vazios. Medido em
   produção: o arco está vazio em 16 de 16 itens, porque a segunda passada da
   IA só roda em item já qualificado e nenhum foi -- 640px de nada na fila
   inteira. Agora são 40px, e os filhos só existem quando têm conteúdo. */
.linha-texto {
  display: flex; flex-direction: column;
  min-height: 40px; margin-top: 3px; overflow: hidden;
}
.linha-arco {
  font-size: var(--fs-sm); line-height: 20px; color: var(--text-secondary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
/* Duas linhas, e não uma com reticências. O corte de uma linha só tirava
   justamente o FIM da frase -- que é onde mora a evidência de fechamento que
   esta tela existe para julgar. */
.linha-frase {
  font-size: var(--fs-sm); line-height: 20px; color: var(--text-tertiary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.frase-seta { opacity: .6; padding: 0 2px; }

/* O GRUPO de comprovantes do mesmo lead (direção A, 14/08).
   Medido: dos 16 itens da fila, dois PARES são do mesmo lead -- dois
   comprovantes cada, no mesmo dia. A tela os mostrava como estranhos, e
   confirmar os dois cria duas vendas para o que pode ser um pagamento em
   parcelas. A tela não decide isso (o sistema não modela parcela, decisão
   travada); ela só para de esconder o parentesco.

   ÂMBAR, e a escolha não é livre: nesta casa cor é estado, e este âmbar é
   exatamente o mesmo que a etiqueta "venda recente · R$ X" já usa na mesma
   tela -- o aviso de que dois números podem ser o mesmo dinheiro. Um matiz
   novo aqui inventaria um sexto estado para dizer a mesma coisa. */
.grupo-lead {
  border: 1px solid var(--conf-fallback-bg);
  border-left: 3px solid var(--conf-fallback-fg);
  border-radius: var(--raio-lg);
  padding: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-4);
}
.grupo-lead-topo {
  font-size: var(--fs-sm); color: var(--conf-fallback-fg);
  padding: 2px 0 var(--sp-2);
}
.grupo-lead .item-fila { margin-bottom: var(--sp-2); }
.grupo-lead .item-fila:last-child { margin-bottom: 0; }
.linha-sinais { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-2); }
/* Os dois ícones andam juntos e não se separam das etiquetas por conta própria:
   soltos num container que quebra linha, eles pulavam sozinhos para uma segunda
   fila em telas médias -- e uma linha mais alta que as vizinhas é exatamente o
   que desmonta a coluna reta de botões que este desenho existe para ter. */
.linha-gatilhos { display: flex; align-items: center; gap: var(--sp-2); flex: none; }
/* O sinal de "a IA leu isto" (Entrega 5). Deliberadamente NÃO é `.badge`:
   etiqueta com fundo colorido, nesta casa, quer dizer estado, e este sinal não
   é estado -- é procedência. Cinza terciário, 12px, sem fundo e sem moldura, é
   o que o dono chamou de sutil: percebe-se ao correr a lista, não disputa com a
   tarja verde ao lado.
   `nowrap` e `flex: none` porque o texto tem 17 caracteres e quebrar em duas
   linhas cresceria a altura da linha -- e altura desigual é o que desmonta a
   coluna reta de botões (o mesmo motivo de `.linha-gatilhos` logo acima). */
.selo-ia {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: var(--fs-caption); line-height: var(--lh-caption);
  color: var(--text-tertiary); white-space: nowrap;
}
/* Sem `opacity` no ícone: ele já herda a cor terciária do texto, e apagá-lo
   mais deixava o desenho do robô mais fraco que a própria legenda ao lado --
   duas intensidades para a mesma informação. */
.selo-ia svg { flex: none; }

/* A FAIXA APERTADA, medida na tela e não estimada: entre 901px e 1200px de
   janela, a coluna da esquerda da linha fica com ~380px (o resto vai para os
   campos de valor/data e os botões, que têm largura fixa). Ali o rótulo do
   sinal era a gota que empurrava os ícones para uma segunda fila -- e linha
   mais alta que a vizinha é exatamente o que desmonta a coluna reta de botões
   que este desenho existe para ter.
   Some o TEXTO e fica o ícone, com a explicação inteira no `title`. O texto
   continua no HTML (só fora da vista), então leitor de tela não perde nada.
   Abaixo de 900px a linha vira uma coluna só, a largura sobra, e o rótulo
   volta -- por isso o `min-width` aqui, e não um `max-width` solto.

   `hover: hover` NÃO é detalhe: sem ele, um tablet em paisagem (1024px) cai
   nesta faixa e o rótulo some, deixando um robô sem explicação -- porque
   `title` não tem equivalente no toque. É a mesma regra que a etiqueta de
   venda recente já seguiu ao pôr o valor no texto visível em vez de só na
   dica. Onde não há mouse, o rótulo fica, e a linha crescer um pouco é o
   preço certo a pagar: ícone mudo é pior que coluna torta. */
@media (hover: hover) and (min-width: 901px) and (max-width: 1200px) {
  .selo-ia-texto {
    position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap;
  }
}
/* A órfã fala a largura inteira, embaixo das duas colunas. */
.linha-banner { grid-column: 1 / -1; }

/* Os gatilhos do comprovante e da conversa. Cinza, não verde: com o botão
   Confirmar verde ao lado, um ícone verde seria um segundo verde querendo
   dizer outra coisa na mesma linha. Verde é decisão; ícone é caminho. */
.ico {
  width: 30px; height: 30px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; color: var(--text-secondary);
  border: 1px solid var(--border); border-radius: var(--raio-sm); cursor: pointer;
  transition: background var(--trans), color var(--trans), border-color var(--trans);
}
.ico:hover { background: var(--bg-elevated); color: var(--text-primary); border-color: var(--border-hover); }

/* ── O valor e a data: campo que parece texto ──────────────────────
   Um `<input>` de verdade o tempo todo, só pintado como texto até receber o
   foco -- por isso funciona no clique simples, no toque e no teclado, sem uma
   linha de JavaScript. O lápis aparece no hover e é quem conta, sem palavra
   nenhuma, que aquele número se edita. */
.decisao { display: grid; grid-template-columns: 150px 152px; gap: var(--sp-4); align-items: center; margin: 0; }
.decisao-valores { display: flex; flex-direction: column; align-items: stretch; gap: 2px; min-width: 0; }
.campo-inline { position: relative; display: flex; align-items: baseline; justify-content: flex-end; gap: 3px; }
.campo-inline .lapis {
  position: absolute; left: 0; top: 50%; transform: translateY(-50%);
  display: flex; color: var(--text-tertiary); opacity: .55; pointer-events: none;
  transition: opacity var(--trans);
}
.campo-inline:hover .lapis { opacity: 1; }
/* A MOLDURA FICA VISÍVEL, e isto REVERTE uma decisão de 13/08 -- a de que o
   campo "parece texto e mostra moldura ao passar o mouse". Pedido do dono em
   20/08, usando a tela: "coloque uma borda no campo de data e valor, pro cara
   saber que é um campo para preencher".
   O motivo dele é mais forte que o meu: o valor da fila é uma SUGESTÃO da
   leitura automática, e ela erra -- no mesmo dia, um comprovante de R$ 40,00
   entrou como R$ 245,60. Um campo que não se anuncia faz a pessoa confirmar o
   número errado sem perceber que podia corrigi-lo. O realce de foco continua,
   mais forte que o repouso. */
.decisao .ed {
  font: inherit; font-variant-numeric: tabular-nums; text-align: right;
  background: var(--bg-surface); border: 1px solid var(--border); border-radius: var(--raio-sm);
  padding: 2px var(--sp-2); color: var(--text-primary); min-width: 0; width: 100%;
  transition: background var(--trans), border-color var(--trans);
}
.decisao .ed.grande { font-size: var(--fs-xl); font-weight: 600; letter-spacing: -0.02em; }
.decisao .ed.pequeno { font-size: var(--fs-caption); color: var(--text-secondary); }
.campo-inline:hover .ed { background: var(--bg-elevated); border-color: var(--border-hover); }
.decisao .ed:focus { background: var(--bg-elevated); border-color: var(--border-hover); }

/* ── Confirmar em cima, Descartar embaixo ──────────────────────────
   Verde e vermelho: exceção assumida à regra de que cor é estado e botão de
   ação é tinta (ver tokens.css). Vale nesta tela, não no resto do app. */
/* O aviso de discordância entre as duas leituras. Atravessa as DUAS colunas do
   grid: ele fala do campo de cima, e recuado embaixo dele viraria legenda do
   botão. Cor de atenção, não de erro -- não há nada errado, há duas leituras. */
.decisao-aviso {
  grid-column: 1 / -1; margin: 0; text-align: right;
  font-size: var(--fs-label); line-height: var(--lh-label); color: var(--aviso);
}

.decisao-acoes { display: flex; flex-direction: column; gap: var(--sp-2); }
.decisao-acoes button {
  width: 100%; font-size: var(--fs-lg); font-weight: 700; letter-spacing: 0.005em;
  padding: 9px var(--sp-3); border: 1px solid transparent; border-radius: var(--raio);
  cursor: pointer; transition: filter var(--trans);
}
.decisao-acoes button:hover { filter: brightness(1.06); }
.decisao-acoes .bt-ok { background: var(--dec-ok-bg); color: var(--dec-ok-fg); }
.decisao-acoes .bt-no { background: var(--dec-no-bg); color: var(--dec-no-fg); }

/* Telas estreitas: a decisão desce para baixo do texto e as duas partes
   ocupam a linha inteira, em vez de espremer três colunas em 360px. */
/* 900px, e não 720: entre esses dois a coluna de texto fica estreita demais e a
   faixa de etiquetas quebra em duas filas, com o mesmo efeito de desalinhar os
   botões. Melhor empilhar antes disso do que espremer três colunas. */
@media (max-width: 900px) {
  .item-fila.linha { grid-template-columns: 1fr; }
  .decisao { grid-template-columns: minmax(0, 1fr) 152px; }
}

/* A seção de decididas recentemente (confirmadas E descartadas). */
.recentes { display: flex; flex-direction: column; gap: var(--sp-3); }
.recente { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3); flex-wrap: wrap; }
.marca-ok { color: var(--conf-codigo-fg); font-weight: 600; }
.marca-no { color: var(--critico); font-weight: 600; }

/* ═══════════════════════════════════════════════════════════════
   Gerador v3 — fluxo único de pixel (mantido intacto)
   ═══════════════════════════════════════════════════════════════ */
.gerador-v3 { display: flex; flex-direction: column; gap: var(--sp-4); }

.gerador-v3 .campo-lp {
  padding: var(--sp-4); background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg); border-left: 3px solid var(--accent);
}
.gerador-v3 .campo-lp input[type=url] { font-family: var(--font-mono); }

.gerador-v3 .passo-card {
  padding: var(--sp-4); background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg); display: flex; flex-direction: column; gap: var(--sp-3);
}
.gerador-v3 .passo-cabecalho {
  display: flex; align-items: center; gap: var(--sp-2);
}
/* O h3 do cartão era `--fs-body`: do mesmo tamanho do texto que ele encabeça,
   então não lia como título. Sobe um degrau e ganha o peso. */
.gerador-v3 .passo-cabecalho h3 {
  margin: 0; font-size: var(--fs-lg); line-height: 22px;
  font-weight: 600; color: var(--text-primary); flex: 1;
}

.gerador-v3 .info-toggle { position: relative; }
.gerador-v3 .info-toggle summary {
  cursor: pointer; list-style: none; width: 22px; height: 22px; border-radius: 50%;
  background: var(--bg-elevated); border: 1px solid var(--border); color: var(--text-tertiary);
  display: flex; align-items: center; justify-content: center; font-size: var(--fs-caption);
  transition: all var(--trans);
}
.gerador-v3 .info-toggle summary::-webkit-details-marker { display: none; }
.gerador-v3 .info-toggle summary:hover { color: var(--accent); border-color: var(--accent); }
.gerador-v3 .info-toggle[open] summary {
  color: var(--accent); border-color: var(--accent); background: var(--accent-muted);
}
.gerador-v3 .info-body {
  position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  width: 340px; padding: var(--sp-3); background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: var(--raio); box-shadow: var(--sombra-lg);
  font-size: var(--fs-sm); color: var(--text-secondary); line-height: 1.55;
}
.gerador-v3 .info-body code {
  background: var(--bg-base); padding: 1px 5px; border-radius: 3px;
  font-size: var(--fs-caption); color: var(--text-primary);
}

.gerador-v3 .instrucao-caixa {
  padding: var(--sp-3) var(--sp-4);
  background: var(--accent-muted);
  border-left: 3px solid var(--accent);
  border-radius: var(--raio); color: var(--text-primary);
  font-size: var(--fs-body); line-height: 1.6;
}
.gerador-v3 .instrucao-caixa code {
  background: var(--bg-base); padding: 1px 6px; border-radius: 3px;
  font-size: var(--fs-sm); font-family: var(--font-mono);
}
.gerador-v3 .instrucao-caixa ol { margin: 0; padding-left: 20px; }
.gerador-v3 .instrucao-caixa ol li + li { margin-top: var(--sp-1); }
.gerador-v3 .instrucao-caixa a { color: var(--accent); }

.gerador-v3 .copy-field { display: flex; gap: var(--sp-2); align-items: stretch; }
.gerador-v3 .copy-field input[type=text], .gerador-v3 .copy-field textarea {
  flex: 1; padding: var(--sp-3); background: var(--bg-base); border: 1px solid var(--border);
  border-radius: var(--raio-sm); color: var(--text-primary);
  font-family: var(--font-mono); font-size: var(--fs-sm); word-break: break-all; height: auto;
}
.gerador-v3 .copy-field textarea { resize: vertical; min-height: 100px; white-space: pre; overflow-x: auto; }
.gerador-v3 .copy-field button { align-self: stretch; white-space: nowrap; }

/* ═══════════════════════════════════════════════════════════════
   Paginador — nenhuma tela carrega tabela inteira
   ═══════════════════════════════════════════════════════════════ */
.paginador {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--sp-4); flex-wrap: wrap;
  margin-top: var(--sp-4); padding-top: var(--sp-4);
  border-top: var(--bord);
}
.paginador-info {
  font-size: var(--fs-caption); color: var(--text-tertiary);
  font-variant-numeric: tabular-nums; margin: var(--sp-3) 0 0;
}
.paginador .paginador-info { margin: 0; }
.paginador-controles { display: flex; gap: var(--sp-2); }

/* ── A marca da origem (14/08) ─────────────────────────────────────
   Desenhada por `views/partials/fonte.ejs`, um lugar só. Antes eram seis views
   repetindo o mesmo quadradinho com estilo inline.
   `color` herda e serve aos estados NEUTROS (não rastreada, link sem canal,
   outra fonte), que são desenhados com `currentColor` e traço fino. As logos
   de plataforma trazem cor própria e ignoram isto. */
.fonte-marca {
  display: inline-flex; align-items: center; justify-content: center;
  vertical-align: middle; color: var(--text-tertiary);
}

/* ═══════════════════════════════════════════════════════════════
   Tela de Conversas — bloco por conversa (14/08)
   Direção B do desenho: a pessoa vem primeiro, e o que não existe
   SOME em vez de virar um traço que o olho precisa ignorar. Com 57%
   das conversas sem origem, é essa regra que separa uma tela limpa
   de uma tela de riscos.
   ═══════════════════════════════════════════════════════════════ */
.conversas { border: var(--bord); border-radius: var(--raio-lg); overflow: hidden; background: var(--bg-surface); }
.conv {
  display: grid; grid-template-columns: minmax(0, 1fr) auto auto;
  gap: var(--sp-4); align-items: center;
  padding: var(--sp-3) var(--sp-5);
  border-bottom: 1px solid var(--border);
}
.conv:last-child { border-bottom: 0; }
.conv-quem { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.conv-nome { font-size: var(--fs-lg); font-weight: 600; color: var(--text-primary); text-decoration: none; }
.conv-nome:hover { text-decoration: underline; }
/* A ULTIMA MENSAGEM no lugar do telefone (direcao A, 14/08). Existe em 97,4%
   das conversas -- o dado mais presente da base -- contra uma etiqueta cinza
   de "sem origem" que aparecia em 57% e nao distinguia nada. Uma linha so, com
   reticencias: aqui ela e uma DICA do assunto, nao a conversa; quem quer ler
   abre o balao ao lado. */
.conv-msg {
  font-size: var(--fs-sm); color: var(--text-primary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.conv-sinais { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; margin-top: var(--sp-1); }
.conv-anuncio { font-family: var(--font-mono); font-size: var(--fs-caption); color: var(--text-tertiary); }
a.conv-anuncio:hover { color: var(--text-secondary); }

/* Data em cima, hora um degrau abaixo em tamanho e em cor: pedido do dono em
   14/08. A hora é o que distingue o que acabou de entrar; a data é o que ancora
   no calendário. Empilhar dá as duas sem gastar largura. */
.conv-dir { display: flex; flex-direction: column; align-items: flex-end; gap: 1px; }
.conv-venda { color: var(--conf-codigo-fg); font-weight: 700; font-size: var(--fs-lg); }
.conv-data { color: var(--text-secondary); font-size: var(--fs-caption); white-space: nowrap; }
.conv-hora {
  color: var(--text-tertiary); font-size: var(--fs-label); line-height: var(--lh-label);
  white-space: nowrap; font-variant-numeric: tabular-nums;
}

@media (max-width: 900px) {
  .conv { grid-template-columns: minmax(0, 1fr) auto; }
  .conv-dir { grid-column: 2; }
  .conv > .ico { grid-column: 2; justify-self: end; }
}

/* ═══════════════════════════════════════════════════════════════
   O RESUMO do detalhe do lead (direção C, 14/08, modelo do dono)
   ═══════════════════════════════════════════════════════════════
   Painéis de linhas rótulo/valor. O rótulo tem largura fixa para os valores
   caírem numa coluna reta descendo a página -- a mesma razão da altura igual
   na fila de comprovantes: o que desalinha não é o valor, é o rótulo ao lado
   mudar de tamanho. */
.painel-info {
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-lg); overflow: hidden; margin-bottom: var(--sp-4);
}
.painel-info > h2, .painel-info > summary > h3 {
  margin: 0; font-size: var(--fs-lg); font-weight: 600;
}
.painel-info > .secao-cabecalho { padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border); }
.campos-info { padding: var(--sp-2) var(--sp-5) var(--sp-4); }
.linha-info {
  display: grid; grid-template-columns: 210px minmax(0, 1fr);
  gap: var(--sp-4); padding: 9px 0; align-items: baseline; margin: 0;
}
.linha-info + .linha-info { border-top: 1px solid var(--border); }
.linha-info dt { color: var(--text-secondary); font-size: var(--fs-sm); }
.linha-info dd {
  margin: 0; font-size: var(--fs-md);
  display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
}
/* A DICA é onde a inferência se declara. A localização sai do DDD, que é a
   região do CHIP e não da pessoa -- e a regra travada desta casa é que palpite
   nunca se apresenta como certeza. */
.dica { color: var(--text-tertiary); font-size: var(--fs-caption); }
/* Quando a ausência é o próprio achado. Medido: em 38 conversas a empresa
   nunca respondeu -- esse é o caso que o dono quer ver, não um campo vazio. */
.dica-forte { color: var(--conf-fallback-fg); font-size: var(--fs-sm); }
.rot-inline { color: var(--text-secondary); font-size: var(--fs-sm); min-width: 190px; }
.rot-bloco {
  font-size: var(--fs-label); letter-spacing: .06em; text-transform: uppercase;
  color: var(--text-tertiary); margin-bottom: var(--sp-2);
}
.bloco-params { border-top: 1px solid var(--border); padding-top: var(--sp-3); }
.valor-forte { color: var(--conf-codigo-fg); }
.msg-inicial { color: var(--text-secondary); }

.venda-reg {
  display: flex; align-items: baseline; gap: var(--sp-3); flex-wrap: wrap; padding: var(--sp-3) 0;
}
.venda-reg + .venda-reg { border-top: 1px solid var(--border); }
.venda-valor { font-size: var(--fs-xl); font-weight: 700; color: var(--conf-codigo-fg); }

/* O identificador do anúncio e os parâmetros de URL são para CONFERIR contra
   outro painel, não para ler -- monoespaçado é o que permite comparar dígito a
   dígito. `word-break` porque um gclid tem 90 caracteres e não pode empurrar a
   página para o lado. */
.mono-id, .param dd {
  font-family: var(--font-mono); font-size: var(--fs-caption); word-break: break-all;
}
.param {
  display: grid; grid-template-columns: 150px minmax(0, 1fr);
  gap: var(--sp-3); padding: 5px 0; font-size: var(--fs-sm); margin: 0;
}
.param dt { color: var(--text-tertiary); font-family: var(--font-mono); font-size: var(--fs-caption); }
.param dd { margin: 0; }

/* Ação de largura inteira no pé do painel, como no modelo que o dono mandou.
   Não é o botão primário: ela abre coisa, não decide nada. */
.bt-largo {
  display: block; width: calc(100% - var(--sp-5) * 2); margin: 0 var(--sp-5) var(--sp-5);
  padding: 11px; text-align: center; background: var(--bg-elevated);
  border: 1px solid var(--border); border-radius: var(--raio);
  color: var(--text-primary); font-size: var(--fs-md); font-family: inherit; cursor: pointer;
}
.bt-largo:hover { border-color: var(--border-hover); }

@media (max-width: 700px) {
  .linha-info { grid-template-columns: 1fr; gap: 2px; }
  .param { grid-template-columns: 1fr; gap: 0; }
}

/* Zona de perigo do detalhe do lead (14/08). Ela existe para dar PESO à ação:
   apagar aqui some com o lead e com todo o histórico de conversas, e o WhatsApp
   não devolve o que foi apagado. Fica no fim da página, separada por moldura
   vermelha discreta -- visível para quem procura, fora do caminho de quem não. */
.zona-perigo {
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4);
  flex-wrap: wrap; margin-top: var(--sp-6); padding: var(--sp-4) var(--sp-5);
  border: 1px solid var(--danger-muted); border-radius: var(--raio-lg);
  background: var(--danger-muted);
}
/* `> div > strong` E NAO `.zona-perigo strong`: o descendente pegava tambem o
   <strong> DENTRO do parágrafo, e ele virava bloco -- "muda os números do
   painel" quebrava a linha em três pedaços no meio da frase. Medido no
   navegador em 20/08, com o texto que separa apagar de anonimizar; o teste
   estava verde e o parágrafo, errado. O título continua alcançado: ele é filho
   direto do <div> que abre toda zona de perigo (as três da tela). */
.zona-perigo > div > strong { display: block; color: var(--text-primary); font-size: var(--fs-md); }
.zona-perigo p { margin: var(--sp-1) 0 0; color: var(--text-secondary); font-size: var(--fs-sm); max-width: 62ch; }
.zona-perigo .perigo {
  font: inherit; font-size: var(--fs-sm); font-weight: 600; cursor: pointer;
  padding: var(--sp-2) var(--sp-4); border-radius: var(--raio); white-space: nowrap;
  background: transparent; color: var(--critico); border: 1px solid var(--critico);
  transition: background var(--trans), color var(--trans);
}
.zona-perigo .perigo:hover { background: var(--critico); color: #fff; }

/* Os números que o extrator não entendeu, listados um a um com o motivo. Fonte
   monoespaçada no número: eles vieram colados de uma planilha, e é comparando
   caractere a caractere com o original que a pessoa entende por que aquele
   ficou de fora. */
.lista-duvidosos { margin: var(--sp-2) 0 var(--sp-3); padding-left: var(--sp-5);
                   font-size: var(--fs-caption); color: var(--text-secondary); }
.lista-duvidosos code { font-family: var(--fonte-mono, monospace); }

/* A contagem de conversas ocultas, em voz baixa ao lado do filtro (14/08).
   Era um banner laranja de faixa inteira; laranja neste painel quer dizer
   atenção, e 26 conversas que o dono não quer analisar não são um alarme. O
   número fica porque contato escondido não pode sumir em silêncio. */
.ocultos-nota {
  align-self: center; font-size: var(--fs-caption); color: var(--text-tertiary);
  text-decoration: none; border-bottom: 1px dotted var(--border-hover);
}
.ocultos-nota:hover { color: var(--text-secondary); }

/* "Decididas recentemente" recolhida (14/08, pedido do dono olhando a tela no
   ar). Ela ocupava mais altura que a própria fila, e a fila é o trabalho -- as
   decididas são consulta, e só quando algo deu errado.
   `<details>` nativo de propósito: abre e fecha sem uma linha de JavaScript,
   funciona com teclado e com leitor de tela de graça, e o estado padrão
   (fechado) é o que empurra a fila para cima. */
.recentes-caixa { padding: 0; }
.recentes-caixa > summary {
  display: flex; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3) var(--sp-4); cursor: pointer; list-style: none;
}
.recentes-caixa > summary::-webkit-details-marker { display: none; }
/* A seta é desenhada por nós para girar na abertura -- o marcador nativo não
   segue o tema e muda de forma entre navegadores. */
.recentes-caixa > summary::before {
  content: ""; width: 7px; height: 7px; flex: none;
  border-right: 1.5px solid var(--text-tertiary); border-bottom: 1.5px solid var(--text-tertiary);
  transform: rotate(-45deg); transition: transform var(--trans); margin-right: 2px;
}
.recentes-caixa[open] > summary::before { transform: rotate(45deg); }
.recentes-caixa > summary h3 { margin: 0; font-size: var(--fs-h2); line-height: var(--lh-h2); }
.recentes-caixa > summary:hover h3 { color: var(--text-primary); }
.recentes-contagem {
  font-size: var(--fs-caption); color: var(--text-tertiary);
  background: var(--bg-elevated); border-radius: 999px; padding: 1px var(--sp-2);
  font-variant-numeric: tabular-nums;
}
.recentes-caixa .recentes { padding: 0 var(--sp-4) var(--sp-4); }

/* O SELETOR DE CLIENTE — `<details>` nativo, o mesmo recurso que "Decididas
   recentemente" usa na fila. Sem JavaScript: um menu de navegação que depende
   de script é um menu que some quando o script falha. */
.seletor-cliente { position: relative; }
.seletor-cliente > summary {
  cursor: pointer; list-style: none; font-weight: 600; color: var(--fg);
  border: var(--bord); border-radius: 8px; padding: 5px 11px; background: var(--bg-alt);
}
.seletor-cliente > summary::-webkit-details-marker { display: none; }
.seletor-cliente > summary::after { content: ' \25BE'; color: var(--fg-mut); }
.seletor-cliente .opcoes {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 20; min-width: 250px;
  background: var(--bg-alt); border: var(--bord); border-radius: 10px; padding: 6px;
  box-shadow: var(--sombra-md); display: flex; flex-direction: column;
}
.seletor-cliente .opcoes a {
  padding: var(--sp-2) 10px; border-radius: 6px; color: var(--fg-mut);
  text-decoration: none; font-size: var(--fs-caption);
}
.seletor-cliente .opcoes a:hover { background: var(--bg); color: var(--fg); }
.seletor-cliente .opcoes a.atual { color: var(--fg); font-weight: 600; }
.seletor-cliente .opcoes hr { margin: 6px var(--sp-1); border: 0; border-top: var(--bord); }

/* A BARRA EMPILHADA do Painel geral: um pedaço por plataforma, sem texto ao
   lado. O vão de 2px entre pedaços é do fundo e NÃO é enfeite — é ele que
   separa duas cores vizinhas para quem enxerga cor de outro jeito. */
/* A BARRA DE ORIGEM — o elemento que este produto tem de mais próprio. Ela
   mostra, de um olhar, o quanto o cliente NÃO sabe.
   Até 19/08 ela fazia o oposto: "Não sabemos" tinha 49% e era o segmento mais
   escuro e mais sólido da barra — o vazio como protagonista. Agora o que se
   sabe é cheio e saturado, e o que não se sabe quase encosta no fundo.
   As pontas arredondadas ficam nas EXTREMIDADES da barra, e não em cada
   pedaço: cantos redondos por dentro transformariam uma barra contínua numa
   fileira de pastilhas, e a leitura de proporção se perde. */
.barra-origem {
  height: 10px; border-radius: 999px; display: flex; gap: 2px;
  min-width: 240px; overflow: hidden;
}
.barra-origem i {
  display: block; height: 100%; min-width: 2px;
  transition: filter var(--trans);
}
.barra-origem i:hover { filter: brightness(1.25); }

/* O QUE NÃO SE SABE recua para o fundo — mas RECUAR NÃO É SUMIR.
   A primeira versão usava `--bg-afundado` com hachura em `--border`, e medi
   1,04:1 contra o fundo: o segmento desaparecia. E aí a barra passava a mentir
   pior do que antes — mostrando só a parte conhecida, como se a soma fosse
   100%, quando o ponto inteiro desta peça é revelar o tamanho do que falta.
   A proporção é a informação; ela não pode ser invisível.
   Quem carrega a leitura agora é a HACHURA, em `--border-hover`, sobre o fundo
   afundado. O segmento continua o mais quieto da barra e continua legível. */
.barra-origem i.p-desconhecida,
.barra-origem i.p-nada,
.barra-origem i.p-outra {
  background: var(--bg-afundado);
  background-image: repeating-linear-gradient(45deg,
    var(--border-hover) 0 1px, transparent 1px 4px);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* O TRILHO VAZIO: cliente sem conversa nenhuma mantém a linha com a mesma
   altura, em vez de colapsar. Uma linha que some da tabela é uma linha que o
   olho não conta ao comparar clientes. */
.barra-origem.vazia {
  background: var(--bg-afundado);
  box-shadow: inset 0 0 0 1px var(--border);
}
/* A COR DA PLATAFORMA SOLTA DO CONTEXTO.
   Ela vivia presa em `.barra-origem i`, então a LEGENDA logo abaixo da barra
   não alcançava — e pintava as mesmas sete cores com `style=` no HTML,
   repetindo o mapeamento. Duas fontes para a mesma verdade: mudar a paleta
   consertava a barra e deixava a legenda mentindo. */
.p-google    { background: var(--p-google); }
.p-facebook  { background: var(--p-facebook); }
.p-instagram { background: var(--p-instagram); }
.p-link      { background: var(--p-link); }
.p-chatgpt   { background: var(--p-chatgpt); }
.p-meta      { background: var(--p-facebook); }
.p-outra,
.p-desconhecida, .p-nada { background: var(--p-nada); }

/* HACHURA = há incerteza. Nunca cor sozinha: a cor já está dizendo qual
   plataforma é, e um segundo significado na mesma tinta não se lê. */
.barra-origem i.incerto {
  background-image: repeating-linear-gradient(135deg,
    rgba(0,0,0,.55) 0 3px, transparent 3px 6px);
}

.legenda-plataformas { display: flex; flex-wrap: wrap; gap: var(--sp-2) var(--sp-4);
                       margin-top: var(--sp-3);
                       font-size: var(--fs-caption); color: var(--text-secondary); }
.legenda-plataformas span { display: inline-flex; align-items: center; gap: var(--sp-1); }

/* A bolinha de estado. Aqui cor SIGNIFICA estado, que é a regra normal desta
   casa — os tokens são os mesmos de sucesso e crítico. */
.ponto { width: 8px; height: 8px; border-radius: 50%; display: inline-block; flex: none; margin-right: 6px; }
.ponto.on  { background: var(--sucesso); }
.ponto.off { background: var(--critico); }

/* O PULSO — decisão do dono em 19/08, ao desenhar o topo do Rastreamento.
   Não veio de acordo anterior nem de proposta minha: é instrução nova daquele
   dia, e fica registrado assim para o comentário não mentir sobre a origem.

   É EXCEÇÃO NOMEADA à regra de movimento de `tokens.css`, que diz que animação
   aqui só faz parecer amador. A exceção se sustenta porque este pulso não é
   enfeite — ele carrega o significado que a cor sozinha não carrega: verde
   parado diz "estava verde quando a página carregou", verde pulsando diz "isto
   está vivo AGORA".

   Ele mora em `.ponto`, e não numa classe do Rastreamento, porque A empresa
   quer dizer exatamente a mesma coisa com a mesma bolinha. As duas passam a
   pulsar, e isso é o resultado pretendido, não efeito colateral.

   `ease-in-out` e não a `--trans` da casa: aquela curva desacelera no fim, que
   é o que uma transição de ida e volta precisa. Um ciclo que respira é
   simétrico nas duas metades.

   Só o verde pulsa. Vermelho piscando seria alarme — outra mensagem. */
@keyframes pulso-vivo { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.ponto.on { animation: pulso-vivo 2.4s ease-in-out infinite; }
/* Movimento que repete para sempre é gatilho vestibular clássico. Quem pediu
   menos movimento continua vendo a COR, que é onde o estado realmente mora. */
@media (prefers-reduced-motion: reduce) { .ponto.on { animation: none; } }

/* ═══════════════════════════════════════════════════════════════
   A CAMADA DE COMPONENTES (18/08)
   ═══════════════════════════════════════════════════════════════
   Medido antes de escrever: 102 estilos dentro do HTML das views, nove telas
   com nove cabeçalhos diferentes. O sistema de tokens já existia e era bom — o
   que faltava era usá-lo. Faltavam QUATRO componentes, não vinte:
   cabeçalho de página, seletor de período, cartões em três, e grupo de ações.
   ═══════════════════════════════════════════════════════════════ */

/* O cabeçalho fica idêntico em toda tela porque agora é um partial só
   (partials/cabecalho-pagina.ejs). Estas regras completam o que faltava: o
   subtítulo colado no título, e a coluna de texto podendo encolher sem empurrar
   as ações para fora. */
.main .cabecalho-texto { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.main .cabecalho-texto h1 { margin: 0; }
.main .cabecalho-texto .subtitulo { margin: 0; }

/* O SELETOR DE PERÍODO — três links num controle segmentado.
   Substitui a caixa de filtro que ocupava a primeira dobra inteira. */
.periodo {
  display: inline-flex; border: var(--bord); border-radius: var(--raio);
  overflow: hidden; background: var(--bg-alt); flex: none;
}
.periodo a {
  padding: var(--sp-1) var(--sp-3); font-size: var(--fs-sm);
  color: var(--fg-mut); text-decoration: none; white-space: nowrap;
  border-right: var(--bord);
}
.periodo a:last-child { border-right: 0; }
.periodo a:hover { color: var(--fg); }
.periodo a.on { background: var(--bg); color: var(--fg); font-weight: 600; }
.periodo a:focus-visible { outline: 2px solid var(--fg); outline-offset: -2px; }

/* Cartões em TRÊS. `.cards` nasceu com quatro colunas, e o painel do cliente
   tem três números — com quatro colunas o terceiro cartão ficava com um vão
   vazio do lado, que o olho lê como "faltou algo". */
.cards-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
@media (max-width: 900px) { .cards-3 { grid-template-columns: 1fr; } }

/* Cartão que é um link: mesmo desenho do cartão comum, mais o estado de foco e
   de hover que um destino navegável precisa ter. */
/* SOMBRA MAIOR NO HOVER SÓ SE FOR CLICÁVEL. Um cartão que só informa e sobe
   ao passar o mouse promete uma ação que não existe. */
a.card-stat { text-decoration: none; color: inherit; }
a.card-stat:hover { border-color: var(--border-hover); box-shadow: var(--sombra-md); }

/* GRUPO DE AÇÕES. O que ele conserta é a fila de botões do canto superior
   direito da tela do cliente — quatro botões do mesmo peso mais um menu
   "Mais", que o dono chamou de confuso e feio. A regra: no máximo UMA ação
   primária, e as demais em tinta secundária. */
.acoes-grupo { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.acoes-grupo form { flex-direction: row; gap: 0; }

/* Título de seção dentro de uma página. Ele existe para tirar
   `style="margin:var(--sp-6) 0 var(--sp-3)"` das views: a margem vinha escrita à mão em cada
   tela, com valor diferente em cada uma. */
/* O TÍTULO DE SEÇÃO PRECISA DE TIPO, e não só de margem.
   Até 18/08 esta regra declarava apenas `margin`, então o tamanho vinha do
   estilo base do `h2` e variava com o contexto. O dono descreveu o sintoma como
   "tamanho dos textos bem fora do esperado", e era isto: a tela tinha sete
   tamanhos entre 11px e 14px, três deles escritos na unha, fora da escala. */
.main .secao-titulo {
  /* -25% no respiro entre seções (sp-6 -> sp-5). Em Rastreamento os buracos
     entre Configuração, Pixel, Google Ads e Meta Ads eram grandes o bastante
     para o dono ler como defeito. Ferramenta profissional é mais densa.
     Continua na escala de 4px: só desce um degrau, não inventa valor. */
  margin: var(--sp-5) 0 var(--sp-3);
  font-size: var(--fs-h2); line-height: var(--lh-h2);
  font-weight: 600; color: var(--text-primary);
}
.main .secao-titulo:first-of-type { margin-top: var(--sp-5); }

/* ═══════════ RASTREAMENTO ═══════════ */
/* SEM `margin-bottom` AQUI. Medido no navegador em 19/08: com ela, o vão até o
   título seguinte dava 88px contra os 64px de todo outro par da tela — porque
   a margem do bloco somava com o `margin-top` de `.secao-titulo`. Duas margens
   escritas em lugares diferentes para o mesmo respiro é como o ritmo vertical
   se perde sem ninguém errar nada sozinho. Quem dá o respiro é o título. */
.bloco-config { display: grid; grid-template-columns: 1fr 1fr; gap: var(--sp-5);
                border: var(--bord); border-radius: var(--raio-lg); background: var(--bg-alt);
                padding: var(--sp-4); }
@media (max-width: 860px) { .bloco-config { grid-template-columns: 1fr; } }
.bloco-config form { gap: var(--sp-2); align-items: flex-start; }
/* `align-items: flex-start` existe para o BOTÃO não esticar. Só que ele encolhe
   todo filho, inclusive o `<label>` do campo — e aí `input { width: 100% }`
   passa a valer 100% de um rótulo encolhido: medido, 276px dentro de uma coluna
   de 684px. O campo esticar é o que se quer; o botão, não. */
.bloco-config form > label { align-self: stretch; }
.bloco-config input { width: 100%; }

/* `flex-direction: row` EXPLÍCITO, e não por omissão — a mesma armadilha que
   `.filtros` já documenta, e na qual eu caí de novo em 18/08. A regra base
   `form { display:flex; flex-direction:column }` se aplica aqui também: sem
   esta linha, `align-items: flex-end` passa a significar "à direita" em vez de
   "embaixo", e `flex: 1 1 200px` faz os campos crescerem na VERTICAL. Uma
   declaração faltando, três sintomas. Hoje há teste cobrando (form-em-linha). */
.form-link-novo { display: flex; flex-direction: row; flex-wrap: wrap;
                  gap: var(--sp-3); align-items: flex-end; margin-top: var(--sp-4); }
.form-link-novo label { flex: 0 1 240px; gap: var(--sp-1); min-width: 0; }
.form-link-novo input { width: 100%; }
.form-link-novo button { flex: none; }

/* A instalação vem recolhida: instalar é uma vez na vida, copiar link é todo
   dia. Dar o mesmo espaço às duas fazia a tela parecer manual. */
.instalacao { border: var(--bord); border-radius: var(--raio-lg); background: var(--bg-alt);
              margin-top: var(--sp-3); }
.instalacao > summary { cursor: pointer; padding: var(--sp-4); list-style: none;
                        display: flex; flex-direction: column; gap: 2px; }
.instalacao > summary::-webkit-details-marker { display: none; }
.instalacao > summary::after { content: '▾'; position: absolute; right: var(--sp-5); color: var(--fg-mut); }
.instalacao { position: relative; }
.instalacao[open] > summary::after { content: '▴'; }
.instalacao-corpo { padding: 0 var(--sp-4) var(--sp-4); }

/* ═══════════ A EMPRESA ═══════════ */
/* Um bloco de linha: assunto à esquerda, ação à direita. Ele substitui a fila
   de quatro botões do canto superior direito, onde "Excluir cliente" dividia
   espaço com "Inspecionar webhook". Cada assunto passa a ter o seu lugar. */
.bloco-linha { border: var(--bord); border-radius: var(--raio-lg); background: var(--bg-alt);
               padding: var(--sp-4); display: flex; align-items: center;
               justify-content: space-between; gap: var(--sp-4); flex-wrap: wrap; }
.bloco-linha-titulo { font-size: var(--fs-md); font-weight: 600; display: flex; align-items: center; }
.bloco-linha form { flex-direction: row; gap: 0; align-items: center; }

.form-busca { flex-direction: row; align-items: flex-end; gap: var(--sp-3); flex-wrap: wrap; }
.form-busca label { flex: 1 1 240px; gap: var(--sp-1); }
.form-busca input { width: 100%; }

/* A zona de perigo: separada, no fim, e visualmente terminal. Excluir apaga
   todas as conversas, cliques e vendas sem desfazer — ela não pode dividir
   espaço com um diagnóstico. */
.perigo-zona { border: 1px solid color-mix(in srgb, var(--critico) 45%, transparent);
               border-radius: var(--raio-lg); padding: var(--sp-4); }
.perigo-zona p { margin: 0 0 var(--sp-3); font-size: var(--fs-caption); color: var(--fg-mut); }
.link-btn.perigo { color: var(--critico); }

/* Cada lugar tem dois destinos: WhatsApp e site. O rótulo à esquerda é o que
   deixa claro qual é qual — sem ele, duas URLs quase iguais empilhadas viram
   ruído. */
.link-destino { display: flex; align-items: center; gap: var(--sp-2); padding: 3px 0; }
.link-destino + .link-destino { border-top: 1px dashed var(--border); margin-top: var(--sp-1); padding-top: var(--sp-2); }
/* LARGURA FIXA, e ela precisa caber o maior rótulo. Medido em 19/08: a caixa
   tinha 62px e "WhatsApp" em caixa alta com tracking ocupa 73px — o rótulo
   encostava na URL sem um pixel de folga ("WHATSAPPhttps://...").
   Fixa e não `min-width`: as duas linhas empilham, e é esta largura que faz as
   duas URLs começarem na mesma coluna. Com `min-width`, "Site" ficaria em 62 e
   "WhatsApp" em 73, e o alinhamento — que é a razão de existir do rótulo —
   se perderia para consertar a folga. */
.rot-destino { font-size: var(--fs-label); text-transform: uppercase; letter-spacing: .06em;
               color: var(--fg-mut); flex: none; width: 80px; }
.link-destino code { flex: 1; min-width: 0; overflow-wrap: anywhere; }
.link-destino button { flex: none; }

/* ─── O "i" de ajuda ──────────────────────────────────────
   Pedido do dono em 18/08: instrução some da tela e vive atrás de um "i" que
   abre ao passar o mouse. O que fica visível é estado e ação.

   O balão precisa de três cuidados que a versão ingênua não tem:
   - `position: fixed` não serve (rolagem descola), então é absoluto e o pai
     ganha `position: relative`;
   - largura em `min()` para nunca ser mais largo que a janela no celular;
   - `pointer-events: none` enquanto fechado, senão ele intercepta cliques de
     uma área invisível. */
/* A linha em que o rótulo e o "i" convivem. Sem ela o "i" cai numa linha só
   dele: o `<label>` herda `flex-direction: column` da regra base de formulário,
   e um `inline-flex` dentro de um flex container vira item de bloco. */
.rotulo-linha { display: inline-flex; align-items: center; gap: 0; }

.ajuda {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 17px; height: 17px; flex: none; margin-left: 7px; vertical-align: middle;
  background: var(--bg-alt); border: 1px solid var(--text-tertiary); border-radius: 50%;
  color: var(--text-secondary); cursor: help; user-select: none;
  transition: background .12s ease, color .12s ease;
}
.ajuda:hover, .ajuda:focus-visible {
  background: var(--text-tertiary); color: var(--bg-base); border-color: var(--text-tertiary); outline: none;
}
.ajuda:focus-visible { box-shadow: 0 0 0 2px var(--accent); }
.ajuda-marca { font-size: var(--fs-label); font-weight: 600; font-style: italic; line-height: 1; font-family: var(--font-serif, Georgia, serif); }

.ajuda-balao {
  position: absolute; left: 0; top: calc(100% + 8px); z-index: 40;
  width: max-content; max-width: min(420px, calc(100vw - 32px));
  padding: var(--sp-3) var(--sp-3);
  background: var(--bg-surface); border: 1px solid var(--border);
  border-radius: var(--raio-md); box-shadow: var(--sombra-lg, 0 8px 24px rgba(0,0,0,.35));
  font-size: var(--fs-sm); line-height: 1.55; font-weight: 400; font-style: normal;
  color: var(--text-secondary); text-align: left; white-space: normal;
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity .12s ease, visibility .12s;
}
.ajuda:hover .ajuda-balao, .ajuda:focus-visible .ajuda-balao,
.ajuda:focus-within .ajuda-balao { opacity: 1; visibility: visible; }
.ajuda-balao code { font-family: var(--font-mono); font-size: var(--fs-caption);
                    background: var(--bg-alt); padding: 1px var(--sp-1); border-radius: 3px;
                    color: var(--text-primary); overflow-wrap: anywhere; }
.ajuda-balao strong { color: var(--text-primary); }

/* Perto da borda direita o balão sairia da tela; esta variante ancora à direita. */
.ajuda.ajuda-fim .ajuda-balao { left: auto; right: 0; }

@media (prefers-reduced-motion: reduce) { .ajuda-balao { transition: none; } }

/* ─── Responsivo: nenhuma caixa corta texto ───────────────
   O dono pediu em 18/08: "o sistema precisa ser responsivo, o que tem de caixa
   cortando texto não está escrito". As regras abaixo atacam as três causas
   reais, e não o sintoma. */

/* 1. O textarea do pixel é o elemento mais largo da aplicação (1358px de
      largura mínima). Sem isto ele dita a largura da página inteira. */
.gerador-v3 .copy-field textarea { width: 100%; min-width: 0; resize: vertical; }
.gerador-v3 .copy-field { min-width: 0; }

/* 2. Campo cujo VALOR é mais longo que a caixa. O input não quebra linha, então
      o texto some à direita — foi o que apareceu em "https://cchdistribuidora.com.b|"
      e "Olá! Vim pelo site e gostari|". Largura cheia resolve os dois. */
.bloco-config label, .bloco-config form { min-width: 0; }
.bloco-config input { width: 100%; min-width: 0; }

/* 3. Toda tabela rola DENTRO de si mesma, em qualquer largura — não só no
      celular, onde esta regra já vivia. Uma tabela larga num desktop estreito
      quebrava o layout do mesmo jeito.

      CUIDADO COM A ESPECIFICIDADE, e eu caí nela em 18/08: a primeira versão
      escrevia `.main .tabela-rolante { max-width: 100% }`, que pesa 0,2,0
      contra os 0,1,0 de `.main > *` — e portanto ANULAVA a largura de leitura
      em toda tabela, silenciosamente. O seletor de classe simples resolve: ele
      empata com `.main > *` e perde por vir antes, que é o que se quer. */
.tabela-rolante { overflow-x: auto; }
.tabela-rolante table { width: 100%; }

@media (max-width: 1100px) {
  .link-destino { flex-wrap: wrap; }
  .rot-destino { width: auto; }
}
@media (max-width: 900px) {
  .main { padding: var(--sp-4); }
  .form-link-novo label { flex: 1 1 100%; }
  .ajuda-balao { left: auto; right: 0; }
}

/* O CABEÇALHO DE SEÇÃO: título à esquerda, ações à direita, na mesma linha.
   Antes disto, uma tela que precisava de um botão ao lado do título resolvia
   com `style=` — foi a origem do único `<h2 style="margin:var(--sp-6) 0 var(--sp-3);">` que
   a base tinha. */
.secao-cabecalho {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--sp-3); margin: var(--sp-5) 0 var(--sp-3);
}
.main > .secao-cabecalho:first-of-type { margin-top: var(--sp-5); }
.secao-cabecalho .secao-titulo { margin: 0; }
.secao-acoes { display: flex; align-items: center; gap: var(--sp-2); flex: none; }

/* O botão do estado vazio. Ele existia como `<a><button>` aninhado no partial
   antigo — botão dentro de link é marcação inválida, e o teclado tropeça nela:
   dois alvos focáveis para uma ação só. Aqui é um link com cara de botão. */
.botao-vazio {
  display: inline-block; margin-top: var(--sp-3);
  padding: var(--sp-2) var(--sp-4); border-radius: var(--raio-md);
  background: var(--accent); color: var(--bg-base);
  font-size: var(--fs-body); font-weight: 500; text-decoration: none;
}
.botao-vazio:hover { filter: brightness(1.08); }

/* O estado vazio quando ele É a página inteira: centralizado, com largura de
   leitura própria. */
.tabela-vazia-pagina { max-width: 520px; margin: var(--sp-8) auto; }

/* A variante COMPACTA: uma linha, sem caixa. Era a classe solta `.vazio-linha`.
   Uma caixa tracejada dentro de um painel de quatro linhas seria pesada demais
   — a densidade do vazio tem de acompanhar a densidade do que ele substitui. */
.tabela-vazia.tabela-vazia-compacta {
  padding: var(--sp-2) 0; border: none; text-align: left;
  display: block;
}
.tabela-vazia-compacta .titulo {
  color: var(--text-tertiary); font-weight: 400; font-size: var(--fs-sm); margin: 0;
}

/* AS PÁGINAS DE ERRO não são estado vazio — elas pegavam emprestada a aparência
   dele (a caixa tracejada), e por isso carregavam quatro `style=` cada uma para
   desfazer o que não servia. São outra coisa: um código grande, uma frase, e uma
   saída. */
.pagina-erro { max-width: 520px; margin: var(--sp-8) auto; text-align: center; }
.pagina-erro .codigo { font-size: 64px; line-height: 1; margin: 0 0 var(--sp-2); }
.pagina-erro .frase { font-size: var(--fs-h2); margin: 0 0 var(--sp-2); }
.pagina-erro .detalhe { color: var(--text-secondary); margin: 0 0 var(--sp-5); }

/* BOTÃO DENTRO DE CÉLULA não estica. A regra base `form { display: flex;
   flex-direction: column }` faz do formulário um container de coluna, e o
   `align-items: stretch` do padrão espicha o botão para a largura inteira da
   célula — 418px medidos na tela de Usuários, contra os ~120px que o texto
   pede. A mesma armadilha do `<label>` que o `campo.ejs` fecha. */
td form { align-items: flex-start; }
td form button, td > button { width: auto; }

/* A legenda: a mesma cor da barra, pelas mesmas classes. O quadradinho é
   pequeno de propósito — quem carrega a informação é o rótulo ao lado. */
.legenda-plataformas i {
  display: inline-block; width: 8px; height: 8px; border-radius: 2px;
  vertical-align: middle; margin-right: var(--sp-1); flex: none;
}
.legenda-plataformas i.incerto {
  background-image: repeating-linear-gradient(135deg, rgba(0,0,0,.55) 0 3px, transparent 3px 6px);
}
/* A LEGENDA TEM DE DIZER O QUE A BARRA MOSTRA. `--p-nada` sólido era o desenho
   antigo; hoje o desconhecido é hachura recuada, e o quadradinho tinha ficado
   mentindo — um bloco escuro na legenda para uma faixa quase vazia na barra.
   Medido em 19/08: 1,81:1 no escuro, onde ele mal aparecia, e 9,74:1 no claro,
   onde virava um quadrado preto. */
.legenda-plataformas i.p-nada,
.legenda-plataformas i.p-desconhecida {
  background: var(--bg-afundado);
  background-image: repeating-linear-gradient(45deg,
    var(--border-hover) 0 1px, transparent 1px 4px);
  box-shadow: inset 0 0 0 1px var(--border);
}

/* ─── Do `style=` para a intenção ─────────────────────────
   As classes abaixo substituem os 41 estilos que estavam escritos dentro do
   HTML. Cada nome diz o QUE a coisa é, e não como ela parece: `.form-estreito`
   e não `.mw-480`. A diferença importa — nome de aparência é classe utilitária
   disfarçada, e reintroduz a mesma deriva com outro rótulo. */

/* Formulário de configuração: linha de leitura curta. Campo de 900px de largura
   para digitar um e-mail não ajuda ninguém a ler o que digitou. */
.form-estreito { max-width: 480px; }

/* Título de bloco dentro de um painel ou diálogo — abaixo da seção, acima do
   conteúdo. O `h3` base zera a margem (a regra `h1,h2,h3,h4 { margin: 0 }`),
   então cada uso reinventava o respiro com `style=`. */
.titulo-bloco { margin: 0 0 var(--sp-3); }

/* Coluna de ações da tabela: alinhada à direita, encostada na borda. */
td.acoes, th.acoes { text-align: right; }

/* Coluna de nome: largura fixa para os nomes se alinharem entre as linhas. */
td.col-nome { width: 200px; font-weight: 500; }

/* Formulário que vive dentro de uma linha (célula, barra de ações): não é
   bloco, não empurra nada. */
.form-inline { display: inline; margin: 0; }

/* Linha inativa — cliente desativado. Opacidade, e não cor: a cor já significa
   plataforma e confiança nesta base. */
tr.inativa { opacity: 0.6; }

/* Ícone e texto na mesma linha, centrados entre si. Aparecia duas vezes com
   `gap` diferente (9px e 6px) sem motivo; ficou um. */
.linha-icone { display: inline-flex; align-items: center; gap: var(--sp-2); vertical-align: middle; }

/* O espaço que empurra o resto para a direita numa linha flex. */
.espacador { flex: 1; }

/* Ação destrutiva escrita como link. A cor é o único aviso, então ela é
   explícita e vem do token. */
.link-btn.perigo { color: var(--critico); }

/* Separador dentro de um bloco: uma linha e respiro dos dois lados. */
.separador { border-top: var(--bord); margin-top: var(--sp-4); padding-top: var(--sp-3); }

/* URL longa que precisa caber: quebra em qualquer ponto, e mantém a cor de
   quem a contém em vez de virar azul de link. */
.link-longo { color: inherit; text-decoration: underline; overflow-wrap: anywhere; }

/* Mensagem de erro que o JavaScript revela. Nasce escondida.
   A cor era `#f66` — hexadecimal cru, a única cor da base fora dos tokens, e
   ela não acompanhava o tema. */
.erro-inline { display: none; color: var(--critico); margin-top: var(--sp-2); font-size: var(--fs-body); }
.erro-inline.visivel { display: block; }

/* Respiros que pertencem ao bloco, e não ao elemento solto. */
.banner.espacado { margin-bottom: var(--sp-4); }
.dica.espacada { margin: 0 0 var(--sp-4); }
.legenda-oculta.afastada { margin-top: var(--sp-3); }
.acoes-grupo.afastado { margin-top: var(--sp-3); }
.bloco-linha.empilhado { display: block; }
.titulo-login { margin-bottom: var(--sp-5); }

/* AS CAIXAS DE CLIENTE DO CONVITE (Tarefa 10, 20/08) -- `fieldset` sem a borda
   e o padding que o navegador dá de graça, porque o desenho já tem os
   próprios. Cada `<label>` de opção desfaz o estilo de RÓTULO DE CAMPO que o
   `label` base impõe (caixa alta, 12px, cor terciária) pela mesma razão que
   `.conectar .aceite` já desfez em 18/08: isto não é rótulo, é uma frase que
   a pessoa lê e marca. */
.opcoes-marcar { border: 1px solid var(--border); border-radius: var(--raio-sm);
                 padding: var(--sp-3); margin: 0; display: flex; flex-direction: column;
                 gap: var(--sp-2); }
.opcoes-marcar legend { padding: 0 0 var(--sp-2); font-size: var(--fs-label);
                        font-weight: 500; text-transform: uppercase;
                        letter-spacing: var(--track-label); color: var(--text-tertiary); }
.opcao-marcar { display: flex; flex-direction: row; align-items: center; gap: var(--sp-2);
                text-transform: none; letter-spacing: normal; font-size: var(--fs-body);
                line-height: var(--lh-body); font-weight: 400; color: var(--text-primary);
                cursor: pointer; }
.opcao-marcar input[type=checkbox] { flex: none; width: 18px; height: 18px; margin: 0;
                                      padding: 0; border: 0; background: none;
                                      cursor: pointer; accent-color: var(--accent); }
