/* Grupo Dls/ - páginas de frente (/comunicacao, /tecnologia, /performance)
 *
 * Uma folha para as três. O que muda de página para página é UMA custom
 * property - `--unidade` - e nada mais: amarelo em comunicação, azul em
 * tecnologia, lime em performance. Se algum dia uma frente precisar de
 * regra própria, ela é a exceção documentada, não o começo de três folhas.
 *
 * O que esta folha NÃO faz, de propósito:
 *
 *   Não redesenha o bloco de cases. Ele é o mesmo da home, com o mesmo
 *   HTML, o mesmo `scenes.css` e o mesmo `scenes/cases.js`. Uma cópia
 *   estilizada aqui seria a segunda versão de um componente que já existe.
 *   O que ela faz é devolver ao bloco o TEMA em que ele foi desenhado -
 *   ver `.frente-page .cases`, mais abaixo.
 *
 *   Não importa `case.css` para conseguir chão preto. Aquela folha tem
 *   53 KB de regras de página de case, e cada uma passaria a valer aqui
 *   para qualquer classe `cs*` que alguém escrevesse depois. O que a
 *   página usa é `.fr-escuro`, que é a troca de tema de `tokens.css`
 *   aplicada num escopo.
 *
 *   Não repete header, rodapé, botão, pílula, link nem entrada por
 *   rolagem. Tudo isso já mora em `layout.css` e em `header.css`.
 */

/* ============================================================
   O CHÃO
   ============================================================ */
/* `data-theme="light"` no html já trocou `--fg`, `--fg-muted` e `--line`
   por tokens.css. Aqui entra só o que é próprio da frente.

   Pintado na raiz E no corpo pelo mesmo motivo da página de case
   (armadilha 11 do HANDOFF): com o html transparente, o fundo do body é
   propagado para a tela e `getComputedStyle(body).backgroundColor` passa a
   devolver branco. Pintar os dois não muda um pixel e devolve a cor à
   medição - que é a diferença entre afirmar que o chão é `#f1f1f1` e
   conseguir provar. */
:root:has(> .frente-page),
.frente-page {
  background: var(--c-offwhite);
}

/* A cor da unidade da página, e o primeiro plano que senta SOBRE ela.
   São duas linhas, não uma, e a segunda nasceu de um defeito medido.

   `--unidade` sozinho bastava enquanto a única frente era amarela. Preto
   sobre #FFDD00 mede 15,4:1; preto sobre o lime #D3FF35 mede ~16:1. Trocar
   só a cor para o azul da Elleven entregava preto sobre #0153FF, que mede
   3,68:1 - o H2 da placa de fechamento passaria por ser texto grande, e
   `.placa__sub` REPROVARIA em AA. A chamada mais importante da página,
   ilegível.

   Daí o token companheiro. Ele responde "o que se lê em cima da cor da
   unidade", e a resposta muda com a cor: preto no amarelo e no lime,
   branco no azul (5,7:1). Uma linha por frente, e a /performance já nasce
   resolvida. */
.frente-page {
  --unidade: var(--c-yellow);
  --sobre-unidade: var(--c-black);
}

/* O anel de foco amarelo do sistema mede 1.07:1 sobre branco - invisível.
   Em página clara ele vira preto, com um segundo anel branco por fora para
   não sumir quando o alvo estiver sobre imagem. Mesma solução da página de
   case, e pelo mesmo motivo.

   AS DUAS CORES VIRARAM TOKEN pelo mesmo motivo que `--sobre-unidade`: a
   /tecnologia tem chão preto, e anel preto em chão preto some. E como ela
   tem seções claras E escuras, o anel não pode ser resolvido no nível da
   página - ele segue a seção. Escrito assim, a /comunicacao ganha junto o
   anel certo dentro da faixa de cases e do fechamento, que são pretos e
   onde ele vinha desenhando linha preta com halo branco por fora. */
.frente-page {
  --foco-linha: var(--c-black);
  --foco-halo: var(--c-white);
}

.fr-escuro,
.fr-fechamento,
.frente-page .cases {
  --foco-linha: var(--c-white);
  --foco-halo: var(--c-black);
}

.frente-page :focus-visible {
  outline: 2px solid var(--foco-linha);
  outline-offset: 3px;
  box-shadow: 0 0 0 5px var(--foco-halo);
}

/* Sobre chão claro o header se subtrai sozinho: branco sobre #f1f1f1
   devolve #0e0e0e, e o mesmo header volta a branco quando o bloco preto da
   conexão, a faixa de cases ou o fechamento passam por baixo. Nada a
   coreografar - é a mesma decisão da página de case. */
.frente-page .header {
  background: none;
  mix-blend-mode: difference;
}

/* O rodapé vem com o fechamento, e não com o corpo claro: emendar preto
   com preto tira a costura, e o botão do rodapé é branco com texto preto -
   sobre chão claro ele desapareceria. */
/* A REGRA DO RODAPÉ ESCURO SUBIU PARA `layout.css` (25/08), junto com as
   das outras três páginas de chão claro. Eram quatro cópias da mesma
   decisão e duas estavam incompletas - o porquê está lá. */

/* Toda âncora de serviço precisa parar ABAIXO do header fixo. Declarado
   uma vez, no alvo, e não somado à mão em cada `scrollTo`. */
.frente-page :target,
.fr-servico {
  scroll-margin-top: clamp(88px, 12vh, 132px);
}

/* ============================================================
   HERO
   ============================================================ */
/* A composição é o KV 3 (`source/kv/3.jpg`), que é o mesmo arquivo que
   chegou como `assets/img/comunicacao/exemplo.png`: texto em caixa alta no
   quadrante superior esquerdo, as duas mãos entrando pelas bordas na meia
   altura, e o squircle com os anéis concêntricos no centro.

   Duas camadas: o palco cobre o hero inteiro e o texto flutua por cima.
   O palco cobrir TUDO é o que permite ao objeto ter a escala que tem sem
   ser cortado - preso à metade de baixo, ele não cabia. */
.fr-hero {
  position: relative;
  min-height: 100dvh;
  display: grid;
  /* Texto em cima, palco no que sobra. A linha de baixo é a correção do
     defeito do subtítulo por cima da mão: com o palco em `position:
     absolute` cobrindo o hero inteiro, o objeto era posicionado por uma
     porcentagem da ALTURA DA TELA, que não sabe onde o texto termina. Numa
     janela de 820px o texto ocupava 65% dela e a mão feminina começava
     66px acima do botão.

     Como linha de grade, o topo do palco É o fim do texto. A relação
     deixa de depender de um número afinado à mão para cada altura de
     janela. */
  grid-template-rows: auto 1fr;
  /* Encurtado de `clamp(120px, 18vh, 200px)`. Cada pixel aqui é um pixel a
     menos para a composição lá embaixo, e era o excesso deste respiro que
     empurrava o subtítulo para dentro da mão feminina. */
  padding-top: clamp(112px, 14vh, 156px);
  padding-bottom: clamp(var(--sp-6), 5vh, var(--sp-8));
  /* Corta só o que TEM que sair pela borda: os braços, na horizontal.
     Nada mais é cortado, e isso é por construção e não por sorte - tudo
     no palco é medido a partir de `--mao-w`, e o palco tem a altura de
     que a mão precisa. Medido em 1920, 1440, 1280, 1024 e 375: zero
     pixels de mão ou de anel fora do hero, em repouso e nos dois
     extremos do movimento. */
  overflow: clip;
}

.fr-hero__texto {
  position: relative;
  z-index: var(--z-raised);
}

/* Justificado como no KV, em TRÊS linhas - é isso que faz o bloco ler como
   o key visual e não como uma parede.

   Escala própria, menor que `--fs-display-xl`. O H1 da home tem 46
   caracteres e cabe em três linhas a 76px; este tem 63, e na mesma escala
   saía em CINCO - 570px só de manchete, que empurrava o CTA para fora da
   primeira dobra em 1280x720. Medido no navegador, não estimado.

   A largura é PERCENTUAL e não `ch`: justificação depende da largura da
   caixa, não da contagem de caracteres - e `ch` num container resolve
   contra a fonte do container, que é a armadilha 9 do HANDOFF e já custou
   três defeitos nesta base. */
.fr-hero__titulo {
  font-size: clamp(1.9rem, 0.9rem + 2.9vw, 3.5rem);
  max-width: min(64%, 900px);
}

.fr-hero__sub {
  max-width: 46ch; /* no próprio elemento que carrega a fonte do corpo */
  margin-top: clamp(var(--sp-5), 4vh, var(--sp-7));
  color: var(--fg-muted);
}

.fr-hero__acoes {
  margin-top: clamp(var(--sp-6), 4vh, var(--sp-7));
}

/* ---------- o palco ----------
   A linha de baixo da grade do hero: começa onde o texto acaba.
   `pointer-events: none` porque nada aqui é clicável.

   `--core-size` é o conjunto dos quatro anéis, e `--core-squircle` a
   fração dele que o núcleo ocupa. `0.26` é o valor da home
   (`scenes.css`): a proporção entre squircle e órbita é do objeto, e
   objeto que atravessa o site não pode ter uma proporção por página.

   O fator `0.62` de `--core-size`, esse é daqui - na home o conjunto é
   medido contra a janela (`min(76vh, 88vw)`), porque lá ele é a cena
   inteira. Aqui ele é medido contra a MÃO, que é quem manda no
   enquadramento. Era `0.66`, e desceu para `0.62`: o objeto encolheu
   ~10% em pixels enquanto a mão encolheu ~4%. Os dois números são
   diferentes de propósito, e a razão está na nota do `--vao` logo
   abaixo - encolher a mão empurra a ponta do dedo PARA FORA do objeto, e
   encolher o objeto na mesma proporção a empurraria para fora dele de
   vez.

   TUDO AQUI SAI DE `--mao-w`, e é isso que faz o conjunto ser à prova de
   altura de janela. Antes o anel era medido em `vh` e o objeto em `%` do
   hero: duas réguas que não sabiam onde o texto acabava nem qual era o
   tamanho da mão, e por isso precisavam ser afinadas à mão para cada
   janela - foi assim que o subtítulo foi parar por cima da foto.

   `--respiro` é a distância entre o FIM DO TEXTO e o topo da caixa da mão
   feminina, e ele deixou de ser um número fixo. Ver a nota do
   `--subida`.

   `--centro` é a altura de referência do gesto: a mão feminina é postada
   a `0.1243` da largura acima dela, que é exatamente onde a ponta do dedo
   dela cai abaixo (0.157 da largura, o valor do KV). `--foco`, esse é
   onde o objeto mora.

   `min-height` é onde a mão feminina TERMINA. Se não couber na dobra, o
   hero cresce além de 100dvh - o que custa um pouco de rolagem, e é
   barato perto de cortar um braço no meio ou deitar uma foto por cima de
   um parágrafo. Nenhuma das duas coisas acontece mais.

   0.5475 é a proporção da foto, medida NO ARQUIVO: 473 / 864. Estava
   escrito 0.518 aqui, que é 473 / 913 - e 913 era o que os atributos
   `width` do HTML diziam, não o que o PNG tem. Todos os fatores desta
   composição saíam desse número errado, e por isso erravam entre 1,5% e
   2% da largura da mão. */
.fr-hero__palco {
  --core-squircle: 0.26;
  /* O vão entre as duas pontas de dedo.
     Era `clamp(150px, 19vw, 380px)`.

     O VÃO É A ÚNICA ALAVANCA DE TAMANHO DA MÃO, e ela tem um preço que
     precisa estar escrito aqui. A mão é uma foto rígida: fixadas a borda
     da tela de um lado e a ponta do dedo do outro, a largura dela não é
     escolhida, é o que sobra entre as duas. Encolher a mão sem soltar a
     borda é, portanto, ABRIR o vão - e abrir o vão afasta a ponta do dedo
     do centro, ou seja, do objeto.

     Por isso os dois pedidos desta rodada - mão menor E objeto menor -
     não podem ser o mesmo número. Se os dois encolhessem 10%, o dedo
     sairia do conjunto de anéis: ele fica onde o vão o deixa, e o vão
     cresce quando a mão diminui. Medido em fração do lado do conjunto,
     contando da borda dele para dentro, é onde a ponta do dedo pousa:

       antes (19vw, fator 0.66)   0.174  - entre o 2º e o 3º anel
       agora (22.5vw, fator 0.62) 0.070  - entre o 1º e o 2º anel
       se o objeto encolhesse 4%  0.041  - raspando o anel externo

     0.070 é o compromisso: mão ~4% menor, objeto ~10% menor, e o dedo
     ainda pousa DENTRO do conjunto. No KV (`exemplo.png`) ele pousa em
     0.34, quase encostado no squircle - esta página nunca esteve lá,
     porque aqui o objeto divide a tela com um texto. */
  --vao: clamp(178px, 22.5vw, 450px);
  /* A mão vai da borda da tela até a beira do vão, e é essa conta - e não
     um `clamp` de largura - que a faz sangrar em qualquer janela. Era
     `clamp(300px, 46vw, 620px)`, que a 1920 deixava 196px de chão entre o
     cotovelo e a borda: o braço acabava no ar.

     O 1.09 é a FOLGA DO MOVIMENTO, e ela existe por um defeito medido: na
     conta exata, a mão encostava na borda parada e se DESCOLAVA dela ao
     andar - o gesto da rolagem puxa a mão para dentro, e os pixels que
     entravam eram o corte diagonal do antebraço aparecendo no meio da
     tela. Com 9% a mais de imagem para fora do quadro, a borda continua
     fora dele durante os 5% de aproximação. O 1.09 NÃO MUDOU nesta
     rodada, e é por isso que a margem de sangria continua a mesma: ela é
     uma fração da mão, e encolhe junto com ela.

     A caixa cresce PARA FORA: `.fr-mao--esq` está ancorada pela direita e
     `.fr-mao--dir` pela esquerda, então a folga não move a ponta do dedo. */
  --mao-w: max(300px, calc((50vw - var(--vao) / 2) * 1.09));
  --core-size: calc(0.62 * var(--mao-w));
  /* Quanto o topo VISÍVEL do anel externo ainda pode subir acima do fim
     do bloco de texto.

     O palco começa onde o texto acaba, mas o que acaba ali é o BOTÃO, e o
     botão mora na esquerda: entre o fim do parágrafo e o fim do bloco há
     73 a 84px de altura que, do meio da tela para a direita, estão
     vazios. Subir 40px dentro dessa faixa é subir ao lado do botão. Subir
     mais, como o objeto subia, é entrar no parágrafo - era 96px acima do
     fim do texto a 1280, e o anel cruzava a caixa do `<p>` por 33px a
     1920 e por 8,9px a 375.

     Este é o único número desta composição que se afina a olho. Todo o
     resto sai dele:

       respiro = topo do anel - subida

     e o topo do anel, contado do palco, é `0.48 x conjunto - 0.1738 x
     mão` - 0.48 porque o anel externo tem 2% de recuo no `viewBox`, e
     0.1738 porque é a soma de `--centro` (0.1243) com `--foco` (0.0495).
     Escrito assim, mudar o fator do conjunto ou a largura da mão reajusta
     o respiro sozinho, em vez de reabrir o defeito.

     O piso de 20px é para a janela baixa e estreita, onde a conta daria
     menos que isso e a mão passaria a encostar no botão. */
  --subida: 40px;
  --respiro: max(
    20px,
    calc(0.48 * var(--core-size) - 0.1738 * var(--mao-w) - var(--subida))
  );
  --centro: calc(0.1243 * var(--mao-w) + var(--respiro));
  /* Onde o SQUIRCLE mora, que não é onde a mão é ancorada.

     `--centro` é a referência das duas caixas, e as duas pontas de dedo
     caem longe dela: a feminina 0.157 da largura ABAIXO, a masculina
     0.058 ACIMA - a diagonal da Criação de Adão, e ela é para ficar.
     Postado em `--centro`, o objeto ficaria 0.0495 da largura acima do
     meio das duas pontas. Pouco de medir e muito de ver, porque o olho
     mede o objeto contra as duas pontas e não contra outra coisa.

     Somada metade da distância entre elas, as duas ficam a 0.1075 da
     largura do núcleo, uma de cada lado. Simétrico por construção, em
     qualquer janela - e a expansão dos anéis, que é concêntrica ao
     núcleo, abre do mesmo ponto. */
  --foco: calc(var(--centro) + 0.0495 * var(--mao-w));
  position: relative;
  min-height: calc(var(--respiro) + 0.5475 * var(--mao-w));
  pointer-events: none;
}

/* ---------- os anéis ----------
   Os quatro retângulos são os da home, copiados coordenada por coordenada
   de `#core-rings`. Eram quatro `div` com porcentagens próprias - um
   desenho PARECIDO com o da home, que é pior do que um diferente: o objeto
   atravessa o site e precisa ser reconhecível.

   Centralizado por `inset: 0` + `margin: auto`, e nunca por `translate`.
   Armadilha 12 do HANDOFF: o GSAP anima a escala destes anéis e, ao
   assumir o elemento, puxaria um `translate` do CSS para dentro da própria
   matriz e o zeraria no inline - o conjunto saltaria para o canto no
   primeiro quadro. Margem automática não é transformação, e por isso
   convive com o GSAP sem disputa. */
.fr-aneis {
  position: absolute;
  width: var(--core-size);
  aspect-ratio: 1;
  left: calc(50% - var(--core-size) / 2);
  top: calc(var(--foco) - var(--core-size) / 2);
  overflow: visible;
  color: var(--fg);
}

/* Traço fino de propósito, e cinza sempre. Os anéis são a órbita do
   objeto, não um segundo objeto: com peso a mais eles passam a competir
   com o squircle, que é quem carrega a cor. Os três valores são os da
   home. */
