/*
   BG DOOH · Estilos e componentes da página.
   Design tokens + componentes. Mobile-first. Zero dependência externa.
   Nenhum valor mágico: todo componente consome variáveis do :root.
   */

/* 1 · DESIGN TOKENS */
:root {
  /* Tema escuro é o único do projeto desde 15/08. `color-scheme` faz o
     navegador desenhar barra de rolagem, campo de formulário e menu nativo na
     variante escura: sem isto, quem está com o sistema no claro vê a página
     escura com widgets brancos aparecendo no meio dela. */
  color-scheme: dark;

  /* COR
     Primária estimada visualmente do logo em PNG.
     [PENDENTE: validar hex com o logo em vetor]
     Contraste medido: #F4600E sobre #0D0B09 = 6.11:1 (AA em texto normal). */
  --color-primary:        #F4600E;   /* OKLCH L 0.672  C 0.197  H 42.3° */
  --color-primary-hover:  #FF7526;   /*      L 0.717  C 0.188  H 45.5° */
  --color-primary-ink:    #0D0B09;   /* texto SOBRE o laranja. 6.11:1 */
  --color-primary-soft:   rgba(244, 96, 14, 0.12);

  /* NEUTROS COM TEMPERATURA · corrigido em 15/08.
     Medidos em OKLCH, os seis neutros estavam todos em matiz 286°, que é
     azul-violeta. A marca está em 42°, laranja. A paleta inteira puxava para
     o lado oposto do próprio acento, e é isso que faz uma interface escura
     parecer genérica: cinza frio embaixo de acento quente lê como duas
     paletas coladas, não como uma.

     Todos foram levados para ~70°, o vizinho quente do laranja, sem virar
     bege. A LUMINÂNCIA DE CADA UM FOI PRESERVADA no terceiro decimal, então
     todos os pares de contraste continuam idênticos: a maior variação medida
     foi de 0,03:1, e nenhum par mudou de veredito.

     Croma entre 0.004 e 0.019: acima disso a superfície começa a competir com
     o acento; abaixo, o olho lê como cinza de renderização e não como
     escolha.                              L      C       H                  */
  --color-surface:        #0D0B09;  /* 0.151  0.0042   67.8°  base          */
  --color-surface-alt:    #161411;  /* 0.192  0.0040   78.2°  seção alterna */
  --color-surface-raised: #1E1B18;  /* 0.224  0.0077   67.4°  cartão elevado*/

  --color-text:           #FBFAF8;  /* 18.7:1 sobre a base. Branco neutro
                                       sobre superfície quente lê frio, então
                                       leva o mesmo croma mínimo dos fundos. */
  --color-text-muted:     #A5A19D;  /* 7.66:1 (era 7.64:1 em #A1A1A6)        */
  /* Decorativa: separa superfícies, não delimita controle, então não tem
     exigência de contraste (SC 1.4.11 vale para o -strong abaixo). Derivada
     do texto: 11% de #FAFAFA sobre a superfície base dá o mesmo #26262A de
     antes, e sobre a rua animada ou o painel de leitura ela acompanha o que
     está atrás. Cinza fixo sobre fundo que se move ora some, ora salta. */
  --color-border:         color-mix(in srgb, var(--color-text) 11%, transparent);
  /* Delimita controles (botão ghost, input, chip, ícone social).
     WCAG 2.2 SC 1.4.11 exige 3:1 contra o fundo REAL. Como esses controles
     aparecem sobre as três superfícies, o valor foi calculado contra a pior
     delas (--color-surface-raised). O #38383E anterior dava 1.69:1.
     #686874 dá 3.58 / 3.35 / 3.12:1 nas três. */
  --color-border-strong:  #71685E;   /* 3.60 / 3.37 / 3.14:1 nas três (era
                                        #686874, 3.58 / 3.35 / 3.12:1) */
  /* Contorno das caixas de conteúdo. O laranja da marca a 70% sobre as
     superfícies escuras dá 3.35:1, acima do mínimo de 3:1 do SC 1.4.11, e
     mesmo assim continua sendo fio de 1px: quem carrega o acento cheio é o
     botão, que é área pintada, e a diferença entre fio e área é o que mantém
     a ordem de leitura mesmo com a cor repetida na página inteira. */
  --color-border-accent:  color-mix(in srgb, var(--color-primary) 70%, transparent);

  --color-success:        #35C46B;
  --color-error:          #FF6B6B;

  /* TIPOGRAFIA
     [PENDENTE: self-hospedar uma sans geométrica em WOFF2 para bater com o
     logo. Stack de sistema por enquanto: zero requisição, zero CLS de fonte.] */
  --font-display: "Segoe UI Variable Display", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  --font-body:    "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;

  --text-xs:   clamp(0.75rem, 0.7rem + 0.2vw, 0.8125rem);
  --text-sm:   clamp(0.875rem, 0.85rem + 0.2vw, 0.9375rem);
  --text-base: clamp(1rem, 0.95rem + 0.25vw, 1.0625rem);
  --text-lg:   clamp(1.125rem, 1rem + 0.5vw, 1.375rem);
  --text-xl:   clamp(1.375rem, 1.2rem + 0.8vw, 1.75rem);
  --text-2xl:  clamp(1.75rem, 1.4rem + 1.5vw, 2.5rem);
  --text-3xl:  clamp(2.25rem, 1.6rem + 2.8vw, 4rem);

  --leading-tight: 1.12;
  --leading-body:  1.65;

  /* ENTRELETRA EM FUNÇÃO DO TAMANHO, não por elemento.
     A regra ótica: quanto maior o tipo, mais os brancos entre as letras
     parecem crescer, e mais precisa ser fechado. Um mesmo -0.02em num título
     de 64px e num de 22px deixa o grande solto e o pequeno apertado. Antes
     desta rodada h1, h2 e h3 compartilhavam -0.02em e havia mais oito valores
     soltos pelo arquivo, nenhum ligado ao tamanho.

     Caixa alta pequena vai para o outro lado: maiúscula não tem ascendente
     nem descendente para o olho se apoiar, então precisa de ar. */
  /* COMPENSAÇÃO DE DESCENDENTE. Recortar a palavra por overflow, que é o
     gesto de ela nascer de dentro da linha, corta o rabo do g, do p, do q e
     do y. O par padding/margem devolve o espaço sem mexer no layout. Em `em`
     porque a descendente é proporcional ao corpo do tipo.
     Estava 0,14em na camada de movimento e 0,16em na splash: mesmo problema,
     dois palpites, e ninguém saberia dizer qual dos dois estava certo. */
  --descida: 0.16em;

  --tracking-display: -0.03em;   /* --text-3xl · manchete do herói  */
  --tracking-title:   -0.02em;   /* --text-2xl · título de seção    */
  --tracking-sub:     -0.01em;   /* --text-xl  · título de cartão   */
  --tracking-mark:   -0.05em;    /* monograma (avatar, sigla)       */
  --tracking-flat:      0;       /* corpo: qualquer ajuste atrapalha */
  --tracking-small:  0.02em;     /* caixa baixa pequena             */
  --tracking-caps:   0.08em;     /* caixa alta pequena              */
  --tracking-wide:   0.14em;     /* caixa alta muito pequena        */

  /* --- ESPAÇAMENTO (base 4px) --- */
  --space-1: 0.25rem;  --space-2: 0.5rem;   --space-3: 0.75rem;
  --space-4: 1rem;     --space-5: 1.25rem;  --space-6: 1.5rem;
  --space-8: 2rem;     --space-10: 2.5rem;  --space-12: 3rem;
  --space-16: 4rem;    --space-24: 6rem;
  --section-y: clamp(3.5rem, 8vw, 7rem);
  /* Altura minima da barra, a cheia e a compacta depois da primeira dobra.
     Em rem para acompanhar a fonte do usuario; a 16px continua 68 e 60.
     Ancoras, sticky da decisao e folga da pill do hero leem daqui. */
  --header-h: 4.25rem;
  --header-h-compact: 3.75rem;
  /* Teto de medida: 60ch no glifo 0. Em texto corrido isso cai perto de
     75 caracteres reais, que e o limite em que o olho ainda acha a proxima linha. */
  --medida: 60ch;

  /* --- FORMA E ELEVAÇÃO --- */
  --radius-sm:   8px;
  --radius-md:   14px;
  --radius-lg:   20px;
  --radius-full: 999px;

  /* ELEVAÇÃO · duas camadas, sempre. Sombra real tem contato curto e escuro
     logo abaixo (diz que o objeto está POUSADO) e difusão longa e fraca em
     volta (diz a que altura). Uma camada só entrega a difusão sem o contato,
     e o cartão fica com cara de adesivo colado. As duas eram de camada única.
     No escuro a sombra é apoio: quem comunica altura é a superfície ficando
     mais clara (surface → alt → raised). Por isso opacidades moderadas. */
  --shadow-sm:
    0 1px 2px rgba(0, 0, 0, 0.30),
    0 2px 6px rgba(0, 0, 0, 0.18);
  --shadow-md:
    0 2px 4px rgba(0, 0, 0, 0.28),
    0 12px 28px rgba(0, 0, 0, 0.30);
  --shadow-lg:
    0 4px 8px rgba(0, 0, 0, 0.30),
    0 24px 56px rgba(0, 0, 0, 0.38);

  /* BRILHO ≠ ELEVAÇÃO. Sombra situa no espaço; brilho é acento de marca.
     Misturar os dois num token só foi o que espalhou cinco literais laranja
     por três arquivos. */
  --glow-primary:      0 0 0 1px var(--color-primary), 0 0 24px rgba(244, 96, 14, 0.22);
  --glow-primary-soft: 0 0 12px rgba(244, 96, 14, 0.45);

  /* VÉU · escurecer o que está atrás para o texto claro passar em contraste.
     Eram quatro literais com quatro alfas (0.55, 0.58, 0.62 e 0.80) para o
     mesmo problema, em três lugares diferentes: véu do menu, chapa da badge
     do totem e botão de som. Quatro valores para uma decisão só é o sintoma
     clássico de valor arbitrário: ninguém escolheu 0.58 em vez de 0.55, foi
     digitado. Dois níveis dão conta, e a diferença entre eles é perceptível.
     Preto puro e não o neutro da marca: véu não é superfície, é ausência de
     luz, e qualquer croma aqui tinge a foto que está por baixo. */
  --veil:        rgba(0, 0, 0, 0.55);   /* camada sobre conteúdo: menu       */
  --veil-strong: rgba(0, 0, 0, 0.72);   /* chapa sobre foto ou vídeo         */

  /* LAYOUT E BREAKPOINTS
     ESCALA ÚNICA DO PROJETO. As cinco camadas de CSS (styles, motion,
     assistant, footer, splash) usam exatamente estes valores. Antes havia
     480, 560, 600, 639, 640 e 900 espalhados pelos arquivos, o que criava
     faixas de largura em que uma camada já tinha trocado de layout e a
     outra ainda não.

       sm   480px   celular grande
       md   640px   tablet em retrato
       lg   900px   tablet em paisagem e notebook
       xl  1200px   desktop
       xxl 1600px   monitor grande e ultrawide

     Custom property dentro de @media ainda não é suportada, então os valores
     aparecem literalmente nas queries. Estes tokens são a referência: mudou
     aqui, mude nas queries.                                                 */
  --bp-sm:   480px;
  --bp-md:   640px;
  --bp-lg:   900px;
  --bp-xl:  1200px;
  --bp-xxl: 1600px;

  /* O container cresce por degraus (ver seção 16). Sem isso, num monitor de
     2560px sobravam 680px vazios de cada lado com o conteúdo espremido. */
  --container: 1200px;
  --container-narrow: 760px;
  --gutter: clamp(1rem, 4vw, 2rem);

  /* --- MOVIMENTO (baseline. O SOAP amplia, não substitui) --- */
  /* SISTEMA DE MOVIMENTO · três durações e duas curvas, e nada fora disso.
     Entrada usa --ease-out (out-expo): começa rápido e assenta devagar, que é
     como algo chegando se comporta. Saída usa --ease-in: começa devagar e
     acelera para fora, que é como algo indo embora se comporta. Usar a mesma
     curva nos dois sentidos é o erro que faz interface parecer borracha. */
  --duration-fast: 150ms;   /* resposta de toque e hover */
  --duration-base: 250ms;   /* padrão de transição de estado */
  --duration-slow: 400ms;   /* entrada de painel e revelação */
  /* Fora da faixa de interação de propósito e por isso declarado, não solto
     no meio de uma regra. A camada da rua não é um estado que responde a
     alguém: é atmosfera acendendo. Em 400ms ela pisca; em 1,4s ela chega. */
  --duration-ambient: 1400ms;
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);   /* entrada */
  --ease-in:     cubic-bezier(0.4, 0, 1, 1);      /* saída */
  --ease-in-out: cubic-bezier(0.45, 0, 0.55, 1);  /* percurso fechado */

  /* --- Z-INDEX · ESCALA FECHADA (registro central, Bloco de Passagem §8) ----
     A escala parava no --z-modal, e as duas camadas acima disso resolveram
     sozinhas: assistente com 99999, splash com 2147483000, o maior inteiro
     que o navegador aceita. Funcionava e era beco sem saída. Agora as seis
     cabem na mesma régua, com folga de mil entre elas.                      */
  --z-base:    1;      /* conteúdo que só precisa ficar acima de um ::before  */
  --z-sticky:  100;    /* barra que gruda ao rolar                            */
  --z-header:  200;    /* cabeçalho fixo                                      */
  --z-overlay: 500;    /* véu do menu de tela cheia                           */
  --z-modal:   1000;   /* painel do menu sobre o véu                          */
  --z-chatbot: 2000;   /* assistente (CONCIERGE, FASE 5)                      */
  --z-splash:  3000;   /* abertura. Acima de tudo porque é temporária         */
}

/* A --color-primary do tema claro já passa em AA como texto (5.09:1), então
   .accent, .eyebrow e .step__num não precisam mais de uma cor à parte.
   Uma variável a menos para manter dessincronizada. */

/* 2 · RESET E BASE */
*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-real, var(--header-h)) + 8px);
  /* A GOTEIRA DA BARRA DE ROLAGEM.
     Duas coisas travam a rolagem nesta página: a splash (splash.css) e o menu
     de tela cheia (§ do menu). As duas fazem overflow: hidden no <html>, o que
     em desktop com barra clássica FAZ A BARRA SUMIR e a página inteira saltar
     uns 15px para a direita. Na splash o salto acontece duas vezes: quando a
     abertura cobre a tela e de novo quando a cortina sobe, exatamente no
     instante em que a marca aparece.
     `stable` reserva a goteira o tempo todo, então esconder a barra não move
     mais nada. Navegador sem suporte ignora e volta ao comportamento de
     antes: não piora nada em lugar nenhum. */
  scrollbar-gutter: stable;
}

body {
  margin: 0;
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--color-text);
  background-color: var(--color-surface);
  -webkit-font-smoothing: antialiased;
  transition: background-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out);
}

/*
   FUNDO AMBIENTE
   Brasa em movimento por trás de toda a página.

   z-index -1 funciona porque o <html> não declara background: o do <body>
   é propagado para a raiz e pintado antes de tudo. O ambiente fica entre
   essa cor de raiz e o conteúdo, sem precisar mexer em nenhum z-index de
   componente.

   DUAS CAMADAS, uma para cada capacidade:
     · o gradiente aqui embaixo é o piso. Sempre existe, custa quase nada e
       roda em qualquer aparelho.
     · o canvas por cima só é ligado pelo JS no desktop, em tema escuro e sem
       movimento reduzido. Se o WebGL2 faltar ou o shader não compilar, ele
       nunca fica visível e o gradiente segue sozinho.

   O TETO DE 0,14 NO ALFA DO SHADER NÃO É ESTÉTICA, É MEDIÇÃO. Acima disso a
   borda de controle (#686874, que precisa de 3:1) cai abaixo do mínimo. Está
   escrito no GLSL, não aqui, para o efeito nascer dentro do limite em vez de
   ser apagado por um véu depois.
   */
.ambient {
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  overflow: hidden;

  /* Piso: três manchas quentes que derivam devagar. Puro gradiente, sem
     imagem e sem GPU dedicada. É o que o celular e o tema claro recebem. */
  background:
    radial-gradient(60% 45% at 18% 12%,
      color-mix(in srgb, var(--color-primary) 5%, transparent), transparent 70%),
    radial-gradient(50% 40% at 84% 34%, rgba(244, 96, 14, 0.035), transparent 72%),
    radial-gradient(70% 50% at 46% 88%, rgba(255, 117, 38, 0.03), transparent 74%);
  background-size: 180% 180%, 170% 170%, 200% 200%;
}

/* Atenção: A DERIVA DO PISO SAIU, E O MOTIVO É MEDIDO.
   Aqui havia `animation: ambient-deriva 46s ease-in-out infinite alternate`,
   que anima `background-position` de três gradientes radiais num elemento
   `position: fixed; inset: 0`. Isso é repintura de viewport inteira a cada
   quadro, e era o item mais caro da página inteira:

     configuração                        1440×900 dpr2   3840×2160 dpr1
     como estava                          34,3ms 29fps    165,1ms  6fps
     sem a deriva                         19,3ms 52fps     40,1ms 25fps
     sem deriva, marquee e a rua          16,9ms 59fps     23,7ms 42fps

   Ou seja: ela sozinha custava 15ms por quadro em 1440 e 125ms em 4K, mais
   do que a cena da avenida inteira, que custa 2,8ms e 9,6ms. E estava quase
   toda escondida atrás do canvas, que roda com --rua em 1.

   O piso continua: são os mesmos três gradientes, parados. O que sumiu foi o
   deslocamento, que ninguém via e todo mundo pagava. */

/* Atenção: A CENA NÃO É MAIS ATENUADA POR OPACIDADE, E ESTE É O LUGAR ONDE ISSO SE
   VÊ (01/09). Aqui havia `--rua: 0.78`, um `--rua: 0.62` dentro de um @media
   e a camada rodando a 62% de opacidade. Não há mais nada disso: o canvas
   compõe a 100% e o recuo acontece na PALETA, em ambient-street.js.

   Por que a troca. Opacidade sobre uma superfície quase preta é, de fato,
   redução de contraste; o argumento que sustentou o mecanismo em 31/08 está
   certo. O que ele não faz é distinguir: é um fator só, uniforme, que recua o
   farol e recua junto o asfalto, o meio-fio e a silhueta do caminhão, que não
   estavam competindo com nada. Para domar os painéis o número teve que descer
   a 0,62, e levou a cena inteira junto.

   A compressão de luminância distingue. Abaixo do joelho não toca em nada;
   acima dele satura suavemente num teto. O resultado medido é uma cena que
   recua MAIS onde competia e MENOS onde não competia, e o número que
   importa, a fração de tela acesa, caiu de 30,9% para 6,4% no monitor.

   Atenção: E NÃO PONHA `filter` AQUI. Já foi tentado e foi medido: `filter:
   saturate() brightness()` neste elemento levava o quadro de 16,70ms para
   50,00ms em 1440×900 (de 60 para 20fps). Filtro numa camada de viewport
   inteira que muda todo quadro obriga o compositor a refazer a passagem 60
   vezes por segundo. A paleta faz a mesma coisa uma vez, no carregamento, de
   graça. */
.ambient__gl {
  display: block;
  width: 100%;
  height: 100%;

  /* A opacidade que sobrou é só a entrada da camada: nasce em 0 e o JS
     adiciona .is-on quando a cena está pronta. Não é mais um botão de
     intensidade: é uma transição de uma vez só. */
  opacity: 0;
  transition: opacity var(--duration-ambient) var(--ease-out);
}
.ambient__gl.is-on { opacity: 1; }

/*
   VÉU CINEMATOGRÁFICO
   Três camadas de gradiente escuro, ESTÁTICAS, por cima do canvas e por baixo
   de tudo mais. Um pseudo-elemento do .ambient, então herda o z-index -1 e o
   pointer-events: none do pai: é impossível ele encostar na interface.

   É estático de propósito. O comentário logo acima documenta que animar
   background-position num elemento fixed de viewport inteira custava 15ms por
   quadro em 1440 e 125ms em 4K. Gradiente parado pinta uma vez e vira textura
   de camada: custo de composição, não de repintura.

   Por que véu E filtro, e não só um dos dois: o filtro age igual em toda a
   tela, e o problema não é uniforme. O céu do alto é a região mais clara da
   cena e é exatamente onde o cabeçalho mora; o asfalto de baixo é onde os
   faróis de perto estouram; e os cantos são onde o olho entra na página. O
   véu escurece essas três zonas e deixa o miolo, que é o ponto de fuga da
   avenida, quase intacto. É lá que a cidade continua acontecendo.
   */
