/* ===========================================================================
   App JC — web app (PWA). Design system canônico: docs/design/design-system-app-jc.md
   e a identidade oficial Closematic AI do brand kit v1.

   LEI 4 (nada de cor sem nome): nenhum hex fora do bloco `:root`. As únicas
   exceções nomeadas pelo design system são `#fff` (branco puro) — o fundo de
   moldura dos protótipos NÃO vem para cá, porque não é cor de produto.

   Verificação (deve listar só as linhas do :root e os #fff):
     grep -nE "#[0-9A-Fa-f]{3,6}" web/app.css
   =========================================================================== */

:root{
  /* Rampa unica, matiz 203 travada, degraus ancorados em CONTRASTE (ADR-0035).
     Os nomes de token do Arcade sobrevivem; so os valores mudam. */
  --navy-card:#07161E;   /* b950 - fundo do card da jornada, fase atual, aba ativa */
  --navy-deep:#09212F;   /* b900 - bloco interno "next up" */
  --navy:#0D3248;        /* b800 - texto principal */
  --navy-tint:#0D3248;   /* b800 - trilha incompleta, fase trancada (sobre o card) */
  --navy-line:#3B98D1;   /* b400 - borda de elemento trancado sobre o card */
  --navy-fade:#8BBEDD;   /* b300 - texto e icone secundario sobre o card */
  --blue-pale:#8BBEDD;   /* b300 - icone de destaque sobre navy-deep */
  --blue:#237BB2;        /* b500 - ACAO: nav ativa, avatar, link, sino */
  --blue-deep:#154B6D;   /* b700 - "dominio, pontos e links" (ADR-0035). O degrau
                            que carrega TEXTO sobre fundo claro: 9,30:1 no branco,
                            8,08:1 sobre --blue-light onde o b500 dava 4,01. */
  --blue-light:#E9F0F5;  /* b100 */
  /* "nao sabemos" (D62). Os 6 usos deste token vivem TODOS sobre fundo claro —
     nenhum sobre o card escuro — e no b300 o traco media 1,87:1 contra o --soft:
     a ausencia estava desenhada e ilegivel, que e' o mesmo que nao estar. O b600
     da 6,09:1 ali. Nao pode ser o b500: aquele degrau e' ACAO (ADR-0035), e uma
     ausencia pintada de acao convida a clicar no que nao existe. */
  --empty:#1D638F;       /* b600 */
  --track:#C9DDEA;       /* b200 - "ainda nao aconteceu" */
  --soft:#F5F8FA;        /* b50  - fundos neutros */
  /* Destaque da marca: a mesma rampa azul oficial usada nas ações do app. */
  --accent-dark:#154B6D;--accent:#237BB2;--accent-light:#E9F0F5;
  --accent-solid:#237BB2;
  --green:#1E9E66;--green-light:#E4F5EC;
  --green-dark:#167D51;   /* verde que carrega TEXTO - 5,14:1 (ADR-0033) */
  --ts:#5E6F87;           /* era #7C8AA5 = 3,48:1, reprovava (ADR-0033/0034) */
  --line:#E4EAF0;         /* borda de card */
  --field-line:#5E6F87;   /* borda de CAMPO - era 1,19:1, reprovava 1.4.11 */
  --sans:'Manrope',system-ui,sans-serif;--display:'Manrope',system-ui,sans-serif;
  /* Largura fixa, um uso so: o codigo de pareamento. Pilha do SISTEMA, sem
     `@font-face` — zero byte novo. Porque: ADR-0177. */
  --mono:ui-monospace,'SF Mono',Menlo,Consolas,monospace;

  /* ── ESCALA DE TEXTO ────────────────────────────────────────────────────
     7 degraus. Antes eram **20 tamanhos distintos**, 5 deles em meio pixel
     (10,5 · 11,5 · 12,5 · 13,5 · 14,5) e dez espremidos entre 10,5 e 15.
     Passo de meio pixel está abaixo do limiar perceptual: ninguém lê "13,5
     contra 14" como hierarquia — lê como descuido. E como quase tudo vivia
     entre 11 e 14, nada dominava, e a hierarquia inteira sobrava para a cor.

     O PISO SUBIU de 10,5 para 12 de propósito: o comprador é dono de empresa
     de serviço, tipicamente 35-55 anos, lendo no sol, com a tela suja e uma
     mão só. Presbiopia começa aos 40, e `micro` é carimbo, nunca corpo.

     A migração foi conservadora: dos 84 tamanhos de texto, 63 mudaram, e o
     maior salto foi 3px — a maioria mexeu 0,5 ou 1px. */
  --t-micro:12px;   /* carimbo de tempo, rótulo de métrica */
  --t-meta:13px;    /* apoio, prévia, legenda */
  --t-body:15px;    /* corpo, campo, botão */
  --t-lead:17px;    /* saudação, entrada de bloco */
  --t-title:20px;   /* título de tela */
  --t-hero:24px;    /* número grande */
  --t-display:28px; /* o maior — hoje só o h1 do login */

  /* ── ESCALA DE ÍCONE ────────────────────────────────────────────────────
     PRÓPRIA, e não a de texto. Ícone e letra não crescem pelo mesmo motivo:
     casar as duas faria a lupa de um campo crescer junto com o rótulo dele,
     que não é a mesma decisão. Eram 10 tamanhos avulsos entre 13 e 26. */
  --i-sm:16px;   /* dentro de linha, ao lado de texto */
  --i-md:20px;   /* dentro de círculo, nav, ação */
  --i-lg:26px;   /* ilustração de estado vazio */

  /* ── ESCALA DE RAIO ─────────────────────────────────────────────────────
     Eram 7 valores (12·14·16·18·20·22·24) sem nenhuma regra dizendo quando
     usar qual — arredondamento no olho, componente a componente. A faixa
     20-24 do "arcade" (§5 do design system) continua; o que sai são os
     degraus intermediários que competiam sem hierarquia. */
  --r-sm:14px;    /* campo, linha, bloco interno */
  --r-md:20px;    /* tile, balão, card pequeno */
  --r-lg:24px;    /* card-herói (jornada, fase) */
  --r-pill:99px;  /* pill e círculo */

  /* ── COR COM ALFA ───────────────────────────────────────────────────────
     A Lei 4 diz "nada de cor sem nome", e `rgba(13,50,72,.06)` é uma cor sem
     nome que o lint de hex não pegava — o buraco fechou junto com estes dois
     tokens. Os dois são o MESMO navy e o MESMO branco do resto da paleta, só
     que translúcidos; escritos assim, trocar a paleta continua trocando tudo. */
  --sombra-coluna:rgba(13,50,72,.06);  /* --navy a 6% — a borda da coluna no desktop */
  --branco-fraco:rgba(255,255,255,.14); /* branco a 14% — chip sobre o card escuro */
  /* ── RAMPA DO PAINEL (2026-08-16) ────────────────────────────────────────
     A malha da referência aprovada tem tamanhos que a escala `--t-*` não tem
     (9 · 10 · 11 · 14 · 16 · 18) e faz hierarquia por PESO. Somar 5 degraus
     aos 7 de `--t-*` daria 12 tamanhos anônimos — a bagunça que o design
     system desfez. Então o painel tem rampa PRÓPRIA e NOMEADA, com trabalho
     definido em cada degrau. Duas rampas com nome se leem; doze degraus sem
     dono, não.
     🔴 Fura o piso de 12px, e isso é decisão da chefia com o render na mão:
     ela aprovou nesses tamanhos. Se parecer pequeno, sobe a rampa inteira. */
  --p-nano:11px;   /* legenda de gráfico, rodapé de métrica */
  --p-rot:11px;    /* rótulo de KPI (CAPS), subtítulo de cartão, chip */
  --p-lin:12px;    /* linha e valor de funil, delta do KPI */
  --p-tab:13px;    /* aba, status bar, avatar */
  --p-card:17px;   /* título de cartão */
  --p-tit:28px;    /* título da tela */
  --r-p:16px;      /* raio do cartão do painel — o degrau da referência */
  /* `--w` é preenchido inline por barra (`style="--w:91%"`); o default aqui
     existe para a barra nunca herdar lixo se o dado faltar. */
  --w:0%;
  --p-hair:rgba(13,50,72,.08);
  --p-sh:0 1px 3px rgba(13,50,72,.10), 0 1px 2px -1px rgba(13,50,72,.10);

  /* ── ESCALA DE ESPAÇO ───────────────────────────────────────────────────
     2026-08-17. Eram 26 valores em 250 usos, escolhidos no olho, e o
     `design-lint` não checava nenhum deles — a maior superfície do arquivo
     sem token E sem portão. 11·12·13 e 14·15·16 conviviam em 132 usos: um
     passo de 1px não produz hierarquia, produz textura de descuido. É o
     mesmo argumento da escala de texto (l. 46).

     Os degraus saem da MEDIÇÃO, não de uma régua 4/8/16 teórica. Isso
     importa em UM ponto que parece erro e não é: 15 e 16 ficaram os dois.
     São papéis diferentes que por acaso estão a 1px —
       16px é a MARGEM DE TELA  (22 dos 27 usos são `margin:0 16px`)
       15px é o RESPIRO DE CARD (11 dos 14 usos são `padding:N 15px`)
     Fundir os dois moveria todos os cards ou toda a margem da tela, e
     nenhum dos dois é melhoria. Por isso a margem tem nome próprio e não é
     degrau: ela nunca mudou em nenhum dos 22 lugares onde aparece. */
  --s-1:2px;      /* fio: ícone↔rótulo, 2ª linha de um par  (1·2·3  → 18 usos) */
  --s-2:4px;      /* aperto: interior vertical de pílula    (4·5    → 19) */
  --s-3:6px;      /* gap curto                              (6·7    → 24) */
  --s-4:9px;      /* interior horizontal de pílula          (8·9·10 → 37) */
  --s-5:12px;     /* o passo padrão: gap de linha, folga entre blocos (11·12·13 → 67) */
  --s-6:15px;     /* respiro interno de card                (14·15  → 40) */
  --s-7:20px;     /* cabeçalho de tela, respiro grande      (18·20·22 → 12) */
  --s-8:32px;     /* tela sem card: login, estado vazio     (32·34  → 2) */
  --s-gutter:16px;/* A MARGEM LATERAL DA TELA. Constante, não degrau. */

  /* ── MOVIMENTO ──────────────────────────────────────────────────────────
     Os números saem da decisão já escrita na l. 210-212: 180ms é percebido
     como RESPOSTA, e acima de ~250ms começa a ser percebido como espera. O
     que faltava era o degrau ABAIXO de 180 — o realce sob o dedo quer ~100ms,
     porque acima disso o dedo já saiu e o realce vira eco.

     São três tokens e não seis porque neste app NADA SAI DE CENA: o router
     substitui a tela inteira (`raiz.replaceWith`, router.js:185), então uma
     curva de saída nunca rodaria. Token que não roda é token que mente. */
  --mov-toque:.10s;  /* realce sob o dedo: :active */
  --mov:.18s;        /* o degrau canônico — o mesmo do @keyframes entrar */
  --ease:ease-out;   /* desacelera na chegada; nada acelera na saída aqui */

  /* ── CAMADA ─────────────────────────────────────────────────────────────
     Não havia `z-index` no app. A nav pill (l. 567) fica por cima hoje por
     ACIDENTE: é o último filho do template E é posicionada, então ganha nos
     dois desempates. No dia em que uma tela renderizar algo posicionado
     depois do `navPill(...)`, a pill some por baixo em silêncio — e num PWA
     `standalone` não há botão de voltar para escapar. */
  --z-base:0;   /* conteúdo da tela */
  --z-nav:10;   /* a barra de navegação fixa */

  /* ── SOMBRA ─────────────────────────────────────────────────────────────
     2026-08-17, decisão da Junia a partir do mockup. O app era plano de
     propósito: isto é desvio consciente, não descuido.

     É `--navy` translúcido, nunca preto — preto sobre paleta fria lê como
     sujeira. Dois degraus e não cinco: numa coluna de 420px, do nível 3 em
     diante ninguém distingue e só custa paint no Android de entrada.

     ⚠️ `rgba()` só é legal dentro do `:root` (design-lint l. 128-138).

     Reversível por uma linha: se ler mal no sol (nota de ICP, l. 53-55),
     `border:1px solid var(--line)` no lugar não mexe em mais nada. */
  --sombra-1:rgba(13,50,72,.07);  /* --navy a 7%  — apoio, contato com a página */
  --sombra-2:rgba(13,50,72,.13);  /* --navy a 13% — a peça que saltou do plano */
  /* ── CONVERSA (Inbox thread) ────────────────────────────────────────────
     A thread usa a paleta oficial Closematic AI, sem a pele bege/verde da
     identidade legada. Os tokens permanecem próprios para preservar a
     semântica dos estados da conversa.

     A primeira versao declarou os quatro numa regra `.shead,.thread,…` para
     escopar a mudanca, e o `design-lint` reprovou — com razao: a Lei 4 pede
     que TODA cor tenha nome no `:root`, senao ela escapa da troca de paleta e
     da busca por token. O `:root` ainda resolve de graca o que o escopo local
     tentava resolver na mao: o `.compositor` e' IRMAO da thread, nao filho, e
     por isso precisava ser listado a dedo na regra — esquecer um seletor ali
     pintava o composer de `--soft` sem erro nenhum.

     Altura do composer: textarea 42 (`min-height`) + 9+9 de padding + 1 de
     borda = 61. Declarada, nao chutada, para os dois numeros mudarem juntos. */
  --bolha-in:#fff;                     /* recebida — branco puro */
  --bolha-out:#8BBEDD;                 /* enviada — azul claro oficial */
  --track-out:#C9DDEA;                 /* onda não tocada sobre a bolha enviada */
  --thread-bg:#F5F8FA;                 /* fundo neutro oficial */
  --sombra-bolha:rgba(13,50,72,.13);   /* navy oficial a 13% */
  --linha-composer:rgba(13,50,72,.08); /* navy oficial a 8% */
  --h-composer:61px;                   /* altura medida, nao chutada — ver acima */
}

*{box-sizing:border-box;margin:0}
/* Toque: sem o atraso do double-tap-zoom e sem o flash cinza do Android por
   cima do `:active{transform:scale(.98)}` que o app já desenha. */
html{-webkit-tap-highlight-color:transparent}
a,button,input,select,textarea,label,[role="button"]{touch-action:manipulation}
h1,h2,h3{text-wrap:balance}
/* CSS de autor vence o `[hidden]{display:none}` do navegador. Sem esta lei,
   qualquer componente que declare `display:flex|grid` reaparece vazio — medido
   no aviso de pareamento do Perfil em 22/08/2026. Sem exceções: hidden é estado
   semântico, não uma sugestão visual. */
[hidden]{display:none!important}

/* No celular o app ocupa a tela inteira; no desktop vira a coluna de 375px do
   protótipo, centrada sobre `soft`. É a mesma tela, não um layout novo. */
/* 🔴 O body NÃO é flex container (mudou 2026-08-10). Era `display:flex` +
   `justify-content:center`, e a centralização do app dependia disso — o que
   funciona até alguém injetar um elemento no `<body>`. Extensão de navegador
   faz exatamente isso: a injetada vira flex-item, divide a linha, e o app é
   EMPURRADO para o lado (medido em produção num notebook com barra de SEO —
   o app apareceu colado na direita, com a nav pill solta no meio da tela).
   Com `margin:0 auto` no próprio #app, a centralização não depende de mais
   ninguém e é imune a qualquer coisa que o navegador do cliente injete. */
body{
  background:var(--soft);
  font-family:var(--sans);
  color:var(--navy);
  min-height:100vh;
  min-height:100dvh;
  -webkit-font-smoothing:antialiased;
}
#app{
  width:100%;
  max-width:420px;
  margin:0 auto;
  background:#fff;
  min-height:100vh;
  min-height:100dvh;
  display:flex;
  flex-direction:column;
  /* espaço da barra de navegação fixa + safe area do iOS */
  padding-bottom:calc(84px + env(safe-area-inset-bottom));
}
#app.no-nav{padding-bottom:0}

/* No DESKTOP a coluna precisa ter BORDA.
   O app é branco sobre `--soft`, e os dois medem 1,07:1 entre si — ou seja, no
   monitor a coluna não tinha nenhum limite visível: lia como uma página que não
   terminou de carregar, não como "o app, no desktop". A hipótese de que boa
   parte do 'muito amador' veio daí é plausível justamente porque a revisão foi
   feita no monitor, não no celular.
   Só borda e sombra, de propósito: emoldurar a coluna como um aparelho exigiria
   limitar a ALTURA, e a nav pill é `position:fixed` no viewport (l. 446) — ela
   se descolaria da moldura e ficaria solta no meio da tela, que é exatamente o
   defeito que o comentário do `body` acima descreve. */
@media (min-width:600px){
  #app{
    border-left:1px solid var(--line);
    border-right:1px solid var(--line);
    box-shadow:0 0 40px var(--sombra-coluna);
  }
}

/* --- tipografia base ---------------------------------------------------- */
h1,h2,h3{font-family:var(--display);font-weight:600;color:var(--navy)}
button{font-family:var(--sans)}

/* --- utilitários de estado (D62: ausência tem cor própria) --------------- */
.empty{color:var(--empty)}

/* --- carregando --------------------------------------------------------- */
.loading{
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:var(--s-5);color:var(--ts);font-size:var(--t-meta);
}
.spinner{
  width:28px;height:28px;border-radius:50%;
  border:3px solid var(--track);border-top-color:var(--blue);
  animation:spin .8s linear infinite;
}
@keyframes spin{to{transform:rotate(360deg)}}

/* --- esqueleto de carregamento (consultoria 2026-08-12, decisão nº1) -------
   O spinner acima continua existindo para a espera DENTRO de uma tela já
   desenhada (a thread da inbox). O esqueleto serve o outro caso: a tela
   inteira ainda não existe, e um spinner centralizado no vazio não diz nada
   sobre o que vem.

   NENHUM token novo, de propósito. `--track` já significa "ainda não
   aconteceu" no design system, que é exatamente o que um esqueleto é; usá-lo
   aqui mantém o `contrast-lint` verde e não inventa um cinza paralelo.

   ⚠️ Estas classes NÃO carregam texto. Se algum dia carregarem, o par de cor
   precisa ir para a tabela do design system e passar pelo portão de contraste
   — `--track` sobre `--soft` é 1,4:1, ilegível de propósito: é forma, não
   informação. */
.sk{flex:1;padding:var(--s-gutter);display:flex;flex-direction:column;gap:var(--s-5)}
.sk i{display:block;background:var(--track);border-radius:var(--r-sm);height:14px}
.sk .bloco{height:96px;border-radius:var(--r-md)}
.sk .alto{height:150px;border-radius:var(--r-md)}
.sk .curto{width:45%}
.sk .medio{width:70%}
.sk .linha{display:flex;gap:var(--s-5);align-items:center;padding:var(--s-5) 0}
.sk .circulo{width:42px;height:42px;border-radius:50%;flex-shrink:0}
.sk .corpo{flex:1;display:flex;flex-direction:column;gap:var(--s-4)}
/* `flex:1` acima serve o `.corpo` DENTRO de `.linha` (horizontal), onde ele
   ocupa o resto da largura. Como filho direto de `.sk` (que é coluna), o mesmo
   `flex:1` faz cada par esticar em ALTURA e espalhar a ficha pela tela inteira
   — os pares ficavam a mais de 200px um do outro, nada parecido com a tela que
   estava por vir. Visto na captura; nenhum proof pegaria. */
.sk > .corpo{flex:none}
/* A pulsação que diz "está vindo" — sem ela o esqueleto lê como conteúdo
   quebrado, cinza e parado. É a diferença entre "carregando" e "vazio".

   É PULSAÇÃO e não o shimmer diagonal de sempre por duas razões medidas:
     1. shimmer precisa de um branco translúcido no gradiente, e cor sem nome
        fora do `:root` viola a lei 4 — o `design-lint` reprova, e reprovou;
     2. `opacity` é composited pelo GPU; `background-position` força repaint da
        caixa a cada quadro. Numa lista de esqueleto isso é a diferença entre
        liso e picotado no celular do cliente, que é o aparelho que importa. */
.sk i{animation:pulsar 1.4s ease-in-out infinite}
@keyframes pulsar{0%,100%{opacity:1}50%{opacity:.45}}

/* --- entrada da tela ------------------------------------------------------
   A troca de tela era um corte seco: o conteúdo aparecia inteiro, no mesmo
   quadro. Uma subida de 6px com fade dá ao olho a informação de que algo
   CHEGOU, em vez de a tela ter sempre estado ali. Curta de propósito — 180ms
   é percebido como resposta; acima de ~250ms começa a ser percebido como
   espera, e aí a animação passa a custar em vez de render. */