.fr-aneis rect {
  fill: none;
  stroke: currentColor;
  stroke-width: 0.82;
  opacity: 0.2;
  /* SEM `transform-box: fill-box`, e a ausência é a correção de um
     defeito medido: a expansão dos anéis não era concêntrica.

     O GSAP transforma SVG escrevendo o ATRIBUTO `transform` com a origem
     já embutida na matriz, e zerando a origem do CSS (`transform-origin:
     0 0`) para não contá-la duas vezes. Isso só fecha com a origem
     PADRÃO do SVG, que é o `view-box`: `0 0` tem de ser o canto do
     viewBox. Com `fill-box`, `0 0` passa a ser o canto da caixa DE CADA
     `rect` - quatro origens diferentes -, e o navegador aplica de novo um
     deslocamento que o GSAP já tinha resolvido.

     Medido a 1280, com os anéis em `scale(1.12)`: os quatro centros
     escorregavam 0,9 / 5,4 / 9,9 / 13,4px para cima e para a esquerda -
     quanto menor o anel, mais ele fugia. O conjunto não inflava a partir
     do squircle, deslizava na diagonal enquanto crescia. Removidas as
     duas declarações, o desvio dos quatro é zero em qualquer escala. */
}

/* ---------- o núcleo ----------
   Chapado na cor da unidade, que é o que a home mostra durante a frente de
   comunicação: lá o gradiente só floresce na convergência, quando as três
   unidades viram uma. Numa página que é de UMA frente, o objeto carrega a
   cor daquela frente e nada mais. Em /tecnologia ele nasce azul e em
   /performance lime, sem uma linha nova - `--unidade` já resolve.

   Sem sombra e sem halo de luz. O facho colorido que existia aqui morreu
   junto: ele era um gradiente com outro nome, e este squircle não tem
   aura. A profundidade do conjunto é dos quatro anéis. */
.fr-nucleo {
  position: absolute;
  width: calc(var(--core-size) * var(--core-squircle));
  aspect-ratio: 1;
  left: calc(50% - var(--core-size) * var(--core-squircle) / 2);
  top: calc(var(--foco) - var(--core-size) * var(--core-squircle) / 2);
  border-radius: var(--r-squircle);
  background: var(--unidade);
}

/* ---------- as mãos ----------
   Entram pelas bordas e param. Nunca se tocam: quem toca é cada mão no
   objeto, e o vão entre elas é onde o núcleo mora.

   DUAS CAIXAS, e o motivo é a armadilha 12 do HANDOFF: quando o GSAP assume
   um elemento, ele puxa `translate`, `rotate` e `scale` do CSS para dentro
   da própria matriz e zera as três no inline. Foi assim que sumiu o
   `translate: -50% -50%` que centralizava o botão de hover dos cases.

   Então a de fora POSICIONA e a de dentro ANDA. O JS toca só na de dentro,
   e nunca disputa uma propriedade com a folha de estilo. Sem JS, a de
   dentro simplesmente não se move e a mão já está no lugar certo. */
/* ---------- onde cada mão fica ----------
   Ancorada pela PONTA DO DEDO, e não pela borda da caixa. A diferença
   importa porque as duas fotos têm proporção e enquadramento diferentes:
   posicionar as duas por `left`/`top` iguais deixa uma ponta 90px abaixo da
   outra, e o gesto - duas mãos mirando o mesmo objeto - se desfaz.

   Os quatro números abaixo foram MEDIDOS no canal alfa dos próprios PNGs,
   varrendo coluna a coluna até a primeira que tem pixel opaco:

     mao-feminina  864x473  ponta em x 99.9%, y 51.4%   (altura = 0.5475 x largura)
     mao-masculina 880x650  ponta em x  0.0%, y 76.6%   (altura = 0.7386 x largura)

   AS DUAS PRIMEIRAS COLUNAS ESTAVAM ERRADAS, e é uma correção desta
   rodada. Estava escrito `913x473` e `913x650`, que é o que os atributos
   `width` do HTML diziam - os arquivos têm 864 e 880. A proporção saía de
   913, então os quatro fatores abaixo erravam, e com eles a posição
   vertical das duas mãos: a ponta feminina caía 0.0145 da largura abaixo
   de onde devia e a masculina 0.0198 abaixo. O objeto, postado no meio
   das duas pelo `--foco`, ficava 0.0172 da largura acima do meio real -
   15px medidos a 1920. Os atributos do HTML também foram corrigidos.

   Daí sai o deslocamento vertical de cada caixa em relação ao CENTRO do
   palco, que é onde o núcleo mora:

     esquerda   0.514 x 0.5475 = 0.2813 da largura, do topo da caixa até a ponta
     direita    0.766 x 0.7386 = 0.5659 da largura, do topo da caixa até a ponta

   E as pontas não miram o mesmo ponto: no KV a mão masculina chega por
   cima e a feminina por baixo - é a diagonal da Criação de Adão. Os
   afastamentos (0.058 acima e 0.157 abaixo, em fração da largura da mão)
   são os do KV, medidos na imagem.

   Se um dia trocar o arquivo de uma das mãos, é esta tabela que muda. */
.fr-mao {
  position: absolute;
  width: var(--mao-w);
  line-height: 0;
}

.fr-mao img {
  display: block;
  width: 100%;
  height: auto;
  filter: grayscale(1) contrast(1.04);
}

/* Ancorada pela DIREITA: é a borda onde mora a ponta do dedo. */
.fr-mao--esq {
  right: calc(50% + var(--vao) / 2);
  top: calc(var(--centro) - 0.1243 * var(--mao-w));
}

/* Ancorada pela ESQUERDA, pelo mesmo motivo.

   A mão masculina é mais alta que a feminina (0.7386 contra 0.5475 da
   largura) e sobe acima do palco, entrando na faixa do texto. Isso é
   inofensivo, e foi medido: nos 12% da esquerda da foto, acima de 55% da
   altura, o canal alfa é ZERO. O que passa por trás da manchete é
   transparente - o braço só começa a existir bem à direita dela. */
.fr-mao--dir {
  left: calc(50% + var(--vao) / 2);
  top: calc(var(--centro) - 0.6239 * var(--mao-w));
}

.js .fr-mao img {
  will-change: transform;
}

/* ============================================================
   A FAIXA DE NÚMEROS
   ============================================================ */
/* A frase que estava no fim do subtítulo do hero. Chão claro, emendando
   com o hero: a `.prova` da home é preta, e uma inversão a um palmo da
   primeira dobra cortaria a página em dois antes do primeiro argumento. */
.fr-numeros {
  padding-block: clamp(var(--sp-7), 8vh, var(--sp-9));
  border-top: 1px solid var(--line);
}

.fr-numeros__grade {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sp-7) var(--gutter);
  margin: 0;
}

.fr-numeros__item {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

/* `dd` antes de `dt` na tela e depois no documento: o algarismo é o que
   para o olho, a legenda é o que o explica. A ordem do documento é a que
   um leitor de tela precisa - termo, depois definição. */
.fr-numeros__item dd {
  order: 1;
  margin: 0;
}

.fr-numeros__item dt {
  order: 2;
  font-size: var(--fs-small);
  max-width: 22ch;
}

/* O terceiro não é algarismo, é o nome do método - na escala do numeral
   ele viraria uma parede de duas linhas. Uma escala abaixo, e continua
   sendo a mesma fileira. */
.fr-numeros__item--nome dd {
  font-size: var(--fs-display-m);
  line-height: var(--lh-display);
}

/* ============================================================
   CONEXÃO
   ============================================================ */
/* O bloco preto. Conexão e mantra dividem o mesmo chão: são um argumento
   só, em dois tempos - a conexão diz o que está errado, o mantra diz em
   que ordem se resolve.

   `.fr-escuro` repete cinco declarações que `.cs--escuro` de `case.css`
   também faz, e a repetição é deliberada. Importar `case.css` inteiro
   traria 53 KB de folha de página de case para uma página que não é uma -
   e cada regra dela passaria a valer aqui, silenciosamente, para qualquer
   classe `cs*` que alguém escrevesse depois. Isto aqui não é uma função
   duplicada: é a troca de tema que `tokens.css` já define, aplicada num
   escopo. A fonte de verdade continua sendo o token. */
.fr-escuro {
  background: var(--c-black);
  color: var(--c-white);
  --fg: var(--c-white);
  --fg-muted: var(--c-gray-on-dark);
  --line: var(--c-line-dark);
}

.fr-conexao {
  padding-block: clamp(var(--sp-9), 14vh, var(--sp-11));
}

.fr-conexao__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(var(--sp-7), 6vw, var(--sp-9));
  align-items: start;
}

/* MEDIDA PRÓPRIA, E NÃO A COLUNA INTEIRA (25/08, pedido do cliente). A
   coluna é `1fr` de uma grade de dois, e a 1497px isso dava 614px de
   texto a 20px - cerca de 60 caracteres por linha, terminando a 89px da
   borda da janela. Com 52ch a caixa fecha em 545px: a linha cai para ~53
   caracteres e o respiro à direita passa de 89px para ~158px, que é o
   que o pedido descreve ("um tiquinho").

   Em tela estreita o `max-width` não cobra nada: a coluna já nasce menor
   que 52ch antes de a grade virar uma coluna só, aos 1024px.

   Vale para as três frentes, que dividem esta seção - e /comunicacao
   pediu o mesmo ajuste no mesmo documento. */
.fr-conexao__textos {
  display: grid;
  gap: var(--sp-5);
  max-width: 52ch;
  color: var(--fg-muted);
}

/* ============================================================
   O MANTRA
   ============================================================ */
/* As três frases do cliente, uma de cada vez, saindo de fora de foco.

   A altura extra é a distância de rolagem que a sequência precisa, e ela
   só existe quando o módulo entra no ar: `mantra.js` põe `is-cinema`. Sem
   JavaScript, ou com movimento reduzido, a seção tem a altura do próprio
   palco e as três frases já estão nítidas - o mesmo texto, sem a
   encenação. */
.fr-mantra {
  padding: 0;
}

/* 260dvh dá 160vh de rolagem depois que o palco gruda, e a sequência tem
   3,3 tempos: ~48vh por frase. É o suficiente para cada uma sair do
   desfoque, ser lida e ficar - e curto o bastante para não virar corredor.
   A 300dvh a seção sozinha valia três telas das quinze da página. */
.fr-mantra.is-cinema {
  height: 260dvh;
}

/* `position: sticky` nativo, e não pin. Sticky não cria `.pin-spacer` -
   que é a origem da rolagem horizontal medida na home quando a janela
   encolhe -, não custa uma ScrollTrigger de pin, e cai sozinho para
   documento comum quando não há JavaScript. */
.fr-mantra__palco {
  position: sticky;
  top: 0;
  min-height: 100dvh;
  display: grid;
  align-content: center;
  /* Centralizadas, e nos dois eixos. Empilhadas na mesma celula, uma
     frase que entra pela esquerda e outra com metade do comprimento
     fariam a sequencia pular de largura a cada troca; centradas, as tres
     giram sobre o mesmo eixo e o que muda entre elas e so o texto. */
  justify-items: center;
  text-align: center;
  gap: clamp(var(--sp-4), 3vh, var(--sp-6));
  padding: clamp(var(--sp-8), 12vh, var(--sp-10)) var(--margin);
}

/* UMA DE CADA VEZ, e por isso as três dividem a mesma célula: cada frase
   entra, é lida, sai de foco, e a próxima chega no lugar exato onde a
   anterior estava. Empilhadas em coluna, "uma de cada vez" deixaria dois
   buracos na tela em todo instante.

   Só com `is-cinema`. Sem JavaScript as três precisam ser lidas juntas, e
   sobrepostas elas seriam três frases em cima uma da outra: a regra de
   empilhar mora no mesmo lugar que a encenação. */
.fr-mantra.is-cinema .fr-mantra__palco {
  align-content: center;
  gap: 0;
}

.fr-mantra.is-cinema .fr-mantra__linha {
  grid-area: 1 / 1;
}

.fr-mantra__linha {
  font-size: var(--fs-display-l);
  line-height: var(--lh-display);
  font-weight: 300;
  max-width: 20ch;
}

/* O desfoque não é enfeite: é o que a página está dizendo. "Pesquisa antes
   de conceito" é a recusa de decidir com a imagem ainda embaçada, e a
   frase chega assim. */
.js .fr-mantra__linha {
  will-change: filter, opacity, transform;
}

/* ============================================================
   BLOCOS E SERVIÇOS
   ============================================================ */
.fr-blocos {
  padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
}

/* Cabeça pegajosa à esquerda, serviços à direita. `position: sticky`
   nativo, e não pin.

   A home prende porque cada cena é um argumento. Isto é documento, e
   documento rola como documento - é a mesma decisão que manteve pin fora
   da página do Ímpar. Sticky não briga com o Lenis, não custa uma
   ScrollTrigger por bloco, e cai sozinho quando não há JS. */
.fr-bloco {
  display: grid;
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  gap: clamp(var(--sp-7), 6vw, var(--sp-9));
  align-items: start;
  padding-block: clamp(var(--sp-8), 9vh, var(--sp-10));
  border-top: 1px solid var(--line);
}

.fr-bloco:first-child {
  border-top: 0;
}

.fr-bloco__cabeca {
  position: sticky;
  top: clamp(96px, 14vh, 168px);
  display: grid;
  gap: var(--sp-4);
}

/* O numeral é a prova do bloco, não a manchete dele: fica em Hairline e
   na cor da unidade, e por isso pode ser grande sem competir com o H2. */
.fr-bloco__num {
  font-family: var(--ff);
  font-weight: 100;
  font-size: var(--fs-numeral);
  line-height: var(--lh-numeral);
  color: var(--unidade);
  /* Amarelo puro sobre #f1f1f1 mede 1.2:1. O numeral é decoração - o
     bloco é identificado pelo título ao lado, que é texto de verdade -
     mas decoração ilegível também não serve. O contorno devolve a forma
     sem tirar a cor. */
  -webkit-text-stroke: 1px var(--c-black);
}

.fr-bloco__apoio {
  max-width: 34ch;
  color: var(--fg-muted);
}

.fr-bloco__lista {
  display: grid;
  gap: clamp(var(--sp-7), 6vh, var(--sp-9));
}

/* ---------- um serviço ----------
   Cartão de contorno, sem preenchimento. O filete que separava dois
   serviços saiu: separador e moldura fazem o mesmo trabalho, e os dois
   juntos davam duas réguas paralelas a cada troca de serviço.

   Contorno e não superfície cheia porque o chão da página já é claro - um
   cartão branco sobre `#f1f1f1` seria quase o mesmo chão com uma sombra
   de diferença. O que separa aqui é a linha, não a cor. (O branco ficava
   guardado para a caixa do gatilho, que era a única superfície cheia
   dentro do cartão; ela saiu em 25/08 e não há mais branco nenhum aqui
   dentro - ver a lápide mais abaixo nesta folha.)

   Raio com teto em pixel, como a placa do fechamento: 20,8% do lado menor
   num cartão largo e baixo devolveria um canto de estádio.

   A pílula de categoria saiu antes, e pelo mesmo princípio: ela repetia,
   em cada uma das onze linhas, a palavra que a cabeça pegajosa ao lado já
   está dizendo o tempo todo. */
.fr-servico {
  display: grid;
  gap: var(--sp-4);
  padding: clamp(var(--sp-5), 3vh, var(--sp-7));
  border: 1px solid var(--line);
  border-radius: min(20.8%, 28px);
  transition: border-color var(--dur-base) var(--ease-out);
}

.fr-servico:hover,
.fr-servico:focus-within {
  border-color: var(--c-black);
}

.fr-servico__topo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
}

/* O ícone mora numa moldura squircle, que é a primitiva de superfície do
   sistema. Pílula seria interativo, e ele não é clicável. */
.fr-servico__icone {
  flex: none;
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border: 1px solid var(--line);
  border-radius: var(--r-squircle);
  background: var(--c-white);
  transition:
    border-color var(--dur-base) var(--ease-out),
    background-color var(--dur-base) var(--ease-out);
}

.fr-servico__icone svg {
  width: 26px;
  height: 26px;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-miterlimit: 10;
}

.fr-servico:hover .fr-servico__icone,
.fr-servico:focus-within .fr-servico__icone {
  border-color: var(--unidade);
  background: var(--unidade);
  /* O `<svg>` dentro é `stroke: currentColor`, então o traço segue daqui.
     No amarelo `--sobre-unidade` é preto, que é o que já era: esta linha é
     inócua na /comunicacao e é o que faz o ícone continuar legível quando
     a unidade é o azul. */
  color: var(--sobre-unidade);
}

.fr-servico__texto {
  max-width: 62ch;
  color: var(--fg-muted);
}

/* Os quatro itens de escopo. Marcador próprio, e não `list-style`: o
   traço da régua do sistema, virado de lado, na mesma espessura e na
   mesma cor que separa cliente de serviço no cartão de case. */
.fr-servico__itens {
  display: grid;
  gap: var(--sp-3);
  margin: 0;
  padding: 0;
  list-style: none;
}

.fr-servico__itens li {
  position: relative;
  padding-left: var(--sp-5);
  font-size: var(--fs-small);
}

.fr-servico__itens li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 10px;
  height: 1px;
  background: var(--fg-muted);
}

/* A CAIXA "PROCURE POR ISSO QUANDO:" SAIU DE TODOS OS SERVICOS (25/08,
   pedido da CEO: "nao queremos falar com leigos"). Eram onze caixas em
   /comunicacao e tres em /performance - /tecnologia ja tinha perdido as
   suas na rodada anterior -, e com elas saem `.fr-servico__gatilho` e
   `.fr-servico__gatilho b`, que nao tinham mais nenhum elemento no site.

   O CARTAO PERDE A UNICA SUPERFICIE CHEIA QUE TINHA, e isso muda uma
   decisao registrada logo acima, em `.fr-servico`: o branco ficava
   "guardado para a caixa do gatilho". Nao ha mais o que guardar. O que
   separa um servico do outro continua sendo a linha do contorno, que era
   o argumento original - a caixa era o destaque de dentro, nao a moldura
   de fora. */