.ambient::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;

  /* --veu vem de CENA.veu, escrito pelo JS. As seis porcentagens abaixo são a
     COMPOSIÇÃO do véu, ou seja quanto cada zona pesa em relação às outras; a
     força do conjunto é este número só.

     Atenção: Opacidade AQUI é legítima, e não contradiz a regra de não atenuar a
     cena por opacidade global. Esta regra não é a cena: é um pseudo-elemento
     de fundo, com z-index -1 e pointer-events: none herdados do .ambient, que
     não tem uma única linha de interface por cima. Atenuar a CENA por
     opacidade achataria o contraste do que se quer ver; atenuar o VÉU por
     opacidade é exatamente dosar um filtro escuro, que é o que ele é. */
  opacity: var(--veu, 1);

  background:
    /* 1 · vinheta. Centro em 45% da altura, que é a linha do horizonte da
       cena: a dissolução acompanha a geometria da avenida em vez de cortar
       um círculo por cima dela. */
    radial-gradient(126% 96% at 50% 45%,
      transparent 40%,
      color-mix(in srgb, var(--color-surface) 58%, transparent) 100%),
    /* 2 · o céu, que é a faixa mais clara da cena e fica atrás do cabeçalho */
    linear-gradient(to bottom,
      color-mix(in srgb, var(--color-surface) 62%, transparent) 0,
      transparent 24%),
    /* 3 · o asfalto de perto, onde os faróis chegam maiores */
    linear-gradient(to top,
      color-mix(in srgb, var(--color-surface) 48%, transparent) 0,
      transparent 32%);
}

/* A vinheta é uma elipse com raio em porcentagem do elemento. Num viewport
   estreito e alto ela deixa de ser moldura e vira tampa: a borda escura chega
   ao miolo e apaga justamente o ponto de fuga, que é a única parte da avenida
   que sobra no enquadramento do celular (ver o comentário do --rua acima).
   Abaixo de 900px a vinheta abre e recua, e as duas faixas horizontais, que
   dependem só da altura, ficam como estão. */
@media (max-width: 899px) {
  .ambient::after {
    background:
      radial-gradient(150% 108% at 50% 45%,
        transparent 54%,
        color-mix(in srgb, var(--color-surface) 42%, transparent) 100%),
      linear-gradient(to bottom,
        color-mix(in srgb, var(--color-surface) 62%, transparent) 0,
        transparent 24%),
      linear-gradient(to top,
        color-mix(in srgb, var(--color-surface) 48%, transparent) 0,
        transparent 32%);
  }
}


/*
   PAINEL DE LEITURA
   A rua roda a 100% por trás de tudo. O contraste do texto NÃO é resolvido
   apagando o efeito, e sim aqui: cada bloco de conteúdo ganha um painel de 85%
   da superfície, então atrás de texto passam 15% da cena.

   85% NÃO É GOSTO, É O MÍNIMO MEDIDO MAIS MARGEM.
   O número anterior era 93%, herdado de uma cena mais fraca e de um
   verificador que sobe a árvore do DOM e por isso não enxergava o canvas:
   ele media a superfície como se a rua não existisse.

   Medido em 20/08 com ferramentas/contraste-fundo.mjs no modo nu, que
   fotografa a cena CRUA atrás de cada linha de texto e resolve a opacidade
   por composição alfa: 2.219 alvos em seis larguras, e o pior deles pede
   81,2%. O pior é sempre texto em --color-text-muted em cima do núcleo
   branco de um farol, ou seja rgb(255,255,255) atrás de #A5A19D.

   85% dá 3,8 pontos de margem sobre o pior caso medido e deixa passar 15% da
   cena em vez de 7%: é o dobro de rua visível pelo mesmo texto legível.

   Como o .container tem no máximo 1200px, em monitor largo o shader fica a
   100% nas laterais: 83px de cada lado em 1366, 360px em 1920, 680px em 2560.
   Mais os vãos entre seções e o cabeçalho translúcido. O efeito aparece
   inteiro onde ninguém precisa ler, e recua onde precisa.

   A máscara horizontal dissolve as bordas do painel: sem ela o conteúdo
   pareceria uma caixa colada em cima do fundo.

   Atenção: A MÁSCARA MEDE var(--gutter), NÃO UMA PORCENTAGEM, E ISSO IMPORTA.
   O texto começa exatamente na borda do padding, que é --gutter. Máscara em
   porcentagem não sabe disso: em 1440 o .container tem 1200 de largura e 32
   de padding, então 4% eram 48px de dissolução para 32px de folga, e os
   primeiros 16px da linha ficavam com o painel pela metade.

   Foi assim que o parágrafo .pacotes__note reprovou em 1440 com 4,05:1
   enquanto passava com 6,91:1 em 1280: não era a cena, era a máscara comendo
   o começo da linha. Casando a dissolução com o gutter, ela acaba onde o
   texto começa, em qualquer largura.

   Atenção: ESTA REGRA JÁ ESTEVE DENTRO DE @media (min-width: 900px), E ERA BUG.
   Abaixo de 900px o texto ficava direto em cima da cena, sem painel nenhum.
   Passava despercebido porque a rua antiga era escura e rala; com a cena de
   20/08 deu 24 elementos reprovados em 360, 390 e 768px e zero em 1024, 1280
   e 1440. A fronteira do problema era exatamente a fronteira da media query.
   O painel cobre o BLOCO DE CONTEÚDO e não a seção inteira, então o respiro
   de cada seção continua transparente: é por ali que a rua aparece no
   celular.
   */
/* --painel-leitura vem de CENA.mascaraConteudo, escrito pelo JS. O padrão de
   85% é rede de segurança e é o mesmo valor: se o JS não rodar, a cena não
   aparece (ver .ambient__gl.is-on) e o painel é inofensivo de qualquer jeito. */
main > section > .container {
  background: linear-gradient(
    90deg,
    transparent 0,
    color-mix(in srgb, var(--color-surface) var(--painel-leitura, 85%), transparent) var(--gutter),
    color-mix(in srgb, var(--color-surface) var(--painel-leitura, 85%), transparent) calc(100% - var(--gutter)),
    transparent 100%
  );
}

/* O cabeçalho tem os dois botões contornados por --color-border-strong, que
   exigem 3:1. A 88% passavam 12% do shader e a borda caía para 2,67:1. */
@media (min-width: 900px) {
  .site-header {
    background: color-mix(in srgb, var(--color-surface) 94%, transparent);
  }
}

/* O canvas agora roda no celular também. O raymarcher anterior não podia:
   50 iterações por pixel deixavam Android mediano 2× acima do orçamento de
   GPU. Sprites copiados custam 0,08 GB/s e passam em qualquer aparelho, então
   o site deixou de ter duas experiências diferentes. */

@media (prefers-reduced-motion: reduce) {
  .ambient { animation: none; }
  .ambient__gl { display: none; }
}

/* Clip por secao, nao no body: o estouro morre onde nasce e o scan oficial
   ainda ve o que vazar do header ou do assistente. */
main > section,
.site-footer {
  overflow-x: clip;
}

/* Trava geral contra rolagem horizontal: nenhum filho de grid ou flex pode
   ultrapassar o pai por causa de conteúdo longo. É o `min-width: auto`
   implícito que causa 90% dos overflows em layouts de grid. */
.cards > *, .ft-top > *, .hero__inner > *, .sobre__inner > *,
/*
   MIN-WIDTH: 0 EM FILHO DE FLEX E GRID
   O valor inicial de min-width em item de flex e grid é `auto`, não `0`: o
   item se recusa a encolher abaixo do conteúdo, e uma palavra longa empurra
   a coluna inteira para fora. É a causa nº 1 de estouro lateral em layout
   moderno, e não aparece em teste até alguém escrever uma palavra grande.
   Aplicado em todo container que carrega texto.
   */
.decisao__inner > *,
.form__row > *,
.cards > *,
.hero__inner > *,
.sobre__inner > *,
.hero__fact > *,
.faq__q > *,
.plano__price > *,
.marquee__head > *,
.site-header__inner > *,
.contato-direto li > *,
.sobre__fatos > *,
.site-nav__list > li { min-width: 0; }

/*
   FIGURA TABULAR
   Numeral proporcional dá larguras diferentes para cada dígito: o `1` é
   estreito, o `8` é largo. Numa lista de especificações isso desalinha a
   coluna da direita linha a linha, e num preço faz o valor tremer quando o
   dígito troca. Em tabela e em preço a figura tem que ser tabular, que é a
   variante de largura fixa que a fonte já traz e ninguém liga.

   Não vai no corpo do texto de propósito: lá o numeral proporcional é o certo,
   porque a leitura é sequencial e não vertical. Aplicar em tudo seria o mesmo
   erro na direção oposta.
   */
.specs,
.plano__price,
.step__num,
.hero__fact,
.contato-direto,
.sobre__fatos,
.ft-contact { font-variant-numeric: tabular-nums; }

/*
   QUEBRA DE STRING LONGA
   `anywhere` só onde pode aparecer coisa sem espaço: número de telefone,
   endereço, URL colada pelo visitante no assistente. Nunca `break-all`, que
   corta palavra comum no meio e destrói a leitura.
   */
.ft-contato a,
.contato-direto__valor,
.site-nav__contact,
.site-nav__list a,
.skip-link,
.brand,
.brand__text,
.field__error { overflow-wrap: anywhere; }

/*
   MEDIDA DE LINHA
   45 a 75 caracteres é a faixa em que o olho encontra o começo da linha
   seguinte sem se perder. Em `ch` e não em `rem` porque a medida é sobre
   CARACTERE: em rem ela se desloca sempre que a família tipográfica muda.
   */
.section__lead,
.stakes__text,
.sobre__text,
.sobre__bio,
.decisao__text,
.hero__subtitle,
.faq__a p,
.pacotes__note,
.incluso__list,
.pontos__note { max-width: var(--medida); }

h1, h2, h3 {
  font-family: var(--font-display);
  line-height: var(--leading-tight);
  /* Base do meio da escala. Quem sai dela é quem muda de tamanho, logo
     abaixo em cada componente, e não cada elemento por conta própria. */
  letter-spacing: var(--tracking-title);
  margin: 0;
  text-wrap: balance;
  overflow-wrap: anywhere;
}

p  { margin: 0 0 var(--space-4); text-wrap: pretty; }
ul, ol, dl, dd { margin: 0; padding: 0; }
li { list-style: none; }
img, svg, figure { display: block; max-width: 100%; }
a { color: inherit; }

:focus-visible {
  outline: 3px solid var(--color-primary);
  outline-offset: 3px;
  /* Sem border-radius aqui. O anel segue o raio do próprio controle; um raio
     no :focus-visible global trocava a forma no foco (pílula virava 8px) em
     qualquer camada carregada antes deste arquivo, o Pular da splash era o
     caso visível. */
}

.skip-link {
  /* translate em vez de top: `top` é propriedade de layout e força reflow a
     cada quadro. O alvo é 60fps, e só transform e opacity compõem na GPU. */
  position: absolute; left: var(--space-4); top: var(--space-4);
  translate: 0 calc(-100% - var(--space-6));
  z-index: var(--z-modal);
  padding: var(--space-3) var(--space-5);
  max-inline-size: calc(100% - var(--space-4) * 2);
  background: var(--color-primary); color: var(--color-primary-ink);
  border-radius: var(--radius-full); font-weight: 700; text-decoration: none;
  transition: translate var(--duration-fast) var(--ease-out);
}
.skip-link:focus-visible,
.skip-link:focus { translate: 0 0; }

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

.section { padding-block: var(--section-y); }

.section__head { margin-bottom: var(--space-10); }
.section__head--narrow { max-width: 46rem; }
.section__title { font-size: var(--text-2xl); font-weight: 800; }
.section__lead {
  margin-top: var(--space-4);
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  max-width: var(--medida);
}

/* O rótulo de seção é rótulo: diz onde você está, não pede clique. Estava em
   --color-primary nas oito seções, e oito usos do acento em texto que ninguém
   toca é o maior gasto isolado da página. Caixa alta, 12px, peso 700 e
   entreletra larga já dão hierarquia de sobra sem consumir a cor de ação.
   7.66:1 sobre a superfície base. */
.eyebrow {
  margin: 0 0 var(--space-3);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.accent { color: var(--color-primary); }

/* 3 · BOTÕES */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  min-height: 48px;                 /* alvo de toque ≥ 44px */
  padding: var(--space-3) var(--space-6);
  border: 1px solid transparent;
  border-radius: var(--radius-full);
  font-family: var(--font-display);
  font-size: var(--text-sm);
  font-weight: 700;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--color-primary); color: var(--color-primary-ink); }
@media (hover: hover) and (pointer: fine) {
  .btn--primary:hover { background: var(--color-primary-hover); }
}

.btn--ghost {
  background: transparent;
  color: var(--color-text);
  border-color: var(--color-border-strong);
}
@media (hover: hover) and (pointer: fine) {
  .btn--ghost:hover { border-color: var(--color-primary); color: var(--color-primary); }
}

.btn--sm { min-height: 44px; padding: var(--space-2) var(--space-5); font-size: var(--text-xs); }
.btn--block { display: flex; width: 100%; }

/* CTA principal do hero: maior que os demais, para ser o ponto mais quente
   da primeira dobra sem depender só de cor. */
.btn--lg { min-height: 56px; padding: var(--space-4) var(--space-8); font-size: var(--text-base); }

/* Secundário de verdade: sem borda, para não competir com o primário.
   Dois botões com o mesmo peso visual viram uma decisão a mais, não a menos. */
.btn--quiet {
  background: transparent;
  color: var(--color-text-muted);
  border-color: transparent;
  padding-inline: var(--space-4);
}
@media (hover: hover) and (pointer: fine) {
  .btn--quiet:hover { color: var(--color-text); }
}
.btn__ico { width: 17px; height: 17px; fill: none; stroke: currentColor;
            stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.btn__ico--fill { fill: currentColor; stroke: none; }

@media (prefers-reduced-motion: reduce) { .hero__pulse::after { animation: none; } }

.btn__arrow { transition: transform var(--duration-fast) var(--ease-out); }

/*
   ESTADOS QUE FALTAVAM: DESABILITADO E ENVIANDO
   Todo controle precisa dos cinco estados. Existiam hover, focus-visible e
   active; faltavam estes dois, e sem eles um botão que não pode ser usado
   parece igual a um que pode, e um envio em curso não dá sinal nenhum de que
   está acontecendo.
   */
.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  /* Sem transform no hover: controle desabilitado que ainda reage mente
     sobre a própria disponibilidade. */
  pointer-events: none;
}

/* O estado "enviando" e o anel girando saíram em 14/08: não existe mais
   espera no site. O único envio virou entrega direta no WhatsApp, que
   acontece no mesmo quadro do clique. Estado que nada dispara é peso morto
   que alguém vai jurar que funciona. Volta se entrar operação assíncrona. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover .btn__arrow { transform: translateX(3px); }
}

/* .chip, .filters, .pontos__empty, .badge--free, .badge--full,
   .card--ponto.is-full e .card__disabled-note saíram em 19/08 junto com o
   filtro de disponibilidade e o dado de vaga que o alimentava. Eram 60 linhas
   de CSS para dois botões que devolviam o mesmo resultado e para três estados
   de cartão que nenhum cartão tem mais. Voltam no commit que devolver a
   disponibilidade real. */

/* 4 · HEADER */
.site-header {
  position: sticky; top: 0; z-index: var(--z-header);
  background: var(--color-surface); /* fallback para navegador sem color-mix */
  background: color-mix(in srgb, var(--color-surface) 88%, transparent);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--color-border);
  padding-top: env(safe-area-inset-top);
}
/* LAYOUT DO CABEÇALHO · dois grupos, não três coisas soltas.
   Era `justify-content: space-between` com três filhos, e com três filhos o
   space-between não centraliza nada: ele empurra o do meio para onde sobrar
   espaço depois que os dois das pontas se medirem. A navegação flutuava num
   ponto que dependia do comprimento do nome da marca, e mudar "Comunicação em
   LED" mexeria na posição dos links. Ninguém sabe dizer o que está errado
   nisso, mas todo mundo vê que o cabeçalho está torto.

   Agora são dois grupos com âncora explícita: a marca à esquerda, e navegação
   mais ação juntas à direita, empurradas pelo `margin-inline-start: auto` da
   .site-nav. O olho lê marca → (respiro) → onde ir → o que fazer, nessa ordem,
   e o respiro do meio é a maior área vazia da barra, que é o que faz um
   cabeçalho parecer calmo. */
.site-header__inner {
  display: flex; align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  min-height: var(--header-h);
  /* Sem transição em min-height: é propriedade de layout e animá-la obriga o
     navegador a refazer o fluxo a cada quadro, justamente durante a rolagem,
     que é o pior momento possível. A compactação passou a ser um degrau
     limpo; o que transiciona é o fundo e a sombra, que compõem na GPU. */
}

/* O header compacta depois da primeira dobra. Não é enfeite: no meio da
   página o cabeçalho já cumpriu o papel de apresentar a marca e passa a ser
   só navegação, então devolve 8px de altura ao conteúdo. A classe é escrita
   pelo main.js; sem JS o header fica na altura cheia e nada quebra. */
.site-header.is-scrolled .site-header__inner { min-height: var(--header-h-compact); }
.site-header.is-scrolled {
  background: color-mix(in srgb, var(--color-surface) 96%, transparent);
  box-shadow: var(--shadow-sm);
}
/* min-height 44: a marca é o link de voltar ao topo e o único alvo do lado
   esquerdo do cabeçalho. A imagem tem 38px, então sem isto o alvo tinha 38 de
   altura. O cabeçalho já tem folga para os 44 e nada se move. */
.brand { display: flex; align-items: center; min-height: 44px;
         min-inline-size: 0;
         gap: var(--space-3); text-decoration: none; }
.brand__mark {
  width: 38px; height: 38px;
  border-radius: var(--radius-full);
  object-fit: cover;
  flex-shrink: 0;
}
.brand__text { display: flex; flex-direction: column; min-inline-size: 0; line-height: 1.2; }
.brand__text strong {
  font-family: var(--font-display);
  font-size: var(--text-base); font-weight: 800;
  letter-spacing: var(--tracking-sub);
}
/* Era 0.6875rem (11px), o menor texto do site inteiro, para uma linha que é a
   descrição do negócio. Subiu para o degrau da escala e ganhou 1px de folga
   do nome acima: duas linhas coladas viram uma mancha, e a assinatura da
   marca é justamente onde o acabamento é mais notado. */
