/* Grupo Dls/ - as sete cenas da Home */

/* ============================================================
   CENA 1 - HERO
   ============================================================ */
.hero {
  position: relative;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  /* Simétrico de propósito: com o conteúdo centralizado na altura,
     padding desigual deslocaria o bloco para baixo do centro real.
     6rem é o piso porque é o que livra o header fixo. */
  padding-block: 6rem;
  overflow: hidden;
  background: var(--c-black);
  color: var(--c-white);
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: 0;
}

.hero__media img,
.hero__media video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* O vídeo entra por cima do poster quando estiver pronto. */
.hero__video {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--dur-scene) var(--ease-out);
}

.hero__video.is-ready {
  opacity: 1;
}

/* Escurecimento e vinheta: o texto precisa de contraste sobre imagem viva. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 1;
  background:
    radial-gradient(120% 90% at 50% 40%, transparent 20%, rgba(0, 0, 0, 0.55) 100%),
    linear-gradient(to top, rgba(0, 0, 0, 0.78) 0%, rgba(0, 0, 0, 0.28) 46%, rgba(0, 0, 0, 0.5) 100%);
}

.hero__inner {
  position: relative;
  z-index: 2;
  width: 100%;
}

/* Justificado precisa de medida definida para os vãos entre palavras
   ficarem largos e regulares, como nos key visuals. Duas linhas no
   desktop é o teto: três linhas de caixa alta empurram o CTA para
   fora da primeira tela. */
.hero__title {
  max-width: min(100%, 1120px);
  margin-bottom: var(--sp-6);
}

.hero__sub {
  color: rgba(255, 255, 255, 0.78);
  margin-bottom: var(--sp-7);
}

/* Tela baixa: notebook de 13 polegadas, janela com devtools aberto.
   A manchete encolhe para o CTA continuar acima da dobra, que é o que
   a regra das duas linhas existe para proteger. */
@media (max-height: 760px) {
  .hero {
    padding-top: 5rem;
  }
  .hero__title {
    font-size: clamp(2rem, 4.4vw, 3.5rem);
    margin-bottom: var(--sp-5);
  }
  .hero__sub {
    margin-bottom: var(--sp-6);
  }
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
}

.hero .btn {
  --btn-bg: var(--c-white);
  --btn-fg: var(--c-black);
  transition-duration: var(--dur-micro);
}

/* O CTA do hero estava sem hover NENHUM, e não por esquecimento: como
   `.hero .btn` tem a mesma especificidade de `.btn:hover` e vem depois
   na cascata (scenes.css carrega depois de layout.css), ele sobrescrevia
   o amarelo do hover com o branco do repouso. O estado precisa ser
   declarado aqui, no mesmo nível, para existir. */
.hero .btn:hover {
  --btn-bg: var(--c-yellow);
  --btn-fg: var(--c-black);
}

.hero .btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--c-white);
  border-color: rgba(255, 255, 255, 0.42);
}

.hero .btn--ghost:hover {
  --btn-bg: var(--c-white);
  --btn-fg: var(--c-black);
}

/* ============================================================
   CENA 2 - PROVA
   ============================================================ */
.prova {
  background: var(--c-black);
  color: var(--c-white);
  padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
}

.prova__figures {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--gutter);
  padding-bottom: clamp(var(--sp-8), 9vh, var(--sp-10));
}

.prova__figure {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.prova__figure dt {
  order: 2;
  font-size: var(--fs-small);
  color: var(--c-white);
  max-width: 18ch;
}

.prova__figure dd {
  order: 1;
  margin: 0;
}

@media (max-width: 767px) {
  .prova__figures {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-7) var(--sp-5);
  }
}