/* ============================================================
   A FAIXA DE CASES
   ============================================================ */
/* O bloco é o da home, sem uma linha nova - mas ele foi desenhado num
   documento de tema escuro, e este documento é `data-theme="light"`. As
   duas regras que ele traz de casa (`background` preto, `color` branca)
   não trocam os TOKENS, então o `.t-label` do olho continuava lendo
   `--fg-muted` do tema claro: #3a3a3a sobre preto, 1.7:1 - o olho
   praticamente sumia.

   Isto devolve ao bloco o tema em que ele foi desenhado. É a mesma troca
   de `.fr-escuro`, e por isso o olho volta a medir o mesmo que na home. */
.frente-page .cases {
  --fg: var(--c-white);
  --fg-muted: var(--c-gray-on-dark);
  --line: var(--c-line-dark);
}

/* ---------- sem JavaScript ----------
   `scenes.css` dá `overflow: hidden` ao `.pan` e largura `max-content` ao
   trilho: quem faz a faixa andar é o pin, e quem devolve rolagem nativa
   quando não há GSAP é a classe `is-swipe`, posta pelo `scenes/cases.js`.

   Sem JavaScript nenhum, a classe não chega - e aí o `overflow: hidden`
   fica valendo sozinho. A faixa continua desenhada, mas do segundo cartão
   em diante ninguém alcança: sete cases viram um.

   Esta regra fecha esse buraco. Fica em `frente.css` e não em `scenes.css`
   de propósito: mexer na folha compartilhada mudaria a home e a página de
   case, que estão fora do que foi pedido. As duas têm o mesmo buraco, e
   ele está reportado. */
html:not(.js) .pan {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}

html:not(.js) .pan .case {
  scroll-snap-align: start;
}

/* ============================================================
   FECHAMENTO
   ============================================================ */
/* Chão preto, emendando com a faixa de cases acima e com o rodapé abaixo.
   Dentro dele, a placa da cor da unidade: recuada, com cantos
   arredondados, texto preto.

   Recuada e não sangrando: foi escolha do cliente, e ela resolve um
   problema real - o rodapé é preto, e uma faixa amarela de borda a borda
   faria amarelo encostar em preto sem intervalo. Com a margem, o amarelo
   encosta só no preto do próprio fechamento, que é onde `#FFDD00` rende
   mais: 15,6:1 com texto preto, medido. */
.fr-fechamento {
  background: var(--c-black);
  padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
}

/* A PLACA MORA EM `layout.css`, e não mais aqui: o índice de /conteudo
   fecha com a mesma peça e não carrega esta folha. O que sobrou nesta
   página são as duas linhas de token acima (`--unidade` e
   `--sobre-unidade`, que a placa lê) e a inversão do botão na frente azul,
   mais abaixo. */

/* ============================================================
   /TECNOLOGIA - A FRENTE AZUL
   ============================================================ */
/* A /comunicacao é uma página CLARA que escurece seções soltas com
   `.fr-escuro`. A /tecnologia é o espelho: página ESCURA que clareia
   seções soltas. Isso é fiel aos assets (o KV e o vídeo têm média
   rgb(2,7,19)) e ao elleven.digital, mas o motivo de peso é mecânico: a
   janela do hero abre para uma faixa preta sangrando, e com chão claro
   esse momento seria um corte de off-white para preto no meio da primeira
   tela, pedindo uma transição que o efeito não pede.

   O documento NÃO leva `data-theme="light"` no `<html>`, ao contrário da
   /comunicacao. Sem ele os tokens já entregam tema escuro, que é o padrão
   da folha. */
.frente-page--tecnologia {
  background: var(--c-black);
  --unidade: var(--c-blue);
  --sobre-unidade: var(--c-white);
  --foco-linha: var(--c-white);
  --foco-halo: var(--c-black);
}

/* O espelho de `.fr-escuro`, e o formato não é invenção: é exatamente o
   que `.produtos` faz em `scenes.css` para ser o respiro branco da home.
   Vale para os serviços. A faixa de cases não precisa - `.cases` já traz
   o próprio preto -, e `.produtos` também não, porque declara o conjunto
   completo em si mesma. */
.fr-claro {
  background: var(--c-offwhite);
  color: var(--c-black);
  --fg: var(--c-black);
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
  --foco-linha: var(--c-black);
  --foco-halo: var(--c-white);
}

/* Sobre chão preto o header já é branco: `mix-blend-mode: difference` com
   branco sobre #000 devolve branco. Nada a fazer. O que muda é o corpo
   claro dos serviços, onde ele se subtrai sozinho, igual à /comunicacao. */

/* PRETO, e não a cor da unidade. Na /comunicacao o numeral é amarelo com
   contorno preto porque amarelo sobre #f1f1f1 mede 1,2:1 e ele sumia. Em
   azul o problema não é legibilidade - é hierarquia: azul sobre claro puxa
   tanto o olho quanto o título ao lado, e o numeral é a prova do bloco,
   não a manchete dele. Preto sólido resolve os dois, e o contorno perde a
   função junto. */
.frente-page--tecnologia .fr-bloco__num {
  color: var(--c-black);
  -webkit-text-stroke: 0;
}

/* Pílula preta sobre placa azul funcionaria (3,68:1 é o mínimo de 3:1 para
   elemento não textual), mas a regra da placa é que o botão INVERTE - e o
   que a placa azul tem por primeiro plano é branco, não preto. */
.frente-page--tecnologia .placa .btn {
  --btn-bg: var(--c-white);
  --btn-fg: var(--c-black);
}

.frente-page--tecnologia .placa .btn:hover {
  --btn-bg: var(--c-black);
  --btn-fg: var(--c-white);
}

/* ---------- o hero ----------
   Uma seção alta com um palco `sticky` dentro. A altura extra só existe
   com `is-abrindo`, posta por `frente/hero-janela.js`; sem ela a seção
   tem uma tela e `--fechado` fica em 0, ou seja, a faixa nasce sangrando.

   `@property` e não custom property crua: registrar o tipo é o que
   garante que `calc()` receba um número e que o valor inicial exista antes
   de o GSAP tocar no elemento. Onde `@property` não existir, a substituição
   textual dentro do `calc()` continua funcionando. */
@property --fechado {
  syntax: "<number>";
  inherits: true;
  initial-value: 1;
}

/* Quanto da saída do TEXTO já correu, e só do texto. Um único leitor,
   `.tec-hero__texto`.

   Ela já teve dois leitores: `.tec-alvo` lia esta mesma variável, e é a
   história que a variável abaixo conta. O nome ficou de quando ela media a
   entrada da cena inteira - não vale renomear uma variável que a folha lê
   em regra viva. Ver a constante `SAIDA`, em `hero-janela.js`, para a
   conta de por que o texto sai onde sai. */