.brand__text small {
  margin-top: 1px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  letter-spacing: var(--tracking-small);
  transition: color var(--duration-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .brand:hover .brand__text small { color: var(--color-text); }
}

.site-nav { display: none; }
.site-nav__list { display: flex; flex-wrap: wrap; gap: var(--space-6); min-inline-size: 0; }
.site-nav__list a {
  position: relative;
  display: inline-block;
  /* Rotulo curto cabe numa linha sozinho. nowrap impedia URL ou palavra
     longa de quebrar e empurrava o documento. O teto de 18rem vive no
     bloco de 900px, onde a lista e inline: e maior que qualquer rotulo
     real e menor que uma URL hostil. */
  padding-block: var(--space-1);
  font-size: var(--text-sm); font-weight: 700;
  color: var(--color-text-muted); text-decoration: none;
  /* A nav de topo só aparece a partir de 900px, largura em que quase sempre
     há mouse. Em tablet deitado, não: lá 1024px vem com dedo, e o link fechava
     em 33px de altura. O sublinhado é `bottom: -2px` no ::after, então crescer
     por min-height mantém o traço colado no texto. */
  min-height: 32px;
  transition: color var(--duration-fast) var(--ease-out);
}

/* Sublinhado que cresce do centro. Um pseudo-elemento com scaleX é mais
   honesto que animar `width`: escala não recalcula layout a cada quadro. */
.site-nav__list a::after {
  /* Espessura do sublinhado: uma decisão, usada quatro vezes. O raio é a
     própria espessura, que é o que dá a ponta arredondada. */
  --sublinhado: 2px;
  content: "";
  position: absolute;
  inset-inline: 0;
  bottom: calc(var(--sublinhado) * -1);
  height: var(--sublinhado);
  border-radius: var(--sublinhado);
  background: var(--color-primary);
  transform: scaleX(0);
  transition: transform var(--duration-base) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .site-nav__list a:hover { color: var(--color-text); }
}
@media (pointer: coarse) {
  .site-nav__list a { min-height: 44px; }
}
.site-nav__list a:focus-visible::after { transform: scaleX(1); }
@media (hover: hover) and (pointer: fine) {
  .site-nav__list a:hover::after { transform: scaleX(1); }
}

/* Seção ativa. O aria-current é escrito pelo main.js conforme a pessoa rola,
   então o indicador não é só visual: leitor de tela anuncia "página atual".
   Sem JS, nenhum link fica marcado e a nav funciona como sempre funcionou. */
.site-nav__list a[aria-current="true"] { color: var(--color-text); }
.site-nav__list a[aria-current="true"]::after { transform: scaleX(1); }

/* A âncora da direita abaixo de 900px. A `.site-nav` some (`display: none`)
   e leva junto o `margin-inline-start: auto` que só existe a partir de 900px:
   sem isto, marca e hambúrguer empacotam à esquerda e o canto de abrir o
   menu, onde o polegar já espera o controle, fica vazio. Medido em 360px:
   o botão começava em x=197 em vez de x=300. Em 900px a margem volta a 0
   porque a âncora passa a ser a própria navegação. */
.site-header__actions {
  display: flex; align-items: center; gap: var(--space-2);
  margin-inline-start: auto;
}
@media (max-width: 899px) {
  .site-header__inner { flex-wrap: nowrap; }
  .site-header .brand { flex: 1; }
  .site-header__actions { flex: none; }
}
.header-cta { display: none; }

/* Filete entre navegação e ação. Só existe acima de 900px, porque abaixo
   disso não há navegação visível para separar de nada. Altura menor que a do
   botão ao lado de propósito: divisor que encosta nas duas bordas vira grade,
   e grade num cabeçalho pesa. Herda a borda decorativa, que é 11% do texto,
   então ele é sentido antes de ser visto. */
.site-header__rule { display: none; }


/*
   BOTÃO DE MENU
   As três barras nascem do MESMO centro, em absoluto, e só o deslocamento as
   separa. Fechar é zerar o deslocamento e girar: o X é geometricamente
   garantido.

   A versão anterior usava `display: grid` com três linhas e `translateY(7px)`
   para juntá-las. A geometria dependia de como o grid distribuía as linhas
   dentro dos 44px, e o resultado na tela era um ">" em vez de um X. Posição
   explícita não tem esse problema: não há distribuição para dar errado.
   */
/* Atenção: O botão nasce escondido e só aparece quando o <html> tem data-js="on",
   atributo escrito pelo script síncrono do <head>.

   Sem isto, abaixo de 900px e com o JavaScript morto, a .site-nav ficava
   display:none pela regra base e o botão ficava visível sem handler nenhum:
   um controle com aparência de funcional que não fazia nada. Quem tocasse
   não concluiria "o script falhou", concluiria "esse site é ruim".

   Não é o cenário raro de JS desligado: o main.js é um IIFE único com 7
   blocos e 2 try, então uma exceção no bloco do tema derruba o menu junto.
   Numa rede ruim, script truncado produz exatamente isso.

   Mesmo padrão do [data-motion="full"] da camada SOAP: o recurso só se
   anuncia quando quem o implementa está de pé. */
.nav-toggle { display: none; }

html[data-js="on"] .nav-toggle {
  /* GEOMETRIA DO HAMBÚRGUER · uma decisão, não seis literais.
     Largura e espessura da barra são os dois únicos números escolhidos aqui.
     Centralização, raio da ponta e afastamento entre as barras saem deles por
     conta, então mudar a barra de 18 para 20px continua com tudo alinhado. */
  --bar-w: 18px;
  --bar-h: 2px;
  --bar-gap: 5.5px;

  position: relative;
  display: block;
  width: 44px; height: 44px;
  padding: 0;
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: transparent; cursor: pointer;
  /* O alvo é redondo e o dedo não some dentro dele: sem isto, um toque rápido
     em Android dispara o realce cinza retangular padrão, que sai por fora do
     círculo e desmancha o acabamento. */
  -webkit-tap-highlight-color: transparent;
  /* Um toque não pode virar seleção de texto nem menu de contexto de imagem
     quando o dedo escorrega. */
  user-select: none;
  /* O botão não é alvo de rolagem: entregar isso ao navegador tira o atraso
     de 300ms de espera por gesto em alguns Android. */
  touch-action: manipulation;
  transition: border-color var(--duration-fast) var(--ease-out),
              background-color var(--duration-fast) var(--ease-out),
              scale var(--duration-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .nav-toggle:hover { border-color: var(--color-primary); }
}
/* FEEDBACK DE TOQUE. É a única coisa entre o dedo encostar e o painel chegar,
   e sem ela o botão parece morto nesse intervalo, que é justamente onde a
   pessoa decide se o site responde ou não.
   `scale` e não `background`: afunda de verdade em vez de piscar, compõe na
   GPU e não invalida nada de layout. 0,92 é o ponto em que se sente sem se
   ver como animação. */
.nav-toggle:active { scale: 0.92; }

/* Aberto, o botão deixa de ser neutro: vira o controle ativo da tela. */
.nav-toggle[aria-expanded="true"] {
  border-color: var(--color-primary);
  background: var(--color-primary-soft);
}

.nav-toggle__bar {
  position: absolute;
  top: 50%; left: 50%;
  width: var(--bar-w); height: var(--bar-h);
  /* o centro da barra no centro do botão, derivado das duas medidas acima */
  margin: calc(var(--bar-h) / -2) 0 0 calc(var(--bar-w) / -2);
  border-radius: var(--bar-h);        /* ponta arredondada = metade da espessura */
  background: var(--color-text);
  transition: transform var(--duration-base) var(--ease-out),
              opacity   var(--duration-fast) var(--ease-out);
}
.nav-toggle__bar:nth-child(1) { transform: translateY(calc(var(--bar-gap) * -1)); }
.nav-toggle__bar:nth-child(3) { transform: translateY(var(--bar-gap)); }

.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(1) { transform: rotate(45deg); }
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(3) { transform: rotate(-45deg); }
/* A barra do meio some encolhendo para o centro, não apagando no lugar:
   o olho lê as outras duas se cruzando em cima dela. */
.nav-toggle[aria-expanded="true"] .nav-toggle__bar:nth-child(2) {
  opacity: 0; transform: scaleX(0.15);
}

/*
   MENU MOBILE
   Estrutura: .site-nav é a camada que escurece a página (o véu), e
   .site-nav__panel é a folha branca/preta que desce do header.

   Sem `position: fixed`: o header já é sticky e serve de referência, então o
   véu é `absolute` a partir dele. A decisão travada nº 4 do projeto vale, o
   FAB do assistente continua sendo o único flutuante.
   */
.site-nav.is-open {
  display: block;
  position: absolute;
  inset-inline: 0;
  top: 100%;
  /* A escala do projeto reservava --z-overlay para o véu e --z-modal para o
     painel, e nenhum dos dois estava em uso: a ordem entre eles saía de ordem
     de pintura, que muda quando alguém acrescenta um `position` a qualquer um
     dos dois. Agora está declarada. Os dois valores são internos ao contexto
     de empilhamento do header, então nada disto passa por cima do assistente. */
  z-index: var(--z-overlay);
}

/* Véu: escurece o que está abaixo do painel até o fim da janela.
   Pseudo-elemento em vez de div: zero markup a mais, e o clique nele tem
   como alvo a própria .site-nav, que o JS usa para fechar. */
.site-nav.is-open::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 100%;
  height: 100svh;
  background: var(--veil);
  /* 3px, não mais. O que o desfoque faz aqui é tirar a nitidez das letras que
     ficaram atrás do véu, e é isso que separa "tem uma camada escura por cima"
     de "o fundo saiu de foco". Acima de 4 ou 5px vira efeito, e o header desta
     mesma página já usa desfoque em 14px: dois valores muito diferentes na
     mesma barra leriam como dois materiais diferentes.
     Custa pouco porque a cena do canvas PARA enquanto o menu está aberto (ver
     ambient-street.js): o fundo desfocado é estático, então o navegador
     desfoca uma vez em vez de sessenta vezes por segundo. */
  backdrop-filter: blur(3px);
  animation: nav-veil var(--duration-base) var(--ease-out) both;
}
/* Atenção: TUDO QUE CONSTRÓI A FOLHA MORA AQUI DENTRO, E ISSO NÃO É ORGANIZAÇÃO.
   Estas propriedades estavam na regra base, sem media query, e o bloco de
   900px desfazia uma por uma. Funcionou até alguém acrescentar uma linha à
   base e esquecer de acrescentar o desfazimento correspondente, que foi
   exatamente o que aconteceu com `overflow-y: auto`.

   O ESTRAGO: overflow-y sozinho não fica sozinho. Quando um eixo recebe valor
   diferente de `visible`, o outro deixa de poder ser `visible` e o navegador
   o computa como `auto`. O painel virou caixa de rolagem nos DOIS eixos, no
   desktop, com 33px de altura. E o sublinhado de cada link é um ::after em
   `bottom: -2px`, ou seja, fora da caixa de conteúdo: scrollHeight passou a
   ser maior que clientHeight e o Chrome desenhou a barra de rolagem nativa
   dela, com as setinhas, encostada nos links do cabeçalho.

   Medido em 1440, 1200, 1024 e 900: barra em todos. Em 899 para baixo não
   aparecia porque aí o painel só existe com o menu aberto.

   Com a folha escopada, o bloco de 900px não precisa desfazer nada, e a
   próxima propriedade que alguém acrescentar aqui não tem como vazar.
   */
@media (max-width: 899px) {
  .site-nav__panel {
    /* Acima do véu, que é ::after posicionado da .site-nav. Sem z-index os
       dois dependiam só de ordem de pintura, e a escala do projeto já tinha
       os dois degraus reservados para isto sem ninguém usar. */
    position: relative;
    z-index: var(--z-modal);
    /* Recorte lateral e inferior do aparelho. Em celular deitado com entalhe,
       o conteúdo do menu passava por baixo dele. max() para o vão nunca ficar
       menor que o do resto do site. */
    padding:
      var(--space-3)
      max(var(--gutter), env(safe-area-inset-right, 0px))
      max(var(--space-5), env(safe-area-inset-bottom, 0px))
      max(var(--gutter), env(safe-area-inset-left, 0px));
    background: var(--color-surface);
    box-shadow: var(--shadow-md);
    animation: nav-panel var(--duration-base) var(--ease-out) both;

    /* Canto arredondado embaixo e nenhuma borda: o painel deixa de ser uma
       gaveta e passa a ser uma folha que desceu do cabeçalho. */
    border: 0;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);

    /* Atenção: O PAINEL PRECISA ROLAR SOZINHO, SENÃO O FIM DELE FICA INALCANÇÁVEL.
       Com o menu aberto a página inteira trava em `overflow: hidden`. Se o
       painel passar da altura da janela, nada mais rola e o que sobrou
       embaixo não existe para quem toca.

       Medido em 20/08, com o menu aberto:

         janela      painel   sobra   telefone do rodapé do menu
         528×467      444px   -33px   cortado
         844×390      456px  -122px   cortado
         390×844      443px  +345px   inteiro
         360×640      467px  +117px   inteiro

       Ou seja: em celular deitado, que é a orientação de quem está no carro,
       o telefone da BG dentro do menu estava fora de alcance.

       --nav-topo é a distância do topo da janela até o painel, escrita pelo
       main.js na abertura e REESCRITA a cada giro de tela ou mudança de
       altura da janela, porque a barra de endereço do celular some e volta
       durante a leitura. O fallback de 4rem cobre o JS ausente. */
    max-height: calc(100svh - var(--nav-topo, 4rem));
    overflow-y: auto;
    /* Explícito, e não o `auto` que o navegador escolheria sozinho: nada aqui
       rola de lado, e barra horizontal dentro de menu é defeito puro. */
    overflow-x: clip;
    /* Sem isto, a rolagem que chega ao fim do painel tenta continuar na
       página por trás do véu. */
    overscroll-behavior: contain;
  }

  /* Saída: sem escalonamento e mais curta, todos juntos. Escopada junto com o
     resto da folha para que uma classe `is-saindo` deixada por engano em
     tamanho de desktop não tenha nenhuma animação para disparar. */
  .site-nav.is-saindo .site-nav__panel {
    animation: nav-panel-sai 180ms var(--ease-in) both;
  }
  .site-nav.is-saindo .site-nav__list a,
  .site-nav.is-saindo .site-nav__foot {
    animation: nav-item-sai 140ms var(--ease-in) both;
  }
}

.site-nav.is-open .site-nav__list {
  flex-direction: column;
  gap: 0;
}

/* Cada item é uma linha de 56px com fio separador e uma seta de afordância.
   A seta é feita de duas bordas giradas: nenhum SVG, nenhum ícone extra. */
.site-nav.is-open .site-nav__list li + li a { border-top: 1px solid var(--color-border); }

.site-nav.is-open .site-nav__list a {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  min-height: 56px;
  padding: var(--space-3) 0;
  font-family: var(--font-display);
  font-size: var(--text-lg);
  font-weight: 700;
  color: var(--color-text);
  /* Atenção: `transform` E NÃO `translate`, DE PROPÓSITO, E NÃO É PREFERÊNCIA.
     O deslocamento do hover era `padding-inline-start`, que é propriedade de
     layout: cada quadro da transição obrigava o navegador a refazer o fluxo
     do painel inteiro, no dedo, no celular.

     A troca óbvia seria `translate`, mas `translate` já é de quem entra: a
     animação nav-item tem fill `both` e mantém `translate: 0 0` aplicado para
     sempre, e animação vence transição na cascata. O item simplesmente não
     sairia do lugar. `transform` é propriedade SEPARADA de `translate`, e as
     duas se compõem na ordem translate → rotate → scale → transform. Então a
     entrada continua dona do `translate` e o hover fica com o `transform`,
     sem disputa e sem refluxo. */
  transition: color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-out);
  /* entra escalonado, item a item */
  animation: nav-item var(--duration-base) var(--ease-out) both;
}
/* Atenção: O LEQUE SAIU. Os itens vinham de um arco de -68deg a +68deg com raio de
   74px, cada um de uma direção diferente, e o rodapé do painel de uma sétima.
   É o efeito que impressiona na primeira abertura e cansa na quinta, e ele
   estava brigando com o pedido de sobriedade: 74px de percurso por item é
   quatro vezes a altura da própria linha.

   Agora os seis chegam da MESMA direção, subindo 10px, escalonados. É a lista
   assentando, não seis peças voando. O escalonamento é o que dá vida; a
   direção única é o que dá compostura.

   Saiu junto a dependência de sin() e cos() em calc(), que existia só para
   desenhar o arco.

   PARA VOLTAR ATRÁS: o keyframe nav-item guarda a forma antiga em comentário. */
.site-nav.is-open .site-nav__list li:nth-child(1) a { animation-delay:  30ms; }
.site-nav.is-open .site-nav__list li:nth-child(2) a { animation-delay:  75ms; }
.site-nav.is-open .site-nav__list li:nth-child(3) a { animation-delay: 120ms; }
.site-nav.is-open .site-nav__list li:nth-child(4) a { animation-delay: 165ms; }
.site-nav.is-open .site-nav__list li:nth-child(5) a { animation-delay: 210ms; }

.site-nav.is-open .site-nav__list a::after {
  content: "";
  /* Reset explícito do sublinhado da nav desktop, que é o mesmo
     pseudo-elemento. Sem isto a seta herda position:absolute e inset-inline
     e some do fluxo do flex. */
  position: static;
  inset: auto;
  flex-shrink: 0;
  width: 8px; height: 8px;
  background: none;
  border-top: 2px solid currentColor;
  border-right: 2px solid currentColor;
  /* EXCEÇÃO DOCUMENTADA: 1px é a ponta da caneta, metade do traço de 2px que
     desenha a seta. Não é raio de controle e não pertence à escala de forma. */
  border-radius: 1px;
  transform: rotate(45deg);
  /* 0.40 e não 0.32: dá 3.69:1 no tema escuro em vez de 2.73:1, sem virar
     ruído visual. A seta é redundante, a linha inteira já é um link com
     texto, então SC 1.4.11 não se aplica. Margem por acabamento, não por
     obrigação. */
  opacity: 0.4;
  transition: opacity var(--duration-fast) var(--ease-out),
              translate var(--duration-fast) var(--ease-out);
}
.site-nav.is-open .site-nav__list a:focus-visible {
  color: var(--color-primary);
  transform: translateX(var(--space-2));
}
@media (hover: hover) and (pointer: fine) {
  .site-nav.is-open .site-nav__list a:hover {
    color: var(--color-primary);
    transform: translateX(var(--space-2));
  }
}
@media (hover: hover) and (pointer: fine) {
  .site-nav.is-open .site-nav__list a:hover::after {
    opacity: 1;
    translate: 3px 0;
  }
}

/* Rodapé do painel: o CTA que o mobile não tinha.
   O .header-cta é display:none abaixo de 900px, então até aqui quem abria o
   site no celular não encontrava nenhum botão de WhatsApp no cabeçalho. */
.site-nav__foot {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-border);
  animation: nav-item var(--duration-base) var(--ease-out) 240ms both;
}
.site-nav__contact {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  margin: var(--space-4) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  text-align: center;
}
.site-nav__contact strong {
  font-weight: 700;
  color: var(--color-text);
  letter-spacing: var(--tracking-small);
}

@keyframes nav-panel {
  from { opacity: 0; translate: 0 -10px; }
  to   { opacity: 1; translate: 0 0; }
}
@keyframes nav-veil {
  from { opacity: 0; }
  to   { opacity: 1; }
}
/* Atenção: UMA ANIMAÇÃO DE ENTRADA, NÃO DUAS.
   Por um tempo conviveram duas: esta, no <a>, e uma `nav-item-arco` no <li>,
   com escalonamentos diferentes. O item chegava duas vezes, e o olho lê isso
   como interface hesitante. O arco foi absorvido aqui: um keyframe, um
   escalonamento, um responsável.

   O leque de -68deg a +68deg é o que sobrou do circle menu do 21st.dev: os
   itens chegam em arco, saindo da direção do botão, em vez de subirem em
   coluna. sin e cos em CSS são de 2023, mesma safra do color-mix que este
   projeto já exige. Onde não houver, a declaração cai e o item entra só com
   fade: degrada para menos animação, nunca para menos menu. */
/* 10px e nenhum `scale`. Escala em texto obriga o navegador a reamostrar o
   glifo a cada quadro, e o que se vê no meio da animação é letra borrada: é
   isto, e não o percurso, que faz um menu parecer barato. Opacidade e
   deslocamento chegam limpos.
   A forma antiga, para quem quiser o arco de volta:
     translate: calc(sin(var(--ang)) * var(--raio))
                calc(cos(var(--ang)) * var(--raio) * -1);
   com --raio: 74px e --ang de -68deg a +68deg distribuídos nos seis itens. */
@keyframes nav-item {
  from { opacity: 0; translate: 0 10px; }
  to   { opacity: 1; translate: 0 0; }
}

/* Saída. Sem ela o menu abre com cuidado e fecha com um corte, que é o
   defeito de acabamento mais comum em menu mobile. Mais rápida que a
   entrada: sair tem que parecer decisão, não despedida. */
/* A saída é mais curta que a entrada e sem escalonamento: os seis somem
   juntos. Sair tem de parecer decisão, e decisão não tem cadência. */
@keyframes nav-item-sai {
  to { opacity: 0; translate: 0 -6px; }
}
@keyframes nav-panel-sai {
  to { opacity: 0; translate: 0 -8px; }
}

/* Atenção: A TRAVA DE ROLAGEM FAZIA O CABEÇALHO INTEIRO DESAPARECER. LEIA ANTES DE
   MEXER, PORQUE A LINHA PARECE INOFENSIVA E NÃO É.

   Era `html[data-nav="open"], html[data-nav="open"] body { overflow: hidden }`.
   O segundo seletor é o problema.

   `position: sticky` gruda no scrollport ANCESTRAL MAIS PRÓXIMO. Enquanto o
   body tem overflow visível, esse scrollport é a janela, e o .site-header
   gruda no topo dela como deveria. No instante em que o body recebe
   `overflow: hidden`, ele vira um contêiner de overflow e passa a ser o
   scrollport mais próximo do header. Só que o body nunca rolou: quem rolou foi
   a janela. Então o deslocamento de rolagem do novo scrollport é zero, o
   sticky não tem de onde grudar e o header volta para a posição estática dele,
   que é o topo do DOCUMENTO.

   Na prática: quem está a 900px do topo aperta o hambúrguer e o cabeçalho
   inteiro, botão e painel juntos, salta 900px para cima, para fora da tela. O
   menu ABRE de verdade (a classe entra, o aria muda), mas ninguém vê. É o
   "clico e ele simplesmente some" relatado três vezes pelo cliente.

   POR QUE PASSOU EM TODA BATERIA DE TESTE ATÉ AQUI: todas abriam o menu com a
   página no topo. Com scrollY em 0, a posição estática do header e a posição
   grudada são a mesma, e o defeito é invisível. O teste estava errado, não o
   relato.

   Medido em 390x844 com scrollY 900:
     html + body hidden  → header em -900px, botão fora da tela
     só html hidden      → header em 0, botão no lugar
     só html clip        → header em 0, botão no lugar
     só body hidden      → header em 0, mas a rolagem salta para 90

   Ficou `html` sozinho: o overflow da raiz se propaga para a janela e trava a
   rolagem do mesmo jeito, sem criar um segundo scrollport no caminho do
   sticky. `clip` também resolveria, mas `hidden` na raiz é o caminho antigo e
   universalmente suportado, e aqui a diferença entre os dois é nenhuma.

   Só abaixo de 900px, que é onde o menu existe. */