/* Faixa de logotipos: a única marquee da página. */
.marquee {
  position: relative;
  overflow: hidden;
  border-block: 1px solid var(--c-line-dark);
  padding-block: var(--sp-6);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

/* O vão entre logotipos mora aqui, e uma vez só: ele é o espaço
   entre dois logos DENTRO da faixa e também o espaço na emenda, entre
   o último da cópia e o primeiro da seguinte. */
.marquee {
  --vao-logos: clamp(var(--sp-7), 6vw, var(--sp-9));
}

/* `padding-inline-end` e não `gap` no elemento de fora, e a diferença
   é a costura do laço. As duas cópias andam `xPercent: -100`, ou seja,
   cada uma pela própria largura. Um `gap` entre elas fica FORA dessa
   largura: ao reiniciar, o conteúdo saltaria de volta pelo tamanho do
   vão, e o salto é visível. Como padding, o vão entra na largura que
   a animação percorre, e a emenda fecha no mesmo pixel.

   Sem ele, Vimaplas (último da lista) encostava em 3 Corações
   (primeiro da cópia): dois logotipos de clientes diferentes lidos
   como um só. */
.marquee__track {
  display: flex;
  width: max-content;
  gap: var(--vao-logos);
  padding-inline-end: var(--vao-logos);
  align-items: center;
  will-change: transform;
}

/* Altura igual à da landing da Rovily (48px no desktop), que é a
   referência já aprovada para esta faixa.

   `max-width` e `object-fit` não são enfeite: vários destes SVGs são
   logotipos deitados, e com só a altura travada eles esticavam a
   ponto de invadir o vizinho. O contain resolve a sobreposição sem
   deformar nenhuma marca. */
.marquee__logo {
  flex: none;
  height: clamp(32px, 3.4vw, 48px);
  width: auto;
  max-width: 140px;
  object-fit: contain;
  opacity: 0.45;
  filter: brightness(0) invert(1);
  transition: opacity var(--dur-base) var(--ease-out);
}

.marquee__logo:hover {
  opacity: 1;
}

/* ============================================================
   CENAS 3 E 4 - AS TRÊS FRENTES E O MÉTODO
   Um pinning só, com um protagonista contínuo. O squircle entra e
   não sai mais do lugar: troca de cor a cada frente, e na convergência
   as três cores se misturam no gradiente enquanto o tema inverte
   em volta dele. O gradiente da identidade é a soma aritmética das
   três unidades, então a cena demonstra a tese em vez de ilustrá-la.
   ============================================================ */
.pin {
  position: relative;
  background: var(--c-black);
  color: var(--c-white);

  /* A medida do núcleo mora aqui porque duas coisas dependem dela: o
     próprio squircle e o vão que o método reserva para ele. Enquanto o
     valor estava só no `.core`, a simetria da cena era um número
     copiado à mão em dois lugares. */
  --core-size: min(76vh, 88vw);
  --core-squircle: 0.26;
}

.pin__stage {
  position: relative;
  min-height: 100dvh;
  overflow: hidden;
  display: grid;
  place-items: center;
}

/* O protagonista.

   Opacidade cheia o tempo todo (17/08). A composição das frentes deixou
   de sobrepor texto e placa: o núcleo sai do eixo durante as três
   frentes e volta ao centro dentro da convergência. Sem sobreposição,
   não há mais o que proteger, e o amarelo, o azul e o lime chegam como
   são. O par CSS deste tween mora logo abaixo, em
   `.pin:not(.no-pin) .frente__box`; o par JS, em `frentes-metodo.js`
   (constante `DESVIO`). */
.core {
  position: absolute;

  /* CENTRALIZADO POR `left`/`top` CALCULADOS, E NUNCA POR `translate`.
     É a armadilha 12 do HANDOFF, e este era o último lugar do site onde
     ela ainda estava aberta - justamente no elemento em que ela mais
     custa, porque aqui o GSAP anima `x` e `y`.

     O que acontecia: ao assumir o elemento, o GSAP puxa `translate`,
     `rotate` e `scale` do CSS para dentro da própria matriz e escreve
     `translate: none` no inline. Quando a leitura do `-50% -50%` dá
     certo, ele vira `xPercent/yPercent` e tudo se compõe; quando não dá,
     o `-50%` some e o núcleo passa a nascer com o canto superior
     esquerdo no centro do palco - meio objeto fora da tela.

     E não dava certo sempre. Medido em 25/08 com o mesmo código: a
     390x844 (iPhone 13) o núcleo saía 164px para fora da borda direita
     e 179px abaixo do lugar; a 400, 500, 767, 800 e 1512 ele nascia
     certo. A diferença não é regra de CSS nenhuma - não há breakpoint
     entre 390 e 400 nesta folha - é o momento em que o GSAP faz a
     leitura contra o momento em que o layout assenta. Bug de corrida,
     que é o pior tipo: passa na sua tela e quebra na do cliente.

     `left`/`top` não são transformação, então não há o que o GSAP
     absorva ou perca, e `x: 0` volta a significar exatamente "no
     centro" - que é o que `frentes-metodo.js` assume no fim da
     convergência. Mesmo idioma de `.fr-aneis` em `frente.css`, que já
     tinha sido corrigido assim. A outra saída válida seria declarar
     `xPercent: -50, yPercent: -50` no GSAP, como `cartao-hover.js` faz;
     esta é melhor aqui porque a cena tem estado sem JS. */
  top: calc(50% - var(--core-size) / 2);
  left: calc(50% - var(--core-size) / 2);
  width: var(--core-size);
  aspect-ratio: 1;
  display: grid;
  place-items: center;
  opacity: 1;
  pointer-events: none;
}

/* ---------- a frente ocupa a metade que sobrou ----------
   50% é do próprio quadro da frente, que já é `--maxw` centrado: metade
   dele é metade da tela. Com isso a caixa não tem como cruzar o eixo
   central em nenhuma largura - e o eixo central é onde o squircle mora
   quando o núcleo volta para casa dentro da convergência.

   O `--sp-6` de folga é para a frase não encostar no objeto: o texto
   para antes do meio, não no meio.

   O par deste número está em `frentes-metodo.js`, na constante `DESVIO`
   (0.25 da largura do palco): metade da tela para o texto, o objeto
   centrado na outra metade. Mexer num sem o outro reabre a sobreposição.

   `:not(.no-pin)` em tudo o que segue: sem GSAP ou com movimento
   reduzido a cena vira empilhamento simples, o núcleo some e não há
   metade nenhuma para dividir. Ali quem manda é `.pin.no-pin`. */
.pin:not(.no-pin) .frente {
  justify-items: start;
  text-align: left;
}

/* 50ch e não 40ch: a 40ch a caixa fecha em 419px e a manchete da
   primeira frente quebra em quatro linhas. Em 50ch (524px) ela cai para
   três, e a segunda e a terceira para duas.

   O teto continua sendo `calc(50% - var(--sp-6))`, que é o que impede a
   caixa de cruzar o eixo do objeto. */
.pin:not(.no-pin) .frente__box {
  justify-self: start;
  justify-items: start;
  text-align: left;
  max-width: min(50ch, calc(50% - var(--sp-6)));
}

/* Celular: quem se divide é a altura. O objeto ocupa a parte de cima,
   o texto a de baixo. Mesmo mecanismo (núcleo sai do eixo, volta na
   convergência), só que na vertical. 38dvh é onde a coluna de texto
   começa; a 375x812 as três sobras ficam 165, 190 e 124 - o mais parelho
   que dá sem mover o objeto para longe do quarto de tela dele. */
@media (max-width: 900px) {
  .pin:not(.no-pin) .frente {
    justify-items: center;
    text-align: center;
    padding-block-start: 38dvh;
  }

  .pin:not(.no-pin) .frente__box {
    justify-self: center;
    justify-items: center;
    text-align: center;
    max-width: 34ch;
  }
}

.core__rings {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

/* 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 da unidade. */
.core__rings 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. */
}

.core__squircle {
  position: relative;
  width: calc(var(--core-size) * var(--core-squircle));
  aspect-ratio: 1;
  border-radius: var(--r-squircle);
  overflow: hidden;
  background: var(--c-yellow);
}

/* Preenchimento chapado com a cor da unidade corrente. Some na
   convergência, quando o gradiente floresce por baixo.

   A travessia entre duas unidades é longa e sem aceleração: amarelo,
   azul e lime são três cores saturadas e distantes entre si, e um corte
   rápido entre elas pisca. Em `linear` e mais de um segundo, a troca
   vira o que ela é na tese do grupo - uma passagem, não um clique. */
.core__flat {
  position: absolute;
  inset: 0;
  background: var(--unit, var(--c-yellow));
  transition: background-color 1200ms linear;
}

.core__gl {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

/* Sem WebGL, o gradiente continua existindo em CSS. */
.core__gl-fallback {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(90% 70% at 22% 8%, var(--grad-1) 0%, transparent 62%),
    radial-gradient(80% 80% at 82% 30%, var(--grad-2) 0%, transparent 58%),
    radial-gradient(70% 70% at 42% 96%, var(--grad-5) 0%, transparent 62%),
    radial-gradient(60% 60% at 76% 88%, var(--grad-4) 0%, transparent 58%),
    var(--grad-3);
}

/* ---------- painéis das frentes ---------- */
/* Centralizado em todas as larguras, e não só no celular. A cena
   seguinte - o método - é inteiramente simétrica em torno do squircle, e
   as duas acontecem dentro do mesmo pinning, com o mesmo objeto em cena:
   trocar de eixo no meio do caminho lê como um corte que a animação não
   pediu.

   Vai embora com isso a alternância esquerda/direita das três frentes,
   que era o que dava ritmo à sequência. É perda consciente: o preço de
   as duas cenas dividirem um eixo só. */
.frente {
  position: absolute;
  inset: 0;
  display: grid;
  align-content: center;
  justify-items: center;
  text-align: center;
  padding-inline: var(--margin);
  max-width: var(--maxw);
  margin-inline: auto;
  pointer-events: none;
}

.frente.is-live {
  pointer-events: auto;
}

/* Sem medida aqui: quem define a largura da caixa é a composição, e são
   duas - a presa (`.pin:not(.no-pin)`, meia tela ao lado do objeto) e a
   empilhada (`.pin.no-pin`). Cada uma declara a sua. */
.frente__box {
  display: grid;
  gap: var(--sp-4);
  justify-self: center;
  justify-items: center;
}

/* Casada com `.t-h2` do método por decisão do cliente (17/08): as duas
   frases fecham a mesma família tipográfica quando a cena passa de uma
   para a outra. Antes era menor (~40px), para não competir com o núcleo
   no corpo de display; se a competição voltar a incomodar, o caminho é
   apertar o `max-width` da frase, não reduzir o corpo. */
.frente__box .t-display {
  font-size: var(--fs-display-m);
}

.frente__unit {
  height: 22px;
  width: auto;
}

/* Preenchido, branco, e sem depender do tema: durante a convergência
   `--fg` vira preto, e um botão que herdasse dele trocaria de lado no
   meio da cena.

   NO HOVER O BOTÃO ASSUME A COR DA UNIDADE - amarelo na Dls/, azul na
   Elleven, lime na Metrik. Até 25/08 ele apenas escurecia, pelo medo de
   que o amarelo do hover padrão puxasse para a Dls/ dentro de um painel
   que podia ser de outra unidade. O pedido do cliente resolve esse medo
   pela raiz: não é UM amarelo para os três, é a cor de cada uma. O
   squircle ao lado já diz de quem é a cena, e o botão passa a concordar
   com ele em vez de ficar mudo.

   A cor vem de `--unit`, escrito no próprio `<article class="frente">`.
   O par `--unit-fg` existe porque o azul da Elleven é escuro (#0153ff,
   2,5:1 contra preto): amarelo e lime pedem texto preto, o azul pede
   branco. Um segundo par de olhos aqui evitaria o único caso em que a
   regra sozinha apagaria o rótulo. */
.frente .btn {
  --btn-bg: var(--c-white);
  --btn-fg: var(--c-black);
}

.frente .btn:hover {
  --btn-bg: var(--unit, var(--c-white));
  --btn-fg: var(--unit-fg, var(--c-black));
}

/* Branco, não cinza. O resumo é a frase que explica o que a unidade
   vende, e ela passa por cima da placa de cor: cinza sobre placa é o
   segundo rebaixamento de contraste num fundo que já era difícil. Em
   branco dá 21:1 sobre o preto da seção e 5.33:1 sobre a pior placa. */
/* Medida própria, e menor que a da caixa: a caixa é larga porque é do
   que a manchete precisa a 48px. O resumo tem 14px, e a largura cheia da
   caixa nesse corpo passaria de 80 caracteres por linha. 58ch aqui é a
   medida do próprio resumo, e resolve por volta de 430px. */
.frente__summary {
  max-width: 58ch;
  font-size: var(--fs-small);
  color: var(--c-white);
}

/* ---------- método ---------- */
/* Três linhas: título, o vão do squircle, e os quatro movimentos. As
   duas linhas elásticas são iguais (1fr cada), então o conjunto fica
   simétrico em torno do meio - que é exatamente onde o squircle está.
   Com isso a distância do título ao squircle e a do squircle aos itens
   saem do mesmo `gap`, e continuam iguais em qualquer altura de tela.

   Antes era `space-between` com dois blocos de alturas diferentes: as
   distâncias só coincidiriam por acaso, e não coincidiam. */
.metodo {
  position: absolute;
  inset: 0;
  padding-inline: var(--margin);
  display: grid;
  /* `minmax(0, 1fr)`, e não `1fr`: o atalho é `minmax(auto, 1fr)`, e o
     mínimo automático deixa a linha crescer além da fração quando o
     conteúdo é maior. Os quatro movimentos são mais altos que o título,
     então a linha de baixo inchava, as duas elásticas deixavam de ser
     iguais e o vão do meio subia 33px - o squircle ficava fora do
     próprio vão que a grade reservou para ele. */
  grid-template-rows: minmax(0, 1fr) auto minmax(0, 1fr);
  /* MENOR DO QUE ERA (6vh, 25/08). Em tela baixa a cena não cabe: o
     conteúdo transborda as duas faixas elásticas, o vão fica travado no
     meio (é onde o squircle mora) e a cabeça sai por cima. Medido a
     1265x720, o rótulo "Metodologia proprietária" nascia a 47px do topo
     - debaixo do header compacto, que tem 64. Apertar o vão é o único
     ajuste que devolve ar ao topo sem tirar o squircle do lugar: ele é
     simétrico, então a cabeça desce e o rodapé sobe o mesmo tanto, e a
     igualdade título-squircle / squircle-elementos continua de pé. */
  gap: clamp(var(--sp-5), 4.5vh, var(--sp-8));
  align-content: center;
  justify-items: center;
  padding-block: clamp(var(--sp-8), 12vh, var(--sp-10));
  pointer-events: none;
  /* O bloco todo sobe metade da diferença entre rodapé e cabeça. A grade
     acima trava o VÃO no meio da tela, porque é lá que o squircle mora -
     e então as duas sobras só empatam se cabeça e rodapé tiverem a mesma
     altura. No celular não têm: o parágrafo desce para o convite, a
     cabeça fica com 112px e o rodapé com 268, e o conjunto lê torto -
     232px de branco em cima contra 76 embaixo.

     Quem mede é o JS (o valor depende de quebra de linha e de fonte
     carregada), e o núcleo sobe o mesmo tanto na convergência: sem isso
     o gradiente sai do vão que a grade reservou para ele e vai parar por
     cima do texto. Sem JS, ou fora do celular, o padrão é zero e nada
     disto acontece. */
  translate: 0 calc(-1 * var(--metodo-shift, 0px));
}

.metodo__head {
  align-self: end;
}

.metodo__vao {
  width: 1px;
  height: calc(var(--core-size) * var(--core-squircle));
}

.metodo__foot {
  align-self: start;
}

/* `--sp-3` e não `--sp-4`: são dois vãos internos, e os 8px que eles
   devolvem vão inteiros para a folga do topo em tela baixa. Rótulo,
   manchete e resumo continuam legíveis como três degraus - a hierarquia
   aqui é de corpo tipográfico, não de distância. */
.metodo__head {
  text-align: center;
  display: grid;
  gap: var(--sp-3);
  justify-items: center;
}

/* `text-align` no próprio parágrafo, e não herdado da cabeça: no celular
   ele se muda para o rodapé, e lá não havia de quem herdar - o bloco
   ficava centralizado com o texto alinhado à esquerda por dentro. */
/* 88ch no desktop cabe a frase em duas linhas (~86 caracteres cada) —
   pedido do cliente (17/08). No celular o container é menor que isso,
   então o `max-width` deixa de importar e a quebra volta a ser natural. */
.metodo__sub {
  max-width: 88ch;
  font-size: var(--fs-body);
  line-height: var(--lh-body);
  text-align: center;
  color: var(--fg-muted);
}

/* Os quatro movimentos e o CTA como um bloco só, ancorado embaixo. Com
   os três filhos soltos no `space-between`, a fileira de elementos ia
   parar no centro vertical - exatamente onde mora o squircle do
   gradiente. O texto passava por cima do protagonista da cena. */
.metodo__foot {
  display: grid;
  justify-items: center;
  gap: clamp(var(--sp-5), 4vh, var(--sp-7));
  width: 100%;
  max-width: var(--maxw);
}

.metodo__elements {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--gutter);
  width: 100%;
  max-width: var(--maxw);
}

/* `align-content: start` porque as quatro colunas têm alturas iguais
   (a grade estica) mas textos de comprimentos diferentes. Sem isto, a
   sobra da coluna mais curta era distribuída entre as linhas dela, e o
   título de "Sentido" descia 8px em relação aos outros três. */
/* A primeira linha é reservada em 28px mesmo que o ícone seja mais
   baixo. Com a largura travada e a altura proporcional, os quatro
   arquivos têm alturas diferentes (28, 28, 20.3, 22.2) - e sem a linha
   fixa cada título nasceria numa altura, quebrando a leitura horizontal
   dos quatro movimentos. O ícone assenta na base dessa linha.

   `align-content: start` porque as colunas têm alturas iguais (a grade
   estica) mas textos de comprimentos diferentes: sem isto, a sobra da
   coluna mais curta era distribuída entre as linhas dela. */
.metodo__el {
  display: grid;
  grid-template-rows: 28px auto auto;
  align-content: start;
  gap: var(--sp-2);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--line);
}