@property --entrada {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* Quanto da saída da MÃO e dos ANÉIS já correu. Existe porque `.tec-alvo`
   lia `--entrada`, e duas coisas com tempos diferentes penduradas na mesma
   variável é uma delas mandando na outra.

   A CONTA ERA DO TEXTO, E A MÃO PAGAVA. `SAIDA` vale 0.06 da janela porque
   é o que tira o texto antes de a faixa alcançar o pé dele a 1280x720 - um
   número medido, e certo, PARA O TEXTO. Para a mão ele é um sumiço de 48px
   de rolagem, e foi o que o cliente descreveu como "some rápido demais".

   Agora ela tem trecho próprio: começa DEPOIS do clique (`ESPERA_MAO`) e
   dura quase três vezes mais (`SAIDA_MAO`), os dois em `hero-janela.js`.
   Medido a 1280x800: a mão sumia em 189px de rolagem e agora leva 387px,
   começando 108px mais tarde; ela chega a meia opacidade com a faixa 37%
   aberta e some com ela em 91%. Ficar até o fim faria dela o objeto órfão
   que a regra do `display: none`, logo abaixo, existe para evitar. */
@property --saida-mao {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* A posição da mão no trajeto, de 0 (ancorada na borda) a 1 (sobre o
   objeto). CONTÍNUA, e não em `steps()`: uma rodada anterior saltava em
   seis degraus para não borrar pixel art, mas `#i-mao` é vetor
   (`<polygon>`/`<path>`), não bitmap - não há grade de amostragem para
   perder, e os seis saltos só liam como movimento robótico. Ver o
   comentário do tween da travessia em `hero-janela.js`. */
@property --passo {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* O cutucão, em pixels inteiros de tela. `<number>` e não `<length>` para a
   folha poder multiplicar por `1px` dentro do mesmo `calc()` que já compõe
   `--dx` - e para o valor nunca virar fracionário no caminho. Escritos por
   `hero-janela.js` a cada quadro em que o ponteiro anda, e só onde existe
   ponteiro de verdade. */
@property --olhar-x {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

@property --olhar-y {
  syntax: "<number>";
  inherits: true;
  initial-value: 0;
}

/* CHÃO CLARO, e o resto da página segue escura. Os tokens são virados aqui
   dentro, exatamente como `.fr-claro` faz mais abaixo - a página é
   `--c-black` por fora, então sem virar `--bg` junto o `.btn` sairia preto
   sobre preto: ele lê `--btn-bg: var(--fg)` e `--btn-fg: var(--bg)`.

   `--c-offwhite` e não `#fff`: é o único chão claro do sistema, é o que
   `[data-theme=light]` e `.fr-claro` usam, e é o valor que o header já
   conta com - `mix-blend-mode: difference` sobre #f1f1f1 devolve #0e0e0e,
   que é a nota logo acima de `.frente-page .header`. Branco puro criaria
   um segundo chão claro que não existe em lugar nenhum do site.

   De quebra é o que a medição pedia: a média do vídeo é rgb(2,7,19), que
   contra preto dá 1,04:1 e contra #f1f1f1 dá 17,5:1. */
.tec-hero {
  position: relative;
  background: var(--c-offwhite);
  color: var(--c-black);
  --bg: var(--c-offwhite);
  --fg: var(--c-black);
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
  --foco-linha: var(--c-black);
  --foco-halo: var(--c-white);
  /* Sem JavaScript, sem GSAP, com movimento reduzido ou em tela estreita a
     classe não chega: aberto é o estado de repouso. */
  --fechado: 0;
  /* A conta do recorte usa `100vw`, e a barra de rolagem faz `100vw` ser
     alguns pixels maior que a caixa. A diferença só existe nos quadros
     intermediários e vale poucos pixels de cada lado, mas ela não pode
     virar rolagem horizontal - que é o defeito que esta base já mediu em
     416px na home. */
  overflow-x: clip;
}

.tec-hero.is-abrindo {
  --fechado: 1;
  /* 240dvh, e o número é a soma de quatro parcelas, não um gosto:
     100dvh do palco + 60vh da aproximação + 20vh da pausa + 60vh da
     abertura. As três últimas estão em `hero-janela.js` como `TRECHO` e
     `PAUSA`, e mexer lá sem mexer aqui deixa a última parte do movimento
     acontecendo depois de o `sticky` já ter soltado.

     Era 180dvh quando havia uma transformação só, e 220 quando o clique
     ainda partia do mesmo pixel da abertura. Os 20 da pausa são o que dá ao
     clique um trecho de rolagem só dele. */
  height: 240dvh;
}

/* O PALCO CABE EM UMA TELA, e isso não é preferência: é a correção de um
   defeito medido. Era uma grade de duas linhas com o texto em cima e a
   faixa embaixo, as duas EM FLUXO. A 1280x720 isso dava 112 + 424 + 476 =
   1012px de conteúdo, e o palco crescia para 1120. Como ele é `sticky` no
   topo, os 400px que sobravam NUNCA entravam na tela: no scroll 0 via-se
   138px de um quadrado de 384, e o marquee inteiro ficava abaixo da dobra.

   A saída foi tirar do fluxo tudo que não é texto. `.tec-alvo` e
   `.tec-hero__baixo` são absolutos e ancorados embaixo, então o palco mede
   só o bloco de texto e o `min-height` manda. O estado fechado e o estado
   aberto nunca precisam caber juntos - é a rolagem que troca um pelo
   outro. */
.tec-hero__palco {
  /* ---------- a geometria da cena, em um lugar só ----------
     Os quatro valores abaixo são lidos pela faixa, pelo recorte, pelos
     anéis e pela mão. Eles têm de morar aqui, e não em `.tec-janela`,
     porque `.tec-alvo` é irmã dela e precisa dos mesmos números para
     aterrissar no mesmo pixel. */

  /* A altura nativa da faixa: 714÷1920 do arquivo de vídeo. */
  --alt: 37.1875vw;

  /* A ÓRBITA É O NÚMERO PRIMÁRIO, E `--lado` DERIVA DELA. A dependência
     era ao contrário, e ela inverteu por uma exigência de desenho: a
     órbita não pode sangrar. Sendo ela a variável de topo, "caber na
     largura útil" vira um termo do `min()` e não uma conta a refazer a
     cada breakpoint.

     3.85 é a razão medida entre os anéis e o núcleo da /comunicacao:
     335 ÷ 87 a 1280x720. 26vw é a largura que aquela órbita ocupa lá.
     Este hero passa a ser a MESMA cena em escala, o que muda é a âncora.

       1280   órbita 333   lado 86,4    quem manda: 26vw
       1440   órbita 374   lado 97,2    26vw
       1920   órbita 420   lado 109,1   o teto de 420 */
  --aneis-k: 3.85;
  --aneis-w: min(26vw, calc(100vw - 2 * var(--margin)), 420px);
  --lado: calc(var(--aneis-w) / var(--aneis-k));

  /* O CENTRO DO NÚCLEO DA IRMÃ ESTÁ A 87% DA ALTURA DA JANELA - 627 de
     720, medido. `7dvh` põe o desta página no mesmo lugar: a 720 dá 50,4,
     e o centro cai em 626,4. O piso de 24 é para telas baixas e o teto de
     64 impede que o objeto suba demais em monitor alto. */
  --folga-baixo: clamp(24px, 7dvh, 64px);

  position: sticky;
  top: 0;
  min-height: 100dvh;
  padding-top: clamp(112px, 14vh, 156px);
  /* A FAIXA DE BAIXO É RESERVADA NO FLUXO, e é isto que torna a colisão
     entre o texto e o objeto estruturalmente impossível.

     `.tec-alvo` é absoluta e ancorada no fim do palco, e o palco cresce com
     o texto: quando o texto é alto o bastante para preencher a coluna, os
     dois ocupam o mesmo lugar. Medido no celular deitado, 667x375: o texto
     acabava em 385 e o objeto ficava em 324..377 - 61px de sobreposição,
     com a manchete impressa por cima. A 375x667 na versão parada o mesmo
     defeito valia 114px.

     Este `padding` reserva exatamente a altura do objeto mais a folga mais
     um respiro. Ele NÃO cobra nada onde já sobrava tela - a 1280x720 o
     conteúdo passa a somar 714 contra os 720 do `min-height`, e o palco
     continua em 720 cravados. */
  padding-bottom: calc(
    var(--lado) + var(--folga-baixo) + clamp(12px, 2vh, 24px)
  );
  /* Sem padding lateral: a faixa precisa da largura inteira. Quem recua o
     texto é o `.shell` dele. */
  /* `hidden` E NÃO `clip`, e a troca é de suporte, não de gosto. Com o
     objeto ancorado à esquerda, a onda do clique escala os anéis a 2,1x:
     333 × 2,1 = 700px centrados em 218, o que põe a borda esquerda em
     −132. O recorte deixou de ser conforto e virou o que segura a página.

     `overflow: clip` só existe no Safari a partir do 16.0; abaixo disso
     ele computa `visible` e os 132px viram rolagem horizontal. `hidden` é
     suportado em tudo, e aqui ele é seguro: um elemento `sticky` gruda em
     relação aos ANCESTRAIS dele, então `overflow` no próprio palco não
     afeta a grudação do palco.

     O `overflow-x: clip` da SEÇÃO tem de continuar `clip`. Ali `hidden`
     faria `overflow-y` computar `auto`, a seção viraria container de
     rolagem, e o `sticky` do palco passaria a grudar num scrollport que
     não rola - trocaria rolagem horizontal por um hero que não gruda. */
  overflow: hidden;
}

/* PENDURADO EM `--entrada`, E SEM O PREFIXO `is-abrindo`. Antes era
   `opacity: var(--fechado)` sob `.tec-hero.is-abrindo`, e o prefixo era
   obrigatório: `--fechado` vale 0 em repouso, então solto ele apagava a
   manchete inteira justamente para quem não tem JavaScript. `--entrada`
   nasce em 0 e `calc(1 - 0)` dá 1 - o repouso já é o estado certo, e a
   regra passa a valer para todo mundo sem guarda.

   O TEXTO SAI DURANTE A ABERTURA, e não durante a travessia. Ele saía
   antes por um argumento que morreu na quinta rodada - "a mão precisa de
   tela vazia" -, e o preço daquele arranjo era um defeito: `.tec-alvo` lia
   a MESMA variável, então a mão chegava ao objeto em opacidade zero e o
   clique inteiro acontecia invisível. Três rodadas de reforço no gesto
   nunca chegaram à tela por causa desta linha.

   A separação foi feita em duas etapas, e vale saber que são duas. A
   primeira mudou o TRECHO: `--entrada` deixou de correr junto com
   `--passo` e passou para a abertura, o que fez a mão chegar inteira ao
   clique. A segunda mudou o LEITOR: `.tec-alvo` saiu desta variável e foi
   para `--saida-mao`, porque enquanto os dois liam a mesma coisa a mão
   sumia na velocidade que o texto pedia. Uma variável, um leitor.

   O texto termina de sumir um pouco ANTES de a faixa alcançar o lugar dele
   - com folga de 13px na tela mais apertada (1280x720; no celular a faixa
   nem chega perto, a folga passa de 100px). A regra não mudou uma letra -
   o que mudou foi o trecho a que a variável estava presa, e depois quem a
   lê.

   Nenhum destino se perde quando o texto sai: o mesmo CTA está no header
   e no fechamento. */
.tec-hero__texto {
  position: relative;
  z-index: var(--z-raised);
  opacity: calc(1 - var(--entrada));
}

.tec-hero__titulo {
  font-size: clamp(1.9rem, 0.9rem + 2.9vw, 3.5rem);
  max-width: min(58%, 820px);
}

.tec-hero__sub {
  max-width: 46ch;
  margin-top: clamp(var(--sp-5), 4vh, var(--sp-7));
  color: var(--fg-muted);
}

.tec-hero__acoes {
  margin-top: clamp(var(--sp-6), 4vh, var(--sp-7));
}

/* ---------- notebook: o texto cede, e a órbita para de cortar ----------
   O DEFEITO: a 1425x760 o anel externo nascia em 471 e o texto terminava
   em 558 - a órbita atravessava a última linha do parágrafo e o botão,
   por cima das letras. Medido, e presente em toda altura abaixo de ~920px,
   que é a faixa da maioria dos notebooks.

   POR QUE ACONTECE: o bloco de texto é ancorado no TOPO e tem altura
   quase constante (~446px a 56px de manchete); a órbita é ancorada no
   FIM do palco e o tamanho dela vem da LARGURA (`26vw`). Numa tela larga
   e baixa os dois crescem para o mesmo lugar. O `padding-bottom` do palco
   já reservava espaço para a colisão - mas reservava o lado do QUADRADO
   (`--lado`), e quem sangra é a ÓRBITA, que é 3,85 vezes maior.

   POR QUE O TEXTO É QUEM CEDE PRIMEIRO: a órbita não encolhe sozinha - o
   quadrado fechado da janela deriva dela (`--lado = --aneis-w / 3.85`), e
   uma janela de 40px deixa de ser um objeto. Já a manchete perder um
   corpo em tela baixa é o que qualquer hero faz.

   TRÊS DEGRAUS, do mais barato para o mais caro, e cada um só entra onde
   o anterior não fecha mais a conta. Acima de 920px de altura nada muda:
   a cena continua exata como foi desenhada. Abaixo de 1025px de largura a
   página tem outro layout, com as suas próprias regras mais abaixo. */

/* DEGRAU 1 - só os vãos. Devolve ~51px sem tocar no corpo da manchete:
   o topo do palco sai de 14vh para 11vh (piso 96) e os dois `margin-top`
   do texto saem de 4vh para 2.5vh. Medido a 1425x901: a folga entre o fim
   do texto e o anel passa de 21px para 72px. */
@media (min-width: 1025px) and (max-height: 920px) {
  .tec-hero__palco {
    padding-top: clamp(96px, 11vh, 156px);
  }

  .tec-hero__sub {
    margin-top: clamp(var(--sp-4), 2.5vh, var(--sp-6));
  }

  .tec-hero__acoes {
    margin-top: clamp(var(--sp-5), 2.5vh, var(--sp-6));
  }
}

/* DEGRAU 2 - a manchete cai para duas linhas. Abaixo de ~860px os vãos
   sozinhos não bastam, e uma linha de manchete vale mais do que todos os
   vãos somados: 56px em três linhas dão 252px de altura, 44px em duas dão
   132. O `max-width` sobe junto com o corpo caindo - é o par que faz a
   frase caber em duas linhas. Só o corpo menor daria três linhas curtas, e
   o justificado abriria vãos entre as palavras.

   O piso do palco desce de 96 para 88 aqui: com a manchete menor, 96px de
   topo já sobravam. */
@media (min-width: 1025px) and (max-height: 860px) {
  .tec-hero__palco {
    padding-top: clamp(88px, 11vh, 156px);
  }

  .tec-hero__titulo {
    font-size: clamp(1.7rem, 0.7rem + 2.3vw, 2.75rem);
    max-width: min(72%, 900px);
  }
}

/* DEGRAU 3 - abaixo de 720px a órbita também cede, pela mesma porta que a
   /performance já usa para o alvo dela (`--alvo-lado: min(26vw, 21svh,
   180px)`): um termo de ALTURA dentro do `min()`. `--lado` desce junto,
   porque é dela que deriva - o objeto inteiro encolhe em escala, sem
   descolar do recorte da janela.

   `(100dvh - 470px) * 1.5` é a inversa da conta da colisão com o texto já
   apertado pelos dois degraus acima: ela mantém ~35px de folga constante
   de 700 para baixo. A 719px ela devolve 373 e o `26vw` continua mandando,
   então a entrada do degrau é contínua - não há salto no limiar. */
@media (min-width: 1025px) and (max-height: 719px) {
  .tec-hero__palco {
    --aneis-w: min(
      26vw,
      calc((100dvh - 470px) * 1.5),
      calc(100vw - 2 * var(--margin)),
      420px
    );
  }
}

/* ---------- o alvo: os anéis e a mão ----------
   Caixa vazia do tamanho e na posição EXATOS do quadrado fechado. Ela não
   pinta nada: existe para dar aos anéis e à mão um sistema de coordenadas
   comum com o recorte, que é filho de `.tec-hero__baixo` e não daqui.

   ANCORADA PELA ÓRBITA, NÃO PELO OBJETO - e NUNCA por `translate`. `left`
   soma a margem do shell à distância da órbita até o objeto; a conta
   inteira está no comentário da regra abaixo, junto de `--aneis-k`. Sem
   `translate` pela mesma razão de sempre, Armadilha 12 do HANDOFF: o GSAP
   anima os `rect` lá dentro e a mão de dentro, e ao assumir um elemento ele
   puxa o `translate` do CSS para a própria matriz e o zera no inline. */
.tec-alvo {
  position: absolute;
  /* ANCORADO PELA ÓRBITA, NÃO PELO OBJETO, e a diferença é o pedido do
     cliente: a órbita não sangra. Alinhar o objeto à margem poria 124px
     de anel para fora de cada lado; alinhar a ÓRBITA põe o anel externo
     na mesma linha vertical do H1 e do CTA, que é a margem do shell.

     `(--aneis-k - 1) / 2` é a distância do canto da órbita ao canto do
     objeto em múltiplos de `--lado`: 1,425. A 1280 isso dá 51,2 + 123 =
     174, e a órbita fecha em 51,2..384,4.

     No estreito a órbita ocupa a largura útil inteira, e o objeto acaba
     CENTRADO por consequência: 20 + 87 × 1,425 = 144, objeto 144..231
     numa tela de 375, centro em 187,5. Uma regra só, dois desenhos.

     Continua sem `translate`: o GSAP anima os `rect` daqui de dentro e a
     mão, e transformação declarada na folha some quando ele assume o
     elemento. Armadilha 12. */
  left: calc(var(--margin) + var(--lado) * (var(--aneis-k) - 1) / 2);
  bottom: var(--folga-baixo);
  width: var(--lado);
  height: var(--lado);
  z-index: var(--z-raised);
  pointer-events: none;
  /* `--saida-mao` E NÃO `--entrada`. Enquanto eram a mesma variável, a mão
     tinha de sumir na velocidade que o TEXTO precisava - ver o bloco do
     `@property`, lá em cima, para a conta que era do texto e a mão pagava. */
  opacity: calc(1 - var(--saida-mao));
}

/* SEM JAVASCRIPT A CAMADA NÃO EXISTE. Em repouso `--fechado` é 0, ou seja,
   a faixa nasce aberta - e uma mão pousada ao lado de um vídeo que já está
   tocando é um objeto órfão, não uma dica. Melhor não desenhar. */
.tec-hero:not(.is-abrindo) .tec-alvo {
  display: none;
}

/* Os quatro anéis, concêntricos ao quadrado e maiores que ele. 2.1 é o que
   os faz passarem da caixa em todos os lados: eles são a órbita do objeto,
   e órbita que cabe dentro dele não é órbita.

   Cortados embaixo, e isso é decisão e não acidente - a mesma que a função
   `entre()` de `frentes-metodo.js` já toma na home: quando o piso e o teto
   se cruzam, o piso vence e o anel aceita ser cortado. */
/* `left`/`top` por conta, e NÃO `inset-inline: 0` + `margin-inline: auto`.
   Foi medido: com os dois lados presos em 0 e uma largura MAIOR que a do
   pai, a caixa fica sobre-restrita e o navegador devolve a largura do pai -
   os anéis saíam com 144px em vez de 302px, ou seja, do tamanho exato do
   objeto que deveriam orbitar. Centralizar por deslocamento é o idioma que
   `.fr-aneis` já usa na /comunicacao, e ele não tem essa ambiguidade.

   Continua sem `translate`, que é o que importa para o GSAP: ele anima os
   `rect` daqui de dentro no clique, e transformação declarada na folha some
   quando ele assume o elemento. Armadilha 12 do HANDOFF. */
.tec-hero .fr-aneis {
  position: absolute;
  /* 3,85, E NÃO MAIS 2,8: O NÚMERO PASSOU A SER O DA PÁGINA IRMÃ. É a razão
     medida entre os anéis e o núcleo da /comunicacao - 335 ÷ 87 a 1280x720
     -, e este hero deixou de ter um teto próprio: com a órbita virando a
     variável de topo (ver `--aneis-w` no palco), "caber na largura útil"
     já é um termo do `min()` dela, então não sobra motivo para reduzir a
     razão só para não sangrar. O hero é a MESMA cena da /comunicacao em
     escala, e usa a mesma razão dela.

     O anel de dentro é o `rect` de 40 num viewBox de 100, ou seja, 0,40 da
     largura do conjunto - e ainda limpa a borda do objeto, com folga.
     Medido a 1280x720, com `--lado` em 86,4px: o anel interno sai com
     133,1px contra um objeto de 86,4px, 23,3px de vão de cada lado. Bem
     mais folgado do que o 2,8 antigo dava (161px contra um objeto de 144,
     8,5px de cada lado).

     O número mora em `--aneis-k`, no palco, e agora vale IGUAL nos dois
     breakpoints: o media query parou de sobrescrevê-lo na Task 1, porque
     `--lado` também passou a derivar da largura útil lá (`--aneis-w`), e
     não precisa mais de um `--aneis-k` menor para não virar parede. */
  width: calc(var(--lado) * var(--aneis-k));
  /* `max-width: none`, E SEM ISTO OS ANÉIS NÃO EXISTEM. `base.css` traz o
     reset de sempre - `img, picture, video, canvas, svg { max-width: 100% }`
     -, e aqui o pai é `.tec-alvo`, que tem exatamente o tamanho do objeto.
     Os 302px viravam 144: a órbita saía do tamanho do que ela orbita, o que
     é o mesmo que não haver órbita.

     Na /comunicacao isso nunca apareceu porque lá o pai é o palco inteiro,
     e 373px cabem folgados em 100% dele. É um reset que só morde quando o
     SVG é maior que a própria caixa - que é justamente o caso de um anel. */
  max-width: none;
  aspect-ratio: 1;
  /* Metade da própria largura para trás do meio da caixa, nos dois eixos.
     Derivado de `--aneis-k` e não escrito à mão: um recuo que não é metade
     exata da largura descentra a órbita do objeto que ela orbita. */
  left: calc(50% - var(--lado) * var(--aneis-k) / 2);
  top: calc(50% - var(--lado) * var(--aneis-k) / 2);
  overflow: visible;
  color: var(--fg);
}

/* ---------- a mão ----------
   DUAS CAIXAS. A de fora faz a aproximação, em CSS puro, lendo `--passo`.
   A de dentro é a que o GSAP move no clique. Nenhuma propriedade com dois
   donos - armadilha 12 de novo.

   A conta é `(1 - --passo)`: o CSS declara a mão no DESTINO e a empurra de
   volta para a origem enquanto o passo é pequeno. Assim o repouso do
   documento é a mão no lugar certo, e é a animação que a tira de lá.

   `--dx` e `--dy` em unidades absolutas de propósito: porcentagem dentro
   de `translate()` resolve contra a caixa DO PRÓPRIO ELEMENTO, então `50%`
   aqui seria metade da mão e não metade da tela. */
.tec-mao {
  --mao-w: clamp(64px, 7vw, 110px);
  /* A PARTIDA SAI DA GEOMETRIA, NÃO DE UM `vw` ESCOLHIDO. Com o alvo
     centrado, `40vw` punha a mão na borda direita; com ele à esquerda o
     mesmo número a põe no meio da tela. A fórmula resolve para "a borda
     direita da mão fica 14px além da borda da tela", em qualquer largura:

       repouso = alvoLeft + (lado/2 − 0,45·maoW) + dx
       quer-se repouso + maoW = 100vw + 14
       → dx = 100vw + 14 − margem − aneisW/2 − 0,55·maoW

     Medido: sangra exatos 14px a 1280, 1440, 1920, 414, 390 e 375. O
     percurso passa a ser o próprio `--dx` - 1027px a 1280, contra 512. */
  --dx: calc(100vw + 14px - var(--margin) - var(--aneis-w) / 2 - var(--mao-w) * 0.55);
  /* 5vh e não 10, e a diferença foi medida na dobra: com 10vh a mão em
     repouso caía em 698..799 numa tela de 720, ou seja, 22% dela visível -
     um naco de desenho no canto, não uma mão esperando. Com 5vh sobram
     ~78%. Ela ainda sangra, o que é o enquadramento certo (as mãos da
     /comunicacao também sangram), mas dá para ver o que é. */
  --dy: 5vh;
  position: absolute;
  width: var(--mao-w);
  /* A proporção do arquivo: 1152 / 1295.3. */
  aspect-ratio: 1152 / 1295.3;
  /* A ponta do dedo mora a ~45% da largura do desenho. Recuar a caixa por
     esse tanto é o que põe a PONTA no eixo do objeto, e não o meio da
     imagem - é a mesma disciplina das mãos da /comunicacao, ancoradas pela
     ponta do dedo e não pela borda da caixa. */
  left: calc(50% - var(--mao-w) * 0.45);
  /* O POUSO: quanto do lado do quadrado a mão desce a partir do topo dele.
     A ponta entra no terço de baixo do objeto, como no KV. 0.30 e não 0.45
     porque a 1280x720 o valor maior punha a base da mão em 727 numa tela de
     720 - ela já chegava cortada AO DESTINO, e o destino é onde ela precisa
     estar inteira.

     É variável porque a razão mão/objeto muda no celular: a 375x667 a mão
     tem 92,7 de altura contra um objeto de 87 - quase um para um (1,07) -,
     e isso deixa menos folga abaixo do pouso do que no desktop. Ver o
     media query. */
  --pouso: 0.30;
  top: calc(var(--lado) * var(--pouso));
  /* O CUTUCÃO ENTRA DENTRO DO MESMO FATOR, e isso é o que o desliga sozinho:
     multiplicado por `(1 - --passo)`, ele vale inteiro enquanto a mão espera
     e chega a zero exatamente quando ela pousa no objeto. Não há nada a
     desmontar em JavaScript quando a rolagem começa. */
  transform: translate(
    calc((1 - var(--passo)) * (var(--dx) + var(--olhar-x) * 1px)),
    calc((1 - var(--passo)) * (var(--dy) + var(--olhar-y) * 1px))
  );
}

/* DUAS CORES ATRAVÉS DE `<use>`, E É POR ISSO QUE NÃO HÁ SELETOR DE CLASSE
   AQUI. O `<use>` clona o símbolo dentro de uma shadow tree: `.mao__corpo` e
   `.mao__contorno` existem no desenho mas NENHUM seletor deste arquivo os
   alcança. Foi medido - as regras por classe não aplicavam nada, e as duas
   formas saíam pretas, porque sem `fill` declarado o padrão do SVG é preto.

   O que atravessa a shadow tree é HERANÇA. `fill` e `color` herdam, então:

     `fill`   pinta o CORPO, que não declara `fill` próprio e herda daqui
     `color`  pinta o CONTORNO, que traz `fill="currentColor"` no símbolo

   Duas propriedades herdáveis, duas formas, uma regra. E o arquivo de
   origem trazia o corpo travado em `#fff`, que sobre `#f1f1f1` mede 1,13:1
   e some - sobre chão claro o cursor de link inverte, como todo sistema
   operacional o desenha. Aqui isso é uma linha, não um segundo arquivo. */
.tec-mao__svg {
  display: block;
  width: 100%;
  height: 100%;
  fill: var(--mao-corpo, var(--fg));
  color: var(--mao-contorno, var(--bg));
}

.tec-hero__baixo {
  /* FORA DO FLUXO, e é isto que devolve o palco a uma tela. Em fluxo, a
     faixa somava 476px aos 536px do texto e empurrava o palco para 1120px
     dentro de uma janela de 720 - 400px que, por causa do `sticky`, nunca
     entravam na tela.

     E SEM TRANSFORMAÇÃO NENHUMA desde a quinta rodada. Ela existia para
     descer a faixa até o recorte central coincidir com o alvo; com o
     recorte assimétrico o quadrado nasce onde tem de nascer, e a faixa
     nunca mais precisa se mexer. Uma caixa transformada a menos, e a
     `.tec-janela` volta a ter um dono só - o GSAP, na escala do clique. */
  position: absolute;
  inset-inline: 0;
  bottom: 0;
}

/* ---------- a janela ----------
   A faixa já está aqui com o tamanho FINAL, e o vídeo dentro dela nunca é
   transformado. Quem se move é o recorte.

   37.1875vw é 714÷1920, a proporção nativa do arquivo. O recorte parte de
   um quadrado centrado de lado `--lado` e vai a zero.

   O RAIO EM PIXEL, E NÃO EM PORCENTAGEM. `inset(... round X)` resolve a
   porcentagem contra a caixa de referência, que aqui é a faixa inteira -
   `round 20.8%` daria 20,8% de 100vw. Calculado a partir de `--lado`, que
   é quadrado, 0.208 nos dois eixos reproduz o squircle do arquivo de marca
   exatamente. É a mesma armadilha que `tokens.css` documenta para raio
   percentual fora do quadrado.

   `--lado` E `--alt` MORAM NO PALCO, não aqui. `.tec-alvo` é irmã desta
   caixa e precisa dos mesmos dois números para pôr os anéis e a mão em
   cima do quadrado; declarados aqui dentro, ela não os enxergaria. */
.tec-janela {
  position: relative;
  width: 100%;
  height: var(--alt);
  background: var(--c-black);
  /* O RECORTE É ASSIMÉTRICO, e é ele que põe o quadrado no lugar do alvo
     sem transladar a faixa. Os quatro valores saem da mesma geometria que
     `.tec-alvo` usa, então os dois não podem se descolar:

       topo   = altura da faixa − folga − lado
       direita= 100% − margem − lado × (k+1)/2
       baixo  = folga
       esquerda = margem + lado × (k−1)/2

     A 1280x720, com a faixa em y 244..720 e x 0..1265:
       inset(339px 1004,6px 50,6px 174px)  → quadrado em 174..260,4 / 583..669,4

     `100%` e NÃO `100vw` na direita: dentro de `inset()` a porcentagem
     resolve contra a caixa do próprio elemento e por eixo, que é o que se
     quer. Com `100vw` a conta ignora a barra de rolagem, e medido a 1280
     isso tirava 15px do lado errado.

     O raio continua em PIXEL e derivado de `--lado`, que é quadrado:
     `round 20.8%` resolveria contra a faixa inteira e daria 20,8% de
     1265. É a armadilha que `tokens.css` documenta. */
  --recorte-topo: calc(var(--alt) - var(--folga-baixo) - var(--lado));
  --recorte-dir: calc(100% - var(--margin) - var(--lado) * (var(--aneis-k) + 1) / 2);
  --recorte-baixo: var(--folga-baixo);
  --recorte-esq: calc(var(--margin) + var(--lado) * (var(--aneis-k) - 1) / 2);

  clip-path: inset(
    calc(var(--recorte-topo) * var(--fechado))
    calc(var(--recorte-dir) * var(--fechado))
    calc(var(--recorte-baixo) * var(--fechado))
    calc(var(--recorte-esq) * var(--fechado))
    round calc(var(--lado) * 0.208 * var(--fechado))
  );
}

/* ABSOLUTO, e não em fluxo: a faixa tem altura própria (`--alt`, declarada
   na mãe), então tirar o filme do fluxo não custa medida nenhuma - e é o
   que permite a fita ficar em cima dele sem uma segunda caixa.

   Havia um segundo elemento aqui, o KV parado do celular, e ele saiu junto
   com a hélice: quem abre a janela agora é o filme nos dois regimes, com a
   fita correndo por cima. O motivo está no `index.php`. */
.tec-janela video {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ---------- a fita ----------
   Cobre a faixa inteira e é recortada pelo `clip-path` da mãe, igual ao
   filme. Ela substituiu a dupla hélice do KV 01, que corria o mesmo texto
   por duas senoides espelhadas: `textPath` distribui o corpo do tipo ao
   longo do arco, então a letra abria nas cristas, apertava nos vales e as
   duas fitas se cruzavam nos nós - textura, não frase.

   `overflow: hidden` aqui e não na mãe: a mãe tem `clip-path`, que já
   recorta, e um `overflow` a mais nela criaria um segundo contexto de
   recorte para o mesmo limite. */
.tec-fita {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  overflow: hidden;
  pointer-events: none;
}

/* A EMENDA É `-50%`, E ELA NÃO PRECISA DE NÚMERO NENHUM. O trilho tem
   `width: max-content` e guarda DUAS cópias idênticas, então metade dele é
   exatamente uma cópia: andar meia largura e voltar a zero põe a segunda
   cópia onde a primeira estava, ao pixel, sem ninguém precisar medir o
   texto. É a mesma emenda do trilho de logotipos da home, escrita em CSS.

   `linear` porque faixa que acelera e desacelera deixa de ser fundo e vira
   gesto - e o gesto desta cena é a janela abrindo, não a fita andando. */
.tec-fita__trilho {
  display: flex;
  width: max-content;
  /* 40s E NAO 26, E O NUMERO E' DERIVADO. O compasso que se le' e' a
     VELOCIDADE, nao a duracao: a volta anda exatamente uma copia, entao
     dobrar o corpo do tipo dobra a distancia e a mesma duracao dobraria a
     velocidade.

     Medido antes: a 1440, com o tipo em 31,7px, a copia media 2070,3px em
     26s = 79,6 px/s. Agora sao quatro repeticoes de tipo em 74,9px:
     10,89px de copia por px de tipo por repeticao, ou 3264px de copia -
     que a 40s da' 81,6 px/s. O mesmo passo, com o dobro do tamanho.

     A EMENDA CONTINUA SENDO `-50%` E NAO PRECISA DE NUMERO. Duas copias
     identicas num trilho `max-content`: metade dele e' uma copia inteira.
     O que a copia PRECISA e' ser mais larga que a janela, senao a volta
     abre um vao - com quatro repeticoes a copia mede 1,70 x a largura da
     tela na faixa do `5,2vw`, e 4.005px fixos depois do teto de 92px. E'
     dai' que sai o "ate' ~4.000px" do `index.php`. */
  animation: tec-fita 40s linear infinite;
}

@keyframes tec-fita {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}

/* Branco, e não o azul do KV. A fita corre sobre um filme de média 10/255:
   branco dá 21:1 e o azul da frente daria 3,7:1, que passa para texto
   grande mas não sobra. O azul fica nos nós, onde é ornamento e não
   leitura.

   `padding-inline-end` E NÃO `gap` NO TRILHO. O vão entre a última
   repetição de uma cópia e a primeira da seguinte precisa entrar na
   LARGURA da cópia, senão os 50% da animação deixam de valer uma cópia
   inteira e o laço salta. É a mesma lição do marquee de logotipos. */
.tec-fita__copia {
  padding-inline-end: 0.6em;
  white-space: nowrap;
  font-weight: 300;
  /* O DOBRO DO CORPO ANTERIOR, a pedido: era `clamp(22px, 2.2vw, 34px)`,
     e a 1440 isso dava 31,7px numa faixa de 535px de altura - a frase
     corria como legenda, nao como manchete. Agora 5,2vw dao' 75px, que e'
     ~14% da altura da faixa aberta.

     O PESO 300 FICA, e e' o que torna o corpo grande possivel: a Light em
     75px continua sendo desenho e nao parede. Em Regular a mesma altura
     fecharia a faixa.

     O piso de 34px e' o celular; o teto de 92 e' o monitor grande, onde
     5,2vw passariam da altura da faixa. */
  font-size: clamp(34px, 5.2vw, 92px);
  letter-spacing: 0.02em;
  color: var(--c-offwhite);
}

.tec-fita__no {
  font-style: normal;
  color: var(--unidade);
}

/* Desligada de verdade, não desacelerada - a regra do sistema. Parada, a
   frase continua legível: é texto reto, não desenho que depende do
   deslocamento para dizer o que diz. */
@media (prefers-reduced-motion: reduce) {
  .tec-fita__trilho {
    animation: none;
  }
}

/* A CAPA AZUL, e ela é a correção de um defeito medido: a média do vídeo é
   rgb(2,7,19), que contra preto dá 1,04:1 - o objeto sumia. Nenhum
   `filter: brightness()` resolve isso: 0,2% de luminância dobrada continua
   0,4%, e chegar a 3:1 exigiria ~8x, o que destrói o arquivo.

   A saída não é corrigir o vídeo, é não pedir a ele o que ele não pode
   dar. Enquanto o recorte é pequeno o objeto NÃO é o vídeo: é um squircle
   azul chapado, na cor da frente, sem aura - exatamente o que o cliente
   pediu. O vídeo só aparece quando já tem a largura inteira.

   Dentro da `.tec-janela`, então o mesmo `clip-path` a recorta: a capa é
   sempre o objeto, ao pixel, sem uma segunda conta para manter em dia. */
.tec-janela__capa {
  position: absolute;
  inset: 0;
  background: var(--unidade);
  opacity: var(--fechado);
}

/* O campo de pontos que respondia ao ponteiro foi REMOVIDO a pedido do
   cliente. Era a trama do KV virada chão, acendendo sob o cursor. Saiu
   junto `frente/campo.js`. Fica o registro para não ser reproposto sem que
   alguém saiba que já esteve aqui. */

/* ---------- a frase da Hostinger ----------
   A dobradiça da página: fecha o bloco escuro do argumento e abre o bloco
   claro do catálogo.

   Mesma forma do `.fr-mantra` - seção alta com caixa `sticky` -, e SEM
   `mantra.js`. O desfoque de lá é motivado, e o arquivo explica: "é a
   recusa de decidir com a imagem ainda embaçada". Esta frase não é mantra,
   é CREDENCIAL. Credencial que chega desfocada e resolve fica teatral, e
   teatro enfraquece prova de terceiro. Ela entra pelo `data-reveal` que já
   existe e fica.

   Branco sobre #0153FF mede 5,7:1: passa AA inclusive para o rótulo
   pequeno, que precisa de 4,5:1. */
.tec-frase {
  background: var(--c-blue);
  color: var(--c-white);
  --fg: var(--c-white);
  --fg-muted: rgba(255, 255, 255, 0.72);
  --line: rgba(255, 255, 255, 0.24);
  --foco-linha: var(--c-white);
  --foco-halo: var(--c-black);
  padding: 0;
}

/* A seção leva `fr-mantra` junto, que é a classe que `mantra.js` procura -
   e por isso herdaria `height: 260dvh`, dimensionado para TRÊS frases a
   ~48vh cada. Aqui há uma. 165dvh dão 65vh de rolagem, que é o mesmo tempo
   por frase da /comunicacao.

   Especificidade acima do empate de propósito: `.fr-mantra.is-cinema` e
   `.tec-frase.is-cinema` valem o mesmo, e depender da ordem do arquivo
   para desempatar é deixar uma armadilha para quem reorganizar a folha. */
.frente-page--tecnologia .tec-frase.is-cinema {
  height: 165dvh;
}

/* A credencial e quem a publicou entram no MESMO movimento. `mantra.js`
   mostra um `[data-mantra]` por vez; marcados em separado, a fonte
   chegaria depois como se fosse um segundo pensamento. */
.tec-frase__bloco {
  display: grid;
  justify-items: center;
  gap: clamp(var(--sp-4), 3vh, var(--sp-5));
}

.js .tec-frase__bloco {
  will-change: filter, opacity, transform;
}

.tec-frase__palco {
  position: sticky;
  top: 0;
  min-height: 100dvh;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  gap: clamp(var(--sp-4), 3vh, var(--sp-5));
  padding: clamp(var(--sp-8), 12vh, var(--sp-10)) var(--margin);
}

.tec-frase__linha {
  font-size: var(--fs-display-l);
  line-height: var(--lh-display);
  font-weight: 300;
  max-width: 18ch;
}

/* A fonte da alegação, e ela é obrigatória: ranking de terceiro sem quem o
   publicou é alegação solta. Pequeno de propósito - quem prova é a frase,
   a linha só diz de onde ela veio. */
.tec-frase__fonte {
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--fg-muted);
}

/* ---------- o bloco de um serviço só ----------
   O bloco 03 tem um serviço. Ele ocupa a largura inteira em vez de sentar
   numa grade de dois com um buraco: célula vazia é erro de planejamento
   aparecendo na tela. Não se funde com o 02 porque os nomes são do cliente
   e dizem coisas diferentes. */
.fr-bloco__lista--solo {
  grid-template-columns: minmax(0, 1fr);
}

/* ============================================================
   RESPONSIVO
   ============================================================ */
/* ---------- tablet ---------- */
@media (max-width: 1024px) {
  .fr-hero__titulo {
    max-width: min(74%, 620px);
  }

  .tec-hero__titulo {
    max-width: min(74%, 620px);
  }

  .fr-conexao__grid {
    grid-template-columns: 1fr;
    gap: var(--sp-7);
  }

  .fr-numeros__grade {
    gap: var(--sp-6) var(--sp-5);
  }
}

/* ---------- celular ----------
   A cabeça do bloco deixa de grudar: sticky numa coluna que virou linha
   cobriria o conteúdo que ela deveria rotular.

   A COMPOSIÇÃO NÃO GIRA - e eu tinha proposto que girasse. Girar quebra as
   duas coisas que fazem a cena funcionar. Primeiro a anatomia: o braço
   deitado é uma foto com luz e perspectiva de braço deitado, e a 90 graus
   ele lê como membro solto. Segundo, e pior, a ancoragem: as posições das
   duas mãos são calculadas a partir da ponta do dedo medida no eixo
   horizontal da imagem, e sob rotação essa conta deixa de valer - as duas
   pontas param de mirar o mesmo objeto.

   O que a tela estreita pede não é outra composição, é o mesmo
   enquadramento com corte mais fechado: as mãos ficam grandes, o vão
   encolhe, e os braços saem pelas duas bordas. */
@media (max-width: 767px) {
  .fr-hero {
    padding-top: clamp(104px, 15vh, 148px);
  }

  .fr-hero__titulo {
    max-width: none;
  }

  .fr-hero__palco {
    /* A mão maior que a tela é o ponto: o corte fechado é o que devolve
       escala aos dedos. Aqui ela não precisa da folga de 9% do desktop -
       com 92vw de largura e o vão em 30vw, ela já sangra muito além da
       borda mesmo depois de andar. */
    --vao: clamp(96px, 30vw, 140px);
    /* Era `min(92vw, 400px)`. Encolheu na mesma proporção do desktop
       (~4%), e aqui isso sai de graça: a largura da mão no celular não é
       calculada a partir do vão, então diminuí-la não afasta a ponta do
       dedo do objeto. O vão fica onde estava, e a ponta continua pousando
       quase no anel interno - que é a mordida do KV. */
    --mao-w: min(88vw, 383px);
    /* O anel cresce em relação à mão: na tela estreita o conjunto precisa
       ser maior que os 0.62 do desktop para não virar um selinho entre
       dois braços grandes. Era `0.82`, e desceu para `0.76` - os mesmos
       ~10% que o objeto encolheu lá. */
    --core-size: calc(0.76 * var(--mao-w));
    /* NEGATIVA no celular, e é de propósito: o anel externo começa
       ABAIXO do fim do texto, em vez de subir ao lado dele. Aqui não
       existe a faixa vazia à direita do botão que o desktop tem - o
       parágrafo ocupa a largura inteira -, então subir é sempre subir em
       cima de alguma coisa.

       E ela é uma RETA EM `dvh`, não um número. O orçamento da descida é
       a folga da linha `1fr` do palco - o que sobra da grade depois da
       mão feminina -, e essa folga é linear na altura da janela. Numa
       tela de 812 sobram 67px e a descida sai de graça: o hero fica em
       exatamente 100dvh. Numa de 667 não sobra nada, porque a mão
       sozinha já é mais alta que o resto da grade, e ali cada pixel de
       descida é um pixel a mais de rolagem antes da faixa de números.

       A reta liga os dois pontos medidos:

         667dvh -> +27px de subida (o objeto ainda encosta no texto)
         812dvh -> -13px          (começa abaixo dele, no fundo da folga)

       O piso de 20px de `--respiro` continua valendo e pega as telas
       muito baixas, onde a conta pediria menos que isso. */
    --subida: calc(210px - 27.5dvh);
  }

  .fr-numeros__grade {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  /* Sozinho numa coluna de meia tela, "Marca Inteira®" quebraria no meio
     da palavra. */
  .fr-numeros__item--nome {
    grid-column: 1 / -1;
  }

  .fr-mantra__linha {
    max-width: none;
  }

  /* ---------- /tecnologia no celular ----------
     A COREOGRAFIA RODA AQUI TAMBÉM, e o que muda é só GEOMETRIA.

     Duas versões anteriores erraram isto, e pelo mesmo motivo: eu tratei o
     gesto como coisa de ponteiro. Não é. Quem move a mão e quem abre a
     janela é a ROLAGEM - o input mais nativo que um celular tem. Do mouse é
     só o seguimento do ponteiro - a mão percorrendo 60% da distância até o
     cursor com teto de 680px - e ele tem guarda própria em `hero-janela.js`.
     Desligar o módulo abaixo de 767px tirava do celular o argumento inteiro
     da página e punha um squircle parado no lugar dele.

     Então nada aqui declara ESTADO. `--fechado`, `--entrada` e `--passo`
     continuam sendo do JavaScript, `.tec-alvo` continua aparecendo por
     `is-abrindo` e a capa azul continua valendo - o objeto volta a ser
     pequeno, que é exatamente o caso que a capa existe para resolver. O
     media query só reescreve os quatro números que a tela estreita não
     comporta. */
  .tec-hero__palco {
    padding-top: clamp(104px, 15vh, 148px);

    /* A ÓRBITA OCUPA A LARGURA ÚTIL INTEIRA, e é só isso que o estreito
       precisa declarar. `26vw` daria 97,5 numa tela de 375, e um objeto de
       25px é um selo. Com a largura útil, a órbita sai em 335 e `--lado`
       em 87 - o mesmo objeto do desktop, e o mesmo `--aneis-k`.

       De quebra o objeto acaba CENTRADO na tela: 20 + 87 × 1,425 = 144,
       objeto 144..231, centro em 187,5 - o meio exato de 375. O
       enquadramento que a tela estreita pedia, sem uma exceção escrita. */
    --aneis-w: calc(100vw - 2 * var(--margin));

    /* A JANELA ABRE EM TELA CHEIA, e este `100%` é a peça inteira.

       Era `min(64vw, 30dvh)`, uma tira de 240px no pé de uma tela de 812.
       O argumento de então era de ÁREA — 90.000px² contra os 7.500 do
       quadrado fechado, 12x — e ele continua verdadeiro; o que ele não
       resolvia é que uma tira de 240px num aparelho de 812 lê como
       divisor, não como abertura. Em tela cheia a abertura vai a
       304.500px², ou **40x** o quadrado fechado, e o gesto passa a ter no
       celular a escala que ele já tem no desktop.

       `100%` E NÃO `100dvh`, e a diferença é load-bearing. O recorte é
       ancorado no PÉ do palco (`--recorte-baixo: --folga-baixo`), e é
       assim que o quadrado fechado cai exatamente sobre `.tec-alvo`, que
       é irmã e mora em `bottom: var(--folga-baixo)`. Com `100%` a faixa
       mede o palco, seja ele 100dvh ou mais; com `100dvh` fixo, um palco
       mais alto (tela baixa, texto grande) descolaria o quadrado da mão
       que o clica.

       Dentro de `inset()` a porcentagem do topo resolve contra a ALTURA
       da própria caixa, então `--recorte-topo: calc(100% - folga - lado)`
       continua sendo a mesma conta de sempre — só que agora o `100%` é a
       tela, e não uma tira. */
    --alt: 100%;
  }

  /* A FAIXA DEIXA DE SER FAIXA e passa a ocupar o palco inteiro. Sem isto
     o `height: 100%` da janela não teria contra o que resolver: a caixa é
     absoluta e, com `bottom` só, a altura dela é a do conteúdo.

     Não cobre o texto: enquanto o recorte é pequeno, TUDO fora do quadrado
     está clipado — e região clipada não pinta nem recebe ponteiro. Quando
     o recorte finalmente abre, o texto já saiu (`--entrada`), e ele é
     `z-raised` de qualquer forma. */
  .tec-hero__baixo {
    inset: 0;
  }

  /* O RECORTE VAI BUSCAR A LUZ, e o número saiu de amostragem. Numa caixa
     de 375x812 o `cover` de um arquivo 1920x714 mostra 17,2% da largura do
     quadro — contra os 58% que a tira de 240px mostrava. O que se perde
     nesse corte foi medido, quadro a quadro em 160 colunas: o arquivo é
     um degradê escuro, luminância média 5 nos dois terços da esquerda e
     subindo para 25 no oitavo direito (picos de 175). Não há forma, marca
     nem texto no quadro — 1,2% dos pixels passam de 60.

     Centrado, o corte cairia na parte mais escura e a tela cheia seria
     uma tela preta. Em 88% ele enquadra 72,9%..90,1% do quadro, que é a
     rampa do escuro para a luz: a tela cheia ganha um foco. */
  .tec-janela video {
    object-position: 88% 50%;
  }

  .tec-mao {
    /* `clamp(64px, 7vw, 110px)` prendia no piso: a 375 dava 64px contra um
       objeto (`--lado`) de 87 agora - razão 0,74. A 1280x720 a mão mede
       89,6px contra um objeto de 86,4px (razão 1,04); `min(22vw, 96px)` dá
       82,5 a 375 - razão 0,95, a mais perto do desktop que dá sem estourar
       o teto de 96px. */
    --mao-w: min(22vw, 96px);
    /* 2vh e não 5: a tela é alta e o objeto está mais perto da borda de
       baixo, então os 5vh do desktop punham 33% da mão abaixo da dobra. */
    --dy: 2vh;
    /* 0.14 e não 0.30, e a conta é a razão mão/objeto: a 1280x720 a mão
       mede 100,7px contra um objeto de 86,4px (razão 1,17) e o pouso cabe
       na folga - a base fecha em 709,8 dentro de 720. A 375x667 a razão é
       mais apertada, 92,7 contra 87 (1,07): com 0,30 a base fecharia em
       652 (14,8px de folga); com 0,14 ela fecha em 638 (28,8px de folga) -
       o dobro de respiro na tela mais baixa. Com 0.14 ela pousa em
       545,5..638,2. */
    --pouso: 0.14;
  }

  /* O FILME FICA, E O KV SAIU. A regra antiga trocava um pelo outro aqui,
     e a troca tinha medição por trás: o `.mp4` tem média 10/255, e numa
     abertura pequena o que se via era uma faixa preta.

     O que mudou não foi o arquivo - foi o que corre por cima. A fita clara
     atravessa a faixa inteira em 22px de corpo, e é ela que carrega a luz e
     a frase. O custo é o `.mp4` de 1,3 MB voltar a descer em rede móvel;
     `preload="none"` continua valendo, então ele só desce quando o hero
     entra na tela. */

  .tec-hero__titulo {
    max-width: none;
  }

  .tec-frase__linha {
    max-width: none;
  }

  .fr-bloco {
    grid-template-columns: 1fr;
    gap: var(--sp-6);
  }

  .fr-bloco__cabeca {
    position: static;
  }

  .fr-servico__topo {
    gap: var(--sp-3);
  }
}

/* ============================================================
   MOVIMENTO REDUZIDO
   ============================================================ */
/* Desligado de verdade, e não atenuado. As mãos nascem no lugar, as três
   frases do mantra nascem nítidas, e o `will-change` sai junto: sem
   animação ele só custa uma camada de composição. */
@media (prefers-reduced-motion: reduce) {
  .js .fr-mao img,
  .js .fr-mantra__linha {
    will-change: auto;
  }
}

/* ============================================================
   /performance — METRIK
   ============================================================ */
/* Uma regra, e ela já estava prevista: o comentário de `--unidade` no topo
   desta folha declara que "a /performance já nasce resolvida". Preto sobre
   o lime #D3FF35 mede ~16:1, então o token companheiro é preto - o mesmo
   do amarelo, e o oposto do azul da Elleven. */
.frente-page--performance {
  --unidade: var(--c-lime);
  --sobre-unidade: var(--c-black);
}

/* A `.fr-conexao__grid--solo` FOI REMOVIDA. Ela existia porque esta seção
   não tinha o parágrafo que as irmãs têm, e a segunda célula ficava vazia
   - 612px de vão a 1440. Agora tem, e a grade é a das irmãs, sem exceção
   nenhuma para manter em dia. Fica o registro para o caso de alguém
   reencontrar a classe num rascunho antigo. */

/* ---------- o hero ----------
   Os quatro números que a cena inteira lê. Em repouso eles descrevem o
   ESTADO FINAL: linha desenhada, alvo aceso, texto inteiro. É o que a
   página mostra sem GSAP e com movimento reduzido - não se deixa um alvo
   vazio parado na tela, e não se cobra rolagem por uma animação que não vai
   acontecer. `hero-linha.js` só entra para começá-los em zero. */
.perf-hero {
  --subida: 1;
  --saida: 0;
  --acerto: 1;
  --halo: 0;

  /* O TETO É DUPLO, e o segundo termo é por ALTURA. Dirigido só por largura,
     o meio-lado do alvo (187px a 1440) passava do recuo do topo numa janela
     larga e BAIXA, e os anéis subiam por cima dos links do menu - o
     `.header` é `fixed` com fundo transparente no topo, então eles não
     sumiam atrás de barra opaca: sobrepunham os links de verdade. Medido
     antes: `folgaHeader` -26 a 1440x760 e -33 a 1366x700, +8 a 1280x800
     (raspando). Com `min(26vw, 34dvh)`: +32, +26 e +38, e +54 a 1440x900,
     +82 a 1920x1080. Subir só o termo de altura de `--cena-topo` não
     resolvia 1366x700 - o alvo cresce por largura e o recuo não.

     O PISO FICAVA FORA DO `min()`, E ISSO ANULAVA O TETO DE ALTURA acima.
     `clamp(180px, min(26vw, 34dvh), 420px)` é, letra por letra,
     `max(180px, min(26vw, 34dvh, 420px))`: abaixo de 529px de altura
     (34dvh < 180px) o piso engole o teto e a guarda deixa de existir.
     Medido a 844x390 antes: `alvoLado` 180 - exatamente o piso -, com o
     teto de altura pedindo 133. Escrito como `min(clamp(...), 34svh)` o
     teto de altura passa a ter veto SEMPRE, e o piso manda só no eixo
     horizontal, que é o eixo para o qual ele foi escrito. Onde os dois já
     concordavam (altura >= 529px) as duas formas dão o mesmo número em
     todos os casos, então nada mudou onde já estava certo.

     `svh` E NÃO `dvh` - ver o bloco de `--cena-topo` logo abaixo. */
  --alvo-lado: min(clamp(180px, 26vw, 420px), 34svh);
  --vao-alvo: clamp(24px, 4vw, 64px);
  /* A caixa da cena, recuada do header em cima e das margens nos lados. É o
     recuo superior que impede o alvo de subir por baixo do menu: o ponto
     final está a 25% DESTA caixa, não da tela.

     `svh` E NÃO `vh` NEM `dvh`, aqui, no recuo de baixo da cena e no lado
     do alvo - nos três números que DEFINEM O SISTEMA DE COORDENADAS. `vh`
     está fora porque ignora a barra de URL e a caixa nasceria errada. `dvh`
     estava aqui e é o que sai: com ele a caixa inteira relayouta enquanto a
     barra sobe e desce, e medido a 414x896 o topo da cena andava 56px
     (535 -> 591) no meio da rolagem. Em repouso isso não custa nada, mas
     `--subida` vai ser dirigido por rolagem, e um sistema de coordenadas
     que muda de tamanho durante a própria animação faz a ponta descolar do
     núcleo em pleno gesto. `svh` é a altura estável - a da barra visível -,
     então a caixa nasce no lugar e fica.

     A tela continua `min-height: 100dvh` de propósito: quem tem que
     acompanhar a barra é o retângulo que PREENCHE a janela, não o que
     mede a cena. E onde a barra de URL existe de verdade (celular e a
     banda) a cena é linha da grade e nem lê a altura da tela - o resíduo
     é o tablet em retrato, onde a cena ainda é `absolute` dentro de uma
     tela `dvh`. Ver o bloco da banda. */
  --cena-topo: clamp(96px, 13svh, 150px);

  position: relative;
  background: var(--c-offwhite);

  /* QUAL DOS DOIS REGIMES ESTÁ NO AR, como número, para o módulo poder
     perguntar à FOLHA em vez de repetir as media queries em JavaScript.

     1 = a cena é `absolute` cobrindo a tela e o texto está POR CIMA dela.
     0 = a cena é a linha `1fr` da grade e o texto está ACIMA dela, em fluxo.

     O bloco dos três retângulos, mais abaixo, é o único lugar que zera isto
     - as mesmas media queries que já decidem onde a cena mora. Sem esta
     variável o módulo teria uma cópia daquela lista, e duas listas que
     precisam concordar são uma que vai divergir. */
  --palco: 1;
}

/* A ALTURA EXTRA É DAQUI, e só daqui. `is-subindo` entra pelo módulo, e só
   quando há GSAP e o sistema não pediu menos movimento. Sem ela a seção tem
   a altura de uma tela e os quatro números ficam no estado final: não se
   cobra 1,3 tela de rolagem por uma animação que não vai acontecer.

   230 = 100 do palco + 100 da subida + 30 da pausa.

   ESTES NÚMEROS TÊM GÊMEO em `hero-linha.js`: `TRECHO` é 1.0 e `PAUSA` é
   0.3. Mexer em um sem o outro deixa o acerto acontecendo depois de o
   `sticky` já ter soltado - o alvo acende fora da tela.

   `svh` E NÃO `dvh`, e isto é desvio consciente do brief da tarefa. Com
   `dvh` a seção CRESCE quando a barra de URL recolhe: 130dvh de curso de
   `sticky` viram ~78px a mais de documento num tablet com barra de 60px, e
   esses 78px deslocam TODO gatilho abaixo do hero sem que nenhum refresh
   aconteça (a remedição desta base só dispara em mudança de LARGURA). É o
   mesmo argumento que trocou `--cena-topo` e `--alvo-lado` por `svh` na
   rodada passada, aplicado à única medida que ainda ia respirar.

   O PREÇO É A PAUSA ENCOLHER ONDE A BARRA EXISTE, E ELA ENCOLHE EM DOBRO -
   a conta escrita aqui antes estava errada por um fator 2. O curso do
   `sticky` é `230svh - 100dvh` (a tela mede `min-height: 100dvh`) e o
   escovado consome `innerHeight`, que é o MESMO `100dvh`: a pausa é
   `230svh - 2 x 100dvh`, não `230svh - 100dvh - 100svh`. Com a barra
   recolhida isso vale `30svh - 2 x barra`, e o limite em que ela ainda é
   positiva é a barra valer menos de 15% da tela - não 30%, como estava
   escrito. Nenhum aparelho de hoje chega perto (a barra do iPad em retrato
   mede ~5% e a de um Android em retrato ~10%), então não há falha prática;
   o que havia era uma margem declarada 2x otimista.

   ONDE A CENA É LINHA DA GRADE ISTO NÃO ENTRA - ver o bloco dos três
   retângulos, que devolve `height: auto`. Lá a tela é mais alta que a
   janela, e uma seção alta faria o `sticky` grudar em `top: 0` desde a
   rolagem zero: a cena, que mora abaixo do texto, tocaria a animação
   inteira fora da janela. */
.perf-hero.is-subindo {
  height: 230svh;
  --subida: 0;
  --saida: 0;
  --acerto: 0;
  --halo: 0;
}

/* Seção alta com caixa `sticky`, e não `pin`. Sticky não cria `.pin-spacer`
   - o espaçador de largura fixa que, sem remedição no `resize`, é a origem
   dos 416px de rolagem horizontal medidos na home -, não custa uma
   ScrollTrigger, e volta a ser documento comum sem JavaScript. Mesma
   escolha do `mantra.js` e do `hero-janela.js`. */
.perf-hero__tela {
  position: sticky;
  top: 0;
  min-height: 100dvh;
  display: grid;
  /* Texto em cima, cena no que sobra - E A SEGUNDA LINHA TEM DOIS DONOS
     POSSÍVEIS. Onde existe coluna livre à direita do H1 a cena é `absolute`
     e atravessa as duas linhas, porque o alvo mora nessa coluna e a linha
     vem de baixo; onde a coluna livre não existe (celular e a banda baixa)
     a cena É esta linha `1fr`, e aí "a cena começa depois do texto" é
     layout e não medição. A linha continua declarada nos dois casos: é ela
     que faz a tela crescer quando o piso de altura da cena não cabe na
     janela. */
  grid-template-rows: auto 1fr;
  /* O RECUO DE CIMA VIROU `svh` JUNTO COM A CENA, e por consequência do
     parágrafo acima: onde a cena é a linha `1fr`, quem define o topo do
     sistema de coordenadas é o FIM DO TEXTO, então o ritmo vertical do
     bloco de texto passou a ser parte da caixa. Com `dvh` aqui e nas duas
     margens do texto, o topo da cena andava ~12px enquanto a barra de URL
     subia (14dvh dá 7,8 desses 12 a 414px de largura) - pouco, mas é
     movimento de graça no meio de uma animação de rolagem. O recuo de
     BAIXO fica em `dvh`: ele está abaixo da cena e não define coordenada
     nenhuma. */
  padding-top: clamp(112px, 14svh, 156px);
  padding-bottom: clamp(var(--sp-6), 5dvh, var(--sp-8));
  /* Corta só o que tem que sair pela borda. Nada aqui deveria sair - ver o
     checkpoint de vãos da Task 6 -, e o `clip` é a rede, não o plano. */
  overflow: clip;
}

.perf-hero__texto {
  position: relative;
  z-index: var(--z-raised);
  /* O texto sai enquanto a linha sobe. `--saida` é escrito pelo módulo; em
     repouso ele é 0 e nada disto acontece.

     E SÓ ACONTECE ONDE O TEXTO ESTÁ POR CIMA DA CENA. Apagá-lo ali REVELA
     a cena, que é a coreografia pretendida - a tela se esvazia enquanto a
     linha ocupa. Onde a cena é linha da grade o texto está ACIMA dela em
     fluxo: apagá-lo deixaria um buraco do tamanho do bloco de texto, e
     `translateY` não devolve o espaço, então a cena não subiria para
     ocupá-lo. O escopo não está aqui - está no módulo, que só DIRIGE
     `--saida` quando `--palco` é 1. Um consumidor, um produtor, e a folha
     decidindo os dois.

     `svh` E NÃO `vh`: era o último `vh` nu da caixa, e agora é decisão. Os
     dois são estáveis durante a rolagem (só `dvh` respira), então o que se
     ganha não é estabilidade - é a caixa inteira falar uma unidade só.
     Ninguém precisa parar para descobrir por que este número mede contra
     uma tela diferente da que `--cena-topo` mede. */
  opacity: calc(1 - var(--saida));
  transform: translateY(calc(var(--saida) * -6svh));
}

/* Escala própria, menor que `--fs-display-xl`, pelo mesmo motivo das irmãs:
   justificado em três linhas lê como key visual; em cinco, lê como parede.
   A largura é PERCENTUAL e não `ch` - justificação depende da largura da
   caixa, e `ch` num container resolve contra a fonte do container.

   É ESTA MEDIDA QUE CRIA O VÃO PARA O ALVO. Os 64% deixam 36% de coluna
   livre à direita, e é lá que o alvo mora no desktop. */
.perf-hero__titulo {
  font-size: clamp(1.9rem, 0.9rem + 2.9vw, 3.5rem);
  max-width: min(64%, 900px);
}

/* Os 46ch NÃO SÃO DECORAÇÃO DE MEDIDA aqui: eles são a caixa mais larga do
   bloco de texto abaixo de ~900px de largura - mais larga que o H1, que
   mede 64% -, e é contra ELES que a coluna livre do alvo é medida. Ver o
   bloco dos três retângulos. */
.perf-hero__sub {
  max-width: 46ch;
  margin-top: clamp(var(--sp-5), 4svh, var(--sp-7));
  color: var(--fg-muted);
}

/* `width: max-content`, E ELE NÃO É COSMÉTICO - é o que faz a caixa dizer a
   verdade sobre onde o botão está. Medido a 1440x900: o `div` saía com
   1310px de largura para um CTA de 298px, ou seja, 1012px de caixa vazia
   atravessando exatamente a coluna livre à direita onde o alvo mora. O
   checkpoint de vãos amostra a curva contra as caixas do texto, e contra
   essa faixa invisível ele acusava -508px de cruzamento num ponto (1184,
   295) que está a 830px do botão. Com a caixa do tamanho do que ela
   desenha, a medida volta a medir o CTA.

   As irmãs herdam o `div` de largura cheia porque lá nada passa por trás
   dele; aqui passa. */
.perf-hero__acoes {
  margin-top: clamp(var(--sp-6), 4svh, var(--sp-7));
  width: max-content;
  max-width: 100%;
}

/* ---------- a cena: o sistema de coordenadas comum ----------
   Uma caixa vazia. Ela não desenha nada por si: existe para que a linha e o
   alvo tenham o MESMO retângulo de referência. É a caixa que faz uma fração
   significar a mesma coisa para os dois.

   São DUAS frações, uma por regime, e o `y` é o mesmo nos dois:
   86% x 25% no `--larga` (>=768px) e 72% x 25% no `--alta` (<768px).

   O recuo inferior usa `svh` pelo mesmo motivo de `--cena-topo`: ele define
   a caixa, e caixa que define coordenada não pode respirar com a barra de
   URL. */
.perf-cena {
  position: absolute;
  inset: var(--cena-topo) var(--margin) clamp(var(--sp-7), 8svh, var(--sp-9)) var(--margin);
  pointer-events: none;
}

.perf-linha {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Uma por orientação. Não é gate por largura: é geometria, e geometria mora
   na folha. O gesto é idêntico nos dois - o que muda é a inclinação dos
   balanços, que numa tela alta precisa ser maior para a subida ainda ler
   como subida. */
.perf-linha--alta { display: none; }

/* `1 3` E NÃO `1`. Com um valor só o vão é igual ao traço, e o padrão se
   repete: o traço seguinte começa em 1, ou seja, ENCOSTA no fim do
   traçado. Com `stroke-linecap: round`, esse encontro pinta a meia-lua do
   remate bem em cima do alvo - um ponto preto de 2,5px parado ali desde o
   primeiro quadro. Medido: com `--subida: 0`, `isPointInStroke` acusava o
   traçado aceso a partir de 99,9%; com o vão em 3, nada é desenhado em
   nenhuma fração. O vão só precisa ser MAIOR que o traçado inteiro para
   nenhuma repetição alcançá-lo; 3 é folgado e legível. */
/* A COR E' PEDIDO DO CLIENTE, E ELA TEM UM PRECO MEDIDO. #96b330 sobre o
   chao claro (#f1f1f1) mede **2,12:1**, medido no navegador; o preto que
   estava aqui media 18,6:1. Os 3:1 que a WCAG 1.4.11 pede valem para objeto grafico
   NECESSARIO ao entendimento, e esta linha nao e': ela e' `aria-hidden`,
   nao carrega informacao que o texto ao lado nao diga, e o alvo continua
   sendo lido pelo nucleo e pelos aneis. Fica o numero registrado - quem
   quiser a mesma familia passando em 3:1 precisa descer o tom.

   O TOM MORA NUM SO' LUGAR, e e' este. O `glow` continua no lime cheio
   (`--c-lime`): sao duas camadas do mesmo gesto, e a de baixo e' luz
   difusa, nao traco. */
.perf-linha {
  --perf-traco: #96b330;
}

.perf-linha__traco {
  stroke: var(--perf-traco);
  stroke-width: 2.5;
  opacity: 0.88;
  stroke-dasharray: 1 3;
  stroke-dashoffset: calc(1 - var(--subida));
}

.perf-linha__glow {
  stroke: var(--c-lime);
  stroke-width: 10;
  opacity: 0.55;
  filter: blur(9px);
  stroke-dasharray: 1 3;
  stroke-dashoffset: calc(1 - var(--subida));
}

/* A CONTA DO SINAL: com `stroke-dashoffset: o`, o padrão começa na posição
   `-o` do traçado. Para o único segmento aceso ficar em `t`, `o = -t`. Daí
   o `calc(-1 * ...)` aqui e o `calc(1 - ...)` nos dois acima - eles fazem
   coisas opostas com a mesma variável, e é assim que um traço que CRESCE e
   um ponto que ANDA saem do mesmo número.

   Some no acerto: a ponta chegou, e manter o ponto aceso ao lado de um
   núcleo aceso deixaria dois lime na tela disputando o mesmo instante. */
.perf-linha__ponta {
  stroke: var(--c-white);
  stroke-width: 7;
  stroke-dasharray: 0.004 1;
  stroke-dashoffset: calc(-1 * var(--subida));
  opacity: calc(1 - var(--acerto));
}

/* ---------- o alvo ----------
   A MESMA FRAÇÃO DA CENA EM QUE O PATH DO REGIME TERMINA. Não são dois
   números afinados um contra o outro: são o mesmo número escrito duas vezes.

   Depois do split do celular são DUAS frações em CINCO lugares:

     --larga (>=768px)  86% x 25%   `left: 86%` aqui  +  `860 140` no
                                    `d` dos TRÊS paths do SVG `0 0 1000 560`
     --alta  (<768px)   72% x 25%   `left: 72%` no `@media`  +  `345.6 155`
                                    no `d` dos TRÊS paths do SVG `0 0 480 620`

   O `y` é 25% nos dois: 140/560 = 155/620. Mexer num lugar sem os outros
   quatro descola a ponta do núcleo que ela acerta.

   O TESTE QUE PROVA A INVARIANTE é `pontaNoNucleo` do checkpoint de vãos:
   distância em px entre o ponto final do path e o CENTRO DO NÚCLEO. Ele
   vale 0 nos dezesseis viewports da matriz - inclusive nos cinco da banda,
   onde a caixa deixou de ser `absolute` e virou linha da grade -, e tem que
   continuar 0. Se subir, algum dos cinco lugares andou sozinho.

   E O CHECKPOINT GANHOU TRÊS MEDIDAS, porque as antigas eram cegas para o
   que a rodada passada quebrou: `sangraBaixo` (o alvo saindo por baixo da
   caixa com `overflow: clip`), `cenaAltura` contra o lado do alvo (a cena
   espremida a 25px para um alvo de 118, com os sete critérios de então
   passando) e `vaoTextoAlvo` (o vão contra a caixa de texto MAIS LARGA, e
   não contra o H1, que abaixo de ~900px é a estreita das três). */
.perf-alvo {
  position: absolute;
  left: 86%;
  top: 25%;
  width: var(--alvo-lado);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  display: grid;
  place-items: center;
}

.perf-alvo .fr-aneis {
  width: 100%;
  height: 100%;
}

/* ---------- o pulso da órbita ----------
   Os anéis respiram enquanto o alvo espera, e continuam respirando depois
   que o núcleo acende. É o que faz um alvo parado ler como alvo e não como
   ornamento - e é a razão de ele poder abrir mão do contorno do núcleo:
   sem o pulso, um squircle vazio no canto da tela não pede nada.

   MORA NO `<svg>`, NÃO NOS `rect`. Os `rect` já têm dono: o GSAP escala os
   quatro no instante do acerto (`hero-linha.js`) e na abertura da janela
   (`hero-janela.js`), e transformação declarada na folha some quando ele
   assume o elemento - armadilha 12 do HANDOFF. O `<svg>` que os contém não
   tem `transform` em lugar nenhum, nem em CSS nem em JS: a propriedade
   está livre, e as duas animações compõem em vez de disputar.

   4% de amplitude e 2,6s de ciclo. Amplitude maior faz a órbita mudar de
   tamanho, e o tamanho dela é uma razão medida contra o objeto que ela
   cerca (`--aneis-k`). O que se quer aqui é sinal de vida, não escala. */
@keyframes fr-pulso {
  from,
  to   { transform: scale(1); }
  50%  { transform: scale(1.04); }
}

.perf-alvo .fr-aneis,
.tec-alvo .fr-aneis {
  animation: fr-pulso 2.6s var(--ease-expand) infinite;
}

/* Desligado de verdade, não atenuado - a regra do sistema. */
@media (prefers-reduced-motion: reduce) {
  .perf-alvo .fr-aneis,
  .tec-alvo .fr-aneis {
    animation: none;
  }
}

/* O NÚCLEO NÃO EXISTE ATÉ SER ACERTADO. Ele tinha um contorno preto de 1px
   que o desenhava vazio, esperando - e o vazio competia com os anéis, que
   já são o alvo. Sem o contorno, o que marca o lugar é a órbita pulsando, e
   o lime só aparece quando a linha chega: `--acerto` sai de 0 no limiar de
   98% da subida, e é ele que pinta o fundo. Um lime que nasce do nada é o
   pagamento; um lime que preenche um contorno já desenhado é uma barra de
   progresso terminando.

   O halo é o que abre e fecha no instante do impacto. */
.perf-nucleo {
  position: absolute;
  width: calc(var(--alvo-lado) * 0.26);
  aspect-ratio: 1;
  border-radius: var(--r-squircle);
  background: color-mix(in srgb, var(--unidade) calc(var(--acerto) * 100%), transparent);

  /* O HALO É FRAÇÃO DO ALVO, e não 90px fixos. Era o último número em pixel
     absoluto de uma cena em que todo o resto já escala: o alvo varia de 82px
     (844x390) a 367px (1920x1080), e um blur constante de 90px valia um
     quarto do halo do desktop no aparelho e uma vez e meia o próprio alvo no
     celular deitado. O halo é o brilho DAQUELE núcleo - ele tem que ser
     proporcional a ele.

     0,3 E 0,06 SAEM DO PALCO, que é onde o halo estava certo e é o momento
     mais visível da página em tela grande. A 1440x900 o alvo mede 306px:
     0,3 x 306 = 92px de blur e 0,06 x 306 = 18,4 de spread, contra os 90 e
     18 de antes - menos de 2% de diferença, e para MAIS, nunca para menos.
     A razão 5:1 entre blur e spread é a de antes, escrita como fração.

     E A SOMA É O QUE FAZ O CRITÉRIO FECHAR SOZINHO. O núcleo mede
     `0,26 x lado` e é concêntrico ao alvo, então o topo dele está a
     `0,37 x lado` do topo dos anéis. DUAS CONTAS, e vale ter as duas
     escritas porque quem mexer no `0,26` acima ou no `0,3` aqui precisa
     saber de qual margem está gastando:

       . A COTA SUPERIOR, `spread + blur`, que é como o defeito foi
         reportado e como a tabela do relatório mede: o halo alcançaria
         `topoDoAlvo + (0,37 - 0,3 - 0,06) x lado`. `0,36 < 0,37` fecha,
         com margem de apenas **0,01 x lado** - 0,8px a 844x390.

       . O ALCANCE REAL, que é maior. Pela spec (CSS Backgrounds & Borders
         §7.1.1) a transição da sombra tem largura igual ao blur e é
         CENTRADA na borda, então ela se estende `spread + blur/2`, não
         `spread + blur`: `0,06 + 0,15 = 0,21 x lado`, e a margem verdadeira
         é **0,16 x lado** - 13px a 844x390 e 49 a 1440x900.

     Ou seja: a desigualdade acima fecha como COTA, e a conclusão é mais
     verdadeira do que ela afirma. Nos dois casos O HALO NÃO PASSA DO TOPO
     DA PRÓPRIA CAIXA DOS ANÉIS, e é isso que importa: ele herda a folga que
     `FOLGA_CLIMAX` já garante para o alvo, e a pergunta "o halo invade a
     faixa do menu?" deixa de precisar de medição por viewport. Medido pela
     cota superior, que é a leitura pessimista: `folgaHalo` de +25 a +86 nos
     sete viewports, contra -17 a -53 nos cinco de grade com 90px fixos.

     Mexer em 0,26 lá em cima muda o 0,37 e portanto a margem: os dois
     números são o mesmo argumento. */
  box-shadow: 0 0 calc(var(--halo) * var(--alvo-lado) * 0.3)
              calc(var(--halo) * var(--alvo-lado) * 0.06)
              color-mix(in srgb, var(--c-lime) 65%, transparent);
}

/* ---------- quando a coluna livre não existe: a cena vira linha da grade ----------
   O regime `--larga` inteiro assume DUAS coisas, e elas são a mesma: que
   sobra coluna à direita do texto para o alvo morar, e que sobra altura
   para a curva passar por BAIXO do bloco de texto antes de subir até ele.
   A curva sobe da esquerda para a direita, então a folga contra o texto é
   medida na quina inferior direita da caixa de texto mais larga - e o `y`
   da curva ali é uma FRAÇÃO da altura da cena, enquanto a quina é um
   número CONSTANTE em pixel. É o mesmo defeito que o `66vh` do celular
   tinha, no regime em que ninguém tinha olhado.

   Medido, `folgaTexto` negativa querendo dizer "a linha passa por trás do
   texto", com a fração da largura da cena em que a quina cai:

      768x600   -117   sub, fração 0,765 -> curva já a 0,437 da altura
      844x390   -102   sub, 0,705 -> 0,517
     1024x600     -3   sub, 0,54  -> 0,644
     1280x650    +31   btn, 0,27  -> 0,873
     1280x600     -7   btn, 0,27  -> 0,873

   O que muda entre eles não é a altura só: é QUANTO DA CENA O TEXTO OCUPA.
   O subtítulo mede 46ch fixos (~520px) e o H1 mede 64% da cena, então
   abaixo de ~900px de largura quem manda na quina é o subtítulo, a fração
   cresce até 0,765 e ali a curva já está na metade da subida. Resolvendo
   `T + f*(H - T - B) > fim do texto` largura a largura, a altura mínima em
   que `--larga` funciona é ~820px a 768 de largura, ~700 a 900, ~560 a
   1024 e ~520 de 1280 pra cima. Não é uma constante: é uma curva.

   E NÃO ADIANTA SUBIR `--cena-topo` PARA COMPRAR ESSA FOLGA - foi a
   primeira tentativa, e ela se morde. Cada pixel de recuo devolve só
   (1 - f) pixel de folga e tira 1 pixel inteiro de altura de cena: a
   768x600 os -117 pediam +208px, o que deixava 248px de cena para um alvo
   de 200 - `cenaAltura` 1,24x o alvo, ou seja, o alvo pendurado para fora
   da própria caixa. A 844x390 nem isso resolve: o texto termina a 441 de
   uma tela de 473, e mesmo com `T = 0` a curva passa a 378 na abscissa do
   botão. NÃO EXISTE SOLUÇÃO com a cena sobreposta ao texto ali.

   Então a cena desce para baixo do texto - o mesmo movimento que o celular
   já fazia, só que agora POR LAYOUT: ela vira a linha `1fr` que a grade da
   tela sempre teve, e "começar depois do texto" deixa de ser um número
   medido contra o H1 de hoje. É isto que mata o `535px`.

   ABAIXO DE 900px DE LARGURA A CONTA NEM CHEGA NA ALTURA: o alvo não cabe
   na coluna livre, em altura nenhuma. A coluna livre é o que sobra à
   direita da caixa de texto MAIS LARGA, e abaixo de ~900px essa caixa é o
   subtítulo (46ch, ~520px), não o H1 (64% da cena). Medido a 768x1024, que
   passava nos sete critérios antigos: coluna livre 162px para um alvo de
   200 - e o alvo, de fato, invade a caixa do subtítulo em 34x31px. O
   checkpoint não via porque `vaoH1Alvo` mede contra o H1, e o H1 é a caixa
   ESTREITA nessa faixa. Daí `vaoTextoAlvo`, que mede contra a mais larga
   das três.

   OS TRÊS RETÂNGULOS, então: até 899px de largura a cena desce sempre, e
   isso não depende de altura nenhuma nem do comprimento do H1 - depende de
   o alvo ser mais largo que a coluna. Medido a 900x760, o primeiro ponto
   em que ela cabe: `vaoTextoAlvo` +40 para um `--vao-alvo` de 36, ou seja,
   900 é a largura em que o vão pedido aparece. De 900 a 1023 o que falta é
   altura: a curva precisa de ~690px (medido +47 de `folgaTexto` a 900x760,
   com queda de ~0,45px por pixel de janela) e `779` cobre com duas linhas
   de H1 de margem. De 1024 a 1279 ela precisa de ~610 (medido +24 a
   1024x650) e `699` cobre. De 1280 pra cima a quina que aperta passa a ser
   a do botão e o lever barato resolve (ver o último bloco).

   OS DOIS ÚLTIMOS NÚMEROS SÃO DEPENDÊNCIA DA COPY - registrado aqui de
   propósito: 779 e 699 saem de onde a curva passa por baixo do fim do
   texto com o H1 de hoje, que `performance/index.php` marca como pendente
   de validação, e por isso os dois levam ~60px de margem sobre a exigência
   medida - uma linha de H1 vale ~26px. O 899 não: ele compara o alvo com a
   medida tipográfica de 46ch, que é do sistema e não da frase. Se a copy
   mudar, remedir os dois - e errar para o lado da grade, que é o layout
   que não depende de copy nenhuma. */
@media (max-width: 899px),
       (min-width: 900px) and (max-width: 1023px) and (max-height: 779px),
       (min-width: 1024px) and (max-width: 1279px) and (max-height: 699px) {
  /* `inset: auto` desfaz o `absolute` E o recuo de baixo do bloco base. A
     margem lateral vira `margin-inline` para a caixa continuar com
     EXATAMENTE a mesma largura de antes - a fração do alvo (86% ou 72%) é
     medida contra ela, e mexer na largura descolaria a ponta do núcleo.

     O PISO É 2,5 VEZES O LADO DO ALVO, e o 2 dentro dele é geometria, não
     gosto: o centro do alvo está a 25% da cena, logo o topo do alvo está a
     0,25*H - S/2 do topo da caixa, e isso só é >= 0 quando H >= 2*S.
     Abaixo de 2x o alvo pendura para fora do próprio sistema de
     coordenadas - a 0,21x (a cena de 25px medida a 360x560, para um alvo
     de 118) a "linha que sobe" virou um risco e o alvo saía 40px pela
     borda de baixo da tela com `overflow: clip`. Exatamente 2x deixaria
     `alvoAbaixoDoBotao` em 0 e o critério pede > 0; 2,5x põe um oitavo do
     diâmetro do alvo entre o botão e ele e deixa a subida da curva (68,5%
     da altura da cena) valer 1,7 diâmetro. 3x compraria mais um oitavo por
     mais meio alvo de altura de hero em TODO telefone, empurrando o alvo
     ainda mais para baixo da dobra nos curtos.

     A tela cresce junto, e é isso mesmo: a 360x560 o texto sozinho ocupa
     469 dos 560px da janela, e não existe layout que ponha uma cena legível
     abaixo dele dentro da dobra. O hero passa a medir ~1,1 tela nos
     telefones altos e ~1,4 nos curtos, e o que era corte vira rolagem. */
  .perf-cena {
    position: relative;
    inset: auto;
    margin-inline: var(--margin);
    min-height: calc(var(--alvo-lado) * 2.5);
  }

  /* O REGIME, DITO EM VOZ ALTA. É esta linha que o módulo lê para saber que
     não há palco - e é por ela existir que a lista de retângulos acima
     continua sendo escrita UMA vez. */
  .perf-hero {
    --palco: 0;
  }

  /* E A ALTURA EXTRA SAI, com a mesma lista. Medido, a tela mede de 1,02 a
     1,74 vez a janela aqui (1,02 a 768x1024, 1,17 a 375x812, 1,30 a
     1024x650, 1,42 a 360x560, 1,74 a 844x390). Com a seção a 230svh o
     `sticky` da tela gruda em `top: 0` desde a rolagem ZERO e só solta
     ~1,3 tela depois; enquanto grudada, só os primeiros 100dvh da tela
     estão dentro da janela - e a cena, aqui, mora ABAIXO do texto, que
     sozinho já ocupa 441 dos 390px da janela a 844x390. A animação
     inteira tocaria fora da tela e o alvo só apareceria depois de o pin
     soltar.

     Encolher a tela para caber em 100dvh não é a saída: a rodada passada
     já mediu que não existe layout sobreposto a 844x390. A saída é não
     pinar. Sem altura extra a tela não tem curso de `sticky` nenhum - ela
     volta a ser documento comum -, e o módulo escova `--subida` pela
     TRAVESSIA da cena pela janela: medido, a cena cabe inteira na janela
     nos cinco viewports acima (205 de 390, 426 de 812, 341 de 650, 294 de
     560, 450 de 1024).

     O FIM DA FAIXA É O ALVO E NÃO A CENA - ver `FOLGA_CLIMAX` em
     `hero-linha.js`, e o clímax que acendia dentro da faixa do menu. Com o
     fim posto pelo alvo, a faixa vale de 103 a 473px de rolagem.

     E O PISO DE 2,5x O ALVO GARANTE QUE A CENA CABE NA JANELA, mas NÃO com
     o número que estava escrito aqui. O teto de 21svh só existe nos dois
     blocos abaixo, e eles usam `min-width: 768px` e `max-width: 767px`, que
     não se encostam em largura fracionária: na fresta entre 767 e 768 o
     teto volta a ser o `34svh` do bloco base e o piso da cena vale
     2,5 x 0,34h = 85svh. A conclusão sobrevive - 85svh ainda é menos que
     uma tela -, o "52,5svh" que estava escrito aqui não. */
  .perf-hero.is-subindo {
    height: auto;
  }
}

/* A BANDA - de 768 a 899 de largura em qualquer altura, e até 1279 quando a
   janela é baixa - fica com o LAYOUT do celular e a GEOMETRIA do desktop:
   o H1 continua a 64%, o alvo continua a
   86% e o path continua o `--larga`. As caixas que a banda produz vão de
   1,5:1 (768x1024) a 3,7:1 (844x390), e o `--larga` é desenhado em 1,79:1:
   distorção de 0,86x a 2,1x. O path do celular é desenhado em 0,77:1 e nas
   mesmas caixas distorceria de 2x a 4,8x - é por isso que o par NÃO é o do
   celular aqui, mesmo o layout sendo. O par continua sendo um número só
   dentro do regime; o que mudou foi só onde a caixa começa.

   A pior das caixas é a de 844x390 (3,7:1, distorção 2,1x), e ela é o preço
   de um celular deitado em que o texto sozinho já mede 441 dos 390px da
   janela: ali não existe caixa boa, só caixa possível.

   O lado do alvo é o que precisa de teto novo aqui: `34svh` daria 200px a
   768x600 e um piso de cena de 500px, ou seja, um hero de 1,7 tela. `21svh`
   é o mesmo termo que o celular usa e pelo mesmo motivo - o que sobra de
   altura depois do texto é o que existe para gastar. */
@media (min-width: 768px) and (max-width: 899px),
       (min-width: 900px) and (max-width: 1023px) and (max-height: 779px),
       (min-width: 1024px) and (max-width: 1279px) and (max-height: 699px) {
  .perf-hero {
    --alvo-lado: min(26vw, 21svh, 180px);
  }
}

/* DE 1280 PRA CIMA a coluna livre é larga o bastante e a quina que aperta
   passa a ser a do BOTÃO, na fração 0,27 da cena, onde a curva já está a
   87% da altura - e aí o lever mais barato resolve sozinho. Zerar o recuo
   de baixo devolve f*B de folga e ainda dá B pixels de cena. Medido a
   1280x600: `folgaTexto` -7 com o recuo de 48px, e +35 sem ele. Acima de
   700px de altura o recuo fica: lá ele é respiro, não falta espaço. */
@media (min-width: 1280px) and (max-height: 700px) {
  .perf-cena {
    bottom: 0;
  }
}

@media (max-width: 767px) {
  /* O H1 vira largura cheia e a coluna livre à direita deixa de existir. A
     cena inteira desce para baixo do texto, e o alvo desce com ela - o
     ponto final continua na MESMA fração da cena que o alvo, então nada
     aqui precisa saber que o alvo se moveu. É esse o motivo de a caixa
     existir.

     O `--cena-topo` DAQUI MORREU, e com ele o `535px`. Ele era um piso em
     pixel para guardar um obstáculo em pixel - "a cena começa depois do
     texto" -, e o raciocínio estava certo: o fim do texto quase não varia
     (469 a 360x560, 472 a 375x667, 480 a 375x812, 487 a 390x844, 499 a
     414x896), e uma fração pura da altura não acompanha um número
     constante. O que estava errado era o piso não ter TETO: quando a
     janela fica mais baixa que ~535 + alvo, quem é espremido é a cena, não
     o alvo. Medido a 360x560 - que é o que um Android 360x640 mostra ao
     abrir, com a barra de URL levantada: `cenaTopo` 535, `cenaAltura` 25,
     `alvoLado` 118, alvo sangrando 40px por baixo da tela. Os sete
     critérios de então passavam todos, porque nenhum deles olhava para a
     altura da cena nem para a borda de baixo.

     E o número era, além disso, uma DEPENDÊNCIA DA COPY: 535 saiu de um H1
     que `performance/index.php` marca como PENDENTE de validação com o
     cliente. Um H1 uma linha mais longo empurra a faixa 469-516 e o piso
     para de vencer, em silêncio.

     Os dois problemas morrem no mesmo movimento: a cena virou a linha
     `1fr` da grade (ver o bloco acima), então "começa depois do texto" é
     LAYOUT - não sobra número para envelhecer com a copy - e o piso de
     altura passou a ser um múltiplo do próprio alvo, que é a única coisa
     contra a qual "cena pequena demais" quer dizer alguma coisa. */
  .perf-hero {
    /* O ALVO PRECISA DE TETO POR ALTURA AQUI, e por um motivo mais duro que
       no desktop: a 360x560 o texto ocupa 469 dos 560px, e um alvo de 180px
       não cabe abaixo dele de jeito nenhum. O piso de 180px de
       `--alvo-lado` era o primeiro lugar para olhar, e é o que sai daqui.

       `48vw` faz o trabalho que o piso fazia (26vw dá 94-108px nesta faixa,
       pequeno demais), `21svh` é o teto que encolhe o alvo onde não há
       altura, e `180px` é o teto de HOJE: o alvo nunca fica maior do que
       já era, só menor onde a janela não comporta. Resolve 118px a 360x560,
       140 a 375x667, 170 a 375x812, 177 a 390x844 e 180 a 414x896.

       `svh` e não `dvh`: com `dvh` o lado do alvo - e portanto o piso de
       altura da cena, que é 2,5x ele - mudava enquanto a barra de URL subia
       e descia, e o sistema de coordenadas mudaria de tamanho no meio da
       animação de `--subida`. */
    --alvo-lado: min(48vw, 21svh, 180px);
  }

  /* 72%, e o `x` final dos paths desceu junto - ver o comentário do hero no
     HTML. Com 86% o alvo sangrava 23px pela direita a 375px. */
  .perf-alvo {
    left: 72%;
  }

  .perf-hero__titulo {
    max-width: none;
  }

  .perf-linha--larga { display: none; }
  .perf-linha--alta  { display: block; }
}

/* ---------- as três camadas ----------
   O cartão herda tudo de `.fr-servico`: moldura de contorno, raio com teto
   em pixel, hover para borda preta, ícone no squircle, caixa branca do
   gatilho. `.perf-camada` acrescenta UMA coisa - a grade 4fr/7fr que a
   cabeça pegajosa levou embora, agora por dentro do cartão e sem `sticky`. */
.perf-camadas {
  display: grid;
  gap: clamp(var(--sp-6), 5vh, var(--sp-8));
  padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
}

.perf-camada {
  /* TRES PECAS EM DUAS COLUNAS, com lugar escrito. A ordem do DOM e'
     nome -> resumo -> escopo (que e' a ordem de leitura e a do celular);
     a grade poe o nome e o escopo na coluna larga e o resumo na estreita,
     atravessando as duas linhas.

     `4fr/7fr` e' a proporcao que a cabeca pegajosa levou embora quando
     esta secao dispensou o `.fr-bloco`. Ela ficou. */
  grid-template-columns: minmax(0, 4fr) minmax(0, 7fr);
  column-gap: clamp(var(--sp-6), 5vw, var(--sp-8));
  row-gap: clamp(var(--sp-5), 3vh, var(--sp-6));
  align-items: start;
}

.perf-camada > .fr-servico__topo {
  grid-column: 2;
  grid-row: 1;
}

/* CENTRADO NA VERTICAL, e nao alinhado ao topo. A coluna da esquerda tem
   duas pecas contra as quatro da direita: ancorada em cima, ela deixava um
   vao morto de ~120px embaixo, e o cartao lia como uma coluna cheia com
   uma sobra ao lado. `grid-row: 1 / -1` faz o resumo atravessar as duas
   linhas da direita, e o `align-self` o centra na altura delas.

   Isso vale para os TRES cartoes com alturas diferentes sem uma segunda
   regra: o resumo se centra no que a direita medir. */
.perf-camada__resumo {
  grid-column: 1;
  /* `1 / span 2` E NAO `1 / -1`, E ISTO FOI MEDIDO. A grade nao declara
     `grid-template-rows`, entao as duas linhas sao IMPLICITAS - e a linha
     `-1` aponta para o fim da grade EXPLICITA, que aqui tem zero linhas.
     Com `1 / -1` o resumo caia em `1 / 1`, ou seja, na primeira linha so':
     ela crescia para os 158px dele, e o `align-self: center` o centrava na
     linha do NOME em vez de na altura do cartao. Medido: centro do resumo
     em 5012 contra 5104 do cartao - 92px acima. */
  grid-row: 1 / span 2;
  align-self: center;
  display: grid;
  justify-items: start;
  gap: var(--sp-4);
}

/* O paragrafo perde a medida de leitura AQUI, e so' aqui: ele deixou de
   morar numa coluna de 7fr e passou a morar numa de 4fr, que ja' e' mais
   estreita que os 62ch de `.fr-servico__texto`. Manter a medida so'
   somaria um teto que nunca morde. */
.perf-camada__resumo .fr-servico__texto {
  max-width: none;
}

.perf-camada__corpo {
  grid-column: 2;
  grid-row: 2;
  display: grid;
  gap: var(--sp-4);
}

/* Duas colunas para as quatro entregas: em coluna unica, num cartao de 7fr,
   cada `li` sobrava com metade da linha vazia e a lista lia como falha de
   diagramacao. */
/* `grid-template-columns` E NAO `columns`: a lista e' `.fr-servico__itens`
   antes de ser `.perf-camada__itens`, e aquela e' `display: grid` - `columns`
   nao se aplica a container de grade e era ignorado inteiro. Medido com a
   regra antiga: os quatro `li` saiam em `left: 367` nas quatro alturas
   4504/4539/4573/4607, uma coluna so'; o layout descrito no comentario nunca
   esteve no ar. `column-gap` continua valendo, que em grade e' a mesma
   propriedade. */
.perf-camada__itens {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: clamp(var(--sp-5), 3vw, var(--sp-7));
}

.perf-camada__itens li {
  break-inside: avoid;
}

/* Pilula porque ela e' rotulo de categoria, nao superficie: o sistema pede
   pilula em interativo e squircle em superficie, e este e' o terceiro caso -
   etiqueta.

   CHEIA, E NAO DE CONTORNO. O contorno e' o que as etiquetas das irmas
   usam, e la' ele esta certo: onze linhas de catalogo, e uma etiqueta
   chapada em cada uma viraria confete. Aqui sao tres, uma por camada, e
   ela e' o que separa uma da outra de relance - o lime cheio e' a unica cor
   da secao, e e' ela que carrega a distincao.

   PRETO SOBRE #D3FF35 MEDE ~16:1, o numero que `--sobre-unidade` ja'
   registra no topo desta folha. Escrito com os dois tokens da unidade e
   nao com hexadecimal solto: a pagina inteira ja' declara que a unidade
   dela e' o lime e que o que se le' sobre ele e' preto.

   `justify-self: start` porque `.perf-camada__resumo` e' grade: sem ele a
   pilula esticaria a linha inteira e deixaria de ser etiqueta.

   SEM HOVER. O cartao tem `:hover` (a borda vira preta) e o icone tambem;
   a etiqueta nao e' alvo de clique e nao reage. Declarar as duas cores aqui
   com token fixo e' o que garante isso: nenhuma regra de estado do cartao
   passa por dentro. */
.perf-camada__pilula {
  justify-self: start;
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-pill);
  background: var(--unidade);
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--sobre-unidade);
}

@media (max-width: 767px) {
  /* Uma coluna, e a ordem do DOM assume: nome, resumo, escopo. As tres
     pecas voltam a ser `auto` na grade - sem isso, `grid-row: 1 / -1` do
     resumo continuaria valendo e ele se sobreporia ao escopo. */
  .perf-camada {
    grid-template-columns: minmax(0, 1fr);
  }

  .perf-camada > .fr-servico__topo,
  .perf-camada__resumo,
  .perf-camada__corpo {
    grid-column: auto;
    grid-row: auto;
    align-self: auto;
  }

  .perf-camada__itens {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------- o campo amarelo ----------
   O MESMO DESENHO DO CAMPO LIME, logo abaixo, na cor da unidade. O mantra
   da /comunicacao era preto como o da /performance foi um dia; a troca é a
   mesma que aquela página já fez - escopo de tema ao lado da classe que
   `mantra.js` procura, e nem uma linha do módulo muda, porque nada nele
   conhece cor.

   Preto sobre #ffdd00 mede 15,59:1. Branco mede 1,35:1, ou seja, some -
   e por isso o anel de foco inverte junto, exatamente como no lime.

   O AMARELO É MAIS PERIGOSO QUE O LIME NUM PONTO: `--c-yellow` é a cor com
   que `.header .btn:hover` pinta a pílula em todo o site. Sobre o campo
   lime isso dava 1,16:1 e era a cor errada de unidade aparecendo; aqui é a
   cor CERTA de unidade, e mesmo assim some - amarelo sobre amarelo é 1:1.
   O escopo `[data-surface="campo"]` de `header.css` cobre os dois casos com
   a mesma pílula preta. */
.fr-amarelo {
  --bg: var(--c-yellow);
  --fg: var(--c-black);
  --fg-muted: rgba(0, 0, 0, 0.62);
  --line: rgba(0, 0, 0, 0.18);
  --fg-invert: var(--c-yellow);
  --bg-invert: var(--c-black);
  --foco-linha: var(--c-black);
  --foco-halo: var(--c-yellow);
  background: var(--c-yellow);
  color: var(--c-black);
}

/* ---------- o campo lime ----------
   O TERCEIRO escopo de tema desta folha, ao lado de `.fr-escuro` e do chão
   claro. Ele existe por uma seção só, e de propósito: o ponto luminoso que
   acerta o alvo no hero volta aqui como campo inteiro. O lime começa do
   tamanho de um pixel e termina do tamanho da tela. Chapado numa terceira
   seção, ele mataria as duas primeiras.

   Preto sobre #D3FF35 mede ~16:1 - o número já está registrado no topo
   desta folha, medido quando `--sobre-unidade` nasceu.

   O ANEL DE FOCO INVERTE JUNTO. O par branco-sobre-preto de `.fr-escuro`
   seria invisível aqui: branco sobre lime mede 1,02:1, que é exatamente o
   motivo de o lime nunca ser traço nem texto sobre claro nesta página. */
.fr-lime {
  --bg: var(--c-lime);
  --fg: var(--c-black);
  --fg-muted: rgba(0, 0, 0, 0.62);
  --line: rgba(0, 0, 0, 0.18);
  --fg-invert: var(--c-lime);
  --bg-invert: var(--c-black);
  --foco-linha: var(--c-black);
  --foco-halo: var(--c-lime);
  background: var(--c-lime);
  color: var(--c-black);
}