@media (max-width: 899px) {
  html[data-nav="open"] { overflow: hidden; }
  /* A barra de rolagem clássica some junto com a trava e o conteúdo pula a
     largura dela. Em celular não acontece, porque a barra é sobreposta, mas a
     janela estreita de um desktop cai nesta faixa e lá acontece. Reservar a
     goteira antes evita o salto sem precisar medir a barra no JS. */
  html { scrollbar-gutter: stable; }
}

/* 5 · HERO */
.hero {
  padding-block: clamp(2.5rem, 7vw, 5.5rem) var(--section-y);
  /* Sem isto, `#hero` (pular splash, anúncio, recarregar) esconde a pill
     sob a barra: padding-top 40px contra header 61px. As outras âncoras
     sobrevivem pelo --section-y; o herói não, porque o padding de cima é menor. */
  scroll-margin-top: calc(var(--header-h) + var(--space-4));
}
/* Três áreas nomeadas desde o celular. A prova sai de dentro do texto e
   vira irmã: no desktop o totem pode ocupar as duas linhas da direita, e
   no celular a ordem vira argumento, prova, produto, que é a dobra que
   converte. Sem isto, as duas colunas de 900px colocariam os números ao
   lado do H1 e empurrariam o totem para baixo. */
.hero__inner {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas:
    "content"
    "facts"
    "media";
  gap: var(--space-8);
}
.hero__content {
  display: flex;
  flex-direction: column;
  grid-area: content;
  min-width: 0;
}
.hero__facts { grid-area: facts; }
.hero__media { grid-area: media; }

/* O acento fica junto enquanto cabe; texto ampliado pode ocupar mais linhas. */
.hero__title {
  font-size: var(--text-3xl);
  font-weight: 800;
  letter-spacing: var(--tracking-display);
  overflow-wrap: anywhere;
  word-break: normal;
  hyphens: none;
  text-wrap: wrap;
}
.hero__title .accent { display: inline-block; max-inline-size: 100%; white-space: normal; }
/* A segunda linha da manchete só se separa a partir do tablet: no celular
   o bloco extra empilhava quatro linhas e o CTA saía da primeira dobra. */
.hero__lead { display: inline; }
/* No celular o subtítulo é --text-base, não --text-lg. O texto do herói tem
   quatro parágrafos: em 18px ele empurrava o botão para 840px de rolagem, ou
   seja, o CTA nascia fora da primeira tela de um iPhone 14. Em 16px continua
   acima do piso de corpo de leitura e devolve o botão para dentro da dobra.
   De 640px para cima a coluna cabe o tamanho maior e ele volta. */
.hero__subtitle {
  margin-top: var(--space-4);
  font-size: var(--text-base);
  color: var(--color-text-muted);
  max-width: 34rem;
}
.hero__subtitle p { margin: 0; }
.hero__subtitle p + p { margin-top: var(--space-3); }
/* Dois argumentos, não quatro blocos iguais: o punch cola no setup,
   e o vão maior abre o segundo movimento (como o LED ocupa a rua). */
.hero__subtitle p:nth-child(3) { margin-top: var(--space-5); }
/* As duas linhas de fecho vêm no branco do texto, contra o cinza do resto:
   são o que a pessoa leva da seção, e destaque por COR pesa menos que por
   tamanho num bloco que já é o mais alto do herói. */
.hero__subtitle strong { color: var(--color-text); font-weight: 700; }
/* Em 320px os dois botões lado a lado ficariam com 8 caracteres cada.
   Largura total até 480px, e a partir daí voltam a dividir a linha. */
/* align-items: center, não o stretch padrão. A partir de 480px os dois
   botões dividem a linha: o primário tem min-height 56 e o quiet 48, e o
   stretch do flex igualava os dois em 62px. Mesma altura + mesmo peso 700
   virava duas ações do mesmo tamanho, e o herói precisa de uma só. */
.hero__actions {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--space-3); margin-top: var(--space-6);
}
.hero__actions .btn { flex: 1 1 100%; }

/* Pill de lugar com ponto de operação ativa.
   align-self: start porque a coluna é flex e o padrão stretch estica a
   cápsula na largura inteira, virando uma barra em vez de um selo. */
.hero__pill {
  display: inline-flex;
  align-items: center;
  align-self: start;
  gap: var(--space-3);
  max-width: 100%;
  width: fit-content;
  margin: 0 0 var(--space-5);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-3);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: var(--color-primary-soft);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-small);
  line-height: 1.35;
  text-wrap: balance;
  color: var(--color-text-muted);
}
.hero__pulse {
  position: relative;
  width: 7px; height: 7px; flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-primary);
}
.hero__pulse::after {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  background: inherit;
  /* Três pulsos e para. Loop eterno ao lado do H1 rouba a atenção do que a
     pessoa veio ler; três batidas cumprem o papel de dizer "está no ar" e
     depois somem do caminho. */
  animation: hero-pulse 2.4s ease-out 3;
}
@keyframes hero-pulse {
  from { transform: scale(1);   opacity: 0.7; }
  to   { transform: scale(3.2); opacity: 0; }
}

/* Rede e ponto compartilham a grade; as colunas se ajustam ao espaço
   disponível, inclusive quando o visitante amplia o texto.

   Gabinete, não cartão de vitrine. O laranja da primeira dobra é o CTA;
   borda de acento, wash, ticks e blur faziam um segundo botão sob o
   primeiro. O filete de cima é a única luz: a mesma faixa de um painel. */
.hero__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 7rem), 1fr));
  align-items: start;
  gap: var(--space-2) 0;
  margin: 0;
  padding: var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background:
    linear-gradient(var(--color-primary), var(--color-primary)) 0 0 / 100% 1px no-repeat,
    var(--color-surface-alt);
}
.hero__fact {
  position: relative;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--space-2);
  min-width: 0;
  padding-right: var(--space-4);
}
.hero__fact + .hero__fact {
  padding-right: 0;
  padding-left: var(--space-5);
}
.hero__fact + .hero__fact::before {
  content: '';
  position: absolute;
  left: 0; top: 0.15rem; bottom: 0.15rem;
  width: 1px;
  background: linear-gradient(
    180deg,
    transparent,
    color-mix(in srgb, var(--color-text) 26%, transparent) 48%,
    transparent
  );
}
.hero__fact strong {
  grid-row: 1;
  align-self: end;
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 0.7rem + 4.2vw, 2.35rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: var(--tracking-title);
  color: var(--color-text);
  overflow-wrap: normal;
}
.hero__fact > span {
  grid-row: 2;
  font-size: var(--text-sm);
  font-weight: 500;
  line-height: 1.35;
  color: var(--color-text-muted);
}
.hero__fact-unit {
  display: block;
  margin-top: 0.2em;
  font-size: var(--text-xs);
  font-weight: 400;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
/* O count-up envolve o numeral num <span.conta> dentro do <strong>. Qualquer
   `span` genérico neste bloco não pode herdar o tamanho do rótulo. */
.hero__fact .conta {
  font-size: inherit;
  font-weight: inherit;
  letter-spacing: inherit;
}

/*
   TOTEM DE LED
   Mockup inteiro em CSS. Nenhuma imagem de moldura, nenhuma biblioteca.
   A altura é reservada por aspect-ratio: zero CLS enquanto o vídeo carrega.
   */
.totem {
  /* Escala fluida contínua: acompanha a largura sem depender de breakpoint.
     Piso de 230px para o vídeo continuar legível em 320px de viewport. */
  --totem-w: clamp(230px, 72vw, 320px);

  /* MATERIAL
     O totem é um objeto físico desenhado, não um controle da interface. Metal
     é frio: os neutros quentes da marca aqui leriam como plástico bege, e foi
     por isso que estes cinzas nasceram fora do :root. O erro não era a
     temperatura, era a quantidade: nove cinzas espalhados por quatro regras,
     dos quais #34343b e #2e2e35 são a mesma decisão digitada duas vezes.
     Cinco degraus nomeados cobrem o gabinete, o poste e a sapata inteiros, e
     ficam presos ao componente que os usa.                                  */
  --metal-hi:   #45454d;   /* aresta batida pela luz     */
  --metal-up:   #3a3a41;   /* topo do gabinete           */
  --metal-mid:  #2e2e35;   /* face frontal               */
  --metal-lo:   #1a1a1f;   /* face em sombra             */
  --metal-deep: #0e0e11;   /* vinco, vão e contato       */
  /* Vermelho de LED aceso. Não é --color-error: o selo AO VIVO não comunica
     falha, e usar o vermelho semântico aqui ensinaria a cor errada ao olho. */
  --led-live:   #FF4D4D;

  /* GEOMETRIA DO GABINETE
     Os dois únicos números escolhidos. O raio da tela sai deles pela regra do
     raio aninhado (interno = externo − padding), em vez de ser um terceiro
     valor que alguém precisa lembrar de recalcular. */
  --gabinete-r:   18px;
  --gabinete-pad: 10px;

  /* EXCEÇÕES DOCUMENTADAS
     1. O selo AO VIVO simula chrome desenhado DENTRO de um painel de LED, não
        texto da página. 10px é a medida que vende a ilusão de escala; na
        escala tipográfica (12 a 13px) o selo fica do tamanho de um rótulo da
        interface e o mockup deixa de parecer um objeto fotografado.
     2. A sombra da sapata precisa de contato mais escuro que --shadow-md
        (0.55 contra 0.28). A escala de elevação descreve cartão sobre
        superfície; isto é um objeto pousado no chão, e com o contato da
        escala o totem flutua dois centímetros acima do piso. */
  --selo-size:   0.625rem;
  --sombra-piso: 0 2px 3px rgba(0, 0, 0, 0.55), 0 16px 26px rgba(0, 0, 0, 0.42);
  --pixel-ink:   rgba(0, 0, 0, 0.32);
  --sombra-vao:  0 0 0 1px rgba(0, 0, 0, 0.9) inset;   /* vão da moldura */

  /* Atenção: ORÇAMENTO VERTICAL. A linha acima dimensiona o totem só pela largura, e
     a altura vem de graça pelo aspect-ratio 9/16 da tela: altura ≈ largura ×
     1,78. Em tela baixa isso explode:

       tela                   totem  % da altura
       528×467 (seu teste)    633px         136%
       iPhone 14 deitado      633px         162%
       iPhone SE deitado      633px         169%

     Ou seja: o produto que a página existe para vender não cabia na tela de
     quem virasse o celular. 42svh é a largura máxima que mantém o totem
     inteiro abaixo de ~75% da altura, já que altura ≈ largura × 1,78.
     Em telas altas o clamp continua mandando e nada muda: em iPhone 14 em pé
     o min() escolhe os 281px do clamp, não os 354px do svh. */
  --totem-w: min(clamp(230px, 72vw, 320px), 42svh);
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding-block: var(--space-6) var(--space-4);
}

/* Luz que o painel joga no ambiente. É o que separa "tela" de "painel aceso".
   Atenção: A largura é limitada ao container de propósito. Antes era
   calc(var(--totem-w) * 2.4) puro, e como o elemento é absoluto e centralizado
   por translateX(-50%), ele estourava dos DOIS lados por igual:

     viewport  brilho  container  estouro por lado
        320px    553px      288px            132px
        414px    715px      381px            167px   ← pior caso
        528px    768px      486px            141px
        768px    768px      707px             31px

   Isso não aparecia como barra de rolagem enquanto o body tinha
   overflow-x: clip. O clip saiu do body em 10/09. O min() com 100% prende
   o brilho ao container e mantém o efeito: em 528px ele ainda vaza 83px
   de cada lado do gabinete, que tem 320px.
   Não usei overflow no .totem porque isso decapitaria a box-shadow de 54px de
   desfoque do .totem__cabinet, que é o que dá volume ao mockup. */
.totem__glow {
  position: absolute;
  top: 0;
  left: 50%;
  /* estouro-ok: gradiente decorativo, pointer-events:none, que já é
     transparente a 68% do raio. Pode ser mais alto que a tela sem prejuízo:
     não empurra nada, não recebe clique e não tem conteúdo. */
  width: min(calc(var(--totem-w) * 2.4), 100%);
  aspect-ratio: 1;
  transform: translateX(-50%);
  background: radial-gradient(circle,
              color-mix(in srgb, var(--color-primary) 20%, transparent) 0%,
              color-mix(in srgb, var(--color-primary)  7%, transparent) 38%,
              transparent 68%);
  pointer-events: none;
  z-index: 0;
}
/* Gabinete: a caixa metálica que segura a tela. */
.totem__cabinet {
  position: relative;
  z-index: var(--z-base);
  width: var(--totem-w);
  padding: var(--gabinete-pad);
  border-radius: var(--gabinete-r);
  background: linear-gradient(160deg,
              var(--metal-up)   0%,
              var(--metal-lo)  26%,
              var(--metal-deep) 70%,
              var(--metal-mid) 100%);
  box-shadow:
    /* aresta superior iluminada e o vinco interno: rendering de material, sem
       equivalente na escala de elevação, que descreve altura e não superfície */
    0 0 0 1px color-mix(in srgb, var(--color-text) 7%, transparent) inset,
    0 2px 3px rgba(0, 0, 0, 0.5) inset,
    var(--shadow-lg);
}

.totem__screen {
  position: relative;
  overflow: hidden;
  /* RAIO ANINHADO. Regra: interno = externo − padding. Escrito como conta e
     não como resultado: assim mexer no gabinete não deixa a moldura com
     espessura desigual no canto sem ninguém saber dizer o porquê. Hoje dá
     8px, que é exatamente --radius-sm. */
  border-radius: calc(var(--gabinete-r) - var(--gabinete-pad));
  background: var(--metal-deep);
  aspect-ratio: 9 / 16;                 /* reserva a altura antes do vídeo chegar */
  box-shadow: var(--sombra-vao);
}

.totem__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Painel de LED tem preto profundo e cor saturada. */
  filter: saturate(1.12) contrast(1.06);
}
/* Fallback dentro do <video>: sem JS o poster fica e este link e o arquivo.
   Sem font-size o censo lia NaN e, num browser sem video, o texto herdava
   nada. Mesmo degrau do corpo, na tinta da pagina. */
.totem__video a {
  font-size: var(--text-sm);
  color: var(--color-text);
}

/* Matriz de pixels: dois gradientes finos cruzados. ~40 bytes de CSS
   fazendo o trabalho que uma textura PNG faria com 30 KB. */
.totem__pixels {
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Tinta da matriz, não cor de interface: entra por mix-blend-mode: multiply
     para escurecer o vídeo em faixas de 1px, do jeito que o vão entre LEDs
     escurece. Preto puro porque multiply com qualquer croma tinge o vídeo. */
  background-image:
    repeating-linear-gradient(0deg,   var(--pixel-ink) 0 1px, transparent 1px 3px),
    repeating-linear-gradient(90deg,  var(--pixel-ink) 0 1px, transparent 1px 3px);
  mix-blend-mode: multiply;
  opacity: 0.55;
}

/* Reflexo do vidro. Diagonal única, bem sutil. */
.totem__sheen {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg,
              color-mix(in srgb, var(--color-text) 10%, transparent) 0%,
              color-mix(in srgb, var(--color-text)  3%, transparent) 24%,
              transparent 46%);
}

.totem__live {
  position: absolute;
  top: var(--space-3); left: var(--space-3);
  display: flex; align-items: center; gap: var(--space-1);
  margin: 0;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  background: var(--veil-strong);
  backdrop-filter: blur(6px);
  font-size: var(--selo-size); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text);
}
.totem__live span {
  width: 6px; height: 6px; border-radius: var(--radius-full);
  background: var(--led-live);
  /* Mais lento e mais sutil que antes: o selo AO VIVO precisa existir, não
     precisa piscar. */
  animation: totem-live 3.4s ease-in-out infinite;
}
@keyframes totem-live { 50% { opacity: 0.25; } }

.totem__sound {
  position: absolute;
  bottom: var(--space-3); right: var(--space-3);
  display: grid; place-items: center;
  width: 44px; height: 44px;
  padding: 0;
  /* 40% e não 22%: a borda é o único contorno do botão sobre vídeo, então
     responde ao SC 1.4.11. Mesma decisão já tomada no .splash__skip. */
  border: 1px solid color-mix(in srgb, var(--color-text) 40%, transparent);
  border-radius: var(--radius-full);
  background: var(--veil-strong);
  backdrop-filter: blur(6px);
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--duration-fast) var(--ease-out),
              color var(--duration-fast) var(--ease-out),
              scale var(--duration-fast) var(--ease-out);
}
/* O hover mexe só na borda e no ícone. Escurecer mais a chapa era um terceiro
   valor de véu para dizer o que a borda laranja já diz sozinha. */
@media (hover: hover) and (pointer: fine) {
  .totem__sound:hover { border-color: var(--color-primary); color: var(--color-primary); }
}
.totem__sound:active { scale: 0.92; }
@media (prefers-reduced-motion: reduce) {
  .totem__sound:active { scale: 1; }
}
.totem__ico {
  width: 19px; height: 19px; fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.totem__ico--on { display: none; }
.totem__sound[aria-pressed="true"] .totem__ico--off { display: none; }
.totem__sound[aria-pressed="true"] .totem__ico--on  { display: block; }

/* Pedestal: poste e sapata. É o que faz o objeto virar totem de rua. */
.totem__base {
  position: relative; z-index: var(--z-base);
  display: flex; flex-direction: column; align-items: center;
  width: 100%;
}
.totem__pole {
  width: 15%;
  height: clamp(20px, 4vw, 32px);
  background: linear-gradient(90deg,
              var(--metal-deep)  0%,
              var(--metal-mid)  42%,
              var(--metal-hi)   52%,
              var(--metal-lo)  100%);
}
.totem__foot {
  width: 42%;
  height: 9px;
  /* Raio aninhado: o pé encosta no chão, então a aresta de baixo é mais
     macia que a de cima. Fica fora da escala de propósito, porque isto não é
     um controle, é um objeto físico desenhado. */
  border-radius: 3px 3px 5px 5px;
  background: linear-gradient(90deg,
              var(--metal-deep)  0%,
              var(--metal-mid)  50%,
              var(--metal-deep) 100%);
  /* Duas camadas, e aqui é onde mais importa: um objeto pousado no chão tem
     contato quase preto logo abaixo da base e difusão larga em volta. Só a
     difusão fazia o totem parecer flutuando dois centímetros acima do piso. */
  box-shadow: var(--sombra-piso);
}

.totem__tag {
  position: relative; z-index: var(--z-base);
  margin: var(--space-5) 0 0;
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-small);
  color: var(--color-text-muted);
  text-align: center;
}

/* Em 900px o hero passa de 1 para 2 colunas e o totem perde espaço.
   Um clamp contínuo cobre de 900px até ultrawide sem nenhum degrau: com
   breakpoints separados em 1200 e 1600 havia um salto de 18% na largura
   exatamente em 1600px, visível ao redimensionar a janela.
   Padding de cima some: o gabinete alinha com o H1, não com a pill. */
@media (min-width: 900px) {
  .totem {
    --totem-w: clamp(292px, 26vw, 400px);
    padding-block: 0 var(--space-4);
  }
}

@media (prefers-reduced-motion: reduce) {
  .totem__live span { animation: none; }
}

/* Apresentação: o vídeo acompanha o argumento antes da escolha dos pontos.
   A proporção preserva o quadro vertical; a altura também limita o tamanho
   para o player caber em janelas baixas. Controles nativos durante a reprodução. */