/* Ícone entre a régua e o título. A largura é que é travada, e a altura
   segue a proporção de cada arquivo - os quatro têm caixas diferentes
   (30x30, 30x23.8), então travar a altura deixaria os mais deitados
   largos demais em relação aos outros.

   `brightness(0)` achata o traço para preto. Os arquivos são amarelos, e
   amarelo sobre o off-white da convergência dá 1.3:1 de contraste: os
   quatro ícones simplesmente não existiriam. Preto é a cor do texto que
   eles acompanham. */
.metodo__icone {
  width: 28px;
  height: auto;
  /* Sem isto o item de grade estica a imagem até a largura da coluna:
     em elemento substituído, `stretch` com `width: auto` deforma. */
  justify-self: start;
  align-self: end;
  margin-bottom: var(--sp-2);
  filter: brightness(0);
}

.metodo__el h3 {
  font-size: var(--fs-title);
  font-weight: 500;
  letter-spacing: -0.02em;
}

.metodo__el p {
  font-size: var(--fs-small);
  color: var(--fg-muted);
}

/* A saída da cena. Centralizado porque a cena inteira é simétrica em
   torno do squircle, e um CTA encostado numa borda quebraria o eixo que
   a convergência acabou de construir.

   `pointer-events` volta a valer só aqui: o resto do bloco é leitura, e
   um painel inteiro capturando o mouse dentro de um pin atrapalha a
   rolagem. */