/* 🔴 SEM `transform` AQUI, E ISSO NÃO É ESCOLHA ESTÉTICA — 2026-08-19.
   Quem recebe `.entrando` é o `palco`, que É o `#app` (`router.js:192`), e a nav
   mora DENTRO dele (`${navPill(...)}` no template de cada tela). Pelo spec do
   CSS, ancestral com `transform` vira containing block dos descendentes
   `position:fixed` — durante os .18s da animação a nav deixava de se ancorar no
   viewport e passava a se ancorar no `#app`, que é mais alto que a tela. Ela ia
   parar no fim do conteúdo, fora de vista, A CADA navegação. Reportado como
   "o footer com as abinhas está sumindo".
   `opacity` sozinha não cria containing block, então a nav fica congelada — que
   é o que se espera de um app.
   ⚠️ Consequência: a entrada perdeu os 6px de deslize e virou fade puro. A
   ADR-0132 fixou `--mov` como o degrau de movimento e ele continua valendo em
   todo o resto; o que saiu foi o deslocamento DESTE keyframe. O conserto
   estrutural (tirar a nav de dentro do `#app`) devolveria os 6px, mexe em 6
   telas e fica registrado como opção, não feito aqui. */
@keyframes entrar{from{opacity:0}to{opacity:1}}
.entrando{animation:entrar var(--mov) var(--ease) both}

/* --- transição --------------------------------------------------------------
   Só nos nós que PERSISTEM entre estados. Item de lista (`.conv`, `.task`) fica
   fora: cada tela é reescrita por `innerHTML` (router.js), então dispararia
   sobre zero mudança hoje e viraria pisca-pisca no dia do render incremental.
   Três propriedades e só elas — `background-color`, `opacity`, `transform` são
   compostas na GPU; `all`, `height`, `box-shadow` e `filter` forçam layout ou
   repaint por quadro. Mesma razão do `.sk i` pulsar por `opacity`. */
.botao,.cta,.linha-acao,.tab,.reply,.nav a,.pulse .cta{
  transition:background-color var(--mov) var(--ease),opacity var(--mov) var(--ease),transform var(--mov-toque) var(--ease);
}
.botao:active,.cta:active,.linha-acao:active,.tab:active,.reply:active,.nav a:active,.pulse .cta:active{transform:scale(.98)}
/* Atrasar o indicador de foco é atrasar a informação de ONDE VOCÊ ESTÁ. */
.botao:focus-visible,.cta:focus-visible,.linha-acao:focus-visible,.tab:focus-visible,.reply:focus-visible,.nav a:focus-visible,.pulse .cta:focus-visible{transition:none}
/* Ponteiro fino (desktop) ganha estado de hover; no toque não existe hover. */
@media (hover:hover){
  .botao:hover,.cta:hover,.tab:hover,.linha-acao:hover,.nav a:hover,.conv:hover{filter:brightness(.96)}
}

/* Movimento reduzido não é "menos animação": é NENHUM movimento. O brilho e a
   subida saem inteiros; o esqueleto vira forma estática, que continua
   comunicando "ainda não chegou" sem nada se mexer. Quem liga essa preferência
   costuma fazê-lo por enjoo ou vertigem — meia dose não serve.
   ⚠️ O spinner contradizia a lei três linhas acima dela: só ficava mais LENTO
   (2,4s), girando por mais tempo na frente de quem pediu para nada girar. */
@media (prefers-reduced-motion:reduce){
  *{transition:none!important}
  .sk i{animation:none}
  .entrando{animation:none}
  .spinner{animation:none;opacity:.6}
}