.hero__watch {
  display: inline-flex; align-items: center; align-self: flex-start;
  gap: var(--space-2); min-height: 44px;
  margin-top: var(--space-3);
  font-size: var(--text-sm); font-weight: 700;
  color: var(--color-text-muted);
  text-decoration: underline; text-underline-offset: .25em;
}
.hero__watch svg { width: 20px; height: 20px; flex-shrink: 0; fill: var(--color-primary); }
.stakes__story { display: grid; gap: var(--space-10); align-items: center; }
.stakes__story > * { min-width: 0; }
.apresentacao {
  margin: 0; justify-self: center;
  width: min(100%, 22rem, calc(78svh * 408 / 720));
  scroll-margin-top: calc(var(--header-h) + var(--space-4));
}
.apresentacao [hidden] { display: none; }
.apresentacao__frame {
  position: relative; aspect-ratio: 408 / 720;
  background: var(--color-surface-alt);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.apresentacao__video,
.apresentacao__cover,
.apresentacao__cover img {
  display: block; width: 100%; height: 100%;
  border-radius: inherit; object-fit: contain;
}
.apresentacao__cover {
  position: absolute; inset: 0;
  color: var(--color-text); text-decoration: none;
  border-radius: inherit;
}
.apresentacao__cover::after {
  content: ''; position: absolute; inset: 50% 0 0;
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  background: linear-gradient(transparent, var(--veil-strong));
  pointer-events: none;
}
.apresentacao__badge {
  position: absolute; top: var(--space-3); left: var(--space-3);
  padding: var(--space-1) var(--space-3);
  background: var(--veil-strong); border-radius: var(--radius-full);
  color: var(--color-text); font-size: var(--text-xs); font-weight: 700;
}
.apresentacao__play {
  position: absolute; z-index: var(--z-base);
  inset-inline: var(--space-4); bottom: var(--space-5);
  display: flex; align-items: center; gap: var(--space-3);
  font-size: var(--text-sm); font-weight: 700;
  color: var(--color-text);
}
.apresentacao__play-icon {
  display: grid; place-items: center; flex-shrink: 0;
  width: 52px; height: 52px; border-radius: var(--radius-full);
  background: var(--color-primary); color: var(--color-primary-ink);
  transition: background-color var(--duration-fast) var(--ease-out);
}
.apresentacao__play-icon svg { width: 24px; height: 24px; fill: currentColor; }
.apresentacao__caption { padding-top: var(--space-4); }
.apresentacao__caption strong { font-family: var(--font-display); font-size: var(--text-lg); }
.apresentacao__caption p {
  margin: var(--space-1) 0 0; font-size: var(--text-sm); color: var(--color-text-muted);
}
.apresentacao__next {
  display: inline-flex; align-items: center; gap: var(--space-2);
  min-height: 44px; margin-top: var(--space-2);
  color: var(--color-primary); font-size: var(--text-sm); font-weight: 700;
  text-underline-offset: .25em;
}
.apresentacao__status { margin: var(--space-2) 0 0; font-size: var(--text-sm); color: var(--color-text-muted); }
.apresentacao__status a { display: inline-block; padding-block: var(--space-3); color: var(--color-text); }
@media (min-width: 900px) {
  .stakes__story { grid-template-columns: minmax(0, 1.25fr) minmax(0, .75fr); gap: var(--space-12); }
  .stakes__story .section__head { max-width: 100%; }
}
@media (hover: hover) and (pointer: fine) {
  .hero__watch:hover { color: var(--color-text); }
  .apresentacao__cover:hover .apresentacao__play-icon { background: var(--color-primary-hover); }
}

/* O placeholder de mídia saiu em 18/08: as fotos dos sete pontos chegaram e
   ele não tinha mais nenhum uso no markup. Componente sem markup é peso morto
   que sobrevive anos porque ninguém sabe se ainda é usado. Os três seletores
   entraram na lista F7 do teste, junto com os de depoimento. */

/* 6 · MARQUEE
   Faixa de reconhecimento. O objetivo é o visitante se achar em 1 segundo,
   não decorar a lista. Por isso ícone + nome, e não só nome.
   */
.marquee {
  position: relative;
  border-block: 1px solid var(--color-border);
  background:
    /* leve calor no centro: a faixa deixa de ser uma tarja cinza */
    radial-gradient(120% 200% at 50% 50%, var(--color-primary-soft), transparent 62%),
    var(--color-surface-alt);
  padding-block: var(--space-5) var(--space-6);
  overflow: hidden;
}

.marquee__head {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-4);
}
.marquee__label {
  margin: 0;
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Botão de pausa: exigência do WCAG 2.2 SC 2.2.2, não enfeite. */
.marquee__toggle {
  /* min-width junto do min-height: abaixo de 640px o rótulo "Pausar" some e
     sobra o ícone, e o botão fechava em 41px de largura por 44 de altura.
     Alvo quadrado de 44 nos dois eixos, sempre. */
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2);
  min-width: 44px; min-height: 44px;
  padding: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-muted);
  font: inherit; font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-small);
  cursor: pointer;
  transition: color var(--duration-fast) var(--ease-out),
              border-color var(--duration-fast) var(--ease-out),
              scale var(--duration-fast) var(--ease-out);
}
.marquee__toggle:active { scale: 0.92; }
@media (prefers-reduced-motion: reduce) {
  .marquee__toggle:active { scale: 1; }
}
@media (hover: hover) and (pointer: fine) {
  .marquee__toggle:hover { color: var(--color-text); border-color: var(--color-text-muted); }
}
.marquee__ico { width: 15px; height: 15px; fill: none; stroke: currentColor;
                stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.marquee__ico--play { display: none; fill: currentColor; stroke: none; }
.marquee__toggle[aria-pressed="true"] .marquee__ico--pause { display: none; }
.marquee__toggle[aria-pressed="true"] .marquee__ico--play  { display: block; }

/* Fade nas bordas. Sem isso o texto é decepado na borda da tela e parece bug. */
/* Fade mais largo, 14% em vez de 7%: a 7% o texto ainda aparecia quase opaco
   na borda e depois sumia de uma vez, o que lê como corte. A 14% ele se
   dissolve, que é o que a faixa quer parecer. */
.marquee__viewport {
  display: flex;
  overflow: hidden;
  /* EXCEÇÃO DOCUMENTADA: #000 aqui não é cor, é o canal alfa da máscara.
     Opaco significa "mostra", transparente significa "esconde", e trocar por
     um token de cor da marca não muda nada além de esconder a intenção. */
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 14%, #000 86%, transparent);
}

.marquee__track {
  display: flex; flex-shrink: 0; align-items: center;
  /* O respiro entre os itens e o respiro antes da repetição são a MESMA
     distância: se divergirem, a emenda do loop aparece como um soluço. */
  --marquee-gap: clamp(var(--space-6), 4vw, var(--space-12));
  gap: var(--marquee-gap);
  padding-right: var(--marquee-gap);
  margin: 0;
  white-space: nowrap;
  animation: marquee 38s linear infinite;
  will-change: transform;
}
/* A cópia é gerada por JS. Se o JS não rodar, a faixa não anima e vira uma
   lista estática legível: degradação correta, nada quebrado. */
.marquee:not([data-marquee-ready]) .marquee__track { animation: none; }

.marquee__item {
  display: inline-flex; align-items: center; gap: var(--space-3);
  font-family: var(--font-display);
  /* --text-lg e não --text-xl: em --text-xl a faixa competia com o H1 logo
     acima, e hierarquia invertida é o que faz um site parecer amador. Ela é
     apoio, não manchete. */
  font-size: var(--text-lg); font-weight: 700;
  letter-spacing: var(--tracking-sub);
  /* Texto cheio, não muted: a faixa é conteúdo, não legenda. */
  color: var(--color-text);
}

/* Ponto separador entre segmentos. Sem ele os itens viram uma frase só e o
   olho não sabe onde um termina. Com ele a faixa lê como enumeração. */
.marquee__item::after {
  content: "";
  width: 4px; height: 4px;
  margin-inline-start: var(--space-3);
  border-radius: var(--radius-full);
  background: var(--color-border-strong);
  flex-shrink: 0;
}
.marquee__glyph {
  width: 1.15em; height: 1.15em; flex-shrink: 0;
  fill: none; stroke: var(--color-primary);
  stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
}

@keyframes marquee { to { transform: translateX(-100%); } }

/* Pausa no hover e no foco. Complementa o botão, não substitui. */
@media (hover: hover) and (pointer: fine) {
  .marquee:hover .marquee__track { animation-play-state: paused; }
}
.marquee:focus-within .marquee__track { animation-play-state: paused; }
.marquee.is-paused .marquee__track   { animation-play-state: paused; }

@media (prefers-reduced-motion: reduce) {
  .marquee__track { animation: none; }
  .marquee__viewport { overflow-x: auto; -webkit-mask-image: none; mask-image: none; }
  .marquee__toggle { display: none; }   /* sem movimento, não há o que pausar */
}

@media (max-width: 479px) {
  .marquee__item { font-size: var(--text-lg); }
  .marquee__toggle-txt { position: absolute; width: 1px; height: 1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  .marquee__toggle { padding-inline: var(--space-3); }
}

/* 7 · CARDS (base) */
.cards { display: grid; gap: var(--space-5); }

.card {
  display: flex; flex-direction: column;
  border: 1px solid var(--color-border-accent);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  transition: border-color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}
.card__title { font-size: var(--text-lg); font-weight: 700; }
.card__text  { margin: var(--space-3) 0 0; color: var(--color-text-muted); font-size: var(--text-sm); }

.card--outline { padding: var(--space-6); background: transparent; }
@media (hover: hover) and (pointer: fine) {
  .card--outline:hover { border-color: var(--color-primary); }
}

/* 8 · STAKES */
.stakes__text { max-width: var(--medida); font-size: var(--text-lg); }
.stakes__cards { margin-top: var(--space-10); }
.stakes__bridge {
  margin: var(--space-10) 0 0;
  font-family: var(--font-display);
  font-size: var(--text-xl);
  letter-spacing: var(--tracking-sub);
  color: var(--color-text-muted);
}
.stakes__bridge strong { color: var(--color-text); }

/* 9 · PONTOS */
/* O CARTÃO DE PONTO EM QUATRO BLOCOS, NESTA ORDEM:
   foto com etiqueta · identificação · ficha técnica · valores · ação.
   Antes eram sete elementos soltos no mesmo nível, separados só por margem, e
   o olho tinha que descobrir sozinho onde uma informação acabava e a outra
   começava. Agrupar em blocos com fundo e moldura próprios é o que faz o
   cartão ser lido em quatro paradas em vez de sete. */

.card--ponto {
  overflow: hidden;
  /* A herança de .card só transita borda e transform. A sombra entra na conta
     aqui porque o cartão de ponto é o único que sobe no hover, e sombra que
     aparece de uma vez enquanto o cartão sobe em 200ms lê como piscada. */
  transition: border-color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .card--ponto:hover {
    border-color: var(--color-border-strong);
    /* 2px e não 6: o cartão tem 500px de altura, e deslocamento grande num
       objeto grande lê como o layout tremendo, não como resposta ao mouse. */
    transform: translateY(-2px);
    /* Subir sem sombra é o cartão trocando de posição sem trocar de altura:
       o olho vê o deslocamento e não recebe a explicação. --shadow-md é a
       mesma sombra do cartão em destaque dos pacotes, então a página tem uma
       ideia só de "isto está à frente". */
    box-shadow: var(--shadow-md);
  }
  .card--ponto:hover .card__img { transform: scale(1.02); }
}
.card--ponto .card__body {
  display: flex; flex-direction: column; flex: 1;
  padding: clamp(.75rem, 4vw, 1.25rem);
}

/* FOTO DO PONTO
   Quadro 1:1 com a foto INTEIRA dentro, sem corte. As sete fotos originais
   têm proporções de 0,75 a 1,18; a mediana é 1,01, então o quadrado é o
   formato que menos sobra no conjunto. O que sobra não é faixa morta: o
   ferramentas/pontos.py preenche com a própria foto desfocada e escurecida,
   então a cena continua para fora do quadro em vez de terminar numa barra.

   `cover` continua aqui de propósito, mesmo com o arquivo já em 1:1: ele é a
   rede para o dia em que alguém trocar uma foto por uma fora de proporção.
   width e height no <img> reservam o espaço antes do byte chegar. */
.card__media {
  position: relative;
  margin: 0;
  overflow: hidden;
  background: var(--color-surface);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.card__img {
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  transition: transform var(--duration-slow) var(--ease-out);
}

/* Atenção: O TETO DE ALTURA SAIU DA IMAGEM E FOI PARA O QUADRO. ISSO CORRIGE CORTE.
   Havia aqui `max-height: 50vh` no próprio <img>, para janela baixa e larga
   (celular deitado, notebook antigo) não virar um cartão que é só foto.

   O efeito colateral era grave: com `width: 100%` e altura travada, o elemento
   deixava de ser quadrado e virava uma tira larga. Aí o `object-fit: cover`
   fazia o que ele faz, cortava topo e base para preencher. Em 528 por 467 o
   quadro dava 496 por 233, ou seja proporção 2,1:1 em cima de um arquivo 1:1:
   sumia mais da metade da altura da foto, painel incluído.

   Agora o teto é de LARGURA e vale para o quadro inteiro. O quadrado continua
   quadrado, encolhe e centraliza, e nenhum pixel da foto é cortado em nenhuma
   janela. O `object-fit: cover` fica como rede para o dia em que alguém trocar
   uma foto por uma fora de proporção. */
.card--ponto .card__media {
  /* Atenção: width DEFINIDA, E align-self NO LUGAR DE margin-inline: auto.
     A primeira versão usava `max-width` mais `margin-inline: auto`. Parecia
     equivalente e não é: item de flex com margem automática no eixo cruzado
     PERDE o stretch e passa a se medir por fit-content. Como a foto é lazy,
     antes de ela chegar o fit-content é ZERO: o quadro nascia 0 por 0, o
     cartão nascia com 529px em vez de 892, e a seção dos pontos inteira
     crescia 1.090px no momento em que as fotos carregavam.

     O estrago não ficava na seção: tudo abaixo dela descia junto, e o clique
     em "Valores" no menu aterrissava 1.163px antes da seção de valores,
     porque o alvo tinha sido calculado com a página menor.

     Com `width` definida o navegador resolve o `width: 100%` do <img>, a
     aspect-ratio 1/1 reserva a altura antes do primeiro byte da foto, e
     `align-self: center` centraliza sem mexer na medida. */
  width: min(100%, 54dvh);
  aspect-ratio: 1 / 1;
  align-self: center;
  /* Quando o teto entra, o quadro descola das laterais do cartão. Arredondar
     os quatro cantos evita o canto reto solto no meio do fundo. */
  border-radius: var(--radius-md);
}
/* Véu do pé da foto: liga a imagem ao corpo do cartão e dá contraste para a
   etiqueta em qualquer foto, sem depender da cor que calhar de estar ali. */
.card__media::after {
  content: "";
  position: absolute; inset-inline: 0; bottom: 0;
  height: 38%;
  background: linear-gradient(to top,
              color-mix(in srgb, var(--color-surface-alt) 92%, transparent),
              transparent);
  pointer-events: none;
}

.card__title { letter-spacing: var(--tracking-sub); }
.card--ponto .card__title { font-size: var(--text-xl); }
.card__scene {
  margin: var(--space-2) 0 var(--space-5);
  color: var(--color-text-muted); font-size: var(--text-sm);
  text-wrap: pretty;
}
/* ALINHAMENTO INTERNO DA LINHA
   Os sete nomes e endereços têm comprimentos diferentes: "Painel Andrino" cabe
   numa linha, "Totem interno Shopping Unisul" ocupa duas, e o endereço da
   Fucap ocupa duas onde o do Andrino ocupa uma. Medido, isso empurrava a ficha
   técnica de um cartão até 84px abaixo da do vizinho na mesma linha, e o olho
   lia a linha como três cartões desencontrados.

   A folga vai para o vão livre entre o endereço e a ficha, não para o fim do
   cartão: `margin-bottom: auto` prende ficha, preço e botão no pé, onde ficam
   na mesma altura nos três cartões, e a diferença de altura do nome vira ar
   depois do endereço, que é onde ar não parece defeito.

   Alinhar por cima exigiria reservar duas linhas fixas para nome e endereço,
   e aí o cartão de nome curto ganharia duas linhas em branco no meio do texto,
   que lê como conteúdo faltando. */
.card--ponto .card__scene { margin-bottom: auto; }

/* ETIQUETA SOBRE A FOTO
   Saiu do corpo do cartão, onde ocupava uma linha inteira acima do título e
   repetia a mesma frase sete vezes em coluna, empurrando o conteúdo que
   diferencia um ponto do outro para baixo. Sobre a foto ela vira o que sempre
   foi, uma etiqueta, e devolve uma linha de altura a cada cartão. */
.badge {
  align-self: flex-start;
  margin: 0;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}
.badge--check {
  position: absolute; top: var(--space-4); left: var(--space-4);
  /* Sem o teto, o bloco absoluto se estica do recuo da esquerda até a borda
     direita da foto: em cartão de 260px a etiqueta encostava na moldura e
     lia como faixa de cabeçalho, não como selo. O teto devolve o mesmo
     recuo dos dois lados quando o texto precisa de duas linhas. */
  max-width: calc(100% - var(--space-8));
  z-index: var(--z-base);
  display: inline-flex; align-items: center; gap: var(--space-2);
  background: var(--veil-strong);
  backdrop-filter: blur(8px);
  color: var(--color-text);
  border: 1px solid color-mix(in srgb, var(--color-text) 22%, transparent);
}
/* O ponto antes do texto: a etiqueta é status, e status sem indicador é só
   uma frase dentro de uma cápsula. */
.badge--check::before {
  content: "";
  width: 6px; height: 6px; flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--color-primary);
}
.badge--featured {
  position: absolute; top: calc(var(--space-3) * -1); left: var(--space-5);
  background: var(--color-primary); color: var(--color-primary-ink); border: 0;
}

/* FICHA TÉCNICA
   Era um par de linhas com hairline em cima e embaixo, solto no fluxo do
   cartão. Agora é uma caixa com superfície própria, um degrau abaixo do
   cartão: o olho reconhece "aqui é especificação" antes de ler a primeira
   palavra, e a separação do bloco de preço deixa de depender só de margem. */
.specs {
  margin: var(--space-5) 0 var(--space-4);
  padding: var(--space-3) clamp(.5rem, 2vw, 1rem);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
}
/* Rótulo em cima, valor embaixo, nas duas linhas.

   Em linha, com o valor à direita, o resultado dependia do comprimento do
   texto: "Resolução  960 × 384 px" cabia numa linha e "Formato  Painel
   horizontal · 5,00 × 2,00 m" não cabia, então uma linha ficava em par e a
   outra quebrada, dentro da mesma caixa. Parecia acidente, porque era.

   Empilhado, as duas linhas têm a mesma forma em qualquer largura, e o valor
   ganha a largura inteira da caixa antes de precisar quebrar, que é o que faz
   a altura da ficha variar menos entre os cartões da mesma linha. */
.specs__row {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  overflow-wrap: anywhere;
  gap: var(--space-1);
  padding: var(--space-2) 0;
  font-size: var(--text-sm);
}
.specs__row + .specs__row { border-top: 1px solid var(--color-border); }
.specs dt {
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
}
.specs dd {
  margin: 0; font-weight: 700; min-width: 0;
  font-variant-numeric: tabular-nums;
  text-wrap: pretty;
}

/* VALORES
   Um prazo por linha, rótulo à esquerda e número à direita.

   Lado a lado não cabe, e a medição diz onde: na faixa de 900px a 1023px a
   grade já está em três colunas e o cartão tem 260px, sobrando 218px de
   conteúdo. Dois valores de 138px mais fio e vão pedem 309px. Como coluna
   `1fr` não encolhe abaixo do conteúdo e o cartão tem `overflow: hidden`, o
   segundo preço não estouraria a tela: sumiria, calado. Reduzir a fonte até
   caber resolveria o estouro entregando um preço de 18px logo acima de um
   botão de 16px, que é desistir da hierarquia para salvar o arranjo.

   Empilhado, os dois números ficam alinhados na mesma borda direita, que é a
   posição em que comparar 399 com 449 custa um movimento de olho vertical em
   vez de um salto lateral por cima de um fio. E cabe em 273px de janela.

   Sem caixa, ao contrário da ficha técnica logo acima: especificação é dado
   de consulta e fica recuada, preço é o que decide e fica na superfície do
   cartão. Nada de laranja aqui, o botão logo abaixo é laranja e acento
   repetido dentro da mesma caixa deixa de dirigir o olho para o clique. */
.preco {
  display: grid;
  margin: 0 0 var(--space-5);
}
.preco__col {
  display: flex; align-items: baseline;
  justify-content: space-between;
  gap: var(--space-1) var(--space-3);
  flex-wrap: wrap;                     /* rótulo e valor nunca se sobrepõem */
  padding: var(--space-2) 0;
}
.preco__col + .preco__col { border-top: 1px solid var(--color-border); }
.preco__prazo {
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  color: var(--color-text-muted);
  flex-shrink: 0;
}
.preco__valor {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-xl); font-weight: 800;
  letter-spacing: var(--tracking-sub);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
  margin-inline-start: auto;           /* encosta na direita mesmo se quebrar */
}
.preco__unidade {
  font-family: var(--font-body); font-size: var(--text-sm); font-weight: 400;
  letter-spacing: var(--tracking-flat); color: var(--color-text-muted);
}
/* A faixa em que a grade já abriu três colunas mas a janela ainda não cresceu:
   cartão de 260px, 218px de conteúdo, e "SEMESTRAL" mais vão mais número
   pedem 223px. Faltam cinco, e o efeito não é estouro: é "SEMESTRAL" caindo
   de linha enquanto "ANUAL" fica onde estava, deixando duas formas diferentes
   dentro do mesmo bloco de preço.

   Diminuir o número até caber trocaria a hierarquia pelo arranjo: o preço
   ficaria menor que o do cartão vizinho na janela seguinte. Empilhar rótulo e
   valor nas duas linhas resolve sem tocar no corpo, e nesta faixa o preço
   passa a ter a mesma forma da ficha técnica logo acima, que é a única outra
   caixa do cartão. */
.card--plano { container-type: inline-size; }
@container (max-width: 22rem) {
  .preco__col { display: grid; gap: var(--space-1); }
  .preco__valor { margin-inline-start: 0; }
}

/* Não repetir `margin-top: auto` no botão. Quem empurra o bloco de baixo para
   o pé do cartão é o `margin-bottom: auto` do endereço, e duas margens
   automáticas na mesma coluna flex dividem a sobra pela metade: metade do ar
   ficaria depois do endereço e metade acima do botão, e ficha e preço
   voltariam a flutuar em alturas diferentes. Medido: com as duas, o preço
   ainda desencontrava 42px na faixa de 900px. Com uma só, zero. */

.pontos__note {
  margin: var(--space-8) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.pontos__note strong { color: var(--color-text); }

/* Carrossel so no celular. Em 640px a grade de duas colunas assume e estas
   regras nao existem. Sem JS o trilho ainda desliza; os dots e o aria
   entram no main.js. */
.pontos__dots { display: none; }

@media (max-width: 639px) {
  .pontos__grid {
    --ponto-largura: min(100%, max(16rem, calc(100% - 2.75rem)));
    display: flex;
    align-items: stretch;
    gap: var(--space-4);
    overflow-x: auto;
    overflow-y: hidden;
    overscroll-behavior-x: contain;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: 0;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    touch-action: pan-x pan-y;
    /* Sem isto o Chromium soma o scrollWidth interno do trilho (sete
       cartoes) no documentElement: 1943px contra 360px de janela, e a
       pagina inteira passa a panar de lado. overflow-x: clip na secao
       nao impede essa soma. contain: paint isola a tinta do trilho
       sem criar scrollport no eixo Y (o sticky de outras secoes
       continua na janela). */
    contain: paint;
  }
  .pontos__grid::-webkit-scrollbar { display: none; }
  .pontos__grid > .card--ponto {
    flex: 0 0 var(--ponto-largura);
    min-inline-size: 0;
    scroll-snap-align: start;
    scroll-snap-stop: always;
  }
  /* Folga depois do ultimo: o flex-basis deixa recorte a direita, e sem
     esta margem o setimo cartao nao chega a alinhar no inicio. */
  .pontos__grid > .card--ponto:last-child {
    margin-inline-end: calc(100% - var(--ponto-largura));
  }
  .pontos__grid > .card--ponto:only-child { flex-basis: 100%; margin-inline-end: 0; }
  /* SOAP revela no viewport com limiar de 12%. O recorte do proximo
     cartao fica abaixo disso, e o peek nascia invisivel. No trilho o
     cartao ja e conteudo da secao: entra visivel. */
  [data-motion="full"] .pontos__grid > .soap-reveal {
    opacity: 1;
    translate: none;
  }
  [data-motion="full"] .pontos__grid > .soap-reveal:not(.is-visible) {
    will-change: auto;
  }
  .pontos__dots:not([hidden]) {
    display: flex;
    justify-content: center;
    align-items: center;
    flex-wrap: wrap;
    gap: 0;
    margin-block-start: var(--space-5);
  }
  .pontos__dot {
    box-sizing: border-box;
    width: 44px;
    height: 44px;
    padding: 0;
    border: 0;
    background: transparent;
    color: inherit;
    display: grid;
    place-items: center;
    cursor: pointer;
  }
  .pontos__dot::after {
    content: "";
    width: 8px;
    height: 8px;
    border-radius: var(--radius-full);
    background: var(--color-border-strong);
    transition: width var(--duration-fast) var(--ease-out),
                background-color var(--duration-fast) var(--ease-out);
  }
  .pontos__dot[aria-current="true"]::after {
    width: 1.375rem;
    background: var(--color-primary);
  }
  @media (hover: hover) and (pointer: fine) {
    .pontos__dot:hover::after { background: var(--color-text-muted); }
    .pontos__dot[aria-current="true"]:hover::after { background: var(--color-primary); }
  }
}

@media (prefers-reduced-motion: reduce) {
  .pontos__grid { scroll-behavior: auto; }
  .pontos__dot::after { transition: none; }
}

/* 10 · PROCESSO */
.card--step {
  padding: var(--space-6);
  --step-num: 44px;
}
/* Anel laranja e numeral no contraste máximo. Sem fundo: superfície dentro de
   superfície seria a terceira camada num alvo de 44px. 44 e o mesmo piso
   dos outros controles da pagina; 42 ficava 2px abaixo da escala e do alvo. */
.step__num {
  display: grid; place-items: center;
  width: var(--step-num); height: var(--step-num);
  margin: 0 0 var(--space-5);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text);
  font-family: var(--font-display); font-weight: 800; font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
}
@media (hover: hover) and (pointer: fine) {
  .card--step:hover { border-color: var(--color-primary); }
}

/* 11 · PACOTES */
.card--plano { position: relative; padding: var(--space-6); }
/* Com a moldura laranja valendo para todos os cartões, borda no tom cheio
   deixou de separar o plano recomendado. A diferença é ESPESSURA, não cor:
   um anel de 1px por fora, para o padding permanecer --space-6 e o texto
   dos dois planos continuar no mesmo eixo. */
.card--plano.is-featured {
  border-color: var(--color-primary);
  background: var(--color-surface-raised);
  box-shadow: var(--shadow-md), 0 0 0 1px var(--color-primary);
}
.plano__price {
  margin: var(--space-4) 0 var(--space-8);
  display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--space-2);
}
.plano__value  {
  font-family: var(--font-display);
  font-size: var(--text-2xl); font-weight: 800;
  letter-spacing: var(--tracking-title);
}
/* Os cartões apresentam os prazos anual e semestral. O valor é um piso
   por ponto; preserve a ressalva "a partir de" e a tabela por local. */