.metodo__cta {
  justify-self: center;
  pointer-events: auto;
}

@media (max-width: 900px) {
  .pin {
    --core-size: min(58vh, 92vw);
    --core-squircle: 0.34;
  }

  /* A centralização e a opacidade do núcleo saíram daqui: valem para
     todas as larguras agora. Sobra o que é mesmo de tela pequena. */
  .frente__box {
    max-width: 34ch;
  }

  /* ---------- o orçamento vertical do método ----------
     A cena inteira precisa caber numa tela de celular, e o conteúdo é
     rótulo + manchete + parágrafo + quatro movimentos + CTA, tudo em
     volta de um squircle que não pode ser coberto. No desktop sobra
     altura; aqui não sobra nada, e com os respiros do desktop o rodapé
     estourava 96px abaixo do palco - o CTA ficava fora da tela.

     Então os respiros encolhem, um a um, e o squircle passa de 34% para
     28% do núcleo, que é quase a proporção que ele já tem no desktop.
     Nada disso muda a animação: muda o quanto de ar cada peça ocupa. */
  .pin {
    --core-squircle: 0.22;
  }

  /* No celular a cena presa fica só com a manchete e o squircle - os
     quatro movimentos saem daqui e viram a seção logo abaixo (o porquê
     está em frentes-metodo.js). Sobra espaço de sobra, e o squircle
     continua opaco, centrado e sem nada por cima. */
  /* O vão é o mesmo dos dois lados do objeto, e o teto dele é a tela
     menor: a 375x667 sobram 274px abaixo do centro para uma grade de
     246, então 24 é o que dá para respirar sem empurrar o último
     movimento para fora. É também por isso que o squircle é 22% aqui e
     26% no desktop - cada ponto percentual a menos vira ar em volta. */
  .metodo {
    gap: var(--sp-5);
    padding-block: var(--sp-4);
  }

  .metodo__head {
    gap: var(--sp-3);
  }

  /* O CTA encosta no squircle, a 24px - o mesmo respiro da cabeça do
     outro lado. O grupo fica inteiro: texto, objeto, ação.

     Sobra ar na tela, e sobra embaixo: o squircle é fixo no centro e a
     cabeça é quatro vezes mais alta que o botão, então a folga cai
     sempre do mesmo lado. Ancorar o botão no pé resolveria o número e
     pioraria o desenho - abriria 267px entre o objeto e a ação, e o
     vazio passaria a estar bem embaixo do squircle, que é justamente o
     que não pode. Assim o vazio fica na borda da tela, onde ele lê como
     margem. */
  .metodo__foot {
    gap: var(--sp-5);
  }

  /* Os quatro movimentos voltaram para dentro do quadro preso, e lá a
     altura é disputada: cada item perde ar por dentro. São quatro deles,
     então quatro pixels entre o ícone e o título valem oito linhas no
     total - e é essa soma que decide se a grade cabe na tela. O vão
     ENTRE os itens não muda: continua o mesmo nos dois eixos. */
  .metodo__el {
    padding-top: var(--sp-2);
    gap: var(--sp-1);
  }

  .metodo__icone {
    width: 24px;
    margin-bottom: 0;
  }

  .metodo__sub {
    font-size: var(--fs-small);
  }

  /* Dois a dois, e um vão só. Com 48 na vertical e 24 na horizontal, a
     distância entre as réguas dos itens mudava conforme a direção em que
     se olhava - dois ritmos na mesma grade de quatro. Um valor para os
     dois eixos e as quatro barras passam a ter o mesmo respiro. */
  .metodo__elements {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--sp-5);
  }

  .metodo__el {
    justify-items: center;
    text-align: center;
  }

  .metodo__icone {
    justify-self: center;
  }
}

/* ---------- o convite solto (só no celular) ----------
   Criada pelo JS quando o CTA sai do quadro preso. Clara por dentro
   porque é a continuação da mesma passagem clara que a convergência
   abriu - não é uma segunda passagem, é a mesma terminando de acontecer
   fora do pin. A seção seguinte, os cases, devolve o preto. */
.convite {
  --bg: var(--c-offwhite);
  --fg: var(--c-black);
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
  background: var(--bg);
  color: var(--fg);
  padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
}

/* A frase que fecha o argumento e o botão que o transforma em conversa,
   um sobre o outro, no meio. */
.convite .shell {
  display: grid;
  justify-items: center;
  gap: var(--sp-6);
}

/* Durante a convergência o fundo já é claro, então os tons secundários
   precisam acompanhar. Alternado por classe porque são valores discretos,
   não interpoláveis. */
.pin.is-light {
  --bg: var(--c-offwhite);
  --fg: var(--c-black);
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
}


/* ---------- sem pin ----------
   Redução de movimento, ou ausência de GSAP. As mesmas três frentes e o
   mesmo método, empilhados. Nenhum conteúdo é escondido para poupar
   animação. */
.pin.no-pin .pin__stage {
  min-height: 0;
  display: block;
  padding-block: var(--sp-9);
}

.pin.no-pin .core {
  display: none;
}

.pin.no-pin .frente,
.pin.no-pin .metodo {
  position: static;
  inset: auto;
  pointer-events: auto;
  padding-block: var(--sp-7);
  max-width: var(--maxw);
  margin-inline: auto;
}

/* Sem pin não há objeto no meio da tela para dar eixo - o núcleo está
   escondido. Empilhadas, as três frentes voltam a ler melhor alinhadas
   à esquerda, como qualquer sequência de blocos de texto.

   O `max-width` fica só na caixa: na `.frente` ele brigaria com o
   `var(--maxw)` que a regra acima usa para conter o painel inteiro. */
.pin.no-pin .frente {
  justify-items: start;
  text-align: left;
}

.pin.no-pin .frente__box {
  justify-self: start;
  justify-items: start;
  text-align: left;
  max-width: 46ch;
}

.pin.no-pin .metodo {
  display: grid;
  gap: var(--sp-7);
  justify-items: start;
  border-top: 1px solid var(--line);
  margin-top: var(--sp-7);
}

.pin.no-pin .metodo__head {
  text-align: left;
  justify-items: start;
}

/* ============================================================
   CENA 5 - CASES
   ============================================================ */
.cases {
  position: relative;
  background: var(--c-black);
  color: var(--c-white);
}

/* O que fica preso. Uma tela cheia, com o título em cima e a faixa
   embaixo: é este bloco que o ScrollTrigger segura enquanto o trilho
   anda. */
.cases__inner {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(var(--sp-6), 5vh, var(--sp-8));
  padding-block: clamp(var(--sp-7), 8vh, var(--sp-9));
}

.cases__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-5);
}

.pan {
  position: relative;
  overflow: hidden;
}

.pan__track {
  display: flex;
  align-items: stretch;
  gap: clamp(var(--sp-5), 3vw, var(--sp-7));
  padding-inline: var(--margin);
  width: max-content;
  will-change: transform;
}

