/* ==========================================================
   LANDING PÚBLICA — CSS PRÓPRIO, SEPARADO DO PRODUTO
   Não compartilha nada com design-variables.css/buttons.css/etc
   (a camada usada em base.html/base_public.html) — landing e produto
   são disciplinas visuais diferentes de propósito. Ver
   templates/landing.html.

   Paleta: NENHUM neutro é cinza. Todos carregam matiz — a página em quente
   (26-30 graus, a família do laranja da marca), os frames de produto e as
   sombras em frio. Ver o bloco de tokens em :root e
   tests/test_neutros_com_matiz.py.
========================================================== */

    :root {
      /* MEDIDO no navegador antes de mudar: a página inteira renderizava com
         luminância 12,7 (0-255) — 43% mais clara que o #08090A da Linear. E a
         causa não era só o token: #090A0C sozinho dá 9,9, e o GRÃO levantava
         os 2,8 restantes. Ruído branco sobre preto só clareia — ele tinha
         virado um véu cinza de 3% cobrindo a página inteira.
         Duas correções, então, e as duas são necessárias: o token desce para
         luminância 6, e o grão passa a modular em torno do fundo em vez de
         somar por cima (ver body::after).
         O ganho não é o fundo em si — é a AMPLITUDE: do piso 6 ao topo do
         card 27 são vinte e um pontos, contra os antigos onze. É por isso que
         o branco salta. */
      /* ---------- neutros COM MATIZ ----------

         Todo neutro da página era dessaturado — 59 usos de branco puro em
         alfa, 14 de preto puro, e os cinzas #FAFAFA e #95989F. Cinza puro é
         a ausência de decisão: ele não pertence a lugar nenhum, e é por isso
         que o escuro ficava morto enquanto o do Raycast parece aceso.

         A escala abaixo é a MESMA LUMINÂNCIA de antes, com um traço de matiz
         quente (26-30 graus, a família do laranja da marca). Manter a
         luminância é o que garante que nenhum contraste caia: as
         substituições foram calculadas uma a uma e medidas ponto a ponto
         depois — a tabela está em tests/test_neutros_com_matiz.py.

         A saturação é graduada por FUNÇÃO, não uniforme:
           superfícies escuras .3-.4  (matiz imperceptível em luminância baixa)
           bordas             ~.24    (não carregam leitura, podem ter mais)
           texto secundário   ~.11
           texto principal    ~.03    (acima disso vira papel velho)

         O que impede a página de virar sépia é a SOMBRA FRIA: luz quente
         produz sombra fria, e é essa oposição que mantém o escuro vivo em vez
         de marrom. Ver --sombra-rgb. */
      --background: #080605;
      --card: #120F0C;
      --secondary: #1E1A17;
      --foreground: #FDF9F5;
      --muted-foreground: #A69C93;

      /* Os brancos dos overlays. Três, porque a restrição é diferente em cada
         um: borda pode carregar matiz, texto não. */
      --luz-rgb: 255, 246, 236;      /* rótulos e marcadores */
      --sup-rgb: 255, 239, 226;      /* superfícies e gradientes de card */
      --borda-rgb: 255, 223, 194;    /* hairlines e bordas */

      /* A EXCEÇÃO FRIA, e ela tem motivo físico nos dois casos.

         Sombra: numa cena de luz quente, a sombra é preenchida por luz
         ambiente fria. Sombra preta pura sobre fundo quente lê como buraco;
         sombra fria lê como sombra. É o que segura o conjunto longe do sépia.

         Frame de produto: é uma TELA, com luz própria, dentro de um ambiente
         quente. Manter o interior dele frio faz a página ler como sala e o
         frame como aparelho — e dá o contraste de temperatura que uma página
         de um matiz só não tem. Ver a sobrescrita em .app. */
      --sombra-rgb: 4, 6, 12;
      --fria-rgb: 220, 230, 255;
      --accent: #F97316;
      --accent-deep: #EA580C;
      --accent-soft: #FB923C;
      --accent-pale: #FDBA74;

      /* A ponta FUNDA da rampa. Já existia no brilho em volta do CTA, onde
         não há texto por cima, mas nunca como cor de objeto.
         Agora ela é um endereço: a PERDA (ver .moment). Separar as duas
         pontas da rampa por significado — funda para o problema, clara para
         o produto medindo, arco inteiro para a ação — é o que permite
         devolver cor às seções sem que nada dispute com o CTA.
         Ver VAGAS em tests/test_hero_produto.py. */
      --brasa: #C2410C;
      --brasa-funda: #7C2D12;
      --whats: #25D366;
      --whats-deep: #1EA952;

      --hairline: rgba(var(--borda-rgb), .1);
      --hairline-forte: rgba(var(--borda-rgb), .18);

      /* ---------- superfície, luz e profundidade ----------

         O problema que estes tokens resolvem: --background (#090A0C) e
         --card (#0E0F12) tinham DOIS pontos de luminosidade de diferença. O
         card não se destacava do fundo por tom nenhum — existia só pela
         borda de 10% de branco. Enquanto havia laranja espalhado, a cor
         disfarçava; sem ela, a página virou um retângulo quase preto com
         caixas 2% mais claras.

         A riqueza aqui vem de LUMINOSIDADE, não de matiz: um gradiente
         vertical em branco de alfa baixo por cima da cor base, como se a luz
         viesse de cima. O topo do card fica ~#1B1C1F contra os #090A0C da
         página — dezoito pontos, não dois. */
      --sup-card: linear-gradient(180deg, rgba(var(--sup-rgb), .055), rgba(var(--sup-rgb), .015));
      --sup-alta: linear-gradient(180deg, rgba(var(--sup-rgb), .085), rgba(var(--sup-rgb), .025));

      /* A aresta superior captando luz. É o detalhe mais característico
         dessas interfaces, e o mais fácil de exagerar: acima de ~7% vira
         aquele brilho plástico de botão de sistema antigo. Só no topo, e só
         em superfície que é card de verdade — nunca em chip, pílula ou
         botão. */
      --luz-topo: inset 0 1px 0 rgba(var(--sup-rgb), .06);

      /* Sombras em DUAS camadas: uma curta de contato, que ancora o objeto
         na superfície, e uma longa e muito difusa, que dá o plano. Pretas
         puras e de alfa baixo — sombra que se anuncia é sombra errada. */
      --sombra-1: 0 1px 2px rgba(var(--sombra-rgb), .45);
      --sombra-2: 0 1px 2px rgba(var(--sombra-rgb), .5), 0 12px 30px -16px rgba(var(--sombra-rgb), .8);
      --sombra-3: 0 2px 6px rgba(var(--sombra-rgb), .5), 0 28px 60px -24px rgba(var(--sombra-rgb), .9);

      /* Escala neutra criada quando o laranja deixou de ser decoração.
         REGRA DA PÁGINA: o acento pertence à AÇÃO PRINCIPAL — o botão que
         leva ao diagnóstico, e mais nada. Um acento por tela.
         Antes havia 12 elementos laranja só na primeira dobra (eyebrow,
         metade da headline, rótulo de mock, pílula, cifra, botão falso...),
         e o CTA real não tinha prioridade visual nenhuma sobre um rótulo
         decorativo dentro de uma demonstração. Tudo isso desceu para cá. */
      /* .58 e .33 em vez de .56 e .32: o branco quente é um pouco mais
         escuro que o puro, e a compensação mantém a luminância composta
         idêntica à de antes (medido: .301 e .106 nos dois casos). */
      --rotulo: rgba(var(--luz-rgb), .58);
      --marcador: rgba(var(--luz-rgb), .33);
      --surface: rgba(var(--sup-rgb), .04);
      --surface-hover: rgba(var(--sup-rgb), .07);

      /* O gradiente antigo era laranja para laranja: cinco paradas, UM
         matiz. Ele mudava de claridade e nunca de temperatura, e é por isso
         que lia como plástico em vez de luz — o olho lê deslocamento de
         MATIZ como energia; deslocamento de claridade, não.
         Este atravessa quatro matizes, do âmbar queimado ao carmim.

         Todas as paradas têm luminância >= 0,235 de propósito: é o piso que
         garante 4,5:1 para a tinta escura do rótulo em qualquer ponto do
         gradiente. Os tons profundos (#C2410C, #7C2D12) foram para o BRILHO
         em volta, onde não há texto por cima. */
      --grad-cta: linear-gradient(100deg, #EA580C, #F97316 26%, #FDBA74 48%, #FB923C 66%, #FB7185 88%, #EA580C);

      /* O brilho do CTA: uma camada difusa atrás do botão, mais sombras
         coloridas em duas distâncias. É a diferença entre um objeto que tem
         cor e um objeto que EMITE — a página inteira tinha zero sombras
         coloridas contra as 39 do Raycast. */
      --bloom-acao:
        0 0 0 1px rgba(255, 214, 170, .34),
        0 2px 10px rgba(194, 65, 12, .55),
        0 14px 44px -10px rgba(249, 115, 22, .62);

      /* Inter VARIÁVEL com eixo opsz — ver o <link> em landing.html.
         O que muda: o navegador passa a interpolar o desenho por tamanho.
         Medido aqui, em 64px: a mesma frase ocupa 1065px no corte Text (o
         que a página usava até agora) e 994px no corte Display — 6,7% mais
         estreita, com contraforma mais fechada e hastes mais finas. É a
         diferença entre "Inter" e "Inter Display", e ela vem de graça:
         font-optical-sizing já é `auto` por padrão. */
      --font: "Inter", system-ui, -apple-system, sans-serif;
      --radius: 8px;
      --radius-lg: 14px;
      --radius-full: 999px;

      --shell: 1330px;
      --gutter: clamp(20px, 4vw, 56px);
      --ease-out: cubic-bezier(.22, 1, .36, 1);

      /* ---------- coreografia ----------

         O que faz o movimento da Linear parecer ensaiado e o nosso parecer
         padrão de framework não é a curva sozinha — é a REGRA de quando usar
         cada duração, e ela era uma só aqui: 300ms em tudo, expo em tudo.

         300ms é a duração errada para hover. O olho lê resposta a input como
         imediata até ~200ms; acima disso o elemento parece estar decidindo se
         obedece. Reação a ponteiro fica em 160ms; a 90ms fica o clique, que
         precisa ser tátil.

         E a curva certa depende da origem do movimento. Expo-out (--ease-saida)
         é ótima para algo que ENTRA vindo de fora: sai rápido, chega devagar,
         como um objeto com massa freando. Aplicada a um hover ela gasta 80%
         da duração nos últimos 10% do caminho — o elemento "chega" cedo e
         fica se ajustando, que é a sensação de borracha. Mudança de estado
         pede --ease-estado: decidida no começo, assentando no fim.

         A terceira regra é ASSIMETRIA. Entrar é resposta a uma intenção do
         usuário e tem que ser rápido; sair não é resposta a nada e pode
         demorar. Onde importa (cards com tilt, CTA), a volta usa --dur-volta,
         mais longa que a ida. Isso é o que dá a impressão de peso. */
      --dur-toque: 90ms;
      --dur-rapida: 160ms;
      --dur-media: 260ms;
      --dur-volta: 420ms;
      --dur-entrada: 640ms;

      /* As duas demonstrações têm uma linha do tempo própria, ESPELHADA em
         landing.js (TRAVEL_MS = 1050). Ficam fora da escala acima de
         propósito: não são resposta a gesto nenhum, são a duração de uma
         cena. Mudar qualquer uma destas sem mudar a constante equivalente no
         JS desencontra o card do espaço que abre para recebê-lo. */
      --demo-viagem: 1.05s;
      --demo-encaixe: .5s;
      --demo-gesto: .35s;
      --ease-saida: cubic-bezier(.16, 1, .3, 1);
      --ease-estado: cubic-bezier(.32, .72, 0, 1);
      --frame-progress: 0;
      --stack-spread: 0;
    }

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

    html {
      background: var(--background);
      color: var(--foreground);
      font-family: var(--font);
      /* Redundante com o padrão do navegador, e escrito assim de propósito:
         é o que faz o corte Display aparecer nos títulos e o corte Text no
         corpo. Se alguém puser `none` aqui, a página inteira volta ao
         desenho antigo sem nenhum outro sintoma. */
      font-optical-sizing: auto;
      scroll-behavior: smooth;
    }

    body {
      margin: 0;
      min-width: 320px;
      background: var(--background);
      color: var(--foreground);
      overflow-x: clip;
      font-weight: 400;
      -webkit-font-smoothing: antialiased;
    }


    /* ---------- grão ----------

       O que impede o fundo escuro de virar preto morto. Ruído gerado por
       feTurbulence num SVG inline (data URI, ~400 bytes) — sem arquivo, sem
       requisição. feColorMatrix saturate=0 tira a cor que o feTurbulence
       produz por padrão: colorido, ele mancharia o fundo de roxo e verde.

       Fixo na viewport e não no documento: rolando, o grão fica parado como
       grão de filme, em vez de deslizar junto e virar textura de papel de
       parede.

       3% é o teto. Acima disso lê como sujeira, e em tela OLED começa a
       aparecer banda nas áreas de preto puro. z-index 50: acima das seções
       (10), abaixo da barra (200) e do flutuante (300).

       ATENÇÃO ao mexer nesta opacidade: ruído branco em composição normal só
       CLAREIA. A conta é result = 0,97·fundo + 3,8, ou seja, este véu levanta
       o piso da página inteira em ~2,8 pontos de luminância, independente do
       fundo. --background está calibrado CONTANDO com isso: 6 no token + 2,8
       do grão = os ~8,6 que aparecem na tela, que é o preto da Linear.
       Subir o grão sem baixar o token clareia a página de novo — foi
       exatamente o que tinha acontecido. */
    body::after {
      content: "";
      position: fixed;
      inset: 0;
      z-index: 50;
      pointer-events: none;
      opacity: .03;

      /* O grão também era cinza puro — um véu dessaturado sobre a página
         inteira, e portanto a maior superfície neutra que existia aqui. O
         ruído continua monocromático (feColorMatrix saturate=0), mas agora
         ele é a MÁSCARA de uma cor quente em vez de ser a cor: o que chega na
         tela é grão âmbar, não grão cinza. Mesmo custo, mesma textura. */
      background-color: rgb(var(--sup-rgb));
      mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
      -webkit-mask-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.82' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23g)'/%3E%3C/svg%3E");
      mask-size: 160px 160px;
      -webkit-mask-size: 160px 160px;
    }

    a { color: inherit; text-decoration: none; }
    button, a { -webkit-tap-highlight-color: transparent; font-family: inherit; }
    :focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: var(--radius); }

    h1, h2, h3 { margin: 0; font-weight: 600; letter-spacing: -.04em; line-height: 1.05; }
    p { margin: 0; }

    .shell { width: 100%; max-width: var(--shell); margin-inline: auto; padding-inline: var(--gutter); }

    .sr-only {
      position: absolute; width: 1px; height: 1px;
      padding: 0; margin: -1px; overflow: hidden;
      clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
    }

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

    .btn {
      display: inline-flex;
      align-items: center;
      justify-content: center;
      gap: 10px;
      min-height: 50px;
      padding: 14px 24px;
      border: none;
      border-radius: var(--radius-full);
      font-size: 16px;
      font-weight: 600;
      letter-spacing: -.01em;
      cursor: pointer;
      /* "background" fica fora da lista de propriedades transicionadas de
         propósito: os CTAs animados (.btn--primary/.btn--whats) já animam
         background-position via @keyframes — misturar os dois criaria
         disputa entre a transição e o keyframe no hover. */
      transition: filter var(--dur-rapida) var(--ease-estado), transform var(--dur-rapida) var(--ease-estado), box-shadow var(--dur-rapida) var(--ease-estado), border-color var(--dur-rapida) var(--ease-estado);
    }

    /* O press é a única coisa na página mais rápida que o hover, e tem que
       ser: 160ms num clique já lê como atraso do sistema, não como resposta
       ao dedo. 90ms é o limite onde o retorno parece simultâneo à mão. */
    .btn:active { transform: scale(.985); transition-duration: var(--dur-toque); }

    /* CTA primário: gradiente laranja em loop contínuo e suave — nunca
       estático, é o elemento de maior hierarquia visual da página (junto
       com .btn--whats, que tem o próprio verde de marca). */
    .btn--primary {
      position: relative;
      /* Cria o contexto de empilhamento para o ::before do brilho: sem ele, o
         z-index negativo escaparia e o brilho iria parar atrás do fundo da
         SEÇÃO, sumindo de vez. */
      isolation: isolate;
      background: var(--grad-cta);
      background-size: 240% 240%;
      /* Tinta escura, não branca, e isto conserta uma falha que existia desde
         o começo: branco sobre #FDBA74 dá 1,9:1 e sobre #F97316 dá 2,8:1 — o
         rótulo do botão mais importante da página nunca passou de AA. Com
         #2A1102 o pior ponto do gradiente fica em 4,85:1.
         De quebra é o que faz o botão ler como ACESO em vez de colorido: luz
         forte com marca escura por cima, não superfície escura com texto
         claro. */
      color: #2A1102;
      box-shadow: var(--bloom-acao);
      animation: btn-gradient-shift 8s ease-in-out infinite;
    }

    /* O bloom propriamente dito: uma cópia desfocada e maior atrás do botão.
       Só a parte que sobra para FORA da caixa aparece — o que fica sob o
       botão é coberto pelo fundo dele. */
    .btn--primary::before {
      content: "";
      position: absolute;
      z-index: -1;
      inset: -22px -26px -26px;
      border-radius: inherit;
      background: radial-gradient(52% 52% at 50% 52%, rgba(249, 115, 22, .62), rgba(194, 65, 12, .28) 55%, transparent 74%);
      filter: blur(18px);
      pointer-events: none;
    }

    .btn--primary:hover {
      filter: brightness(1.08) saturate(1.06);
      transform: translateY(-1px);
      box-shadow:
        0 0 0 1px rgba(255, 224, 190, .5),
        0 3px 14px rgba(194, 65, 12, .6),
        0 20px 56px -10px rgba(249, 115, 22, .78);
    }
    .btn--primary::after { content: "→"; font-size: 17px; }

    /* Secundário: sem movimento contínuo — só responde ao hover/toque. */
    .btn--ghost {
      background: rgba(var(--sup-rgb), .03);
      border: 1px solid var(--hairline);
      color: var(--foreground);
    }
    .btn--ghost:hover {
      background: var(--surface-hover);
      border-color: var(--hairline-forte);
      transform: translateY(-2px);
      box-shadow: 0 10px 24px rgba(var(--sombra-rgb), .35);
    }

    /* CTA de WhatsApp: superfície neutra com o GLIFO verde, igual ao botão
       flutuante. Era um bloco verde cheio com gradiente animado e glow
       pulsando — um terceiro sistema de cor competindo com o laranja da ação
       principal, para uma ação secundária. O WhatsApp é reconhecido pelo
       ícone, não por um botão inteiro pintado. */
    .btn--whats {
      border: 1px solid var(--hairline-forte);
      border-top-color: rgba(var(--borda-rgb), .26);
      background: var(--surface);
      color: var(--foreground);
      transition:
        background var(--dur-rapida) var(--ease-estado),
        border-color var(--dur-rapida) var(--ease-estado),
        transform var(--dur-rapida) var(--ease-estado);
    }
    .btn--whats:hover {
      background: var(--surface-hover);
      border-color: rgba(37, 211, 102, .4);
      transform: translateY(-1px);
    }

    /* Só o ícone carrega o verde. */
    .btn--whats .btn-icon-whats { color: var(--whats); }

    .btn--sm { min-height: 42px; padding: 10px 18px; font-size: 14px; }

    .btn-icon-whats { width: 18px; height: 18px; flex: none; }

    @keyframes btn-gradient-shift {
      0%, 100% { background-position: 0% 50%; }
      50% { background-position: 100% 50%; }
    }

    /* ---------- nav ---------- */

    /* Os dois estados da barra vivem em variáveis, e só nelas. O bloco .nav
       abaixo lê as variáveis; .is-scrolled só as reescreve. Sem isso, cada
       ajuste de "como fica ao rolar" viraria uma segunda cópia inteira da
       regra, e as duas sairiam de sincronia na primeira manutenção. */
    .nav-wrap {
      --nav-gap-topo: 20px;
      --nav-pad-y: 11px;
      --nav-vidro: rgba(18, 15, 12, .34);
      --nav-blur: blur(20px) saturate(150%);
      --nav-borda: rgba(var(--borda-rgb), .07);
      --nav-sombra: 0 4px 16px rgba(var(--sombra-rgb), .10);
      --nav-marca: 34px;

      position: fixed;
      inset: 0 0 auto;
      z-index: 200;
      padding: var(--nav-gap-topo) var(--gutter) 0;
      pointer-events: none;
      transition: padding-top var(--dur-media) var(--ease-out);
    }

    /* Rolou: a barra encolhe, o vidro fica mais denso e a borda ganha
       presença. Nenhum valor salta — são os mesmos tokens, deslocados. */
    .nav-wrap.is-scrolled {
      --nav-gap-topo: 12px;
      --nav-pad-y: 7px;
      --nav-vidro: rgba(18, 15, 12, .58);
      --nav-blur: blur(30px) saturate(190%);
      --nav-borda: rgba(var(--borda-rgb), .13);
      --nav-sombra: 0 10px 34px rgba(var(--sombra-rgb), .38);
      --nav-marca: 30px;
    }

    .nav {
      display: flex;
      align-items: center;
      justify-content: space-between;
      gap: clamp(20px, 3vw, 44px);
      width: 100%;
      max-width: var(--shell);
      margin-inline: auto;
      padding: var(--nav-pad-y) var(--nav-pad-y) var(--nav-pad-y) 22px;
      border: 1px solid var(--nav-borda);
      border-radius: var(--radius-full);
      /* Translucidez de verdade: 34% no topo deixa o gradiente do hero passar
         BORRADO por trás, que é o que faz o vidro parecer vidro. A versão
         anterior tinha 72% de opacidade — o blur existia e não aparecia,
         porque quase nada atravessava para ser borrado.
         saturate() junto do blur é o que separa "vidro" de "fumaça": sem ele,
         a cor que atravessa chega lavada e a barra vira um cinza chapado. */
      background: var(--nav-vidro);
      backdrop-filter: var(--nav-blur);
      -webkit-backdrop-filter: var(--nav-blur);
      box-shadow: var(--nav-sombra);
      pointer-events: auto;
      transition:
        padding var(--dur-media) var(--ease-out),
        background var(--dur-media) var(--ease-out),
        backdrop-filter var(--dur-media) var(--ease-out),
        -webkit-backdrop-filter var(--dur-media) var(--ease-out),
        border-color var(--dur-media) var(--ease-out),
        box-shadow var(--dur-media) var(--ease-out);
    }

    /* Sem suporte a backdrop-filter (Firefox antigo, alguns Android), o fundo
       translúcido deixaria o texto da barra sobre o hero sem nenhuma
       separação. Aí, e só aí, o fundo volta a ser quase opaco. */
    @supports not (backdrop-filter: blur(1px)) {
      .nav { background: rgba(18, 15, 12, .88); }
      .nav__panel { background: rgba(18, 15, 12, .97); }
    }

    /* A marca virou arquivo, não composição de CSS. O bloco antigo desenhava
       um quadrado com gradiente e a letra "N" dentro — um placeholder, nunca
       o logo. Com a assinatura oficial em SVG não há mais nada pra estilizar
       além do tamanho: cor, espaçamento e proporção vêm de dentro do arquivo,
       que é justamente o ponto de ter um kit de marca. */
    .nav__brand { display: inline-flex; align-items: center; }

    /* width/height já vêm no HTML (evitam o pulo de layout). A altura fixa
       aqui é o que manda de verdade quando a barra encolhe no mobile — o
       width:auto deixa a proporção livre pra acompanhar. */
    .nav__brand img {
      display: block;
      height: var(--nav-marca);
      width: auto;
      transition: height var(--dur-media) var(--ease-out);
    }

    /* Respiro. O gap era 2px com padding lateral de 12px — os links quase se
       encostavam e a barra lia como uma lista, não como navegação. Barra
       desse tipo erra por excesso de espaço, nunca por falta. */
    .nav__links { display: flex; align-items: center; gap: 6px; }

    /* 14px contra os 16px da marca e os 14px do CTA: os links são o nível
       mais baixo da hierarquia da barra, e o hero é que manda na tela. O
       pill de fundo no hover saiu — era ele que dava a todos os itens o
       mesmo peso e deixava a barra com cara de template. */
    .nav__links a {
      position: relative;
      padding: 9px 18px;
      color: var(--muted-foreground);
      font-size: 14px;
      font-weight: 500;
      letter-spacing: -.005em;
      white-space: nowrap;
      transition: color var(--dur-rapida) var(--ease-estado);
    }

    /* Traço da seção ativa. Escala no eixo X em vez de aparecer e sumir:
       escala anima na GPU e não reflui a barra. Fica no ::after pra não
       depender de borda, que somaria altura ao link. */
    .nav__links a::after {
      content: "";
      position: absolute;
      left: 18px;
      right: 18px;
      bottom: 1px;
      height: 1.5px;
      border-radius: 2px;
      background: var(--foreground);
      opacity: 0;
      transform: scaleX(.35);
      transition:
        transform var(--dur-media) var(--ease-saida),
        opacity var(--dur-rapida) var(--ease-estado);
    }

    /* Hover mostra o traço apagado — prévia do estado ativo, não um estado
       concorrente. Ativo confirma em cor cheia. */
    .nav__links a:hover { color: var(--foreground); }
    .nav__links a:hover::after { opacity: .3; transform: scaleX(1); }

    .nav__links a.is-active { color: var(--foreground); }
    .nav__links a.is-active::after { opacity: 1; transform: scaleX(1); }

    /* CTA da barra: SÓLIDO NEUTRO, não o gradiente da marca.
       Ele e o CTA do hero tinham exatamente o mesmo tratamento — duas
       pílulas laranja com sombra brilhante, uma a 700px da outra, e no mobile
       as duas na mesma tela. Tratamento idêntico para uma utilidade
       permanente e para a ação principal da página significa que nenhuma das
       duas é a principal.
       Aqui ele é uma superfície clara sobre vidro escuro: continua sendo o
       elemento mais sólido da barra e o mais fácil de acertar, sem gastar o
       acento que agora pertence a um botão só. */
    .nav__cta {
      /* inline-flex + min-height: o padding sozinho dava ~38px de altura, e
         no mobile este é o único CTA visível na barra (os links somem). 44px
         é o mínimo confortável pra um alvo de toque. */
      display: inline-flex;
      align-items: center;
      min-height: 44px;
      padding: 9px 18px;
      border-radius: var(--radius-full);
      border: 1px solid rgba(var(--borda-rgb), .14);
      background: rgba(var(--sup-rgb), .10);
      color: var(--foreground);
      font-size: 14px;
      font-weight: 600;
      letter-spacing: -.01em;
      white-space: nowrap;
      transition:
        background var(--dur-rapida) var(--ease-estado),
        border-color var(--dur-rapida) var(--ease-estado),
        transform var(--dur-rapida) var(--ease-estado);
    }

    .nav__cta:hover {
      background: rgba(var(--sup-rgb), .17);
      border-color: rgba(var(--borda-rgb), .24);
      transform: translateY(-1px);
    }

    .nav__cta:active { transform: scale(.985); transition-duration: var(--dur-toque); }

    /* ---------- ações da barra (menu + CTA) ---------- */

    .nav__actions { display: flex; align-items: center; gap: 8px; }

    /* Botão do menu: só existe abaixo de 780px. 44x44 de área de toque, mas
       desenho discreto — duas linhas curtas, sem caixa nem borda, porque o
       CTA ao lado é o único elemento da barra que deve pedir clique. */
    .nav__toggle {
      display: none;
      position: relative;
      width: 44px;
      height: 44px;
      padding: 0;
      border: 0;
      border-radius: var(--radius-full);
      background: transparent;
      color: var(--foreground);
      cursor: pointer;
      transition: background var(--dur-rapida) var(--ease-estado);
    }

    .nav__toggle:hover { background: var(--surface); }

    .nav__toggle-bar {
      position: absolute;
      left: 50%;
      width: 17px;
      height: 1.5px;
      margin-left: -8.5px;
      border-radius: 2px;
      background: currentColor;
      /* transform-origin no centro: as duas barras giram sobre o mesmo eixo e
         o X sai simétrico sem precisar corrigir posição depois. */
      transition:
        transform var(--dur-media) var(--ease-saida),
        opacity var(--dur-rapida) var(--ease-saida);
    }

    .nav__toggle-bar:nth-child(1) { top: 18px; }
    .nav__toggle-bar:nth-child(2) { bottom: 18px; }

    /* Aberto: as duas barras andam até o meio e cruzam. */
    .nav-wrap.is-menu-aberto .nav__toggle-bar:nth-child(1) {
      transform: translateY(4.25px) rotate(45deg);
    }

    .nav-wrap.is-menu-aberto .nav__toggle-bar:nth-child(2) {
      transform: translateY(-4.25px) rotate(-45deg);
    }

    /* ---------- painel do mobile ---------- */

    /* Mesma família de vidro da barra, um degrau mais denso: ele cobre
       conteúdo em movimento e precisa de leitura garantida. */
    .nav__panel {
      display: none;
      max-width: var(--shell);
      margin: 10px auto 0;
      padding: 10px;
      border: 1px solid var(--nav-borda);
      border-radius: var(--radius-lg);
      /* Mais denso e mais borrado que a barra (.58 / 30px) de propósito. A
         barra é uma moldura e pode deixar a página passar; o painel é uma
         superfície de LEITURA, com quatro alvos de toque — e o que passa por
         trás dele é a headline do hero, o maior texto da página. Com o mesmo
         vidro da barra, os rótulos do menu disputavam atenção com ela.
         Continua sendo vidro: a cor e a luz do fundo atravessam, e o que
         estiver atrás aparece como fantasma borrado — só não como texto. */
      background: rgba(18, 15, 12, .89);
      backdrop-filter: blur(44px) saturate(190%);
      -webkit-backdrop-filter: blur(44px) saturate(190%);
      box-shadow: 0 18px 44px rgba(var(--sombra-rgb), .45);
      pointer-events: auto;

      /* Fechado. visibility (e não display) porque é ela que tira o painel do
         foco do teclado E permite transição — display:none cortaria a
         animação pela metade. O 0s com atraso na saída segura a visibilidade
         até o fade terminar. */
      opacity: 0;
      visibility: hidden;
      transform: translateY(-10px) scale(.98);
      transform-origin: top center;
      transition:
        opacity var(--dur-rapida) var(--ease-saida),
        transform var(--dur-media) var(--ease-saida),
        visibility 0s linear var(--dur-media);
    }

    .nav-wrap.is-menu-aberto .nav__panel {
      opacity: 1;
      visibility: visible;
      transform: translateY(0) scale(1);
      transition:
        opacity var(--dur-rapida) var(--ease-saida),
        transform var(--dur-media) var(--ease-saida),
        visibility 0s;
    }

    .nav__panel a {
      display: flex;
      align-items: center;
      min-height: 48px;
      padding: 0 16px;
      border-radius: var(--radius);
      color: var(--muted-foreground);
      font-size: 15px;
      font-weight: 500;
      transition:
        background var(--dur-rapida) var(--ease-estado),
        color var(--dur-rapida) var(--ease-estado);
    }

    .nav__panel a:hover,
    .nav__panel a:focus-visible {
      background: var(--surface);
      color: var(--foreground);
    }

    /* Mesma marcação de seção ativa da barra, traduzida para uma lista: aqui
       o traço embaixo não funcionaria, então o sinal é cor cheia mais um
       ponto do acento. */
    .nav__panel a.is-active { color: var(--foreground); }

    .nav__panel a.is-active::after {
      content: "";
      width: 5px;
      height: 5px;
      margin-left: auto;
      border-radius: var(--radius-full);
      background: var(--marcador);
    }

    /* A pílula era o único objeto do hero que BLOQUEAVA a luz em vez de
       estar dentro dela: fundo a 66% de opacidade por cima do campo de
       feixes, ou seja, um recorte escuro no meio da coisa que dá rosto à
       primeira tela. Cai para 30% com desfoque do que está atrás — o feixe
       atravessa e a pílula passa a ser vidro, não laje.

       O rótulo desce para --rotulo. O branco cheio o igualava à headline de
       66px logo abaixo, dois níveis com a mesma tinta. Medido glifo a glifo
       (Range.getClientRects, não a caixa): 5,32:1 sobre o pior pixel do
       campo. Passa AA; era 14,80:1 sobre a laje opaca. */
    .badge {
      display: inline-flex;
      align-items: center;
      gap: 9px;
      padding: 8px 16px;
      border: 1px solid rgba(251, 146, 60, .22);
      border-top-color: rgba(253, 186, 116, .30);
      border-radius: var(--radius-full);
      background: rgba(18, 15, 12, .30);
      -webkit-backdrop-filter: blur(14px) saturate(1.25);
      backdrop-filter: blur(14px) saturate(1.25);
      box-shadow: inset 0 1px 0 rgba(253, 186, 116, .10);
      color: var(--rotulo);
      font-size: 14px;
      font-weight: 500;
      letter-spacing: -.01em;
    }

    /* "24h por dia" é uma afirmação de ESTADO — a mesma que .app__ping faz
       dentro do frame, em laranja e pulsando. Aqui ela era um ponto branco a
       33% parado, que lê como bullet de lista. Agora é o mesmo sinal, com o
       mesmo significado, do lado de fora do frame. */
    .badge::before {
      content: "";
      width: 6px; height: 6px;
      border-radius: 50%;
      background: var(--accent);
      box-shadow: 0 0 0 3px rgba(249, 115, 22, .16), 0 0 9px 1px rgba(249, 115, 22, .7);
      animation: pulso-vivo 2.6s var(--ease-estado) infinite;
    }

    /* Um só keyframe para os dois pontos vivos da página (aqui e na
       conclusão do Consultor). São 6px: animar box-shadow neles repinta uma
       área de ~20px², irrelevante — a lição dos feixes foi sobre filter e
       mix-blend-mode em camadas grandes, não sobre isto. */
    @keyframes pulso-vivo {
      0%, 68%, 100% { box-shadow: 0 0 0 3px rgba(249, 115, 22, .16), 0 0 9px 1px rgba(249, 115, 22, .6); }
      34%           { box-shadow: 0 0 0 6px rgba(249, 115, 22, .03), 0 0 16px 2px rgba(249, 115, 22, 1); }
    }
    .step-num {
      display: block;
      margin-bottom: 14px;
      /* Era --marcador (branco a 33%), que media 2,89:1 — reprovava AA. */
      color: var(--accent-pale);
      font-size: 15px;
      font-weight: 600;
      letter-spacing: .04em;
      font-variant-numeric: tabular-nums;
    }
    /* ---------- hero estático ----------
       Escrito a partir de 375px e expandido pra cima, não o contrário.

       A altura NÃO é 100vh: no mobile, `vh` conta a barra de endereço do
       navegador, e um hero de 100vh empurra o CTA pra fora da primeira tela
       justamente no aparelho onde ele mais importa. `min-height` com svh
       (a menor altura visível, já descontando a barra) mais um teto de
       conteúdo garante headline + subheadline + CTA acima da dobra em
       qualquer tela a partir de 375x667. */

    /* O hero era 80svh de altura com o texto centrado verticalmente e um mock
       de 527px ao lado. Numa tela de 900px ele acabava 180px antes da dobra, e
       esses 180px não eram o começo da próxima seção — eram o vazio antes
       dela. Nenhum monitor via um hero dono da primeira tela.

       Agora a altura vem do CONTEÚDO: a tese ocupa a primeira tela e o frame
       de produto começa antes da dobra e termina depois dela. Numa tela de
       900px o hero mede ~1150px, com ~290px de produto acima da dobra — e é
       esse corte, feito pela borda inferior da TELA, que convida a rolar.

       Tentei antes fixar a altura em `100svh + 250px`. Funcionava a 900px e
       abria um vão morto de 137px numa tela de 1080 — a mesma faixa vazia que
       eu tinha acabado de tirar, de volta por outra porta: o hero crescia com
       a janela, o frame tinha teto, e a diferença virava nada. Com altura
       automática a folga de baixo é só o padding, em qualquer tela. */
    .hero {
      position: relative;
      display: grid;
      grid-template-rows: auto auto;
      align-content: start;
      padding: clamp(112px, 17svh, 168px) 0 clamp(30px, 4vh, 60px);
      overflow: hidden;
      background: var(--background);
    }

    /* Fundo 100% CSS. Zero requisição de rede, zero deslocamento de layout,
       alguns bytes de CSS no lugar dos 9 MB de frames. */
    .hero__bg {
      position: absolute;
      inset: 0;
      overflow: hidden;
      /* Aqui embaixo fica só a base escura. O campo de luz são os
         .hero__feixe, elementos próprios — ver logo abaixo. */
      background: linear-gradient(180deg, #0C0906, var(--background) 62%);

      /* O hero tem overflow:hidden, então tudo aqui dentro — gradiente, halos
         e trama — é CORTADO em linha reta na borda de baixo. Com os halos
         fracos e coloridos isso não aparecia; com atmosfera de verdade, o
         corte virou uma emenda horizontal visível entre o hero e a seção
         seguinte. Dissolvendo a camada antes da borda, o que sobra embaixo é
         o próprio var(--background) do .hero — exatamente a cor com que a
         próxima seção começa. */
      mask-image: linear-gradient(180deg, #000 78%, transparent 99%);
      -webkit-mask-image: linear-gradient(180deg, #000 78%, transparent 99%);
    }

    /* ---------- o campo: a luz da marca em escala de tela ----------

       Aqui estavam três halos radiais em alfa .06-.095. Medido, o resultado
       era 3,7% de pixels cromáticos na primeira tela, dos quais só 1,1%
       laranja — menos do que o azul frio decorativo, e menos do que o balão
       verde do WhatsApp dentro do mock. A marca não aparecia na tela onde ela
       deveria mandar.

       A referência (medida no mesmo dia, mesmo script, 1440x900) é o Raycast:
       20,7% de pixels cromáticos na primeira tela e NENHUM elemento de
       interface colorido. A barra é vidro escuro, o botão é claro, o texto é
       branco. Todo o vermelho deles é uma fonte de luz do tamanho da tela,
       atrás de tudo.

       É essa a inversão: o acento continua sendo só da ação nos OBJETOS, e o
       campo passa a ser luz de verdade. Feixes diagonais com núcleo quase
       branco, borda muito difusa, um deles frio para dar a franja de prisma —
       é a franja que impede a composição de parecer um gradiente de CSS.

       Cada feixe é um elemento porque só transform e opacity animam na GPU;
       o desfoque é ESTÁTICO (filter nunca entra em keyframe: animar blur
       força repintura da camada inteira a cada quadro). */

    /* A maciez vem do PRÓPRIO gradiente, não de filter: blur().

       Medido: com blur(44px) em cinco camadas do tamanho do hero, a rolagem
       caía de 60 para 13 fps — desfoque de área grande é recomposto a cada
       quadro, e não há truque de will-change que salve. Trocando por um
       radial-gradient elíptico dimensionado pelo próprio elemento, a queda
       suave existe igual e o custo é o de pintar um gradiente: zero por
       quadro, porque só o transform anima.

       screen continua: sobre fundo quase preto ele soma luz, então dois
       feixes cruzados CLAREIAM no cruzamento em vez de um tapar o outro. Sem
       o filter, ele sai barato. */
    .hero__feixe {
      position: absolute;
      top: -55%; bottom: -55%;
      left: -6%; right: -6%;
      transform: rotate(21deg);
      background-repeat: no-repeat;
      mix-blend-mode: screen;
      pointer-events: none;

      /* A borda do próprio elemento aparecia como uma diagonal reta no alto
         à esquerda: girado 21 graus, o retângulo entra na área visível, e o
         gradiente mais à esquerda ainda não tinha chegado ao transparente
         quando a caixa acabava. A máscara garante a dissolução nas laterais
         independente de onde cada camada caia — e, sendo estática, não custa
         quadro nenhum. */
      mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
      -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
    }

    /* DOIS elementos com várias camadas de gradiente cada, e não cinco
       elementos.

       Medido, com números, porque a diferença é grande e nada intuitiva:

         5 camadas, com deriva ...... 39 fps na rolagem
         5 camadas, paradas ......... 56 fps
         3 camadas, com deriva ...... 50 fps
         2 camadas, com deriva ...... o que está aqui

       O custo não é a animação em si nem o mix-blend-mode (testei os dois
       isolados: 1 a 2 fps cada). É o NÚMERO de camadas grandes e misturadas
       vivas ao mesmo tempo: qualquer animação obriga o compositor a refazer
       todas elas a cada quadro. Empacotar os feixes como camadas de
       background dentro de dois elementos resolve sem tirar um feixe sequer
       da composição.

       Dentro de um mesmo elemento as camadas se cobrem (composição normal);
       entre os dois elementos elas se SOMAM, por causa do screen. Por isso os
       feixes que precisam cruzar e clarear no cruzamento estão em elementos
       diferentes. */
    .hero__feixe--1 {
      background-image:
        radial-gradient(50% 50% at 50% 50%, #FFC08A 0%, #F97316 24%, #C2410C 48%, transparent 76%),
        radial-gradient(50% 50% at 50% 50%, #FDBA74 0%, #F97316 26%, #C2410C 50%, transparent 78%),
        radial-gradient(50% 50% at 50% 50%, #FDBA74 0%, #F97316 22%, #C2410C 46%, transparent 78%);
      background-size: 30% 100%, 33% 100%, 36% 100%;
      background-position: -8% 50%, 34% 50%, 76% 50%;
      opacity: .72;
    }

    /* O núcleo branco e o frio. O frio sozinho não colore nada — o que ele
       faz é abrir a franja nas bordas dos quentes, e é isso que lê como luz
       atravessando alguma coisa em vez de duas cores sobrepostas. */
    .hero__feixe--2 {
      background-image:
        radial-gradient(50% 50% at 50% 50%, #FFFFFF 0%, #FFD5A8 16%, #F97316 42%, transparent 74%),
        radial-gradient(50% 50% at 50% 50%, #BFF6EF 0%, #2DD4BF 30%, transparent 72%);
      background-size: 17% 100%, 15% 100%;
      background-position: 16% 50%, 58% 50%;
      opacity: .58;
    }

    /* A LUZ NÃO SE MEXE, e isto foi uma decisão medida, não uma omissão.

       Os feixes tinham deriva de 52 e 61 segundos, deslocando 4%. Medindo a
       rolagem a 1440x900:

         feixes parados ........................ 60 fps
         feixes com deriva, sem mix-blend-mode .. 40 fps
         feixes com deriva e screen ............. 17 fps

       Não é o blend sozinho (parado, com screen, dá 60) nem a animação
       sozinha: é a combinação, porque uma camada misturada que se move obriga
       o compositor a refazer a mistura com o fundo a cada quadro, na área
       inteira dela. Testei também animar só a opacidade — 20 fps.

       Quatro por cento de deslocamento em cinquenta segundos é movimento que
       ninguém percebe. Rolagem a 17 fps é defeito que todo mundo sente. O
       movimento da primeira dobra fica por conta do gradiente do CTA, que é
       um elemento pequeno e sem blend, e do que vier depois na página. */

    /* ---------- o véu ----------

       A escolha aqui foi explícita: escurecer o FUNDO atrás do texto, e não
       clarear o texto. Clarear o cinza secundário para caber num fundo forte
       é o começo de perder a hierarquia inteira — em duas rodadas o "fraco"
       vira "médio" e a página perde um nível de leitura.

       Elipse centrada no bloco de texto (50% 31% é onde a coluna centrada
       vive), forte no meio e dissolvendo antes da borda, para os feixes
       continuarem acesos em volta. Os contrastes medidos glifo a glifo estão
       em tests/test_campo_de_luz.py. */
    .hero__veu {
      position: absolute;
      inset: 0;
      pointer-events: none;
      /* Só a lavagem de cima, que impede os feixes de estourarem atrás da
         barra de vidro. A proteção do TEXTO está em .hero__copy::before —
         medida em % da caixa do hero, ela encolhia junto com a janela e
         deixava o subtítulo em 3,82:1 a 1024x768. */
      background: linear-gradient(180deg, rgba(8, 6, 5, .5), transparent 24%);
    }

    /* Trama fina por cima do gradiente, pra o fundo não ficar "liso demais"
       numa tela grande. É um SVG inline de 40x40 em data URI (~180 bytes),
       não um arquivo. */
    .hero__bg::after {
      content: "";
      position: absolute;
      inset: 0;
      opacity: .5;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cpath d='M0 0h40v40H0z' fill='none'/%3E%3Cpath d='M0 .5h40M.5 0v40' stroke='rgba(255,239,226,.028)' stroke-width='1'/%3E%3C/svg%3E");
      mask-image: radial-gradient(75% 55% at 50% 35%, #000, transparent 78%);
      -webkit-mask-image: radial-gradient(75% 55% at 50% 35%, #000, transparent 78%);
    }

    /* O texto do hero é uma coluna centrada, não uma das duas colunas de uma
       grade. O arranjo anterior era 1,08fr de texto contra 0,92fr de mock:
       larguras quase iguais, que é exatamente o que dizia ao olho que as duas
       coisas pesavam o mesmo. O produto agora vem embaixo, em sangria, e o
       texto vira a tese sobre ele. */
    .hero__copy {
      position: relative;
      z-index: 1;
      display: grid;
      justify-items: center;
      text-align: center;
    }

    /* O VÉU do texto.

       A escolha aqui foi explícita: escurecer o fundo atrás do texto, e não
       clarear o texto. Clarear o cinza secundário para caber num fundo forte
       é o começo de perder a hierarquia inteira — em duas rodadas o "fraco"
       vira "médio" e a página perde um nível de leitura.

       Ancorado ao bloco de texto, não à seção: assim ele acompanha a coluna
       em qualquer viewport, e dissolve antes das bordas para os feixes
       continuarem acesos em volta. Os contrastes medidos linha a linha, em
       375, 1024 e 1440, estão em tests/test_campo_de_luz.py. */
    .hero__copy::before {
      content: "";
      position: absolute;
      z-index: -1;
      inset: -11% -5% -15%;
      pointer-events: none;
      /* Elipse não serve: o bloco de texto é uma COLUNA, e o quanto cada
         linha se aproxima da borda da elipse muda com a largura da janela.
         Medido, a mesma elipse dava 5,14:1 a 1440 e 4,22:1 a 1024 — o
         subtítulo passava a quebrar em mais linhas e escorregava para a
         franja clara.

         Aqui a proteção é uma FAIXA de densidade constante no miolo, com
         queda só nas laterais (o gradiente) e no topo e base (a máscara).
         O texto fica sempre no patamar plano, em qualquer viewport, e a luz
         continua acesa acima, abaixo e nos dois lados. */
      background: linear-gradient(90deg,
        transparent, rgba(8, 6, 5, .9) 10%, rgba(8, 6, 5, .9) 90%, transparent);
      mask-image: linear-gradient(180deg, transparent, #000 9%, #000 91%, transparent);
      -webkit-mask-image: linear-gradient(180deg, transparent, #000 9%, #000 91%, transparent);
    }

    /* 700 lia como material de vendas em negrito, não como editorial — a
       diferença entre 600 e 700 nesse corpo é a diferença entre confiança e
       esforço. 66px também era grande demais: a frase ocupava três linhas e
       a headline virava um bloco, não uma sentença.
       A ênfase que se perdeu ao tirar a cor volta aqui, em entrelinha (1.06)
       e no espaço acima e abaixo. */
    .hero__title {
      max-width: 19ch;
      margin-top: 22px;
      font-size: clamp(31px, 5.4vw, 60px);
      font-weight: 600;
      line-height: 1.06;
      letter-spacing: -.032em;
    }

    /* A headline era bicolor: a primeira metade em branco e "Não sabe por
       quê." em gradiente laranja. Isso partia UMA frase em dois objetos
       visuais e obrigava a reler para juntar — além de gastar o acento na
       tipografia em vez de na ação. A ênfase agora vem de tamanho,
       entrelinha e espaço, que é de onde ela deveria ter vindo desde o
       começo. O <span> saiu do HTML junto. */

    .hero__deck {
      max-width: 46rem;
      margin-top: 18px;
      font-size: clamp(16px, 1.3vw, 20px);
      line-height: 1.55;
      color: var(--muted-foreground);
    }

    /* Entrada em CSS puro, sem IntersectionObserver: o hero é a primeira
       tela e não pode depender de JS pra aparecer. Se o script falhar ou
       demorar, o conteúdo já está lá — a animação é enfeite, não gate. */
    /* O desfoque é o que separa "aparecer" de "materializar", e é a assinatura
       do movimento dessas páginas. Custa pouco aqui porque a animação NÃO é
       `forwards`: terminada, o filter volta a `none` sozinho e nenhum destes
       elementos fica com camada de composição permanente. Por isso ele está
       só no hero — nos 45 elementos do reveal, blur(0) residual manteria
       quarenta e cinco camadas vivas pelo resto da rolagem.

       800ms para 8px de deslocamento parece lento no papel; não é. O olho lê
       a nitidez chegando, não a posição — o movimento pode ser curto porque
       o foco é que conta a história. */
    .hero__anim {
      animation: hero-in .8s var(--ease-saida) backwards;
    }

    /* 80ms entre irmãos, não 80 crescendo até 360: a primeira dobra inteira
       fecha em 720ms. Acima disso o visitante já começou a ler a headline
       enquanto o CTA ainda está entrando. */
    .hero__anim:nth-child(1) { animation-delay: .02s; }
    .hero__anim:nth-child(2) { animation-delay: .10s; }
    .hero__anim:nth-child(3) { animation-delay: .18s; }
    .hero__anim:nth-child(4) { animation-delay: .26s; }
    .hero__anim:nth-child(5) { animation-delay: .34s; }

    @keyframes hero-in {
      from { opacity: 0; transform: translateY(8px); filter: blur(9px); }
      to   { opacity: 1; transform: translateY(0); filter: blur(0); }
    }

    /* O frame entra por último e SEM desfoque: é o maior elemento da página,
       e um filter num objeto de 1400x560 custa muito mais que nos cinco
       blocos de texto acima. Sobe um pouco mais que eles (18px contra 8) —
       distância maior lê como massa maior. */
    .hero__palco.hero__anim {
      animation-name: palco-in;
      animation-duration: .9s;
      animation-delay: .42s;
    }

    @keyframes palco-in {
      from { opacity: 0; transform: translateY(18px); }
      to   { opacity: 1; transform: translateY(0); }
    }

    .hero__actions { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 30px; }
    .hero__note { margin-top: 26px; max-width: 34rem; font-size: 14px; line-height: 1.5; color: rgba(166, 156, 147, .82); }

    /* ---------- o palco: o produto em sangria ---------- */

    /* A peça que a página nunca teve. O mock anterior tinha 527px — 40% do
       shell de 1330 — e flutuava à direita do texto com ar em volta; a
       primeira aparição do produto em tamanho de verdade só acontecia na
       terceira tela. Aqui ele encosta nas duas bordas do viewport e é cortado
       pela altura do hero.

       O corte é o ponto: um objeto que continua além da dobra é a única
       maneira honesta de dizer "tem mais" sem escrever "role para baixo". */
    .hero__palco {
      position: relative;
      z-index: 1;
      display: flex;
      justify-content: center;
      margin-top: clamp(34px, 5vh, 64px);
      padding: 0;
    }

    /* Sangria de verdade: até 1560px de viewport o frame TEM a largura da
       tela — sem calha, encostando nos dois lados. Acima disso ele para de
       crescer e centraliza, porque um frame de 2000px de largura deixa de ser
       uma tela de produto e vira papel de parede. */
    .app {
      width: min(100%, 1560px);
      border: 1px solid var(--hairline);
      border-top-color: rgba(var(--borda-rgb), .16);
      border-bottom: 0;
      border-radius: 16px 16px 0 0;
      background: var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), 0 -1px 0 rgba(var(--sup-rgb), .02), 0 40px 80px -40px rgba(var(--sombra-rgb), .95);
      overflow: hidden;
      font-size: 13px;
      line-height: 1.45;
      text-align: left;
    }

    /* ---------- a exceção fria: tudo que representa o PRODUTO ----------

       A página é uma sala de luz quente. Os frames de produto são TELAS, com
       luz própria, dentro dela. Manter o interior deles frio faz a página ler
       como ambiente e o frame como aparelho — e dá o contraste de temperatura
       que uma página de um matiz só nunca tem. É o segundo mecanismo (junto
       com a sombra fria) que impede o conjunto de virar sépia.

       Uma sobrescrita de tripletes resolve o frame inteiro: as variáveis do
       CSS são substituídas no ponto de USO, então --hairline, --sup-card e
       --luz-topo, definidos lá no :root, passam a resolver frio aqui dentro
       sem precisar reescrever uma regra sequer.

       Vale para os três frames: o do hero e as duas demonstrações das seções.
       Um mock frio e outro quente seria pior que tudo ser cinza. */
    .app,
    .demo-card {
      --sup-rgb: var(--fria-rgb);
      --borda-rgb: var(--fria-rgb);
      --luz-rgb: var(--fria-rgb);
      --card: #0D0F13;
    }

    /* --- barra de chrome --- */

    .app__chrome {
      display: flex;
      align-items: center;
      gap: 16px;
      padding: 0 16px;
      height: 42px;
      border-bottom: 1px solid var(--hairline);
      background: rgba(var(--sup-rgb), .022);
    }

    .app__pontos { display: flex; gap: 6px; flex: none; }
    .app__pontos i { width: 9px; height: 9px; border-radius: 50%; background: rgba(var(--sup-rgb), .13); }

    .app__rota {
      display: flex; align-items: center; gap: 8px;
      font-size: 11.5px; letter-spacing: .01em;
      color: var(--rotulo);
    }
    .app__rota b { font-weight: 400; color: var(--marcador); }

    /* A única coisa da barra que é laranja, e ela quer dizer "acontecendo
       agora" — não "importante". */
    .app__estado {
      display: flex; align-items: center; gap: 8px;
      margin-left: auto;
      font-size: 11.5px;
      color: var(--rotulo);
      white-space: nowrap;
    }

    .app__ping {
      position: relative;
      width: 6px; height: 6px; flex: none;
      border-radius: 50%;
      background: var(--accent);
    }

    .app__ping::after {
      content: "";
      position: absolute;
      inset: -3px;
      border-radius: 50%;
      border: 1px solid var(--accent);
      animation: app-ping 2.4s ease-out infinite;
    }

    @keyframes app-ping {
      0%   { opacity: .55; transform: scale(.6); }
      70%  { opacity: 0;   transform: scale(1.5); }
      100% { opacity: 0;   transform: scale(1.5); }
    }

    /* --- as quatro colunas --- */

    /* Quatro colunas porque o produto TEM quatro: navegação, fila, conversa e
       o lead extraído dela. Um frame de 1440px com duas colunas ficaria com
       balões de 700px de largura, que nenhum aplicativo de conversa faz. */
    .app__corpo {
      display: grid;
      grid-template-columns: 172px 300px minmax(0, 1fr) 264px;
      /* minmax(0, 1fr) e não a linha implícita `auto`: com `auto`, uma coluna
         cujo conteúdo é mais alto que o frame ESTICA a linha e vaza por
         baixo. Foi o que aconteceu — a conversa media 540px dentro de um
         corpo de 468, e o indicador de "digitando" (o sinal de "agora", a
         informação mais importante do frame) caía fora da borda e sumia no
         overflow. */
      grid-template-rows: minmax(0, 1fr);
      height: clamp(390px, 52svh, 520px);
    }

    /* min-height: 0 pelo mesmo motivo: o padrão `auto` impede um item de
       grade de ficar menor que o conteúdo dele. */
    .app__corpo > * {
      min-width: 0; min-height: 0;
      overflow: hidden;
      border-right: 1px solid var(--hairline);
    }
    .app__corpo > *:last-child { border-right: 0; }

    .app__titulo {
      font-size: 10.5px; font-weight: 600;
      letter-spacing: .1em; text-transform: uppercase;
      color: var(--marcador);
    }

    /* --- navegação --- */

    .app__rail { display: grid; align-content: start; gap: 2px; padding: 12px 10px; }

    .app__rail-item {
      display: flex; align-items: center; gap: 10px;
      padding: 8px 10px;
      border-radius: var(--radius);
      font-size: 12.5px;
      color: var(--muted-foreground);
    }

    .app__rail-item b {
      margin-left: auto;
      font-size: 11px; font-weight: 500;
      font-variant-numeric: tabular-nums;
      color: var(--marcador);
    }

    .app__rail-item.is-atual { background: rgba(var(--sup-rgb), .06); color: var(--foreground); }
    .app__rail-item.is-atual b { color: var(--rotulo); }

    /* --- fila de conversas --- */

    .app__lista { display: grid; grid-template-rows: auto minmax(0, 1fr); }

    .app__lista-topo {
      display: grid; gap: 10px;
      padding: 13px 14px 11px;
      border-bottom: 1px solid var(--hairline);
    }

    .app__filtros { display: flex; gap: 6px; }

    .app__filtros i {
      padding: 3px 9px;
      border-radius: var(--radius-full);
      font-size: 11px; font-style: normal;
      color: var(--muted-foreground);
      background: rgba(var(--sup-rgb), .04);
    }

    .app__filtros i.is-atual { background: rgba(var(--sup-rgb), .12); color: var(--foreground); }

    .app__fila { margin: 0; padding: 0; list-style: none; overflow: hidden; }

    .app__linha {
      position: relative;
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto;
      gap: 2px 10px;
      padding: 10px 14px;
      border-bottom: 1px solid rgba(var(--borda-rgb), .045);
    }

    .app__quem { font-size: 12.5px; font-weight: 600; letter-spacing: -.01em; }
    .app__quando { font-size: 11px; color: var(--marcador); font-variant-numeric: tabular-nums; }

    .app__previa {
      grid-column: 1 / -1;
      font-size: 12px;
      color: var(--muted-foreground);
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }

    /* A conversa que está sendo respondida NESTE segundo. Marcador de 2px na
       borda, fundo levemente aceso e a hora em laranja — três sinais fracos
       que juntos dizem "aqui". */
    .app__linha.is-agora { background: rgba(249, 115, 22, .055); }
    .app__linha.is-agora::before {
      content: "";
      position: absolute;
      left: 0; top: 0; bottom: 0;
      width: 2px;
      background: var(--accent);
    }
    .app__linha.is-agora .app__quem { color: var(--foreground); }
    .app__linha.is-agora .app__quando { color: var(--accent-soft); }

    /* --- conversa aberta --- */

    .app__conversa { display: grid; grid-template-rows: auto minmax(0, 1fr); }

    .app__conversa-topo {
      display: flex; align-items: center; gap: 14px;
      padding: 11px 18px;
      border-bottom: 1px solid var(--hairline);
    }

    .app__conversa-quem { display: grid; gap: 1px; }
    .app__conversa-quem strong { font-size: 13px; font-weight: 600; letter-spacing: -.01em; }
    .app__conversa-quem span { font-size: 11.5px; color: var(--muted-foreground); }

    .app__agora {
      display: flex; align-items: center; gap: 7px;
      margin-left: auto;
      padding: 4px 10px;
      border: 1px solid rgba(249, 115, 22, .3);
      border-radius: var(--radius-full);
      background: rgba(249, 115, 22, .09);
      font-size: 11px;
      color: var(--accent-soft);
      white-space: nowrap;
    }

    /* align-content: end e não start — é o que toda conversa faz: a mensagem
       mais nova encosta embaixo e o histórico sobe. Com `start` sobravam
       ~110px de vazio sob o indicador de "digitando" numa coluna de 500px de
       altura, e a coluna mais importante do frame era a única com buraco.
       De quebra, o que o corte come no topo são as mensagens ANTIGAS, que é
       o lado certo para cortar. */
    .app__thread {
      display: grid; gap: 8px; align-content: end;
      padding: 16px 18px;
      overflow: hidden;

      /* A mensagem mais antiga dissolve no topo em vez de ser cortada numa
         linha reta. Resolve os dois casos com a mesma regra: quando a
         conversa é mais alta que a coluna, o corte não vira uma borda; quando
         é mais baixa, não sobra um degrau visível. É também o que um app de
         conversa faz de verdade ao rolar. */
      mask-image: linear-gradient(180deg, transparent, #000 16%);
      -webkit-mask-image: linear-gradient(180deg, transparent, #000 16%);
    }

    .app__msg {
      position: relative;
      max-width: 72%;
      padding: 8px 12px 8px;
      border-radius: 12px;
      font-size: 12.5px;
      line-height: 1.5;
    }

    .app__msg i {
      display: block;
      margin-top: 3px;
      font-size: 10px; font-style: normal;
      font-variant-numeric: tabular-nums;
      color: rgba(var(--luz-rgb), .38);
    }

    .app__msg--in {
      justify-self: start;
      background: rgba(var(--sup-rgb), .055);
      border-top-left-radius: 4px;
    }

    /* Mesmo verde-escuro do tema escuro do WhatsApp real que o resto da
       página já usa — a marca aparece pelo tom certo, não por uma área
       grande de #25D366. */
    .app__msg--out {
      justify-self: end;
      background: #0A5B4B;
      border-top-right-radius: 4px;
    }

    .app__digitando {
      justify-self: start;
      display: flex; gap: 4px;
      margin: 2px 0 0;
      padding: 10px 12px;
      border-radius: 12px;
      border-top-left-radius: 4px;
      background: rgba(var(--sup-rgb), .055);
    }

    .app__digitando span {
      width: 5px; height: 5px;
      border-radius: 50%;
      background: var(--accent);
      opacity: .35;
      animation: app-digitando 1.3s ease-in-out infinite;
    }

    .app__digitando span:nth-child(2) { animation-delay: .16s; }
    .app__digitando span:nth-child(3) { animation-delay: .32s; }

    @keyframes app-digitando {
      0%, 60%, 100% { opacity: .3; transform: translateY(0); }
      30%           { opacity: 1;  transform: translateY(-3px); }
    }

    /* --- o lead extraído da conversa --- */

    .app__lead { display: grid; align-content: start; gap: 14px; padding: 14px 16px; }

    .app__campos { margin: 0; display: grid; gap: 10px; }
    .app__campos > div { display: grid; gap: 2px; }
    .app__campos dt { font-size: 10.5px; color: var(--marcador); }
    .app__campos dd { margin: 0; display: flex; align-items: center; gap: 6px; font-size: 12.5px; }

    .app__wa { width: 13px; height: 13px; flex: none; color: var(--whats); }

    .app__cifra { font-weight: 600; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }

    .app__etapas { display: flex; flex-wrap: wrap; gap: 5px; padding-top: 2px; }

    .app__etapa {
      padding: 3px 9px;
      border-radius: var(--radius-full);
      background: rgba(var(--sup-rgb), .045);
      font-size: 10.5px;
      color: var(--marcador);
    }

    .app__etapa.is-feita { background: rgba(var(--sup-rgb), .09); color: var(--rotulo); }

    /* A etapa em que o negócio está AGORA — mesmo significado do ponto da
       barra e da linha acesa na fila. */
    .app__etapa.is-atual {
      background: rgba(249, 115, 22, .14);
      border: 1px solid rgba(249, 115, 22, .34);
      padding: 2px 8px;
      color: var(--accent-soft);
    }

    /* ---------- método: a sequência ----------

       Era uma grade de quatro cards iguais, logo depois de OUTRA grade de
       quatro cards iguais em "Onde você perde". A segunda ocorrência da mesma
       forma é onde a página passa a ler como template: o olho aprende o
       padrão na primeira e para de olhar na segunda.

       Mas a razão de trocar ESTA e não a outra é de conteúdo, não de variedade.
       Quatro caixas de mesmo tamanho lado a lado afirmam "quatro coisas
       independentes". Um método não é isso: 01 acontece antes de 02, e 03 só
       existe porque 02 aconteceu. A ordem era informação e a grade apagava —
       a numeração 01-04 estava ali justamente tentando dizer, sozinha, o que
       a forma negava.

       "Onde você perde" fica com a grade, e está certo: lá são quatro
       exemplos independentes da mesma falha, sem ordem entre si.

       Efeito colateral bem-vindo: quatro cards a menos. A página tinha 54
       retângulos com borda para 680 palavras. */

    .method {
      display: grid;
      gap: 0;
      /* Mesmo clamp de .moments, para as duas seções pulsarem no mesmo ritmo. */
      margin: clamp(40px, 5vw, 70px) 0 0;
      padding: 0;
      list-style: none;
    }

    /* Três colunas no desktop: marcador, nome da etapa, o que ela faz. É o
       que usa a largura que a grade desperdiçava — cada card tinha 294px
       (22% do shell) e o texto quebrava em cinco linhas dentro deles. */
    .method__step {
      position: relative;
      display: grid;
      grid-template-columns: 44px minmax(0, 210px) minmax(0, 1fr);
      align-items: start;
      column-gap: clamp(18px, 2.6vw, 40px);
      row-gap: 8px;
      padding: 0 0 clamp(30px, 3.4vw, 46px);
    }

    .method__step:last-child { padding-bottom: 0; }

    /* O trilho: o que faz a sequência ler como sequência. Sai do marcador,
       desce até o próximo, e desvanece — uma linha de opacidade constante
       viraria uma régua e competiria com o conteúdo. */
    .method__step::before {
      content: "";
      position: absolute;
      left: 21px;
      top: 46px;
      bottom: 6px;
      width: 1px;

      /* Duas camadas: um PULSO curto e quente que desce, e o trilho de
         repouso embaixo. O pulso é uma parada de background animada por
         background-position — repinta uma faixa de 1px de largura, que é
         barato o bastante para os quatro rodarem sem custo mensurável
         (medido: 60fps rolando a página inteira, o mesmo de antes). */
      background:
        linear-gradient(180deg,
          rgba(249, 115, 22, .95) 0%, rgba(249, 115, 22, .30) 55%, transparent 100%)
          0% -140% / 100% 70% no-repeat,
        linear-gradient(180deg,
          rgba(var(--borda-rgb), .34), rgba(var(--borda-rgb), .05));
      animation: pulso-desce 7.2s linear infinite;
    }

    .method__step:last-child::before { display: none; }

    /* O movimento aqui CODIFICA o conteúdo, e é a mesma razão que fez a
       grade virar sequência na rodada passada: a luz desce o trilho e cada
       nó acende quando ela chega, 01 antes de 02 antes de 03. Um loop que
       diz "estas são quatro coisas em ordem" — não um brilho decorativo.
       O ciclo é longo (7,2s) e o acender ocupa 8% dele: é ambiente, não
       piscante. */
    .method__step:nth-child(1)::before,
    .method__step:nth-child(1) .step-num { animation-delay: 0s; }
    .method__step:nth-child(2)::before,
    .method__step:nth-child(2) .step-num { animation-delay: .55s; }
    .method__step:nth-child(3)::before,
    .method__step:nth-child(3) .step-num { animation-delay: 1.1s; }
    .method__step:nth-child(4)::before,
    .method__step:nth-child(4) .step-num { animation-delay: 1.65s; }

    @keyframes pulso-desce {
      0%   { background-position: 0% -140%, 0 0; }
      22%  { background-position: 0% 240%,  0 0; }
      100% { background-position: 0% 240%,  0 0; }
    }

    /* O marcador continua sendo .step-num — mudou de tipografia solta para um
       nó do trilho. É o único pedaço que mantém tratamento de superfície:
       precisa parecer apoiado SOBRE a linha, senão a linha atravessa. */
    .method__step .step-num {
      display: grid;
      place-items: center;
      width: 44px;
      height: 44px;
      margin: 0;
      border: 1px solid rgba(251, 146, 60, .26);
      border-top-color: rgba(253, 186, 116, .42);
      border-radius: 50%;
      background: var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-1), 0 0 0 0 rgba(249, 115, 22, 0);
      font-size: 13px;
      letter-spacing: .02em;
      animation: no-acende 7.2s var(--ease-estado) infinite;
    }

    @keyframes no-acende {
      /* border-top-color vem junto em cada parada: `border-color` sozinho
         numa keyframe sobrescreve os quatro lados e apagaria a aresta de luz
         do topo durante o ciclo inteiro. */
      0%, 26%, 100% {
        box-shadow: var(--luz-topo), var(--sombra-1), 0 0 0 0 rgba(249, 115, 22, 0);
        border-color: rgba(251, 146, 60, .26);
        border-top-color: rgba(253, 186, 116, .42);
      }
      8% {
        box-shadow: var(--luz-topo), var(--sombra-1), 0 0 18px 2px rgba(249, 115, 22, .42);
        border-color: rgba(253, 186, 116, .75);
        border-top-color: rgba(255, 214, 170, .9);
      }
    }

    .method__title {
      align-self: center;
      min-height: 44px;
      display: flex;
      align-items: center;
      font-size: clamp(19px, 1.6vw, 23px);
      font-weight: 600;
      letter-spacing: -.02em;
    }

    .method__deck {
      align-self: center;
      min-height: 44px;
      display: flex;
      align-items: center;
      margin: 0;
      font-size: 15px;
      line-height: 1.55;
      color: var(--muted-foreground);
    }

    /* ---------- seções ---------- */

    /* scroll-margin-top existe por causa da barra flutuante: sem ele, clicar
       num link do menu encosta a seção no topo do viewport e a barra cobre o
       próprio título para onde a pessoa acabou de pedir para ir. O valor é a
       altura da barra rolada (~72px) mais folga. O padding generoso da seção
       disfarçava isso no desktop e não disfarçava no mobile. */
    /* Padding ASSIMÉTRICO, e menor.
       Antes era clamp(84,10vw,148px) em cima E embaixo, o que deixava 144px
       mortos no fim de TODA seção — medidos: 20% do hero, 22% do método, 16%
       do consultor. Somados aos 148px do topo da seção seguinte, davam ~290px
       de nada entre duas ideias, sempre o mesmo valor. Espaço uniforme não é
       ritmo: é padding fazendo o trabalho que a composição deveria fazer.
       Agora o topo abre a seção e o fim apenas a encerra — a folga real entre
       duas seções vem do topo da seguinte, num valor só, e não da soma de
       dois paddings iguais.

       scroll-margin-top existe por causa da barra flutuante: sem ele, clicar
       num link do menu encosta a seção no topo do viewport e a barra cobre o
       próprio título para onde a pessoa pediu para ir. */
    .section {
      position: relative;
      z-index: 10;
      padding: clamp(72px, 7.5vw, 116px) 0 clamp(40px, 4vw, 60px);
      background: var(--background);
      scroll-margin-top: 96px;
    }

    /* ---------- atmosfera ----------

       Toda a atmosfera da página vivia dentro de .hero__bg: três halos e uma
       trama fina, só no hero. As outras cinco seções eram `background:
       var(--background)` chapado. Rolando, a página perdia o ar depois da
       primeira dobra e virava um plano — e foi isso que ficou exposto quando
       o laranja saiu.

       Cada seção ganha UM halo grande e muito difuso, em posição diferente,
       para a leitura ser "espaço iluminado" e não "faixa de cor". O halo é do
       tamanho da seção e some antes das bordas, então não desenha faixa.

       z-index -1 num filho de elemento posicionado pinta DEPOIS do fundo do
       pai e ANTES do conteúdo — é o que põe o halo entre os dois sem precisar
       de outra camada. */
    .section::before {
      content: "";
      position: absolute;
      z-index: -1;
      inset: 0;
      pointer-events: none;
      background: var(--atmosfera, none);

      /* A máscara é o que separa "atmosfera" de "faixa".
         Sem ela, um halo centrado perto da borda da seção acende exatamente
         na emenda com a seção anterior e desenha uma costura horizontal — foi
         o que aconteceu entre o hero e "Onde você perde" na primeira
         tentativa. Aqui o halo nasce e morre dentro da própria seção, então
         nenhuma transição entre seções tem aresta. */
      mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
      -webkit-mask-image: linear-gradient(180deg, transparent, #000 14%, #000 86%, transparent);
    }

    /* Estes halos eram BRANCOS (3,5% de --sup-rgb) ou azuis frios, e essa era
       a causa de raiz de "os cards são ilhas neutras": medido, #metodo tinha
       0,0% de pixels cromáticos e #consultor 0,1%, contra 21,9% da primeira
       tela. Enquanto o ar em volta é branco, qualquer cor posta num card vira
       adesivo colado num ambiente que não a explica.

       As três seções onde vivem os cards recebem o campo da MARCA, em escala
       de seção e posições diferentes — para ser campo, e não a mesma faixa
       repetida três vezes. Continua sendo luz atrás de tudo: nenhum objeto
       colorido novo entra na página por aqui.

       #faq e #diagnostico ficam frios de propósito. O contraste de
       temperatura entre seções é o que impede o conjunto de virar sépia, e
       o diagnóstico já tem o CTA com o arco inteiro da rampa — dar campo
       quente a ele seria pôr laranja atrás de laranja. */
    #problema    { --atmosfera:
                     radial-gradient(64% 46% at 14% -6%, rgba(249, 115, 22, .30), transparent 66%),
                     radial-gradient(52% 40% at 92% 22%, rgba(194, 65, 12, .23), transparent 68%),
                     radial-gradient(78% 52% at 12% -4%, rgba(var(--sup-rgb), .03), transparent 68%); }
    #metodo      { --atmosfera:
                     radial-gradient(56% 52% at 96% 16%, rgba(249, 115, 22, .22), transparent 66%),
                     radial-gradient(44% 40% at 6% 88%, rgba(194, 65, 12, .14), transparent 68%); }
    #consultor   { --atmosfera:
                     radial-gradient(70% 54% at 50% 112%, rgba(234, 88, 12, .34), transparent 68%),
                     radial-gradient(40% 44% at 4% 6%, rgba(249, 115, 22, .24), transparent 66%); }
    #faq         { --atmosfera: radial-gradient(66% 50% at 6% 96%, rgba(170, 190, 230, .026), transparent 66%); }
    #diagnostico { --atmosfera: radial-gradient(74% 46% at 50% -2%, rgba(var(--sup-rgb), .038), transparent 66%); }

    /* Variação de luminosidade entre seções.
       Era um gradiente de UM ponto (#090A0C -> #0B0C0F), invisível na prática.
       Agora a seção alternada é de fato mais funda no meio, o que dá ritmo ao
       scroll — e continua sutil o bastante para não virar listra: o degradê
       ocupa a altura inteira e não tem aresta em lugar nenhum. */
    .section--alt { background: linear-gradient(180deg, var(--background), #040302 46%, var(--background)); }

    /* Trama geométrica, agora fora do hero também.
       Mesma malha de 40px do fundo do hero, mascarada em elipse para nunca
       encostar nas bordas da seção. É o que impede a área vazia entre um card
       e outro de ficar sem nenhuma informação de superfície. */
    .section--alt::after {
      content: "";
      position: absolute;
      z-index: -1;
      inset: 0;
      pointer-events: none;
      opacity: .55;
      background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='40' height='40'%3E%3Cpath d='M0 .5h40M.5 0v40' stroke='rgba(255,239,226,.022)' stroke-width='1'/%3E%3C/svg%3E");
      mask-image: radial-gradient(62% 58% at 50% 50%, #000, transparent 76%);
      -webkit-mask-image: radial-gradient(62% 58% at 50% 50%, #000, transparent 76%);
    }

    .section__head { max-width: 46rem; }
    /* Neutro. O eyebrow é um rótulo de orientação, não uma chamada — e
       repetido em cinco seções ele sozinho colocava laranja em toda tela. */
    .section__eyebrow { display: inline-flex; align-items: center; gap: 10px; margin-bottom: 18px; color: var(--rotulo); font-size: 13px; font-weight: 600; letter-spacing: .02em; text-transform: uppercase; }
    .section__eyebrow::before { content: ""; width: 20px; height: 1px; border-radius: var(--radius-full); background: var(--marcador); }
    /* Um degrau abaixo do hero, e não quase igual: 50px contra 66px eram
       dois títulos disputando o mesmo nível. */
    .section__title { font-size: clamp(26px, 3vw, 42px); font-weight: 600; line-height: 1.1; letter-spacing: -.028em; }
    .section__deck { margin-top: 20px; max-width: 40rem; font-size: clamp(16px, 1.25vw, 19px); line-height: 1.6; color: var(--muted-foreground); }

    /* problema */
    .moments { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: clamp(14px, 1.6vw, 22px); margin-top: clamp(40px, 5vw, 70px); }

    /* Estes quatro cards são o endereço da PERDA, e por isso levam a ponta
       FUNDA da rampa (--brasa / --brasa-funda) e nunca a clara: a seção fala
       de prejuízo, e a cor da ação não pode significar isso.

       A brasa se acumula no PÉ do card — a perda escorre para baixo, o card
       fica mais quente embaixo que em cima. É emissão de dentro da
       superfície, não um filete colado na borda: filete aqui seria a terceira
       forma com trilho da página, depois do #metodo, e repetição de forma foi
       exatamente o que arrumamos na rodada anterior.

       A aresta de cima deixa de captar luz branca e passa a captar luz
       quente. Numa página cuja fonte de luz é laranja, uma aresta que devolve
       branco está fisicamente errada. */
    .moment {
      position: relative;
      padding: 30px 28px 32px;
      border: 1px solid rgba(194, 65, 12, .30);
      border-top-color: rgba(194, 65, 12, .62);
      border-radius: var(--radius-lg);
      background:
        radial-gradient(132% 78% at 50% 124%,
          rgba(194, 65, 12, .62), rgba(124, 45, 18, .20) 44%, transparent 72%),
        var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-2);
      transition:
        background var(--dur-volta) var(--ease-saida),
        border-color var(--dur-volta) var(--ease-saida);
    }

    /* O holofote: a superfície acende ONDE A MÃO ESTÁ. É o vocabulário do
       Raycast — eles iluminam superfícies, não as listram.
       Custo de listener: zero. O requestAnimationFrame do tilt já calcula a
       posição relativa do cursor neste mesmo card; são duas linhas a mais no
       mesmo bloco (ver landing.js). Sem o JS, --mx/--my caem no 50% do
       fallback e o holofote fica centrado — degrada para um brilho fixo, não
       para nada quebrado. */
    .moment::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 0;
      opacity: 0;
      border-radius: inherit;
      pointer-events: none;
      background: radial-gradient(300px 220px at var(--mx, 50%) var(--my, 50%),
        rgba(194, 65, 12, .34), rgba(124, 45, 18, .12) 44%, transparent 68%);
      transition: opacity var(--dur-volta) var(--ease-saida);
    }

    /* A ida é mais curta que a volta, como no tilt: acender responde a uma
       intenção, apagar não. */
    .moment:hover::after { opacity: 1; transition-duration: var(--dur-media); }

    /* .moments .moment, e não .moment, porque `.tilt-card:hover` mais abaixo
       tem a mesma especificidade e vem depois — venceria e devolveria a borda
       neutra justamente no estado em que ela deve esquentar. E as duas cores
       vêm juntas: `border-color` sozinho apaga a aresta de luz do topo. */
    .moments .moment:hover {
      border-color: rgba(194, 65, 12, .5);
      border-top-color: rgba(194, 65, 12, .85);
    }

    /* O texto sobe acima do holofote. Sem isto o gradiente do ::after
       passaria por cima dos dois parágrafos. */
    .moment > * { position: relative; z-index: 1; }

    .moment__when { font-size: clamp(19px, 1.6vw, 24px); font-weight: 600; letter-spacing: -.03em; line-height: 1.25; }
    .moment__what { margin-top: 14px; font-size: 16px; line-height: 1.6; color: var(--muted-foreground); }

    /* .demo-card (mais abaixo) mostra o produto em uso de verdade: conversa
       de WhatsApp e pipeline do CRM, ambos animados em HTML/CSS. A página
       inteira não carrega nenhuma imagem — só a fonte e este CSS. */
    .demo-feature { margin-top: clamp(40px, 5vw, 70px); display: flex; justify-content: center; }

    /* Antes (fotos estáticas removidas no Ponto 7) isto era um gradiente
       ABSOLUTO por cima da imagem — fazia sentido sobre uma foto, mas sobre
       texto de UI ao vivo (as bolhas de chat, o lead do kanban) uma legenda
       absoluta corre o risco real de sobrepor conteúdo que cresce (ex: a
       bolha de resposta quebrando em 3 linhas numa tela estreita). Por isso
       agora é uma barra de rodapé no fluxo normal, nunca por cima de nada. */
    .figure__cap {
      position: static;
      display: flex; justify-content: space-between; gap: 16px;
      padding: 12px 18px;
      border-top: 1px solid var(--hairline);
      background: rgba(var(--sup-rgb), .015);
      font-size: 12px; font-weight: 500; letter-spacing: .05em; text-transform: uppercase;
      color: var(--muted-foreground);
      font-variant-numeric: tabular-nums;
    }

    /* consultor */
    /* Três faixas, não duas colunas: texto e mock lado a lado em cima, e os
       números atravessando a largura inteira embaixo.
       Antes as métricas ficavam empilhadas DENTRO da coluna de texto, o que
       dava 600px de conteúdo à esquerda contra 260px de mock à direita —
       340px mortos e duas colunas sem relação nenhuma. Agora as três partes
       têm papéis: o texto explica, o mock mostra, os números concluem.
       align-items:start porque as duas colunas de cima têm alturas
       diferentes por natureza e devem começar juntas, não flutuar centradas. */
    .consultor {
      display: grid;
      grid-template-columns: 1fr 1fr;
      align-items: start;
      gap: clamp(32px, 4vw, 72px);
    }

    .metrics {
      grid-column: 1 / -1;
      display: grid;
      grid-template-columns: repeat(4, minmax(0, 1fr));
      gap: 12px;

      /* Sem margin-top: como a faixa agora é uma LINHA do grid .consultor, o
         row-gap dele já separa as métricas das duas colunas de cima. Manter a
         margem somava os dois espaçamentos (72px + 44px) e abria um buraco de
         116px onde deveria haver um só. */
      margin-top: 0;
    }

    .metric {
      padding: 20px;
      border: 1px solid var(--hairline);
      border-top-color: rgba(251, 146, 60, .18);
      border-radius: var(--radius);
      background: var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-2);
    }
    /* Aqui a metáfora é INSTRUMENTO, não brasa: o card fica neutro e quem
       acende é a LEITURA. É o que separa este tratamento do dos .moment, que
       iluminam a superfície — dois cards de mesma forma na mesma página
       precisam de recursos diferentes, senão vira uma regra genérica repetida.

       O número em branco cheio era o defeito de conteúdo: o argumento da
       seção é "com número, não com achismo" e o número tinha exatamente o
       mesmo peso do rótulo. Ponta CLARA da rampa — é medição do produto
       funcionando, não perda. Medido: 9,91:1. */
    .metric dt { font-size: 13px; font-weight: 500; color: var(--muted-foreground); }
    .metric dd {
      margin: 8px 0 0;
      font-size: clamp(20px, 1.9vw, 26px);
      font-weight: 600;
      letter-spacing: -.03em;
      font-variant-numeric: tabular-nums;
      color: var(--accent-pale);
      /* Halo curto: acende o algarismo sem sujar o card em volta. */
      text-shadow: 0 0 26px rgba(249, 115, 22, .3);
    }
    /* A unidade não é a leitura — fica neutra, e é o contraste entre as duas
       que faz o algarismo saltar. */
    .metric dd small { font-size: 14px; font-weight: 500; color: var(--muted-foreground); text-shadow: none; letter-spacing: 0; }

    /* O card de conclusão ocupa a linha inteira e inverte a hierarquia dos
       outros: aqui a afirmação é o texto grande (dt) e a recomendação é a
       linha de apoio (dd) — o oposto de um card de número, porque o que
       importa é a frase, não um valor isolado. */
    /* Era a coisa mais barulhenta da página — borda laranja, fundo laranja e
       texto laranja — para sustentar uma afirmação secundária. Agora se
       destaca por SUPERFÍCIE (fundo um degrau mais claro que os cards
       vizinhos), que é o recurso certo para "isto conclui os dois de cima". */
    /* Um degrau ACIMA dos dois cards de número, não ao lado: ele conclui os
       dois. A distinção agora é de elevação (superfície mais clara + sombra
       mais longa), que era o que a borda laranja fazia de forma barulhenta. */
    .metric--claim {
      grid-column: span 2;
      border-color: var(--hairline-forte);
      border-top-color: rgba(var(--borda-rgb), .2);
      background: var(--sup-alta), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-3);
    }

    .metric--claim dt {
      font-size: clamp(15px, 1.35vw, 18px);
      font-weight: 600;
      line-height: 1.35;
      letter-spacing: -.015em;
      color: var(--foreground);
    }

    /* A única parte da frase que é a promessa do produto. Quando tirei a
       borda laranja barulhenta deste card, na rodada da contenção, a ênfase
       saiu junto e sobrou distinção só por superfície — que resolve
       hierarquia, não ênfase de frase. Isto devolve a ênfase sem devolver o
       barulho: uma expressão, não o card inteiro. Medido: 9,04:1. */
    .metric--claim dt strong { color: var(--accent-pale); font-weight: 600; }

    .metric--claim dd {
      margin-top: 10px;
      display: flex;
      align-items: baseline;
      gap: 9px;
      font-size: 13.5px;
      font-weight: 500;
      line-height: 1.5;
      letter-spacing: 0;
      color: var(--muted-foreground);
      /* herdado de .metric dd, que agora acende: aqui a linha é de apoio */
      text-shadow: none;
    }

    /* "Veja agora quem está esperando resposta NESTE MOMENTO" é uma afirmação
       ao vivo escrita como nota de rodapé. Recebe o mesmo ponto do badge —
       e é a única coisa que repito de propósito entre dois cards, porque os
       dois fazem literalmente a mesma afirmação. */
    .metric--claim dd::before {
      content: "";
      flex: none;
      width: 6px;
      height: 6px;
      border-radius: 50%;
      transform: translateY(-1px);
      background: var(--accent);
      box-shadow: 0 0 0 3px rgba(249, 115, 22, .16), 0 0 9px 1px rgba(249, 115, 22, .7);
      animation: pulso-vivo 2.6s var(--ease-estado) infinite;
    }

    /* faq */
        /* 800px, não os 1330px do shell. A linha inteira tinha 1218px de largura
       para um texto de 17px, então o "+" ficava a mais de mil pixels da
       pergunta a que pertence — não era um acordeão, eram duas colunas que
       não se falavam. */
    .faq { max-width: 800px; margin-top: clamp(36px, 4vw, 56px); border-top: 1px solid var(--hairline); }

    .faq details {
      border-bottom: 1px solid var(--hairline);
      transition: border-color var(--dur-rapida) var(--ease-estado);
    }

    .faq summary {
      display: flex; align-items: center; justify-content: space-between; gap: 20px;
      padding: 24px 4px;
      font-size: clamp(17px, 1.4vw, 20px); font-weight: 600; letter-spacing: -.02em;
      cursor: pointer; list-style: none;
      transition: color var(--dur-rapida) var(--ease-estado);
    }

    .faq summary::-webkit-details-marker { display: none; }

    .faq summary:hover { color: var(--foreground); }
    .faq summary:active::after { transform: scale(.9); transition-duration: var(--dur-toque); }

    .faq summary::after {
      content: "+";
      flex: none;
      display: grid; place-items: center;
      width: 28px; height: 28px;
      border: 1px solid var(--hairline);
      border-radius: 50%;
      color: var(--rotulo);
      font-size: 17px; font-weight: 400;
      transition: transform var(--dur-rapida) var(--ease-estado), background var(--dur-rapida) var(--ease-estado), border-color var(--dur-rapida) var(--ease-estado);
    }

    .faq summary:hover::after { border-color: var(--hairline-forte); color: var(--foreground); }
    .faq details[open] summary::after { content: "−"; background: var(--surface); }
    .faq p { padding: 0 4px 26px; max-width: 52rem; font-size: 16px; line-height: 1.65; color: var(--muted-foreground); }

    /* diagnóstico */
    .diag {
      max-width: 720px;
      margin-inline: auto;
      padding: clamp(28px, 3.4vw, 48px);
      border: 1px solid var(--hairline);
      border-top-color: rgba(var(--borda-rgb), .14);
      border-radius: var(--radius-lg);
      background: var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-3);
    }

    .diag__grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px; margin-top: 30px; }
    .field { display: grid; gap: 8px; }
    .field--wide { grid-column: 1 / -1; }
    .field label { font-size: 14px; font-weight: 500; color: var(--muted-foreground); }

    .field input {
      width: 100%;
      min-height: 48px;
      padding: 12px 14px;
      border: 1px solid rgba(var(--borda-rgb), .12);
      border-radius: var(--radius);
      background: var(--secondary);
      color: var(--foreground);
      font-family: inherit;
      font-size: 15px;
      font-weight: 500;
      transition: border-color var(--dur-rapida) var(--ease-estado);
    }

    .field input::placeholder { color: rgba(166, 156, 147, .6); }
    .field input:focus { outline: none; border-color: var(--accent); }

    /* ---------- chips customizados (segmento / leads / dificuldade) ----------
       Ponto 4 do refinamento: substitui os <select> nativos por radiogroups
       estilizados como chips clicáveis. O <input type="radio"> continua
       existindo de verdade (só visualmente escondido) — o form submete os
       mesmos pares name=value de sempre, o backend não muda nada. */

    .chip-field { border: 0; margin: 0; padding: 0; display: grid; gap: 10px; }

    .chip-field legend {
      padding: 0;
      font-size: 14px;
      font-weight: 500;
      color: var(--muted-foreground);
    }

    /* Marca visual de campo opcional: sem isso, um bloco de chips idêntico
       aos obrigatórios faz o visitante achar que precisa responder pra
       enviar — e desistir no meio custa o lead inteiro. */
    .legend__optional {
      display: inline-block;
      margin-left: 6px;
      padding: 2px 7px;
      border: 1px solid var(--hairline);
      border-radius: 999px;
      font-size: 10.5px;
      font-weight: 600;
      letter-spacing: .04em;
      text-transform: uppercase;
      color: var(--muted-foreground);
      vertical-align: middle;
    }

    .chip-group { display: flex; flex-wrap: wrap; gap: 10px; }

    .chip {
      position: relative;
      display: inline-flex;
      align-items: center;
      gap: 9px;
      padding: 11px 16px;
      border: 1px solid rgba(var(--borda-rgb), .12);
      border-radius: var(--radius);
      background: var(--secondary);
      color: var(--muted-foreground);
      font-size: 14px;
      font-weight: 500;
      cursor: pointer;
      box-shadow: 0 1px 2px rgba(var(--sombra-rgb), .25);
      transition: border-color var(--dur-rapida) var(--ease-estado), background var(--dur-rapida) var(--ease-estado), color var(--dur-rapida) var(--ease-estado), transform var(--dur-rapida) var(--ease-estado), box-shadow var(--dur-rapida) var(--ease-estado);
    }

    /* input real, só visualmente escondido — precisa continuar "presente"
       (não display:none) pra validação HTML5 (required) e o foco por
       teclado funcionarem normalmente. */
    .chip input {
      position: absolute;
      width: 1px; height: 1px;
      margin: 0;
      opacity: 0;
      pointer-events: none;
    }

    .chip:hover {
      border-color: var(--hairline-forte);
      color: var(--foreground);
      transform: translateY(-1px);
    }

    .chip:has(input:checked) {
      border-color: rgba(var(--borda-rgb), .55);
      background: rgba(var(--sup-rgb), .12);
      color: var(--foreground);
      box-shadow: 0 4px 14px rgba(var(--sombra-rgb), .3);
    }


    /* Marcar um chip é o gesto mais repetido do formulário — sem press ele
       é o único controle da página que não responde ao dedo. */
    .chip:active { transform: scale(.97); transition-duration: var(--dur-toque); }

    .chip:has(input:focus-visible) {
      outline: 2px solid var(--accent);
      outline-offset: 2px;
    }

    .diag .btn { width: 100%; margin-top: 26px; }
    .diag__note { margin-top: 18px; font-size: 13px; line-height: 1.55; color: rgba(166, 156, 147, .82); text-align: center; }

    /* cta final */
    .closer { display: grid; justify-items: center; text-align: center; gap: 20px; padding: clamp(60px, 7vw, 104px) 0; border-top: 1px solid var(--hairline); }
    .closer h2 { font-size: clamp(26px, 3vw, 42px); }
    .closer p { max-width: 40rem; font-size: clamp(16px, 1.2vw, 18px); line-height: 1.6; color: var(--muted-foreground); }

    /* footer */
    .footer { padding: clamp(48px, 5vw, 76px) 0 40px; border-top: 1px solid var(--hairline); background: var(--background); }
    .footer__grid { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: clamp(28px, 4vw, 64px); }
    .footer__brand { display: block; height: 37px; width: auto; }
    .footer__tagline { margin-top: 16px; max-width: 30rem; font-size: 15px; line-height: 1.6; color: var(--muted-foreground); }
    .footer h3 { font-size: 13px; font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--muted-foreground); }
    .footer ul { margin: 16px 0 0; padding: 0; list-style: none; display: grid; gap: 11px; }
    .footer li a { font-size: 15px; color: var(--foreground); opacity: .82; }
    .footer li a:hover { opacity: 1; color: var(--foreground); }
    .footer__legal { margin-top: clamp(36px, 4vw, 56px); padding-top: 24px; border-top: 1px solid var(--hairline); font-size: 13px; color: var(--muted-foreground); }

    /* whatsapp flutuante — mesmo gradiente verde em loop + glow pulsando em
       sincronia (mesma duração/easing das outras animações de botão). */
    /* Antes: círculo verde cheio, com gradiente animado e glow pulsando,
       fixo por cima de tudo. Ele cobria a pílula "Quente" do mock no hero e
       sentava em cima da headline "trocar de ferramenta." a 375px.
       Duas correções: superfície escura de vidro com o glifo verde (a marca
       do WhatsApp continua reconhecível pelo ÍCONE, que é como ela é
       reconhecida de fato), e só aparece depois que o hero sai da tela — o
       que elimina as duas colisões e devolve a primeira dobra ao CTA. */
    .wa-float {
      position: fixed;
      right: 22px; bottom: 22px;
      z-index: 300;
      display: grid; place-items: center;
      width: 52px; height: 52px;
      border: 1px solid rgba(var(--borda-rgb), .12);
      border-radius: 50%;
      color: var(--whats);
      background: rgba(18, 15, 12, .72);
      backdrop-filter: blur(18px) saturate(160%);
      -webkit-backdrop-filter: blur(18px) saturate(160%);

      /* Escondido até o hero sair. visibility (e não display) para poder
         animar e, junto, sair do foco do teclado enquanto não está lá. */
      opacity: 0;
      visibility: hidden;
      transform: translateY(10px) scale(.9);
      transition:
        opacity var(--dur-media) var(--ease-saida),
        transform var(--dur-media) var(--ease-saida),
        visibility 0s linear var(--dur-media);
      box-shadow: 0 10px 30px rgba(var(--sombra-rgb), .45);
    }

    /* Havia aqui, depois do `color: var(--whats)` acima e portanto vencendo
       dele, um `color: #06301A` e um `background-size: 240% 240%` — restos do
       botão anterior, que era um disco VERDE CHEIO com gradiente animado e
       glifo escuro por cima. Quando o disco virou vidro escuro, o glifo
       continuou escuro: verde 6% de luminância sobre vidro de 5%. Medido na
       tela, o ícone estava praticamente invisível. */ 

    /* Revelado quando o hero sai da tela (classe posta pelo landing.js). */
    body.is-passou-do-hero .wa-float {
      opacity: 1;
      visibility: visible;
      transform: translateY(0) scale(1);
      transition:
        opacity var(--dur-media) var(--ease-saida),
        transform var(--dur-media) var(--ease-saida),
        visibility 0s,
        background var(--dur-rapida) var(--ease-estado),
        border-color var(--dur-rapida) var(--ease-estado);
    }

    .wa-float:hover {
      background: rgba(20, 22, 26, .88);
      border-color: rgba(37, 211, 102, .45);
    }

    .wa-float svg { width: 26px; height: 26px; }

    /* ---------- reveal on scroll + stagger ----------
       Fade + translateY ao entrar na viewport, disparado via
       IntersectionObserver em landing.js — só transform/opacity (barato,
       não recalcula layout). O stagger entre irmãos usa nth-child fixo em
       vez de JS por elemento: mais simples e igualmente suave.

       Duas coisas estavam erradas aqui, e as duas são de DISTÂNCIA, não de
       curva:

       28px era longe demais. Quem faz isso bem move de 8 a 12px — o
       suficiente para o olho registrar que a coisa chegou agora, longe do
       suficiente de virar um bloco deslizando para dentro da tela. Com 28px
       o movimento vira o assunto; com 10px, ele só data o elemento.

       E o stagger de 80ms com seis irmãos empilhava 400ms de atraso: o
       último card de "Como funciona" só começava a aparecer meio segundo
       depois do primeiro, e a fileira lia como slideshow em vez de uma
       fileira. 45ms mantém a cascata legível e fecha os seis em 225ms —
       dentro da mesma "batida" do primeiro. */

    [data-reveal] {
      opacity: 0;
      transform: translateY(10px);
      transition:
        opacity var(--dur-entrada) var(--ease-saida),
        transform var(--dur-entrada) var(--ease-saida);
    }

    [data-reveal].is-revealed { opacity: 1; transform: translateY(0); }

    /* Dez posições porque as fileiras de chip do diagnóstico têm mais que
       seis irmãos — sem isto, do sétimo em diante todos entram juntos no
       tempo do primeiro, e a cascata quebra bem no formulário. */
    .stagger > [data-reveal]:nth-child(1)  { transition-delay: 0ms; }
    .stagger > [data-reveal]:nth-child(2)  { transition-delay: 45ms; }
    .stagger > [data-reveal]:nth-child(3)  { transition-delay: 90ms; }
    .stagger > [data-reveal]:nth-child(4)  { transition-delay: 135ms; }
    .stagger > [data-reveal]:nth-child(5)  { transition-delay: 180ms; }
    .stagger > [data-reveal]:nth-child(6)  { transition-delay: 225ms; }
    .stagger > [data-reveal]:nth-child(7)  { transition-delay: 270ms; }
    .stagger > [data-reveal]:nth-child(8)  { transition-delay: 315ms; }
    .stagger > [data-reveal]:nth-child(9)  { transition-delay: 360ms; }
    .stagger > [data-reveal]:nth-child(n+10) { transition-delay: 405ms; }

    /* ---------- tilt 3D (Ponto 6) ----------
       Baseline sempre com uma leve rotação de perspectiva (funciona em
       touch/reduced-motion, que nunca recebem o listener de mousemove —
       ver landing.js). Em mouse de precisão, --tilt-x/--tilt-y são
       atualizados a cada movimento e SOMADOS à baseline; ao tirar o mouse,
       landing.js remove as variáveis e o card volta suavemente ao
       repouso, porque a transição já está declarada aqui. */

    .tilt-card {
      --tilt-x: 0deg;
      --tilt-y: 0deg;
      transform: perspective(1000px) rotateX(calc(2deg + var(--tilt-x))) rotateY(calc(-4deg + var(--tilt-y)));
      /* Esta duração é a da VOLTA ao repouso — longa de propósito, porque
         sair do card não é resposta a nenhuma intenção e pode ter peso. */
      transition:
        transform var(--dur-volta) var(--ease-saida),
        box-shadow var(--dur-volta) var(--ease-saida),
        border-color var(--dur-volta) var(--ease-saida);
    }

    /* E esta é a da IDA. Enquanto o cursor está sobre o card (classe posta
       por landing.js no mousemove), 90ms quase lineares: o card segue a mão
       em vez de perseguí-la. Com os 500ms de expo que havia aqui, cada
       movimento do mouse reiniciava uma interpolação longa e o card ficava
       sempre um passo atrás — é o mesmo defeito do hover de 300ms, só que
       repetido sessenta vezes por segundo.
       A assimetria (90ms para entrar, 420ms para voltar) é o que separa um
       tilt que parece físico de um que parece um efeito. */
    .tilt-card.is-seguindo {
      transition: transform var(--dur-toque) linear;
    }

    .tilt-card:hover { border-color: var(--hairline-forte); }

    /* ---------- cards 3D de produto (Ponto 7) ----------
       "Janela flutuante": sombra em camadas + o tilt-card acima. Ficam
       mais retos (não totalmente planos) no hover — landing.js reduz a
       rotação dinamicamente ao seguir o cursor. */

    .demo-card {
      position: relative;
      margin: 0;
      overflow: hidden;
      border: 1px solid var(--hairline);
      border-top-color: rgba(var(--borda-rgb), .15);
      border-radius: var(--radius-lg);
      background: var(--sup-card), var(--card);
      box-shadow: var(--luz-topo), var(--sombra-3);
    }

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

    .demo-card__chrome {
      display: flex;
      align-items: center;
      gap: 7px;
      padding: 14px 16px;
      border-bottom: 1px solid var(--hairline);
      background: rgba(var(--sup-rgb), .02);
    }

    .demo-card__dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(var(--sup-rgb), .16); }
    .demo-card__title { margin-left: 8px; font-size: 11px; font-weight: 600; letter-spacing: .05em; color: var(--muted-foreground); text-transform: uppercase; }

    /* demo 1: conversa de WhatsApp */

    .demo-chat { display: flex; flex-direction: column; gap: 10px; padding: 22px 20px 30px; min-height: 224px; }

    .demo-bubble {
      max-width: 80%;
      padding: 11px 14px;
      border-radius: 14px;
      font-size: 14px;
      line-height: 1.5;
      opacity: 0;
      transform: translateY(8px);
      transition: opacity var(--dur-volta) var(--ease-saida), transform var(--dur-volta) var(--ease-saida);
    }

    .demo-bubble--in {
      align-self: flex-start;
      background: var(--secondary);
      color: var(--foreground);
      border-bottom-left-radius: 4px;
      opacity: 1;
      transform: translateY(0);
    }

    /* Era #1EA952 em saturação cheia — no mobile este balão era a MAIOR
       massa de cor da página inteira, maior que o CTA, e num verde que nem é
       da nossa marca. Agora usa o verde-escuro que o WhatsApp de verdade usa
       no tema escuro (#005C4B): é simultaneamente mais fiel ao produto que a
       demonstração imita e muito mais quieto. */
    .demo-bubble--out {
      align-self: flex-end;
      background: #0A5B4B;
      color: rgba(var(--luz-rgb), .95);
      border-bottom-right-radius: 4px;
    }

    .demo-bubble--out.is-visible { opacity: 1; transform: translateY(0); }

    .demo-bubble--typing {
      align-self: flex-end;
      display: flex;
      align-items: center;
      gap: 5px;
      padding: 14px 16px;
      /* Mesmo verde-escuro do balão de resposta: eles são a mesma coisa em
         dois momentos, e ficar um em #1EA952 e o outro em #0A5B4B fazia o
         indicador de digitação brilhar mais que a própria resposta. */
      background: #0A5B4B;
      border-bottom-right-radius: 4px;
    }

    .demo-bubble--typing.is-active { opacity: 1; transform: translateY(0); }

    .demo-bubble--typing span {
      width: 6px; height: 6px;
      border-radius: 50%;
      background: rgba(var(--sup-rgb), .85);
      animation: typing-dot 1.1s ease-in-out infinite;
    }

    .demo-bubble--typing span:nth-child(2) { animation-delay: .15s; }
    .demo-bubble--typing span:nth-child(3) { animation-delay: .3s; }

    @keyframes typing-dot {
      0%, 60%, 100% { opacity: .35; transform: translateY(0); }
      30% { opacity: 1; transform: translateY(-3px); }
    }

    /* demo 2: kanban */

    .demo-kanban { position: relative; display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; padding: 22px 20px 24px; }

    /* O trilho de progresso saiu.
       Ele existia porque o mock não tinha conteúdo: com quatro caixas vazias,
       uma barra preenchendo era a única coisa que comunicava "o funil anda".
       Com as colunas cheias e um card mudando de etapa, ele virou uma linha
       branca grossa no rodapé competindo com os leads — informação repetida,
       no elemento mais chamativo do card. */

        /* align-items: stretch (padrão) e não center: os cards precisam ocupar a
       largura da coluna, como ocupam num kanban de verdade. */
    .demo-kcol { display: flex; flex-direction: column; gap: 8px; min-width: 0; }

    .demo-kcol__head { display: flex; align-items: center; justify-content: space-between; gap: 6px; padding: 0 2px 2px; }

    /* Contagem por coluna: é o dado que um kanban sempre mostra e que estava
       faltando para o mock parecer um sistema em uso, e não um diagrama. */
    .demo-kcol__contagem {
      font-size: 10px;
      font-weight: 600;
      color: var(--muted-foreground);
      font-variant-numeric: tabular-nums;
    }

    /* ---------- card de lead dentro do funil ---------- */

    .kmini {
      display: grid;
      gap: 2px;
      padding: 8px 10px 9px;
      border: 1px solid var(--hairline);
      border-top-color: rgba(var(--borda-rgb), .16);
      border-radius: var(--radius);
      background: var(--sup-card), var(--secondary);
      box-shadow: var(--luz-topo), var(--sombra-1);
      min-width: 0;
    }

    /* O segundo card de cada coluna fica um degrau mais apagado: dá
       profundidade à pilha sem inventar mais um tom de superfície. */
    .kmini--secundario { opacity: .62; }

    .kmini__nome,
    .kmini__item,
    .kmini__valor { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

    .kmini__nome { font-size: 11.5px; font-weight: 600; letter-spacing: -.01em; color: var(--foreground); }
    .kmini__item { font-size: 10.5px; color: var(--muted-foreground); }
    .kmini__valor { margin-top: 3px; font-size: 11px; font-weight: 600; letter-spacing: -.015em; color: rgba(var(--luz-rgb), .78); font-variant-numeric: tabular-nums; }

    .demo-kcol__label {
      font-size: 10px;
      font-weight: 600;
      letter-spacing: .04em;
      text-transform: uppercase;
      color: var(--muted-foreground);
      transition: color var(--dur-volta) var(--ease-estado);
    }

    .demo-kcol__label.is-active { color: var(--foreground); }

    /* Espaço que ABRE na coluna que vai receber o lead, como acontece quando
       se arrasta um card num kanban de verdade — e altura zero em todas as
       outras, para os cards ficarem colados no cabeçalho.
       Reservar espaço nas quatro deixava um vazio de 60px sob três títulos: a
       caixa tracejada tinha sumido, mas o buraco que ela ocupava não. */
    .demo-kcol__slot {
      position: relative;
      width: 100%;
      height: 0;
      transition: height var(--demo-encaixe) var(--ease-out);
    }

    .demo-kcol__slot.is-active { height: 68px; }

    /* A coluna onde o lead está agora ganha um leve realce de fundo — no
       lugar do que antes era a borda acesa do slot. O realce vai na COLUNA e
       não no slot porque o slot fica escondido atrás do card em trânsito. */
    .demo-kcol:has(.demo-kcol__slot.is-active) .demo-kcol__head { color: var(--foreground); }

    .demo-kcol__slot.is-landing::after {
      content: "";
      position: absolute;
      inset: -2px;
      border-radius: var(--radius);
      border: 1px solid rgba(var(--borda-rgb), .3);
      animation: kslot-ring .62s cubic-bezier(.22, 1, .36, 1) forwards;
    }

    @keyframes kslot-ring {
      0% { opacity: .9; transform: scale(1); }
      100% { opacity: 0; transform: scale(1.06); }
    }

    /* O lead em trânsito é um card IGUAL aos das colunas (herda .kmini) — o
       que muda é que ele é absoluto e desliza. Antes era uma etiqueta de
       formato próprio correndo por baixo das colunas, o que lia como um
       elemento de outra natureza; agora lê como um card mudando de etapa,
       que é o que acontece de verdade no CRM.
       left/top vêm do JS, medidos do slot reservado no topo da coluna. */
    .demo-klead {
      position: absolute;

      /* left: 0, não 20px. --col-x é medido como (slot.left - kanban.left) e
         portanto JÁ contém o padding lateral do card — somar 20px aqui punha
         o card em trânsito 20px à direita da coluna dele, encostando no card
         da coluna seguinte. */
      left: 0;
      top: var(--col-y, 60px);
      width: var(--col-w, 120px);
      border-color: rgba(var(--borda-rgb), .22);
      border-top-color: rgba(var(--borda-rgb), .3);
      background: var(--sup-alta), #1E2126;
      box-shadow: var(--luz-topo), var(--sombra-3);

      /* translate/scale como propriedades independentes (não no shorthand
         transform) justamente pra terem durações diferentes: o deslocamento
         é longo e suave, enquanto o "pegar e soltar" é curto. Com tudo num
         transform só, os dois compartilhariam a mesma curva e o movimento
         voltaria a parecer um salto seco. */
      translate: var(--col-x, 0px) 0;
      scale: 1;
      transition:
        translate var(--demo-viagem) var(--ease-out),
        /* `top` muda quando a grade tem duas linhas (mobile) e a abertura de
           um espaço na linha de cima empurra a de baixo. Transicionado para a
           correção deslizar em vez de saltar. */
        top var(--demo-encaixe) var(--ease-out),
        scale var(--demo-gesto) var(--ease-estado),
        box-shadow var(--demo-gesto) var(--ease-estado),
        opacity var(--dur-rapida) var(--ease-estado);
    }

    /* Em trânsito: sobe de leve e projeta sombra maior, como um cartão
       erguido da mesa. A classe sai antes do fim do percurso, então a
       descida acontece já chegando — o efeito é um arco, não um teleporte. */
    .demo-klead.is-moving {
      scale: 1.045;
      box-shadow: 0 18px 34px rgba(var(--sombra-rgb), .45);
      border-color: var(--hairline-forte);
    }

    /* Volta do fim do funil pro começo: em vez de desfazer o caminho de
       trás pra frente (que leria como "o lead regrediu"), a etiqueta some e
       reaparece na primeira coluna, como um lead novo entrando. */
    .demo-klead.is-resetting { opacity: 0; }
    .demo-klead.is-instant { transition: opacity var(--dur-rapida) var(--ease-estado); }


    /* ---------- mobile ---------- */

    /* A barra troca de forma MUITO antes do resto da página, e o limiar veio
       de medição, não de palpite.

       Com os links na barra, o conteúdo dela precisa de ~1010px. Medindo
       .nav.scrollWidth contra a largura real:

         781px  ->  937 num espaço de 719   ESTOURA
         880px  ->  943 num espaço de 810   ESTOURA
         1024px ->  951 num espaço de 942   ESTOURA
         1100px -> 1010 num espaço de 1012  cabe

       Ou seja: entre 781 e ~1090 o CTA "Ver diagnóstico" vazava para fora da
       barra — inclusive em iPad deitado, que é 1024. O limiar original de
       768px vinha de uma convenção, não de uma conta.

       1100px é o primeiro valor redondo acima do ponto de estouro. Perder os
       links para dentro do painel um pouco cedo custa um toque a mais; deixar
       o CTA cortado custa a ação principal.

       Só a TROCA DE FORMA mora aqui. Os apertos de espaçamento (margem menor,
       marca menor) continuam a 780px, porque são sobre tela estreita, não
       sobre o conteúdo da barra. */
    @media (max-width: 1100px) {
      /* Os links saem da barra e vão para o painel. Empilhá-los na barra era
         o que ela NÃO pode fazer — deixaria de ser uma barra. */
      .nav__links { display: none; }
      .nav__toggle { display: block; }
      .nav__panel { display: block; }
    }

    /* O frame perde uma coluna de cada vez, na ordem inversa da importância:
       primeiro a navegação (decorativa aqui), depois o painel do lead, por
       último a fila. O que nunca sai é a CONVERSA — é ela que conta o que o
       produto faz. Espremer as quatro colunas numa tela estreita produziria
       exatamente o que a página não pode ter: um mock ilegível fingindo ser
       um produto. */
    @media (max-width: 1240px) {
      .app__corpo { grid-template-columns: 292px minmax(0, 1fr) 256px; }
      .app__rail { display: none; }
    }

    @media (max-width: 1000px) {
      .app__corpo { grid-template-columns: 280px minmax(0, 1fr); }
      .app__lead { display: none; }
    }

    @media (max-width: 900px) {
      .consultor { grid-template-columns: 1fr; }
      /* Coluna única: a faixa de 4 vira 2, e a conclusão volta a atravessar. */
      .metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
      .metric--claim { grid-column: 1 / -1; }
      .footer__grid { grid-template-columns: 1fr 1fr; }
    }

    @media (max-width: 780px) {
      /* Menos folga no topo: numa tela de 667px de altura, 20px de margem
         custam quase 3% da primeira dobra. */
      .nav-wrap { --nav-gap-topo: 12px; --nav-pad-y: 7px; }
      .nav-wrap.is-scrolled { --nav-gap-topo: 8px; --nav-pad-y: 5px; }

      .nav { padding: var(--nav-pad-y) var(--nav-pad-y) var(--nav-pad-y) 16px; gap: 10px; }

      .nav__cta { padding: 9px 15px; font-size: 14px; }

      /* FORA no mobile.
         Medi a sobreposição com os glifos renderizados, varrendo a página
         inteira a 375px: o botão encostava em texto em 12 pontos de rolagem.
         Não é ajustável — numa tela de 375px com margem de 20px, um botão
         fixo no canto ocupa os últimos ~38px da coluna de conteúdo, e não
         existe tamanho ou posição que o tire de cima do texto.
         A página tem outras quatro entradas para o WhatsApp (botão do hero,
         seção "Prefere resolver agora?", rodapé e o link do topo), então o
         que se perde aqui é redundância — e o que se ganha é a leitura.
         No desktop ele fica: lá sobra margem lateral e ele não cobre nada. */
      .wa-float { display: none; }

      /* 28px em vez de 34px: a assinatura é larga (4,1:1), e a 375px a barra
         precisa caber marca + botão de menu + CTA na mesma linha. */
      .nav-wrap { --nav-marca: 28px; }
      .nav-wrap.is-scrolled { --nav-marca: 26px; }

      /* Hero mais curto no mobile: o que precisa caber acima da dobra é
         headline + subheadline + os dois CTAs. A nota do rodapé do hero
         some — ela é reforço, e disputaria espaço com o botão.

         O frame aparece inteiro logo abaixo do CTA — mais curto, sem corte,
         sem disputar a primeira dobra, que aqui é inegociável. */
      .hero { padding: 92px 0 0; }
      .hero__title { max-width: none; font-size: clamp(29px, 8.2vw, 40px); }
      .hero__deck { font-size: 16px; }

      /* Empilha os CTAs em largura total: alvo de toque generoso e sem
         risco de os dois botões brigarem por espaço a 375px. */
      .hero__actions { width: 100%; gap: 10px; margin-top: 24px; }
      .hero__actions .btn { flex: 1 1 100%; justify-content: center; }
      .hero__note { display: none; }

      /* No mobile a mesma quantidade de luz ocupa proporcionalmente muito
         mais tela: a 375px, feixes com a largura do desktop viram um fundo
         laranja chapado atrás da headline. Ficam três em vez de cinco, mais
         estreitos, e o véu fecha mais. O alvo de cromaticidade aqui é menor
         de propósito — 8 a 10%, contra 12 a 18% no desktop. */
      /* Feixes bem mais largos e menos opacos: a mesma quantidade de luz
         ocupa proporcionalmente muito mais tela a 375px. E fica um feixe a
         menos em cada camada — o alvo de cromaticidade aqui é 8 a 12%, contra
         17 a 24% no desktop. */
      .hero__feixe--1 {
        background-size: 62% 100%, 66% 100%;
        background-position: -16% 50%, 62% 50%;
        opacity: .5;
      }
      .hero__feixe--2 {
        background-size: 34% 100%, 30% 100%;
        background-position: 10% 50%, 78% 50%;
        opacity: .34;
      }

      .hero__veu {
        background:
          radial-gradient(88% 40% at 50% 27%, rgba(8, 6, 5, .94), rgba(8, 6, 5, .7) 55%, transparent 86%),
          linear-gradient(180deg, rgba(8, 6, 5, .58), transparent 24%);
      }

      /* Só a conversa. A fila de nomes a 375px vira uma coluna de 120px com
         tudo cortado por reticências — informação nenhuma, ruído todo. */
      .app__corpo { grid-template-columns: minmax(0, 1fr); height: 342px; }
      .app__lista { display: none; }
      .app__rota { display: none; }
      .app__chrome { height: 38px; gap: 12px; }
      .app__msg { max-width: 84%; }
      .hero__palco { margin-top: 30px; }

      /* Cantos superiores retos: encostando nas duas bordas de uma tela
         estreita, o raio de 16px deixa duas fatias de fundo visíveis no topo
         e o frame parece descolado da página. */
      .app { border-radius: 12px 12px 0 0; border-left: 0; border-right: 0; }

      /* Duas colunas: marcador e conteúdo empilhado. A coluna do nome da
         etapa não cabe ao lado da descrição numa tela estreita. */
      .method__step {
        grid-template-columns: 38px minmax(0, 1fr);
        column-gap: 16px;
        row-gap: 4px;
        padding-bottom: 26px;
      }
      .method__step::before { left: 18px; top: 40px; }
      .method__step .step-num { width: 38px; height: 38px; font-size: 12px; }
      .method__title { min-height: 38px; }
      .method__deck { min-height: 0; display: block; grid-column: 2; }

      .moments { grid-template-columns: 1fr; }
      .diag__grid { grid-template-columns: 1fr; }
      .metrics { grid-template-columns: 1fr; }
      .metric--claim { grid-column: auto; }
      .footer__grid { grid-template-columns: 1fr; }
      .wa-float { right: 16px; bottom: 16px; }

      /* Toque não tem cursor pra seguir — landing.js nem liga o listener de
         tilt nesses dispositivos (media query hover:none), mas a rotação
         BASELINE do CSS ainda existiria sem isso; zeramos aqui pra manter
         só a sombra em camadas como "profundidade estática", sem o card
         rotacionado ocupando espaço extra numa tela estreita. */
      .tilt-card { transform: none; }
      .demo-card--chat { max-width: 100%; }
      .demo-kanban { grid-template-columns: repeat(2, minmax(0, 1fr)); row-gap: 18px; }

      /* Só o primeiro card de cada coluna: com dois, o mock viraria uma torre
         de ~600px numa tela de 667px. Um card por coluna já mostra que a
         coluna tem conteúdo, que é o que ele precisa dizer. */
      .kmini--secundario { display: none; }
    }

    /* Abaixo de 380px (iPhone SE e afins) três elementos na mesma linha ficam
       no limite. O CTA é o único que não pode encolher a ponto de virar um
       alvo ruim, então quem cede é a marca e o espaçamento. */
    @media (max-width: 380px) {
      .nav-wrap { --nav-marca: 26px; }
      .nav { padding-left: 12px; gap: 6px; }
      .nav__actions { gap: 2px; }
      .nav__cta { padding: 9px 13px; font-size: 13.5px; }
    }

    @media (prefers-reduced-motion: reduce) {
      html { scroll-behavior: auto; }
      *, *::before, *::after {
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .01ms !important;
      }
      /* A barra continua trocando de estado ao rolar (o vidro fica mais
         denso, a altura encolhe) — o que some é a interpolação. Quem pediu
         menos movimento não pediu menos contraste. O painel do mobile também
         passa a aparecer e sumir sem deslizar. */
      .nav-wrap, .nav, .nav__brand img, .nav__panel, .nav__toggle-bar,
      .wa-float {
        transition: none !important;
      }

      /* A entrada do hero é `backwards`, então sem isto o conteúdo ficaria
         no estado inicial (opacity 0) até a animação de .01ms terminar.
         Zerar a animação aqui garante o hero visível de imediato. */
      .hero__anim { animation: none; }

      /* Loops contínuos desligados de vez. A regra global acima já corta a
         duração pra .01ms, mas com `alternate` + `infinite` isso deixaria os
         elementos travados no estado final do keyframe, não no inicial —
         `animation: none` é o único jeito de garantir o repouso. O fundo
         continua com gradiente e brilhos, só que parados. */
      /* Os feixes já são estáticos para todo mundo (ver a nota de
         desempenho em .hero__feixe), então não há nada a parar neles — e a
         LUZ fica: quem pediu menos movimento não pediu menos página. O que
         para é o gradiente do CTA e o pulso do ponto de status. */
      .btn--primary,
      .app__ping::after { animation: none; }

      /* Os pontos vivos e a luz que desce o trilho param, e param no estado
         ACESO de repouso — não no vazio. O ponto continua laranja com halo, o
         nó continua com o anel quente, o trilho continua desenhado. Quem
         pediu menos movimento não pediu menos página: o que sai é a
         interpolação, não a cor nem o sinal. */
      .badge::before,
      .metric--claim dd::before,
      .method__step::before,
      .method__step .step-num { animation: none; }

      /* Os três pontos de "digitando" param, mas o balão CONTINUA: ele é o
         que diz que a IA está respondendo naquele instante, e essa é a única
         informação do frame que não está escrita em texto. Parado, ele lê
         como um indicador estático — que é exatamente o certo aqui. */
      .app__digitando span { animation: none; opacity: .75; transform: none; }
      /* Conteúdo continua visível e funcional (Ponto de validação): reveal
         já é garantido por landing.js (IntersectionObserver roda sempre),
         e o tilt dinâmico nem é ligado quando reduced-motion está ativo —
         ver landing.js. Só a rotação baseline "de repouso" é neutralizada
         aqui, pra ninguém com essa preferência ver um card inclinado parado. */
      .tilt-card { transform: none; }
    }

/* ---------- erro de validação do formulário de diagnóstico ---------- */
/* Só usado quando /diagnostico rejeita o POST (campo obrigatório faltando)
   e re-renderiza a mesma landing com o motivo — cor própria da paleta da
   landing (vermelho sobre --card escuro), não um token do produto. */

.diag__alert {
  margin-top: 20px;
  padding: 14px 16px;
  border: 1px solid rgba(239, 68, 68, .4);
  border-radius: var(--radius);
  background: rgba(239, 68, 68, .12);
  color: #FCA5A5;
  font-size: 14px;
  font-weight: 500;
  line-height: 1.5;
}

/* ---------- tela de confirmação (/diagnostico, sucesso) ---------- */

.confirm {
  min-height: 100svh;
  display: grid;
  place-items: center;
  padding: var(--gutter);
}

.confirm__card {
  max-width: 520px;
  padding: clamp(36px, 5vw, 56px);
  border: 1px solid var(--hairline);
  border-top-color: rgba(var(--borda-rgb), .14);
  border-radius: var(--radius-lg);
  background: var(--sup-card), var(--card);
  box-shadow: var(--luz-topo), var(--sombra-3);
  text-align: center;
  display: grid;
  justify-items: center;
  gap: 18px;
}

.confirm__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: 50%;
  background: rgba(37, 211, 102, .16);
  color: var(--whats);
  font-size: 26px;
}

.confirm__card h1 {
  font-size: clamp(24px, 2.6vw, 32px);
}

.confirm__card p {
  max-width: 34rem;
  font-size: 16px;
  line-height: 1.6;
  color: var(--muted-foreground);
}

.confirm__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 6px;
}