/* "a partir de" antes do valor, e não depois: o cartão do prazo mostra o piso
   da tabela, e piso sem aviso vira promessa de preço único. O prefixo é a
   ressalva chegando antes do número, no mesmo lugar em que a nota da seção
   abaixo abre a faixa inteira. */
.plano__prefix { color: var(--color-text-muted); font-size: var(--text-sm); }
.plano__period { color: var(--color-text-muted); font-size: var(--text-sm); }
/* RITMO INTERNO DO CARTÃO
   O cartão tem quatro zonas: quem é, quanto custa, o que inclui, o que fazer.
   O vão entre zonas era --space-6 (24px) e o vão entre itens da lista é
   --space-3 (12px): razão de 2 para 1. Abaixo de 3 para 1 o olho agrupa tudo
   como um bloco só, e o botão vira o quinto item da lista de inclusões em vez
   da conclusão do cartão. Com --space-10 (40px) a razão vai a 3,3 e as quatro
   zonas passam a ser lidas como quatro. */
.plano__features {
  display: grid; gap: var(--space-3);
  margin-bottom: var(--space-10);
  font-size: var(--text-sm);
}
.plano__features li { position: relative; padding-left: var(--space-6); color: var(--color-text-muted); }
/* O marcador era laranja, nos doze itens dos três cartões. Doze usos do
   acento como enfeite contra UM botão principal na mesma seção: o laranja
   deixava de significar "clique aqui" e passava a significar "tem uma bolinha
   aqui". Raridade é o que torna o acento irresistível, e ele estava sendo
   gasto em pontuação.

   Neutro forte, que é o token com 3:1 garantido contra as três superfícies.
   Dentro do cartão de plano o laranja agora aparece em dois lugares só, e os
   dois são decisão: o selo do plano recomendado e o botão. */
.plano__features li::before {
  content: ""; position: absolute; left: 0; top: 0.55em;
  width: 8px; height: 8px; border-radius: var(--radius-full);
  background: var(--color-border-strong);
}
.card--plano .btn { margin-top: auto; }
/* O QUE ESTÁ INCLUSO
   Fora dos cartões porque é igual nos dois prazos. Dentro, viraria duas listas
   idênticas que obrigam a comparação item a item para descobrir que a única
   diferença é o prazo. Reusa .plano__features: mesmo marcador, mesmo ritmo,
   nenhum componente novo para manter. */
.incluso { margin-top: var(--space-10); }
.incluso__label {
  margin: 0 0 var(--space-5);
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}
.incluso__list { margin-bottom: 0; }

.pacotes__note { margin: var(--space-8) 0 0; font-size: var(--text-sm); color: var(--color-text-muted); }

/* 12 · PROVA */
.prova__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-4);
  margin-bottom: var(--space-6);
}
.prova__label,
.prova__count {
  margin: 0;
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}
.prova__count {
  flex-shrink: 0;
  white-space: nowrap;
}
.prova__count-num {
  font-size: var(--text-sm);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--tracking-flat);
  color: var(--color-primary);
}
/*
   PAREDE DE MARCAS
   Trinta e cinco marcas na cor original, cada uma numa placa clara.

   A PLACA É CLARA POR NECESSIDADE, NÃO POR GOSTO.
   Parte dos arquivos tem arte escura (feita para viver sobre branco); o resto
   tem arte clara sobre fundo colorido. Sobre placa escura, as primeiras
   somem; sobre placa clara, as outras continuam legíveis porque trazem o
   próprio fundo junto. Placa clara é o único fundo em que as 35 sobrevivem.
   O `ferramentas/marcas.py` decide marca a marca o que fazer com o fundo e dá
   o mesmo raio de canto às que o mantêm, para que a variação seja só de cor.

   PLACAS SOLTAS, NÃO UM RETÂNGULO BRANCO.
   A malha antiga era um painel único (gap de 1px + overflow hidden): lia como
   folha colada sobre a rua. Aqui cada célula é um módulo, com vão o bastante
   para a superfície escura aparecer entre elas, o mesmo vocabulário dos
   cartões da página, e a metáfora do painel de LED. 35 fecha em 5 colunas
   (sete linhas) e em 7 colunas (cinco linhas). Nenhuma célula vazia.
   */
.logos {
  display: grid;
  /* Três colunas no celular e não quatro: em 360px, quatro colunas dão célula
     de ~80px, e nela o Nelson e o Silva viram borrão. Três dão ~110px.
     35 não divide por 3, então a PRIMEIRA célula ocupa duas colunas e a malha
     fecha em 12 linhas. Ser a primeira e não a última não é detalhe: a célula
     dupla vira hierarquia (a marca de maior peso de prova abre a parede) em
     vez de sobra de conta. */
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  isolation: isolate;
  margin-bottom: var(--space-10);
}
.logos__item {
  display: grid;
  place-items: center;
  position: relative;
  /* A altura acompanha a PROPORÇÃO DA TELA das marcas (300×190). Célula alta
     demais deixa a marca boiando, e o olho lê a linha como desalinhada. */
  min-height: clamp(96px, 26vw, 112px);
  padding: var(--space-2);
  /* Off-white e não branco puro: a paleta do projeto é quente, e #FFF ao lado
     de #FBFAF8 lê como duas famílias. */
  background-color: color-mix(in srgb, var(--color-text) 93%, var(--color-surface));
  border-radius: var(--radius-sm);
  /* `transform` e não `translate`: o SOAP já usa `translate` na entrada, e as
     duas propriedades se compostam. Ver motion.css e o hover do .card--ponto. */
  transition:
    background-color var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out),
    transform var(--duration-base) var(--ease-out);
}
.logos__mark {
  /* Atenção: LARGURA FIXA, E NÃO `max-width` COM `max-height`.
     As imagens saem do ferramentas/marcas.py na MESMA tela de 300×190, com a
     área de tinta já equalizada. Essa normalização só sobrevive se a tela
     inteira for exibida no mesmo tamanho. O `max-height` fica só como trava
     para célula baixa. */
  width: 92%;
  height: auto;
  max-height: 84%;
  object-fit: contain;
  /* A contenção vem da saturação: a 88% a parede assenta um degrau abaixo do
     laranja da marca, e volta ao original no hover. */
  filter: saturate(0.88);
  transition: filter var(--duration-fast) var(--ease-out);
}
@media (hover: hover) and (pointer: fine) {
  .logos__item:hover {
    z-index: 1;
    background-color: var(--color-text);
    transform: translateY(-2px);
    box-shadow: var(--shadow-sm), 0 0 0 1px var(--color-border-accent);
  }
  .logos__item:hover .logos__mark { filter: saturate(1); }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
  .logos__item:hover { transform: none; }
}

.logos__item:first-child { grid-column: span 2; }
/* Na célula dupla a marca cresce, mas menos que a célula: 58% de duas colunas
   dá cerca de 1,4× o tamanho das outras, não 2×. É destaque, não outdoor. */
.logos__item:first-child .logos__mark { width: 60%; }

@media (min-width: 640px) {
  /* Cinco colunas fecham 35 em sete linhas. A célula dupla sai. */
  .logos {
    grid-template-columns: repeat(5, 1fr);
    gap: var(--space-3);
  }
  .logos__item {
    min-height: clamp(100px, 12vw, 118px);
    padding: var(--space-3);
  }
  .logos__item:first-child { grid-column: span 1; }
  .logos__item:first-child .logos__mark { width: 92%; }
}

@media (min-width: 1200px) {
  /* Sete colunas fecham 35 em cinco linhas, com célula larga o bastante para
     o Nelson e o Silva não voltarem a ser borrão. */
  .logos { grid-template-columns: repeat(7, 1fr); }
  .logos__item { min-height: clamp(108px, 7.2vw, 128px); }
}

/* 13 · SOBRE */
.sobre__inner { display: grid; gap: var(--space-10); align-items: start; }
.sobre__media,
.sobre__content { min-width: 0; }
.sobre__text {
  /* --space-4 no topo é o MESMO valor de `.section__lead`, que é o lead de
     todas as outras seções. O `.sobre__text` nunca teve o vão porque nasceu
     com classe própria, e o título encostava no primeiro parágrafo: era
     inconsistência do sistema, não escolha. Agora as duas famílias de lead
     respiram igual. */
  margin-top: var(--space-4);
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  /* Caiu de --space-8 para --space-6 em 31/08: deixou de ser o último
     parágrafo da coluna. Vão de fim de bloco entre dois parágrafos do mesmo
     bloco lê como corte. */
  margin-bottom: var(--space-6);
}

/* Retrato + placa. A foto é o objeto desta seção: o rosto de quem atende.
   A legenda era um bilhete laranja SOBRE o retrato, segundo acento, terço
   de baixo coberto, credencial espremida. Placa embaixo libera a cara e
   fala o mesmo idioma do gabinete do herói. */
.sobre__figure {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: 0;
}
.sobre__img {
  width: 100%;
  height: auto;
  aspect-ratio: 4 / 5;

  /* Atenção: ORÇAMENTO VERTICAL. Retrato 4/5 em largura total fica mais alto que a
     tela sempre que a tela é baixa:

       tela                   retrato  % da altura
       528×467 (seu teste)      607px         130%
       iPhone SE deitado        767px         205%
       iPhone 14 deitado        975px         250%

     O max-height corta em vez de deformar, porque o object-fit abaixo já é
     cover e o object-position é center top: o que sai é o rodapé da foto, o
     rosto do Binho fica. Em celular em pé nada muda, o retrato lá dá 53% a
     64% da tela e o cap nem é atingido. */
  max-height: 70svh;

  object-fit: cover;
  object-position: center top;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  box-shadow: var(--shadow-md);
}
.sobre__caption {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background:
    linear-gradient(var(--color-primary), var(--color-primary)) 0 0 / 100% 1px no-repeat,
    var(--color-surface-alt);
  font-size: var(--text-sm);
}
.sobre__caption strong { font-family: var(--font-display); font-weight: 800; }
.sobre__caption span {
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  font-weight: 400;
}
/* Cargo em 700 no branco do texto: nome, função, credencial. Três pesos,
   três papéis. Sem isso o cargo e a formação liam como a mesma linha. */
.sobre__caption strong + span {
  font-weight: 700;
  color: var(--color-text);
}
/* TERCEIRA LINHA DA LEGENDA: A CREDENCIAL.
   Cargo e formação são dois tipos de informação. No mesmo tamanho, na mesma
   cor e sem nada entre eles, o olho lê os dois como uma frase só quebrada em
   duas linhas. O fio custa 1px e resolve. `span + span` casa só o segundo:
   o primeiro filho da legenda é o <strong> do nome. */
.sobre__caption span + span {
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
  font-weight: 400;
  color: var(--color-text-muted);
}

/*
   BIOGRAFIA
   Dois parágrafos do mesmo tamanho e da mesma cor viram um bloco cinza único e
   o olho não sabe onde pousar. O primeiro é a promessa e fica no degrau `lg`;
   o segundo é o lastro e desce para o degrau base. A diferença de tamanho é o
   que diz qual dos dois é argumento e qual é prova, sem precisar de rótulo.
   */
.sobre__bio {
  color: var(--color-text-muted);
  margin-bottom: var(--space-8);
}

/*
   INVENTÁRIO · TRÊS FATOS CONFERÍVEIS
   Rótulo pequeno em cima, número grande embaixo: é a MESMA ordem do `.specs`
   do cartão de ponto. A página passa a ter um jeito só de mostrar ficha
   técnica, em vez de dois componentes com a mesma função e formas diferentes.

   Atenção: O NUMERAL NÃO É LARANJA, E ISSO É DECISÃO, NÃO ESQUECIMENTO.
   O acento desta página significa "clique aqui" e está preso na allowlist de
   `teste-frontend.py` (_ACENTO_LEGITIMO). Três numerais de enfeite em laranja
   passariam despercebidos na revisão e cobrariam exatamente a força que o
   botão do fim desta seção precisa ter. O número já é o maior tipo do bloco:
   ele não precisa de cor para ser visto primeiro.

   Atenção: O RÓTULO NÃO PODE QUEBRAR EM DUAS LINHAS, E É POR ISSO QUE OS TRÊS SÃO
   SUBSTANTIVOS DE UMA PALAVRA. Medido em 360px: a coluna dá 91px e "NO SHOPPING
   UNISUL" pedia 93px em caixa alta com --tracking-caps. Ele quebrava, empurrava
   o numeral do meio para baixo, e os três números deixavam de pousar na mesma
   linha, que é a única coisa que uma ficha de números precisa fazer. "Painéis",
   "Totem" e "Cidades" cabem com folga. A linha DE BAIXO pode quebrar à vontade:
   rodapé irregular não desalinha nada. O endereço do totem está na seção
   "pontos", que é onde ele se decide.

   auto-fit e não três colunas fixas: se um quarto fato entrar, ele se recoloca
   sozinho em vez de espremer os outros três. */
.sobre__fatos {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(5.5rem, 1fr));
  gap: var(--space-6) 0;
  margin: 0 0 var(--space-8);
  padding-top: var(--space-6);
  /* Fio de separação: acima é prosa, abaixo é ficha. O corte é o que autoriza
     o olho a parar de ler e começar a escanear. */
  border-top: 1px solid var(--color-border);
}
.sobre__fatos > div {
  position: relative;
  min-width: 0;
  padding-right: var(--space-4);
}
.sobre__fatos > div + div {
  padding-left: var(--space-5);
  padding-right: 0;
}
.sobre__fatos > div + div::before {
  content: "";
  position: absolute;
  left: 0; top: 0.15rem; bottom: 0.15rem;
  width: 1px;
  background: linear-gradient(
    180deg,
    transparent,
    color-mix(in srgb, var(--color-text) 26%, transparent) 48%,
    transparent
  );
}
.sobre__fatos dt {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--color-text-muted);
}
.sobre__fatos dd {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.fato__num {
  display: block;
  margin-bottom: var(--space-1);
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 800;
  /* line-height 1 e não --leading-tight: numeral sozinho não tem descendente
     nem ascendente para acomodar, e 1.12 abriria um vão fantasma sob o dígito
     que faria o rótulo de baixo parecer desalinhado dos três. */
  line-height: 1;
  letter-spacing: var(--tracking-title);
  color: var(--color-text);
}

/* 14 · FAQ */
/* DUAS COLUNAS COM PROVA AO LADO
   A seção era uma coluna estreita com seis acordeões empilhados: seis caixas
   iguais, uma embaixo da outra, sem nada para o olho pousar. É a seção mais
   alta e mais monótona da página, e é justamente onde a pessoa está tirando a
   última objeção antes de falar com a BG.

   A foto ao lado resolve duas coisas de uma vez: dá um ponto de descanso na
   composição e coloca a prova do produto (um painel real, na rua, com peça de
   anunciante no ar) do lado das respostas sobre o produto.

   Ordem no código = ordem de leitura: título, foto, perguntas. No desktop a
   grade recoloca a foto na coluna 1 ocupando as duas linhas, sem que o leitor
   de tela ouça a legenda antes do <h2>. */
.faq__inner { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-8); }
.faq__inner > * { min-inline-size: 0; }
.faq__head { margin-bottom: 0; }