/* A largura do cartão sai da ALTURA da moldura, e não do viewport:
   dentro de um pin de tela cheia é a altura que é escassa. Com
   `aspect-ratio` na moldura, a largura do cartão vem junto.

   E ela precisa estar DECLARADA no cartão, não só na moldura. Sem isto o
   cartão é um `flex: none` medido pelo conteúdo mais largo que houver
   dentro dele - e o conteúdo mais largo era a fileira de tags. Medido a
   375: a moldura pedia 285px e os oito cartões saíam com 285, 328, 364,
   394, 394, 394 e 406 - QUATRO deles mais largos que a própria tela, e
   nenhum do tamanho do vizinho. Uma faixa de cartões em que cada cartão
   tem uma largura não é uma faixa, é uma fila.

   `--case-w` é a fonte única: a moldura e a caixa do CTA passam a ser
   `width: 100%` dela, e o celular só troca o valor.

   A largura é da FAIXA, e por isso mora em `.pan__track > .case`: o mesmo
   cartão vive também na grade de `/cases` (`.ci-grade`, três colunas
   `1fr`), e lá quem manda na largura é a coluna. Uma largura fixa em
   `.case` quebraria a grade inteira para consertar a faixa. */
.case {
  flex: none;
  display: grid;
  grid-template-rows: auto 1fr;
  gap: var(--sp-4);
}

.pan__track > .case {
  --case-w: calc(clamp(240px, 46vh, 520px) * 4 / 5);
  width: var(--case-w);
}

.case__frame {
  position: relative;
  height: clamp(240px, 46vh, 520px);
  aspect-ratio: 4 / 5;
  border-radius: var(--r-squircle-4x5);
  overflow: hidden;
  background: #0d0d0d;
}

.case__frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* A distorção de hover é feita aqui, em transform e filter, e não num
     contexto WebGL por card: oito contextos GL numa faixa que rola é
     armadilha de performance em celular. */
  transition:
    transform var(--dur-scene) var(--ease-out),
    filter var(--dur-base) var(--ease-out);
}

.case:hover .case__frame img {
  transform: scale(1.07) rotate(0.6deg);
  filter: saturate(1.15) contrast(1.06);
}

/* Deslocamento de canal: o "shift" de RGB no hover, sem canvas. */
.case__frame::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(115deg, var(--c-blue), transparent 42%, var(--c-yellow));
  mix-blend-mode: color-dodge;
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}

.case:hover .case__frame::after {
  opacity: 0.16;
}

/* ---------- botão redondo de hover ----------
   Segue o cursor dentro da moldura e não tem função de clique própria:
   o cartão inteiro já é um link. Por isso é `aria-hidden` no HTML, e
   `pointer-events: none` aqui - um alvo dentro de outro alvo confunde
   quem navega por toque, e um alvo que foge do cursor é pior ainda.

   O centro continua em `top/left: 50%` com `translate`, e o
   acompanhamento entra como `x/y` do GSAP por cima: `translate` e
   `transform` são propriedades separadas e se somam. Assim, sem JS ou
   com movimento reduzido, o botão nasce no meio exato da moldura em vez
   de no canto superior esquerdo.

   Branco a 90% com desfoque atrás: chapado, ele apaga o trecho da capa
   que está justamente convidando ao clique. */
.case__hover {
  position: absolute;
  top: 50%;
  left: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: clamp(84px, 9vw, 116px);
  aspect-ratio: 1;
  border-radius: var(--r-pill);
  translate: -50% -50%;
  background: color-mix(in srgb, var(--c-white) 78%, transparent);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  color: var(--c-black);
  font-size: var(--fs-label);
  font-weight: 500;
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  text-align: center;
  pointer-events: none;
  opacity: 0;
  scale: 0.7;
  transition:
    opacity var(--dur-base) var(--ease-out),
    scale var(--dur-base) var(--ease-out);
}

/* Com o acompanhamento ligado, quem anima é o GSAP - e a transição de
   CSS precisa sair de cena por inteiro. Deixá-la ligada faz a opacidade
   ser interpolada duas vezes, uma por cada motor, e o resultado é um
   botão que atrasa para aparecer e trava para sumir. */
.case__hover.is-seguindo {
  transition: none;
}

/* Só enquanto o CSS estiver no comando. Com o acompanhamento ligado, a
   revelação inteira é do GSAP: `scale` aqui e `scale` lá seriam duas
   mãos escrevendo na mesma propriedade. */
.case:hover .case__hover:not(.is-seguindo),
.case:focus-visible .case__hover:not(.is-seguindo) {
  opacity: 1;
  scale: 1;
}

/* ---------- corpo do cartão ----------
   Frente, cliente, régua, serviço. Nesta ordem: a frente diz de quem é
   o trabalho dentro do grupo, o cliente diz para quem, e a tag diz o
   quê. A régua separa quem contratou de o que foi contratado. */
.case__body {
  display: grid;
  align-content: start;
  gap: var(--sp-2);
  /* Item de grade nasce com tamanho mínimo `auto`, que é o min-content do
     conteúdo: sem isto o corpo se recusa a ser mais estreito que a pílula
     mais longa, e transborda o cartão em vez de apertar a pílula. */
  min-width: 0;
}

/* A frente é lista, e o separador dos itens dela é o mesmo traço da
   régua horizontal, virado de lado: 1px, `--c-line-dark`. Um ponto ou
   uma barra tipográfica seriam um segundo desenho fazendo o trabalho
   que a régua já faz na seção inteira.

   SÓ a frente. A tag não leva filete - ela é pílula de contorno, e o
   contorno já diz onde uma acaba e a outra começa (ver `.case__tag`).

   O vão do `gap` fica antes da barrinha e o `padding` depois, os dois
   iguais: a barra cai no meio exato do intervalo. */
.case__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3);
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--c-gray-on-dark);
}

.case__eyebrow > span:not(:first-child) {
  padding-left: var(--sp-3);
  border-left: 1px solid var(--c-line-dark);
}

.case__client {
  font-size: var(--fs-title);
  font-weight: 500;
  letter-spacing: -0.02em;
}

/* O nome do projeto, colado no cliente: os dois são um par - para quem
   foi o trabalho e qual trabalho foi - e o cliente fala mais alto porque
   é ele que a pessoa reconhece de longe.

   SEM CAIXA-ALTA, ao contrário do olho e da tag. Aqueles dois são
   rótulos e a maiúscula os marca como rótulo; este é nome próprio, e a
   mesma maiúscula o empurraria para o mesmo balde.

   `--sp-1` em vez do `--sp-2` da grade do corpo: com o vão cheio as três
   linhas ficam igualmente espaçadas, e aí cliente e projeto leem como
   dois fatos separados em vez de um só.

   CUSTA ALTURA, e na faixa da home a altura é o que é escasso - a largura
   do cartão sai dela, e a seção inteira é presa em `top top`, de modo que
   o que não couber na janela fica cortado durante o pin inteiro.

   Medido: a linha custa 23px de cartão, e a folga de `.cases__inner` cai
   de 79px para 56 a 1280x720, de 198 para 175 a 1920x1080. O ponto em que
   a seção deixa de caber sai de ~532px de altura útil para ~572 - uma
   janela que já não cabia antes, agora 40px mais cedo. É o preço, e está
   escrito aqui para quem for mexer na altura da moldura saber de onde
   veio a conta. */
.case__project {
  margin-top: calc(var(--sp-1) - var(--sp-2));
  font-size: var(--fs-small);
  line-height: 1.35;
  color: var(--c-gray-on-dark);
}

.case__tags {
  display: flex;
  flex-wrap: wrap;
  /* `center` e não `stretch`: as pílulas têm a mesma fonte e uma linha só,
     então a altura já é igual - e esticar um `inline-block` jogaria o texto
     para o topo da pílula em vez de centrá-lo. */
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
  margin-top: var(--sp-3);
  padding-top: var(--sp-4);
  border-top: 1px solid var(--c-line-dark);
}

/* Pílula de contorno, e não sólida: a tag informa, não convida ao
   clique. Sólida ela competiria com o botão de hover da moldura.
   Entre uma e outra não vai filete: o próprio contorno da pílula já
   diz onde uma acaba e a outra começa, e o traço no meio do vão era
   uma segunda separação para o mesmo limite. */