/* --- erro (nunca vermelho: o design system não tem vermelho) ------------- */
.erro{
  margin:var(--s-5) var(--s-gutter);background:var(--accent-light);border-radius:var(--r-md);
  padding:var(--s-5) var(--s-6);display:flex;align-items:center;gap:var(--s-5);
}
.erro .ic{
  width:36px;height:36px;border-radius:50%;background:var(--accent);
  display:flex;align-items:center;justify-content:center;flex-shrink:0;
}
.erro .ic i{font-size:var(--i-md);color:#fff}
.erro p,.erro h1{font-size:var(--t-meta);color:var(--accent-dark);font-weight:500}

/* =========================================================================
   LOGIN — única tela sem referência canônica (as 5 do design são pós-login).
   Construída com as peças existentes: título Manrope navy, campo de borda
   `line`, CTA pill azul. Nenhum token novo foi inventado para ela.
   ========================================================================= */
.login{flex:1;display:flex;flex-direction:column;justify-content:center;padding:32px 24px;gap:22px}
/* O lockup é o export oficial do Brand Kit v1.1. Fica acima do "Welcome back"
   e não substitui o título: a marca diz de quem é o app, o título diz o que
   fazer nesta tela. */
.login .lockup{display:flex;align-items:center;gap:11px;margin-bottom:2px}
.login .lockup img{display:block;width:min(220px,100%);height:auto}
.login .marca{display:flex;flex-direction:column;gap:6px}
.login .marca h1{font-size:var(--t-display);letter-spacing:-.4px}
.login .marca p{font-size:var(--t-body);color:var(--ts)}
.login form{display:flex;flex-direction:column;gap:11px}
.campo{display:flex;flex-direction:column;gap:6px}
.campo label{font-size:var(--t-meta);font-weight:500;color:var(--ts)}
.campo input{
  font-family:var(--sans);font-size:var(--t-body);color:var(--navy);
  background:#fff;border:1px solid var(--field-line);border-radius:var(--r-sm);
  padding:14px 16px;width:100%;
}
.campo input:focus{outline:2px solid var(--blue);outline-offset:1px;border-color:var(--blue)}
.botao{
  background:var(--blue);color:#fff;font-size:var(--t-body);font-weight:500;
  padding:15px 18px;border-radius:var(--r-pill);border:0;width:100%;margin-top:4px;
}
.botao:disabled{opacity:.55}
.login .nota{font-size:var(--t-micro);color:var(--ts);text-align:center;line-height:1.5}
/* magic link como LINK de texto — ação secundária, não um 3º botão cheio */
.login .link-btn{
  background:none;border:0;color:var(--blue);font-size:var(--t-body);font-weight:500;
  min-height:44px;padding:8px;width:100%;text-align:center;cursor:pointer;
}
.login .link-btn:disabled{opacity:.55}
/* Nota que vale para o arquivo inteiro: número de PR aqui vai SEM cerquilha —
   o design-lint lê a cerquilha seguida de 3 ou 6 dígitos como cor crua. */

/* =========================================================================
   CHROME COMPARTILHADO — header e nav pill (protótipo aprovado, verbatim)
   ========================================================================= */
.header{display:flex;align-items:center;justify-content:space-between;padding:20px 20px 12px}
.who{display:flex;align-items:center;gap:11px}
.avatar{
  width:46px;height:46px;border-radius:50%;background:var(--blue);color:#fff;
  display:flex;align-items:center;justify-content:center;
  font-family:var(--display);font-weight:600;font-size:var(--t-lead);
}
.hey{font-family:var(--display);font-weight:600;font-size:var(--t-lead);color:var(--navy)}
/* Telas irmãs abrem com o título da tela — a saudação é privilégio da home
   (nota de chrome 1 do design system). */
.titulo-tela{font-family:var(--display);font-weight:600;font-size:var(--t-title);color:var(--navy)}
/* Pontos ganhos sao RECOMPENSA, nao demanda: ninguem esta esperando o dono por
   causa deles. Pela ADR-0034 isso e' azul — e o b700 e' o degrau que a ADR-0035
   destina a "pontos". Uma segunda cor de marca roubava a atencao do pulso de lead na mesma
   tela, que e' o unico lugar da home onde alguem esta mesmo esperando. */
.pts{display:flex;align-items:center;gap:4px;margin-top:1px;font-size:var(--t-meta);font-weight:500;color:var(--blue-deep)}
.pts i{color:var(--blue);font-size:var(--i-sm)}
/* `min-width:0` é o que faz o ellipsis valer: sem ele o filho flex adota
   `min-width:auto` e ESTICA até caber o nome inteiro, empurrando o selo para
   fora da linha em vez de cortar o nome. */
.quem{min-width:0}
/* O nome da EMPRESA — qual conta está aberta, não quem está usando. Uma linha
   sempre: razão social longa empilharia três alturas de texto no cabeçalho. */
.empresa{
  margin-top:1px;font-size:var(--t-micro);font-weight:500;color:var(--ts);
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}

/* ── O SELO DE FRESCOR (substituiu o sino) ──────────────────────────────────
   🚫 SEM ANIMAÇÃO: ponto pulsando numa tela que fica aberta é bateria, gatilho
   para quem tem enjoo de movimento, e em dois dias ninguém mais vê.
   🚫 SEM COR DE ALERTA: ninguém espera por um carimbo de tempo.

   O terceiro estado é o que importa: `sem` usa `--empty`, a cor de "não
   sabemos". O BFF leu o banco e não falou com o GHL, então o número pode estar
   velho — e isso não é "está tudo bem" nem é alarme. */
.vivo{
  display:flex;align-items:center;gap:var(--s-3);flex-shrink:0;
  font-size:var(--t-micro);font-weight:500;
  padding:var(--s-3) var(--s-4);border-radius:var(--r-pill);
  background:var(--soft);color:var(--ts);
}
.vivo b{width:7px;height:7px;border-radius:50%;background:currentColor;display:block;flex-shrink:0}
.vivo.fresco{background:var(--green-light);color:var(--green-dark)}
.vivo.sem{color:var(--empty)}

/* A FAIXA DE ABAS virou chrome (`faixaDeAbas`), então a regra de ALVO DE TOQUE
   mora aqui e não na tela que a inventou. O botão media 30px — abaixo do piso
   legal de 24 e longe dos 44 que funcionam para quem toca de pé, com a mão
   suja. `.tabs .tab` vence `.tab` por especificidade, então corrige a Inbox
   junto: alvo pequeno não é problema de uma tela só. */
.tabs .tab{min-height:44px;display:inline-flex;align-items:center;justify-content:center}

/* =========================================================================
   HOME — estrutura originada no protótipo histórico do Arcade e atualizada
   para o Brand Kit v1.1. As variantes do pulso de leads existem
   porque o design system §4.2 define TRÊS humores e o protótipo desenhou um.
   ========================================================================= */
/* ── O PRIMITIVO DE CARD ───────────────────────────────────────────────────
   A superfície de card (fundo, raio, respiro) estava redesenhada em cada
   componente, e saíram CINCO respiros para a mesma ideia (12·13·14·15·16).
   Passa a ter um lugar só. Quem não herda declara o seu, e é a ORDEM que faz
   isso funcionar: este bloco vem antes, e especificidade igual dá a vitória a
   quem vem depois. Preço medido na adoção: `.overall` +2, `.ag-linha` +6 — é a
   uniformização, não efeito colateral.

   A `.conv` SAIU deste grupo em 27/08, quando a lista virou linha uniforme
   (WhatsApp) e deixou de ser card: ela não quer raio nem o respiro de card, e
   mantê-la aqui obrigava a desfazer tudo por sobrescrita — inclusive zerar o
   raio à mão, que o `slop-lint` reprova com razão: zero não é degrau da escala
   nem círculo. Sair do grupo é mais barato que anular o grupo.
   Os outros quatro seguem idênticos. */
.card,.overall,.ag-linha,.phase{background:#fff;border-radius:var(--r-md);padding:var(--s-6)}
.card{margin:0 var(--s-gutter) var(--s-5)}
.card.plano{background:var(--soft)}
.card.contorno{border:1px solid var(--line)}
/* A única peça que sai do plano: o card do lead esperando. Dois degraus — o
   curto encosta na página, o longo levanta (ADR-0132). */
.card.urgente{box-shadow:0 1px 2px var(--sombra-1),0 8px 20px var(--sombra-2)}
/* O pulso pinta a própria superfície e é filho único: herdar o respiro daria
   caixa dentro de caixa. O `overflow` conserta um defeito MEDIDO — `.pulse` não
   tem raio e raio de pai não recorta filho, então o destaque vinha de canto reto
   dentro de um card de 20px. */
.card:has(.pulse){padding:0;overflow:hidden}

/* --- pulso de leads: 3 humores ------------------------------------------ */
.pulse{padding:13px 15px;display:flex;align-items:center;gap:11px}
.pulse .ic{
  width:40px;height:40px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;flex-shrink:0;
}
.pulse .ic i{font-size:var(--i-md);color:#fff}
.pulse .txt{flex:1;min-width:0}
.pulse h2{font-size:var(--t-body);font-weight:500;color:var(--navy);font-family:var(--sans)}
.pulse p{font-size:var(--t-micro);font-weight:500}

/* esperando — azul de atenção, e é o único humor com botão */
.pulse.wait{background:var(--accent-light)}
.pulse.wait .ic{background:var(--accent)}
.pulse.wait p{color:var(--accent-dark)}
/* O botão do pulso é uma PÍLULA dentro de uma linha flex — o oposto do `.cta`
   genérico lá embaixo (linha ~236), que é o botão de largura total dos cards da
   Journey.
   ⚠️ `display`, `width` e `margin-top` PRECISAM estar declarados aqui. A regra
   `.cta` vence por não ter concorrente nessas três propriedades: especificidade
   maior só ganha no que ela declara. Sem estas linhas o botão herda
   `display:block;width:100%`, come a linha inteira, empurra o `.txt` para uma
   coluna de uma palavra por linha e ainda transborda o card.
   Foi exatamente o que aconteceu: `.pulse .cta` nasceu no bloco 2, o `.cta`
   genérico entrou no bloco 3 (`134cefc`) e quebrou o de cima sem tocar nele. */
/* AFORDANCIA x DEMANDA (ADR-0034): `.cta` serve dois papeis. Na jornada e
   'abrir tarefa' — ninguem esperando, entao AZUL (regra generica acima). Aqui,
   dentro do pulso, e 'reply'/'call' para um lead parado com relogio correndo:
   demanda, e por isso o unico .cta de atenção. */
.pulse .cta{
  display:inline-block;width:auto;margin-top:0;white-space:nowrap;
  background:var(--accent-solid);color:#fff;font-size:var(--t-meta);font-weight:500;
  padding:9px 18px;border-radius:var(--r-pill);border:0;text-decoration:none;flex-shrink:0;
}
/* ── PULSO DOMINANTE ───────────────────────────────────────────────────────
   Quando há lead esperando, este bloco vira o dono da tela. Não é "o mesmo
   card maior": ele se reorganiza — o ícone e o texto ficam em cima, o botão
   desce FULL-WIDTH com altura de dedo, e os contadores viram um rodapé.

   Antes, o bloco com relógio correndo e dinheiro em jogo era o MENOR da tela:
   título de 14px, botão de 34px encostado na margem, e o card preto da jornada
   (um to-do de setup) dominando logo abaixo. Saliência visual — tamanho ×
   contraste — é o que governa a primeira fixação; a ordem no DOM não governa
   nada. A ordem já estava certa desde sempre, o peso é que não existia.

   O botão em `--t-body` e 15px de padding dá 45px de altura: passa o mínimo
   obrigatório do WCAG (24px) e alcança a recomendação de 44px, que é a que
   importa para quem toca com a mão suja, de pé, entre um serviço e outro. */
.pulse.big{flex-direction:column;align-items:stretch;gap:13px;padding:16px 15px}
.pulse.big .topo{display:flex;align-items:center;gap:11px}
.pulse.big h2{font-family:var(--display);font-size:var(--t-lead);font-weight:600;line-height:1.25}
.pulse.big p{font-size:var(--t-meta);margin-top:3px}
.pulse.big .cta{
  display:block;width:100%;text-align:center;white-space:normal;
  font-size:var(--t-body);padding:15px 18px;
}
/* Contexto, não ação: desce para o rodapé e fica no azul profundo de apoio. */
.pulse.big .rodape{
  display:block;text-align:center;font-size:var(--t-micro);font-weight:500;
  color:var(--accent-dark);text-decoration:none;opacity:.85;
}
/* Resumo acionável da fila: o número governa a leitura, não um contato nem um
   cronômetro técnico. A ação permanece curta nas três larguras. */
.pulse.follow-up{display:grid;grid-template-columns:44px minmax(0,1fr) auto;align-items:center;gap:var(--s-5);padding:var(--s-6)}
.pulse.follow-up .ic{width:44px;height:44px}
.pulse.follow-up h2{font-family:var(--display);font-size:var(--t-lead);font-weight:600;line-height:1.25}
.pulse.follow-up p{margin-top:var(--s-1);font-size:var(--t-meta);line-height:1.4}
.pulse.follow-up .cta{min-height:44px;display:inline-flex;width:auto;align-items:center;justify-content:center;gap:var(--s-2);padding:0 var(--s-5)}
.pulse.follow-up .cta i{font-size:var(--i-sm)}
@media (max-width:599px){
  .pulse.follow-up{grid-template-columns:44px minmax(0,1fr);align-items:start}
  .pulse.follow-up .cta{grid-column:2;justify-self:start}
}
/* em dia — verde CONFIRMA */
.pulse.ok{background:var(--green-light)}
.pulse.ok .ic{background:var(--green)}
.pulse.ok p{color:var(--green-dark)}
/* convite — azul INFORMA */
.pulse.invite{background:var(--soft)}
.pulse.invite .ic{background:#fff}
.pulse.invite .ic i{color:var(--blue)}
.pulse.invite p{color:var(--ts)}

/* --- card da jornada ----------------------------------------------------- */
.journey{background:var(--navy-card);border-radius:var(--r-lg);padding:16px 17px;text-decoration:none;display:block}
/* ── JORNADA COLAPSADA ─────────────────────────────────────────────────────
   A contraparte do pulso dominante: com lead esperando, a trilha inteira vira
   uma linha. Continua sendo o mesmo link, com o próximo passo e o progresso
   escritos — só deixa de ser o objeto mais pesado da tela num momento em que
   a coisa mais importante é outra. */
.journey.slim{display:flex;align-items:center;gap:10px;padding:13px 15px;border-radius:var(--r-md)}
.journey.slim .l{display:flex;align-items:center;gap:9px;flex:1;min-width:0}
.journey.slim .l i{font-size:var(--i-sm);color:var(--blue-pale);flex-shrink:0}
.journey.slim .l span{font-size:var(--t-meta);font-weight:500;color:#fff;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.journey.slim em{font-size:var(--t-micro);font-weight:500;font-style:normal;color:var(--navy-fade);flex-shrink:0}
.journey.slim > i{font-size:var(--i-sm);color:var(--navy-fade);flex-shrink:0}
.jhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--s-5)}
.jhead span{font-family:var(--display);font-weight:600;font-size:var(--t-body);color:#fff}
.jhead em{background:#fff;color:var(--navy);font-size:var(--t-micro);font-weight:500;font-style:normal;padding:var(--s-2) var(--s-5);border-radius:var(--r-pill)}
.trail{display:flex;align-items:center;gap:var(--s-3);margin-bottom:var(--s-5)}
.node{
  width:30px;height:30px;border-radius:50%;display:flex;align-items:center;
  justify-content:center;border:2.5px solid #fff;flex-shrink:0;
}
.node.done{background:var(--green)}
.node.done i{font-size:var(--i-sm);color:#fff}
/* O no da fase ATUAL diz "voce pode fazer isto agora" — afordancia, nao demanda:
   ninguem do outro lado esta esperando. Azul pela identidade vigente. A cor
   legada também reprovava contraste com branco num numero de 13px. */
.node.now{width:34px;height:34px;background:var(--blue);font-family:var(--display);font-weight:600;font-size:var(--t-meta);color:#fff}
.node.lock{background:var(--navy-tint);border-style:dashed;border-color:var(--navy-line)}
.node.lock i{font-size:var(--i-sm);color:var(--navy-fade)}
.seg{flex:1;height:5px;border-radius:var(--r-pill);background:var(--navy-tint)}
.seg.done{background:var(--green)}
.next{background:var(--navy-deep);border-radius:var(--r-sm);padding:var(--s-4) var(--s-6);display:flex;align-items:center;justify-content:space-between;gap:var(--s-4)}
.next .l{display:flex;align-items:center;gap:var(--s-4);min-width:0}
.next .l i{font-size:var(--i-sm);color:var(--blue-pale);flex-shrink:0}
.next span{font-size:var(--t-meta);font-weight:500;color:#fff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.next em{background:var(--blue);color:#fff;font-size:var(--t-micro);font-weight:500;font-style:normal;padding:var(--s-2) var(--s-5);border-radius:var(--r-pill);flex-shrink:0}

/* --- métricas do mês ----------------------------------------------------- */
/* O grid dos 3 números virou uma linha DENTRO do link, para o rodapé "see this
   month's report ›" caber embaixo sem entrar na grade e virar um quarto card. */
.metrics{display:block;padding:0 var(--s-gutter) var(--s-6)}
.mrow{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--s-4)}
/* A única pista de que a faixa é tocável — antes ela era um `<a>` com
   `text-decoration:none` e `color:inherit`, ou seja, um link sem nenhum sinal
   de link. Em `--blue` porque é AÇÃO (ADR-0034), e com chevron porque a seta é
   o que o polegar procura. */
.mver{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-1);
  margin-top:var(--s-4);font-size:var(--t-meta);font-weight:500;color:var(--blue)}
.mver i{font-size:var(--i-sm)}
.m{background:var(--soft);border-radius:var(--r-md);padding:var(--s-5);text-align:center}
.m b{display:block;font-family:var(--display);font-weight:600;font-size:var(--t-hero);color:var(--navy);letter-spacing:-.5px}
.m span{font-size:var(--t-micro);font-weight:500;color:var(--ts)}
.m.ok{background:var(--green-light)}
.m.ok b{color:var(--green-dark)}
/* A ausência tem cor própria (D62): o traço não é um número apagado. */
.m b.empty{color:var(--empty)}

/* =========================================================================
   JOURNEY — `docs/design/tela-journey.html`, verbatim onde possível.
   Os nós (.node) são os MESMOS da home: um vocabulário só para a trilha.
   ========================================================================= */
.overall{margin:0 var(--s-gutter) var(--s-6);background:var(--soft)}
.orow{display:flex;justify-content:space-between;align-items:baseline;margin-bottom:var(--s-4)}
.orow b{font-family:var(--display);font-weight:600;font-size:var(--t-body);color:var(--navy)}
.orow span{font-size:var(--t-micro);font-weight:500;color:var(--ts)}
.obar{height:7px;border-radius:var(--r-pill);background:var(--track);overflow:hidden}
.obar i{display:block;height:100%;border-radius:var(--r-pill);background:var(--green)}

.phase{margin:0 var(--s-gutter) var(--s-5);border-radius:var(--r-lg);overflow:hidden}
.phase.done{background:var(--green-light);padding:var(--s-5) var(--s-6);display:flex;align-items:center;gap:var(--s-5)}
.phase.done .ic{width:38px;height:38px;border-radius:50%;background:var(--green);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.phase.done .ic i{font-size:var(--i-md);color:#fff}
.phase.done h3{font-size:var(--t-body);font-weight:500;color:var(--navy)}
.phase.done p{font-size:var(--t-micro);font-weight:500;color:var(--green-dark)}

/* Os `17px` ficam crus de propósito: não há degrau em 17 (a escala vai de 15 a
   20) e este padding é o MESMO do `.journey` — o mesmo card navy visto na Home,
   congelado nesta rodada. Mover só um desfaz o par. Mudam juntos. */
.phase.now{background:var(--navy-card);padding:16px 17px}
.phhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--s-6)}
.phhead span{font-family:var(--display);font-weight:600;font-size:var(--t-body);color:#fff}
.phhead em{background:#fff;color:var(--navy);font-size:var(--t-micro);font-weight:500;font-style:normal;padding:var(--s-2) var(--s-5);border-radius:var(--r-pill)}

.task{display:flex;gap:var(--s-5);position:relative}
.task .rail{display:flex;flex-direction:column;align-items:center;flex-shrink:0}
.vseg{width:5px;flex:1;min-height:14px;border-radius:var(--r-pill);background:var(--navy-tint);margin:var(--s-1) 0}
.vseg.done{background:var(--green)}
.tbody{flex:1;padding-bottom:var(--s-5);min-width:0}
.tbody.last{padding-bottom:0}
.trow{display:flex;align-items:center;justify-content:space-between;gap:var(--s-4)}
.tbody h4{font-size:var(--t-body);font-weight:500;color:#fff;padding-top:var(--s-3)}
.tbody h4.muted{color:var(--navy-fade)}
/* ⚠️ O 2º seletor NAO e' redundante, e a medicao provou: o card do quiz nao tem
   `.tbody`, entao a descricao caia FORA desta regra e herdava a cor do
   documento — medido em Chrome, 1,37:1 sobre o `--navy-card`, ou seja a frase
   que explica POR QUE pedimos cada resposta, invisivel. O lint de contraste nao
   pega esta classe (so mede pares da MESMA regra); quem pega e' o
   `trava-fluxo.proof.mjs`, no DOM renderizado. */
.tbody .desc,.phase.now>.desc{font-size:var(--t-micro);color:var(--navy-fade);margin-top:var(--s-2);line-height:1.45}
.chip{font-size:var(--t-micro);font-weight:500;font-style:normal;padding:var(--s-2) var(--s-5);border-radius:var(--r-pill);flex-shrink:0;margin-top:var(--s-2)}
.chip.won{background:var(--branco-fraco);color:var(--navy-fade)}
.chip.go{background:var(--blue);color:#fff}
.cta{display:block;width:100%;margin-top:var(--s-5);background:var(--blue);color:#fff;font-family:var(--sans);font-size:var(--t-body);font-weight:500;padding:var(--s-6);border-radius:var(--r-pill);border:0;text-align:center}
/* "em breve" — capacidade que ainda não existe. Nunca botão morto (D62). */
.embreve{margin-top:var(--s-5);display:flex;align-items:center;gap:var(--s-3);font-size:var(--t-micro);color:var(--navy-fade)}
.embreve i{font-size:var(--i-sm)}
/* pular: existe no motor (skippable), mas nunca compete com a ação primária */
.pular{margin-top:var(--s-4);background:none;border:0;color:var(--navy-fade);font-size:var(--t-meta);text-decoration:underline;padding:var(--s-1)}

.phase.lock{background:var(--soft);padding:var(--s-6)}
.lkhead{display:flex;align-items:center;gap:var(--s-5)}
.lkhead .ic{width:38px;height:38px;border-radius:50%;background:var(--track);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.lkhead .ic i{font-size:var(--i-md);color:var(--ts)}
.lkhead h3{font-size:var(--t-body);font-weight:500;color:var(--navy)}
.lkhead p{font-size:var(--t-micro);font-weight:500;color:var(--ts)}

/* --- formulário da tarefa (dentro do card navy da fase atual) ------------- */
.tform{margin-top:var(--s-5);display:flex;flex-direction:column;gap:var(--s-4)}
/* `.rot` e' o rotulo do grupo sim/nao: dois botoes nao dividem um <label>, entao
   ali vai um <span> apontado por aria-labelledby. Mesma pinta do <label>. */
.tform label,.tform .rot{font-size:var(--t-meta);font-weight:500;color:var(--navy-fade);display:block;margin-bottom:var(--s-2)}
/* A lista É explícita, e a tentação de trocá-la por `input:not([type=file])
   :not([type=checkbox])` foi MEDIDA e recusada: cada `:not()` carrega a
   especificidade do argumento, então a versão com exclusões sobe de (0,2,1)
   para (0,3,1) e passa a VENCER `.tform .busca input{padding-left:36px}`
   (l. 762). O `padding` shorthand daqui apaga o `padding-left` de lá, e o texto
   da busca das 5.102 categorias entra DEBAIXO da lupa — medido em jsdom com o
   `app.css` real: 36px → 15px, com o campo comum imóvel nos dois casos como
   controle. Nenhum lint deste repo vê isso.
   ⚠️ Tipo novo de `<input>` no formulário da jornada TEM de ser somado aqui:
   fora da lista ele sai cru, caixa branca do navegador dentro do card navy —
   e no desktop nada denuncia, porque lá o teclado não muda. */
.tform input[type=text],.tform input[type=number],.tform input[type=tel],
.tform input[type=email],.tform input[type=url],.tform textarea,.tform select{
  font-family:var(--sans);font-size:var(--t-body);color:var(--navy);background:#fff;
  border:0;border-radius:var(--r-sm);padding:var(--s-5) var(--s-6);width:100%;
}
.tform textarea{min-height:78px;resize:vertical}
.tform input[type=file]{
  font-family:var(--sans);font-size:var(--t-meta);color:#fff;width:100%;
  background:var(--navy-deep);border:1px dashed var(--navy-line);
  border-radius:var(--r-sm);padding:var(--s-5) var(--s-6);
}
.tform input[type=file]::file-selector-button{
  font-family:var(--sans);font-size:var(--t-meta);font-weight:500;
  background:#fff;color:var(--navy);border:0;border-radius:var(--r-pill);
  padding:var(--s-3) var(--s-6);margin-right:var(--s-5);
}
.tform .sim-nao{display:flex;gap:var(--s-4)}
.tform .sim-nao button{
  flex:1;border:1.5px solid var(--navy-tint);background:none;color:#fff;
  font-family:var(--sans);font-size:var(--t-body);padding:var(--s-5);border-radius:var(--r-pill);
}
.tform .sim-nao button[aria-pressed=true]{background:#fff;color:var(--navy);border-color:#fff}
.tform .dica{font-size:var(--t-micro);color:var(--navy-fade);line-height:1.4}
/* "falta preencher" e' instrucao para o dono, nao um lead esperando: azul claro
   sobre o card escuro (ADR-0034). 9,20:1 contra o --navy-card. */
.tform .falta{color:var(--navy-fade);font-size:var(--t-micro);font-weight:500}

/* --- a recusa de UM campo, ao lado da caixa que a causou ------------------
   Mora dentro do `[data-campo]`, e não junto do `.falta` do formulário: com uma
   pergunta por vez a falta do quiz é sempre "responda esta", e a recusa de
   FORMA precisa apontar a caixa — "o telefone tem 10 dígitos" 200px abaixo do
   telefone é a mesma frase valendo menos. Mesmos tokens do `.falta`: é
   instrução para o dono, não alarme. */
.tform .recusa-campo{
  margin-top:var(--s-2);
  font-size:var(--t-micro);
  font-weight:500;
  color:var(--navy-fade);
  line-height:1.4;
}
/* O palpite seguro vira BOTÃO, nunca troca automática: adivinhar domínio de
   e-mail manda o acesso do cliente para a caixa de outra pessoa. 44px de alvo
   pelo mesmo piso de toque do resto (WCAG 2.2 AA §2.5.8) — aqui pelo padding,
   porque `min-height` numa caixa `inline` não vale. */
.tform .usar-sugestao{
  border:0;
  background:none;
  padding:var(--s-3) 0;
  font-family:var(--sans);
  font-size:var(--t-micro);
  font-weight:600;
  color:#fff;
  text-decoration:underline;
}
/* A caixa recusada se diz recusada por mais de UMA via: o texto ao lado e o anel
   na borda. Cor sozinha não serve a quem não a distingue (WCAG 1.4.1).
   🚫 O anel NÃO usa destaque de ação: nesta
   casa significa ALGUÉM ESTÁ ESPERANDO (um lead, ADR-0034). Telefone com 4
   dígitos não é ninguém esperando — é instrução para o dono, exatamente como o
   `.falta` logo acima. Mesmo token dos dois, de propósito: a caixa e a frase que
   a explica falam com a mesma voz.
   🚫 E é `outline`, NÃO `box-shadow`: a catraca anti-slop limita o vocabulário
   de sombra a 5 formas distintas, e uma sexta entrou aqui na primeira tentativa.
   Não subi o teto — anel de ESTADO não é sombra: sombra diz elevação, e este
   traço diz "esta caixa foi recusada". `outline` é o que a casa já usa para
   marcar estado (`:focus-visible`), não desloca layout com `border:0` no campo,
   e some sozinho quando o foco entra — enquanto o cliente digita, quem manda é
   o anel de foco; a frase da recusa continua na tela do lado.
   ⚠️ Duas correções que vieram de um verificador em contexto limpo, e as duas
   eram o comentário mentindo:
   · `:not(:focus-visible)` é o que TORNA VERDADE a frase acima. Não existe
     regra de foco para `.tform input` (o `.campo input:focus` da l. 379 é do
     LOGIN), e declaração de autor vence a do navegador independentemente de
     especificidade — sem esta exclusão o anel de recusa ficava por cima do anel
     de foco e APAGAVA o foco enquanto o cliente digitava.
   · O token é `--field-line` e não `--navy-fade`: o anel encosta no INTERIOR
     BRANCO do campo, e ali `--navy-fade` mede 2,00:1 — abaixo dos 3:1 de
     1.4.11. `--field-line` dá 5,12:1 sobre branco, e é o token nomeado para
     borda de campo. O `contrast-lint` não pega este par por construção: ele só
     mede pares declarados na MESMA regra, e o fundo aqui vem do campo. */
.tform input[aria-invalid=true]:not(:focus-visible){outline:2px solid var(--field-line);outline-offset:0}

/* --- busca no catalogo do Google (trade principal e secundarios) ----------
   5.102 categorias: `select` nao serve no celular, entao e' busca com lista.
   Tudo dentro do card navy da fase atual, por isso os tokens sao os do card. */
.tform .busca{position:relative;display:flex;align-items:center}
.tform .busca i{position:absolute;left:13px;font-size:var(--i-sm);color:var(--navy-fade)}
.tform .busca input{padding-left:36px}
.tform .chips{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.tform .chips:empty{display:none}
.tform .chip{
  display:inline-flex;align-items:center;gap:var(--s-2);
  background:var(--blue-light);color:var(--blue-deep);
  /* Sem respiro VERTICAL: o "x" dentro mede 44px (alvo de toque), e somar o
     padding daria uma pílula de 56. A altura do chip É o alvo. */
  font-size:var(--t-meta);font-weight:500;padding:0 var(--s-4) 0 var(--s-5);border-radius:var(--r-pill);
}
.tform .chip button{background:none;border:0;color:var(--blue-deep);display:flex;padding:var(--s-1);cursor:pointer}
.tform .chip button i{font-size:var(--i-sm)}
.tform .sugestoes{list-style:none;padding:0;margin:var(--s-3) 0 0;display:flex;flex-direction:column;gap:var(--s-1)}
.tform .sugestoes button{
  width:100%;text-align:left;background:#fff;border:0;border-radius:var(--r-sm);
  padding:var(--s-5);display:flex;flex-direction:column;gap:var(--s-1);cursor:pointer;
}
.tform .sugestoes .n{font-size:var(--t-body);color:var(--navy);font-weight:500}
.tform .sugestoes .via{font-size:var(--t-micro);color:var(--ts)}
.tform .sugestoes .vazio{font-size:var(--t-meta);color:var(--navy-fade);padding:var(--s-4) var(--s-1)}
.tform .servicos{display:flex;flex-direction:column;gap:var(--s-5)}
.tform .servicos .grupo{display:flex;flex-direction:column;gap:var(--s-2)}
.tform .serv{display:flex;align-items:center;gap:var(--s-4);font-size:var(--t-body);color:#fff;font-weight:400}
.tform .serv input{width:17px;height:17px;flex-shrink:0;accent-color:var(--blue)}

/* A opcao sob as SETAS, no combobox do catalogo. E' CONTORNO e nao fundo de
   proposito: trocar o fundo mexeria no par cor-de-texto x fundo ja medido
   (`--navy` sobre `#fff`), e o `contrast-lint` so mede pares da MESMA regra — o
   defeito passaria calado. `-2px` para ficar DENTRO do raio da linha. */
.tform .sugestoes button.ativo{outline:2px solid var(--blue);outline-offset:-2px}

/* =========================================================================
   QUIZ — uma pergunta por vez (ADR-0152).

   Tres regras, e nenhuma tem cor: o card REUSA `.phase.now` (o `--navy-card`
   para o qual todo o markup de `.tform` foi escrito) e a barra REUSA `.obar`.
   Reusar nao e' economia: card novo com fundo proprio exigiria remedir todos os
   pares de `.tform` contra ele, e o lint so mede pares da MESMA regra.

   🚫 SEM `transition`: o card NAO PERSISTE entre perguntas, e transicao em no
   que nasce nao dispara. Quem move e' `.entrando`.
   ========================================================================= */
.quiz .obar{margin:0 var(--s-gutter) var(--s-6)}
.quiz .phase.now{margin-bottom:0}
/* "voltar" e "pular" na mesma linha: os dois sao saidas laterais e tem o mesmo
   peso de link. Empilhados, ocupariam mais altura que a propria pergunta. */
.qacoes{display:flex;align-items:center;gap:var(--s-6);flex-wrap:wrap}

/* Volta para o Google, na tarefa do desvio. E' a saida de quem disse "nao
   tenho perfil" por engano — sem ela, aquela resposta e' irreversivel pela
   interface. Peso de LINK, nao de CTA: nao disputa com a acao principal da
   tarefa, so precisa existir e ser achavel. */
.reconectar{margin-top:var(--s-4);display:flex;flex-direction:column;gap:var(--s-3)}
.reconectar .link-btn{
  background:none;border:0;color:var(--blue-pale);font-size:var(--t-meta);font-weight:500;
  padding:var(--s-3) 0;text-align:left;cursor:pointer;
}
.reconectar .falta{color:var(--navy-fade);font-size:var(--t-micro);font-weight:500}

/* Barra inferior encostada, como na malha aprovada — a pílula flutuante saiu
   junto do rótulo: pílula + texto ficava alta demais e comia a tela. */
.nav{
/* BARRA, não pílula — corrigido em 2026-08-19 contra a REFERÊNCIA da chefia.
   O merge de 18/08 (PR 2403 × main) tinha escolhido a pílula flutuante de
   17/08 alegando a conta de altura. Os 5 prints da referência mostram barra
   branca full-width em TODAS as telas, e a ADR-0124 dá a nav como decisão
   dela — a escolha do merge contrariou as duas. Volta a barra.

   A conta de altura continua fechando, e com MAIS folga que a pílula: 52 de
   alvo + 6+6 de padding = 64, contra os 84px que o `#app` reserva. A pílula
   gastava 70 dos mesmos 84 (46+8+16 de margem); sem margem lateral e sem
   margem de baixo, a barra cabe melhor.

   `z-index` fica: entrou em 17/08 e a razão dele não tinha nada a ver com o
   formato — escreve a intenção de ficar por cima antes que alguém renderize
   um elemento posicionado depois do `navPill(...)` e a perca em silêncio. */
  position:fixed;bottom:0;left:0;right:0;margin:0 auto;z-index:var(--z-nav);
  width:min(100%,420px);
  display:flex;justify-content:space-around;background:#fff;
  border-top:1px solid var(--p-hair);
  padding:6px 4px calc(6px + env(safe-area-inset-bottom));
}
/* Ícone sobre rótulo, como a referência. O alvo é 52px — acima do mínimo de
   44 da WCAG 2.2 AA, e o ICP justifica a folga: dono de empresa de serviço,
   no celular, de pé, entre um serviço e outro. */
.nav a{
  flex:1;min-width:0;min-height:52px;border-radius:var(--r-sm);
  display:flex;flex-direction:column;align-items:center;justify-content:center;
  gap:3px;text-decoration:none;
}
.nav i{font-size:var(--i-md);color:var(--ts)}
.nav a span{
  font-size:var(--p-nano);font-weight:600;color:var(--ts);line-height:1;
  /* 4 abas: 367px/4 = 91,8px por alvo (com 5 seriam 73,4). A aritmética e a
     armadilha — o botão do menu NÃO pode virar um 5º item daqui — estão em
     `chrome.js`, no bloco do array ABAS; não repito aqui porque comentário
     neste app é payload (peso.mjs:140).
     Truncar é rede, não conserto: abaixo de ~360px "performance" corta com
     reticência. Diminuir a fonte foi tentado e os lints reprovaram (10px é
     valor cru e fura o piso de 12px). Se incomodar, ENCURTAR o rótulo. */
  max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
}
/* Ativo é COR, não fundo: na referência o item aceso muda ícone e rótulo para
   a cor da marca e o fundo continua branco. Pastilha preenchida chamaria mais
   atenção que o conteúdo da tela. */
.nav a.on i,.nav a.on span{color:var(--blue)}

/* --- MENU DO TOPO (bloco 5) ----------------------------------------------
   🔴 O botão vive FORA da `.nav` — a aritmética está no bloco de `.nav a span`
   acima, e a asserção que a impede de ser desfeita em
   `tests/unit/menu-do-topo.test.ts` ("o botão fica FORA da barra"). Não existe
   `menu.proof.mjs`: não recrie a citação.

   `position:fixed` no topo, mesmo `--z-nav` da barra: os dois são cromo do app
   e nunca aparecem ao mesmo tempo no mesmo ponto da tela. */
.menu-botao{
  position:fixed;top:calc(var(--s-5) + env(safe-area-inset-top));right:var(--s-gutter);
  z-index:var(--z-nav);
  /* 44px é o piso da WCAG 2.2 AA. O alvo da barra é 52; aqui fica 44 porque o
     botão divide a faixa do topo com o título da tela, e roubar altura dele
     empurraria o conteúdo. 44 é piso, não meta — não descer daqui. */
  min-width:44px;min-height:44px;
  display:flex;align-items:center;justify-content:center;
  background:#fff;border:1px solid var(--p-hair);border-radius:var(--r-sm);
  cursor:pointer;
}
.menu-botao i{font-size:var(--i-md);color:var(--ts)}
.menu-botao.on i{color:var(--blue)}

/* O painel ancora no botão. `[hidden]` já é `display:none!important` pelo reset,
   então fechado ele não ocupa espaço NEM é lido — o atributo é a fonte da
   verdade, não esta folha. */
.menu-painel{
  position:fixed;top:calc(var(--s-5) + 44px + var(--s-3) + env(safe-area-inset-top));
  right:var(--s-gutter);z-index:var(--z-nav);
  min-width:200px;max-width:calc(100vw - (var(--s-gutter) * 2));
  background:#fff;border:1px solid var(--p-hair);border-radius:var(--r-sm);
  /* REUSA a sombra do card elevado em vez de inventar a sétima: o slop-lint
     tem teto de 6 sombras distintas, e "uma por componente" é exatamente o
     acúmulo que ele existe para impedir. */
  box-shadow:0 1px 2px var(--sombra-1),0 8px 20px var(--sombra-2);
  padding:var(--s-3);
}
.menu-painel a,.menu-painel .pendente{
  display:flex;align-items:center;gap:var(--s-4);
  min-height:44px;padding:0 var(--s-4);border-radius:var(--r-sm);
  text-decoration:none;color:var(--navy);font-size:var(--t-meta);font-weight:600;
}
.menu-painel a i,.menu-painel .pendente i{font-size:var(--i-md);color:var(--ts)}
.menu-painel a.on,.menu-painel a.on i{color:var(--blue)}

/* O item SEM CANAL AINDA — hoje só o Suporte, enquanto `APPJC_SUPORTE_WHATSAPP`
   não existir. Ele fica na lista de propósito: sumir faria quem procura suporte
   não achar nada e concluir que não há. A linha diz que existe e que está sendo
   ligado, e o desenho tem de deixar claro que ela NÃO é tocável — senão a
   pessoa toca, nada acontece, e isso é pior que a linha ausente.
   Três sinais, porque um só falha para alguém: cor esmaecida (quem enxerga),
   `cursor:default` (quem usa mouse) e `aria-disabled` no HTML (leitor de tela).
   O foco não precisa de sinal: sem `href` o elemento não entra na ordem de
   tabulação, então o teclado passa direto. */
.menu-painel .pendente{color:var(--ts);cursor:default}
.menu-painel .pendente small{margin-left:auto;font-size:var(--t-micro);font-weight:500}

/* --- INBOX (bloco 4) ------------------------------------------------------ */
/* Cabeçalho próprio: a Inbox não usa `cabecalhoTela` porque troca os pontos
   pelo selo de espera — o que importa aqui é quantos aguardam resposta. */
.shead{display:flex;align-items:center;justify-content:space-between;padding:var(--s-7) var(--s-7) var(--s-6)}
.shead h1{font-family:var(--display);font-weight:600;font-size:var(--t-hero);color:var(--navy);letter-spacing:-.3px;min-width:0}
.waiting{display:flex;align-items:center;gap:var(--s-2);font-size:var(--t-meta);font-weight:500;color:var(--accent-dark);background:var(--accent-light);padding:var(--s-3) var(--s-5);border-radius:var(--r-pill)}
.waiting b{width:7px;height:7px;border-radius:50%;background:var(--accent);display:block}
.voltar{background:none;border:0;padding:0;color:var(--navy);display:flex;align-items:center;text-decoration:none}
.voltar i{font-size:var(--i-md)}
/* O botao de LIGAR do cabecalho da conversa. Espelha a caixa do `.voltar` — ele
   e' o irmao do outro lado do `space-between`, e caixa diferente desalinharia o
   titulo do meio. Azul e' AFORDANCIA (ADR-0034): "voce pode ligar", nao demanda;
   destaque de alerta aqui leria como "ligue AGORA", que nao e' o que o botao afirma. */
.ligar{color:var(--blue-deep);display:flex;align-items:center;text-decoration:none}
.ligar i{font-size:var(--i-md)}

.tabs{display:flex;flex-wrap:wrap;gap:var(--s-3);padding:0 var(--s-gutter) var(--s-6)}
.tab{font-size:var(--t-meta);font-weight:500;color:var(--ts);background:var(--soft);padding:var(--s-3) var(--s-6);border-radius:var(--r-pill);border:0;font-family:var(--sans)}
.tab.on{background:var(--navy-card);color:#fff}

/* 🔴 LINHA UNIFORME (27/08). Todas iguais — o estado saiu do FUNDO e virou
   badge (ver `.badge` abaixo). A referência da operadora é o WhatsApp: fundo
   branco, avatar, nome, prévia, separador fino.

   A primeira versao mantinha a `.conv` no primitivo de card e desfazia o card
   AQUI, por sobrescrita — para nao mexer num seletor que serve outros quatro
   componentes. O `slop-lint` reprovou o raio zerado a mao dessa sobrescrita, e
   com razao: zero nao e degrau da escala nem circulo, e raio anulado a mao e
   exatamente o tipo de valor que a escala existe para nao ter. Entao a `.conv`
   saiu do grupo e declara o que herdava (o `#fff`, que e o mesmo do WhatsApp).
   Os outros quatro nao mudaram uma linha. */
.conv{background:#fff;margin:0;padding:var(--s-5) var(--s-gutter);
  display:flex;align-items:center;gap:var(--s-5);
  border:0;border-bottom:1px solid var(--line)}
.conv:last-child{border-bottom:0}
.av{width:42px;height:42px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-family:var(--display);font-weight:600;font-size:var(--t-body);flex-shrink:0}
/* Iniciais no avatar sao TEXTO. O b500 sobre o b100 dava 4,01:1 — reprovava por
   pouco, e passou batido na medicao a mao. O b700 resolve com 8,08:1. */
.av.b{background:var(--blue-light);color:var(--blue-deep)}
/* Avatar da conversa quente: azul de atenção (o lead esta esperando). A cor
   legada falhava com branco; o --accent-solid e' o azul oficial que carrega
   texto, 4,57:1, e ja e' o mesmo do botao de reply logo ao lado. */
.av.c{background:var(--accent-solid);color:#fff}
.av.g{background:var(--green-light);color:var(--green-dark)}
/* ⚠️ 27/08: com a lista uniforme, a linha passou a usar `.av.b` para TODAS as
   conversas — um par só, em vez de três cores dizendo estado. Medido com grep:
   `.av.c` e `.av.g` ficaram SEM nenhum consumidor no `js/`. Não removo aqui
   porque apagar CSS é decisão da operadora, não minha; fica nomeado para o
   `/techdebt` levar. Zero par de cor novo foi criado nesta mudança. */
.ct{flex:1;min-width:0}
.cn{display:flex;align-items:center;gap:var(--s-3)}
.cn h3{font-size:var(--t-body);font-weight:500;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cn i{font-size:var(--i-sm);color:var(--ts);flex-shrink:0}
.cn i.done{color:var(--green-dark)}
.cp{font-size:var(--t-micro);color:var(--ts);margin-top:var(--s-1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.cp.hot{color:var(--accent-dark);font-weight:500}
/* Ligação e "sem prévia" em itálico: sinalizam que ali não houve texto. */
.cp.call{color:var(--navy-tint);font-style:italic}
.rt{display:flex;flex-direction:column;align-items:flex-end;gap:var(--s-3);flex-shrink:0}
.tm{font-size:var(--t-micro);color:var(--ts);font-weight:500}
/* O carimbo da lista leva RELOGINHO (pedido da operadora, referência WhatsApp).
   `.conv .tm` e não `.tm`: o mesmo nome de classe carrega o horário DENTRO da
   thread, onde não há ícone — sem o escopo, cada balão ganharia um relógio. */
.conv .tm{display:inline-flex;align-items:center;gap:var(--s-2)}
.conv .tm i{font-size:var(--t-micro);opacity:.75}
/* 🔴 O BADGE — o estado que saiu do fundo da linha mora aqui.
   Verde do WhatsApp, e o par é medido: #fff sobre --green-dark. O `min-width`
   com `padding` lateral faz o badge de 1 dígito ser um círculo e o de 2+ virar
   pílula sozinho, sem segunda regra. */
.badge{min-width:20px;height:20px;padding:0 6px;border-radius:var(--r-pill);
  background:var(--green-dark);color:#fff;
  font-size:var(--t-micro);font-weight:600;line-height:20px;text-align:center}
/* Pendente SEM contagem: ponto, não número. O servidor não contou, e escrever
   "1" ali seria inventar o fato (D62). Mesmo verde, mesma altura de linha. */
.badge.ponto{min-width:10px;width:10px;height:10px;padding:0;align-self:center;margin-right:5px}
.reply{background:var(--accent-solid);color:#fff;font-size:var(--t-meta);font-weight:500;padding:var(--s-4) var(--s-6);border-radius:var(--r-pill);border:0;font-family:var(--sans)}

/* Estado vazio de tela inteira. NOME PRÓPRIO de propósito: `.empty` já é a
   classe de COR da métrica ausente na Home (D62) — dar layout a ela quebraria
   os números da Home. */
.semnada{margin:var(--s-6) var(--s-gutter) 0;text-align:center;padding:var(--s-7)}
.semnada i{font-size:var(--i-lg);color:var(--empty)}
.semnada p{font-size:var(--t-micro);color:var(--ts);margin-top:var(--s-3)}
/* O "Try again" do estado SEM SINAL. Declara a CAIXA inteira, nao so a pintura:
   `.cta` generico e' `display:block;width:100%` (bloco 3), e a especificidade
   maior daqui so ganha no que ESTA regra declara — foi assim que o botao do
   pulso transbordou o card na D81. Virou regra da casa. */
.semnada .cta{display:inline-block;width:auto;margin-top:var(--s-5);padding:var(--s-4) var(--s-7);font-size:var(--t-meta)}

/* --- thread --------------------------------------------------------------- */
/* 🔴 A THREAD GANHOU CARA DE CONVERSA (27/08). A paleta dela mora no `:root`
   (`--bolha-in`, `--bolha-out`, `--thread-bg`, `--sombra-bolha`,
   `--linha-composer`, `--h-composer`), em tokens NOVOS — o pedido era "sem
   tocar no --blue global" e "sem mexer no --soft global", e nenhum token
   compartilhado mudou de valor. O que era `--blue` na bolha de saida virou
   verde de bolha, e so na bolha. */
.thread{background:var(--thread-bg);
  /* 🔴 `flex:1` — o bege precisa chegar ATE o composer, nao so ate a ultima
     mensagem. Sem isto sobra uma faixa BRANCA entre o fim da conversa e a
     caixa de escrever (visto no screenshot, nao no codigo): a tela fica com
     tres fundos diferentes empilhados. Funciona porque a raiz da tela E' o
     `#app`, que ja e' `display:flex;flex-direction:column` — nao precisou de
     container novo nem de altura calculada a mao, que quebraria quando o
     cabecalho mudasse de tamanho. */
  flex:1;
  /* 🔴 E QUEM ROLA E' ELA (#2720). `min-height:0` desarma o tamanho minimo
     automatico do item flex — sem ele `overflow` nao corta nada, a caixa so
     cresce. Os dois andam juntos, e o proof tem um mutante para cada um.
     `overflow-y` e nao `overflow`: rolagem horizontal aqui seria barra
     fantasma no Android quando um balao encosta na borda. */
  min-height:0;overflow-y:auto;overscroll-behavior:contain;
  padding:var(--s-5) var(--s-gutter) var(--s-4);
  display:flex;flex-direction:column;gap:var(--s-4);
  /* A folga do composer FIXO. Sem ela o último balão nasce debaixo da caixa de
     escrever — e `ancorarNoFim` levaria a rolagem ao fim de um conteúdo cujo
     fim está escondido, que é pior que não rolar: parece que chegou. */
  padding-bottom:calc(var(--h-composer) + var(--s-5))}
.msg{position:relative;display:flex;flex-direction:column;align-items:flex-start;gap:var(--s-1)}
.msg.saiu{align-items:flex-end}
/* `position:relative` porque o RABINHO é um `::before` ancorado na bolha, e
   `overflow` fica visível pelo mesmo motivo. */
.bolha{position:relative;max-width:78%;padding:var(--s-4) var(--s-5);border-radius:var(--r-md);
  font-size:var(--t-meta);line-height:1.45;background:var(--bolha-in);color:var(--navy);
  word-break:break-word;box-shadow:0 1px .5px var(--sombra-bolha)}
.msg.saiu .bolha{background:var(--bolha-out);color:var(--navy)}
/* Mensagem sem conteúdo é nomeada, não sumida — e a cor diz que é ausência. */
.bolha.vazia{color:var(--ts);font-style:italic}
.msg.saiu .bolha.vazia{color:var(--navy)}
/* --- gramatica de conversa: bloco, cauda, tique, avatar ------------------- */
/* 🔴 O RABINHO agora e' DESENHADO (27/08), e mudou de ponta.
   Ate aqui a "cauda" era o canto de baixo do ULTIMO balao descendo de 20 para
   14 — rabinho fingido. O print da operadora mostra o rabinho de verdade, no
   PRIMEIRO balao do bloco, dos dois lados. `!seq` e' exatamente "primeiro do
   bloco" (ver `marcarSequencia`): nao precisou de flag nova.
   O triangulo herda a cor da bolha pelo token, entao ele nunca desencontra do
   fundo quando a cor mudar. */
.msg:not(.seq) .bolha::before,.msg:not(.seq) .audio::before{content:"";position:absolute;top:0;width:0;height:0}
.msg:not(.saiu):not(.seq) .bolha,.msg:not(.saiu):not(.seq) .audio{border-top-left-radius:var(--s-1)}
.msg:not(.saiu):not(.seq) .bolha::before,.msg:not(.saiu):not(.seq) .audio::before{left:-7px;
  border-top:8px solid var(--bolha-in);border-left:7px solid transparent}
.msg.saiu:not(.seq) .bolha,.msg.saiu:not(.seq) .audio{border-top-right-radius:var(--s-1)}
.msg.saiu:not(.seq) .bolha::before,.msg.saiu:not(.seq) .audio::before{right:-7px;
  border-top:8px solid var(--bolha-out);border-right:7px solid transparent}
/* Continuacao do mesmo lado: o respiro cai de 9px (o gap da `.thread`) para
   4px. Margem NEGATIVA porque `gap` e' do container e nao se anula por filho;
   os dois numeros sao degraus da escala, nao px avulso. */
.msg.seq{margin-top:calc(var(--s-2) - var(--s-4))}
/* O AVATAR do contato. Absoluto de proposito: um wrapper por balao custaria um
   no de DOM em cada mensagem (500 no teto) so para posicionar um circulo que
   aparece uma vez por bloco. A calha e' o `padding-left` do lado de la, e ela
   fica reservada MESMO sem avatar (nome desconhecido): sem isso a thread
   saltaria de indentacao no instante em que o nome chegasse. Reusa
   `.av.b` — mesmo par ja medido da lista (8,08), zero cor nova. */
.msg:not(.saiu){padding-left:calc(28px + var(--s-3))}
.av.at{position:absolute;left:0;bottom:0;width:28px;height:28px;font-size:var(--t-micro)}
/* 🔴 O CARIMBO MORA DENTRO DA BOLHA (27/08), canto inferior direito.
   `float:right` e nao flex: e' o que faz o texto ESCOAR em volta dele quando a
   mensagem e' curta, em vez de empurrar uma linha inteira so para o horario —
   o comportamento do WhatsApp. O `margin-left` impede o carimbo de encostar na
   ultima palavra; o `margin-top` alinha a base com a linha de texto.

   ⚠️ A COR MUDOU E TEM NUMERO. Ela era `--ts`, medida contra `--soft` quando o
   carimbo vivia FORA da bolha. Dentro do verde da bolha (`--bolha-out`) o `--ts` cai para
   **4,31** e REPROVA o piso de 4,5 — o `--navy-tint` da 6,19 no verde e 7,11 no
   branco. Trocar o fundo sem remedir a cor teria passado despercebido: o
   contraste some sem erro nenhum. */
.msg .tm{float:right;display:inline-flex;align-items:center;gap:var(--s-2);
  margin:var(--s-2) 0 0 var(--s-4);color:var(--navy-tint);background:transparent}
/* Cor e fundo na MESMA regra, e o fundo declarado e' o do `body` (--soft) de
   verdade: e' o que deixa o par mensuravel pelo contrast-lint, mesma tecnica do
   `.diasep`. Sem alerta extra — entrega e' descricao, nao demanda.
   A ORDEM importa: o otimista carrega `saiu`+`enviando` e o incerto
   `saiu`+`incerto`, todos (0,3,0) — quem vem depois no arquivo ganha, por isso
   `saiu` primeiro e `incerto` por ultimo (bloco do compositor, abaixo). */
/* Dentro da bolha o fundo e' a propria bolha — os dois pares medidos acima.
   O `background:--soft` que estas regras carregavam era do tempo em que o
   carimbo ficava sobre o fundo da tela; mante-lo desenharia um retangulo
   cinza-azulado dentro do verde. */
.msg.saiu .tm{color:var(--navy-tint);background:transparent}
.msg.enviando .tm{color:var(--navy-tint);background:transparent}
/* Separador de dia. A PILL e' o que separa: `.thread` tem gap uniforme, entao
   texto solto no meio leria como mais uma linha de evento. `--ts` sobre
   `--soft` = 4,80 (piso 4,5). Cor e fundo na MESMA regra de proposito: e' o que
   deixa o par mensuravel pelo contrast-lint. Sem alerta extra — separador e'
   descricao, nao demanda (ADR-0034). */
/* A pilula do separador passou a viver sobre o BEGE. O fundo dela vira o branco
   da bolha (`--bolha-in`) por dois motivos: pertence a paleta da conversa, e o
   par medido continua passando — `--ts` sobre #fff = 4,94 (piso 4,5).
   ⚠️ `--ts` DIRETO sobre o bege daria 3,96 e reprovaria; e' a pilula que
   segura o contraste, nao a cor do texto. */
.diasep{align-self:center;font-size:var(--t-micro);font-weight:500;color:var(--ts);
  background:var(--bolha-in);border-radius:var(--r-pill);padding:var(--s-2) var(--s-5);
  box-shadow:0 1px .5px var(--sombra-bolha)}
/* Ligação é evento no meio da conversa, não balão. */
.evento{display:flex;align-items:center;justify-content:center;gap:var(--s-3);font-size:var(--t-micro);color:var(--navy-tint);font-style:italic;padding:var(--s-2) 0}
.evento i{font-size:var(--i-sm)}
/* O play da gravacao. Azul e' AFORDANCIA (ADR-0034): "voce pode ouvir", nao
   "alguem esta esperando". Alvo de 28px porque ele mora dentro de uma linha de
   12px — sem tamanho proprio, o dedo erraria. */
.tocar{flex:none;width:28px;height:28px;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:var(--r-pill);background:var(--blue-light);color:var(--blue-deep);cursor:pointer}
.tocar i{font-size:var(--i-sm)}
/* Baixando: o WAV inteiro vem antes do primeiro som, e numa ligacao longa isso
   demora. O botao precisa PARECER ocupado — sem isto o toque nao devolve nada
   por segundos e a pessoa toca de novo. */
.tocar.baixando{opacity:.55;cursor:progress}
.tocar.tocando{background:var(--blue);color:#fff}
/* Falhou (nao ha gravacao, ou nao consegui buscar): o botao SAI e vira frase.
   Cor de ausencia, e sem italico — a linha ja e' italica, e frase que some no
   fundo e' frase que nao foi lida. */
.evento .sem-gravacao,.ligacao .sem-gravacao{color:var(--empty);font-style:normal}

/* --- O PLAYER DA GRAVACAO (estilo WhatsApp) -------------------------------
   A ligacao COM gravacao deixa de ser uma linha de texto e vira cartao: play +
   onda em cima, legenda e horario embaixo. A ligacao SEM gravacao continua
   sendo a linha italica de antes — cartao vazio prometeria som que nao existe.
   O cartao usa o par JA MEDIDO da pilula de dia (`--ts` sobre `--bolha-in` =
   4,94, piso 4,5); cor e fundo na mesma regra para o contrast-lint enxergar. */
/* 🔴 Altura MEDIDA, nao chutada. O cartao custa espaco vertical.
   ⚠️ #2720 derrubou a razao ORIGINAL desta nota ("cada pixel empurra o
   cabecalho para fora da tela mais cedo"): a thread rola por dentro agora e o
   cabecalho nao sai. A medicao vale; o que ela protege virou quantas ligacoes
   cabem de uma vez. Na
   primeira versao o cartao custava +34px por ligacao contra a linha antiga;
   apertado aqui, custa ~+18px. Padding vertical zero de proposito: a `.thread`
   ja separa os filhos com `gap:var(--s-4)`, e somar padding a um gap que ja
   existe e' o jeito classico de dobrar espaco sem perceber. */
/* 🔴 28/08 (#2724) — A LIGACAO E' BOLHA, E O LADO VEM DA DIRECAO.
   Ela deixou de ser `.evento` (linha centrada, italica, de AVISO DE SISTEMA) e
   passou a morar dentro de `.msg`/`.msg.saiu`, que ja existiam para o balao de
   texto. Alinhamento, calha do avatar e rabinho vem DE LA — nenhum deles foi
   reescrito aqui. O que sobra nesta regra e' so o miolo do cartao.
   `position:relative` e' o que ancora o rabinho, igual `.bolha`. */
.ligacao .audio{position:relative;color:var(--ts);background:var(--bolha-in);
  border-radius:var(--r-md);padding:var(--s-2) var(--s-4) var(--s-3);
  box-shadow:0 1px .5px var(--sombra-bolha);min-width:min(280px,78vw);max-width:min(340px,86vw)}
/* Ligacao FEITA pelo dono: o mesmo verde e o mesmo `--navy` do balao enviado —
   par ja medido (11,67), zero cor nova. Cor e fundo na MESMA regra porque e'
   assim que o `contrast-lint` enxerga um par; separados, ele nao mede. */
.msg.saiu .audio{background:var(--bolha-out);color:var(--navy)}
/* Sem gravacao NAO e' cartao de player: e' uma linha, e ela encolhe ate o
   conteudo. Herdar os 280px de largura minima daria um retangulo quase vazio
   com seis palavras dentro — e a bolha ficaria com cara de erro de layout. */
.ligacao .audio-mudo{display:flex;align-items:center;gap:var(--s-3);
  min-width:0;font-size:var(--t-micro)}
.ligacao .audio i{font-size:var(--i-sm)}
.audio-topo{display:flex;align-items:center;gap:var(--s-3)}
.audio-play{display:flex;align-items:center;gap:var(--s-3);flex:1;min-width:0}
/* A onda ocupa o resto da linha. `align-items:center` faz as barras crescerem
   para os DOIS lados a partir do meio, como no WhatsApp — ancoradas embaixo
   elas viravam um grafico de barras. */
.onda{flex:1;min-width:0;display:flex;align-items:center;gap:2px;height:20px;overflow:hidden}
/* `flex:1` em cada barra faz a onda caber em QUALQUER largura sem media query:
   32 barras dividem o espaco disponivel. Largura fixa estouraria no 320px. */
/* `--track` e' o token de "ainda nao aconteceu" — a mesma semantica da barra
   que ainda nao tocou, e da mesma familia do `--blue` que pinta a tocada. */
.onda span{flex:1;min-width:0;border-radius:var(--r-pill);background:var(--track)}
.onda span.tocada{background:var(--blue)}
/* A onda usa apenas degraus da paleta oficial. `--track-out` sobre a bolha sky
   dá 1,43:1 e a barra tocada em `--blue` sobe para 2,31:1, preservando a
   progressão visual. A onda é decorativa (`aria-hidden`); o sentido permanece
   no botão e na legenda. */
.msg.saiu .onda span{background:var(--track-out)}
/* A legenda mantem "incoming call · 13s" e o horario vai para a ponta — e' a
   leitura do print de referencia: o que a linha E' fica a esquerda, QUANDO foi
   fica a direita. */
.audio-meta{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  margin-top:var(--s-2);font-size:var(--t-micro)}
.audio-meta i{font-size:var(--i-sm);vertical-align:-1px}
.audio-hora{flex:none;color:var(--navy-tint)}
/* Sem gravacao o grupo inteiro (play + onda) vira frase, e ela ocupa a linha
   que a onda ocupava — por isso o `flex:1`, senao a frase encolhia no canto. */
.audio-play + .sem-gravacao,.audio-topo .sem-gravacao{flex:1}

/* --- A FICHA DO CONTATO (#2725) ------------------------------------------ */
/* O titulo da conversa vira BOTAO sem virar botao: `font:inherit` e
   `color:inherit` fazem ele herdar do `<h1>` que o envolve, entao o cabecalho
   nao muda de aparencia — muda de comportamento. Zero chrome de botao.
   O alvo de 44px NAO custa altura nenhuma: `.voltar` e `.ligar`, os dois
   irmaos dele no `.shead`, ja estao na mesma regra, e a linha ja media 44.
   `text-align:left` porque `<button>` centraliza por padrao e o titulo nao. */
.titulo-conversa{padding:0;border:0;background:none;font:inherit;color:inherit;
  letter-spacing:inherit;text-align:left;cursor:pointer;max-width:100%;
  overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
/* ⚠️ `display:block` acima VENCE o `inline-flex` da regra de 44px de proposito:
   flex quebraria o `text-overflow:ellipsis` de um nome longo. O alvo continua
   valendo — `min-height` nao depende do display. */
/* 🔴 `margin:auto` EXPLICITO, e nao e' redundante. O `<dialog>` modal centra
   sozinho pelo `margin:auto` da folha do browser — mas o reset la em cima
   (`*{box-sizing:border-box;margin:0}`) zera esse auto em TODO elemento, e a
   caixa nasce encostada no canto superior esquerdo. Sem erro e sem aviso: ela
   abre, mostra tudo certo, e fica no lugar errado. Foi o screenshot que pegou;
   nenhuma assercao teria pegado — a de posicao existe agora, por causa disto. */
dialog.ficha{margin:auto;border:0;padding:0;max-width:min(340px,88vw);width:100%;
  border-radius:var(--r-lg);background:#fff;color:var(--navy);overflow:hidden}
/* Mesmo veu da trava, e pelo MESMO motivo tecnico — nao e' copia por gosto:
   `::backdrop` so passou a HERDAR do elemento de origem em 2024 (Chrome 122 ·
   Safari 17.4 · Firefox 125). Antes disso ele nao tem pai, `var(--…)` nao
   resolve e o veu sai TRANSPARENTE, falha muda e justo no aparelho velho.
   `currentColor` serve os dois mundos. Ver a nota longa em `dialog.trava`. */
dialog.ficha::backdrop{background:currentColor;opacity:.55}
@supports (backdrop-filter:blur(1px)){
  dialog.ficha::backdrop{backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:.34}
}
.ficha-head{display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-3);padding:var(--s-5) var(--s-5) var(--s-4);
  background:#fff;color:var(--ts);border-bottom:1px solid var(--line)}
.ficha-head h2{font-family:var(--sans);font-weight:500;font-size:var(--t-micro);
  text-transform:uppercase;letter-spacing:.6px}
/* 44px de alvo, e `--ts` sobre branco = 5,12:1 — sai do caminho do olho sem
   sair do alcance do dedo. Mesma escolha do `.trava-fechar`. */
.ficha-fechar{flex:none;width:44px;min-height:44px;display:inline-flex;
  align-items:center;justify-content:center;
  margin:calc(var(--s-3) * -1) calc(var(--s-3) * -1) calc(var(--s-3) * -1) 0;
  border:0;border-radius:var(--r-pill);background:#fff;color:var(--ts)}
.ficha-fechar i{font-size:var(--i-md)}
.ficha-corpo{padding:var(--s-5)}
.ficha-nome{font-family:var(--display);font-weight:600;font-size:var(--t-title);
  color:var(--navy);word-break:break-word}
.ficha-campo{margin-top:var(--s-4);display:flex;flex-direction:column;gap:2px}
.ficha-rotulo{font-size:var(--t-micro);color:var(--ts)}
.ficha-valor{font-size:var(--t-meta);color:var(--navy);word-break:break-word}
/* A ACAO. `--blue-deep` sobre branco = 9,30:1 — e azul e' "voce pode fazer"
   (ADR-0034), que e' exatamente o que este botao oferece. Coral aqui seria
   dizer que alguem esta esperando, e ninguem esta: quem abre a ficha e' o dono.
   Cor e fundo na MESMA regra, senao o `contrast-lint` nao ve o par. */
.ficha-ligar{margin-top:var(--s-5);min-height:44px;display:flex;
  align-items:center;justify-content:center;gap:var(--s-3);
  border-radius:var(--r-pill);background:var(--blue-deep);color:#fff;
  font-size:var(--t-meta);font-weight:500;text-decoration:none}
.ficha-ligar i{font-size:var(--i-sm)}
/* Sem numero, o botao NAO sai e a frase toma o lugar — botao que nao pode
   funcionar e' pior que botao nenhum (mesma regua do play da gravacao). */
.ficha-sem{margin-top:var(--s-5);font-size:var(--t-micro);color:var(--ts)}

/* --- COMPOSITOR (a resposta pelo app) ------------------------------------- */
/* Substituiu o aviso de somente-leitura (`.somenteleitura`), removido junto com
   a linha que ele pintava — CSS de tela que nao existe mais so serve pra fazer
   a proxima pessoa procurar o markup. */
/* Substitui o aviso de so-leitura. Tudo que sai daqui vira SMS real no
   telefone do cliente do dono, entao o estado de envio precisa ser visivel:
   o balao otimista nasce esmaecido e so firma quando o servidor confirma. */
/* 🔴 O COMPOSER E' FIXO (27/08) — o pedido central da operadora: "a caixa de
   responder fica grudada na ultima mensagem". Ele rolava junto com a pagina.

   As tres travas que este item exigiu, cada uma por um modo de falha proprio:

   1. ELE NAO E' O UNICO FIXO. A `.nav` ja e' `position:fixed` no rodape e o
      `#app` reserva `84px` para ela. O composer se ancora ACIMA dela — dai o
      `bottom` somar a altura da nav e o `env(safe-area-inset-bottom)`. Sem
      isso, os dois disputariam o mesmo rodape e a nav ficaria por cima da
      caixa de escrever.
   2. QUEM ROLA E' A `.thread`, nao a pagina (mudou em #2720; ver `rolante()`).
      A folga do composer segue como `padding-bottom` DA THREAD, nao `margin`
      daqui — e agora por um motivo mais direto: rolando por dentro, esse
      padding faz parte do conteudo rolavel, entao o ultimo balao termina acima
      da caixa TAMBEM no fim da rolagem. Sem ele `ancorarNoFim` chega a um fim
      escondido — e a tela parece ter chegado quando nao chegou.
   3. A LARGURA SEGUE O `#app` (`max-width:420px`), nao o viewport: no desktop
      um composer full-width sairia de baixo do app e flutuaria no vazio. */
.compositor{position:fixed;z-index:var(--z-nav);
  bottom:calc(64px + env(safe-area-inset-bottom));
  left:0;right:0;margin:0 auto;width:min(100%,420px);
  display:flex;align-items:flex-end;gap:var(--s-4);
  padding:var(--s-4) var(--s-gutter);
  background:var(--thread-bg);border-top:1px solid var(--linha-composer)}
.compositor textarea{flex:1;resize:none;min-height:42px;max-height:120px;padding:var(--s-5);
  border:1px solid var(--field-line);border-radius:var(--r-sm);background:#fff;color:var(--navy);
  font-family:var(--sans);font-size:var(--t-meta);line-height:1.45}
/* Azul oficial: escrever e responder sao afordancia, ninguem espera por elas. */
.compositor textarea:focus{outline:2px solid var(--blue);outline-offset:-1px}
.compositor textarea:disabled{opacity:.6}
.compositor button{flex:none;width:42px;height:42px;display:flex;align-items:center;justify-content:center;
  border:0;border-radius:var(--r-sm);background:var(--blue);color:#fff;cursor:pointer}
.compositor button:disabled{opacity:.5;cursor:default}
.compositor button i{font-size:var(--i-sm)}
/* O erro fala do que NAO aconteceu — por isso ocupa a linha inteira, abaixo
   da caixa, e nao um toast que some antes de ser lido. */
.compositor-erro{margin:-6px var(--s-gutter) var(--s-5);padding:var(--s-4) var(--s-5);border-radius:var(--r-sm);
  background:var(--soft);color:var(--navy);font-size:var(--t-micro)}
/* Regiões vivas de erro ficam SEMPRE no DOM (senão o leitor de tela não anuncia
   a troca de texto). Vazia = invisível; o texto é o único estado. */
.compositor-erro:empty,.falta:empty,.r-unavailable[data-gbp-erro]:empty{display:none}
/* Balao ainda nao confirmado pelo servidor. O estado vive no carimbo/icone;
   reduzir a opacidade do grupo inteiro também lavava o texto e derrubava o
   contraste para 2,39:1 sobre o fundo da thread. */
.msg.enviando .bolha{box-shadow:inset 0 0 0 1px var(--field-line),0 1px .5px var(--sombra-bolha)}
/* O TERCEIRO estado: o envio falhou de um jeito que NAO prova que nada saiu
   (timeout, 5xx, conexao que caiu depois do POST). Nao pode parecer enviado
   nem sumir — as duas leituras erradas custam caro em direcoes opostas:
   sumir faz o dono reenviar (SMS duplicado), firmar deixa um cliente sem
   resposta. O tracejado e' "isto esta' em aberto", nao "isto falhou". */
.msg.incerto .bolha{outline:1px dashed var(--field-line);outline-offset:-2px}
.msg.incerto .tm{color:var(--empty);background:var(--soft)}


/* --- RESULTS (bloco 5) ---------------------------------------------------- */
/* `.metrics` e `.m` são REUSADOS da Home: mesma forma de card, mesma classe
   `.empty` para a ausência (D62). Não se duplica aqui o que já existe lá.

   🐛 E era reuso PELA METADE: `.metrics{display:block}` e a grade de 3 colunas
   vive em `.mrow`, que a Home emite e a Results NÃO emitia — esta tela
   empilhava tiles de largura cheia enquanto o comentário dizia que as duas
   compartilhavam o primitivo. Pararam de compartilhar quando o `.mrow` foi
   extraído para caber o rodapé da Home. O `row-gap` é a única diferença: aqui
   são duas fileiras, lá é uma. */
.metrics .mrow{row-gap:var(--s-5)}

/* ── O COMPARATIVO ──────────────────────────────────────────────────────────
   A cor vem de `bom`, NUNCA de `direcao` (regra 4): o mockup que originou esta
   tela pintava "COST PER LEAD ↗ +80.7%" de VERDE. E a direção lê SEM COR, pelo
   triângulo do `::before` — ~8% dos homens têm daltonismo vermelho-verde.

   🚫 SEM DESTAQUE DE ALERTA, nem para o delta ruim: gastá-lo num número faria
   o card do lead parado deixar de
   significar. Esta é tela de LEITURA, que confirma e nunca cobra (chrome 3);
   por isso "ruim" fica na cor normal do texto e quem diz a direção é a seta. */
.delta{
  display:block;margin-top:var(--s-1);font-style:normal;
  font-size:var(--t-micro);font-weight:500;color:var(--navy);
}
.delta.bom{color:var(--green-dark)}
.delta.sobe::before{content:"\25B2";margin-right:var(--s-1)}
.delta.desce::before{content:"\25BC";margin-right:var(--s-1)}

/* ── O GRÁFICO ──────────────────────────────────────────────────────────────
   As barras são conteúdo NÃO-TEXTUAL (WCAG 1.4.11, piso 3:1) e o `--blue` dá
   4,62:1 contra o branco. 🚫 NUNCA `--track` para dado real: 1,40:1, ilegível
   DE PROPÓSITO, porque ali ele significa "ainda não aconteceu".

   `width:100%` e altura fixa, nunca largura intrínseca — o `home-ui.proof`
   afirma `docScrollW <= docClientW`, e um SVG que traz a própria largura é a
   forma mais fácil de furar isso. */
.graf{margin:0 16px 14px}
.graf .barras{display:block;width:100%;height:48px}
.graf rect{fill:var(--blue)}
/* A tabela é o fallback acessível — `<details>` nativo dá teclado de graça. */
.graf summary,.graf-linha summary{
  min-height:44px;display:flex;align-items:center;
  font-size:var(--t-meta);font-weight:500;color:var(--blue-deep);cursor:pointer;
}
.graf table,.graf-linha table{width:100%;border-collapse:collapse;font-size:var(--t-micro)}
.graf th,.graf td,.graf-linha th,.graf-linha td{text-align:left;padding:var(--s-3) 0;border-bottom:1px solid var(--line)}
.graf thead th,.graf-linha thead th{color:var(--ts);font-weight:500}
.graf tbody th,.graf-linha tbody th{font-weight:500;color:var(--navy)}
.graf td,.graf-linha td{text-align:right;font-weight:500;color:var(--navy);font-variant-numeric:tabular-nums}

/* GBP — período do cliente e gráfico mensal (ADR-0223). Selects NATIVOS de
   propósito: funcionam em todo aparelho hoje, com teclado e leitor de tela. */
.r-periodo{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--s-4);margin-bottom:var(--s-5)}
/* #3397 — a 120px os dois rótulos dividiam a linha COM o botão e sobravam
   ~130px por select: "September 2026" saía "September 20". A 170px o botão
   desce para a própria linha e o mês por extenso cabe. */
.r-periodo label{display:flex;flex-direction:column;gap:var(--s-2);flex:1 1 170px;min-width:0;font-size:var(--t-micro);font-weight:500;color:var(--ts)}
.r-periodo select{font-family:var(--sans);font-size:var(--t-meta);color:var(--navy);background:var(--soft);border:0;border-radius:var(--r-sm);padding:var(--s-3) var(--s-4);min-height:44px;width:100%}
.r-periodo button{min-height:44px;font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:#fff;background:var(--navy-card);border:0;border-radius:var(--r-pill);padding:var(--s-3) var(--s-6)}
.r-periodo+.r-unavailable{margin-top:0;margin-bottom:var(--s-4)}
/* A linha do painel do Google: mesma caixa 100×48 do gráfico de leads, o dobro
   da altura na tela. Nasce vazia na primeira pintura e some se ficar vazia. */
.graf-linha{margin:var(--s-5) 0 var(--s-4)}
.graf-linha:empty{display:none}
.graf-linha .tabs{padding:0 0 var(--s-4);flex-wrap:wrap}
.graf-linha svg{display:block;width:100%;height:96px}
.graf-linha .area{fill:var(--blue-light)}
.graf-linha .linha{fill:none;stroke:var(--blue);stroke-width:2px}

.period{display:flex;align-items:center;gap:5px;font-size:var(--t-meta);font-weight:500;color:var(--navy);background:var(--soft);padding:7px 13px;border-radius:var(--r-pill)}
.sect{padding:0 16px;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.sect h2{font-family:var(--display);font-weight:600;font-size:var(--t-body);color:var(--navy)}
.src{font-size:var(--t-micro);font-weight:500;color:var(--ts);background:var(--soft);padding:4px 9px;border-radius:var(--r-pill)}

/* A linha de resposta. O verde é do estado `todos` — zero MEDIDO é conquista.
   Os estados `sem_leads`/`sem_dado` usam o cinza neutro: não houve o que
   responder, e pintar isso de verde seria parabenizar o cliente por nada. */
.row{margin:0 16px 14px;background:var(--green-light);border-radius:var(--r-md);padding:12px 15px;display:flex;align-items:center;gap:11px}
.row .ic{width:36px;height:36px;border-radius:50%;background:var(--green);display:flex;align-items:center;justify-content:center;flex-shrink:0}
.row .ic i{font-size:var(--i-md);color:#fff}
.row h3{font-size:var(--t-body);font-weight:500;color:var(--navy)}
.row.neutro{background:var(--soft)}
.row.neutro .ic{background:var(--empty)}
/* Sem resposta pendente NÃO é alarme: tela de leitura não pede ação (chrome 3).
   Azul informa; um alarme cobraria — e cobrança é papel da Home. */
.row:not(.ok):not(.neutro){background:var(--blue-light)}
.row:not(.ok):not(.neutro) .ic{background:var(--blue)}

/* O painel do Google. Borda tracejada porque a moldura também comunica
   ausência — é um lugar reservado, não um resultado. */
.nodata{margin:0 16px 9px;border:1px dashed var(--empty);border-radius:var(--r-md);padding:14px 15px}
.ngrid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:11px}
.n{display:flex;flex-direction:column;gap:2px}
/* Cor por ESTADO, não fixa: o design assume tudo vazio, mas no dia em que o
   snapshot gravar as métricas o número precisa sair legível, não cinza-fantasma. */
.n b{font-family:var(--display);font-weight:600;font-size:var(--t-hero);color:var(--navy);letter-spacing:-.5px;line-height:1.1}
.n b.empty{color:var(--empty)}
.n span{font-size:var(--t-micro);font-weight:500;color:var(--ts)}

.connect{margin:0 16px 14px;background:var(--blue-light);border-radius:var(--r-md);padding:13px 15px;display:flex;align-items:center;gap:11px}
.connect .ic{width:38px;height:38px;border-radius:50%;background:#fff;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.connect .ic i{font-size:var(--i-md);color:var(--blue)}
.connect h3{font-size:var(--t-meta);font-weight:500;color:var(--navy);line-height:1.35}
.connect p{font-size:var(--t-micro);color:var(--blue);font-weight:500;margin-top:2px}

/* A legenda que ensina a ler a tela: o traço é a única convenção que o cliente
   precisa aprender, e ela fica escrita em vez de subentendida. */
/* A folga da nav pill mora no `#app` (`padding-bottom:84px + safe-area`), não
   aqui. Este `margin-bottom` era 88px e só fazia sentido enquanto `.note` era o
   último elemento da tela — verdade na Results, FALSO no Profile (onde ela
   aparece no meio) e na Journey (onde ela abre a tela). Medido: ~106px de vão
   no meio do perfil, e 88px de branco entre "conectado com sucesso" e a barra
   de progresso. Folga de chrome dentro de componente de conteúdo vaza para
   todo lugar onde o componente for reusado. */
.note{margin:0 16px 14px;display:flex;align-items:flex-start;gap:7px;padding:11px 14px;background:var(--soft);border-radius:var(--r-sm);font-size:var(--t-micro);line-height:1.4;color:var(--ts)}
.note i{font-size:var(--i-sm);flex-shrink:0;margin-top:1px}
.note.aviso{margin-bottom:14px;background:var(--accent-light);color:var(--accent-dark)}

/* --- Results aprovado: uma PWA, composições adaptativas ------------------- */
#app.results-page{background:var(--soft)}
.results-page .shead{padding-left:var(--s-gutter);padding-right:var(--s-gutter)}
.r-layout{display:grid;grid-template-columns:1fr;gap:var(--s-5);padding:0 var(--s-gutter) var(--s-7)}
.r-card{min-width:0;padding:var(--s-6);background:#fff;border:1px solid var(--p-hair);border-radius:var(--r-p);box-shadow:var(--p-sh)}
.r-section-head{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--s-5);margin-bottom:var(--s-5)}
.r-section-head h2{font-family:var(--sans);font-size:var(--p-card);font-weight:700;line-height:1.25}
.r-section-head span{font-size:var(--p-rot);font-weight:600;color:var(--ts);text-align:right}
.r-health-body{display:flex;align-items:center;gap:var(--s-6)}
.r-health-body strong{font-size:var(--t-body);color:var(--navy)}
.r-health-body p,.r-state p,.r-through,.r-caveat,.r-latest,.r-unavailable{margin-top:var(--s-2);font-size:var(--t-meta);line-height:1.45;color:var(--ts)}
.r-score{width:64px;height:64px;display:grid;place-items:center;flex:0 0 auto;border-radius:var(--r-pill);background:conic-gradient(var(--track) 0 70%,var(--soft) 70%)}
.r-score i{width:46px;height:46px;border-radius:var(--r-pill);background:#fff}
.r-state{display:flex;align-items:center;gap:var(--s-5);padding:var(--s-5);margin-bottom:var(--s-5);border-radius:var(--r-sm);background:var(--blue-light);color:var(--blue-deep)}
.r-state>i{font-size:var(--i-md)}
.r-state strong{font-size:var(--t-meta)}
.r-overview,.r-actions,.r-mini-grid{display:grid;gap:var(--s-4)}
.r-overview{grid-template-columns:repeat(2,minmax(0,1fr));margin-bottom:var(--s-4)}
.r-actions{grid-template-columns:repeat(2,minmax(0,1fr))}
.r-actions .website{grid-column:1/-1}
.r-value{min-width:0;padding:var(--s-5);border:1px solid var(--line);border-radius:var(--r-sm);background:var(--soft)}
.r-value b{display:block;font-family:var(--sans);font-size:var(--t-hero);font-weight:700;line-height:1.1;color:var(--navy);font-variant-numeric:tabular-nums}
.r-value b.empty{color:var(--empty)}
.r-value span{display:block;margin-top:var(--s-2);font-size:var(--t-micro);font-weight:500;color:var(--ts)}
.r-through{margin-bottom:0}
/* ADR-0233 — faixa, nao legenda: os numeros abaixo sao do ultimo snapshot, e
   `.r-through` cinza some ao lado de um numeral grande — justo onde a ressalva
   E' a informacao. Par de cor ja aprovado no contraste (8,08:1). */
.r-snapshot{display:flex;align-items:center;gap:var(--s-3);margin:var(--s-4) 0;padding:var(--s-4);border-left:3px solid var(--blue-deep);border-radius:var(--r-sm);background:var(--blue-light);color:var(--blue-deep);font-size:var(--t-meta);line-height:1.45}
.r-snapshot>i{flex:0 0 auto;font-size:var(--i-sm)}
.r-card .metrics{padding:0}
.r-card .metrics .mrow{grid-template-columns:repeat(2,minmax(0,1fr))}
.r-card .row,.r-card .note{margin-left:0;margin-right:0;margin-bottom:0}
.r-card .row{margin-top:var(--s-5)}
.r-card .note{margin-top:var(--s-4)}
.r-reputation{display:grid;grid-template-columns:1fr;gap:var(--s-5)}
.r-reputation article{min-width:0;padding:var(--s-5);border:1px solid var(--line);border-radius:var(--r-sm)}
.r-reputation h3{margin-bottom:var(--s-4);font-family:var(--sans);font-size:var(--t-body);font-weight:700}
.r-mini-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
.r-card .graf{margin:0}
.r-posts{list-style:none;padding:0;margin-top:var(--s-4)}
.r-posts li{padding:var(--s-4) 0;border-top:1px solid var(--line)}
.r-posts a{display:flex;color:inherit;text-decoration:none}
.r-posts li>a,.r-posts li>span.r-post-text{gap:var(--s-4)}
.r-post-thumb{flex:0 0 56px;width:56px;height:56px;border-radius:var(--r-sm);object-fit:cover;background:var(--soft)}
.r-post-text{display:block;min-width:0;font-size:var(--t-meta);line-height:1.45;color:var(--ts)}
.r-post-text b{display:block;font-weight:600;color:var(--navy)}
.r-post-text span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
.r-keywords{list-style:none;padding:0}
.r-keywords li{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);padding:var(--s-4) 0;border-bottom:1px solid var(--line);font-size:var(--t-meta)}
.r-keywords li:last-child{border-bottom:0}
.r-keywords span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.r-keywords b{flex:0 0 auto;color:var(--navy);font-variant-numeric:tabular-nums}
.r-keywords b.empty{color:var(--empty)}
.r-note{margin:0}

@media (min-width:1024px){
  #app.results-page{max-width:1280px;border-left:1px solid var(--line);border-right:1px solid var(--line)}
  .results-page .shead,.r-layout{padding-left:var(--s-7);padding-right:var(--s-7)}
  .r-layout{grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--s-6)}
  .r-health{grid-column:span 4}
  [data-section="google-performance"]{grid-column:span 8}
  [data-section="lead-pipeline"],[data-section="reputation-content"]{grid-column:1/-1}
  [data-section="performance-trend"]{grid-column:span 8}
  [data-section="top-searches"]{grid-column:span 4}
  .r-reputation{grid-template-columns:repeat(2,minmax(0,1fr))}
  .r-actions{grid-template-columns:repeat(5,minmax(0,1fr))}
  .r-actions .website{grid-column:auto}
  .r-card .metrics .mrow{grid-template-columns:repeat(6,minmax(0,1fr))}
  .r-note{grid-column:1/-1}
}

/* Home (#2842): mesmos nós nas três faixas; missão e sprints lado a lado em telas largas. */
.mission-home{padding-bottom:calc(96px + env(safe-area-inset-bottom))}
.mission-home>.p-hd{min-width:0}
.mission-home>.p-hd>div:first-child{min-width:0}
.mission-home>.p-hd h1{overflow-wrap:anywhere}
.mission-home>.p-hd .dir{flex-shrink:0}
.mission-toolbar{min-width:0;width:100%;display:flex;flex-direction:column;gap:var(--s-5);overflow:hidden}
.mission-toolbar .p-chips{padding-top:0}
.mission-pulse{margin-top:var(--s-5)}

.mission-control{display:grid;margin:var(--s-5) var(--s-gutter) 0;border-radius:var(--r-lg);overflow:hidden;
  background:var(--navy-card);box-shadow:var(--p-sh)}
.mission-hero{padding:var(--s-7);color:#fff}
.mission-hero .jhead{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5)}
.mission-hero .jhead span,.mission-sprints>div>span{font-size:var(--t-micro);font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--navy-fade)}
.mission-hero .jhead em{font-size:var(--t-micro);font-style:normal;color:var(--navy-fade)}
.mission-hero h2{max-width:560px;margin:var(--s-5) 0 var(--s-3);font-size:var(--t-hero);line-height:1.2;color:#fff}
.mission-hero>p{max-width:580px;margin:0;color:var(--navy-fade);font-size:var(--t-meta);line-height:1.55}
.mission-hero .trail{margin-top:var(--s-7)}
.mission-hero .next{margin-top:var(--s-5)}
.mission-next{width:100%;min-height:48px;margin-top:var(--s-5);display:flex;align-items:center;justify-content:space-between;
  gap:var(--s-5);padding:0 var(--s-6);border:0;border-radius:var(--r-sm);background:var(--blue);color:#fff;
  font-family:var(--sans);font-size:var(--t-body);font-weight:800}
.mission-next i{font-size:var(--i-sm)}

.mission-sprints{padding:var(--s-7);background:var(--navy-deep);color:#fff}
.mission-sprints h3{margin:var(--s-2) 0 0;font-size:var(--t-title);color:#fff}
.mission-sprints ol{list-style:none;margin:var(--s-7) 0 0;padding:0;display:grid;gap:var(--s-5)}
.mission-sprints li{display:grid;grid-template-columns:36px minmax(0,1fr);gap:var(--s-5);align-items:center;opacity:.62}
.mission-sprints li.done,.mission-sprints li.now{opacity:1}
.mission-sprint-node{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border:1px solid var(--navy-line);border-radius:var(--r-pill);color:var(--navy-fade)}
.mission-sprints li.done .mission-sprint-node{border-color:var(--green-dark);background:var(--green-dark);color:#fff}
.mission-sprints li.now .mission-sprint-node{border-color:var(--blue-pale);background:var(--blue);color:#fff}
.mission-sprints li>div:last-child{min-width:0;display:grid;grid-template-columns:1fr auto;gap:var(--s-1) var(--s-5)}
.mission-sprints li span{font-size:var(--t-micro);color:var(--navy-fade)}
.mission-sprints li strong{grid-column:1/-1;font-size:var(--t-meta);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.mission-sprints li>div>i{grid-column:1/-1;height:4px;border-radius:var(--r-pill);overflow:hidden;background:var(--navy-tint)}
.mission-sprints li>div>i b{display:block;width:var(--w);height:100%;background:var(--blue-pale);border-radius:var(--r-pill)}

.mission-section{margin:var(--s-5) var(--s-gutter) 0}
.mission-section-head{display:flex;align-items:end;justify-content:space-between;gap:var(--s-5);margin-bottom:var(--s-5)}
.mission-section-head span{font-size:var(--t-micro);font-weight:800;letter-spacing:.08em;text-transform:uppercase;color:var(--blue-deep)}
.mission-section-head h2{margin:var(--s-2) 0 0;font-size:var(--t-title)}
.mission-section-head>a{min-height:44px;display:inline-flex;align-items:center;gap:var(--s-2);color:var(--blue-deep);font-size:var(--t-meta);font-weight:800;text-decoration:none;white-space:nowrap}
.mission-win-grid{display:grid;gap:var(--s-4)}
.mission-win{min-height:76px;display:grid;grid-template-columns:36px minmax(0,1fr) auto;align-items:center;gap:var(--s-5);
  padding:var(--s-5);border:1px solid var(--line);border-radius:var(--r-md);background:#fff;color:var(--navy);text-decoration:none;box-shadow:var(--p-sh)}
.mission-win>i:first-child{width:36px;height:36px;display:flex;align-items:center;justify-content:center;border-radius:var(--r-pill);background:var(--green-light);color:var(--green-dark);font-size:var(--i-sm)}
.mission-win span{font-size:var(--t-meta);font-weight:800;line-height:1.35}
.mission-win>i:last-child{color:var(--blue-deep)}
.mission-empty{margin:0;padding:var(--s-6);border:1px dashed var(--track);border-radius:var(--r-md);color:var(--ts);font-size:var(--t-meta);line-height:1.5}
/* #3397 — legenda da grade. Não é `.mission-empty`: aquilo substitui um bloco
   inteiro sem dado; esta apoia uma grade que TEM dado, só não em todo card. */
.mission-nota{margin:var(--s-4) 0 0;color:var(--ts);font-size:var(--t-micro);line-height:1.5}

.mission-metrics{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-4)}
.mission-metric{min-width:0;display:grid;grid-template-columns:32px minmax(0,1fr) auto;align-items:center;gap:var(--s-3);
  padding:var(--s-5);border:1px solid var(--line);border-radius:var(--r-md);background:#fff;color:var(--navy);text-decoration:none;box-shadow:var(--p-sh)}
.mission-metric-icon{width:32px;height:32px;display:flex;align-items:center;justify-content:center;border-radius:var(--r-sm);background:var(--blue-light);color:var(--blue-deep)}
.mission-metric>span{grid-column:1/-1;color:var(--ts);font-size:var(--t-micro);font-weight:700}
.mission-metric>strong{font-size:var(--t-hero);line-height:1;font-variant-numeric:tabular-nums}.mission-metric>strong.empty{color:var(--empty)}
/* #3397 — a ausência tem forma (D62), não tamanho de manchete. No degrau do
   número (`--t-hero`) o `—` é uma barra larga que pesa MAIS que qualquer
   dígito: a hierarquia inverte e "não medimos" grita mais que o dado ao lado.
   A cor fica (`--empty` é contraste medido); cede o tamanho. `line-height`
   segue o do número, senão a grade fica de alturas desiguais. */
.mission-metric>strong.empty{font-size:var(--t-body);line-height:var(--t-hero)}
.r-value b.empty{font-size:var(--t-body);line-height:calc(var(--t-hero)*1.1)}
.mission-metric>i{color:var(--ts)}
.mission-chart-grid{display:grid;gap:var(--s-5);margin-top:var(--s-5)}
.mission-chart-grid>.p-card{margin:0;min-width:0;text-decoration:none;color:var(--navy)}
.mission-google{display:block}
.mission-google-bars{display:grid;gap:var(--s-5);margin-top:var(--s-7)}
.mission-google-bars>div{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);padding-bottom:var(--s-4);border-bottom:1px solid var(--line)}
.mission-google-bars span{font-size:var(--t-meta);color:var(--ts)}
.mission-google-bars b{font-size:var(--t-lead);font-variant-numeric:tabular-nums}

.mission-support{margin:var(--s-5) var(--s-gutter) 0}
.mission-support>a,.mission-support>div{min-height:72px;display:grid;grid-template-columns:44px minmax(0,1fr) auto;align-items:center;gap:var(--s-5);
  padding:var(--s-5);border-radius:var(--r-md);background:var(--blue-light);color:var(--navy);text-decoration:none}
.mission-support>a>i:first-child,.mission-support>div>i:first-child{width:44px;height:44px;display:flex;align-items:center;justify-content:center;border-radius:var(--r-pill);background:var(--green-dark);color:#fff;font-size:var(--i-md)}
.mission-support span{display:grid;gap:var(--s-1)}
.mission-support strong{font-size:var(--t-body)}
.mission-support small{color:var(--ts);font-size:var(--t-micro);line-height:1.4}

@media (min-width:600px){
  .mission-home>.mission-toolbar,.mission-home>.mission-pulse,.mission-home>.mission-control,
  .mission-home>.mission-section,.mission-home>.mission-support{grid-column:1/-1;margin:0}
  .mission-toolbar{flex-direction:row;align-items:center;justify-content:space-between}
  .mission-toolbar .p-chips{margin:0;padding-left:0;padding-right:0}
  .mission-control{grid-template-columns:minmax(0,1.15fr) minmax(260px,.85fr)}
  .mission-win-grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .mission-chart-grid{grid-template-columns:minmax(0,1.35fr) minmax(240px,.65fr)}
}
@media (min-width:1024px){
  .mission-home{grid-template-columns:repeat(12,minmax(0,1fr));padding:var(--s-8)}
  .mission-home>.p-hd,.mission-home>.mission-toolbar,.mission-home>.mission-pulse,
  .mission-home>.mission-control,.mission-home>.mission-section,.mission-home>.mission-support{grid-column:1/-1}
  .mission-win-grid{grid-template-columns:repeat(4,minmax(0,1fr))}
  .mission-metrics{grid-template-columns:repeat(4,minmax(0,1fr))}
  .mission-hero,.mission-sprints{padding:var(--s-8)}
}

/* --- perfil e schedule (2026-08-11) --------------------------------------- */
/* Cores por TOKEN, nunca hex: a lei 4 do design system é verificada no CI. */
.secao{margin:0 var(--s-gutter) var(--s-7)}
/* Manrope 600, não Inter 700: só as faces de `fonts.css` existem no disco —
   Inter 400/500 e Manrope 500/600. Pedir Inter 700 fazia o browser SINTETIZAR
   o negrito (engorda o traço por transformação, sem desenho), e como as três
   regras do perfil pediam peso não servido, a tela inteira saía borrada ao
   lado de um "your profile" em Manrope 600 de verdade. */
.secao h3{font-family:var(--display);font-size:var(--t-meta);font-weight:600;color:var(--ts);margin:0 0 var(--s-4);letter-spacing:.02em}
/* O `gap:1px` não é degrau e não vira `--s-1`: com `overflow:hidden` e os
   `.dado` colados ele é o FIO DIVISOR, desenhado como gap. 2px engrossa. */
.campos{margin:0 0 var(--s-5);display:flex;flex-direction:column;gap:1px;border-radius:var(--r-sm);overflow:hidden}
/* `.dado`, não `.campo`: `.campo` já era o par label+input do LOGIN (l. 123).
   Como este bloco vem depois no arquivo com a mesma especificidade, ele vencia
   a cascata e injetava `background`, `padding` e `justify-content` nos campos
   de e-mail e senha — o caixote cinza de canto reto atrás dos labels do login,
   que ninguém desenhou. Dois componentes sem relação disputando um seletor sem
   escopo, na primeira tela que o cliente vê. */
.dado{display:flex;justify-content:space-between;align-items:baseline;gap:var(--s-5);background:var(--soft);padding:var(--s-5) var(--s-6)}
.dado dt{font-size:var(--t-micro);color:var(--ts);flex-shrink:0}
.dado dd{margin:0;font-size:var(--t-meta);font-weight:500;text-align:right;word-break:break-word}
/* `text-decoration:none` porque esta classe deixou de ser só de `<button>`: o
   Calendar e o WhatsApp de suporte são `<a>` de verdade (link abre em nova aba,
   aparece no teclado, é anunciado por leitor de tela), e sem esta linha eles
   apareciam sublinhados no meio de uma lista de itens que não são. */
.linha-acao{display:flex;align-items:center;gap:var(--s-5);width:100%;background:var(--soft);border:0;border-radius:var(--r-sm);
  padding:var(--s-6);margin-bottom:var(--s-4);font:inherit;font-size:var(--t-body);font-weight:500;color:inherit;text-align:left;
  text-decoration:none;cursor:pointer}
.linha-acao:disabled{opacity:.55;cursor:progress}
.linha-acao span{flex:1}
/* `--blue`, não `--blue-500`: esse token NUNCA existiu no `:root`. Custom
   property inválida numa propriedade herdada cai no valor do pai, então os dois
   ícones saíam em `--navy` — a mesma cor do texto ao lado — e perdiam a
   afordância azul que a ADR-0034 usa em todo o resto do app. Silencioso por
   construção: o lint procurava hex, e nome sem cor não é hex. */
.linha-acao i:first-child{font-size:var(--i-sm);color:var(--blue)}
.linha-acao i:last-child{font-size:var(--i-sm);color:var(--ts)}

/* --- codigo de pareamento (ADR-0177) ------------------------------------- */
.cod{font-family:var(--mono);font-size:var(--t-display);font-weight:500;letter-spacing:.06em;
  text-align:center;color:var(--navy);background:var(--soft);border-radius:var(--r-sm);
  padding:var(--s-6) var(--s-4);margin-bottom:var(--s-4);
  user-select:all;-webkit-user-select:all}
.cod-prazo{text-align:center;font-size:var(--t-meta);color:var(--ts);margin-bottom:var(--s-5)}

.vazio{text-align:center;padding:var(--s-8) var(--s-7)}
.vazio .ic-grande{width:56px;height:56px;margin:0 auto var(--s-6);border-radius:var(--r-md);background:var(--soft);
  display:flex;align-items:center;justify-content:center}
.vazio .ic-grande i{font-size:var(--i-lg);color:var(--blue)}
.vazio h3{font-size:var(--t-body);margin:0 0 var(--s-4);color:inherit;letter-spacing:0}
.vazio p{margin:0 0 var(--s-3);font-size:var(--t-meta);line-height:1.5;color:var(--ts)}
/* Sem `opacity`: a 80% o `--ts` compunha sobre o branco um cinza de 3,42:1, e
   nesse tamanho a regra exige 4,5:1. A frase que reprovava é justamente a que
   existe para tranquilizar quem pagou ("nothing for you to do") — desenhada e
   ilegível. O `contrast-lint` não alcança este caso por construção: ele lê
   pares de `color`+`background` na mesma regra, e aqui quem derruba o contraste
   é o alpha. Opacidade sobre texto é contraste que o portão não vê. */
.vazio p.sub{font-size:var(--t-micro);color:var(--ts)}
a.metrics{text-decoration:none;color:inherit}

/* --- schedule (bloco 2) ---------------------------------------------------
   Azul inteira: compromisso marcado é hora combinada, não espera por resposta.
   Usar alerta extra aqui gastaria o sinal de urgência que o app tem. */
.ag-destaque{background:var(--blue-pale);border:1px solid var(--blue-light);border-radius:var(--r-md);padding:var(--s-6)}
.ag-quando{display:flex;align-items:baseline;gap:var(--s-4);margin-bottom:var(--s-2)}
.ag-dia{font-family:var(--display);font-weight:600;font-size:var(--t-meta);color:var(--blue-deep);letter-spacing:.02em}
.ag-hora{font-size:var(--t-micro);color:var(--ts);font-weight:500}
.ag-quem{font-size:var(--t-body);font-weight:500;color:var(--navy);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

.ag-linha{display:flex;gap:var(--s-5);align-items:flex-start;border:1px solid var(--line);margin-bottom:var(--s-4)}
/* O que já aconteceu continua na tela (a janela vai 7 dias para trás) mas
   recuado, para não competir com o que ainda vai acontecer. Recuo por COR e não
   por opacity: alpha sobre texto é contraste que o portão de contraste não vê
   — a lição já escrita no `.vazio p.sub` logo acima. */
.ag-linha.passou .ag-quem{color:var(--ts)}
.ag-hora-col{font-size:var(--t-micro);color:var(--ts);font-weight:500;flex-shrink:0;min-width:76px;padding-top:var(--s-1)}
.ag-corpo{flex:1;min-width:0}

.ag-meta{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);margin-top:var(--s-3)}
.ag-selo{display:inline-flex;align-items:center;gap:var(--s-2);font-size:var(--t-micro);color:var(--ts);background:var(--soft);border-radius:var(--r-pill);padding:var(--s-1) var(--s-4)}
.ag-selo i{font-size:var(--i-sm)}
/* O selo da IA é o único com cor própria: é a entrega que o cliente paga, e a
   tela só o mostra quando `created_source` prova que foi ela. */
.ag-selo.ia{background:var(--blue-light);color:var(--blue-deep)}
.ag-conversa{display:inline-flex;align-items:center;gap:var(--s-2);font-family:var(--sans);font-size:var(--t-micro);font-weight:500;background:var(--blue);color:#fff;border:0;border-radius:var(--r-pill);padding:var(--s-2) var(--s-5);text-decoration:none}
.ag-conversa i{font-size:var(--i-sm)}

/* =========================================================================
   PAINEL — a malha da referência aprovada (2026-08-16)
   =========================================================================
   A chefia aprovou a réplica de `docs/design/mockup-replica-figma.html` e
   mandou subir "sem perder o que fizemos lá". Então:

     · o que ENTRA  → a malha: cartão branco r16 sobre chão cinza, hairline +
       uma sombra, KPI 2×2, gráfico, funil com barra que cresce.
     · o que FICA   → o pulso de lead (atenção em azul oficial), a jornada, o
       traço de ausência (D62), os estados dirigidos por `humor`. Nada disso
       muda de comportamento; muda a moldura em volta.

   ── A RAMPA PRÓPRIA DO PAINEL ────────────────────────────────────────────
   A referência tem tamanhos que a nossa escala não tem (9 · 10 · 11 · 14 · 18),
   e a hierarquia dela é feita por PESO. Somar 5 degraus aos 7 de `--t-*` daria
   12 tamanhos anônimos — que é exatamente a bagunça que o design system
   desfez ("eram 20 tamanhos, 5 deles em meio pixel").

   Então o painel ganha rampa PRÓPRIA e NOMEADA, `--p-*`, com trabalho definido.
   Duas rampas com nome são legíveis; doze degraus sem dono não são.

   🔴 TRADE-OFF REGISTRADO, decisão da Junia: isto fura o piso de 12px que o
   design system fixou pensando no ICP — dono de empresa de serviço, 35-55 anos,
   lendo no sol. Ela viu o render nesses tamanhos e aprovou. Se depois parecer
   pequeno, sobe-se a rampa inteira um degrau sem tocar no layout.
   ========================================================================= */

/* o painel pinta o chão de cinza; o cartão é que é branco */
.home-page{background:var(--soft)}

/* --- cabeçalho: empresa 11/600 sobre título 20/800 ----------------------- */
.p-hd{display:flex;align-items:flex-start;justify-content:space-between;padding:4px 16px 8px}
.p-hd .quem{margin:0;font-size:var(--p-lin);font-weight:600;color:var(--ts)}
.p-hd h1{margin:0;font-family:var(--sans);font-size:var(--p-tit);font-weight:800;
  color:var(--navy);letter-spacing:-.5px;line-height:32px}
.p-hd .dir{display:flex;gap:8px;align-items:center}
.p-hd .sino{width:32px;height:32px;border-radius:var(--r-pill);background:#fff;
  box-shadow:var(--p-sh);display:grid;place-items:center;color:var(--navy)}
.p-hd .sino i{font-size:var(--i-sm)}
.p-hd .av{width:32px;height:32px;border-radius:var(--r-pill);background:var(--blue);
  color:#fff;display:grid;place-items:center;font-size:var(--p-tab);font-weight:700}

/* --- chips de contexto --------------------------------------------------- */
.p-chips{display:flex;gap:8px;padding:0 16px;flex-wrap:wrap}
.p-chip{display:inline-flex;align-items:center;gap:4px;padding:6px 10px;border-radius:var(--r-sm);
  background:#fff;border:1px solid var(--p-hair);box-shadow:var(--p-sh);
  font-size:var(--p-rot);font-weight:600;color:var(--navy)}
.p-chip .pt{width:6px;height:6px;border-radius:var(--r-pill);background:var(--green)}
.p-chip i{font-size:var(--p-rot);color:var(--ts)}

/* --- cartão de painel ---------------------------------------------------- */
.p-card{margin:12px 16px 0;padding:16px;border-radius:var(--r-p);background:#fff;
  border:1px solid var(--p-hair);box-shadow:var(--p-sh)}
.p-card h3{margin:0;font-family:var(--sans);font-size:var(--p-card);font-weight:700;
  color:var(--navy);line-height:20px}
.p-card .sub{margin:0;font-size:var(--p-rot);font-weight:400;color:var(--ts);line-height:15px}

/* --- gráfico ------------------------------------------------------------- */
.p-graf{margin-top:8px}
.p-graf svg{display:block;width:100%;height:108px;overflow:visible}
/* #3397 — saiu `.p-graf .eixo{…;fill:var(--ts)}`: apontava para um `<text>` de
   SVG que `grafico()` nunca emitiu, e venceria por especificidade a régua real
   (12px), deixando a da Home em 11. A Home usa a MESMA régua agora. */
.p-leg{display:flex;gap:12px;margin-top:8px;flex-wrap:wrap}
.p-leg span{display:inline-flex;align-items:center;gap:4px;font-size:var(--p-nano);
  font-weight:400;color:var(--ts)}
.p-leg i{width:8px;height:8px;border-radius:var(--r-pill);display:inline-block}

/* --- schedule · a GRADE DE MÊS (v1.5) -------------------------------------
   Desenho e custo: `docs/calendario-v1.5-desenho.md`, opção B. O porquê mora
   lá; aqui fica só o que não dá para ler no CSS.

   Alvo de toque MEDIDO em Chrome real (`getBoundingClientRect`), coluna a
   375 px: célula 47,3×47,3 · seta 44×44 · "all days" 73,2×44. A 320 px a
   célula cai para 44×44 — `min-height` segura o piso e `aspect-ratio` obriga a
   largura a acompanhar, então ela encolhe quadrada em vez de virar uma tira.

   🔴 NENHUM recuo por `opacity`: alpha sobre texto é contraste que o
   `contrast-lint` não enxerga (a lição de `.vazio p.sub` e `.ag-linha.passou`).
   Os cinco estados da célula declaram cor E fundo NA MESMA REGRA de propósito —
   é a única forma de o portão medir. Medido: normal 13,4:1 · hoje 8,08:1 ·
   selecionado 18,38:1 · inerte 5,12:1 · fora do mês 4,80:1. */
.ag-aviso{display:flex;align-items:center;gap:8px;margin:0 16px 12px;padding:10px 13px;border-radius:var(--r-sm);font-size:var(--t-micro);background:var(--blue-light);color:var(--blue-deep)}
.ag-aviso i{font-size:var(--i-sm)}

.ag-nav{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:6px}
.ag-mes{font-family:var(--display);font-weight:600;font-size:var(--t-body);color:var(--navy);letter-spacing:.02em}
.ag-seta{display:flex;align-items:center;justify-content:center;width:44px;min-height:44px;border:0;border-radius:var(--r-pill);background:var(--soft);color:var(--blue-deep)}
.ag-seta i{font-size:var(--i-md)}
/* A seta travada continua VISÍVEL: ela É a borda da janela −7/+45. */
.ag-seta[disabled]{background:var(--soft);color:var(--ts)}

.ag-sem{display:grid;grid-template-columns:repeat(7,1fr);gap:2px;padding-bottom:4px;text-align:center;font-size:var(--t-micro);font-weight:500;background:#fff;color:var(--ts)}
.ag-grade{display:grid;grid-template-columns:repeat(7,1fr);gap:2px}
.ag-cel{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;aspect-ratio:1;min-height:44px;min-width:0;padding:0;border:0;border-radius:var(--r-sm);font-family:var(--sans);font-size:var(--t-meta);font-weight:500;background:#fff;color:var(--navy)}
/* Fora do mês: cinza cheio — "esta caixa completa a grade", sem competir. */
.ag-cel.fora{background:var(--soft);color:var(--ts)}
/* Fora de −7/+45: o servidor NÃO buscou aqui. Segue clicável e legível de
   propósito — quem tocar lê "nenhum compromisso neste dia" em vez de achar um
   botão morto (D62: "não buscamos" ≠ "não há nada"). */
.ag-cel.inerte{background:#fff;color:var(--ts)}
.ag-cel.hoje{background:var(--blue-light);color:var(--blue-deep)}
/* Selecionado = o mesmo par de `.tab.on`. Um significado, um desenho só. */
.ag-cel.sel{background:var(--navy-card);color:#fff}
/* Indicador não-textual (§1.4.11, piso 3:1). O `contrast-lint` não alcança este
   par — o fundo vem da célula, não desta regra — então vai medido à mão: azul
   sobre branco 4,62 · sobre `--blue-light` 4,01 · branco sobre `--navy-card` 18,38. */
.ag-ponto{width:5px;height:5px;border-radius:var(--r-pill);background:var(--blue)}
.ag-cel.sel .ag-ponto{background:#fff}

.ag-diahead{display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:10px}
.ag-diahead h3{margin:0}
.ag-todos{min-height:44px;padding:0 14px;border:0;border-radius:var(--r-pill);font-family:var(--sans);font-size:var(--t-micro);font-weight:500;background:var(--soft);color:var(--blue-deep)}
.ag-diavazio{padding:16px;border-radius:var(--r-md);text-align:center;font-size:var(--t-meta);background:var(--soft);color:var(--ts)}

/* 42 células sem foco visível é armadilha de tabulação. Mesmo `outline` dos
   campos (`.campo input:focus`) — um significado, um desenho. */
.ag-cel:focus-visible,.ag-seta:focus-visible,.ag-todos:focus-visible{outline:2px solid var(--blue);outline-offset:1px}

/* --- alvos de toque (WCAG 2.2 AA §2.5.8) ------------------------------------
   Mediam 18-21px em Chrome real, abaixo do mínimo de 24 — e o piso que importa
   é 44: o comprador toca de pé, com a mão suja (mesma nota de ICP que levantou
   o piso da fonte). `min-height` + `inline-flex`: a caixa cresce, o peso visual
   de link não. Juntos e não espalhados porque é invariante — "todo alvo passa?"
   precisa de UM lugar para ser respondida. */
.voltar,.ligar,.pular,.titulo-conversa,.tform .chip button,.tform .serv{min-height:44px;display:inline-flex;align-items:center}

/* --- convite de acesso ----------------------------------------------------
   Esta rota chega ANTES de qualquer tela autenticada. Sem um bloco próprio,
   `<main>`, `<h1>` e `<p>` caíam no estilo padrão do navegador: texto colado
   ao canto superior esquerdo, exatamente o defeito medido no Chrome em
   22/08/2026. A peça usa a mesma superfície clara, raio e sombra do painel;
   não é um segundo design system. */
.convite-tela{
  display:grid;
  place-items:center;
  min-height:100dvh;
  padding:max(var(--s-7),env(safe-area-inset-top)) var(--s-gutter)
          max(var(--s-7),env(safe-area-inset-bottom));
  background:var(--soft);
}
.convite-tela .cartao{
  width:min(100%,360px);
  padding:var(--s-7);
  display:flex;
  flex-direction:column;
  align-items:stretch;
  gap:var(--s-5);
  background:#fff;
  border:1px solid var(--line);
  border-radius:var(--r-lg);
  box-shadow:var(--p-sh);
}
.convite-tela h1{
  font-size:var(--t-title);
  line-height:1.2;
}
.convite-tela p{
  color:var(--ts);
  font-size:var(--t-body);
  line-height:1.55;
}
.convite-tela .cta{
  width:100%;
  min-height:48px;
  margin-top:var(--s-2);
}

/* =========================================================================
   A TRAVA — o onboarding como `<dialog>` modal sobre a Home (js/trava.js)
   ========================================================================= */

/* `padding:0`/`border:0` desfazem o UA stylesheet do `<dialog>` (margem e borda
   de janela de 1998). Mesma largura máxima do `#app`: a caixa é a MESMA coluna
   do app, não uma janela de outra medida por cima dela. */
dialog.trava{
  padding:0;
  border:0;
  /* 🔴 `margin:auto` NÃO é redundante com o UA stylesheet — e a ausência dela
     aqui foi defeito no ar. O reset global deste arquivo (o seletor universal
     com `margin:0`, l. 180) zera a margem de TUDO, inclusive a do `<dialog>`,
     e é ela que centra a caixa no top layer. Sem esta linha o `<dialog>` modal
     (que o UA posiciona com `inset:0`) encosta no CANTO SUPERIOR ESQUERDO da
     tela, com os cantos arredondados na borda — foi exatamente o que a chefia
     viu ao ser segurada pelo portão em 19/08. A trava nasceu sem `margin:auto`;
     Chrome 151 reproduced the failure before this invariant was added.
     🚫 Nenhum comentário DENTRO desta regra pode conter chave de fechamento: a
     sonda do `trava-fluxo.proof.mjs` lê o corpo da regra até a primeira que
     encontra, e uma chave no comentário corta a leitura no meio. Medido — o
     controle negativo do proof caiu por isso em 19/08. */
  margin:auto;
  width:min(100% - var(--s-7),420px);
  max-height:min(88dvh,760px);
  border-radius:var(--r-lg);
  background:#fff;
  color:var(--navy);
  display:flex;
  flex-direction:column;
  overflow:hidden;
  box-shadow:0 18px 48px var(--sombra-2);
}


/* 🚫 NÃO APAGUE ESTA REGRA ACHANDO QUE É REDUNDANTE COM O NAVEGADOR.
   Ela parece só repetir o que o UA stylesheet já faz (`dialog:not([open])
   {display:none}`) — e não repete: a regra logo acima declara `display:flex` em
   `dialog.trava` SEM condição, e CSS de autor vence CSS de UA. O `display:none`
   do navegador perde para a nossa própria linha, e a caixa FECHADA continua
   ocupando layout. A regra de cima é que criou a exceção; esta a desfaz.

   Ficou latente enquanto a trava só existia TRAVADA — ela nunca fechava por
   toque. O `×` e a abertura voluntária tornaram o caminho alcançável, e aí o
   defeito vira tela: medido em Chrome no app real, depois de fechar pelo `×` o
   `<dialog>` seguia com `display:flex`, `visibility:visible`, 420×157 e o texto
   do cabeçalho dentro — um cartão branco órfão no fim da Home, com frase de
   onboarding e sem nada explicando o que é.

   `:not([open])` aqui, e não mover o `display` para `dialog.trava[open]` lá em
   cima: `display` é a ÚNICA propriedade que briga com o UA, e movê-la partiria
   o box model da caixa em duas regras — quem for ler como ela desenha passaria
   a precisar das duas, e o `contrast-lint` deste repo mede pares declarados na
   MESMA regra. Assim a exceção fica isolada e visível.

   Provado em `trava-fluxo.proof.mjs` ("a caixa fechada pelo × NÃO ocupa
   layout"), com controle negativo que serve o CSS sem esta linha. */
dialog.trava:not([open]){display:none}

/* 🚫 O BLUR VAI NO `::backdrop`, NUNCA `filter:blur()` NO `#app`: `filter` num
   ancestral cria containing block para `position:fixed`, e a `.nav` é fixed —
   ela se descolaria da moldura e passaria a rolar com o conteúdo, que é a falha
   já escrita verbatim no comentário do `#app` no desktop (l. 190-200). E
   forçaria repaint de blur de página inteira por frame de rolagem.

   `background:currentColor` e não um token: `::backdrop` só passou a HERDAR do
   elemento de origem em 2024 (Chrome 122 · Safari 17.4 · Firefox 125). Antes
   disso ele não tem pai, `var(--navy-card)` não resolve e o véu sai
   TRANSPARENTE — falha muda, e justo no aparelho velho. `currentColor` serve os
   dois mundos: com herança é o `--navy` da caixa, sem ela é o preto inicial.
   (Pela Lei 4 o certo seria um `--veu-trava` no `:root`; ver o relatório.) */
dialog.trava::backdrop{background:currentColor;opacity:.55}
@supports (backdrop-filter:blur(1px)){
  /* Com blur o véu pode ser mais leve: o ponto é o cliente VER o app que
     comprou por trás enquanto responde. */
  dialog.trava::backdrop{backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);opacity:.34}
}

/* Cabeça CLARA, corpo com o card navy que a jornada já renderiza. Não é gosto:
   o markup dela é escrito para `--navy-card` (`.tbody h4` e `.tform .serv` são
   brancos) — num painel branco esses pares caem para 1:1, e o `contrast-lint`
   não pegaria nenhum, porque só mede pares declarados na mesma regra. */
.trava-head{
  padding:var(--s-7) var(--s-gutter) var(--s-5);
  background:#fff;
  color:var(--navy);
  border-bottom:1px solid var(--line);
  flex-shrink:0;
  position:relative;
}
/* A saída da abertura VOLUNTÁRIA (card da Home). Nasce `hidden` no HTML e só
   `abrir()` a revela, conferindo o modo — na trava do portão ela não existe,
   senão seria o botão de pular que o cabeçalho de `trava.js` diz não existir.
   44px pelo mesmo piso de toque do resto (WCAG 2.2 AA §2.5.8). `--ts` sobre
   branco = 5,12:1: sai do caminho do olho sem sair do alcance do dedo. */
.trava-fechar{
  position:absolute;
  top:var(--s-4);
  right:var(--s-4);
  width:44px;
  min-height:44px;
  display:inline-flex;
  align-items:center;
  justify-content:center;
  border:0;
  border-radius:var(--r-pill);
  background:#fff;
  color:var(--ts);
}
.trava-fechar i{font-size:var(--i-md)}
.trava-fechar[hidden]{display:none}
.trava-head h1{font-family:var(--display);font-weight:600;font-size:var(--t-title);color:var(--navy)}
.trava-head p{margin-top:var(--s-2);font-size:var(--t-meta);color:var(--ts);line-height:1.45}
/* Classe DUPLA de propósito: `.trava-recusa` sozinha (0,1,0) PERDERIA para
   `.trava-head p` (0,1,1) e a cor voltaria ao cinza.
   🚫 `.trava-contador` saiu em 2026-08-17 junto com o `N of M answered` do
   cabeçalho da caixa: o quiz não mostra total nenhum. */
.trava-head .trava-recusa{font-weight:500;color:var(--blue-deep)}
/* O que ainda falta, na caixa TRAVADA. Duas classes pelo mesmo motivo da linha
   acima: `.trava-falta` sozinha (0,1,0) perderia para `.trava-head p` (0,1,1).
   Peso 500 e `--navy` (o texto principal, 12,6:1 sobre branco): é a frase que
   responde "por que estou preso?", e ela não pode ser mais fraca que o apoio
   cinza logo acima dela. */
.trava-head .trava-falta{font-weight:500;color:var(--navy)}
.trava-head .trava-falta:empty{display:none}

/* Região viva que nasce VAZIA e some quando não tem o que dizer. Escondê-la com
   `hidden` e só então pôr texto dentro é o jeito de um `role="status"` NÃO
   anunciar — o leitor precisa do nó já presente para ver a mudança. Vale também
   para as caixas de recusa da jornada, que trocaram `hidden` por texto vazio. */
[data-recusa]:empty,[data-falta]:empty,[data-erro-conectar]:empty{display:none}

/* `overscroll-behavior:contain`: sem isto, chegar ao fim da lista continua
   rolando a Home ATRÁS do véu — a página que a trava existe para segurar. */
/* #3449: `flex:1` é base 0% e porcentagem contra altura automática resolve a
   zero. Base `auto` mede o conteúdo; `min-height:0` devolve encolher e rolar. */
.trava-corpo{
  flex:1 1 auto;
  min-width:0;
  min-height:0;
  overflow-y:auto;
  overscroll-behavior:contain;
  -webkit-overflow-scrolling:touch;
  padding:var(--s-5) 0 var(--s-7);
  background:#fff;
  color:var(--navy);
}

/* `:has` é ignorado por quem não o suporta, e aí sobra o `overscroll-behavior`
   acima — degradação, não quebra. */
body:has(dialog.trava[open]),body:has(dialog.ficha[open]){overflow:hidden}

/* --- busca da Inbox (#2766) ------------------------------------------------
   Mesmo desenho da busca do catálogo do Google (`.tform .busca`), classe
   própria: aquela é escopada no card NAVY da jornada, e reusá-la aqui traria
   os tokens de contraste da superfície errada. Os 13px/36px são os mesmos de
   lá de propósito — a lupa fica no mesmo lugar nas duas telas. */
.ibusca{position:relative;display:flex;align-items:center;margin:0 var(--s-gutter) var(--s-6)}
.ibusca i{position:absolute;left:13px;font-size:var(--i-sm);color:var(--ts);pointer-events:none}
.ibusca input[type=search]{
  font-family:var(--sans);font-size:var(--t-body);color:var(--navy);
  background:var(--soft);border:0;border-radius:var(--r-pill);
  padding:var(--s-4) var(--s-8) var(--s-4) 36px;width:100%;
  -webkit-appearance:none;appearance:none;
}
.ibusca input[type=search]::placeholder{color:var(--ts)}
/* O "x" NATIVO do WebKit apareceria ao lado do nosso: dois botões de limpar, e
   o nativo sem rótulo para leitor de tela. */
.ibusca input[type=search]::-webkit-search-cancel-button,
.ibusca input[type=search]::-webkit-search-decoration{-webkit-appearance:none;display:none}
.ibusca-x{position:absolute;right:var(--s-3);background:none;border:0;
  padding:var(--s-3);display:flex;align-items:center;color:var(--ts)}
.ibusca-x[hidden]{display:none}
.ibusca-x i{position:static;font-size:var(--i-sm)}

/* 🔴 A lista ganhou container (`.lista-inbox`) para a busca repintar só ela sem
   derrubar o foco do campo. Efeito colateral de ESTRUTURA: a última `.conv`
   virou `:last-child`, coisa que nunca foi (o `<nav>` sempre veio depois dela),
   e `.conv:last-child{border-bottom:0}` apagaria um separador que existe hoje.
   Esta linha devolve o traço. A mudança é de árvore, não de desenho. */
.lista-inbox > .conv:last-child{border-bottom:1px solid var(--line)}

/* O anúncio da busca para leitor de tela. Invisível aos olhos, presente à
   árvore de acessibilidade — `display:none` e `visibility:hidden` REMOVEM o nó
   dessa árvore, e um `role="status"` removido não anuncia nada. Por isso o
   recorte de 1px em vez do caminho curto.
   `white-space:nowrap` impede que o texto recortado force barra de rolagem. */
.ibusca-status{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;border:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap
}

/* =========================================================================
   CLIENT APP ADAPTATIVO — Brand Kit v1.1 (#2776, #2777)

   O telefone continua sendo a composição-base aprovada. Tablet usa a largura
   para comparar blocos relacionados; desktop troca a barra inferior por uma
   rail persistente e dá à Inbox uma leitura mestre/detalhe. Os mesmos nós e
   ações atravessam as três faixas — não há uma segunda aplicação escondida em
   media query.
   ========================================================================= */
.p-main,.profile-main{display:contents}
.state-main{min-width:0;display:flex;flex:1;flex-direction:column}
.inbox-shell{min-width:0;display:block;flex:1}
/* 🔴 TETO DE ALTURA (#2720). Sem ele a `.thread` crescia ate caber o conteudo,
   o DOCUMENTO rolava, e o `.shead` — nome do contato e botao voltar — saia da
   tela (medido: `shead.top=-338`, 10 ligacoes a 400x844). Em PWA no celular,
   sem barra do browser, o caminho de saida da conversa some.
   O valor NAO e' escolhido: e' onde a corrente de flex ja terminava com
   conversa curta (`#app` = `min-height:100dvh` + `padding-bottom:calc(84px +
   env(safe-area-inset-bottom))` da nav fixa). Dai a geometria de quem ja cabia
   nao mudar um pixel, nem nascer faixa branca ate a nav — bloco 4 do
   `inbox-rolagem-thread.proof.mjs` mede os dois.
   `flex:none` porque `flex:1` traz `flex-basis:0%`, que ignoraria o `height`.
   So `.thread-view`: a LISTA segue rolando o documento, e as faixas >=768px
   redeclaram esta regra abaixo — este teto e' o caso do telefone. */
.inbox-shell.thread-view{display:flex;flex:none;
  height:calc(100dvh - 84px - env(safe-area-inset-bottom))}
.inbox-empty-pane,.inbox-sidebar{display:none}
.thread-pane{min-width:0;display:flex;flex:1;flex-direction:column}
.conv{color:inherit;text-decoration:none;cursor:pointer}
.conv[aria-current="page"]{background:var(--blue-light)}
.state-cta{display:inline-flex;width:auto;align-items:center;justify-content:center;text-decoration:none}

/* O anel é global porque foco não é ornamento de componente: qualquer alvo
   novo precisa nascer localizável pelo teclado sem entrar numa allowlist. */
:where(a,button,input,textarea,select):focus-visible{outline:2px solid var(--blue);outline-offset:2px}
/* Alvo de foco PROGRAMÁTICO (o `<main>` que o router foca ao trocar de tela, a
   seção que `?focus=` foca na Performance): ninguém chega nele pelo Tab, e o
   anel do navegador em volta da tela inteira era o primeiro pixel do app em
   todo cold load. O anel dos controles continua na regra acima. */
[tabindex="-1"]:focus{outline:0}

/* Alvos pequenos encontrados na auditoria. O desenho interno continua leve;
   a caixa clicável é que chega ao piso de 44px. */
.tab{min-height:44px;display:inline-flex;align-items:center;justify-content:center}
.voltar{min-width:44px;justify-content:center}
.ligar{min-width:44px;justify-content:center}
.tform .chip button{min-width:44px;justify-content:center}
.semnada .cta{min-height:44px}
/* Pílulas pequenas por desenho: a caixa TOCÁVEL cresce por pseudo-elemento, o
   desenho não. */
.ag-conversa,.pulse .cta{position:relative}
.ag-conversa::before,.pulse .cta::before{content:"";position:absolute;left:0;right:0;top:50%;height:44px;transform:translateY(-50%)}
.tocar{min-width:44px;width:44px;height:44px}
.compositor button{width:44px;height:44px}
.ibusca-x{min-width:44px;min-height:44px;justify-content:center}

/* Tablet: espaço ganho vira composição, não apenas linhas mais compridas. */
@media (min-width:600px){
  #app:not(.no-nav){max-width:840px}
  #app.no-nav{max-width:560px;border-left:1px solid var(--line);border-right:1px solid var(--line);box-shadow:0 0 40px var(--sombra-coluna)}
  .login{padding:var(--s-8)}
  .nav,.compositor{width:min(100%,840px)}

  .p-main{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--s-6);padding:var(--s-7)}
  .p-main>.p-hd,.p-main>.p-chips,.p-main>.erro{grid-column:1/-1}
  .p-main>.p-hd,.p-main>.p-chips{padding:0}
  .p-main>.card,.p-main>.p-card{margin:0}

  #app.profile-page{background:var(--soft)}
  .profile-main{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));align-content:start;gap:var(--s-6);padding:var(--s-7)}
  .profile-main>.header{grid-column:1/-1;padding:0}
  .profile-main>.secao{min-width:0;margin:0;padding:var(--s-6);border:1px solid var(--p-hair);border-radius:var(--r-p);background:#fff;box-shadow:var(--p-sh)}
  .profile-main .secao>.note{margin-left:0;margin-right:0}

  /* Em tablet/desktop o calendário ganha densidade, não células gigantes. */
  .ag-cal{width:calc(100% - (2 * var(--s-gutter)));max-width:760px;margin-left:auto;margin-right:auto}
  .ag-cel{height:56px;aspect-ratio:auto}
}

/* Tablet amplo: Inbox acompanha a composição expandida das outras abas. Em
   768–1023px a navegação ainda é inferior, mas lista e conversa já cabem lado
   a lado; manter só a lista nessa faixa fazia Inbox parecer um telefone
   esticado enquanto Results já aproveitava a largura disponível. */
@media (min-width:768px){
  .inbox-shell,.inbox-shell.thread-view{
    display:grid;
    grid-template-columns:minmax(280px,40%) minmax(0,1fr);
    height:calc(100dvh - 84px - env(safe-area-inset-bottom));
    min-height:calc(100dvh - 84px - env(safe-area-inset-bottom));
    background:var(--soft)
  }
  .inbox-list-pane{min-width:0;overflow:auto;overscroll-behavior:contain;background:#fff;border-right:1px solid var(--line)}
  .inbox-list-view>.inbox-list-pane{height:100%}
  .inbox-empty-pane{display:flex;min-width:0;align-items:center;justify-content:center;flex-direction:column;gap:var(--s-4);padding:var(--s-8);text-align:center;color:var(--ts)}
  .inbox-empty-pane>i{font-size:var(--i-lg);color:var(--blue)}
  .inbox-empty-pane h2{font-size:var(--t-title)}
  .inbox-empty-pane p{max-width:420px;font-size:var(--t-meta);line-height:1.5}

  .inbox-sidebar{display:block;height:100%}
  .inbox-sidebar-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);padding:var(--s-7) var(--s-gutter) var(--s-6)}
  .inbox-sidebar-head h2{font-size:var(--t-title)}
  .inbox-sidebar-head a{min-height:44px;display:inline-flex;align-items:center;color:var(--blue-deep);font-size:var(--t-meta);font-weight:600;text-decoration:none}
  .inbox-sidebar .conv{padding-left:var(--s-5);padding-right:var(--s-5)}
  .thread-pane{height:100%;overflow:hidden;background:var(--thread-bg)}
  .thread-pane>.shead{flex:none;background:#fff;border-bottom:1px solid var(--line)}
  .thread-pane>.thread{min-height:0;overflow:auto;padding-bottom:var(--s-5)}
  .thread-pane>.compositor{position:sticky;bottom:0;left:auto;right:auto;width:100%;margin:0;flex:none}
  .thread-pane>.compositor-erro{flex:none;margin:0 var(--s-gutter) var(--s-5)}
}

/* Desktop: rail + área de trabalho. A nav deixa de disputar altura com o
   conteúdo e a Inbox mantém contexto enquanto a conversa está aberta. */
@media (min-width:1024px){
  #app:not(.no-nav){max-width:1280px;display:grid;grid-template-columns:88px minmax(0,1fr);align-content:start;padding-bottom:0;border-left:1px solid var(--line);border-right:1px solid var(--line);box-shadow:0 0 40px var(--sombra-coluna)}
  #app:not(.no-nav)>:not(.nav){grid-column:2;min-width:0}
  .nav{position:sticky;top:0;bottom:auto;left:auto;right:auto;grid-column:1;grid-row:1/span 100;align-self:start;width:88px;height:100dvh;margin:0;flex-direction:column;justify-content:center;gap:var(--s-4);padding:max(var(--s-5),env(safe-area-inset-top)) var(--s-4) max(var(--s-5),env(safe-area-inset-bottom));border-top:0;border-right:1px solid var(--p-hair)}
  .nav a{flex:none;width:100%;min-height:72px}

  .results-page .shead,.r-layout{padding-left:var(--s-8);padding-right:var(--s-8)}

  .inbox-shell,.inbox-shell.thread-view{display:grid;grid-template-columns:minmax(300px,360px) minmax(0,1fr);height:100dvh;min-height:100dvh;background:var(--soft)}
  .inbox-list-pane{min-width:0;overflow:auto;overscroll-behavior:contain;background:#fff;border-right:1px solid var(--line)}
  .inbox-list-view>.inbox-list-pane{height:100dvh}
  .inbox-empty-pane{display:flex;min-width:0;align-items:center;justify-content:center;flex-direction:column;gap:var(--s-4);padding:var(--s-8);text-align:center;color:var(--ts)}
  .inbox-empty-pane>i{font-size:var(--i-lg);color:var(--blue)}
  .inbox-empty-pane h2{font-size:var(--t-title)}
  .inbox-empty-pane p{max-width:420px;font-size:var(--t-meta);line-height:1.5}

  .inbox-sidebar{display:block;height:100dvh}
  .inbox-sidebar-head{display:flex;align-items:center;justify-content:space-between;gap:var(--s-5);padding:var(--s-7) var(--s-gutter) var(--s-6)}
  .inbox-sidebar-head h2{font-size:var(--t-title)}
  .inbox-sidebar-head a{min-height:44px;display:inline-flex;align-items:center;color:var(--blue-deep);font-size:var(--t-meta);font-weight:600;text-decoration:none}
  .inbox-sidebar .conv{padding-left:var(--s-5);padding-right:var(--s-5)}
  .thread-pane{height:100dvh;overflow:hidden;background:var(--thread-bg)}
  .thread-pane>.shead{flex:none;background:#fff;border-bottom:1px solid var(--line)}
  .thread-pane>.thread{min-height:0;overflow:auto;padding-bottom:var(--s-5)}
  .thread-pane>.compositor{position:sticky;bottom:0;left:auto;right:auto;width:100%;margin:0;flex:none}
  .thread-pane>.compositor-erro{flex:none;margin:0 var(--s-gutter) var(--s-5)}
}

/* Inbox pagination actions must clear the fixed navigation at narrow/tablet widths. */
[data-conversation-pages]{padding:var(--s-4) 0 calc(96px + env(safe-area-inset-bottom))}

/* --- TOPO (ADR-0300): menu à esquerda, avatar abre o Profile, recorte do
   aparelho respeitado. Prova de geometria: tools/topo-sem-colisao.proof.mjs */
.menu-botao,.menu-painel{right:auto;left:var(--s-gutter)}
@media (min-width:1024px){
  .menu-botao,.menu-painel{left:calc(88px + var(--s-gutter))}
}
/* Cabeçalhos reservam a faixa do botão; os seletores longos vencem paddings mais específicos. */
.p-hd,.header,.shead,.results-page .shead,.inbox-sidebar-head{
  padding-left:calc(var(--s-gutter) + 44px + var(--s-4))}
.header,.shead,.results-page .shead,.inbox-sidebar-head{padding-top:calc(var(--s-7) + env(safe-area-inset-top))}
.p-hd{padding-top:calc(var(--s-5) + env(safe-area-inset-top))}
.p-main>.p-hd,.profile-main>.header{padding-left:calc(var(--s-gutter) + 44px + var(--s-4))}
/* No telefone, o próximo bloco deve começar abaixo do alvo fixo de 44px. */
@media (max-width:599px){
  .profile-main>.header{min-height:calc(var(--s-5) + 44px + var(--s-4) + env(safe-area-inset-top))}
}
/* Conversa estreita: o voltar ocupa o lugar do menu. */
.thread-pane>.shead{padding-left:var(--s-7)}
.inbox-shell.thread-view~.menu-botao{display:none}
@media (min-width:768px){
  .inbox-shell.thread-view~.menu-botao{display:flex}
}
/* Alvo de 44px sobre o desenho de 32. */
.p-hd a.av{position:relative;text-decoration:none}
.p-hd a.av::after{content:"";position:absolute;inset:-6px;border-radius:var(--r-pill)}
/* #3126: valor e mês sob os gráficos da Performance. */
.eixo{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;list-style:none;margin:var(--s-2) 0 0;padding:0;font-size:var(--t-micro);line-height:1.2;color:var(--ts);text-align:center}
.eixo b{display:block;color:var(--navy);font-weight:600}
.eixo b.empty{color:var(--ts)}
.eixo i{display:block;font-style:normal}
.eixo-linha{display:flex;justify-content:space-between}
/* #3125: alerta de lead esperando ocupa a largura da Performance. */
.r-layout>.r-alerta{grid-column:1/-1}

/* Mobile Home: keep connection status from squeezing title words (#3265). */
@media (max-width:599px){
  .mission-home>.p-hd{display:grid;grid-template-columns:minmax(0,1fr) 32px;gap:var(--s-4) var(--s-5);align-items:start}
  .mission-home>.p-hd .dir{display:contents}
  .mission-home>.p-hd .vivo{grid-column:1/-1;grid-row:2;justify-self:start}
  .mission-home>.p-hd .av{grid-column:2;grid-row:1}
}

/* #3453: controls scoped to the onboarding selectors. */
.onboarding-selector{border:0;padding:0;margin:0;min-width:0;display:grid;gap:var(--s-3);color:var(--navy-fade)}
.onboarding-selector legend{margin-bottom:var(--s-4);color:#fff;font-size:var(--t-body);font-weight:500}
.onboarding-day{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s-2);align-items:center}
.onboarding-times{grid-column:1/-1;display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);gap:var(--s-3)}
.onboarding-times[hidden]{display:none}
.onboarding-selector input,.onboarding-selector select{min-width:0;max-width:100%;box-sizing:border-box}
.onboarding-selector input[type=time],.onboarding-selector input[type=search]{font-family:var(--sans);font-size:var(--t-body);color:var(--navy);background:#fff;border:0;border-radius:var(--r-sm);padding:var(--s-5) var(--s-3);width:100%}
.onboarding-selector button:not([data-remove-city]){font-family:var(--sans);font-size:var(--t-meta);color:var(--navy);background:#fff;border:0;border-radius:var(--r-sm);padding:var(--s-4);min-height:44px;cursor:pointer}
.onboarding-profile{display:grid;gap:var(--s-2)}
.onboarding-selector [data-city-results]{display:grid;gap:var(--s-2)}
.onboarding-selector [data-selector-error]:empty,.onboarding-selector [data-city-status]:empty{display:none}