.faq__media {
  margin: 0;
  min-width: 0;
}
.faq__img {
  display: block;
  width: 100%; height: auto;
  aspect-ratio: 3 / 2;
  /* Teto de altura pelo ORÇAMENTO VERTICAL, não por gosto. Sem ele, em
     iPhone SE deitado (667×375) a foto ficava com 409px, ou seja 109% da
     tela: a pessoa rolaria uma tela inteira só de imagem antes de chegar na
     primeira pergunta. Com 60svh ela ocupa 225px ali e continua inteira nas
     telas em pé. O `cover` corta em vez de deformar. */
  max-height: 60svh;
  object-fit: cover;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  /* A referência usa cartão branco com sombra macia. Aqui a superfície é
     escura, então quem separa a foto do fundo é a borda de 1px, não a sombra:
     sombra escura sobre fundo escuro é borrão. A elevação vem do token, em
     duas camadas, e só o suficiente para a foto pousar. */
  box-shadow: var(--shadow-md);
}
.faq__caption {
  margin: var(--space-3) 0 0;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.faq__list { display: grid; gap: var(--space-3); align-content: start; }
.faq__item {
  container-type: inline-size;
  border: 1px solid var(--color-border-accent);
  border-radius: var(--radius-md);
  background: var(--color-surface-alt);
  transition: border-color var(--duration-fast) var(--ease-out);
}
.faq__item[open],
.faq__item:focus-within { border-color: var(--color-primary); }
@media (hover: hover) and (pointer: fine) {
  /* Fechado, o único sinal de clique era o cursor e o +. A borda laranja no
     hover é o mesmo vocabulário dos cartões da página: isto responde. Aberto
     já está na cor cheia, então o hover não inventa um segundo estado.
     :focus-within acima e o par do teclado, fora deste media. */
  .faq__item:hover { border-color: var(--color-primary); }
}
.faq__q {
  display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center;
  overflow-wrap: anywhere;
  gap: min(var(--space-4), 3vw);
  min-height: 56px;
  padding: min(var(--space-4), 4vw) min(var(--space-5), 5vw);
  cursor: pointer;
  font-family: var(--font-display);
  font-size: var(--text-base); font-weight: 700;
  list-style: none;
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q::after {
  content: "+";
  flex-shrink: 0;
  font-size: var(--text-xl); font-weight: 400; line-height: 1;
  color: var(--color-primary);
  transition: transform var(--duration-base) var(--ease-out);
}
.faq__item[open] .faq__q::after { transform: rotate(45deg); }
.faq__a { padding: 0 min(var(--space-5), 5vw) var(--space-5); }
.faq__a p { margin: 0; color: var(--color-text-muted); }
@container (max-width: 16rem) {
  .faq__q { grid-template-columns: minmax(0, 1fr); }
  .faq__q::after { justify-self: end; }
}

/* 15 · DECISÃO E FORM */
.decisao__inner { display: grid; gap: var(--space-10); align-items: start; }
.decisao__text { font-size: var(--text-lg); color: var(--color-text-muted); }

/* OS TRÊS CONTATOS DIRETOS
   Eram três linhas soltas embaixo do texto, sem nada dizendo que duas delas
   são clicáveis e a do meio não. O trilho de 2px na esquerda resolve isso sem
   ícone e sem palavra: acende laranja onde há link, fica cinza no endereço.
   A regra de cima separa o bloco do argumento, que é outro assunto. */
.contato-direto {
  display: grid; gap: var(--space-5);
  margin-top: var(--space-8);
  padding-top: var(--space-8);
  border-top: 1px solid var(--color-border);
}
.contato-direto li {
  display: flex; flex-direction: column; gap: var(--space-1);
  padding-left: var(--space-4);
  border-left: 2px solid var(--color-border);
  transition: border-color var(--duration-base) var(--ease-out);
}
/* :has() falha inteiro se o navegador não conhece, e aí as três linhas ficam
   com o trilho cinza. Degradar para uniforme é degradar para o estado
   anterior, que já era aceitável: nada quebra, só se perde a distinção. */
.contato-direto li:has(a) {
  border-left-color: color-mix(in srgb, var(--color-primary) 38%, transparent);
}
.contato-direto li:focus-within { border-left-color: var(--color-primary); }
@media (hover: hover) and (pointer: fine) {
  .contato-direto li:hover { border-left-color: var(--color-primary); }
}
.contato-direto__label {
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-wide); text-transform: uppercase;
  color: var(--color-text-muted);
}
/* WhatsApp e Instagram são os dois links de conversão desta seção, e tinham
   30px de altura de alvo. Mesma régua do rodapé: 32px de piso, 44 no dedo. */
.contato-direto a {
  display: inline-flex; align-items: center; min-height: 32px;
  font-family: var(--font-display); font-size: var(--text-lg); font-weight: 700;
  color: var(--color-primary); text-decoration: none;
  overflow-wrap: anywhere;
}
@media (pointer: coarse) {
  .contato-direto a { min-height: 44px; }
}
@media (hover: hover) and (pointer: fine) {
  .contato-direto a:hover { text-decoration: underline; }
}

/* O CARTÃO DO FORMULÁRIO
   Antes: fundo chapado, borda de 1px, sombra média. Lê como caixa desenhada.
   Agora ele é um objeto sob a mesma luz da cena que corre atrás dele: a rampa
   curta clareia só o topo, o fio de luz marca a quina de cima, e a sombra
   longa afasta o cartão da página. Três camadas que dizem a mesma coisa, que
   é "isto está na frente", em vez de uma borda pedindo para ser notada.

   A rampa termina em 42%: gradiente de ponta a ponta pinta a caixa inteira de
   uma cor que não é token nenhum, e aí o cartão sai da paleta sem que ninguém
   perceba. Do meio para baixo é --color-surface-alt puro. */
.form-card {
  position: relative;
  padding: clamp(1rem, 3vw, 2rem);
  border: 1px solid var(--color-border-accent);
  border-radius: var(--radius-lg);
  background: linear-gradient(
    to bottom,
    var(--color-surface-raised) 0%,
    var(--color-surface-alt) 42%
  );
  box-shadow: var(--shadow-lg);
  /* prende o fio de luz dentro do raio. Não corta o anel de foco: o padding
     do cartão é 24px e o anel gasta 6px. */
  overflow: hidden;
}
.form-card::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in srgb, var(--color-primary) 52%, transparent) 50%,
    transparent 100%
  );
}

.form-card__title { font-size: var(--text-xl); font-weight: 800; letter-spacing: var(--tracking-sub); }
/* A linha embaixo do lead divide o cartão em cabeçalho e corpo. Sem ela o
   título, a promessa e o primeiro rótulo são três textos empilhados com pesos
   diferentes, e o olho não sabe onde o formulário começa. */
.form-card__lead {
  margin: var(--space-2) 0 var(--space-6);
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-muted); font-size: var(--text-sm);
}

/* O RITMO VINHA DE DOIS LUGARES E BRIGAVA CONSIGO MESMO.
   O espaço entre campos era `margin-bottom` no .field, e o espaço entre as
   colunas de uma linha era `gap` no .form__row. Duas fontes para a mesma
   medida: mexer numa desalinhava a outra. Agora o vão é do formulário, uma
   vez só, e o campo não tem opinião sobre o que vem depois dele. */
.form { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-5); }
.form .field { margin-bottom: 0; }
/* Rótulos, controles e erros compartilham suas linhas quando há duas colunas. */
.form__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--space-5); align-items: start; }

.field { display: flex; flex-direction: column; gap: var(--space-2); margin-bottom: var(--space-4); }
/* "Opcional" estava só no placeholder do campo de empresa, e placeholder some
   no primeiro caractere: a informação desaparecia justo para quem começou a
   digitar e ficou em dúvida se precisava terminar. No rótulo ela fica. */
.field__label { font-size: var(--text-sm); font-weight: 700; overflow-wrap: anywhere; }
/* Inline e não item de flex. Como flex, o rótulo virava uma caixa que crescia
   e empurrava o selo para a borda direita do campo, a meia tela de distância
   da palavra que ele qualifica. Selo longe do que qualifica não qualifica
   nada. `white-space: nowrap` para "opcional" nunca quebrar sozinho. */
.field__opc {
  margin-inline-start: var(--space-2);
  font-size: var(--text-xs); font-weight: 700;
  letter-spacing: var(--tracking-caps); text-transform: uppercase;
  white-space: nowrap;
  color: var(--color-text-muted);
}
.field__input {
  width: 100%; min-inline-size: 0; min-height: 48px;
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border-strong);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  /* --text-base é clamp(1rem, ...), então o piso É 16px e o iOS não dá zoom.
     O literal garantia a regra e sonegava a escala. */
  font: inherit; font-size: var(--text-base);
  /* A sombra interna de 1px é o que faz a caixa parecer FUNDA em vez de
     desenhada. Campo escuro sobre cartão escuro, sem ela, é um retângulo de
     borda; com ela o olho lê um poço, e poço pede para ser preenchido. */
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.28);
  transition:
    border-color var(--duration-fast) var(--ease-out),
    background-color var(--duration-fast) var(--ease-out);
}
.field__input::placeholder { color: var(--color-text-muted); }
/* Sem hover o campo só reage depois do clique, e até lá nada na tela confirma
   que ele é um controle. `:not(:focus)` porque o foco já tem cor própria e
   duas regras disputando a mesma borda piscam na transição. */
@media (hover: hover) and (pointer: fine) {
  .field__input:hover:not(:focus) { border-color: var(--color-text-muted); }
}
.field__input:focus { border-color: var(--color-primary); }
.field__input--area {
  min-height: 132px; resize: vertical;
  line-height: var(--leading-body);
}
.field__input[aria-invalid="true"] { border-color: var(--color-error); }
.field__error { margin: 0; font-size: var(--text-sm); color: var(--color-error); }

/* A SETA DO SELECT
   A nativa é desenhada pelo sistema e não obedece a paleta: no print do
   cliente ela aparece clara e fria no meio de um cartão quente e escuro.
   Aqui ela é um quadrado de 8px com duas bordas, girado 45°, na cor do texto
   secundário, e acende junto com a borda quando o campo recebe foco.

   Fica no .field e não no select porque select não aceita pseudo-elemento de
   forma confiável. `pointer-events: none` para o clique atravessar a seta e
   abrir a lista, que é o que a pessoa está tentando fazer. */
.field--select { display: grid; grid-template-columns: minmax(0, 1fr); }
.field--select .field__input {
  grid-area: 2 / 1;
  appearance: none; -webkit-appearance: none;
  padding-right: var(--space-10);
  /* "Ainda não sei, quero uma sugestão" não cabe na coluna estreita e o select
     corta no meio da palavra. Reticências dizem que continua; corte seco
     parece defeito de renderização. */
  text-overflow: ellipsis;
}
.field--select::after {
  content: "";
  grid-area: 2 / 1;
  place-self: center end;
  margin-inline-end: var(--space-4);
  width: 8px; height: 8px;
  border-right: 2px solid var(--color-text-muted);
  border-bottom: 2px solid var(--color-text-muted);
  rotate: 45deg;
  pointer-events: none;
  transition: border-color var(--duration-fast) var(--ease-out);
}
.field--select:focus-within::after { border-color: var(--color-primary); }

/* O AVISO ANTES DO BOTÃO
   Vinha como parágrafo solto e lia como letra miúda, que é exatamente o
   contrário do que ele é: a explicação de para onde o clique leva. A chapa
   de 4% o transforma em nota, custa nada e não mexe no contraste do texto,
   que continua em 6,7:1 sobre o cartão. */
.form__note {
  margin: 0;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--color-text) 4%, transparent);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
}
.form__status { margin: 0; font-size: var(--text-sm); }

/* Honeypot. Precisa existir no DOM para o bot preencher, e não pode ser visto
   nem alcançado por gente. O left: -9999px clássico empurra a caixa de layout
   para fora do documento; em LTR o navegador ignora isso para rolagem, mas em
   RTL vira estouro de verdade, e em qualquer direção suja a área rolável.
   clip-path recorta sem tirar o elemento do lugar: zero efeito no layout. */
.form__hp {
  position: absolute;
  /* EXCEÇÃO DOCUMENTADA: 1px e -1px são o idioma consagrado de esconder da
     vista sem esconder do leitor de tela. Não são medidas de espaçamento. */
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}
/* A nota subiu para ACIMA do botão e passou a dizer onde o clique leva. Por
   isso saiu de --text-xs (texto necessário não pode ser o menor da tela) e
   alinha à esquerda, na mesma borda dos campos. */
.form__note {
  margin: var(--space-5) 0 var(--space-3);
  font-size: var(--text-sm);
  line-height: 1.5;
  color: var(--color-text-muted);
  text-wrap: pretty;
}
.form__status {
  margin: var(--space-4) 0 0; padding: var(--space-4);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  background: var(--color-primary-soft);
  border: 1px solid var(--color-primary);
}

/* 16 · BREAKPOINTS
   Mobile-first. Tudo acima desta seção descreve 320px; daqui para baixo o
   layout só ganha espaço, nunca perde. Escala única: 480 / 640 / 900 / 1200 / 1600.
   */

/* ---- sm · 480px · celular grande ---- */
@media (min-width: 480px) {
  .hero__actions .btn { flex: 0 1 auto; }   /* botões param de ocupar a linha toda */
  .hero__facts { gap: var(--space-3) 0; }
  .hero__fact { padding-right: var(--space-6); }
  .hero__fact + .hero__fact { padding-left: var(--space-6); }
  .hero__fact strong {
    font-size: clamp(1.75rem, 1.1rem + 1.6vw, 2.5rem);
  }
}

/* ---- md · 640px · tablet em retrato ----
   Continua UMA coluna. Duas colunas nesta faixa espremeriam os quatro
   parágrafos em ~35ch e o totem viraria um filete. A composição própria
   é coluna editorial (teto 40rem) com o produto maior no centro. */
@media (min-width: 640px) {
  .hero__subtitle {
    margin-top: var(--space-5);
    font-size: var(--text-lg);
  }
  .hero__subtitle p + p { margin-top: var(--space-4); }
  .hero__subtitle p:nth-child(3) { margin-top: var(--space-6); }
  .hero__actions { margin-top: var(--space-8); }
  .hero__lead { display: block; }

  @media (max-width: 899px) {
    .hero__inner { justify-items: center; }
    .hero__content,
    .hero__facts {
      width: 100%;
      max-width: 40rem;
    }
    .hero__media { width: 100%; }
    .totem {
      --totem-w: min(clamp(260px, 56vw, 360px), 42svh);
      padding-block: var(--space-4) var(--space-3);
    }
  }

  .cards--3, .cards--4 { grid-template-columns: repeat(2, 1fr); }
  @supports (grid-template-rows: subgrid) {
    .card--outline, .card--step {
      display: grid;
      grid-template-rows: subgrid;
      row-gap: var(--space-3);
      align-content: start;
    }
    .card--outline { grid-row: span 2; }
    .card--step { grid-row: span 3; }
    .card--step .step__num { margin-bottom: var(--space-2); }
    .card--outline .card__text, .card--step .card__text { margin: 0; align-self: start; }
  }
  /* ÓRFÃO EM 2 COLUNAS. Três cartões (stakes) ou sete (pontos) deixam o
     último sozinho na coluna 1, com a metade direita vazia. Centrar fecha o
     buraco sem esticar o cartão: esticar diria "este pesa mais", e não pesa.
     `nth-child(odd)` só casa quando o último ABRE linha nova. Com 4 itens a
     regra desliga. Em 900px as 3 colunas ligam e esta faixa acaba. */
  @media (max-width: 899px) {
    .cards--3 > li:last-child:nth-child(odd),
    .cards--4 > li:last-child:nth-child(odd) {
      grid-column: 1 / -1;
      justify-self: center;
      width: calc((100% - var(--space-5)) / 2);
    }
  }
  /* Os dois prazos são um par: nunca viram três colunas, então a regra entra
     aqui e não volta a mudar em 900px. */
  .cards--2 { grid-template-columns: repeat(2, 1fr); }
  .form__row { grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr)); }
  @supports (grid-template-rows: subgrid) {
    .form__row { row-gap: var(--space-2); }
    .form__row > .field { display: grid; grid-row: span 3; grid-template-rows: subgrid; }
  }

  /* Havia aqui `.pontos__grid > li:last-child:nth-child(odd) { grid-column:
     span 1 }`, defendendo o sétimo cartão de "ficar esticado" em duas
     colunas. Item de grade não estica sozinho: `span 1` já é o padrão, então
     a regra nunca fez nada. Fazia uma coisa só, e ruim: com especificidade
     (0,3,1) ela vencia a regra de centralizar o órfão em três colunas, que
     tem (0,3,0), e o cartão continuava encostado na margem sem que ninguém
     entendesse por quê. Código defensivo contra um problema inexistente que
     acaba impedindo a correção do problema existente. */
}