.case__tag {
  display: inline-block;
  padding: 0.5em 0.9em;
  border: 1px solid var(--c-line-dark);
  border-radius: var(--r-pill);
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--c-gray-on-dark);
  line-height: 1;
  max-width: 100%;
  transition:
    border-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out);
}

/* ---------- as tags NA FAIXA ----------
   SÓ A PRINCIPAL, e um contador para o resto.

   Era "duas visíveis e um contador", e duas não cabem: a largura do
   cartão vem da ALTURA da janela, então quanto cabe na fileira não é
   uma questão de celular contra desktop. A 1920x1080 o cartão tem 397px
   e "Campanha publicitária" mais "Gestão de mídias sociais" pedem 398.
   Não cabia em lugar nenhum - no celular só aparecia primeiro.

   Por isso a regra é uma só, e não uma por breakpoint: um `@media` de
   largura seria o instrumento errado para um problema de altura.

   As demais continuam no HTML: elas são o que o case FOI, e apagar dado
   de cliente para resolver layout é trocar a coisa certa pela fácil.
   Quem decide o que aparece é esta folha.

   E SÓ NA FAIXA. No índice `/cases` o mesmo cartão vive numa grade com
   FILTRO POR SERVIÇO: esconder a segunda tag ali faria quem filtrasse por
   "Design gráfico" receber cartões que mostram "Campanha publicitária" -
   o filtro dizendo uma coisa e o cartão outra. Lá as tags são o
   vocabulário do filtro, e a grade tem coluna larga e quebra de linha à
   vontade: aparecem todas.

   A faixa é chamariz, o índice é catálogo. Trabalhos diferentes. */
.pan__track .case__tags {
  flex-wrap: nowrap;
}

.pan__track .case__tag:not(:first-child):not(.case__tag--mais) {
  display: none;
}

/* O nome do projeto NA FAIXA cabe em uma linha ou não cabe.

   Não é vaidade de largura: os cartões da faixa se esticam juntos
   (`align-items: stretch`), então um nome que quebra em duas linhas
   levanta os OITO cartões de uma vez, e são outros 19px em cima dos 23
   que esta linha já custa. A folga de 56px a 1280x720 vira 37 por causa
   de um título comprido em um case só.

   Na grade de `/cases` não entra: lá a coluna é larga, a altura não é
   escassa e o nome inteiro sempre é melhor que o nome cortado. */
.pan__track .case__project {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* O contador não nomeia serviço nenhum, então não finge ser uma pílula
   clicável: sem contorno. E não encolhe - quem cede espaço é o nome. */
.case__tag--mais {
  flex: none;
  border-color: transparent;
}

/* Rede de segurança da faixa, e só isso: em janela muito baixa o cartão
   encolhe junto (a largura vem da ALTURA) e um nome de serviço longo passa
   a não caber nem sozinho. A 1280x600 o cartão tem 221px e a pílula mais o
   contador pedem 235. Reticências ali é pior que o nome inteiro e melhor
   que a pílula rasgando o cartão. Em janela normal nunca dispara.

   `min-width: 0` é o que faz as reticências existirem: item flex nasce com
   mínimo `auto` = min-content, e com `white-space: nowrap` o min-content é
   a frase inteira. Sem ele a pílula se recusa a encolher - medido a
   1280x600, cinco dos oito cartões com a pílula para fora do cartão. */
.pan__track .case__tag {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.case:hover .case__tag {
  border-color: rgba(255, 255, 255, 0.5);
  color: var(--c-white);
}

/* Case com número de resultado ganha destaque; sem número, entra
   sem destaque, como o copy determina. */
.case__result {
  font-size: var(--fs-small);
  color: var(--c-yellow);
}

/* ---------- cartão final de CTA ----------
   Mesma moldura e mesma altura dos outros, sem imagem: contorno e
   texto centralizado. */
.case--cta .case__cta-box {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: clamp(240px, 46vh, 520px);
  aspect-ratio: 4 / 5;
  padding: var(--sp-6);
  border: 1px solid var(--c-line-dark);
  border-radius: var(--r-squircle-4x5);
  /* Equilibra as linhas quando a frase não cabe em uma só, em vez de
     encher a primeira e deixar uma órfã embaixo. */
  text-wrap: balance;
  overflow: hidden;
  /* A régua de preenchimento inverte por mistura, e mistura vaza para
     fora do elemento se ninguém a isolar. Aqui o cartão é o teto. */
  isolation: isolate;
  font-size: var(--fs-title);
  font-weight: 500;
  letter-spacing: -0.02em;
  text-align: center;
  transition:
    border-color var(--dur-base) var(--ease-out),
    background-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out);
}

.case--cta:hover .case__cta-box {
  border-color: var(--c-white);
  background: var(--c-white);
  color: var(--c-black);
}

/* ---------- a régua que enche o cartão ----------
   Sobe de baixo para cima no último trecho da rolagem, com a seção
   ainda presa. Não é um fundo branco atrás do texto: é uma camada de
   `difference` por cima de tudo, então onde ela passa o preto vira
   branco e o texto branco vira preto de uma vez só. O cartão não muda
   de cor, ele vira do avesso - que é o mesmo gesto da inversão do
   header, aplicado ao último objeto da faixa.

   Sem JS a régua fica em zero e o cartão continua sendo o cartão de
   contorno que já era. */
.case__cta-fill {
  position: absolute;
  inset: 0;
  z-index: 3;
  background: var(--c-white);
  mix-blend-mode: difference;
  transform: scaleY(0);
  transform-origin: bottom;
  pointer-events: none;
}

/* A SETA SAIU DESTE CARTÃO (25/08), na home e nas três frentes. Ela
   ficava fora do eixo: o cartão é uma caixa alta e centrada, o texto
   quebrava em duas linhas e a seta - centrada só na vertical, ao lado -
   empurrava o conjunto para a esquerda. Lia como desalinhamento, e era.
   Sem ela a frase cabe em uma linha em mais resoluções e, quando não
   cabe, quebra centrada como qualquer título. O destino do cartão já
   está dito pelo texto e pela régua que o inverte no fim da faixa.
   As regras de tamanho e deslocamento da seta saíram junto: não há mais
   `.btn__arrow` dentro deste cartão em página nenhuma. */

/* Sem pin: a faixa vira rolagem horizontal do próprio navegador,
   com encaixe por card. */
.pan.is-swipe {
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scrollbar-width: thin;
}

.pan.is-swipe .case {
  scroll-snap-align: start;
}

@media (max-width: 767px) {
  /* No celular não há pin: a seção volta a ser fluxo normal e o cartão
     é medido pela largura, que ali é o que falta. */
  .cases__inner {
    min-height: 0;
    padding-block: clamp(var(--sp-8), 10vh, var(--sp-10));
  }
  .pan__track > .case {
    --case-w: 76vw;
  }
  .pan__track > .case .case__frame,
  .pan__track > .case.case--cta .case__cta-box {
    height: auto;
    width: 100%;
  }
}

/* ============================================================
   CENA 6 - CONTEÚDO
   ============================================================ */
/* Escura, como o resto da página.
   O off-white é exclusivo da convergência do método: ele é a única
   troca de tema do site, e é coreografada. Um segundo trecho claro aqui
   transformaria a virada em alternância, e alternância não comunica
   clímax nenhuma. */
.notas {
  background: var(--c-black);
  color: var(--c-white);
}

.notas__head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5) var(--sp-7);
  margin-bottom: var(--sp-8);
}

.notas__head-texto {
  display: grid;
  gap: var(--sp-4);
  max-width: 46ch;
}

.notas__head-cta {
  flex: none;
}

.notas__head .t-muted {
  color: var(--c-gray-on-dark);
}

.notas__list {
  border-top: 1px solid var(--c-line-dark);
}

.nota {
  position: relative;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: var(--sp-5);
  align-items: center;
  padding-block: clamp(var(--sp-5), 3.5vh, var(--sp-7));
  border-bottom: 1px solid var(--c-line-dark);
  transition: padding-left var(--dur-base) var(--ease-out);
}

.nota:hover {
  padding-left: var(--sp-5);
}

.nota__title {
  font-size: clamp(1.25rem, 2.4vw, 2rem);
  font-weight: 300;
  letter-spacing: var(--tr-display);
  line-height: 1.15;
  max-width: 24ch;
}

/* O CTA da linha é uma pílula de contorno, e não um texto cinza: esta é
   a única saída de cada nota, e como texto discreto ela não estava
   sendo lida como saída nenhuma. */
.nota__cta {
  display: inline-flex;
  align-items: center;
  justify-self: end;
  padding: 0.72em 1.3em;
  border: 1px solid var(--c-line-dark);
  border-radius: var(--r-pill);
  font-size: var(--fs-small);
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  transition:
    background-color var(--dur-base) var(--ease-out),
    border-color var(--dur-base) var(--ease-out),
    color var(--dur-base) var(--ease-out);
}

.nota:hover .nota__cta,
.nota:focus-visible .nota__cta {
  background: var(--c-white);
  border-color: var(--c-white);
  color: var(--c-black);
}

/* A imagem segue o cursor. Medida fixa e igual para todas: o que muda
   entre as notas é a imagem, não o tamanho dela - uma miniatura maior
   numa linha faz a lista parecer hierarquizada sem ser. */
.nota__thumb {
  position: absolute;
  top: 50%;
  right: 18%;
  width: 210px;
  aspect-ratio: 4 / 3;
  translate: 0 -50%;
  border-radius: var(--r-squircle-4x3);
  overflow: hidden;
  opacity: 0;
  scale: 0.9;
  pointer-events: none;
  transition:
    opacity var(--dur-base) var(--ease-out),
    scale var(--dur-base) var(--ease-out);
}

.nota__thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

@media (hover: hover) and (pointer: fine) {
  .nota:hover .nota__thumb {
    opacity: 1;
    scale: 1;
  }
}

@media (max-width: 767px) {
  /* Sem cursor não há imagem que o siga, e a nota ficava sendo só uma
     linha de texto. Aqui a miniatura volta ao fluxo, acima do título. */
  .nota {
    grid-template-columns: 1fr;
    justify-items: start;
    gap: var(--sp-4);
  }
  .nota__thumb {
    position: static;
    width: 100%;
    aspect-ratio: 16 / 9;
    translate: none;
    scale: 1;
    opacity: 1;
    border-radius: var(--r-squircle-16x9);
  }
  .nota__cta {
    justify-self: start;
  }
}

/* ============================================================
   CENA 7 - FECHAMENTO E CONTATO
   ============================================================ */
.contato {
  position: relative;
  background: var(--c-black);
  color: var(--c-white);
  overflow: hidden;
}

/* Segunda e última aparição do gradiente na página.
   Ele mora atrás da coluna de texto, fora do caminho do formulário:
   um brilho colorido por trás de campos de entrada derruba o contraste
   justamente onde a conversão acontece. */
.contato__glow {
  position: absolute;
  top: -22%;
  left: -18%;
  width: min(46vw, 520px);
  aspect-ratio: 1;
  border-radius: var(--r-squircle);
  background:
    radial-gradient(70% 70% at 30% 20%, var(--grad-1), transparent 70%),
    radial-gradient(70% 70% at 70% 80%, var(--grad-5), transparent 70%),
    var(--grad-3);
  filter: blur(110px);
  opacity: 0.3;
  pointer-events: none;
}

.contato__grid {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.05fr);
  gap: clamp(var(--sp-7), 6vw, var(--sp-10));
  align-items: start;
}

.contato__pitch {
  display: grid;
  gap: var(--sp-5);
  align-content: start;
}

.contato__sub {
  color: rgba(255, 255, 255, 0.74);
  max-width: 44ch;
}

.contato__direct {
  display: grid;
  gap: var(--sp-4);
  margin-top: var(--sp-6);
  padding-top: var(--sp-6);
  border-top: 1px solid var(--c-line-dark);
  font-size: var(--fs-small);
}

.contato__direct dt {
  font-size: var(--fs-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--c-gray-on-dark);
  margin-bottom: var(--sp-1);
}

.contato__direct dd {
  margin: 0;
}

/* ---------- celular: o formulário entra antes dos canais ----------
   Mesmo pedido, mesma razão e mesmo remédio de `/contato` (25/08). Aqui
   a peça tem uma diferença: os canais (`.contato__direct`) não são irmãos
   do formulário, moram dentro de `.contato__pitch` junto com a manchete e
   o convite. Empilhado, isso punha manchete + convite + quatro canais na
   frente do primeiro campo.

   `display: contents` no pitch faz os três filhos dele virarem itens da
   própria grade, e aí o `order` alcança cada um: manchete e convite ficam
   onde estão (o formulário precisa do convite que o apresenta), e só os
   CANAIS descem para depois. É exatamente o que o pedido diz - "o
   formulário antes das informações de contato" -, e não "antes de tudo".

   O vão da grade passa a valer entre TODOS eles, então ele desce para o
   `--sp-5` que era o vão interno do pitch; quem precisa de mais ar pede
   por conta própria (o formulário abaixo, e os canais no filete que já
   tinham). */
@media (max-width: 900px) {
  .contato__grid {
    grid-template-columns: 1fr;
    gap: var(--sp-5);
  }

  .contato__pitch {
    display: contents;
  }

  .contato__grid > .form {
    order: 1;
    margin-top: var(--sp-6);
  }

  .contato__direct {
    order: 2;
  }
}

/* ============================================================
   RESPOSTAS DIRETAS (AEO)
   ============================================================ */
.faq {
  background: var(--c-black);
  color: var(--c-white);
  border-top: 1px solid var(--c-line-dark);
}

.faq__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.6fr);
  gap: clamp(var(--sp-6), 5vw, var(--sp-9));
  align-items: start;
}

.faq__item {
  border-bottom: 1px solid var(--c-line-dark);
}

.faq__item summary {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-5);
  padding-block: var(--sp-5);
  cursor: pointer;
  list-style: none;
  font-size: var(--fs-title);
  font-weight: 300;
  letter-spacing: var(--tr-display);
}

.faq__item summary::-webkit-details-marker {
  display: none;
}

.faq__sign {
  flex: none;
  width: 14px;
  height: 14px;
  position: relative;
}

.faq__sign::before,
.faq__sign::after {
  content: "";
  position: absolute;
  inset: 50% 0 auto 0;
  height: 1px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-out);
}

.faq__sign::after {
  transform: rotate(90deg);
}

.faq__item[open] .faq__sign::after {
  transform: rotate(0deg);
}

.faq__answer {
  padding-bottom: var(--sp-6);
  color: var(--c-gray-on-dark);
  max-width: 68ch;
}

@media (max-width: 900px) {
  .faq__grid {
    grid-template-columns: 1fr;
  }
}

/* ============================================================
   PRODUTOS PROPRIETÁRIOS
   Duas caixas 50/50 entre o formulário e as respostas diretas.

   É o único respiro claro de uma página preta do topo ao rodapé, e a
   quebra é o argumento: o que está aqui não é conteúdo do grupo, são
   duas marcas com identidade própria. A seção hospeda, não assina - e
   por isso cada caixa aparece na superfície da própria KV: o Velk em
   azul-marinho, o Krama em branco com o contorno em gradiente.

   As paletas moram em variáveis locais de cada caixa, e não em
   `data-theme`: a folha de tokens reserva o tema para cena inteira.
   Aqui a cena não inverte - são dois objetos com superfície própria.
   ============================================================ */
.produtos {
  background: var(--c-offwhite);
  color: var(--c-black);
  --fg: var(--c-black);
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
}