/* ---- lg · 900px · tablet em paisagem e notebook ---- */
@media (min-width: 900px) {
  .site-nav__list > li {
    flex: 0 0 auto;
    max-inline-size: 18rem;
  }
  .site-nav__list a { max-inline-size: 100%; }
  .site-nav { display: block; max-inline-size: 100%; }
  /* A âncora do grupo da direita. É o que troca a distribuição acidental do
     space-between por uma decisão: todo o espaço que sobra vai para um único
     vão, entre a marca e a navegação. */
  .site-nav { margin-inline-start: auto; }
  /* Precisa casar a especificidade de html[data-js="on"] .nav-toggle (0,2,1),
     senão aquela regra vence esta mesmo vindo antes no arquivo, e o
     hambúrguer reaparece no desktop. */
  html[data-js="on"] .nav-toggle { display: none; }
  .header-cta { display: inline-flex; }

  /* A ação encosta na navegação, mas com respiro maior que o de entre links,
     senão o botão vira o sexto item do menu. --space-5 contra os --space-6
     internos da lista dá a proporção certa: perto o suficiente para o olho
     ler como um grupo, longe o suficiente para não virar mais um link. */
  .site-header__actions { gap: var(--space-5); margin-inline-start: 0; }
  .site-header__rule {
    display: block;
    width: 1px;
    height: 20px;
    background: var(--color-border-strong);
    opacity: 0.55;
  }

  /* A FAIXA APERTADA · 900 a 1199px.
     É onde a navegação inline liga e o container ainda é estreito. Somando
     marca, cinco links, filete e botão, o conteúdo pede ~910px e o container
     em 900px oferece 836. Faltavam 76px, e o resultado era link comprimido
     com o vão do meio zerado: o cabeçalho parecia lotado justamente na
     largura mais comum de notebook pequeno.

     Isto já acontecia antes de 15/08, e com 52px a mais, porque o alternador
     de tema ocupava esse espaço. Ficava escondido pelo overflow-x: clip do
     body, que saiu em 10/09. Sem esse recorte, o aperto desta faixa vira
     barra de rolagem no scan oficial.

     Duas devoluções, nesta ordem de prioridade:
       · o vão entre links cai de 24 para 16px  → +32px
       · a assinatura da marca some              → +43px
     Some 75px, e o vão do meio volta a existir. A assinatura é a primeira a
     sair porque é a informação menos necessária da barra: quem está no site
     já sabe que a BG faz LED, e ela continua no rodapé e no herói. */
  @media (max-width: 1199px) {
    .site-nav__list { gap: var(--space-4); }
    .brand__text small { display: none; }
  }

  /* No desktop o menu não é um painel: a lista volta a ser inline no
     cabeçalho. Tudo que constrói a folha mobile é desligado aqui, inclusive
     o véu, o rodapé com CTA, as setas e o escalonamento de entrada. */
  .site-nav.is-open { position: static; }
  .site-nav.is-open::after { content: none; }
  /* O painel não precisa de desfazimento nenhum: a folha inteira está dentro
     de @media (max-width: 899px) e nunca chega aqui. Era daqui que faltava
     desfazer o overflow, e é por isso que a barra de rolagem aparecia. */
  .site-nav__foot { display: none; }
  .site-nav.is-open .site-nav__list { flex-direction: row; gap: var(--space-6); }
  .site-nav.is-open .site-nav__list li + li a { border-top: 0; }
  .site-nav.is-open .site-nav__list a {
    display: inline-block; min-height: 0; padding: 0; animation: none;
    padding-block: var(--space-1);
    font-family: var(--font-body); font-size: var(--text-sm); font-weight: 700;
    color: var(--color-text-muted);
  }
  /* De volta ao sublinhado da nav desktop: posição, tamanho e cor do base. */
  .site-nav.is-open .site-nav__list a::after {
    position: absolute; inset-inline: 0; inset-block-start: auto;
    bottom: calc(var(--sublinhado) * -1);
    width: auto; height: var(--sublinhado);
    border: 0; border-radius: var(--sublinhado);
    background: var(--color-primary);
    transform: scaleX(0);
  }
  /* FAQ em duas colunas. A foto vai para a coluna 1 ocupando as duas linhas;
     título e perguntas empilham na coluna 2. `align-items: start` impede a
     foto de esticar para acompanhar a altura do acordeão: imagem esticada
     para preencher grade é o erro que faz layout parecer automático. */
  .faq__inner {
    grid-template-columns: 0.85fr 1.15fr;
    grid-template-rows: auto 1fr;
    column-gap: var(--space-12);
    row-gap: var(--space-6);
    align-items: start;
  }
  .faq__media { grid-column: 1; grid-row: 1 / span 2; }
  .faq__head,
  .faq__list { grid-column: 2; }

  .site-nav.is-open .site-nav__list a[aria-current="true"]::after { transform: scaleX(1); }
  @media (hover: hover) and (pointer: fine) {
    .site-nav.is-open .site-nav__list a:hover::after { transform: scaleX(1); }
    /* Desfaz o deslocamento do item mobile. Era `padding-inline-start: 0`,
       que deixou de significar coisa alguma quando o hover passou de padding
       para transform: sem esta troca, um `is-open` que sobrasse em tamanho de
       desktop faria o link do cabeçalho escorregar 8px no hover. */
    .site-nav.is-open .site-nav__list a:hover { color: var(--color-text); transform: none; }
  }

  .cards--3 { grid-template-columns: repeat(3, 1fr); }
  /* .cards--4 NÃO abre em 4 colunas aqui: ver o @media de 1150px, no fim
     desta seção, e o motivo escrito lá. Entre 640px e 1149px a grade fica em
     2 colunas, que é o que a regra do @media de 640px já faz. */

  /* ÓRFÃO DA ÚLTIMA LINHA. (regra escrita com `li` para casar a
     especificidade do resto do arquivo, não por acaso)
     Sete pontos em três colunas deixam o sétimo sozinho na coluna 1, com dois
     terços da linha vazios e o cartão encostado na margem esquerda. Centrar
     fecha o buraco sem inventar um cartão "destaque" nem esticar um cartão
     para três colunas, que diria "este é mais importante" sem ser verdade.

     A conta é geral, não um remendo para o número 7: só casa quando o último
     item ABRE linha nova (nth-child(3n+1) e :last-child ao mesmo tempo).
     Com 8 pontos ela desliga sozinha, com 10 ela volta a valer. */
  .pontos__grid > li:last-child:nth-child(3n + 1) { grid-column: 2; }

  /* Totem nas duas linhas da direita, prova sob o argumento. `start`
     alinha o gabinete com a manchete; `center` deixava um vão morto em
     volta do produto porque a coluna da esquerda é mais alta. */
  .hero__inner {
    grid-template-columns: minmax(0, 1.18fr) minmax(0, 0.82fr);
    grid-template-areas:
      "content media"
      "facts media";
    column-gap: var(--space-12);
    row-gap: var(--space-8);
    align-items: start;
  }
  .hero__media { align-self: start; }
  .hero {
    padding-block: var(--space-8) var(--section-y);
  }
  .hero__actions { margin-top: var(--space-6); }
  .hero__subtitle p:nth-child(3) { margin-top: var(--space-5); }
  /* Teto abaixo do --text-3xl: em 4rem a linha "por onde Tubarão passa"
     não cabe na coluna e deixa "passa" órfão. 3,35rem fecha a manchete
     em três linhas e a cidade inteira na terceira. */
  .hero__title {
    font-size: clamp(2.5rem, 1.8rem + 1.4vw, 3.35rem);
  }
  .sobre__inner  { grid-template-columns: 0.8fr 1.2fr; gap: var(--space-16); }
  .decisao__inner{ grid-template-columns: 1fr 1fr; gap: var(--space-16); }
  /* Toda a rolagem do formulário acontecia com o telefone fora da tela. Quem
     chega no último campo perdeu de vista o argumento e o WhatsApp, que é o
     caminho mais curto e o que a seção inteira defende. Com o bloco preso, os
     dois ficam à mão até o botão.
     min-height 760: preso e cortado é pior que solto. Em janela baixa a coluna
     não cabe inteira e a regra não entra.
     Nota para a linha "nenhum position: sticky vive dentro de <main>" lá em
     cima: agora vive este. `overflow-x: clip` no <section> não cria contexto
     de rolagem, então o sticky continua colando na janela. Verificado em
     1440x900 e 1280x800. */
  @media (min-height: 760px) {
    .decisao__pitch { position: sticky; top: calc(var(--header-real, var(--header-h)) + var(--space-8)); }
  }
}

/*
   Atenção: AS 4 COLUNAS COMEÇAM EM 1150px, NÃO EM 900px.
   Medido em 900px com o texto de 31/08: a coluna dá 200px, e descontado o
   padding de 24px de cada lado sobram 152px de texto, que em --text-sm são
   cerca de 20 caracteres por linha. "O / desenvolvimento / da arte está /
   incluso na / contratação.": uma palavra por linha, com rio branco no meio
   do parágrafo. Abaixo de ~45 caracteres o olho gasta mais tempo achando o
   começo da linha seguinte do que lendo.

   Não é o número de colunas que está errado: é que os quatro textos novos têm
   o dobro do comprimento dos antigos, e 4 colunas passaram a exigir mais
   largura de tela do que exigiam. Em 1150px a coluna dá ~265px, ou ~29
   caracteres, que ainda é estreito mas é texto de cartão e se lê.

   Entre 640px e 1149px ficam 2 colunas, que é o que o @media de 640px já faz.
   Se os textos voltarem a encurtar, este número volta para 900.
   */
@media (min-width: 1150px) {
  .cards--4 { grid-template-columns: repeat(4, 1fr); }
}

/*
   O FIO QUE LIGA OS QUATRO PASSOS
   Quatro cartões lado a lado são quatro cartões. O que dizia "isto é uma
   sequência, nesta ordem" eram só os numerais dentro dos círculos, e numeral
   é informação que precisa ser LIDA: quem escaneia a seção via quatro caixas
   iguais e seguia em frente. O fio passa na altura do centro dos círculos e
   transforma a leitura em uma varredura da esquerda para a direita, que é o
   gesto que a seção quer.

   Decorativo por completo: o <ol> já carrega a ordem para o leitor de tela e
   o `.step__num` é aria-hidden. Nada aqui muda o que é anunciado.

   O fio é --color-border-strong, não o laranja: o acento já está na moldura
   dos cartões e no anel dos numerais, e um terceiro traço laranja no vão
   entre eles empataria a ligação com a informação.

   Só existe em 4 colunas. Em 2 colunas ele apontaria para o fim da linha e
   morreria no ar depois do segundo cartão, e em 1 coluna não há vizinho ao
   lado para ligar. Por isso a regra vive no MESMO @media que cria as 4
   colunas: se um dos dois mudar de ponto de virada, o outro muda junto ou o
   fio aparece onde não há sequência para ligar.
   */

@media (min-width: 1150px) {
  .card--step { position: relative; }

  /* :not(:last-child) e não :last-child::before { display:none }: o fio nunca
     chega a existir no quarto cartão, em vez de existir e ser apagado. */
  .card--step:not(:last-child)::before {
    content: "";
    position: absolute;
    /* O bloco de posicionamento de um filho absoluto é a CAIXA DE PADDING do
       pai, então `left: 100%` já cai por dentro da borda de 1px. Os 2px de
       largura extra devolvem esse pixel de cada lado: o fio atravessa o vão
       inteiro e encosta nas duas bordas, sem sobra e sem falha. */
    left: 100%;
    width: calc(var(--space-5) + 2px);
    /* Centro do círculo: o padding do cartão mais metade de --step-num.
       Escrito como conta e não como resultado, para o fio acompanhar se
       algum dos dois mudar. */
    top: calc(var(--space-6) + var(--step-num) / 2);
    height: 1px;
    background: var(--color-border-strong);
  }
}

/* ---- xl · 1200px · desktop ----
   Nesta faixa o container já bateu no teto e o layout está no formato final.
   Só a mídia do hero ganha presença, porque a coluna cresceu. */
@media (min-width: 1200px) {
  .hero__inner {
    column-gap: var(--space-16);
    row-gap: var(--space-10);
  }
}

/* ---- xxl · 1600px · monitor grande e ultrawide ----
   Sem esta faixa, em 2560px sobravam 680px vazios de cada lado com o
   conteúdo espremido em 1200px. O container cresce, mas os blocos de texto
   mantêm o próprio max-width em rem: linha de leitura não acompanha a tela,
   senão a leitura piora em vez de melhorar. */
@media (min-width: 1600px) {
  :root {
    --container: 1360px;
    --section-y: clamp(4.5rem, 7vw, 8.5rem);
    /* A manchete do herói sobe junto, mas pelo TOKEN e não por uma regra de
       componente: assim ela continua sendo o degrau 3xl da escala em vez de
       virar um tamanho avulso que só existe acima de 1600px. */
    --text-3xl: clamp(3rem, 3.4vw, 4.4rem);
  }
}

@media (min-width: 2000px) {
  :root { --container: 1500px; }
  /* Trava de leitura: mesmo com o container maior, texto corrido não passa
     disso. Acima de ~75 caracteres por linha o olho perde a próxima linha. */
  .section__lead, .stakes__text, .sobre__text, .decisao__text { max-width: var(--medida); }
}

/* 17 · MOVIMENTO REDUZIDO */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  /* EXCEÇÃO DOCUMENTADA: 0.01ms não é uma duração da escala, é o idioma de
     desligar animação sem usar `animation: none`, que cancelaria o estado
     final de quem anima com `forwards` e deixaria elementos invisíveis. */
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  /* A faixa "Atendemos" tem bloco próprio de reduced-motion na seção 6,
     mais completo que este. Não repetir aqui. */
}

/* 18 · IMPRESSÃO */
@media print {
  .site-header, .marquee { display: none; }
  /* EXCEÇÃO DOCUMENTADA: papel é branco. O tema escuro da tela impresso
     gastaria a folha inteira de tinta para entregar contraste pior. */
  body { background: #fff; color: #000; }
}

/* motion.css estende os tokens de movimento. footer.css e assistant.css
   mantêm seus componentes escopados; o chatbot usa o z-index reservado. */

/* Atenção: AQUI HAVIA UM BORRÃO NAS BARRAS DO HAMBÚRGUER. ELE FAZIA O ÍCONE SUMIR.
   Portei o crossfade em `filter: blur(10px)` do circle menu do 21st.dev, onde
   ele acontece entre DOIS NÓS DE ÍCONE que se substituem. Aqui não há troca de
   nó: são três barras de 2px de altura que giram. Borrar 3px uma barra de 2px
   não desfoca a barra, APAGA a barra.

   O que aparecia na tela: ao tocar no menu, o ícone sumia por 160ms na
   abertura e por 280ms no fechamento, deixando a borda redonda vazia. Quem
   toca não conclui "está animando", conclui "o botão sumiu" e toca de novo,
   o que reabre e refecha o menu.

   Duas coisas saíram junto com o borrão:
     · o `filter`, que além de destrutivo era a única propriedade da página
       animada fora de transform e opacity;
     · o `transition` que eu tinha reescrito aqui. Ele listava `translate` e
       `rotate`, e por especificidade derrubava o `transform` da regra base
       (linha 843), que é a propriedade que o X realmente usa. As barras
       passaram a SALTAR para o X em vez de girar até ele.

   Do porte restou a sacudida do fechamento, logo abaixo: ela é aditiva, roda
   no botão e não encosta no ícone.

   Fechamento: o original sacode o gatilho e pulsa a escala enquanto os itens
   recolhem. Aqui é uma animação só, curta, e some sozinha. */
/* Atenção: A SACUDIDA DO FECHAMENTO FOI REMOVIDA, E ISSO É CORREÇÃO, NÃO CORTE.
   Era `nav-toggle-fecha`: quatro quadros-chave levando o botão a translate
   ±2px e scale 1.12. Dois problemas, e o segundo é o que decide.

   Primeiro: 2px de translate é literalmente o botão pulando de lugar durante
   a própria animação. Segundo, e pior: quem fecha o menu ACABOU de apertar o
   botão, e o `:active` já respondeu com um afundamento. A sacudida chegava
   logo depois, então um toque produzia dois movimentos seguidos no mesmo
   controle, em direções opostas. O olho lê isso como recuo, não como resposta.

   O que confirma o fechamento é o X virando hambúrguer, que já acontece e é o
   sinal certo: fala do estado do menu, não do botão. Com a sacudida fora,
   saíram junto uma classe, um temporizador de 280ms no main.js, um
   quadro-chave e uma exceção de movimento reduzido. */

/* MOVIMENTO REDUZIDO · só o que a seção 17 não alcança.
   Atenção: CORREÇÃO DE UMA AFIRMAÇÃO MINHA, ANTES DE ELA VIRAR VERDADE NO ARQUIVO.
   Eu tinha escrito aqui que quem pede menos movimento continuava recebendo o
   leque de -68deg a +68deg, porque o bloco antigo mirava em
   `.site-nav__list li` e a animação mudou para o <a> faz tempo. O seletor
   morto é real, mas a conclusão era falsa: a seção 17 zera `animation-duration`
   e `transition-duration` em `*` com !important, então o leque já não
   acontecia. O bloco antigo era código morto, não uma falha de acessibilidade.

   Sobrou o que duração não resolve, porque é ESTADO e não percurso: as duas
   propriedades abaixo aplicam de uma vez mesmo com a duração zerada, e aí um
   salto instantâneo de 8px é mais agressivo do que o deslize de 150ms que ele
   substitui. E a seção 17 evita `animation: none` de propósito, com motivo
   documentado lá, então nada aqui reintroduz isso.

   O toque continua respondendo: sem o afundamento, o botão troca de fundo. É
   feedback sem deslocamento, que é exatamente o que a preferência pede. */
@media (prefers-reduced-motion: reduce) {
  .site-nav.is-open .site-nav__list a:focus-visible { transform: none; }
  .nav-toggle:active {
    scale: 1;
    background: var(--color-primary-soft);
    border-color: var(--color-primary);
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
  .site-nav.is-open .site-nav__list a:hover { transform: none; }
}

/*
   MENU MOBILE · CABER E ACABAMENTO

   Atenção: EM TELA BAIXA O MENU NÃO CABIA, E ROLAR NÃO É RESPOSTA PARA SEIS ITENS.
   Cinco linhas de 56px mais o CTA mais o contato dão cerca de 444px. Num
   celular deitado sobram 334px abaixo do cabeçalho. A rolagem interna resolve
   o alcance, mas quem abre um menu de seis itens espera VER os seis.

   A saída é mudar a forma, não encolher a fonte: abaixo de 620px de altura a
   lista vira duas colunas. Cinco itens em duas colunas são três linhas, e o
   painel inteiro passa a caber com folga. A composição continua a mesma, o
   que muda é o arranjo, que é exatamente o que se espera de um layout
   responsivo de verdade.
   */
/* Atenção: AS DUAS COLUNAS PRECISAM DE LARGURA, E A REGRA SÓ PEDIA ALTURA.
   A condição era `(max-width: 899px) and (max-height: 620px)`, escrita para
   celular deitado. Só que 620px de altura também é a de um celular pequeno EM
   PÉ: um 320x568 caía aqui e recebia duas colunas dentro de 288px de área
   útil. Cada coluna pedia 126px mais o vão, o conteúdo media 334px contra 320
   do painel, e "Os pontos" e "Valores" saíam 13px pela direita. Não aparecia
   barra de rolagem porque o painel corta, então o defeito era silencioso: o
   fim das duas palavras simplesmente não estava na tela.

   `min-width: 480px` é o piso de celular deitado. Abaixo disso a coluna única
   é a resposta certa em qualquer altura, e cabe: em 320x568 o painel fecha em
   440px contra 499px de janela disponível. */
@media (max-width: 899px) and (max-height: 620px) and (min-width: 480px) {
  .site-nav.is-open .site-nav__list {
    display: grid;
    grid-template-columns: 1fr 1fr;
    column-gap: var(--space-4);
  }
  /* O fio separador some entre colunas e vira fio de linha: com border-top no
     `li + li` a segunda coluna ganharia um traço solto no topo. */
  .site-nav.is-open .site-nav__list li + li a { border-top: 0; }
  .site-nav.is-open .site-nav__list li a {
    border-bottom: 1px solid var(--color-border);
  }
  .site-nav.is-open .site-nav__list a { min-height: 46px; }
  .site-nav__foot { margin-top: var(--space-4); padding-top: var(--space-4); }
  .site-nav__contact { margin-top: var(--space-3); font-size: var(--text-xs); }
}

/* Barra de acento à esquerda da linha. Nasce em escala zero e cresce a partir
   do topo no foco, no hover e no item da seção atual. É o detalhe que faz a
   linha responder ao dedo em vez de só trocar de cor. */
.site-nav.is-open .site-nav__list a {
  position: relative;
  border-radius: var(--radius-sm);
}
.site-nav.is-open .site-nav__list a::before {
  content: "";
  position: absolute;
  inset-block: 18%;
  inset-inline-start: calc(var(--space-2) * -1);
  width: 2px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  scale: 1 0;
  transform-origin: top;
  transition: scale var(--duration-base) var(--ease-out);
}
.site-nav.is-open .site-nav__list a:focus-visible::before,
.site-nav.is-open .site-nav__list a:active::before,
.site-nav.is-open .site-nav__list a[aria-current="true"]::before { scale: 1 1; }
@media (hover: hover) and (pointer: fine) {
  .site-nav.is-open .site-nav__list a:hover::before { scale: 1 1; }
}

/* Atenção: NO DEDO NÃO EXISTE HOVER, E ERA SÓ ELE QUE DAVA RESPOSTA À LINHA.
   Quem toca cobre a linha inteira com o dedo, então trocar a cor do texto não
   é feedback: o texto está embaixo da mão. A chapa atrás da linha aparece nas
   bordas, que é a parte que continua visível, e some junto com o menu.
   Entre o toque e o painel fechar passam quase 200ms de animação de saída, e
   sem nada aqui esses 200ms parecem o site ignorando o toque. */
.site-nav.is-open .site-nav__list a:active {
  background: color-mix(in srgb, var(--color-text) 7%, transparent);
}
.site-nav__foot .btn:active { scale: 0.985; }

/* O contato do rodapé do painel deixa de ser uma linha solta e vira bloco,
   com o telefone como o elemento clicável que ele sempre deveria ter sido. */
.site-nav__contact {
  flex-wrap: wrap;
  line-height: 1.5;
}

@media (prefers-reduced-motion: reduce) {
  .site-nav.is-saindo .site-nav__panel,
  .site-nav.is-saindo .site-nav__list a,
  .site-nav.is-saindo .site-nav__foot { animation: none; }
  .site-nav.is-open .site-nav__list a::before { transition: none; }
}

/* 404. Pagina real de nao encontrado. Reusa tokens e .btn; sem splash. */
.lost {
  min-height: calc(100dvh - var(--header-h));
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-block: var(--section-y);
}
.lost__title {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--text-2xl);
  font-weight: 800;
  letter-spacing: var(--tracking-sub);
  line-height: var(--leading-tight);
}
.lost__lead {
  margin: var(--space-4) 0 0;
  font-size: var(--text-lg);
  color: var(--color-text-muted);
  max-width: var(--medida);
}
.lost__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-top: var(--space-8);
}
.lost__nap {
  margin: var(--space-10) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.lost__foot {
  border-top: 1px solid var(--color-border);
  padding-block: var(--space-6);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.lost__foot .container {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  justify-content: space-between;
}
body:has(.lost) .site-header__inner { flex-wrap: wrap; padding-block: var(--space-2); }
body:has(.lost) .brand { flex: 1 1 12rem; }
body:has(.lost) .site-header__actions { max-inline-size: 100%; }
/* Na home, a partir de 900px a margem auto sai das acoes e vai para a nav.
   A 404 nao tem nav: sem esta regra o WhatsApp cola na marca. */
@media (min-width: 900px) {
  body:has(.lost) .site-header__actions { margin-inline-start: auto; }
}