.produtos__head {
  display: grid;
  gap: var(--sp-4);
  margin-bottom: clamp(var(--sp-6), 5vh, var(--sp-8));
}

/* Teto próprio, e maior que os 22ch de `.t-h2`: a manchete tem 28
   caracteres e cabe numa linha só, mas em 22ch (528px na fonte de 48px)
   ela quebrava em duas e a segunda ficava com duas palavras. */
.produtos__head .t-h2 {
  max-width: 34ch;
}

/* Duas linhas, e a medida foi aferida no navegador: neste teto os 126
   caracteres caem em duas linhas cheias. */
.produtos__sub {
  max-width: 63ch;
  color: var(--fg-muted);
}

.produtos__grade {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(var(--sp-4), 2vw, var(--sp-6));
  align-items: stretch;
}

/* Raio um passo acima do cartão de case (16px): a caixa tem três vezes
   o tamanho dele, e raio fixo em peça grande devolve menos maciez
   aparente do que o mesmo número em peça pequena. */
.produto {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  display: flex;
  min-height: clamp(360px, 44vh, 480px);
  padding: clamp(var(--sp-5), 2.6vw, var(--sp-7));
  /* A borda existe nas duas caixas, e só aparece numa. Sem ela aqui, o
     contorno de 2px do Krama encurtava a caixa de conteúdo dele em 4px
     e o pé assentava dois pixels acima do vizinho - desalinho pequeno
     o suficiente para ninguém saber nomear e grande o suficiente para
     todo mundo sentir. */
  border: 2px solid transparent;
  border-radius: clamp(16px, 1.8vw, 24px);
  background: var(--bg);
  color: var(--fg);
}

/* A arte da marca mora numa camada só dela, atrás do texto. `inset: 0`
   com o recorte da caixa: o elemento sangra pela borda e é a caixa que
   corta, não uma máscara a mais. */
.produto__arte {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
}

.produto__corpo {
  position: relative;
  z-index: 1;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  width: 100%;
}

/* O logotipo entregue no lugar do nome digitado. A faixa reserva a
   altura do mais alto dos dois: sem o piso, o wordmark de 30px
   empurrava um título 5px abaixo do outro. */
.produto__origem {
  display: flex;
  align-items: center;
  min-height: 30px;
}

.produto__logo {
  height: var(--alt-logo, 26px);
  width: auto;
  display: block;
}

/* O vazio inteiro entre o logotipo e o título. É o único `auto` do
   bloco: com dois, o espaço livre se dividiria entre eles e o grupo de
   baixo desgrudaria da base. Assim o logotipo fica no topo, o resto
   assenta no pé, e o vazio no meio é o que dá escala à caixa. */
.produto__titulo {
  margin-top: auto;
  font-weight: 300;
  font-size: var(--fs-title);
  line-height: 1.14;
  letter-spacing: var(--tr-display);
  max-width: 20ch;
}

/* O piso reserva quatro linhas, e existe por causa do empilhamento: com
   o grupo de baixo ancorado no pé, a diferença de altura entre os dois
   textos sobe para o título. O do Velk tem 163 caracteres e cai em
   quatro linhas; o do Krama tem 115 e cai em três - e os dois títulos
   ficavam 25px fora de registro, exatamente uma entrelinha. Duas
   manchetes lado a lado com uma linha de diferença não leem como
   composição, leem como erro.

   O piso absorve a sobra AQUI, entre o texto e o botão, que é onde ela
   não aparece. 6em são quatro linhas porque a entrelinha é 1,5 - os
   dois números andam juntos, e mexer num pede mexer no outro. */
.produto__texto {
  font-size: var(--fs-body);
  line-height: 1.5;
  min-height: calc(4 * 1.5em);
  max-width: 38ch;
  color: var(--fg-muted);
}

.produto__pe {
  padding-top: var(--sp-3);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-4);
}

/* ---------- Velk ----------
   A cor do KV: azul-marinho com o brilho violeta entrando por cima, e a
   grade fina que o material da marca usa de fundo. A grade é CSS e não
   o `velk-pattern.svg` de 36KB - são linhas retas, e desenhá-las custa
   duas declarações. */
.produto--velk {
  --bg: #1b1652;
  --fg: var(--c-white);
  --fg-muted: rgba(255, 255, 255, 0.74);
  --line: rgba(255, 255, 255, 0.16);
  --alt-logo: 30px;
  background:
    radial-gradient(118% 86% at 64% -12%, rgba(90, 78, 251, 0.42) 0%, rgba(90, 78, 251, 0) 60%),
    var(--bg);
}

.produto--velk .produto__arte {
  background-image:
    repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.05) 0 1px, transparent 1px 64px);
  -webkit-mask-image: radial-gradient(90% 70% at 78% 8%, #000 0%, transparent 72%);
  mask-image: radial-gradient(90% 70% at 78% 8%, #000 0%, transparent 72%);
}

.produto--velk .produto__arte img {
  position: absolute;
  right: -4%;
  bottom: -14%;
  height: 62%;
  width: auto;
  opacity: 0.22;
}

.produto--velk .produto__cta {
  --btn-bg: #5a4efb;
  --btn-fg: var(--c-white);
}

.produto--velk .produto__cta:hover {
  --btn-bg: var(--c-white);
  --btn-fg: #1b1652;
}

/* ---------- Krama ----------
   O contorno em gradiente é a assinatura da KV, e é CSS e não bitmap:
   as quatro paradas saem do próprio logotipo e o traço acompanha
   qualquer altura de caixa. Esticado como imagem, ele deformaria junto
   com o conteúdo.

   Duas camadas no mesmo `background`: a de cima pinta a superfície até
   a borda interna (`padding-box`), a de baixo pinta o gradiente até a
   externa (`border-box`). A borda transparente é a janela por onde a de
   baixo aparece. */
.produto--krama {
  --bg: #ffffff;
  --fg: #181818;
  --fg-muted: var(--c-gray-on-light);
  --line: var(--c-line-light);
  --alt-logo: 23px;
  background:
    linear-gradient(var(--bg), var(--bg)) padding-box,
    linear-gradient(135deg, #ff00f4 0%, #b72aff 34%, #00c6fc 68%, #ffc100 100%) border-box;
}

/* O brilho do KV, na mesma diagonal do contorno. Fica no canto oposto
   ao texto e some antes de chegar nele. */
.produto--krama .produto__arte {
  background:
    radial-gradient(120% 100% at 108% 6%, rgba(255, 0, 244, 0.20) 0%, rgba(183, 42, 255, 0.12) 34%, rgba(0, 198, 252, 0) 62%);
}

/* Preto da própria marca (#181818, o mesmo do wordmark), e o magenta do
   KV no hover - com texto escuro, porque branco sobre #ff00f4 dá 3,2:1
   e não passa. Escuro sobre magenta dá 5,5:1. */
.produto--krama .produto__cta {
  --btn-bg: #181818;
  --btn-fg: var(--c-white);
}

.produto--krama .produto__cta:hover {
  --btn-bg: #ff00f4;
  --btn-fg: #181818;
}

@media (max-width: 900px) {
  .produtos__grade {
    grid-template-columns: 1fr;
  }

  /* Empilhadas, as caixas não precisam mais reservar altura para
     alinhar uma com a outra: quem manda passa a ser o conteúdo. */
  .produto {
    min-height: 0;
  }

  /* Sem a altura reservada, o vazio do meio deixa de existir e o
     logotipo colaria no título. Um respiro fixo no lugar do `auto`. */
  .produto__titulo {
    margin-top: var(--sp-6);
  }

  /* Empilhadas, as caixas não disputam registro nenhuma com a outra, e
     o piso do texto viraria um vazio visível antes do botão. */
  .produto__texto {
    min-height: 0;
  }

  .produto--velk .produto__arte img {
    height: 52%;
    bottom: -10%;
  }
}
