/* =====================================================================
   HAL-AI — CAMADA DE MOVIMENTO (motion.css)
   Carregada DEPOIS de style.css em todas as páginas.

   Princípios (não negociáveis):
   1. Anima-se apenas transform, opacity, filter e background-position.
      Nunca top/left/width/height/margin.
   2. will-change só onde há transform contínuo (marquee, blobs, borda
      cônica). Em mais nada.
   3. Nenhum style="" no HTML-fonte: atraso, variante e ritmo vêm de
      atributo (data-*) ou classe. O JS pode escrever custom properties
      em runtime (style.setProperty) — isso é permitido.
   4. O bloco @media (prefers-reduced-motion: reduce) no fim do arquivo é
      o ÚNICO lugar do projeto onde !important é permitido.
   5. Nada gera overflow horizontal: todo brilho/blob mora em contêiner
      com overflow:hidden.

   Ordem: tokens > reveal > hero > borda cônica > rimlight > spotlight >
   marquee > blobs > texto gradiente > botões > contador > wipe >
   linhas de dados > barra de progresso > trilho > abas/menu >
   elevação e microinterações > desdobramento 3D das telas (18) >
   fundos de IA reativos ao cursor (19) > efeitos premium (20) >
   animação ligada ao scroll (21) > parallax das telas do app (22) >
   cascata por contêiner (23) > botão magnético (24) >
   linha do tempo que se desenha (25) > número em destaque (26) >
   borda de transição entre seções (27) > cartão em camadas (28) >
   cursor de precisão (29) > overshoot do desdobramento (30) >
   rastro do cursor (33) > fundo reativo ao mouse (34) >
   3D intenso: desdobramento, cena e carta (35) > acabamento (36) >
   desdobramento 3D extremo (37) > abertura cinematográfica (38) >
   o site se construindo (39) >
   responsivo (31, 31-BIS, 37-BIS e 38-BIS) > reduced-motion (32).

   6. Animação ligada ao scroll (animation-timeline: view()) SEMPRE
      dentro de @supports, e o estado declarado FORA do @supports é o
      ESTADO FINAL — quem não suporta vê a peça pronta, nunca vazia.
   7. A abertura (38) é a ÚNICA peça que roda sem gatilho de scroll nem
      de ponteiro: é automática, dura menos de 3s do primeiro traço à
      cortina, tem botão de pular sempre focável e não existe em
      <=700px nem para quem pediu menos movimento.
   ===================================================================== */


/* =====================================================================
   1. TOKENS DE MOVIMENTO
   As duas curvas do sistema. --ease é a curva de ENTRADA (saída suave,
   usada em hover, reveal e cascata); --ease-io é simétrica, para loops,
   crossfades e wipe. O que gira ou preenche continuamente usa linear.
   ===================================================================== */
:root {
  /* curvas do sistema (alinha style.css ao padrão de movimento do site) */
  --ease: cubic-bezier(.12, .23, .5, 1);
  --ease-io: cubic-bezier(.44, 0, .56, 1);

  /* ritmos */
  --t-fast: .2s;    /* estado: hover, foco, cor */
  --t-mid: .4s;     /* troca de painel, abertura de menu */
  --t-slow: .6s;    /* reveal de scroll */
  --t-hero: 1s;     /* entrada de load do hero */
  --stagger: 100ms; /* assinatura temporal: tudo escalona de 100 em 100 */

  /* durações de loop (sobrescrevíveis por elemento) */
  --marquee-dur: 40s;
  --glow-dur: 3s;
  --gradient-dur: 6s;
  --pulse-dur: 3.4s;

  /* deslocamentos de entrada */
  --reveal-y: 20px;
  --reveal-x: 28px;
}

/* Registra o ângulo da borda cônica como propriedade animável.
   Sem @property (navegador antigo) a animação de --a simplesmente não
   roda: a borda fica com o gradiente parado, que continua bonito.
   O fallback com rotate real está em .glow-border--rotate. */
@property --a {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}


/* =====================================================================
   2. REVEAL EM SCROLL — [data-reveal] → .is-visible
   O JS observa o elemento uma única vez (once) e adiciona .is-visible.
   Elementos que entram no SCROLL sobem; os que entram no LOAD descem
   (ver seção 3). A distinção é deliberada.
   ===================================================================== */
[data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-y), 0);
  transition:
    opacity var(--t-slow) var(--ease),
    transform var(--t-slow) var(--ease);
}

[data-reveal].is-visible {
  opacity: 1;
  transform: none;
}

/* variantes de direção */
[data-reveal="left"]  { transform: translate3d(calc(var(--reveal-x) * -1), 0, 0); }
[data-reveal="right"] { transform: translate3d(var(--reveal-x), 0, 0); }
[data-reveal="scale"] { transform: scale(.94); }
[data-reveal="fade"]  { transform: none; }

[data-reveal="left"].is-visible,
[data-reveal="right"].is-visible,
[data-reveal="scale"].is-visible { transform: none; }

/* escalonamento — 100ms por degrau, teto de 600ms.
   Nada de style="transition-delay:.3s" no HTML. */
[data-reveal-delay="1"] { transition-delay: 100ms; }
[data-reveal-delay="2"] { transition-delay: 200ms; }
[data-reveal-delay="3"] { transition-delay: 300ms; }
[data-reveal-delay="4"] { transition-delay: 400ms; }
[data-reveal-delay="5"] { transition-delay: 500ms; }
[data-reveal-delay="6"] { transition-delay: 600ms; }



/* =====================================================================
   3. ENTRADA DO HERO NO LOAD — .hero [data-hero-in]
   Vem DE CIMA (-20px) e ENCOLHE de scale(1.06) → 1. É o oposto do
   reveal de scroll, e é isso que faz a dobra "assentar" em vez de
   "subir". Cascata de 100ms, sem JS: dispara sozinho no load.
   'both' mantém o estado inicial durante o delay (sem flash).
   ===================================================================== */
@keyframes hero-in {
  from {
    opacity: 0;
    transform: translate3d(0, -20px, 0) scale(1.06);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.hero [data-hero-in] {
  opacity: 0;
  animation: hero-in var(--t-hero) var(--ease) both;
}

.hero [data-hero-in="1"] { animation-delay: 100ms; }
.hero [data-hero-in="2"] { animation-delay: 200ms; }
.hero [data-hero-in="3"] { animation-delay: 300ms; }
.hero [data-hero-in="4"] { animation-delay: 400ms; }
.hero [data-hero-in="5"] { animation-delay: 500ms; }
.hero [data-hero-in="6"] { animation-delay: 600ms; }

/* Mídia do hero: mesma entrada, um pouco mais longa e com repouso 3D
   leve. perspective fica DENTRO do transform para não criar contexto
   3D na seção inteira. */
@keyframes hero-shot-in {
  from {
    opacity: 0;
    transform: perspective(1200px) translate3d(0, -24px, 0) scale(1.06) rotateX(8deg);
  }
  to {
    opacity: 1;
    transform: perspective(1200px) translate3d(0, 0, 0) scale(1) rotateX(0deg);
  }
}

.hero [data-hero-in="media"] {
  opacity: 0;
  animation: hero-shot-in 1.2s var(--ease) 400ms both;
}


/* =====================================================================
   4. BORDA COM GRADIENTE CÔNICO GIRANDO — .glow-border
   A assinatura visual. Uma moldura de 1px em que o gradiente gira; o
   truque é mascarar o preenchimento (content-box XOR padding-box), de
   modo que sobre só a borda. O ::before tem pointer-events:none e fica
   abaixo do conteúdo.
   USO: no máximo UM .glow-border girando por viewport. Para os demais,
   use .glow-border--hover (gira só no hover/foco) — conic girando é
   repintura de GPU a cada frame.
   ===================================================================== */
.glow-border {
  position: relative;
  isolation: isolate;
  border-radius: var(--radius-lg);
}

/* Decoração vai para z-index -1 (o `isolation: isolate` do pai impede que ela
   escape do contexto). Assim TODO filho — estático ou posicionado — pinta por
   cima naturalmente, e não precisamos forçar `position: relative` nos filhos.
   A regra antiga (`> * { position: relative }`) sobrescrevia o
   `position: absolute` de quem depende dele: foi o que desmontou o selo
   "USPTO 2025" sobre a imagem da patente. */
.glow-border > * { z-index: 1; }

/* Estado base (também é o fallback se a máscara não existir): borda
   estática de gradiente da marca, sem custo nenhum. */
.glow-border::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  background: linear-gradient(120deg, var(--brand), var(--brand-2), transparent 70%);
  opacity: .5;
}

@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .glow-border::before {
    padding: 1px;
    opacity: 1;
    background: conic-gradient(from var(--a),
      transparent 0 60%,
      var(--brand) 75%,
      var(--brand-2) 85%,
      transparent 100%);
    -webkit-mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    mask-composite: exclude;
    will-change: transform;
    animation: glow-spin var(--glow-dur) linear infinite;
  }
}

@keyframes glow-spin {
  to { --a: 360deg; }
}

/* Gira só no hover/foco — versão recomendada para grade de cards. */
.glow-border--hover::before { animation-play-state: paused; }

.glow-border--hover:hover::before,
.glow-border--hover:focus-within::before { animation-play-state: running; }

/* Halo difuso opcional (estado "IA processando"): aura cônica fora da
   borda. Só entra/sai — blur nunca em loop. */
.glow-border--aura::after {
  content: "";
  position: absolute;
  inset: -2px;
  z-index: 0;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background: conic-gradient(from var(--a),
    var(--brand), var(--brand-2), var(--accent), var(--brand));
  filter: blur(0);
  transition:
    opacity .5s var(--ease-io),
    filter .5s var(--ease-io);
}

.glow-border--aura.is-busy::after,
.glow-border--aura:hover::after {
  opacity: .35;
  filter: blur(18px);
}

/* FALLBACK EXPLÍCITO (navegador sem @property, ou quando se quer o
   fundo opaco do card por cima do giro): o quadrado cônico gira com
   transform e um painel interno de 1px de recuo tapa o miolo.
   Precisa de overflow:hidden no elemento. */
@keyframes glow-rotate {
  to { transform: rotate(360deg); }
}

.glow-border--rotate {
  overflow: hidden;
  background-color: var(--bg-1);
}

.glow-border--rotate::before {
  inset: -150%;
  padding: 0;
  border-radius: 0;
  opacity: 1;
  background: conic-gradient(from 0deg,
    transparent 0 60%,
    var(--brand) 75%,
    var(--brand-2) 85%,
    transparent 100%);
  -webkit-mask: none;
  mask: none;
  will-change: transform;
  animation: glow-rotate var(--glow-dur) linear infinite;
}

.glow-border--rotate::after {
  content: "";
  position: absolute;
  inset: 1px;
  z-index: 0;
  border-radius: inherit;
  background-color: var(--bg-1);
  opacity: 1;
  filter: none;
}


/* =====================================================================
   5. RIMLIGHT — .rimlight
   Luz radial que sobe da borda inferior INTERNA do card. Não é glow
   externo: é a lâmpada embutida no rodapé. Custo zero (só opacity) e
   funciona no teclado via :focus-within.
   ===================================================================== */
.rimlight {
  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.rimlight::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: .28;
  background: radial-gradient(41% 40% at 50% 100%,
    rgba(129, 140, 248, .35),
    transparent 100%);
  transition: opacity .3s var(--ease-io);
}

.rimlight > * { z-index: 1; }

.rimlight:hover::before,
.rimlight:focus-within::before,
.rimlight.is-featured::before { opacity: 1; }

/* variante fria (azul de marca) para blocos sobre fundo mais claro */
.rimlight--brand::before {
  background: radial-gradient(41% 40% at 50% 100%,
    rgba(8, 11, 239, .45),
    transparent 100%);
}

/* variante de sucesso, para cards de resultado/checklist */
.rimlight--success::before {
  background: radial-gradient(41% 40% at 50% 100%,
    rgba(34, 197, 94, .28),
    transparent 100%);
}


/* =====================================================================
   6. SPOTLIGHT QUE SEGUE O CURSOR — [data-spotlight]
   O JS escreve --mx/--my via style.setProperty (permitido). O HTML-fonte
   não tem style="". Sem JS o gradiente fica centrado e invisível
   (opacity 0), então nada quebra.
   ===================================================================== */
[data-spotlight] {
  position: relative;
  --mx: 50%;
  --my: 50%;
  isolation: isolate;
}

[data-spotlight]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(340px circle at var(--mx) var(--my),
    rgba(8, 11, 239, .20),
    transparent 60%);
  transition: opacity .25s var(--ease);
}

[data-spotlight] > * { z-index: 1; }

[data-spotlight]:hover::after,
[data-spotlight]:focus-within::after { opacity: 1; }

/* aura clara — para superfícies muito escuras, onde o azul da marca
   praticamente some */
[data-spotlight="accent"]::after {
  background: radial-gradient(340px circle at var(--mx) var(--my),
    rgba(129, 140, 248, .18),
    transparent 60%);
}


/* =====================================================================
   7. MARQUEE INFINITO — [data-marquee] + .marquee-track
   O JS duplica o conteúdo do track (a cópia leva aria-hidden="true"), e
   o track desloca -50% em loop linear. Máscara de fade nas bordas.
   DIFERENÇA DELIBERADA em relação ao benchmark: PAUSA no hover e no
   focus-within — requisito de WCAG 2.2.2 para movimento acima de 5s.
   Duração por --marquee-dur (faixas diferentes, ritmos diferentes).
   ===================================================================== */
[data-marquee] {
  position: relative;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.marquee-track {
  display: flex;
  align-items: center;
  gap: var(--marquee-gap, 48px);
  width: max-content;
  will-change: transform;
  animation: marquee-run var(--marquee-dur) linear infinite;
}

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

[data-marquee]:hover .marquee-track,
[data-marquee]:focus-within .marquee-track { animation-play-state: paused; }

/* sentido inverso — para a segunda faixa de um par */
[data-marquee="reverse"] .marquee-track,
.marquee--reverse .marquee-track { animation-direction: reverse; }

/* ritmos prontos */
.marquee--slow { --marquee-dur: 60s; }
.marquee--fast { --marquee-dur: 26s; }

/* itens da faixa: leve realce ao passar o mouse (a faixa já parou) */
.marquee-track > * { flex: 0 0 auto; }

.marquee-item {
  opacity: .72;
  filter: grayscale(1);
  transition:
    opacity var(--t-fast) var(--ease),
    filter var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

.marquee-item:hover,
.marquee-item:focus-visible {
  opacity: 1;
  filter: grayscale(0);
  transform: translate3d(0, -2px, 0);
}


/* =====================================================================
   8. BLOBS DE FUNDO — .blobs
   Duas manchas grandes e borradas que derivam devagar. Sempre dentro de
   um contêiner com overflow:hidden (o .hero já tem). z-index 0 e
   pointer-events:none: nunca capturam clique nem empurram layout.
   ===================================================================== */
.blobs {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  contain: paint;
}

.blobs::before,
.blobs::after {
  content: "";
  position: absolute;
  border-radius: 50%;
  filter: blur(120px);
  will-change: transform;
}

.blobs::before {
  width: 520px;
  height: 520px;
  top: -180px;
  left: -140px;
  background: rgba(8, 11, 239, .16);
  animation: blob-drift-a 26s var(--ease-io) infinite alternate;
}

.blobs::after {
  width: 420px;
  height: 420px;
  right: -120px;
  bottom: -160px;
  background: rgba(84, 4, 148, .14);
  animation: blob-drift-b 18s var(--ease-io) infinite alternate;
}

@keyframes blob-drift-a {
  to { transform: translate3d(90px, 60px, 0) scale(1.12); }
}

@keyframes blob-drift-b {
  to { transform: translate3d(-80px, -50px, 0) scale(1.08); }
}

/* Contêiner genérico para quem não é .hero. */
.blobs-wrap {
  position: relative;
  overflow: hidden;
}

.blobs-wrap > *:not(.blobs) { position: relative; z-index: 1; }

/* O .hero do style.css já tem dois blobs estáticos em ::before/::after.
   Quando a página inserir <div class="blobs">, desligamos os estáticos —
   dois blurs sobrepostos custam caro e lavam a seção. */
@supports selector(:has(*)) {
  .hero:has(> .blobs)::before,
  .hero:has(> .blobs)::after { display: none; }
}

/* Granulação sutil: mata o banding dos gradientes radiais grandes sobre
   o roxo escuro. Não anima — é correção técnica, não efeito. */
.grain::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 160px 160px;
}


/* =====================================================================
   9. TEXTO COM GRADIENTE ANIMADO — .gradient-text--animated
   Estende o .gradient-text do style.css. O deslize é de
   background-position (composto, barato). As pontas ficam em --accent:
   sobre #0d0524 o azul puro da marca tem contraste baixo demais e some.
   ===================================================================== */
.gradient-text--animated {
  color: var(--accent); /* fallback antes do clip */
  background-image: linear-gradient(90deg,
    var(--accent) 0%,
    var(--brand-2) 30%,
    var(--brand) 50%,
    var(--brand-2) 70%,
    var(--accent) 100%);
  background-size: 200% 100%;
  background-position: 0% 0;
  -webkit-background-clip: text;
  background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: gradient-slide var(--gradient-dur) linear infinite;
}

@keyframes gradient-slide {
  to { background-position: 200% 0; }
}

/* Borda com gradiente deslizante — versão linear e barata do conic,
   para quando o card não é o destaque principal da viewport. */
.slide-border {
  position: relative;
  isolation: isolate;
  border-radius: var(--radius);
}

.slide-border > * { z-index: 1; }

@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .slide-border::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    padding: 1px;
    border-radius: inherit;
    pointer-events: none;
    background: linear-gradient(90deg,
      var(--brand), var(--brand-2), var(--accent), var(--brand));
    background-size: 200% 100%;
    -webkit-mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    mask-composite: exclude;
    animation: gradient-slide var(--gradient-dur) linear infinite;
  }
}


/* =====================================================================
   10. BOTÃO PREMIUM — brilho que atravessa
   O botão NÃO se move além do que o style.css já faz: o brilho é um
   ::after que translada. isolation + overflow garantem que o brilho não
   vaze e não crie scroll horizontal.
   ===================================================================== */
.btn-primary,
.btn-success {
  position: relative;
  isolation: isolate;
  overflow: hidden;
}

.btn-primary > *,
.btn-success > * { position: relative; z-index: 1; }

.btn-primary::after,
.btn-success::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background: linear-gradient(105deg,
    transparent 38%,
    rgba(255, 255, 255, .28) 50%,
    transparent 62%);
  transform: translate3d(-130%, 0, 0);
  transition: transform .65s var(--ease-io);
}

.btn-primary:hover::after,
.btn-primary:focus-visible::after,
.btn-success:hover::after,
.btn-success:focus-visible::after { transform: translate3d(130%, 0, 0); }

/* Brilho pulsando no CTA principal de uma seção — use uma vez por
   página, no botão que realmente importa. */
@keyframes cta-breathe {
  0%, 100% { box-shadow: 0 14px 34px rgba(8, 11, 239, .35); }
  50%      { box-shadow: 0 18px 46px rgba(129, 140, 248, .55); }
}

.btn--breathe { animation: cta-breathe 3.2s var(--ease-io) infinite; }

/* Estado de envio do formulário: spinner no lugar do movimento. */
#contact-submit-btn.is-loading {
  pointer-events: none;
  opacity: .75;
}

#contact-submit-btn.is-loading::before {
  content: "";
  width: 15px;
  height: 15px;
  margin-right: 4px;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin 700ms linear infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}


/* =====================================================================
   11. CONTADOR — [data-count]
   O JS conta de 0 até o número. O CSS só garante que o número não
   trema: tabular-nums fixa a largura dos dígitos.
   ===================================================================== */
[data-count] {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
  letter-spacing: -.01em;
}

.stat-value {
  display: block;
  font-size: clamp(2rem, 4.4vw, 3rem);
  font-weight: 800;
  line-height: 1.05;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.stat-label {
  display: block;
  margin-top: 6px;
  font-size: .92rem;
  color: var(--muted);
}


/* =====================================================================
   12. WIPE REVEAL DE SEÇÃO — .wipe
   clip-path é composto na GPU e não força layout (ao contrário de
   animar height). Revela de baixo para cima quando ganha .is-visible.
   ===================================================================== */
.wipe {
  clip-path: inset(0 0 100% 0);
  transition: clip-path .9s var(--ease-io);
}

.wipe.is-visible { clip-path: inset(0 0 0 0); }

/* Combinado com um deslocamento leve do conteúdo, vira "cortina". */
.wipe > * {
  transform: translate3d(0, 24px, 0);
  transition: transform .9s var(--ease-io);
}

.wipe.is-visible > * { transform: none; }

/* variantes de direção */
.wipe--down { clip-path: inset(100% 0 0 0); }
.wipe--left { clip-path: inset(0 100% 0 0); }
.wipe--down.is-visible,
.wipe--left.is-visible { clip-path: inset(0 0 0 0); }


/* =====================================================================
   13. LINHAS CONVERGENTES COM PULSOS DE DADOS — .data-lines
   A página insere o SVG inline; aqui está o CSS que o anima. Cada
   caminho estático é um <path> comum; cada pulso leva class="data-pulse"
   mais uma classe de defasagem .data-pulse--1 … --6 (nada de style="").
   Ajuste --pulse-len ao comprimento real do path se quiser precisão.
   ===================================================================== */
.data-lines {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  overflow: hidden;
  -webkit-mask-image: radial-gradient(60% 60% at 50% 55%, #000 30%, transparent 100%);
  mask-image: radial-gradient(60% 60% at 50% 55%, #000 30%, transparent 100%);
}

.data-lines svg {
  width: 100%;
  height: 100%;
  display: block;
}

.data-lines path {
  fill: none;
  stroke: rgba(197, 176, 228, .10);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.data-lines path.data-pulse {
  --pulse-len: 140;
  --pulse-total: 3000;
  stroke: var(--accent);
  stroke-width: 1.6;
  stroke-linecap: round;
  stroke-dasharray: var(--pulse-len) var(--pulse-total);
  stroke-dashoffset: var(--pulse-total);
  opacity: 0;
  animation: data-pulse-run var(--pulse-dur) linear infinite;
}

@keyframes data-pulse-run {
  0%   { stroke-dashoffset: var(--pulse-total); opacity: 0; }
  12%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

.data-pulse--1 { animation-delay: 0s; }
.data-pulse--2 { animation-delay: .55s; }
.data-pulse--3 { animation-delay: 1.1s; }
.data-pulse--4 { animation-delay: 1.7s; }
.data-pulse--5 { animation-delay: 2.2s; }
.data-pulse--6 { animation-delay: 2.9s; }

/* pulso na cor da marca, para uma segunda camada de linhas */
.data-pulse--brand { stroke: var(--brand-2); }

/* nós de convergência: pequenos círculos que respiram no centro */
.data-lines circle.data-node {
  fill: var(--accent);
  opacity: .5;
  transform-box: fill-box;
  transform-origin: center;
  animation: data-node-pulse 2.6s var(--ease-io) infinite;
}

@keyframes data-node-pulse {
  0%, 100% { opacity: .35; transform: scale(1); }
  50%      { opacity: .9;  transform: scale(1.35); }
}

/* Os anéis/curvas de fundo entram junto com o reveal da seção. */
.data-lines path.data-ring {
  opacity: 0;
  transition: opacity .8s var(--ease);
}

.is-visible .data-lines path.data-ring,
.data-lines.is-visible path.data-ring { opacity: 1; }


/* =====================================================================
   14. BARRA DE PROGRESSO DE 1px — .progress-line
   Item de acordeão / aba ativa. Anima scaleX (transform), nunca width.
   A duração vem de --progress-dur, para casar com o auto-avanço do JS.
   ===================================================================== */
.progress-line {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  z-index: 1;
  pointer-events: none;
  overflow: hidden;
  background-color: rgba(197, 176, 228, .12);
}

.progress-line::after {
  content: "";
  position: absolute;
  inset: 0;
  transform: scaleX(0);
  transform-origin: left center;
  background: linear-gradient(90deg, var(--brand) 0%, var(--brand-2) 45%, var(--accent) 100%);
}

@keyframes progress-fill {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

.is-active > .progress-line::after,
[aria-selected="true"] > .progress-line::after,
[open] > .progress-line::after {
  animation: progress-fill var(--progress-dur, 12s) linear forwards;
}

/* pausa o avanço enquanto o usuário está lendo/interagindo */
.is-active:hover > .progress-line::after,
.is-active:focus-within > .progress-line::after,
[aria-selected="true"]:hover > .progress-line::after,
[aria-selected="true"]:focus-within > .progress-line::after { animation-play-state: paused; }


/* =====================================================================
   15. TRILHO HORIZONTAL COM VINHETA — .rail + .rail-fade
   Carrossel de cards com scroll nativo e snap. A vinheta escurece as
   pontas sem cortar nada e sem overlay opaco.
   ===================================================================== */
.rail-wrap {
  position: relative;
}

.rail {
  display: flex;
  gap: 20px;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: 24px;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 6px;
}

.rail::-webkit-scrollbar { display: none; }

.rail > * {
  flex: 0 0 min(320px, 82%);
  scroll-snap-align: center;
}

/* vinheta radial por cima (fiel ao benchmark) */
.rail-fade {
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(42% 50% at 50% 50%,
    rgba(13, 5, 36, 0) 44%,
    rgba(13, 5, 36, .30) 66%,
    rgba(13, 5, 36, .90) 100%);
}

/* alternativa mais leve: só apaga as laterais, não escurece o centro */
.rail-wrap--mask .rail {
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
  mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}

.rail-wrap--mask .rail-fade { display: none; }


/* =====================================================================
   16. ABAS, ACORDEÃO E MEGA-MENU
   Trocas de painel curtas. O painel abre por grid-template-rows 0fr→1fr
   (não anima layout dos filhos) ou por fade curto — nunca por height.
   ===================================================================== */
/* painéis genéricos [data-tabs] / [data-panel] */
@keyframes panel-in {
  from { opacity: 0; transform: translate3d(0, 8px, 0); }
  to   { opacity: 1; transform: none; }
}

[data-panel]:not([hidden]),
[data-chat-panel]:not([hidden]) {
  animation: panel-in .35s var(--ease) both;
}

/* gatilho de aba: sublinhado que cresce do centro */
[data-tab],
[data-chat-tab] {
  position: relative;
  transition: color var(--t-fast) var(--ease);
}

[data-tab]::after,
[data-chat-tab]::after {
  content: "";
  position: absolute;
  left: 12%;
  right: 12%;
  bottom: -1px;
  height: 2px;
  border-radius: 2px;
  background: var(--gradient);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform .28s var(--ease);
}

[data-tab][aria-selected="true"]::after,
[data-tab].is-active::after,
[data-chat-tab][aria-selected="true"]::after,
[data-chat-tab].is-active::after { transform: scaleX(1); }

/* acordeão: abre sem animar height */
.accordion-panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--t-mid) var(--ease);
}

.accordion-panel > * { min-height: 0; overflow: hidden; }

.is-active > .accordion-panel,
[open] > .accordion-panel { grid-template-rows: 1fr; }

/* chips do índice de página */
[data-page-nav] a,
[data-page-nav] button {
  transition:
    color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    transform var(--t-fast) var(--ease);
}

[data-page-nav] a:hover,
[data-page-nav] button:hover { transform: translate3d(0, -1px, 0); }

[data-page-nav] a.is-active,
[data-page-nav] a[aria-current="true"],
[data-page-nav] button.is-active,
[data-page-nav] button[aria-current="true"] {
  color: var(--text);
  border-color: rgba(129, 140, 248, .45);
  background-color: rgba(129, 140, 248, .14);
}

/* mega-menu: [hidden] impede transição, então a entrada é animação */
/* O painel do mega-menu é centrado com `left: 50%` + `translate(-50%, 0)`.
   Este keyframe terminava em `transform: none` e, com `fill-mode: both`, o
   estado final apagava justamente esse -50%: o painel nascia colado no meio
   da janela e vazava ~220px pela direita em 1440px (390px em 1100px). Por
   isso o -50% aparece nos dois quadros. */
@keyframes mega-in {
  from { opacity: 0; transform: translate(-50%, -8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* Só no desktop: abaixo de 1025px o painel vira acordeão no fluxo, onde
   deslocar metade da largura para a esquerda não faz sentido nenhum. */
@media (min-width: 1025px) {
  .nav-item--mega .mega-panel:not([hidden]) {
    animation: mega-in .22s var(--ease) both;
  }
}

.nav-item--mega .nav-trigger .nav-chevron {
  display: inline-block;
  transition: transform .22s var(--ease);
}

.nav-item--mega .nav-trigger[aria-expanded="true"] .nav-chevron {
  transform: rotate(180deg);
}


/* =====================================================================
   17. ELEVAÇÃO E MICROINTERAÇÕES CURTAS
   No tema escuro a sombra preta some: elevação = borda que clareia +
   glow colorido + realce interno no topo. As três juntas dão a
   profundidade que a sombra dava no tema claro.
   ===================================================================== */
.lift {
  transition:
    transform var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    box-shadow var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease);
}

.lift:hover,
.lift:focus-within {
  transform: translate3d(0, -4px, 0);
  border-color: rgba(129, 140, 248, .35);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, .14),
    0 24px 60px -30px rgba(8, 11, 239, .75);
}

.lift:active { transform: translate3d(0, -1px, 0); }

.lift--sm:hover,
.lift--sm:focus-within { transform: translate3d(0, -2px, 0); }

/* mídia com zoom lento no hover do card */
.zoom-media {
  overflow: hidden;
  border-radius: inherit;
}

.zoom-media img {
  transition: transform .6s var(--ease), filter .6s var(--ease);
}

.lift:hover .zoom-media img,
.zoom-media:hover img {
  transform: scale(1.05);
  filter: saturate(1.08);
}

/* seta que anda no hover do link/CTA */
.arrow-move {
  display: inline-block;
  transition: transform var(--t-fast) var(--ease);
}

a:hover > .arrow-move,
button:hover > .arrow-move { transform: translate3d(4px, 0, 0); }

/* ponto de status pulsando (online, ao vivo) */
.pulse-dot {
  position: relative;
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background-color: var(--success);
}

.pulse-dot::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background-color: var(--success);
  animation: dot-ping 1.8s var(--ease-io) infinite;
}

@keyframes dot-ping {
  0%   { opacity: .7; transform: scale(1); }
  70%  { opacity: 0;  transform: scale(2.6); }
  100% { opacity: 0;  transform: scale(2.6); }
}

/* flutuação suave — mockups e ilustrações do hero */
.float { animation: float-y 7s var(--ease-io) infinite alternate; }

@keyframes float-y {
  to { transform: translate3d(0, -10px, 0); }
}

/* erro de formulário: chacoalhada curta, só transform */
#form-error:not(:empty) { animation: shake .32s var(--ease-io) both; }

@keyframes shake {
  0%, 100% { transform: translate3d(0, 0, 0); }
  25%      { transform: translate3d(-5px, 0, 0); }
  75%      { transform: translate3d(5px, 0, 0); }
}

/* toast de sucesso: entra deslizando (o .show é do style.css) */
#success-toast { transition: transform .35s var(--ease), opacity .35s var(--ease); }

/* navbar: a transição da pílula ao rolar (a classe .scrolled é do JS) */
.navbar {
  transition:
    background-color .3s var(--ease),
    border-color .3s var(--ease),
    box-shadow .3s var(--ease),
    backdrop-filter .3s var(--ease);
}

/* âncoras de seção: o header fixo não pode tapar o título */
/* scroll-margin-top removido: o deslocamento de âncora vive só no
   `scroll-padding-top` do html, em style.css. Somar os dois dobrava o offset. */

/* seções longas abaixo da dobra: ganho grátis de pintura */
/* .section--defer marcava seções para `content-visibility: auto`. REMOVIDO.
   Com altura estimada por `contain-intrinsic-size`, o tamanho da página mudava
   conforme as seções entravam em render: o scroll saltava para cima no meio da
   leitura e o clique numa âncora calculava o destino com alturas erradas
   (a de Materiais, na sala de imprensa, caía em outro trecho). O ganho de
   render não paga esse preço. A classe fica como marcação, sem efeito. */
.section--defer {
  content-visibility: visible;
}




/* =====================================================================
   18. DESDOBRAMENTO 3D DAS TELAS DO APP — [data-reveal="unfold"]
   A tela chega TOMBADA para trás, longe no eixo Z, e "deita" na posição
   ao entrar na viewport. É o gesto principal do site.

   Como a profundidade é real (e não um rotateX decorativo):
   - a figura tem `perspective` como PROPRIEDADE (projeta os filhos) e
     `perspective()` dentro do próprio transform (projeta ela mesma);
   - `transform-style: preserve-3d` deixa o .app-shot viver no mesmo
     espaço 3D, então o translateZ dele "descola" a moldura do plano;
   - a sombra é um pseudo-elemento que ABRE junto (escala + opacidade),
     nunca um box-shadow animado;
   - os filhos do .app-shot__body entram em cascata: a tela se MONTA.

   Intensidade por variável — dá para calibrar por página sem tocar no
   HTML (ver .unfold--suave / .unfold--extremo) e o bloco responsivo
   reduz tudo em telas pequenas.
   ===================================================================== */
[data-reveal="unfold"] {
  --unfold-persp: 1000px;   /* curta de propósito: exagera a profundidade */
  --unfold-rx: 42deg;       /* quanto a "tampa" está tombada */
  --unfold-rz: .9deg;       /* desalinho mínimo: tira o ar de máquina */
  --unfold-z: -220px;       /* distância real no eixo Z */
  --unfold-y: 72px;
  --unfold-scale: .94;
  --unfold-dur: 1.35s;
  --unfold-ease: cubic-bezier(.16, .84, .28, 1);
  --unfold-pop: 22px;       /* quanto a moldura sai do plano no repouso */

  position: relative;
  opacity: 0;
  transform-origin: 50% 0%;
  transform-style: preserve-3d;
  perspective: var(--unfold-persp);
  transform:
    perspective(var(--unfold-persp))
    translate3d(0, var(--unfold-y), var(--unfold-z))
    rotateX(var(--unfold-rx))
    rotateZ(var(--unfold-rz))
    scale(var(--unfold-scale));
  transition:
    opacity .8s var(--ease),
    transform var(--unfold-dur) var(--unfold-ease);
  /* só transform: `will-change: opacity` é grouping property e ACHATA o
     preserve-3d — o translateZ do .app-shot perderia a profundidade. */
  will-change: transform;
}

[data-reveal="unfold"].is-visible {
  opacity: 1;
  transform:
    perspective(var(--unfold-persp))
    translate3d(0, 0, 0)
    rotateX(0deg)
    rotateZ(0deg)
    scale(1);
}

/* calibragem por peça — a mesma coreografia, com outro volume */
[data-reveal="unfold"].unfold--suave {
  --unfold-rx: 30deg;
  --unfold-z: -140px;
  --unfold-y: 52px;
  --unfold-rz: .4deg;
  --unfold-pop: 16px;
}

[data-reveal="unfold"].unfold--extremo {
  --unfold-rx: 46deg;
  --unfold-z: -260px;
  --unfold-y: 80px;
  --unfold-rz: 1.2deg;
  --unfold-persp: 900px;
  --unfold-pop: 24px;
}

/* SOMBRA QUE ACOMPANHA
   Começa apertada e alta (a tela ainda está de pé) e se abre conforme
   a tela deita. Só transform + opacity: nada de box-shadow animado.
   Fica dentro dos limites horizontais da figura — não gera overflow. */
[data-reveal="unfold"]::before {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 10% 8% -6% 8%;
  border-radius: 50%;
  pointer-events: none;
  background:
    radial-gradient(52% 50% at 50% 50%,
      rgba(4, 1, 14, .78),
      rgba(8, 11, 239, .18) 58%,
      transparent 78%);
  filter: blur(22px);
  opacity: 0;
  transform-origin: 50% 0%;
  transform: translate3d(0, -34px, 0) scale3d(.52, .3, 1);
  transition:
    opacity var(--unfold-dur) var(--unfold-ease),
    transform var(--unfold-dur) var(--unfold-ease);
}

[data-reveal="unfold"].is-visible::before {
  opacity: 1;
  transform: translate3d(0, 12px, 0) scale3d(1, 1, 1);
}

/* A MOLDURA SAI DO PLANO
   translateZ próprio dentro do espaço 3D da figura: com perspectiva de
   1000px, 34px de Z valem ~3,5% de aumento óptico — a moldura flutua
   acima da sombra em vez de estar colada nela. */
[data-reveal="unfold"] .app-shot {
  position: relative;
  z-index: 1;
  overflow: hidden;
  transform: translate3d(0, 0, 0);
  transition: transform 1s var(--unfold-ease) .12s;

  /* O translateZ final aumenta a moldura opticamente (perspectiva 1000px,
     Z de ~22px ≈ +2,2%). Sem compensar na largura, a tela terminava mais larga
     que o container e encostava nas bordas da viewport. Recuamos a caixa de
     layout na mesma proporção: o efeito de profundidade fica, o vazamento não. */
  width: 97%;
  margin-inline: auto;
}

[data-reveal="unfold"].is-visible .app-shot {
  transform: translate3d(0, 0, var(--unfold-pop));
}

/* VARREDURA 1 — brilho diagonal (nome e keyframe originais preservados) */
[data-reveal="unfold"] .app-shot::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 30%,
    rgba(8, 11, 239, .10) 42%,
    rgba(129, 140, 248, .26) 48%,
    rgba(255, 255, 255, .18) 52%,
    rgba(84, 4, 148, .14) 58%,
    transparent 70%);
  transform: translate3d(-120%, 0, 0);
  opacity: 0;
}

[data-reveal="unfold"].is-visible .app-shot::after {
  animation: app-brilho 1.5s var(--ease) .55s 1;
}

@keyframes app-brilho {
  0%   { transform: translate3d(-120%, 0, 0); opacity: 0; }
  25%  { opacity: 1; }
  100% { transform: translate3d(120%, 0, 0);  opacity: 0; }
}

/* VARREDURA 2 — linha fina de scanner que desce uma vez pela tela.
   É uma faixa de 14% de altura desenhada em background-image; o que
   anda é background-position (composto, sem layout). */
[data-reveal="unfold"] .app-shot::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  background-image: linear-gradient(180deg,
    transparent 0%,
    rgba(129, 140, 248, .22) 40%,
    rgba(129, 140, 248, .75) 49%,
    rgba(255, 255, 255, .9) 50%,
    rgba(129, 140, 248, .75) 51%,
    rgba(129, 140, 248, .22) 60%,
    transparent 100%);
  background-repeat: no-repeat;
  background-size: 100% 14%;
  background-position: 0 -20%;
}

[data-reveal="unfold"].is-visible .app-shot::before {
  animation: app-scanline 1.7s var(--ease-io) .3s 1;
}

@keyframes app-scanline {
  0%   { opacity: 0;   background-position: 0 -20%; }
  10%  { opacity: .95; }
  82%  { opacity: .5; }
  100% { opacity: 0;   background-position: 0 120%; }
}

/* CASCATA INTERNA — a tela se monta.
   Cada filho direto do corpo entra 70ms depois do anterior, começando
   quando o desdobramento já passou da metade. */
[data-reveal="unfold"] .app-shot__body > * {
  opacity: 0;
  transform: translate3d(0, 14px, 0);
  transition:
    opacity .5s var(--ease),
    transform .6s var(--ease);
}

[data-reveal="unfold"].is-visible .app-shot__body > * {
  opacity: 1;
  transform: none;
}

[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(1)  { transition-delay: 420ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(2)  { transition-delay: 490ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(3)  { transition-delay: 560ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(4)  { transition-delay: 630ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(5)  { transition-delay: 700ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(6)  { transition-delay: 770ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(7)  { transition-delay: 840ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(8)  { transition-delay: 910ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(9)  { transition-delay: 980ms; }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(n+10) { transition-delay: 1050ms; }

/* a barra de título entra junto com a moldura, um pouco antes do corpo */
[data-reveal="unfold"] .app-shot__bar {
  opacity: 0;
  transform: translate3d(0, -8px, 0);
  transition:
    opacity .45s var(--ease) .3s,
    transform .55s var(--ease) .3s;
}

[data-reveal="unfold"].is-visible .app-shot__bar {
  opacity: 1;
  transform: none;
}

/* HOVER — inclinação sutil e elevação real (Z), só em ponteiro fino. */
@media (hover: hover) and (pointer: fine) {
  [data-reveal="unfold"].is-visible:hover .app-shot {
    transform: translate3d(0, -6px, calc(var(--unfold-pop) + 10px)) rotateX(-2deg);
  }

  [data-reveal="unfold"].is-visible:hover::before {
    opacity: .9;
    transform: translate3d(0, 22px, 0) scale3d(1.04, 1.12, 1);
  }
}


/* =====================================================================
   19. FUNDOS COM TEMA DE IA QUE REAGEM AO CURSOR — [data-ai-bg] + .ai-bg
   Camada decorativa para colocar ATRÁS de seções importantes. Zero
   imagem, zero JS além das duas variáveis de cursor (--mx/--my, as
   mesmas que o main.js já escreve nos [data-spotlight]).

   MARCAÇÃO
     <section class="section" data-ai-bg>
       <div class="ai-bg ai-bg--mesh" aria-hidden="true"></div>
       <div class="container"> … conteúdo … </div>
     </section>

   O contêiner [data-ai-bg] recebe --mx/--my em px (canto superior
   esquerdo dele); as camadas herdam. Sem JS, tudo cai no centro
   (50%/50%) e nada quebra.

   Variantes combináveis: pode haver mais de uma .ai-bg na mesma seção
   (ex.: aurora + malha), porque cada uma é uma camada própria.
   ===================================================================== */
[data-ai-bg] {
  position: relative;
  overflow: hidden;
  --mx: 50%;
  --my: 50%;
}

[data-ai-bg] > *:not(.ai-bg) {
  position: relative;
  z-index: 1;
}

.ai-bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  contain: paint;

  --ai-cell: 46px;    /* passo da malha */
  --ai-reach: 320px;  /* raio do halo do cursor */
  --ai-dim: .85;      /* intensidade geral da camada */

  opacity: var(--ai-dim);
}

/* intensidade sem tocar nas cores */
.ai-bg--suave { --ai-dim: .5; }
.ai-bg--forte { --ai-dim: 1; }

/* recorte vertical: a camada morre antes de encostar nas bordas da
   seção, evitando a linha dura de corte */
.ai-bg,
.ai-bg--mesh,
.ai-bg--circuito {
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 16%, #000 84%, transparent 100%);
}


/* --- 19.1 MALHA NEURAL — .ai-bg--mesh --------------------------------
   Duas camadas idênticas de nós + arestas. A de baixo é o desenho
   apagado; a de cima é a mesma grade em cor de marca, revelada por uma
   máscara radial presa ao cursor: a malha ACENDE onde o ponteiro passa.
   --------------------------------------------------------------------- */
.ai-bg--mesh::before,
.ai-bg--mesh::after {
  content: "";
  position: absolute;
  inset: -2px;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(197, 176, 228, .55) 0 1.5px, transparent 2.2px),
    linear-gradient(90deg, rgba(129, 140, 248, .16) 0 1px, transparent 1px),
    linear-gradient(180deg, rgba(129, 140, 248, .12) 0 1px, transparent 1px);
  background-size:
    var(--ai-cell) var(--ai-cell),
    var(--ai-cell) var(--ai-cell),
    var(--ai-cell) var(--ai-cell);
  background-position: 0 0, 0 0, 0 0;
}

/* camada apagada: deriva um passo de grade a cada volta (só position) */
.ai-bg--mesh::before {
  opacity: .3;
  animation: ai-mesh-drift 48s linear infinite;
}

@keyframes ai-mesh-drift {
  to {
    background-position:
      var(--ai-cell) var(--ai-cell),
      var(--ai-cell) 0,
      0 var(--ai-cell);
  }
}

/* camada acesa: mesma grade, cor de marca, revelada só perto do cursor */
.ai-bg--mesh::after {
  background-image:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .95) 0 1.8px, transparent 2.6px),
    linear-gradient(90deg, rgba(129, 140, 248, .5) 0 1px, transparent 1px),
    linear-gradient(180deg, rgba(8, 11, 239, .45) 0 1px, transparent 1px);
  opacity: 0;
  transition: opacity .4s var(--ease-io);
  -webkit-mask-image: radial-gradient(var(--ai-reach) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .55) 42%, transparent 70%);
  mask-image: radial-gradient(var(--ai-reach) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .55) 42%, transparent 70%);
}

[data-ai-bg]:hover .ai-bg--mesh::after,
[data-ai-bg]:focus-within .ai-bg--mesh::after { opacity: 1; }

/* halo difuso de "processamento" atrás da malha, também preso ao cursor */
.ai-bg--mesh {
  background-image: radial-gradient(calc(var(--ai-reach) * 1.4) circle at var(--mx, 50%) var(--my, 50%),
    rgba(8, 11, 239, .22),
    rgba(84, 4, 148, .10) 45%,
    transparent 72%);
  background-repeat: no-repeat;
}


/* --- 19.2 TRILHAS DE CIRCUITO — .ai-bg--circuito ---------------------
   Grade de trilhas com cantos em 90° (o conic-gradient desenha o
   joelho) e dois pulsos de luz correndo por elas: um na horizontal,
   outro na vertical. O que anda é background-position.
   --------------------------------------------------------------------- */
.ai-bg--circuito::before {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    /* joelhos de 90° nos cruzamentos */
    conic-gradient(from 180deg at 2px 2px,
      rgba(84, 4, 148, .0) 0 25%,
      rgba(129, 140, 248, .22) 25% 26%,
      rgba(129, 140, 248, 0) 26% 100%),
    repeating-linear-gradient(90deg,
      rgba(129, 140, 248, .13) 0 1px,
      transparent 1px 96px),
    repeating-linear-gradient(180deg,
      rgba(197, 176, 228, .09) 0 1px,
      transparent 1px 120px);
  background-size: 96px 120px, auto, auto;
  opacity: .8;
}

.ai-bg--circuito::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image:
    linear-gradient(90deg,
      transparent 0%,
      rgba(129, 140, 248, .35) 35%,
      rgba(255, 255, 255, .95) 50%,
      rgba(129, 140, 248, .35) 65%,
      transparent 100%),
    linear-gradient(180deg,
      transparent 0%,
      rgba(8, 11, 239, .45) 35%,
      rgba(129, 140, 248, .95) 50%,
      rgba(8, 11, 239, .45) 65%,
      transparent 100%);
  background-repeat: no-repeat, no-repeat;
  background-size: 240px 1px, 1px 200px;
  background-position: -240px 120px, 192px -200px;
  animation: ai-circuito-pulso 7s linear infinite;
}

@keyframes ai-circuito-pulso {
  0%   { background-position: -240px 120px, 192px -200px; }
  100% { background-position: calc(100% + 240px) 120px, 192px calc(100% + 200px); }
}

/* trilha extra, defasada — para seções altas */
.ai-bg--circuito.ai-bg--duplo::after {
  animation-duration: 4.6s;
  background-position: -240px 240px, 384px -200px;
}


/* --- 19.3 PARTÍCULAS EM TRÊS PROFUNDIDADES — .ai-bg--particulas ------
   Três camadas de pontos com tamanhos, opacidades e durações
   diferentes: o parallax nasce da diferença de velocidade. As duas
   pseudo-camadas andam em translate3d (GPU); a camada de fundo, a mais
   lenta, anda em background-position.
   --------------------------------------------------------------------- */
.ai-bg--particulas {
  background-image: radial-gradient(circle at 50% 50%, rgba(197, 176, 228, .3) 0 1px, transparent 1.6px);
  background-size: 132px 132px;
  animation: ai-part-fundo 120s linear infinite;
}

@keyframes ai-part-fundo {
  to { background-position: 132px -132px; }
}

.ai-bg--particulas::before,
.ai-bg--particulas::after {
  content: "";
  position: absolute;
  inset: -25%;
  will-change: transform;
}

/* camada média */
.ai-bg--particulas::before {
  background-image:
    radial-gradient(circle at 50% 50%, rgba(129, 140, 248, .45) 0 1.4px, transparent 2.2px);
  background-size: 78px 78px;
  opacity: .55;
  animation: ai-part-media 38s var(--ease-io) infinite alternate;
}

@keyframes ai-part-media {
  from { transform: translate3d(-18px, 14px, 0); }
  to   { transform: translate3d(22px, -20px, 0); }
}

/* camada da frente: pontos maiores, mais rápidos */
.ai-bg--particulas::after {
  background-image:
    radial-gradient(circle at 30% 30%, rgba(255, 255, 255, .5) 0 1.8px, transparent 2.8px),
    radial-gradient(circle at 70% 80%, rgba(8, 11, 239, .5) 0 2.2px, transparent 3.2px);
  background-size: 190px 190px, 260px 260px;
  opacity: .4;
  animation: ai-part-frente 24s var(--ease-io) infinite alternate;
}

@keyframes ai-part-frente {
  from { transform: translate3d(24px, -18px, 0) scale(1); }
  to   { transform: translate3d(-26px, 26px, 0) scale(1.06); }
}


/* --- 19.4 AURORA — .ai-bg--aurora -----------------------------------
   Véu de gradiente da marca que respira (escala) e desloca (posição).
   Sem blur: os gradientes já são macios, e blur em loop é caro.
   --------------------------------------------------------------------- */
.ai-bg--aurora::before {
  content: "";
  position: absolute;
  inset: -30%;
  background-image:
    radial-gradient(42% 38% at 22% 32%, rgba(8, 11, 239, .38), transparent 70%),
    radial-gradient(38% 34% at 78% 22%, rgba(84, 4, 148, .34), transparent 72%),
    radial-gradient(48% 42% at 55% 86%, rgba(129, 140, 248, .2), transparent 70%);
  transform-origin: 50% 40%;
  will-change: transform;
  animation: ai-aurora-respira 22s var(--ease-io) infinite alternate;
}

@keyframes ai-aurora-respira {
  from { transform: translate3d(-2%, 1%, 0) scale(1); }
  to   { transform: translate3d(3%, -2%, 0) scale(1.14); }
}

.ai-bg--aurora::after {
  content: "";
  position: absolute;
  inset: 0;
  background-image: linear-gradient(115deg,
    transparent 20%,
    rgba(129, 140, 248, .12) 42%,
    rgba(84, 4, 148, .16) 52%,
    transparent 72%);
  background-size: 260% 100%;
  background-position: 0 0;
  opacity: .8;
  animation: ai-aurora-veu 26s linear infinite;
}

@keyframes ai-aurora-veu {
  to { background-position: 260% 0; }
}


/* =====================================================================
   20. EFEITOS PREMIUM
   Peças pequenas e reaproveitáveis. Todas degradam para "nada
   acontece" — nunca para "conteúdo sumiu".
   ===================================================================== */

/* --- 20.1 CARTÃO QUE INCLINA COM O CURSOR — .tilt --------------------
   Usa as MESMAS --mx/--my do spotlight (px, medidos a partir do canto
   superior esquerdo do cartão). Como CSS não divide comprimento por
   comprimento, o ângulo sai de atan2(): --tilt-alcance é a "distância
   do olho" e --tilt-cx/--tilt-cy são a meia-largura/meia-altura
   estimadas do cartão. O clamp em ±--tilt-max (6deg) garante que, mesmo
   com a estimativa errada, a inclinação satura num valor confortável.

   MARCAÇÃO: <article class="card tilt" data-spotlight> … </article>
   (o data-spotlight é o que faz o main.js escrever --mx/--my)
   --------------------------------------------------------------------- */
.tilt {
  --tilt-max: 6deg;
  --tilt-cx: 210px;
  --tilt-cy: 150px;
  --tilt-alcance: 620px;
  --tilt-rx: 0deg;
  --tilt-ry: 0deg;
  --mx: var(--tilt-cx);
  --my: var(--tilt-cy);

  position: relative;
  transform-style: preserve-3d;
  transform: perspective(900px);
  transition:
    transform .45s var(--ease),
    box-shadow .45s var(--ease);
}

/* brilho especular seguindo o ponteiro */
.tilt::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background:
    radial-gradient(240px circle at var(--mx) var(--my),
      rgba(255, 255, 255, .16),
      rgba(129, 140, 248, .1) 40%,
      transparent 66%);
  transition: opacity .3s var(--ease-io);
}

/* NÃO forçar `position: relative` nos filhos do .tilt.
   O brilho especular é um ::after absoluto com z-index 2, e conteúdo estático
   já pinta abaixo de um pseudo-elemento posicionado — a regra era redundante.
   Pior: ela sobrescrevia o `position: absolute` de filhos que dependem disso,
   e foi o que jogou o selo "USPTO 2025" para dentro do fluxo, criando uma
   faixa vazia embaixo da imagem da patente. */
.tilt > * { z-index: 1; }

@media (hover: hover) and (pointer: fine) {
  .tilt:hover,
  .tilt:focus-within {
    transform:
      perspective(900px)
      rotateX(var(--tilt-rx))
      rotateY(var(--tilt-ry))
      translate3d(0, -4px, 26px);
  }

  /* enquanto o cursor está dentro, a resposta é curta (sem arrasto);
     ao sair, a volta é longa e macia */
  .tilt:hover { transition-duration: .14s; }

  .tilt:hover::after,
  .tilt:focus-within::after { opacity: 1; }

  @supports (transform: rotate(atan2(1px, 1px))) {
    .tilt {
      --tilt-ry: clamp(
        calc(var(--tilt-max) * -1),
        atan2(calc(var(--mx) - var(--tilt-cx)), var(--tilt-alcance)),
        var(--tilt-max));
      --tilt-rx: clamp(
        calc(var(--tilt-max) * -1),
        atan2(calc(var(--tilt-cy) - var(--my)), var(--tilt-alcance)),
        var(--tilt-max));
    }
  }
}

/* proporções prontas (só ajustam onde a inclinação satura) */
.tilt--largo { --tilt-cx: 320px; --tilt-cy: 130px; }
.tilt--alto  { --tilt-cx: 170px; --tilt-cy: 240px; }
.tilt--sutil { --tilt-max: 3.5deg; }

/* camada que sobe no eixo Z junto com a inclinação (ícone, selo, número) */
.tilt--profundo .tilt__camada {
  transform: translate3d(0, 0, 0);
  transition: transform .45s var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .tilt--profundo:hover .tilt__camada,
  .tilt--profundo:focus-within .tilt__camada {
    transform: translate3d(0, -2px, 42px);
  }
}


/* --- 20.2 REVELAÇÃO POR MÁSCARA — .reveal-mask ----------------------
   Máscara de 300% de largura que desliza: o bloco não "aparece", ele é
   descoberto. Combina com o observer existente ([data-reveal] →
   .is-visible).

   MARCAÇÃO: <h2 class="reveal-mask" data-reveal="fade">Texto</h2>
   --------------------------------------------------------------------- */
.reveal-mask {
  --mask-dur: 1s;
  -webkit-mask-image: linear-gradient(96deg, #000 0 32%, rgba(0, 0, 0, .4) 48%, transparent 62%);
  mask-image: linear-gradient(96deg, #000 0 32%, rgba(0, 0, 0, .4) 48%, transparent 62%);
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-size: 300% 100%;
  mask-size: 300% 100%;
  -webkit-mask-position: 100% 0;
  mask-position: 100% 0;
  transition:
    -webkit-mask-position var(--mask-dur) var(--ease-io),
    mask-position var(--mask-dur) var(--ease-io);
}

.reveal-mask.is-visible {
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
}

/* variante vertical: descobre de baixo para cima */
.reveal-mask--sobe {
  -webkit-mask-image: linear-gradient(180deg, transparent 38%, rgba(0, 0, 0, .4) 52%, #000 68%);
  mask-image: linear-gradient(180deg, transparent 38%, rgba(0, 0, 0, .4) 52%, #000 68%);
  -webkit-mask-size: 100% 300%;
  mask-size: 100% 300%;
  -webkit-mask-position: 0 100%;
  mask-position: 0 100%;
}

.reveal-mask--sobe.is-visible {
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
}

.reveal-mask--lento { --mask-dur: 1.5s; }


/* --- 20.3 PULSO DO CONTADOR — .count-glow ---------------------------
   O main.js marca o elemento com data-contando="ok" no instante em que
   começa a contar (duração fixa de 1400ms). O pulso entra com atraso
   equivalente: acende exatamente quando o número para.

   MARCAÇÃO: <span class="stat-value count-glow" data-count="128">0</span>
   --------------------------------------------------------------------- */
.count-glow {
  display: inline-block;
  transform-origin: 50% 60%;
}

.count-glow[data-contando="ok"],
.count-glow.is-counted {
  animation: count-glow-pulso 1s var(--ease-io) 1.4s 1 both;
}

@keyframes count-glow-pulso {
  0% {
    transform: scale(1);
    filter: drop-shadow(0 0 0 rgba(129, 140, 248, 0));
  }
  35% {
    transform: scale(1.07);
    filter: drop-shadow(0 0 16px rgba(129, 140, 248, .85));
  }
  100% {
    transform: scale(1);
    filter: drop-shadow(0 0 0 rgba(129, 140, 248, 0));
  }
}


/* --- 20.4 BORDA QUE ACENDE AO ENTRAR NA VIEWPORT — .edge-light ------
   Nada de animar `border`: a moldura é um gradiente mascarado
   (content-box XOR padding-box) cujo background-size cresce de 0% a
   100% quando o elemento ganha .is-visible.

   MARCAÇÃO: <div class="card edge-light" data-reveal> … </div>
   --------------------------------------------------------------------- */
.edge-light {
  position: relative;
  isolation: isolate;
  border-radius: var(--radius, 14px);
}

.edge-light > * { z-index: 1; }

@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .edge-light::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 0;
    padding: 1px;
    border-radius: inherit;
    pointer-events: none;
    background-image: linear-gradient(90deg,
      var(--brand) 0%,
      var(--accent) 45%,
      var(--brand-2) 100%);
    background-repeat: no-repeat;
    background-size: 0% 100%;
    -webkit-mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    mask-composite: exclude;
    transition: background-size 1.2s var(--ease-io);
  }

  .edge-light.is-visible::before { background-size: 100% 100%; }
}

.edge-light--lento::before { transition-duration: 1.9s; }


/* --- 20.5 PILHA DE CARTÕES COM PROFUNDIDADE — .depth-stack ----------
   Os filhos ocupam a MESMA célula de grid (empilhados) e se afastam no
   eixo Z. No hover a pilha se abre em leque.

   MARCAÇÃO:
     <div class="depth-stack">
       <article class="card">frente</article>
       <article class="card">meio</article>
       <article class="card">fundo</article>
     </div>
   --------------------------------------------------------------------- */
.depth-stack {
  display: grid;
  perspective: 1100px;
  perspective-origin: 50% 30%;
}

.depth-stack > * {
  grid-area: 1 / 1;
  transform-style: preserve-3d;
  transition:
    transform .6s var(--ease),
    opacity .4s var(--ease);
}

.depth-stack > *:nth-child(1) { z-index: 3; }

.depth-stack > *:nth-child(2) {
  z-index: 2;
  opacity: .75;
  transform: translate3d(0, 14px, -70px);
}

.depth-stack > *:nth-child(3) {
  z-index: 1;
  opacity: .5;
  transform: translate3d(0, 26px, -140px);
}

.depth-stack > *:nth-child(n+4) {
  z-index: 0;
  opacity: 0;
  transform: translate3d(0, 34px, -200px);
}

@media (hover: hover) and (pointer: fine) {
  .depth-stack:hover > *:nth-child(1),
  .depth-stack:focus-within > *:nth-child(1) {
    transform: translate3d(0, -26px, 40px) rotateX(3deg);
  }

  .depth-stack:hover > *:nth-child(2),
  .depth-stack:focus-within > *:nth-child(2) {
    opacity: .95;
    transform: translate3d(0, 26px, -20px) rotateX(1.5deg);
  }

  .depth-stack:hover > *:nth-child(3),
  .depth-stack:focus-within > *:nth-child(3) {
    opacity: .8;
    transform: translate3d(0, 76px, -80px);
  }
}


/* --- 20.6 GRÃO DE ACABAMENTO — .noise -------------------------------
   Ruído de SVG inline, 3% de opacidade. Não anima: é acabamento, tira
   o banding dos gradientes e dá textura de impressão fina à seção.
   Complementa .grain (que é a mesma ideia dentro de .hero/.blobs).
   --------------------------------------------------------------------- */
.noise { position: relative; }

.noise::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  opacity: .03;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23g)'/%3E%3C/svg%3E");
  background-size: 200px 200px;
}

.noise--forte::after { opacity: .045; }


/* --- 20.7 CORREÇÃO DO SPOTLIGHT --------------------------------------
   A seção 6 desenha a aura no ::before, mas as regras de hover
   apontavam para o ::after (que não existe): a aura nunca acendia.
   Aqui o gatilho é ligado ao pseudo certo. Nenhum nome muda.
   --------------------------------------------------------------------- */
[data-spotlight]:hover::before,
[data-spotlight]:focus-within::before { opacity: 1; }

[data-spotlight="accent"]::before {
  background: radial-gradient(340px circle at var(--mx) var(--my),
    rgba(129, 140, 248, .18),
    transparent 60%);
}



/* =====================================================================
   21. ANIMAÇÃO LIGADA AO SCROLL — .sc-*
   O salto de qualidade: em vez de "entrou/não entrou", o progresso da
   animação é o próprio progresso do elemento na viewport. Zero JS, zero
   listener de scroll, tudo no compositor.

   REGRA DE SEGURANÇA (a mais importante do bloco): o estado declarado
   FORA do @supports é o estado FINAL. Quem não tem animation-timeline
   (Safari e Firefox estáveis, hoje) vê a peça pronta, nítida, cheia —
   nunca uma tela em branco. O @supports só ADICIONA o percurso.

   MARCAÇÃO
     <div class="sc-moldura">
       <img class="sc-parallax" src="…" alt="…">
     </div>
     <section class="sc-secao">
       <div class="sc-progresso" aria-hidden="true"></div>
       <div class="sc-escala sc-desfoca"> … </div>
     </section>
   ===================================================================== */
:root {
  --sc-desloc: 42px;   /* parallax: deslocamento para cada lado do repouso */
  --sc-blur: 6px;      /* desfoque inicial de .sc-desfoca */
  --sc-escala-de: .94; /* escala inicial de .sc-escala */
}

/* Moldura obrigatória do parallax: o que desliza não pode vazar.
   Sem ela a mídia deslocada empurraria a vizinhança visualmente. */
.sc-moldura {
  position: relative;
  overflow: hidden;
  border-radius: inherit;
}

/* Seção que EMPRESTA seu progresso a filhos (a barra .sc-progresso).
   view-timeline-name não pinta nada: só nomeia um cronômetro. */
.sc-secao {
  position: relative;
}

/* --- 21.1 PARALLAX DE MÍDIA — .sc-parallax --------------------------
   A imagem sobe mais devagar que a página. Só translate3d: nenhuma
   propriedade de layout entra na conta.
   --------------------------------------------------------------------- */
.sc-parallax {
  display: block;
  max-width: 100%;
  /* Nenhum `transform` declarado de propósito: o valor inicial (none) JÁ É
     o estado final. Declarar `translate3d(0,0,0)` aqui promoveria uma
     camada inútil no navegador sem suporte E sobrescreveria o transform
     de base de quem combinasse a classe com .tilt ou .lift. */
}

.sc-parallax--suave { --sc-desloc: 24px; }
.sc-parallax--forte { --sc-desloc: 72px; }

@keyframes sc-parallax-y {
  from { transform: translate3d(0, calc(var(--sc-desloc) * -1), 0); }
  to   { transform: translate3d(0, var(--sc-desloc), 0); }
}

/* sentido invertido — para a segunda mídia de um par, cria contraponto */
@keyframes sc-parallax-y-inverso {
  from { transform: translate3d(0, var(--sc-desloc), 0); }
  to   { transform: translate3d(0, calc(var(--sc-desloc) * -1), 0); }
}

/* --- 21.2 ESCALA DE ENTRADA — .sc-escala ---------------------------- */
.sc-escala {
  /* mesmo raciocínio do parallax: sem transform de base, o repouso é o
     estado final e nada é sobrescrito */
  transform-origin: 50% 60%;
}

@keyframes sc-escala-entra {
  from { opacity: .6; transform: scale(var(--sc-escala-de)); }
  to   { opacity: 1;  transform: scale(1); }
}

/* --- 21.3 FOCO QUE ENTRA — .sc-desfoca ------------------------------
   Sem regra de base: `filter` já nasce `none`, que é o estado final.
   Só o @supports adiciona o percurso do desfoque.
   --------------------------------------------------------------------- */

@keyframes sc-desfoca-entra {
  from { filter: blur(var(--sc-blur)); }
  to   { filter: blur(0); }
}

/* --- 21.4 BARRA DE PROGRESSO DA SEÇÃO — .sc-progresso ---------------
   Linha de 2px que preenche conforme a seção atravessa a viewport. O
   preenchimento é o ::after em scaleX — nunca width.
   --------------------------------------------------------------------- */
.sc-progresso {
  position: relative;
  display: block;
  height: 2px;
  overflow: hidden;
  border-radius: 2px;
  background-color: rgba(197, 176, 228, .12);
}

.sc-progresso::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  transform-origin: left center;
  transform: scaleX(1); /* estado final = fallback: barra cheia */
  background-image: linear-gradient(90deg,
    var(--brand) 0%,
    var(--brand-2) 45%,
    var(--accent) 100%);
}

/* variante grossa, para hero de página de produto */
.sc-progresso--grosso { height: 4px; }

@keyframes sc-progresso-enche {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

/* --- 21.5 O PERCURSO (só onde o navegador sabe correr) --------------- */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .sc-parallax {
      will-change: transform;
      animation-name: sc-parallax-y;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    .sc-parallax--inverso { animation-name: sc-parallax-y-inverso; }

    .sc-escala {
      animation-name: sc-escala-entra;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: entry 6% entry 96%;
    }

    .sc-desfoca {
      animation-name: sc-desfoca-entra;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: entry 10% entry 88%;
    }

    /* a barra usa o cronômetro da SEÇÃO, não o dela própria: uma linha
       de 2px de altura entraria e sairia da viewport em um piscar. */
    .sc-secao { view-timeline-name: --sc-secao; view-timeline-axis: block; }

    .sc-progresso::after {
      animation-name: sc-progresso-enche;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: --sc-secao;
      animation-range: cover 0% cover 100%;
    }

    /* Se não houver .sc-secao acima, o nome --sc-secao não resolve: a
       animação fica INATIVA e o elemento mantém o estilo base, que é a
       barra cheia. Nunca sobra uma barra vazia para sempre. */
  }
}


/* =====================================================================
   22. PROFUNDIDADE NAS TELAS DO APP — .app-figure--parallax
   A moldura e a sombra passam pela viewport em velocidades diferentes.
   É o mesmo truque do teatro: o cenário de trás anda menos que o da
   frente, e o cérebro lê a diferença como distância.

   POR QUE A MOLDURA USA `translate` E NÃO `transform`:
   o .app-shot já tem uma TRANSIÇÃO de `transform` vinda do
   desdobramento ([data-reveal="unfold"] .app-shot → translate3d Z).
   Se o parallax também escrevesse `transform`, uma animação mataria a
   outra. `translate` é a propriedade independente de transformação:
   compõe ANTES do transform, roda no mesmo compositor, não toca em
   layout — e as duas coreografias convivem sem se anular.

   MARCAÇÃO: <figure class="app-figure app-figure--parallax"
                     data-reveal="unfold"> … </figure>
   ===================================================================== */
.app-figure--parallax {
  --ap-moldura: 16px;  /* deslocamento da moldura */
  --ap-sombra: 40px;   /* deslocamento da sombra: 2,5× mais rápido */
  position: relative;
}

.app-figure--parallax .app-shot {
  translate: 0 0; /* estado final = fallback */
}

/* Sombra própria da variante. O ::before da figura já é a sombra do
   desdobramento; esta é a segunda camada, a que "descola" no scroll.
   Fica dentro dos limites horizontais (inset lateral de 10%). */
.app-figure--parallax::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 24% 10% -10% 10%;
  border-radius: 50%;
  pointer-events: none;
  opacity: .55;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(8, 11, 239, .42),
    rgba(84, 4, 148, .18) 55%,
    transparent 78%);
  filter: blur(30px);
  /* sem transform de base: o repouso já é o estado final */
}

.app-figure--parallax .app-shot,
.app-figure--parallax .media-note { position: relative; z-index: 1; }

@keyframes ap-moldura-desliza {
  from { translate: 0 calc(var(--ap-moldura) * -1); }
  to   { translate: 0 var(--ap-moldura); }
}

@keyframes ap-sombra-desliza {
  from { transform: translate3d(0, calc(var(--ap-sombra) * -1), 0) scale(.92); opacity: .3; }
  50%  { opacity: .6; }
  to   { transform: translate3d(0, var(--ap-sombra), 0) scale(1.06); opacity: .3; }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .app-figure--parallax .app-shot {
      will-change: translate;
      animation-name: ap-moldura-desliza;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }

    .app-figure--parallax::after {
      will-change: transform;
      animation-name: ap-sombra-desliza;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}


/* =====================================================================
   23. CASCATA POR CONTÊINER — [data-stagger]
   Antes, escalonar uma grade exigia data-reveal-delay="1..6" escrito à
   mão em cada cartão (e travava em 6). Agora o RITMO mora no pai.

   DOIS MODOS
   a) padrão — os filhos têm o próprio [data-reveal] (cada um entra ao
      aparecer) e o pai só distribui o atraso. É o modo seguro: se o JS
      não rodar, o próprio [data-reveal] já garante o estado final.
   b) [data-stagger="filhos"] — o PAI é o [data-reveal]; os filhos são
      HTML puro e herdam entrada + atraso. Só existe com [data-reveal]
      no pai, e o JS garante .is-visible mesmo sem IntersectionObserver.

   MARCAÇÃO
     <div class="grid" data-stagger>
       <article data-reveal> … </article>   ← sem delay escrito
     </div>
     <ul class="lista" data-reveal data-stagger="filhos"> <li>…</li> </ul>
   ===================================================================== */
[data-stagger] {
  --stagger-passo: 85ms;
  --stagger-base: 0ms;
  --stagger-y: 18px;
}

.cascata--rapida { --stagger-passo: 55ms; }
.cascata--lenta  { --stagger-passo: 135ms; }
.cascata--tardia { --stagger-base: 160ms; }

/* modo (b): estado inicial dos filhos. Escopo com [data-reveal] no
   próprio seletor — sem ele nada fica escondido, e é isso que impede o
   pesadelo do "conteúdo invisível para sempre". */
[data-reveal][data-stagger="filhos"] > * {
  opacity: 0;
  transform: translate3d(0, var(--stagger-y), 0);
  transition:
    opacity var(--t-slow) var(--ease),
    transform var(--t-slow) var(--ease);
}

[data-reveal][data-stagger="filhos"].is-visible > * {
  opacity: 1;
  transform: none;
}

/* O ritmo. Vale para o filho que tem [data-reveal] próprio (modo a) e
   para o filho herdeiro (modo b). Doze degraus e um teto. */
[data-stagger] > [data-reveal]:nth-child(1),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(1)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 0); }
[data-stagger] > [data-reveal]:nth-child(2),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(2)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 1); }
[data-stagger] > [data-reveal]:nth-child(3),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(3)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 2); }
[data-stagger] > [data-reveal]:nth-child(4),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(4)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 3); }
[data-stagger] > [data-reveal]:nth-child(5),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(5)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 4); }
[data-stagger] > [data-reveal]:nth-child(6),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(6)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 5); }
[data-stagger] > [data-reveal]:nth-child(7),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(7)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 6); }
[data-stagger] > [data-reveal]:nth-child(8),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(8)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 7); }
[data-stagger] > [data-reveal]:nth-child(9),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(9)  { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 8); }
[data-stagger] > [data-reveal]:nth-child(10),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(10) { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 9); }
[data-stagger] > [data-reveal]:nth-child(11),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(11) { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 10); }
[data-stagger] > [data-reveal]:nth-child(12),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(12) { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 11); }
[data-stagger] > [data-reveal]:nth-child(n+13),
[data-reveal][data-stagger="filhos"].is-visible > *:nth-child(n+13) { transition-delay: calc(var(--stagger-base) + var(--stagger-passo) * 12); }

/* Depois de entrar, o atraso não pode contaminar o hover: um cartão com
   .lift no 8º lugar responderia ao mouse 600ms depois. Aqui ele zera. */
[data-stagger] > [data-reveal].is-visible:hover,
[data-stagger] > [data-reveal].is-visible:focus-within,
[data-reveal][data-stagger="filhos"].is-visible > *:hover,
[data-reveal][data-stagger="filhos"].is-visible > *:focus-within {
  transition-delay: 0s;
}


/* =====================================================================
   24. BOTÃO MAGNÉTICO — .magnetico  (atributo: [data-magnetico])
   O rótulo é atraído pelo ponteiro: alguns pixels, nunca mais que
   --mag-max. Quem escreve --mx/--my é o main.js, que JÁ observa o
   seletor [data-magnetico] — a CLASSE sozinha não recebe as variáveis.
   Portanto o elemento precisa das DUAS coisas:

     <a class="btn btn-primary magnetico" data-magnetico href="…">
       <span class="magnetico__rotulo">Falar com um especialista</span>
     </a>

   A matemática é a mesma do .tilt: como o CSS não sabe a largura real,
   --mag-cx/--mag-cy são a meia-caixa ESTIMADA e o clamp satura o
   deslocamento num valor confortável mesmo com a estimativa errada.
   Nenhum pseudo-elemento é usado: o ::after do .btn-primary é o brilho
   que atravessa, e ele fica intacto.
   ===================================================================== */
.magnetico,
[data-magnetico] {
  --mag-cx: 92px;   /* meia-largura estimada */
  --mag-cy: 26px;   /* meia-altura estimada */
  --mag-max: 6px;   /* teto do deslocamento */
  --mag-forca: .16; /* fração do afastamento do cursor */
  --mx: var(--mag-cx);
  --my: var(--mag-cy);
}

.magnetico--largo { --mag-cx: 140px; }
.magnetico--alto  { --mag-cy: 40px; }
.magnetico--forte { --mag-forca: .26; --mag-max: 9px; }
.magnetico--sutil { --mag-forca: .1;  --mag-max: 4px; }

.magnetico__rotulo {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  transform: translate3d(0, 0, 0);
  transition: transform .42s var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .magnetico:hover .magnetico__rotulo,
  .magnetico:focus-visible .magnetico__rotulo,
  [data-magnetico]:hover .magnetico__rotulo,
  [data-magnetico]:focus-visible .magnetico__rotulo {
    /* perseguir é curto; soltar é longo (a volta usa a duração base) */
    transition-duration: .11s;
    will-change: transform;
    transform: translate3d(
      clamp(calc(var(--mag-max) * -1),
            calc((var(--mx) - var(--mag-cx)) * var(--mag-forca)),
            var(--mag-max)),
      clamp(calc(var(--mag-max) * -1),
            calc((var(--my) - var(--mag-cy)) * var(--mag-forca)),
            var(--mag-max)),
      0);
  }
}


/* =====================================================================
   25. LINHA DO TEMPO QUE SE DESENHA — .timeline--anima
   O traço vertical do .timeline (style.css, ::before de 1px) cresce de
   cima para baixo em scaleY, e cada marcador acende 90ms depois do
   anterior. Combina com [data-reveal] no próprio <div class="timeline">.

   MARCAÇÃO: <div class="timeline timeline--anima" data-reveal> … </div>

   Segurança: o estado escondido só existe quando há [data-reveal] no
   elemento (o JS sempre entrega .is-visible, com ou sem
   IntersectionObserver). Sem [data-reveal], a linha do tempo é a de
   sempre, desenhada e parada.
   ===================================================================== */
.timeline--anima {
  --tl-passo: 90ms;
  --tl-base: 120ms;
}

.timeline--anima.cascata--rapida { --tl-passo: 60ms; }
.timeline--anima.cascata--lenta  { --tl-passo: 140ms; }

/* o traço */
[data-reveal].timeline--anima::before {
  transform-origin: 50% 0;
  transition: transform 1.15s var(--ease-io);
}

[data-reveal].timeline--anima:not(.is-visible)::before {
  transform: scaleY(0);
}

[data-reveal].timeline--anima.is-visible::before {
  transform: scaleY(1);
}

/* os marcadores */
[data-reveal].timeline--anima .timeline-item::before {
  transform-origin: 50% 50%;
  transition:
    opacity .4s var(--ease),
    transform .55s var(--ease),
    filter .55s var(--ease);
}

[data-reveal].timeline--anima:not(.is-visible) .timeline-item::before {
  opacity: 0;
  transform: scale(.2);
  filter: drop-shadow(0 0 0 rgba(129, 140, 248, 0));
}

[data-reveal].timeline--anima.is-visible .timeline-item::before {
  opacity: 1;
  transform: scale(1);
  filter: drop-shadow(0 0 10px rgba(129, 140, 248, .55));
}

/* o anel que pinga uma vez em cada marcador */
.timeline--anima .timeline-item::after {
  content: "";
  position: absolute;
  top: 8px;
  left: -30px;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1px solid var(--accent);
  pointer-events: none;
  opacity: 0;
}

[data-reveal].timeline--anima.is-visible .timeline-item::after {
  animation: tl-pingo .95s var(--ease-io) both;
}

@keyframes tl-pingo {
  0%   { opacity: 0;   transform: scale(.6); }
  25%  { opacity: .85; }
  100% { opacity: 0;   transform: scale(2.9); }
}

/* o ritmo: doze degraus, marcador e anel no mesmo compasso */
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(1)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(1)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 0); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 0); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(2)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(2)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 1); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 1); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(3)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(3)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 2); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 2); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(4)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(4)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 3); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 3); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(5)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(5)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 4); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 4); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(6)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(6)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 5); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 5); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(7)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(7)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 6); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 6); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(8)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(8)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 7); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 7); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(9)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(9)::after  { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 8); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 8); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(10)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(10)::after { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 9); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 9); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(11)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(11)::after { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 10); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 10); }
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(n+12)::before,
[data-reveal].timeline--anima.is-visible .timeline-item:nth-child(n+12)::after { transition-delay: calc(var(--tl-base) + var(--tl-passo) * 11); animation-delay: calc(var(--tl-base) + var(--tl-passo) * 11); }


/* =====================================================================
   26. NÚMERO EM DESTAQUE — .stat--destaque
   O main.js marca o [data-count] com data-contando="ok" quando começa
   a contar, e a contagem dura exatamente 1400ms. O pico entra em
   1,35s: o brilho estoura no instante em que o número congela, e a
   escala volta quase toda — sobra um resíduo de luz que MANTÉM o
   número em destaque depois do efeito.

   MARCAÇÃO
     <div class="stat stat--destaque">
       <span class="stat-value" data-count="128">0</span>
       <span class="stat-label">agentes no ar</span>
     </div>
   ===================================================================== */
.stat--destaque {
  position: relative;
  isolation: isolate;
}

.stat--destaque .stat-value {
  display: inline-block;
  transform-origin: 50% 62%;
}

.stat--destaque .stat-value[data-contando="ok"],
.stat--destaque [data-count][data-contando="ok"],
.stat--destaque .stat-value.is-counted {
  animation: stat-destaque-pico 1.05s var(--ease-io) 1.35s 1 both;
}

@keyframes stat-destaque-pico {
  0% {
    transform: scale(1);
    filter: drop-shadow(0 0 0 rgba(129, 140, 248, 0));
  }
  38% {
    transform: scale(1.09);
    filter: drop-shadow(0 0 22px rgba(129, 140, 248, .95));
  }
  100% {
    transform: scale(1.015);
    filter: drop-shadow(0 0 9px rgba(129, 140, 248, .38));
  }
}

/* auréola atrás do número, no mesmo compasso. z-index -1 dentro do
   isolamento do cartão: fica acima do fundo e abaixo do texto. */
.stat--destaque::after {
  content: "";
  position: absolute;
  inset: 6% 12% 34% 12%;
  z-index: -1;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(129, 140, 248, .38),
    rgba(8, 11, 239, .16) 55%,
    transparent 78%);
  filter: blur(18px);
  transform: scale(.6);
}

.stat--destaque:has([data-contando="ok"])::after {
  animation: stat-destaque-aurea 1.4s var(--ease-io) 1.3s 1 both;
}

@keyframes stat-destaque-aurea {
  0%   { opacity: 0;   transform: scale(.6); }
  35%  { opacity: 1;   transform: scale(1.06); }
  100% { opacity: .34; transform: scale(1); }
}


/* =====================================================================
   27. TRANSIÇÃO ENTRE SEÇÕES — .borda-luz
   Uma linha de 1px no topo da seção que ACENDE do centro para as duas
   bordas quando a seção entra. É o que costura duas seções vizinhas
   sem desenhar um separador duro.

   MARCAÇÃO: <section class="section borda-luz" data-reveal="fade"> … </section>

   Segurança em três camadas:
   1. o padrão é a linha ACESA (scaleX(1)) — se nada disparar, o
      acabamento continua lá;
   2. com [data-reveal], ela começa apagada e acende no .is-visible;
   3. sem [data-reveal], mas com suporte a animation-timeline, ela
      acende sozinha ao entrar na viewport.
   ===================================================================== */
.borda-luz {
  position: relative;
}

.borda-luz::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 1px;
  z-index: 1;
  pointer-events: none;
  background-image: linear-gradient(90deg,
    transparent 0%,
    rgba(129, 140, 248, .55) 22%,
    rgba(255, 255, 255, .85) 50%,
    rgba(129, 140, 248, .55) 78%,
    transparent 100%);
  transform-origin: 50% 50%;
  transform: scaleX(1);           /* estado final = fallback */
  transition:
    transform 1.1s var(--ease-io),
    opacity .8s var(--ease-io);
}

/* camada 2: quando a seção é observada, começa apagada */
[data-reveal].borda-luz:not(.is-visible)::before {
  opacity: 0;
  transform: scaleX(0);
}

/* variante embaixo, para fechar uma seção em vez de abrir */
.borda-luz--baixo::before {
  top: auto;
  bottom: 0;
}

/* variante de marca: a linha é azul, sem o núcleo branco */
.borda-luz--marca::before {
  background-image: linear-gradient(90deg,
    transparent 0%,
    var(--brand) 30%,
    var(--brand-2) 50%,
    var(--brand) 70%,
    transparent 100%);
}

@keyframes borda-luz-acende {
  from { opacity: 0; transform: scaleX(0); }
  to   { opacity: 1; transform: scaleX(1); }
}

/* camada 3 */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .borda-luz:not([data-reveal])::before {
      animation-name: borda-luz-acende;
      animation-duration: auto;
      animation-timing-function: linear;
      animation-fill-mode: both;
      animation-timeline: view();
      animation-range: entry 0% entry 70%;
    }
  }
}


/* =====================================================================
   28. CARTÃO EM CAMADAS — .camadas
   Duas placas no mesmo espaço 3D. No hover a da frente sobe no eixo Z e
   a de trás RECUA — o afastamento entre elas é que produz a sensação de
   volume, não a sombra. A sombra só confirma.

   MARCAÇÃO
     <article class="card camadas">
       <div class="camadas__fundo" aria-hidden="true"></div>
       <div class="camadas__frente"> … conteúdo … </div>
     </article>

   O main.js já escreve --mx/--my em .camadas: o brilho especular do
   fundo segue o ponteiro sem uma linha de JS nova.
   ===================================================================== */
.camadas {
  --cam-z-frente: 46px;
  --cam-z-fundo: -54px;
  --cam-dur: .55s;
  --mx: 50%;
  --my: 50%;

  position: relative;
  isolation: isolate;
  perspective: 1000px;
  perspective-origin: 50% 40%;
  transform-style: preserve-3d;
}

.camadas--sutil { --cam-z-frente: 26px; --cam-z-fundo: -30px; }
.camadas--fundo { --cam-z-frente: 70px; --cam-z-fundo: -80px; }

.camadas__fundo {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  border-radius: inherit;
  pointer-events: none;
  transform-style: preserve-3d;
  transform: translate3d(0, 0, 0);
  background-image:
    radial-gradient(220px circle at var(--mx) var(--my),
      rgba(129, 140, 248, .18),
      transparent 62%),
    linear-gradient(160deg,
      rgba(255, 255, 255, .05),
      rgba(8, 11, 239, .08) 60%,
      transparent 100%);
  transition:
    transform var(--cam-dur) var(--ease),
    opacity var(--cam-dur) var(--ease);
}

/* a sombra mora no fundo (elemento próprio) para não disputar
   pseudo-elemento com .rimlight, .glow-border ou .noise no mesmo card */
.camadas__fundo::after {
  content: "";
  position: absolute;
  inset: auto 8% -6% 8%;
  height: 34%;
  border-radius: 50%;
  pointer-events: none;
  opacity: .35;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(4, 1, 14, .8),
    rgba(8, 11, 239, .2) 58%,
    transparent 80%);
  filter: blur(16px);
  transform: scale3d(.8, .7, 1);
  transition:
    transform var(--cam-dur) var(--ease),
    opacity var(--cam-dur) var(--ease);
}

.camadas__frente {
  position: relative;
  z-index: 1;
  transform-style: preserve-3d;
  transform: translate3d(0, 0, 0);
  transition: transform var(--cam-dur) var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .camadas:hover .camadas__frente,
  .camadas:focus-within .camadas__frente {
    transform: translate3d(0, -6px, var(--cam-z-frente));
  }

  .camadas:hover .camadas__fundo,
  .camadas:focus-within .camadas__fundo {
    opacity: .9;
    transform: translate3d(0, 8px, var(--cam-z-fundo));
  }

  .camadas:hover .camadas__fundo::after,
  .camadas:focus-within .camadas__fundo::after {
    opacity: .8;
    transform: scale3d(1, 1, 1);
  }
}


/* =====================================================================
   29. CURSOR DE PRECISÃO — .alvo-cursor
   Um anel fino que acompanha o ponteiro DENTRO do bloco. Nenhum JS
   novo: o main.js já lista .alvo-cursor entre os elementos que recebem
   --mx/--my.

   Só transform (o anel tem tamanho fixo e é recuado por margem
   ESTÁTICA de meia-caixa; margem nunca entra na animação). overflow
   hidden no bloco garante que o anel não vaze quando o cursor passa
   rente à borda.

   MARCAÇÃO: <div class="painel alvo-cursor"> … </div>
   CUIDADO: usa ::after — não combine no MESMO elemento com .tilt,
   .noise ou .btn-primary, que também usam ::after.
   ===================================================================== */
.alvo-cursor {
  --alvo-tam: 38px;
  --mx: 50%;
  --my: 50%;

  position: relative;
  overflow: hidden;
  isolation: isolate;
}

.alvo-cursor > * { z-index: 1; }

.alvo-cursor::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  z-index: 2;
  width: var(--alvo-tam);
  height: var(--alvo-tam);
  margin: calc(var(--alvo-tam) / -2);
  border-radius: 50%;
  border: 1px solid rgba(129, 140, 248, .5);
  box-shadow: inset 0 0 12px rgba(129, 140, 248, .18);
  pointer-events: none;
  opacity: 0;
  transform: translate3d(var(--mx), var(--my), 0);
  transition: opacity .22s var(--ease-io);
}

.alvo-cursor--grande { --alvo-tam: 64px; }
.alvo-cursor--marca::after { border-color: rgba(8, 11, 239, .65); }

@media (hover: hover) and (pointer: fine) {
  .alvo-cursor::after { will-change: transform; }

  .alvo-cursor:hover::after,
  .alvo-cursor:focus-within::after { opacity: 1; }
}


/* =====================================================================
   30. REFINO DO DESDOBRAMENTO — overshoot
   O que faltava para a tela ter PESO: ela não pode parar no zero. Passa
   um grau ou dois além, hesita e volta. A curva faz isso sozinha — o
   quarto ponto de controle acima de 1 leva o rotateX a ~-1,3° antes de
   assentar, e leva junto o translateY e a escala, porque um transform
   interpola como um bloco só. Nenhum keyframe novo, nenhuma disputa
   com a transição que já existe.
   ===================================================================== */
[data-reveal="unfold"] {
  --unfold-overshoot: 1.055; /* 1 = sem passar do ponto; 1.09 = elástico */
  --unfold-ease: cubic-bezier(.16, .86, .3, var(--unfold-overshoot));
}

/* escape: peças em que o retorno atrapalha (tela com muito texto) */
[data-reveal="unfold"].unfold--sem-overshoot { --unfold-overshoot: 1; }

/* mais peso ainda, para a tela-herói de uma página de produto */
[data-reveal="unfold"].unfold--elastico { --unfold-overshoot: 1.09; }


/* =====================================================================
   33. RASTRO DO CURSOR — .rastro
   O ponteiro deixa de ser uma seta do sistema e passa a ter PESO: um
   ponto nítido que não atrasa nunca e um halo grande que chega depois.
   A diferença de velocidade entre os dois É o rastro — não há cópia,
   não há canvas, não há partícula. Dois elementos e uma interpolação.

   ARQUITETURA (o que evita jank)
   - .rastro é o CARRO: uma âncora de tamanho zero, position:fixed, que
     o JS move para a posição CRUA do ponteiro a cada frame. Sem
     transição: qualquer atraso aqui vira arrasto no núcleo.
   - .rastro__nucleo mora na origem do carro. Como não tem transform de
     posição própria, ele já está exatamente sob o ponteiro.
   - .rastro__halo é deslocado pela DIFERENÇA entre a posição suavizada
     (--hx/--hy, que o JS interpola) e a posição crua (--rx/--ry). A
     subtração é feita aqui, em calc(): o JS só escreve quatro números.
   - o que ESCALA nos estados nunca é o elemento que translada: a escala
     mora no ::before do halo e no próprio núcleo (que só escala). Assim
     uma transição de escala jamais briga com a interpolação de posição.

   MARCAÇÃO (criada pelo JS, não existe no HTML-fonte)
     <div class="rastro" aria-hidden="true">
       <span class="rastro__halo"></span>
       <span class="rastro__nucleo"></span>
     </div>

   z-index 85: acima de todo conteúdo e abaixo do cromo (navbar 100,
   mega-menu ~1000, modais 300+). Nada aqui captura ponteiro.
   ===================================================================== */
.rastro {
  /* posição crua do ponteiro (viewport) — o JS escreve */
  --rx: -300px;
  --ry: -300px;
  /* posição suavizada do halo (viewport) — o JS interpola e escreve */
  --hx: -300px;
  --hy: -300px;
  /* escalas de estado — só o CSS mexe */
  --nucleo-e: 1;
  --halo-e: 1;
  --halo-op: .85;
  --rastro-escala: 1;   /* calibragem global do halo (o responsivo baixa) */

  position: fixed;
  top: 0;
  left: 0;
  z-index: 85;
  width: 0;
  height: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
  opacity: 1;
  transform: translate3d(var(--rx), var(--ry), 0);
  transition: opacity .3s var(--ease-io);
  will-change: transform;
}

/* ponteiro saiu da janela (ou ainda não se moveu): o rastro apaga.
   Só opacity — o elemento continua lá, pronto para o próximo frame. */
.rastro--oculto { opacity: 0; }

/* --- 33.1 O NÚCLEO — segue sem atraso --------------------------------
   8px de luz com miolo branco. Não translada: o carro já o pôs no
   lugar certo. Só a escala responde a estado.
   --------------------------------------------------------------------- */
.rastro__nucleo {
  position: absolute;
  top: 0;
  left: 0;
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  border-radius: 50%;
  pointer-events: none;
  background-image: radial-gradient(circle at 50% 50%,
    rgba(255, 255, 255, .98) 0 26%,
    var(--accent) 58%,
    rgba(129, 140, 248, 0) 76%);
  box-shadow:
    0 0 10px rgba(129, 140, 248, .75),
    0 0 22px rgba(8, 11, 239, .45);
  transform: scale(var(--nucleo-e));
  transition: transform .22s var(--ease);
}

/* --- 33.2 O HALO — chega depois --------------------------------------
   Translada pela diferença entre a posição suavizada e a crua. Sem
   transição de propósito: quem suaviza é a interpolação do JS, quadro
   a quadro. Uma transição aqui somaria dois atrasos e viraria borracha.
   --------------------------------------------------------------------- */
.rastro__halo {
  position: absolute;
  top: 0;
  left: 0;
  width: 260px;
  height: 260px;
  margin: -130px 0 0 -130px;
  border-radius: 50%;
  pointer-events: none;
  transform: translate3d(
    calc(var(--hx) - var(--rx)),
    calc(var(--hy) - var(--ry)),
    0);
  will-change: transform;
}

/* o brilho propriamente dito. Fica no ::before para que a ESCALA de
   estado não dispute a propriedade transform com o deslocamento. */
.rastro__halo::before {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: 50%;
  pointer-events: none;
  opacity: var(--halo-op);
  background-image: radial-gradient(circle at 50% 50%,
    rgba(129, 140, 248, .34) 0%,
    rgba(8, 11, 239, .22) 34%,
    rgba(84, 4, 148, .12) 56%,
    rgba(84, 4, 148, 0) 72%);
  filter: blur(14px);
  transform: scale(calc(var(--halo-e) * var(--rastro-escala)));
  transition:
    transform .38s var(--ease),
    opacity .38s var(--ease-io);
}

/* anel de precisão: nasce apagado e só aparece sobre o que é clicável */
.rastro__halo::after {
  content: "";
  position: absolute;
  inset: 108px;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  border: 1px solid rgba(34, 197, 94, .7);
  box-shadow: 0 0 14px rgba(34, 197, 94, .22);
  transform: scale(.45);
  transition:
    opacity .28s var(--ease-io),
    transform .38s var(--ease);
}

/* --- 33.3 ESTADOS ----------------------------------------------------
   O JS só troca a classe no .rastro; as variáveis descem por herança.
   --------------------------------------------------------------------- */

/* sobre link, botão ou campo: o halo cresce, o núcleo encolhe e o anel
   verde fecha em volta — a leitura é "aqui dá para clicar". */
.rastro--ativo {
  --halo-e: 1.34;
  --halo-op: 1;
  --nucleo-e: .5;
}

.rastro--ativo .rastro__halo::after {
  opacity: 1;
  transform: scale(1);
}

/* botão do mouse apertado: tudo encolhe por um instante */
.rastro--pressionado {
  --halo-e: .78;
  --halo-op: .7;
  --nucleo-e: .68;
}

.rastro--pressionado .rastro__halo::after { transform: scale(.86); }

/* variante fria, para páginas que vivem sobre fundo muito escuro */
.rastro--marca .rastro__halo::before {
  background-image: radial-gradient(circle at 50% 50%,
    rgba(8, 11, 239, .40) 0%,
    rgba(84, 4, 148, .24) 38%,
    rgba(84, 4, 148, 0) 72%);
}


/* =====================================================================
   34. FUNDO QUE REAGE AO MOUSE — .fundo-reativo
   Uma camada de seção cuja LUZ mora onde o cursor está. Não é o
   spotlight de cartão da seção 6: aqui o alcance é de seção inteira, e
   a camada nasce acesa em repouso (nunca preta) e só GANHA presença
   quando o ponteiro entra.

   MARCAÇÃO
     <section class="section" data-fundo-reativo>
       <div class="fundo-reativo fundo-reativo--grade" aria-hidden="true"></div>
       <div class="container"> … conteúdo … </div>
     </section>

   O pai [data-fundo-reativo] recebe --mx/--my em px (canto superior
   esquerdo dele) do main.js — as MESMAS variáveis do spotlight. Sem JS,
   o fallback var(--mx, 50%) centra tudo e a seção continua bonita.
   Zero imagem: só gradiente e máscara. Zero clique: pointer-events none.
   ===================================================================== */
[data-fundo-reativo] {
  position: relative;
}

[data-fundo-reativo] > *:not(.fundo-reativo) {
  position: relative;
  z-index: 1;
}

.fundo-reativo {
  --fr-raio: 620px;     /* alcance do halo do cursor */
  --fr-celula: 44px;    /* passo da grade / da rede de nós */
  --fr-repouso: .45;    /* presença com o ponteiro fora */
  --fr-ativo: 1;        /* presença com o ponteiro dentro */

  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
  contain: paint;
  opacity: var(--fr-repouso);
  background-image: radial-gradient(var(--fr-raio) circle at var(--mx, 50%) var(--my, 50%),
    rgba(8, 11, 239, .28) 0%,
    rgba(84, 4, 148, .16) 38%,
    rgba(84, 4, 148, .06) 58%,
    transparent 74%);
  background-repeat: no-repeat;
  transition: opacity .55s var(--ease-io);
}

/* o ponteiro entra: a seção acende. Sai: volta ao repouso, macio. */
[data-fundo-reativo]:hover > .fundo-reativo,
[data-fundo-reativo]:focus-within > .fundo-reativo {
  opacity: var(--fr-ativo);
}

/* intensidade sem tocar nas cores */
.fundo-reativo--suave { --fr-repouso: .28; --fr-ativo: .7; }
.fundo-reativo--forte { --fr-repouso: .6;  --fr-ativo: 1; --fr-raio: 760px; }

/* recorte vertical opcional: a camada morre antes das bordas da seção */
.fundo-reativo--vinheta {
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
  mask-image: linear-gradient(180deg, transparent 0%, #000 14%, #000 86%, transparent 100%);
}

/* --- 34.1 GRADE TÉCNICA — .fundo-reativo--grade ----------------------
   Duas grades idênticas. A de baixo é o desenho apagado, sempre lá; a
   de cima é a mesma grade em cor de marca, recortada por uma máscara
   radial presa ao cursor: as células perto do ponteiro ACENDEM.
   --------------------------------------------------------------------- */
.fundo-reativo--grade::before,
.fundo-reativo--grade::after {
  content: "";
  position: absolute;
  inset: -1px;
  pointer-events: none;
  background-image:
    linear-gradient(90deg, rgba(197, 176, 228, .14) 0 1px, transparent 1px 100%),
    linear-gradient(180deg, rgba(197, 176, 228, .10) 0 1px, transparent 1px 100%);
  background-size: var(--fr-celula) var(--fr-celula);
}

.fundo-reativo--grade::before { opacity: .55; }

.fundo-reativo--grade::after {
  background-image:
    linear-gradient(90deg, rgba(129, 140, 248, .55) 0 1px, transparent 1px 100%),
    linear-gradient(180deg, rgba(8, 11, 239, .5) 0 1px, transparent 1px 100%);
  opacity: 0;
  transition: opacity .4s var(--ease-io);
  -webkit-mask-image: radial-gradient(calc(var(--fr-raio) * .58) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .5) 44%, transparent 72%);
  mask-image: radial-gradient(calc(var(--fr-raio) * .58) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .5) 44%, transparent 72%);
}

[data-fundo-reativo]:hover > .fundo-reativo--grade::after,
[data-fundo-reativo]:focus-within > .fundo-reativo--grade::after { opacity: 1; }

/* --- 34.2 REDE DE NÓS — .fundo-reativo--nos --------------------------
   Mesma lógica da grade, com pontos no lugar das linhas: os nós perto
   do cursor acendem em branco e os de longe continuam em lilás fraco.
   --------------------------------------------------------------------- */
.fundo-reativo--nos::before,
.fundo-reativo--nos::after {
  content: "";
  position: absolute;
  inset: -2px;
  pointer-events: none;
  background-image:
    radial-gradient(circle at 50% 50%, rgba(197, 176, 228, .5) 0 1.5px, transparent 2.3px);
  background-size: var(--fr-celula) var(--fr-celula);
}

.fundo-reativo--nos::before { opacity: .4; }

.fundo-reativo--nos::after {
  background-image:
    radial-gradient(circle at 50% 50%, rgba(255, 255, 255, .95) 0 1.8px, transparent 2.7px),
    radial-gradient(circle at 50% 50%, rgba(129, 140, 248, .45) 0 3.4px, transparent 5px);
  background-size:
    var(--fr-celula) var(--fr-celula),
    calc(var(--fr-celula) * 2) calc(var(--fr-celula) * 2);
  opacity: 0;
  transition: opacity .4s var(--ease-io);
  -webkit-mask-image: radial-gradient(calc(var(--fr-raio) * .5) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .45) 40%, transparent 68%);
  mask-image: radial-gradient(calc(var(--fr-raio) * .5) circle at var(--mx, 50%) var(--my, 50%),
    #000 0%, rgba(0, 0, 0, .45) 40%, transparent 68%);
}

[data-fundo-reativo]:hover > .fundo-reativo--nos::after,
[data-fundo-reativo]:focus-within > .fundo-reativo--nos::after { opacity: 1; }


/* =====================================================================
   35. 3D MAIS INTENSO
   O desdobramento ganha ângulo, distância e uma sombra que ABRE junto;
   e chegam três peças novas: uma cena com espaço 3D compartilhado,
   camadas de profundidade dentro dela e um cartão que gira em dois
   eixos seguindo o ponteiro.
   ===================================================================== */

/* --- 35.1 REFORÇO DO DESDOBRAMENTO — [data-reveal="unfold"] ----------
   Mesma coreografia da seção 18, com mais volume: a tela chega mais
   tombada e mais longe, e a sombra deixa de ser um borrão de opacidade
   constante — ela começa CURTA e ESCURA (a tela está de pé, a luz passa
   rente) e termina LARGA e DIFUSA (a tela deitou, a sombra se espalhou).
   O overshoot da seção 30 continua valendo: só as variáveis mudam.

   As calibragens .unfold--suave / .unfold--extremo continuam mandando
   (têm mais especificidade); .unfold--extremo é reescrito aqui para
   continuar sendo o mais forte dos três.
   --------------------------------------------------------------------- */
[data-reveal="unfold"] {
  --unfold-persp: 900px;
  --unfold-rx: 54deg;
  --unfold-rz: 1.1deg;
  --unfold-z: -320px;
  --unfold-y: 86px;
  --unfold-scale: .9;
  --unfold-pop: 24px;
}

[data-reveal="unfold"].unfold--extremo {
  --unfold-persp: 820px;
  --unfold-rx: 60deg;
  --unfold-rz: 1.5deg;
  --unfold-z: -380px;
  --unfold-y: 96px;
  --unfold-scale: .87;
  --unfold-pop: 28px;
}

/* o Z maior aumenta a moldura opticamente; recuamos a caixa de layout
   na mesma proporção para nada encostar na borda da viewport */
[data-reveal="unfold"] .app-shot { width: 96%; }

/* A SOMBRA QUE ABRE — de faca a mancha */
[data-reveal="unfold"]::before {
  background:
    radial-gradient(52% 50% at 50% 50%,
      rgba(4, 1, 14, .92),
      rgba(8, 11, 239, .22) 55%,
      transparent 78%);
  filter: blur(9px);
  transform: translate3d(0, -44px, 0) scale3d(.4, .2, 1);
  transition:
    opacity var(--unfold-dur) var(--unfold-ease),
    transform var(--unfold-dur) var(--unfold-ease),
    filter var(--unfold-dur) var(--unfold-ease);
}

[data-reveal="unfold"].is-visible::before {
  opacity: .95;
  filter: blur(34px);
  transform: translate3d(0, 16px, 0) scale3d(1.04, 1.1, 1);
}

/* --- 35.2 CENA 3D — .cena-3d + .profundidade-1…4 ---------------------
   Um espaço tridimensional COMPARTILHADO: vários elementos deixam de
   ser irmãos no plano e passam a estar a distâncias diferentes do olho.

   O passo é medido a partir da profundidade 2, que fica no plano zero:
   a 1 recua, a 3 e a 4 avançam. Isso mantém a escala óptica dentro de
   ±8% e garante que nada cresça o suficiente para vazar na horizontal.

   O afastamento é proporcional a --cena-fator: pequeno em repouso,
   inteiro quando o bloco entra (.is-visible), maior ainda no hover.

   MARCAÇÃO
     <div class="cena-3d" data-reveal>
       <div class="profundidade-1"> … fundo … </div>
       <div class="profundidade-3"> … frente … </div>
     </div>

   ATENÇÃO: não coloque overflow, filter, opacity<1 nem isolation no
   .cena-3d — todas são grouping properties e ACHATAM o preserve-3d.
   --------------------------------------------------------------------- */
.cena-3d {
  --cena-persp: 1150px;
  --cena-passo: 26px;
  --cena-fator: .38;

  position: relative;
  max-width: 100%;
  perspective: var(--cena-persp);
  perspective-origin: 50% 42%;
  transform-style: preserve-3d;
}

.cena-3d--curta { --cena-persp: 780px; }
.cena-3d--longa { --cena-persp: 1700px; --cena-passo: 34px; }
.cena-3d--sutil { --cena-passo: 16px; }

/* quando o bloco entra, as camadas se SEPARAM: é o gesto que conta a
   profundidade. O estado de repouso já é visível — nada some. */
.cena-3d.is-visible { --cena-fator: 1; }

.profundidade-1,
.profundidade-2,
.profundidade-3,
.profundidade-4 {
  transform-style: preserve-3d;
  transition: transform .85s var(--ease);
}

.profundidade-1 { transform: translate3d(0, 0, calc(var(--cena-passo) * -1 * var(--cena-fator))); }
.profundidade-2 { transform: translate3d(0, 0, 0); }
.profundidade-3 { transform: translate3d(0, 0, calc(var(--cena-passo) * 1 * var(--cena-fator))); }
.profundidade-4 { transform: translate3d(0, 0, calc(var(--cena-passo) * 2 * var(--cena-fator))); }

@media (hover: hover) and (pointer: fine) {
  .cena-3d:hover,
  .cena-3d:focus-within { --cena-fator: 1.55; }

  .cena-3d:hover .profundidade-1,
  .cena-3d:hover .profundidade-2,
  .cena-3d:hover .profundidade-3,
  .cena-3d:hover .profundidade-4,
  .cena-3d:focus-within .profundidade-1,
  .cena-3d:focus-within .profundidade-2,
  .cena-3d:focus-within .profundidade-3,
  .cena-3d:focus-within .profundidade-4 { transition-duration: .45s; }
}

/* --- 35.3 CARTÃO QUE GIRA COM O CURSOR — .carta-3d -------------------
   Irmão mais forte do .tilt: gira mais, tem brilho especular próprio e
   — o detalhe que vende o volume — uma SOMBRA que se desloca na direção
   CONTRÁRIA à inclinação. É assim que a luz se comporta: se o cartão
   cai para a direita, a sombra escorre para a esquerda.

   A matemática é a do .tilt: como o CSS não conhece a largura real,
   --c3-cx/--c3-cy são a meia-caixa ESTIMADA e o clamp satura o ângulo
   num valor confortável mesmo com a estimativa errada.

   MARCAÇÃO: <article class="card carta-3d"> … </article>
   (o main.js precisa listar .carta-3d entre quem recebe --mx/--my)

   CUIDADO: usa ::before (sombra) e ::after (brilho). Não combine no
   MESMO elemento com .tilt, .noise, .alvo-cursor ou .btn-primary.
   --------------------------------------------------------------------- */
.carta-3d {
  --c3-persp: 820px;
  --c3-max: 11deg;      /* saturação do giro */
  --c3-cx: 200px;       /* meia-largura estimada */
  --c3-cy: 150px;       /* meia-altura estimada */
  --c3-alcance: 480px;  /* "distância do olho": quanto menor, mais gira */
  --c3-desloc: 22px;    /* quanto a sombra escorre, no máximo */
  --c3-rx: 0deg;
  --c3-ry: 0deg;
  --mx: var(--c3-cx);
  --my: var(--c3-cy);

  position: relative;
  transform-style: preserve-3d;
  transform: perspective(var(--c3-persp));
  transition: transform .55s var(--ease);
}

.carta-3d > * { z-index: 1; }

/* proporções prontas (só mudam onde o giro satura) */
.carta-3d--largo   { --c3-cx: 320px; --c3-cy: 130px; }
.carta-3d--alto    { --c3-cx: 170px; --c3-cy: 250px; }
.carta-3d--sutil   { --c3-max: 6deg; --c3-desloc: 12px; }
.carta-3d--intenso { --c3-max: 15deg; --c3-alcance: 380px; --c3-desloc: 30px; }

/* A SOMBRA CONTRÁRIA
   Mora FORA da caixa pelo rodapé (inset inferior negativo) e dentro
   dela na horizontal: escorre para os lados sem nunca gerar overflow. */
.carta-3d::before {
  content: "";
  position: absolute;
  inset: auto 9% -13% 9%;
  height: 32%;
  z-index: 0;
  border-radius: 50%;
  pointer-events: none;
  opacity: .4;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(4, 1, 14, .85),
    rgba(8, 11, 239, .22) 55%,
    transparent 80%);
  filter: blur(18px);
  transform: translate3d(0, 0, 0) scale3d(.86, .8, 1);
  transition:
    transform .55s var(--ease),
    opacity .55s var(--ease);
}

/* BRILHO ESPECULAR — a luz bate onde o ponteiro está */
.carta-3d::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background-image:
    radial-gradient(260px circle at var(--mx) var(--my),
      rgba(255, 255, 255, .2) 0%,
      rgba(129, 140, 248, .12) 38%,
      transparent 68%),
    linear-gradient(120deg,
      transparent 42%,
      rgba(255, 255, 255, .07) 50%,
      transparent 58%);
  transition: opacity .32s var(--ease-io);
}

@media (hover: hover) and (pointer: fine) {
  .carta-3d:hover,
  .carta-3d:focus-within {
    transform:
      perspective(var(--c3-persp))
      rotateX(var(--c3-rx))
      rotateY(var(--c3-ry))
      translate3d(0, -6px, 34px);
  }

  /* perseguir é curto (sem arrasto); soltar usa a duração base, longa */
  .carta-3d:hover { transition-duration: .13s; }

  .carta-3d:hover::after,
  .carta-3d:focus-within::after { opacity: 1; }

  /* a sombra vai para o lado OPOSTO ao do cursor e se espalha */
  .carta-3d:hover::before,
  .carta-3d:focus-within::before {
    opacity: .78;
    transform: translate3d(
      clamp(calc(var(--c3-desloc) * -1),
            calc((var(--c3-cx) - var(--mx)) * .16),
            var(--c3-desloc)),
      clamp(0px, calc((var(--c3-cy) - var(--my)) * .06), 10px),
      0)
      scale3d(1, 1, 1);
    transition-duration: .2s;
  }

  @supports (transform: rotate(atan2(1px, 1px))) {
    .carta-3d {
      --c3-ry: clamp(
        calc(var(--c3-max) * -1),
        atan2(calc(var(--mx) - var(--c3-cx)), var(--c3-alcance)),
        var(--c3-max));
      --c3-rx: clamp(
        calc(var(--c3-max) * -1),
        atan2(calc(var(--c3-cy) - var(--my)), var(--c3-alcance)),
        var(--c3-max));
    }
  }
}

/* camada interna que sobe no Z junto com o giro (ícone, selo, número) */
.carta-3d__camada {
  transform: translate3d(0, 0, 0);
  transition: transform .5s var(--ease);
}

@media (hover: hover) and (pointer: fine) {
  .carta-3d:hover .carta-3d__camada,
  .carta-3d:focus-within .carta-3d__camada {
    transform: translate3d(0, -3px, 46px);
  }
}


/* =====================================================================
   36. ACABAMENTO
   Duas peças pequenas que separam "site bonito" de "site caro".
   ===================================================================== */

/* Ângulo do contorno que corre a borda. Sem @property a animação de
   --ab não roda: sobra o pulso de opacidade sobre um gradiente parado,
   que continua sendo um acabamento correto — nunca um buraco. */
@property --ab {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* --- 36.1 CONTORNO QUE PERCORRE A BORDA — .brilho-borda --------------
   Uma volta, uma vez, quando o elemento entra. Não é a .glow-border
   (que gira em loop): esta acende, dá a volta e some.

   MARCAÇÃO: <article class="card brilho-borda" data-reveal> … </article>
   Precisa de [data-reveal] no mesmo elemento — é o .is-visible que
   dispara. Sem ele, nada acontece e o card fica como sempre foi.
   --------------------------------------------------------------------- */
.brilho-borda {
  --bb-dur: 1.7s;
  --bb-atraso: .12s;

  position: relative;
  isolation: isolate;
  border-radius: var(--radius, 14px);
}

.brilho-borda > * { z-index: 1; }

.brilho-borda--lento { --bb-dur: 2.6s; }
.brilho-borda--tardio { --bb-atraso: .5s; }

@supports ((-webkit-mask-composite: xor) or (mask-composite: exclude)) {
  .brilho-borda::before {
    content: "";
    position: absolute;
    inset: 0;
    z-index: -1;
    padding: 1px;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    background: conic-gradient(from var(--ab),
      transparent 0 58%,
      rgba(129, 140, 248, .55) 70%,
      rgba(255, 255, 255, .95) 78%,
      var(--brand) 86%,
      transparent 100%);
    -webkit-mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
      linear-gradient(#000 0 0) content-box,
      linear-gradient(#000 0 0);
    mask-composite: exclude;
  }

  .brilho-borda.is-visible::before {
    animation: brilho-borda-corre var(--bb-dur) var(--ease-io) var(--bb-atraso) 1 both;
  }
}

@keyframes brilho-borda-corre {
  0%   { --ab: 0deg;   opacity: 0; }
  14%  { opacity: 1; }
  82%  { opacity: 1; }
  100% { --ab: 360deg; opacity: 0; }
}

/* --- 36.2 TÍTULO QUE SE MONTA PALAVRA A PALAVRA — .texto-revela ------
   Cada palavra é um <span> que sobe girando no eixo X, 70ms depois da
   anterior. Dez degraus e um teto.

   MARCAÇÃO
     <h2 class="texto-revela" data-reveal="fade">
       <span>Agentes</span> <span>que</span> <span>trabalham</span>
     </h2>

   Segurança: o estado escondido só existe com [data-reveal] no próprio
   elemento — e o main.js entrega .is-visible mesmo sem
   IntersectionObserver. Sem [data-reveal], o título é um título normal.
   --------------------------------------------------------------------- */
.texto-revela {
  --tr-passo: 70ms;
  --tr-base: 60ms;
}

.texto-revela--rapido { --tr-passo: 45ms; }
.texto-revela--lento  { --tr-passo: 110ms; }

/* estado final (o que vale se nada disparar) */
[data-reveal].texto-revela > span {
  display: inline-block;
  transform-origin: 50% 100%;
  opacity: 1;
  transform: perspective(700px) translate3d(0, 0, 0) rotateX(0deg);
  transition:
    opacity .5s var(--ease),
    transform .78s cubic-bezier(.16, .86, .3, 1.04);
}

/* estado inicial: só enquanto o observador ainda não liberou */
[data-reveal].texto-revela:not(.is-visible) > span {
  opacity: 0;
  transform: perspective(700px) translate3d(0, .45em, -60px) rotateX(-62deg);
}

/* variante sem giro: só a subida, para títulos muito longos */
[data-reveal].texto-revela--suave:not(.is-visible) > span {
  transform: translate3d(0, .4em, 0);
}

[data-reveal].texto-revela--suave > span {
  transform: translate3d(0, 0, 0);
}

[data-reveal].texto-revela.is-visible > span:nth-child(1)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 0); }
[data-reveal].texto-revela.is-visible > span:nth-child(2)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 1); }
[data-reveal].texto-revela.is-visible > span:nth-child(3)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 2); }
[data-reveal].texto-revela.is-visible > span:nth-child(4)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 3); }
[data-reveal].texto-revela.is-visible > span:nth-child(5)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 4); }
[data-reveal].texto-revela.is-visible > span:nth-child(6)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 5); }
[data-reveal].texto-revela.is-visible > span:nth-child(7)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 6); }
[data-reveal].texto-revela.is-visible > span:nth-child(8)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 7); }
[data-reveal].texto-revela.is-visible > span:nth-child(9)  { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 8); }
[data-reveal].texto-revela.is-visible > span:nth-child(10) { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 9); }
[data-reveal].texto-revela.is-visible > span:nth-child(n+11) { transition-delay: calc(var(--tr-base) + var(--tr-passo) * 10); }


/* =====================================================================
   37. DESDOBRAMENTO 3D EXTREMO — [data-reveal="unfold"] (terceira volta)
   As seções 18, 30 e 35.1 continuam valendo: aqui só as VARIÁVEIS e a
   coreografia mudam. Nenhum nome novo é exigido do HTML.

   O que faltava para o gesto ser SENTIDO e não apenas notado:

   1. PERSPECTIVA CURTA. 1000px (seção 18) e 900px (seção 35.1) são
      quase uma câmera de teleobjetiva: a deformação some. 700px é uma
      grande-angular — o topo da tela some no fundo e a base cresce.
      É a perspectiva, não o ângulo, que faz a profundidade doer.
   2. ÂNGULO DE VERDADE: 60deg de rotateX. A tela chega DEITADA para
      trás, não apenas inclinada.
   3. UM EIXO A MAIS: 4deg de rotateY. Sem ele o movimento é chapado,
      simétrico, de apresentação de slides. Com ele a peça tem um lado
      mais perto do olho — e o cérebro lê volume.
   4. OVERSHOOT VISÍVEL: a curva passa ~2,3deg além do zero e volta.
      (o quarto ponto de controle 1.15 tem pico real ≈ 1.038 →
      1.038 × 60deg = 62,3deg percorridos sobre 60deg de amplitude.)
   5. SOMBRA QUE ABRE: começa como uma FACA (blur 6px, 32% de largura,
      colada) e termina como uma MANCHA (blur 46px, 110% de largura).
   6. CASCATA INTERNA MARCADA: cada bloco do corpo entra com 22px de
      subida E 8deg de rotateX próprios, em degraus de 95ms.
   7. VARREDURA NO FIM: o brilho diagonal (app-brilho, nome original)
      deixa de acontecer no meio do caminho e passa a correr QUANDO a
      tela termina de deitar — 1,12s, com o desdobramento em 1,6s.

   Em <=900px tudo isso encolhe (bloco 37-BIS): o exagero em tela
   pequena atrapalha a leitura.
   ===================================================================== */
[data-reveal="unfold"] {
  /* --- geometria --- */
  --unfold-persp: 700px;
  --unfold-rx: 60deg;
  --unfold-ry: 4deg;      /* eixo novo: tira o ar de movimento chapado */
  --unfold-rz: 1.4deg;
  --unfold-z: -380px;
  --unfold-y: 104px;
  --unfold-scale: .86;
  --unfold-pop: 26px;

  /* --- tempo --- */
  --unfold-dur: 1.6s;
  --unfold-overshoot: 1.15;  /* pico real ~1.038 → volta de ~2,3deg */

  /* --- cascata interna (a tela se monta) --- */
  --unfold-filho-y: 22px;
  --unfold-filho-rx: 8deg;
  --unfold-filho-persp: 620px;
  --unfold-filho-base: 560ms;
  --unfold-filho-passo: 95ms;

  /* --- varredura de luz do fim --- */
  --unfold-varredura-atraso: 1.12s;
  --unfold-varredura-dur: 1.1s;

  /* o ponto de fuga sobe: o topo da tela afunda mais que a base.
     30% (e não 20%) é o limite: acima disso o crescimento óptico do
     repouso empurra a base da moldura para cima da legenda. */
  perspective-origin: 50% 30%;

  transform:
    perspective(var(--unfold-persp))
    translate3d(0, var(--unfold-y), var(--unfold-z))
    rotateX(var(--unfold-rx))
    rotateY(var(--unfold-ry))
    rotateZ(var(--unfold-rz))
    scale(var(--unfold-scale));
}

/* Estado final. A lista de funções é IDÊNTICA à do estado inicial —
   é o que garante interpolação limpa (transform interpola função a
   função; listas diferentes caem em matriz e o movimento embola). */
[data-reveal="unfold"].is-visible {
  transform:
    perspective(var(--unfold-persp))
    translate3d(0, 0, 0)
    rotateX(0deg)
    rotateY(0deg)
    rotateZ(0deg)
    scale(1);
}

/* --- 37.1 CALIBRAGENS — os três volumes da mesma coreografia -------- */

/* metade da intensidade: telas com muito texto, blocos secundários */
[data-reveal="unfold"].unfold--suave {
  --unfold-persp: 900px;
  --unfold-rx: 30deg;
  --unfold-ry: 2deg;
  --unfold-rz: .6deg;
  --unfold-z: -190px;
  --unfold-y: 52px;
  --unfold-scale: .93;
  --unfold-pop: 15px;
  --unfold-dur: 1.4s;
  --unfold-overshoot: 1.08;
  --unfold-filho-y: 14px;
  --unfold-filho-rx: 4deg;
  --unfold-filho-base: 460ms;
  --unfold-filho-passo: 70ms;
  --unfold-varredura-atraso: .95s;
}

/* o máximo: tela-herói de página de produto */
[data-reveal="unfold"].unfold--extremo {
  --unfold-persp: 620px;
  --unfold-rx: 64deg;
  --unfold-ry: 5deg;
  --unfold-rz: 1.8deg;
  --unfold-z: -420px;
  --unfold-y: 118px;
  --unfold-scale: .82;
  --unfold-pop: 34px;
  --unfold-dur: 1.75s;
  --unfold-overshoot: 1.2;
  --unfold-filho-y: 28px;
  --unfold-filho-rx: 10deg;
  --unfold-filho-base: 620ms;
  --unfold-filho-passo: 105ms;
  --unfold-varredura-atraso: 1.25s;
}

/* o elástico continua sendo o mais solto dos quatro */
[data-reveal="unfold"].unfold--elastico { --unfold-overshoot: 1.26; }

/* --- 37.2 A SOMBRA: de faca a mancha -------------------------------
   Só transform, opacity e filter. Os limites horizontais (inset 6%)
   e o scaleX máximo (1,1 × 88% = 96,8% da figura) garantem que ela
   nunca encoste na borda da viewport.
   --------------------------------------------------------------------- */
[data-reveal="unfold"]::before {
  inset: 12% 6% -10% 6%;
  background:
    radial-gradient(54% 50% at 50% 50%,
      rgba(4, 1, 14, .95),
      rgba(8, 11, 239, .24) 54%,
      transparent 78%);
  filter: blur(6px);
  transform: translate3d(0, -58px, 0) scale3d(.32, .14, 1);
}

[data-reveal="unfold"].is-visible::before {
  opacity: .95;
  filter: blur(46px);
  transform: translate3d(0, 22px, 0) scale3d(1.1, 1.24, 1);
}

/* o Z de repouso é maior: recuamos a caixa de layout na mesma
   proporção (26px de Z em 700px de perspectiva ≈ +3,9%) */
[data-reveal="unfold"] .app-shot { width: 95%; }

/* --- 37.3 A VARREDURA DE LUZ DO FIM --------------------------------
   Mesmo pseudo-elemento e MESMO keyframe (app-brilho) da seção 18: o
   que muda é QUANDO e QUANTO. Ela agora corre quando a tela termina
   de deitar, e não no meio da queda.
   --------------------------------------------------------------------- */
[data-reveal="unfold"] .app-shot::after {
  background: linear-gradient(
    112deg,
    transparent 26%,
    rgba(8, 11, 239, .16) 40%,
    rgba(129, 140, 248, .42) 47%,
    rgba(255, 255, 255, .34) 51%,
    rgba(84, 4, 148, .20) 57%,
    transparent 72%);
}

[data-reveal="unfold"].is-visible .app-shot::after {
  animation: app-brilho var(--unfold-varredura-dur) var(--ease-io) var(--unfold-varredura-atraso) 1 both;
}

/* a linha de scanner desce um pouco antes, enquanto a tela ainda cai */
[data-reveal="unfold"].is-visible .app-shot::before {
  animation: app-scanline 1.5s var(--ease-io) .35s 1;
}

/* --- 37.4 CASCATA INTERNA — cada bloco tem o SEU desdobramento ------
   perspective() dentro do próprio transform: a peça se projeta sem
   criar contexto 3D no corpo da tela (que precisa continuar plano
   para não brigar com o preserve-3d da figura).
   --------------------------------------------------------------------- */
[data-reveal="unfold"] .app-shot__body > * {
  transform-origin: 50% 0%;
  transform:
    perspective(var(--unfold-filho-persp))
    translate3d(0, var(--unfold-filho-y), 0)
    rotateX(var(--unfold-filho-rx));
  transition:
    opacity .55s var(--ease),
    transform .78s var(--unfold-ease);
}

[data-reveal="unfold"].is-visible .app-shot__body > * {
  transform:
    perspective(var(--unfold-filho-persp))
    translate3d(0, 0, 0)
    rotateX(0deg);
}

/* o ritmo, agora por variável (o bloco responsivo só troca os números) */
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(1)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 0); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(2)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 1); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(3)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 2); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(4)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 3); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(5)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 4); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(6)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 5); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(7)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 6); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(8)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 7); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(9)  { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 8); }
[data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(n+10) { transition-delay: calc(var(--unfold-filho-base) + var(--unfold-filho-passo) * 9); }

/* a barra de título chega antes do corpo, com o seu próprio tombo */
[data-reveal="unfold"] .app-shot__bar {
  transform-origin: 50% 100%;
  transform: perspective(620px) translate3d(0, -14px, 0) rotateX(-12deg);
  transition:
    opacity .5s var(--ease) .34s,
    transform .7s var(--unfold-ease) .34s;
}

[data-reveal="unfold"].is-visible .app-shot__bar {
  transform: perspective(620px) translate3d(0, 0, 0) rotateX(0deg);
}

/* --- 37.5 HOVER — a sombra acompanha o novo volume ------------------ */
@media (hover: hover) and (pointer: fine) {
  [data-reveal="unfold"].is-visible:hover::before {
    opacity: 1;
    transform: translate3d(0, 30px, 0) scale3d(1.16, 1.34, 1);
  }
}


/* =====================================================================
   38. ABERTURA CINEMATOGRÁFICA — .abertura
   Uma camada de tela cheia que roda UMA VEZ, sozinha, no primeiro
   carregamento, e sai. É a resposta em CSS 3D + SVG à referência de
   WebGL: linhas-guia de desenho técnico, círculos tracejados, marcas
   de cota, e a interface se MONTANDO peça por peça antes do site
   aparecer.

   QUEM FAZ O QUÊ
   - o JS cria o elemento, acrescenta .p1 … .p5 na ordem, depois
     .saindo e por fim .abertura--fim (que tira a camada do caminho);
   - o CSS abaixo entrega o desenho de cada passo. Nenhuma classe some:
     elas se ACUMULAM (.p3 implica .p1 e .p2 já aplicadas).

   ESTRUTURA ESPERADA
     <div class="abertura" data-abertura>
       <div class="abertura__cena">
         <svg class="abertura__guias" viewBox="0 0 1200 700"> … </svg>
         <div class="abertura__tela">
           <div class="abertura__barra"><span></span>×3</div>
           <div class="abertura__corpo">
             <div class="abertura__bloco"></div>×5
           </div>
         </div>
         <p class="abertura__marca">Hal-AI</p>
         <p class="abertura__linha">…</p>
       </div>
       <button class="abertura__pular" type="button">Pular</button>
     </div>

   LINHA DO TEMPO (o JS segue estes números; total p1→p5 = 2760ms)
     0     .p1   traçado das guias
     620   .p2   a moldura entra
     1120  .p3   as cinco peças voam
     1720  .p4   assentamento + varredura + marca
     2340  .p5   a cena recua
     2760  .saindo   a cortina sobe
     3280  .abertura--fim  display:none

   SVG: se cada forma levar pathLength="900", o traçado fica exato sem
   ninguém medir nada — é o valor de --l. Quem precisar de outro
   comprimento usa .abertura__guia--curta / --longa.
   ===================================================================== */
.abertura {
  /* durações de cada passo (o JS usa os MESMOS números) */
  --ab-p1: 700ms;
  --ab-p2: 620ms;
  --ab-p3: 520ms;
  --ab-p4: 620ms;
  --ab-p5: 420ms;
  --ab-saida: 520ms;

  --ab-curva: cubic-bezier(.16, .86, .3, 1.06);
  --ab-persp: 900px;

  position: fixed;
  inset: 0;
  z-index: 9000;              /* acima do mega-menu (1000) e do cromo */
  display: grid;
  place-items: center;
  overflow: hidden;
  /* opaco: o site não pode piscar por trás */
  background-color: var(--bg-0);
  background-image:
    radial-gradient(78% 58% at 50% 42%, rgba(8, 11, 239, .18), transparent 72%),
    radial-gradient(52% 44% at 12% 96%, rgba(84, 4, 148, .16), transparent 74%);
  touch-action: none;
  overscroll-behavior: contain;
  clip-path: inset(0 0 0 0);
  transition:
    clip-path var(--ab-saida) var(--ease-io),
    opacity var(--ab-saida) var(--ease-io);
}

/* a camada sai de cena de vez */
.abertura--fim { display: none; }

/* --- 38.1 A CENA — o espaço 3D compartilhado ------------------------
   Sem overflow, sem filter, sem opacity < 1: qualquer uma delas
   ACHATA o preserve-3d e a moldura perderia a projeção.
   --------------------------------------------------------------------- */
.abertura__cena {
  position: relative;
  /* 147vh = 86vh × (1200/700): a terceira trava da largura é o que
     mantém a proporção quando a janela é baixa. Um max-height aqui
     brigaria com o aspect-ratio e achataria a cena. */
  width: min(1120px, 88vw, 147vh);
  aspect-ratio: 1200 / 700;
  perspective: var(--ab-persp);
  perspective-origin: 50% 42%;
  transform-style: preserve-3d;
  transform: perspective(1400px) translate3d(0, 0, 0);
  transition: transform var(--ab-p5) var(--ease-io);
}

/* p5 — a entrega: a cena recua e abre espaço para o site */
.abertura.p5 .abertura__cena {
  transform: perspective(1400px) translate3d(0, -2%, -300px);
}

/* sombra de chão da moldura (aparece no assentamento) */
.abertura__cena::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: auto 22% 18% 22%;
  height: 9%;
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  background: radial-gradient(50% 50% at 50% 50%,
    rgba(4, 1, 14, .92),
    rgba(8, 11, 239, .26) 52%,
    transparent 80%);
  filter: blur(30px);
  transform: scale3d(.5, .4, 1);
  transition:
    opacity var(--ab-p4) var(--ease-io),
    transform var(--ab-p4) var(--ab-curva);
}

.abertura.p4 .abertura__cena::after {
  opacity: .9;
  transform: scale3d(1, 1, 1);
}

/* linha de horizonte: acende junto com o traçado */
.abertura__cena::before {
  content: "";
  position: absolute;
  z-index: 0;
  left: 4%;
  right: 4%;
  bottom: 16%;
  height: 1px;
  pointer-events: none;
  opacity: 0;
  background-image: linear-gradient(90deg,
    transparent 0%,
    rgba(129, 140, 248, .55) 24%,
    rgba(255, 255, 255, .8) 50%,
    rgba(129, 140, 248, .55) 76%,
    transparent 100%);
  transform: scaleX(.2);
  transition:
    opacity var(--ab-p1) var(--ease-io),
    transform var(--ab-p1) var(--ease-io);
}

.abertura.p1 .abertura__cena::before {
  opacity: .8;
  transform: scaleX(1);
}

/* --- 38.2 p1 — O TRAÇADO (desenho técnico) --------------------------
   Todas as formas do SVG nascem DESENHADAS (estado final fora de
   qualquer classe de passo) e é o .p1 que as faz aparecer traço a
   traço. --l é o comprimento do caminho: 900 casa com pathLength="900".
   --------------------------------------------------------------------- */
.abertura__guias {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
  opacity: 1;
  transition: opacity var(--ab-p5) var(--ease-io);
}

/* p5 — o desenho técnico se apaga antes da entrega */
.abertura.p5 .abertura__guias { opacity: 0; }

.abertura__guias :is(line, path, polyline, polygon, rect, circle, ellipse) {
  --l: 900;
  --guia-op: .5;
  fill: none;
  stroke: var(--accent);
  stroke-width: 1;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  opacity: var(--guia-op);
  stroke-dasharray: var(--l);
  stroke-dashoffset: 0;   /* estado FINAL: desenhado */
}

/* Comprimentos prontos, para quem não usar pathLength.
   Os modificadores carregam o .abertura__guias no seletor de propósito:
   a regra-base acima é (0,1,1) — uma classe solta perderia para ela e
   o --l nunca chegaria a valer. */
.abertura__guias .abertura__guia--curta { --l: 320; }
.abertura__guias .abertura__guia--longa { --l: 1800; }

/* eixos: mais discretos e contínuos */
.abertura__guias .abertura__guia--eixo { --guia-op: .28; stroke-width: 1; }

/* marcas de cota nas bordas: curtas, grossas, mais acesas */
.abertura__guias .abertura__guia--marca {
  --l: 60;
  --guia-op: .75;
  stroke-width: 2;
}

/* rótulos de medida */
.abertura__guias text {
  fill: var(--muted);
  font-family: inherit;
  font-size: 13px;
  letter-spacing: .16em;
  text-transform: uppercase;
  opacity: .55;   /* estado final */
}

@keyframes ab-tracar {
  from { stroke-dashoffset: var(--l); opacity: 0; }
  18%  { opacity: var(--guia-op); }
  to   { stroke-dashoffset: 0; opacity: var(--guia-op); }
}

@keyframes ab-surge {
  from { opacity: 0; }
  to   { opacity: var(--guia-op, .5); }
}

@keyframes ab-gira {
  to { transform: rotate(360deg); }
}

.abertura.p1 .abertura__guias :is(line, path, polyline, polygon, rect, ellipse):not(.abertura__guia--circulo),
.abertura.p1 .abertura__guias circle:not(.abertura__guia--circulo) {
  animation: ab-tracar var(--ab-p1) var(--ease-io) both;
}

.abertura.p1 .abertura__guias text {
  animation: ab-surge 420ms var(--ease) 260ms both;
}

/* escalonamento do traçado: 45ms por forma, teto no décimo traço */
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(1)  { animation-delay: 0ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(2)  { animation-delay: 45ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(3)  { animation-delay: 90ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(4)  { animation-delay: 135ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(5)  { animation-delay: 180ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(6)  { animation-delay: 225ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(7)  { animation-delay: 270ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(8)  { animation-delay: 315ms; }
.abertura.p1 .abertura__guias :is(line, path, circle, rect, polyline):nth-child(n+9) { animation-delay: 360ms; }

/* círculos tracejados: não se desenham — aparecem e giram devagar */
.abertura__guias .abertura__guia--circulo {
  --guia-op: .34;
  stroke-dasharray: 5 9;
  stroke-dashoffset: 0;
  transform-box: fill-box;
  transform-origin: 50% 50%;
  animation: ab-gira 16s linear infinite;
}

.abertura__guias .abertura__guia--circulo.abertura__guia--curta {
  animation-duration: 9s;
  animation-direction: reverse;
}

.abertura.p1 .abertura__guias .abertura__guia--circulo {
  animation:
    ab-gira 16s linear infinite,
    ab-surge 520ms var(--ease) 120ms both;
}

/* --- 38.3 p2 — A MOLDURA ENTRA -------------------------------------- */
.abertura__tela {
  position: absolute;
  z-index: 2;
  inset: 12% 20% 24% 20%;
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  border: 1px solid var(--stroke);
  background-color: var(--bg-1);
  background-image:
    radial-gradient(560px 320px at 10% -10%, rgba(8, 11, 239, .28), transparent 62%),
    radial-gradient(460px 280px at 96% 0%, rgba(84, 4, 148, .24), transparent 60%);
  transform-style: preserve-3d;
  transform-origin: 50% 88%;
  opacity: 0;
  transform: rotateX(70deg) translateZ(-600px);
  transition:
    transform var(--ab-p2) var(--ab-curva),
    opacity 320ms var(--ease);
}

/* posição inclinada: ainda em perspectiva, já legível */
.abertura.p2 .abertura__tela {
  opacity: 1;
  transform: rotateX(26deg) translateZ(-140px);
}

/* borda acesa (só opacity anima; o box-shadow é estático) */
.abertura__tela::before {
  content: "";
  position: absolute;
  inset: -1px;
  z-index: 3;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  box-shadow:
    inset 0 0 0 1px rgba(129, 140, 248, .75),
    0 0 30px rgba(8, 11, 239, .55),
    0 0 90px rgba(84, 4, 148, .35);
  transition: opacity var(--ab-p2) var(--ease-io);
}

.abertura.p2 .abertura__tela::before { opacity: 1; }

/* a barra de título da moldura */
.abertura__barra {
  display: flex;
  align-items: center;
  gap: 7px;
  flex: 0 0 auto;
  padding: 10px 14px;
  border-bottom: 1px solid rgba(197, 176, 228, .14);
  background-color: rgba(255, 255, 255, .05);
  border-radius: 13px 13px 0 0;
}

.abertura__barra span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: rgba(197, 176, 228, .3);
  opacity: 0;
  transform: scale(.4);
  transition:
    opacity 300ms var(--ease),
    transform 420ms var(--ab-curva);
}

.abertura__barra span:nth-child(1) { background-color: rgba(239, 68, 68, .6); }
.abertura__barra span:nth-child(2) { background-color: rgba(251, 191, 36, .6); }
.abertura__barra span:nth-child(3) { background-color: rgba(34, 197, 94, .55); }

.abertura.p2 .abertura__barra span {
  opacity: 1;
  transform: scale(1);
}

.abertura.p2 .abertura__barra span:nth-child(1) { transition-delay: 180ms; }
.abertura.p2 .abertura__barra span:nth-child(2) { transition-delay: 240ms; }
.abertura.p2 .abertura__barra span:nth-child(3) { transition-delay: 300ms; }

/* --- 38.4 p3 — AS PEÇAS VOAM PARA O LUGAR --------------------------- */
.abertura__corpo {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 8px;
  padding: 12px;
  transform-style: preserve-3d;
}

.abertura__bloco {
  border-radius: 8px;
  border: 1px solid rgba(197, 176, 228, .16);
  background-image: linear-gradient(150deg,
    rgba(255, 255, 255, .09),
    rgba(8, 11, 239, .16) 55%,
    rgba(84, 4, 148, .12) 100%);
  opacity: 0;
  transition:
    transform var(--ab-p3) var(--ab-curva),
    opacity 300ms var(--ease);
}

/* onde cada peça mora quando a interface está montada */
.abertura__bloco:nth-child(1) { grid-area: 1 / 1 / 4 / 3; }
.abertura__bloco:nth-child(2) { grid-area: 1 / 3 / 2 / 7; }
.abertura__bloco:nth-child(3) { grid-area: 2 / 3 / 4 / 5; }
.abertura__bloco:nth-child(4) { grid-area: 2 / 5 / 3 / 7; }
.abertura__bloco:nth-child(5) { grid-area: 3 / 5 / 4 / 7; }

/* De onde cada peça vem — cinco direções diferentes. A lista de
   funções é a MESMA nas cinco (e no estado montado): é o que mantém a
   interpolação função a função, em vez de cair em matriz. */
.abertura__bloco:nth-child(1) { transform: translate3d(-190%, 6%, -260px) rotateX(0deg) rotateY(34deg) rotateZ(0deg); }
.abertura__bloco:nth-child(2) { transform: translate3d(0, -260%, -300px) rotateX(-38deg) rotateY(0deg) rotateZ(0deg); }
.abertura__bloco:nth-child(3) { transform: translate3d(-70%, 210%, -340px) rotateX(0deg) rotateY(0deg) rotateZ(-10deg); }
.abertura__bloco:nth-child(4) { transform: translate3d(210%, -50%, -240px) rotateX(0deg) rotateY(-30deg) rotateZ(0deg); }
.abertura__bloco:nth-child(5) { transform: translate3d(170%, 190%, -380px) rotateX(30deg) rotateY(0deg) rotateZ(0deg); }

.abertura.p3 .abertura__bloco {
  opacity: 1;
  transform: translate3d(0, 0, 0) rotateX(0deg) rotateY(0deg) rotateZ(0deg);
}

.abertura.p3 .abertura__bloco:nth-child(1) { transition-delay: 0ms; }
.abertura.p3 .abertura__bloco:nth-child(2) { transition-delay: 60ms; }
.abertura.p3 .abertura__bloco:nth-child(3) { transition-delay: 120ms; }
.abertura.p3 .abertura__bloco:nth-child(4) { transition-delay: 180ms; }
.abertura.p3 .abertura__bloco:nth-child(5) { transition-delay: 240ms; }

/* --- 38.5 p4 — O ASSENTAMENTO --------------------------------------- */
.abertura.p4 .abertura__tela {
  transform: rotateX(0deg) translateZ(0);
  transition-duration: var(--ab-p4);
}

/* a varredura de luz atravessa a tela recém-assentada.
   O que anda é background-position: fica DENTRO da caixa, sem vazar. */
.abertura__tela::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  background-image: linear-gradient(108deg,
    transparent 0%,
    rgba(129, 140, 248, .28) 34%,
    rgba(255, 255, 255, .42) 50%,
    rgba(8, 11, 239, .22) 66%,
    transparent 100%);
  background-repeat: no-repeat;
  background-size: 60% 100%;
  /* Com uma faixa de 60% da largura, a conta do background-position em
     porcentagem é offset = (100% - 60%) × pct: só em -160% a faixa fica
     inteira fora pela esquerda, e só em 260% ela sai pela direita. */
  background-position: -160% 0;
}

@keyframes ab-varredura {
  0%   { opacity: 0;   background-position: -160% 0; }
  15%  { opacity: 1; }
  85%  { opacity: 1; }
  100% { opacity: 0;   background-position: 260% 0; }
}

.abertura.p4 .abertura__tela::after {
  animation: ab-varredura 760ms var(--ease-io) 140ms 1 both;
}

/* a marca e a linha entram por último */
.abertura__marca,
.abertura__linha {
  position: absolute;
  left: 0;
  right: 0;
  z-index: 2;
  margin: 0;
  text-align: center;
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  transition:
    opacity 460ms var(--ease),
    transform 560ms var(--ab-curva);
}

.abertura__marca {
  bottom: 10%;
  font-size: clamp(1.5rem, 3.4vw, 2.4rem);
  font-weight: 800;
  letter-spacing: -.02em;
  color: var(--text);
}

.abertura__linha {
  bottom: 2%;
  font-size: clamp(.82rem, 1.5vw, 1rem);
  letter-spacing: .04em;
  color: var(--muted);
}

.abertura.p4 .abertura__marca,
.abertura.p4 .abertura__linha {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}

.abertura.p4 .abertura__marca { transition-delay: 180ms; }
.abertura.p4 .abertura__linha { transition-delay: 300ms; }

/* p5 — a marca desbota junto com a cena */
.abertura.p5 .abertura__marca,
.abertura.p5 .abertura__linha {
  opacity: 0;
  transform: translate3d(0, -10px, 0);
  transition-delay: 0ms;
}

/* --- 38.6 .saindo — a cortina sobe ---------------------------------- */
.abertura.saindo {
  opacity: 0;
  clip-path: inset(0 0 100% 0);
}

/* --- 38.7 O BOTÃO PULAR — ninguém fica preso ------------------------
   Sempre visível, sempre focável, em todos os passos.
   --------------------------------------------------------------------- */
.abertura__pular {
  position: absolute;
  z-index: 5;
  right: clamp(14px, 3vw, 32px);
  bottom: clamp(14px, 3vw, 32px);
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 18px;
  border-radius: 999px;
  border: 1px solid rgba(197, 176, 228, .3);
  background-color: rgba(255, 255, 255, .06);
  color: var(--text-soft);
  font: inherit;
  font-size: .82rem;
  font-weight: 600;
  letter-spacing: .02em;
  cursor: pointer;
  opacity: 1;
  transform: translate3d(0, 0, 0);
  transition:
    transform var(--t-fast) var(--ease),
    border-color var(--t-fast) var(--ease),
    background-color var(--t-fast) var(--ease),
    color var(--t-fast) var(--ease);
}

.abertura__pular:hover {
  color: var(--text);
  border-color: rgba(129, 140, 248, .6);
  background-color: rgba(129, 140, 248, .16);
  transform: translate3d(0, -2px, 0);
}

.abertura__pular:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  color: var(--text);
  border-color: rgba(129, 140, 248, .75);
}

.abertura__pular:active { transform: translate3d(0, 0, 0); }


/* =====================================================================
   39. O SITE SE CONSTRUINDO — body.construindo / body.construido
   Enquanto a abertura roda, o cromo (barra utilitária + navbar) espera
   fora da tela e o hero segura a cascata. Quando a cortina sobe, o
   cromo desce e o hero entra logo atrás — o site se MONTA, não aparece
   pronto.

   POR QUE AS DUAS CLASSES ESTÃO EM TODOS OS SELETORES
   o JS troca .construindo por .construido no fim. Se a regra valesse
   só para .construindo, no instante da troca a animação seria
   CANCELADA e o cromo saltaria para o lugar sem se mover. Com as duas
   no mesmo seletor, a animação computada não muda: ela simplesmente
   continua. E como o atraso é FINITO, mesmo que o JS morra no meio o
   cromo e o hero chegam sozinhos — nada fica invisível para sempre.
   ===================================================================== */
:root {
  --ab-cromo: 2560ms;  /* o cromo começa a descer 200ms antes da cortina */
  /* O hero entra ENQUANTO o véu levanta, não depois dele. Em 2900ms havia
     um silêncio: véu já transparente, peças ainda no ar e o resto da dobra
     vazio. */
  --ab-hero: 2420ms;
}

@keyframes ab-cromo-desce {
  from { opacity: 0; transform: translate3d(0, -100%, 0); }
  to   { opacity: 1; transform: translate3d(0, 0, 0); }
}

body.construindo .utility-bar,
body.construido .utility-bar {
  animation: ab-cromo-desce 620ms var(--ease) var(--ab-cromo) both;
}

body.construindo .navbar,
body.construido .navbar {
  animation: ab-cromo-desce 680ms var(--ease) calc(var(--ab-cromo) + 90ms) both;
}

/* O HERO: mesma animação hero-in de sempre (seção 3), só que o relógio
   começa depois da cortina. Nenhum keyframe novo, nenhum estado novo. */
body.construindo .hero [data-hero-in],
body.construido .hero [data-hero-in] { animation-delay: var(--ab-hero); }

body.construindo .hero [data-hero-in="1"],
body.construido .hero [data-hero-in="1"] { animation-delay: calc(var(--ab-hero) + 100ms); }
body.construindo .hero [data-hero-in="2"],
body.construido .hero [data-hero-in="2"] { animation-delay: calc(var(--ab-hero) + 200ms); }
body.construindo .hero [data-hero-in="3"],
body.construido .hero [data-hero-in="3"] { animation-delay: calc(var(--ab-hero) + 300ms); }
body.construindo .hero [data-hero-in="4"],
body.construido .hero [data-hero-in="4"] { animation-delay: calc(var(--ab-hero) + 400ms); }
body.construindo .hero [data-hero-in="5"],
body.construido .hero [data-hero-in="5"] { animation-delay: calc(var(--ab-hero) + 500ms); }
body.construindo .hero [data-hero-in="6"],
body.construido .hero [data-hero-in="6"] { animation-delay: calc(var(--ab-hero) + 600ms); }
body.construindo .hero [data-hero-in="media"],
body.construido .hero [data-hero-in="media"] { animation-delay: calc(var(--ab-hero) + 400ms); }

/* A trava de rolagem existe SÓ enquanto a camada existe de fato: se o
   JS remover a .abertura e esquecer a classe, a página volta a rolar
   sozinha. Onde :has() não existe, não há trava — e nada quebra. */
@supports selector(:has(*)) {
  body.construindo:has(> .abertura:not(.abertura--fim)) {
    overflow: hidden;
  }
}


/* =====================================================================
   31. RESPONSIVO — a intensidade do 3D é proporcional à tela
   Em 1440px o exagero impressiona; em 380px ele atrapalha a leitura.
   Aqui a mesma coreografia perde volume, e o que depende de ponteiro
   fino simplesmente sai de cena.
   ===================================================================== */
@media (max-width: 900px) {
  /* desdobramento: menos tombo, menos Z, perspectiva mais longa */
  [data-reveal="unfold"] {
    --unfold-persp: 1400px;
    --unfold-rx: 24deg;
    --unfold-rz: .4deg;
    --unfold-z: -110px;
    --unfold-y: 48px;
    --unfold-scale: .97;
    --unfold-dur: 1.05s;
    --unfold-pop: 12px;
  }

  [data-reveal="unfold"]::before { filter: blur(16px); }

  /* cascata mais curta: em tela estreita a espera cansa */
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(1)  { transition-delay: 300ms; }
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(2)  { transition-delay: 345ms; }
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(3)  { transition-delay: 390ms; }
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(4)  { transition-delay: 435ms; }
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(5)  { transition-delay: 480ms; }
  [data-reveal="unfold"].is-visible .app-shot__body > *:nth-child(n+6) { transition-delay: 520ms; }

  /* fundos: malha mais fina, tudo mais discreto */
  .ai-bg {
    --ai-cell: 34px;
    --ai-reach: 220px;
    --ai-dim: .6;
  }

  .ai-bg--particulas::after { opacity: .25; }

  /* pilha 3D: menos afastamento */
  .depth-stack > *:nth-child(2) { transform: translate3d(0, 10px, -40px); }
  .depth-stack > *:nth-child(3) { transform: translate3d(0, 18px, -80px); }

  /* --- camada nova (21-30): mesma coreografia, menos volume --- */
  :root {
    --sc-desloc: 24px;
    --sc-blur: 4px;
    --sc-escala-de: .965;
  }

  .app-figure--parallax { --ap-moldura: 9px; --ap-sombra: 22px; }
  .app-figure--parallax::after { filter: blur(20px); }

  [data-stagger] { --stagger-passo: 55ms; --stagger-y: 12px; }

  .timeline--anima { --tl-passo: 60ms; --tl-base: 90ms; }

  /* o style.css encolhe o recuo do trilho em 900px: o anel acompanha */
  .timeline--anima .timeline-item::after { left: -26px; }

  .camadas { --cam-z-frente: 26px; --cam-z-fundo: -30px; }

  .magnetico,
  [data-magnetico] { --mag-max: 4px; --mag-forca: .12; }

  .alvo-cursor { --alvo-tam: 30px; }

  .stat--destaque::after { filter: blur(12px); }

  [data-reveal="unfold"] { --unfold-overshoot: 1.03; }

  /* --- camada de cursor e 3D novo (33-36): menos volume --- */
  .rastro { --rastro-escala: .72; }

  .fundo-reativo,
  .fundo-reativo--suave,
  .fundo-reativo--forte { --fr-raio: 420px; --fr-celula: 34px; }

  .cena-3d,
  .cena-3d--longa,
  .cena-3d--sutil { --cena-passo: 15px; --cena-persp: 1500px; }

  .carta-3d,
  .carta-3d--sutil,
  .carta-3d--intenso {
    --c3-max: 6deg;
    --c3-alcance: 640px;
    --c3-desloc: 10px;
    --c3-persp: 1100px;
  }

  .carta-3d::before { filter: blur(12px); }

  .texto-revela,
  .texto-revela--lento { --tr-passo: 45ms; }

  .brilho-borda,
  .brilho-borda--lento { --bb-dur: 1.2s; }
}

@media (max-width: 600px) {
  /* o desdobramento vira um assentamento leve */
  [data-reveal="unfold"] {
    --unfold-persp: 1600px;
    --unfold-rx: 14deg;
    --unfold-rz: 0deg;
    --unfold-z: -50px;
    --unfold-y: 32px;
    --unfold-scale: .985;
    --unfold-dur: .9s;
    --unfold-pop: 0px;
  }

  /* sem ponteiro fino não há tilt nem spotlight: só peso de render */
  .tilt {
    transform: none;
    transition: none;
  }

  .tilt::after { display: none; }

  .tilt--profundo .tilt__camada { transform: none; }

  [data-spotlight]::before { display: none; }

  .ai-bg--mesh::after { display: none; }

  /* a camada da frente das partículas some: em tela pequena vira sujeira */
  .ai-bg--particulas::after { display: none; }

  /* a pilha 3D desempilha e vira lista: legibilidade acima do efeito */
  .depth-stack {
    grid-auto-flow: row;
    gap: 16px;
    perspective: none;
  }

  .depth-stack > * {
    grid-area: auto;
    opacity: 1;
    transform: none;
  }

  /* --- camada nova (21-30): parallax, magnético e cursor saem de cena.
     Em tela pequena o dedo cobre o ponteiro e o scroll já é o efeito. --- */
  .sc-parallax {
    animation: none;
    transform: none;
    will-change: auto;
  }

  .app-figure--parallax .app-shot {
    animation: none;
    translate: none;
    will-change: auto;
  }

  .app-figure--parallax::after {
    animation: none;
    transform: none;
    opacity: .4;
    will-change: auto;
  }

  /* escala e foco continuam, mas quase imperceptíveis */
  :root { --sc-escala-de: .985; --sc-blur: 3px; }

  .magnetico__rotulo { transform: none; }

  .alvo-cursor::after { display: none; }

  /* as camadas desempilham: sem ponteiro fino, o Z não tem gatilho */
  .camadas { perspective: none; }

  .camadas__frente,
  .camadas__fundo { transform: none; }

  .camadas__fundo::after { opacity: .3; transform: scale3d(.9, .85, 1); }

  [data-reveal="unfold"] { --unfold-overshoot: 1.02; }

  /* --- camada de cursor e 3D novo (33-36): o espaço 3D achata --- */
  .cena-3d {
    perspective: none;
    transform-style: flat;
  }

  .profundidade-1,
  .profundidade-2,
  .profundidade-3,
  .profundidade-4 { transform: none; }

  .carta-3d__camada { transform: none; }

  .fundo-reativo--grade::after,
  .fundo-reativo--nos::after { display: none; }
}


/* =====================================================================
   31-BIS. SEM PONTEIRO FINO — o que depende de mouse deixa de EXISTIR
   Em celular e tablet não há ponteiro para rastrear: rastro, fundo
   reativo e cartas 3D viram peso morto (camada extra para compor,
   variável que ninguém escreve, sombra que nunca se desloca). Aqui eles
   saem de cena — o CONTEÚDO, nunca.
   ===================================================================== */
@media (hover: none) {
  /* o rastro não é criado para ninguém ver: se o JS criar, some */
  .rastro { display: none; }

  /* a camada reativa inteira sai: é decoração de ponteiro */
  .fundo-reativo { display: none; }

  /* o cartão CONTINUA lá; o que sai é o 3D e seus pseudo-elementos */
  .carta-3d {
    transform: none;
    transition: none;
    transform-style: flat;
  }

  .carta-3d::before,
  .carta-3d::after { display: none; }

  .carta-3d__camada {
    transform: none;
    transition: none;
  }
}


/* =====================================================================
   37-BIS. O EXAGERO É PROPORCIONAL À TELA
   Os blocos 31 e 31-BIS já baixam rx, z, y, escala, duração e pop em
   <=900px e <=600px — aqueles números continuam mandando e NÃO são
   tocados aqui. Este bloco só calibra o que a seção 37 trouxe de novo
   (o eixo Y, a cascata com giro próprio, a sombra que abre e a
   varredura final), para que em tela pequena a coreografia volte a ser
   próxima da anterior: um assentamento, não um tombo.
   ===================================================================== */
@media (max-width: 900px) {
  [data-reveal="unfold"] {
    --unfold-ry: .8deg;
    --unfold-filho-y: 12px;
    --unfold-filho-rx: 3deg;
    --unfold-filho-base: 300ms;
    --unfold-filho-passo: 45ms;
    --unfold-varredura-atraso: .7s;
    --unfold-varredura-dur: .8s;
    perspective-origin: 50% 40%;
  }

  /* o pop menor não precisa de tanto recuo de caixa */
  [data-reveal="unfold"] .app-shot { width: 96%; }

  /* a sombra volta a ser discreta */
  [data-reveal="unfold"]::before {
    inset: 10% 8% -6% 8%;
    filter: blur(12px);
    transform: translate3d(0, -30px, 0) scale3d(.5, .28, 1);
  }

  [data-reveal="unfold"].is-visible::before {
    opacity: .85;
    filter: blur(24px);
    transform: translate3d(0, 12px, 0) scale3d(1, 1.06, 1);
  }

  [data-reveal="unfold"].is-visible:hover::before {
    transform: translate3d(0, 18px, 0) scale3d(1.04, 1.12, 1);
  }

  [data-reveal="unfold"] .app-shot__bar {
    transform: perspective(620px) translate3d(0, -8px, 0) rotateX(-5deg);
  }
}

@media (max-width: 600px) {
  /* sem eixo Y e sem giro nos blocos: em 380px isso vira tremor */
  [data-reveal="unfold"] {
    --unfold-ry: 0deg;
    --unfold-filho-y: 8px;
    --unfold-filho-rx: 0deg;
    --unfold-varredura-atraso: .55s;
  }

  [data-reveal="unfold"] .app-shot__bar {
    transform: perspective(620px) translate3d(0, -6px, 0) rotateX(0deg);
  }
}


/* =====================================================================
   38-BIS. ONDE A ABERTURA NÃO EXISTE
   O JS não deve criar a camada em tela pequena nem para quem pediu
   menos movimento. Aqui está a garantia do outro lado: se ela existir
   mesmo assim, some — e o cromo e o hero deixam de esperar por uma
   cortina que não vai subir.
   ===================================================================== */
@media (max-width: 700px) {
  .abertura { display: none; }

  :root {
    --ab-cromo: 0ms;
    --ab-hero: 0ms;
  }

  body.construindo .utility-bar,
  body.construido .utility-bar,
  body.construindo .navbar,
  body.construido .navbar { animation: none; }
}


/* =====================================================================
   32. PREFERS-REDUCED-MOTION
   Único lugar do projeto onde !important é permitido.
   Regra de ouro: o reveal precisa terminar VISÍVEL. Matar a transição
   sem forçar opacity:1 faria o conteúdo sumir — é o bug clássico.
   Aqui entram TAMBÉM o desdobramento novo (com a cascata interna), os
   fundos de IA, o tilt e todos os efeitos premium.
   ===================================================================== */
/* --- Camada WebGL da abertura ------------------------------------------
   Quando o WebGL responde, o canvas assume e a cena em CSS sai de cena —
   menos a marca e a linha de texto, que são as mesmas nos dois caminhos.
   Se o contexto falhar, o JS remove a classe e a cena em CSS volta a valer
   sem que o visitante perceba a troca. */
.abertura__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.abertura--gl .abertura__guias,
.abertura--gl .abertura__tela {
  display: none;
}

.abertura--gl .abertura__cena {
  position: relative;
  z-index: 1;
  pointer-events: none;
  background: none;
}

.abertura--gl .abertura__marca,
.abertura--gl .abertura__linha {
  position: relative;
  z-index: 2;
}

/* =====================================================================
   ABERTURA COM CONTINUIDADE (.ab2)
   A janela do Omega é DOM porque ela não desaparece: cada peça viaja até
   o elemento real do site. O que o CSS entrega aqui é o palco 3D, o
   desenho da janela e o estado de voo — o trajeto é medido em JS (FLIP).
   ===================================================================== */

.abertura.ab2 {
  position: fixed;
  inset: 0;
  z-index: 9000;
  display: grid;
  place-items: center;
  background-color: var(--bg-0);
  transition: opacity .5s var(--ease), background-color .5s var(--ease);
}

.abertura.ab2 .abertura__canvas {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

.abertura.ab2.ab-saindo {
  opacity: 0;
  background-color: transparent;
  pointer-events: none;
}

/* O VÉU LEVANTA ENQUANTO AS PEÇAS VOAM.
   Antes, a cortina só saía no fim de todo o voo: as peças pousavam contra
   um fundo opaco, apagavam — e o site inteiro surgia 1,4s depois. Era o
   "menu que aparece do nada", e a espera parecia travamento. Agora o fundo
   se dissolve durante o voo, então cada peça pousa sobre a barra real, já
   visível. A revelação do site é um movimento só: o véu subindo. */
/* Elemento que RECEBEU uma peça da abertura. Ele já entrou em cena — voou
   até aqui, na frente do visitante. A animação de entrada do hero, que
   dispara sozinha no load, não pode fazê-lo entrar uma segunda vez. */
.ab-morfado {
  animation: none !important;
  opacity: 1 !important;
}

.abertura.ab2.ab-morfando {
  background-color: transparent;
  pointer-events: none;
  transition: background-color .6s cubic-bezier(.4, 0, .3, 1);
}

.abertura.ab2.ab-morfando .abertura__canvas {
  opacity: 0;
  transition: opacity .65s var(--ease);
}

.abertura.ab2.ab-morfando .abertura__pular {
  opacity: 0;
  pointer-events: none;
  transition: opacity .25s linear;
}

/* --- palco: é ele que dá a perspectiva --- */
.ab-palco {
  position: relative;
  z-index: 1;
  width: min(980px, 78vw);
  perspective: 1500px;
  perspective-origin: 50% 42%;
  pointer-events: none;
}

/* --- a janela do Omega --- */
.ab-omega {
  position: relative;
  overflow: hidden;
  border-radius: 16px;
  border: 1px solid rgba(129, 140, 248, .30);
  background-color: #140a24;
  box-shadow: 0 50px 90px rgba(0, 0, 0, .6), inset 0 1px 0 rgba(255, 255, 255, .05);
  transform-style: preserve-3d;
  transform: perspective(1500px) rotateX(52deg) rotateZ(-1.5deg) translate3d(0, 90px, -280px) scale(.9);
  opacity: 0;
  transition:
    transform 1.05s cubic-bezier(.16, .86, .3, 1.02),
    opacity .5s var(--ease),
    box-shadow 1.05s var(--ease);
}

.ab-monta .ab-omega {
  opacity: 1;
  transform: perspective(1500px) rotateX(26deg) rotateZ(-.8deg) translate3d(0, 30px, -120px) scale(.96);
}

.ab-frontal .ab-omega {
  transform: perspective(1500px) rotateX(0deg) rotateZ(0deg) translate3d(0, 0, 0) scale(1);
  box-shadow: 0 70px 120px rgba(0, 0, 0, .68), 0 0 0 1px rgba(129, 140, 248, .22);
}

/* na hora do morph a moldura sai de cena e deixa as peças passarem.
   Ela abre (scale > 1) enquanto esvanece: a leitura é de uma janela que se
   dissolve para os lados, não de um retângulo que apaga. */
.ab-morfando .ab-omega {
  transform: perspective(1500px) scale(1.06);
  opacity: 0;
  transition:
    transform .78s cubic-bezier(.22, .78, .26, 1),
    opacity .5s var(--ease) .08s;
}

/* O conteúdo que NÃO voa também precisa sair com intenção — senão nove
   pedaços escapam e o resto do quadro simplesmente pisca. Os números e as
   linhas do painel afundam; os módulos laterais, não: eles se chamam
   Squads, Agentes, Canais, CRM, Relatórios, que é o próprio conteúdo do
   menu do site. Esses sobem na direção da barra, junto com as peças. Tudo
   converge para o mesmo lugar. */
.ab-morfando :is(.ab-omega__cartoes, .ab-omega__linhas, .ab-omega__marca-txt) {
  animation: ab-dissolve .5s cubic-bezier(.4, 0, 1, 1) both;
}
.ab-morfando .ab-omega__linhas { animation-delay: .06s; }

@keyframes ab-dissolve {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translateY(22px) scale(.97); }
}

.ab-morfando .ab-omega__lado-item {
  animation: ab-converge .52s cubic-bezier(.3, 0, .6, 1) both;
}
.ab-morfando .ab-omega__lado-item:nth-child(1) { animation-delay: 0ms; }
.ab-morfando .ab-omega__lado-item:nth-child(2) { animation-delay: 40ms; }
.ab-morfando .ab-omega__lado-item:nth-child(3) { animation-delay: 80ms; }
.ab-morfando .ab-omega__lado-item:nth-child(4) { animation-delay: 120ms; }
.ab-morfando .ab-omega__lado-item:nth-child(5) { animation-delay: 160ms; }

@keyframes ab-converge {
  from { opacity: 1; transform: none; }
  to   { opacity: 0; transform: translate3d(64px, -120px, 0) scale(.9); }
}

/* a moldura precisa de camada própria: sem isto o navegador repinta a
   janela inteira a cada quadro do voo */
.ab-omega { will-change: transform, opacity; }
.ab-morfando .ab-omega,
.abertura.ab2.ab-saindo { will-change: auto; }

/* --- barra de título --- */
.ab-omega__barra {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 16px;
  border-bottom: 1px solid rgba(197, 176, 228, .12);
  background-color: rgba(255, 255, 255, .03);
}

.ab-omega__pontos { display: inline-flex; gap: 6px; }

.ab-omega__pontos span {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background-color: rgba(197, 176, 228, .30);
}

.ab-omega__marca { display: inline-flex; align-items: center; gap: 9px; }

.ab-omega__logo {
  width: 26px;
  height: 26px;
  border-radius: 7px;
  display: block;
}

.ab-omega__marca-txt {
  color: var(--text-soft);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .01em;
}

/* --- menu do produto: vira o menu do site --- */
.ab-omega__nav {
  display: flex;
  gap: 6px;
  padding: 10px 16px;
  border-bottom: 1px solid rgba(197, 176, 228, .10);
}

.ab-omega__nav-item {
  padding: 7px 13px;
  border-radius: 9px;
  color: var(--muted);
  font-size: .84rem;
  font-weight: 600;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}

.ab-omega__nav-item.e-ativo {
  background-color: rgba(8, 11, 239, .30);
  color: var(--text);
}

.ab-monta .ab-omega__nav-item { opacity: 1; transform: none; }
.ab-monta .ab-omega__nav-item:nth-child(1) { transition-delay: .18s; }
.ab-monta .ab-omega__nav-item:nth-child(2) { transition-delay: .24s; }
.ab-monta .ab-omega__nav-item:nth-child(3) { transition-delay: .30s; }
.ab-monta .ab-omega__nav-item:nth-child(4) { transition-delay: .36s; }
.ab-monta .ab-omega__nav-item:nth-child(5) { transition-delay: .42s; }

/* --- corpo: coluna de módulos + painel --- */
.ab-omega__corpo {
  display: grid;
  grid-template-columns: 168px minmax(0, 1fr);
  gap: 0;
  min-height: 300px;
}

.ab-omega__lado {
  display: flex;
  flex-direction: column;
  gap: 3px;
  padding: 14px 10px;
  border-right: 1px solid rgba(197, 176, 228, .10);
  background-color: rgba(0, 0, 0, .18);
}

.ab-omega__lado-item {
  padding: 8px 11px;
  border-radius: 8px;
  color: var(--muted);
  font-size: .8rem;
  font-weight: 600;
  opacity: 0;
  transform: translateX(-10px);
  transition: opacity .38s var(--ease), transform .38s var(--ease);
}

.ab-omega__lado-item.e-ativo {
  background-color: rgba(129, 140, 248, .14);
  color: var(--text);
  box-shadow: inset 2px 0 0 var(--accent);
}

.ab-monta .ab-omega__lado-item { opacity: 1; transform: none; }
.ab-monta .ab-omega__lado-item:nth-child(1) { transition-delay: .30s; }
.ab-monta .ab-omega__lado-item:nth-child(2) { transition-delay: .36s; }
.ab-monta .ab-omega__lado-item:nth-child(3) { transition-delay: .42s; }
.ab-monta .ab-omega__lado-item:nth-child(4) { transition-delay: .48s; }
.ab-monta .ab-omega__lado-item:nth-child(5) { transition-delay: .54s; }

.ab-omega__painel {
  display: flex;
  flex-direction: column;
  gap: 16px;
  padding: 22px 24px 26px;
}

/* Este título é o que vira a manchete da página, então ele precisa já ter
   a FORMA dela: duas linhas, peso alto. Quando era uma linha fina, o voo
   tinha de esticar 4,6x na vertical e o texto chegava como um borrão. */
.ab-omega__titulo {
  max-width: 66%;
  color: var(--text);
  font-size: 1.95rem;
  font-weight: 800;
  line-height: 1.22;
  letter-spacing: -.02em;
  text-wrap: balance;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}

.ab-monta .ab-omega__titulo { opacity: 1; transform: none; transition-delay: .42s; }

.ab-omega__cartoes { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }

.ab-omega__cartao {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 11px 13px;
  border-radius: 10px;
  border: 1px solid rgba(197, 176, 228, .12);
  background-color: rgba(255, 255, 255, .035);
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .42s var(--ease), transform .42s var(--ease);
}

.ab-monta .ab-omega__cartao { opacity: 1; transform: none; }
.ab-monta .ab-omega__cartao:nth-child(1) { transition-delay: .50s; }
.ab-monta .ab-omega__cartao:nth-child(2) { transition-delay: .56s; }
.ab-monta .ab-omega__cartao:nth-child(3) { transition-delay: .62s; }

.ab-omega__cartao-tit {
  color: var(--muted);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}

.ab-omega__cartao-num {
  color: var(--text);
  font-size: 1.35rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
}

.ab-omega__linhas { display: flex; flex-direction: column; gap: 8px; }

.ab-omega__linha {
  height: 9px;
  border-radius: 5px;
  background-color: rgba(197, 176, 228, .12);
  transform-origin: 0 50%;
  transform: scaleX(0);
  transition: transform .5s var(--ease);
}

.ab-monta .ab-omega__linha { transform: scaleX(1); }
.ab-monta .ab-omega__linha:nth-child(1) { transition-delay: .60s; }
.ab-monta .ab-omega__linha:nth-child(2) { transition-delay: .66s; width: 82%; }
.ab-monta .ab-omega__linha:nth-child(3) { transition-delay: .72s; width: 58%; }

/* --- as ações: viram os botões do site --- */
.ab-omega__acoes { display: flex; gap: 10px; margin-top: 2px; }

.ab-omega__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 12px 20px;
  border-radius: 999px;
  font-size: .9rem;
  font-weight: 700;
  white-space: nowrap;
  opacity: 0;
  transform: translateY(16px);
  transition: opacity .45s var(--ease), transform .45s var(--ease);
}

.ab-omega__btn--verde { background-color: var(--success); color: #04220f; }
.ab-omega__btn--marca { background-image: var(--gradient); color: #fff; }

.ab-monta .ab-omega__btn { opacity: 1; transform: none; }
.ab-monta .ab-omega__btn:nth-child(1) { transition-delay: .70s; }
.ab-monta .ab-omega__btn:nth-child(2) { transition-delay: .78s; }

/* --- varredura de luz sobre a tela montada --- */
.ab-omega__varre {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(115deg,
    transparent 38%,
    rgba(129, 140, 248, .16) 48%,
    rgba(255, 255, 255, .10) 52%,
    transparent 64%);
  transform: translateX(-120%);
  opacity: 0;
}

.ab-varre .ab-omega__varre { animation: ab-varredura 1s var(--ease) 1; }

@keyframes ab-varredura {
  0%   { transform: translateX(-120%); opacity: 0; }
  22%  { opacity: 1; }
  100% { transform: translateX(120%); opacity: 0; }
}

/* --- a peça em voo: sai do 3D e vai para a tela ---
   Todas as peças voam dentro deste palco. Ele existe por um motivo de
   desempenho: um só elemento promovido a camada, em vez de nove soltos
   no corpo do documento. */
.ab-voo {
  position: fixed;
  inset: 0;
  z-index: 9100;
  pointer-events: none;
  contain: layout style;
}

.ab-voando {
  position: fixed !important;
  margin: 0 !important;
  pointer-events: none;
  transform-origin: 50% 50%;
  /* A opacidade tem tempo PRÓPRIO e curto: a peça atravessa a tela visível
     e só apaga nos últimos 200ms, já pousada sobre o elemento real. Antes
     ela desaparecia junto com o voo — e o dono via só o logotipo saltar,
     porque era a única peça grande o bastante para ainda se enxergar. */
  /* Desaceleração pura: a peça parte no primeiro quadro e assenta no
     destino. A curva anterior tinha partida lenta — os pedaços demoravam a
     sair e depois corriam, o que dava a leitura de solavanco. */
  transition:
    transform .72s cubic-bezier(.22, .78, .26, 1),
    opacity .18s linear;
  will-change: transform, opacity;
  /* fora do .ab-monta a peça herdaria opacity 0; o JS fixa 1 ao decolar,
     isto é a rede de segurança */
  animation: none !important;
}

/* o texto da peça não pode reflowar durante o voo: ela é escalada, não
   redimensionada. O título é a exceção — ele já nasce em duas linhas. */
.ab-voando { white-space: nowrap; }
.ab-voando[data-morf="titulo"] { white-space: normal; }

/* O cromo NÃO se desloca enquanto as peças chegam: elas estão virando a
   navbar, e um alvo em movimento faria o pouso errar (medi 10px de desvio
   antes de tirar o translate). Só a opacidade sobe. */
body.ab-recebendo .navbar,
body.ab-recebendo .utility-bar {
  /* rápido de propósito: a barra precisa estar INTEIRA no lugar antes da
     primeira peça pousar (o primeiro pouso é em 820ms). Se ela ainda
     estivesse subindo, veria-se o menu "aparecer do nada" ao lado das
     peças, em vez de recebê-las. */
  animation: ab-surge .3s var(--ease) both;
}

/* o conteúdo, esse sim, pode subir para o lugar */
body.ab-recebendo main {
  animation: ab-chega .7s var(--ease) .18s both;
}

/* Enquanto a cortina está no ar o site existe (para as medidas do FLIP)
   mas não é PINTADO. Medi 57ms por quadro com a página compondo blobs,
   backdrop-filter e fundos de IA por baixo de uma cortina opaca — trabalho
   inteiramente jogado fora, e era a maior parte do travamento.
   O :has() garante que isto só vale enquanto a camada existe de fato. */
@supports selector(:has(*)) {
  body.construindo:not(.ab-recebendo):has(> .abertura.ab2:not(.abertura--fim))
    > :is(.utility-bar, header, main, .site-footer) {
    visibility: hidden;
  }
}

@keyframes ab-surge {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Só o deslocamento. A opacidade do conteúdo fica por conta do véu que
   levanta — dois fades sobrepostos deixavam um vale escuro no meio. */
@keyframes ab-chega {
  from { transform: translateY(-14px); }
  to   { transform: none; }
}

/* enquanto a cortina está no ar, o corpo não rola */
body.construindo { overflow: hidden; }

@media (max-width: 1024px) {
  .abertura.ab2 { display: none !important; }
}

@media (prefers-reduced-motion: reduce) {
  /* abertura: não existe */
  .abertura { display: none !important; }

  html { scroll-behavior: auto !important; }

  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    animation-delay: 0s !important;
    transition-duration: .001ms !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }

  /* ---- tela do app: entra reta, montada, sem desdobrar ---- */
  [data-reveal="unfold"],
  [data-reveal="unfold"].is-visible {
    opacity: 1 !important;
    transform: none !important;
    perspective: none !important;
    transform-style: flat !important;
  }

  [data-reveal="unfold"]::before,
  [data-reveal="unfold"].is-visible::before { display: none !important; }

  [data-reveal="unfold"] .app-shot,
  [data-reveal="unfold"].is-visible .app-shot,
  [data-reveal="unfold"].is-visible:hover .app-shot { transform: none !important; }

  [data-reveal="unfold"] .app-shot::after,
  [data-reveal="unfold"].is-visible .app-shot::after,
  [data-reveal="unfold"] .app-shot::before,
  [data-reveal="unfold"].is-visible .app-shot::before {
    animation: none !important;
    opacity: 0 !important;
  }

  /* a cascata interna não existe: a tela já chega montada */
  [data-reveal="unfold"] .app-shot__body > *,
  [data-reveal="unfold"].is-visible .app-shot__body > *,
  [data-reveal="unfold"] .app-shot__bar,
  [data-reveal="unfold"].is-visible .app-shot__bar {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }

  /* ---- fundos de IA: ficam, mas parados ---- */
  .ai-bg::before,
  .ai-bg::after,
  .ai-bg--mesh::before,
  .ai-bg--mesh::after,
  .ai-bg--circuito::before,
  .ai-bg--circuito::after,
  .ai-bg--particulas,
  .ai-bg--particulas::before,
  .ai-bg--particulas::after,
  .ai-bg--aurora::before,
  .ai-bg--aurora::after {
    animation: none !important;
    transform: none !important;
    transition: none !important;
  }

  .ai-bg { --ai-dim: .4; }

  /* o halo de cursor não persegue ninguém: fica no centro e apagado */
  .ai-bg--mesh::after,
  [data-ai-bg]:hover .ai-bg--mesh::after { opacity: 0 !important; }

  .ai-bg--mesh { background-image: none !important; }

  /* ---- efeitos premium ---- */
  .tilt,
  .tilt:hover,
  .tilt:focus-within,
  .tilt--profundo:hover .tilt__camada,
  .tilt--profundo:focus-within .tilt__camada {
    transform: none !important;
    transition: none !important;
  }

  .tilt::after,
  .tilt:hover::after,
  .tilt:focus-within::after { opacity: 0 !important; }

  .reveal-mask,
  .reveal-mask.is-visible,
  .reveal-mask--sobe,
  .reveal-mask--sobe.is-visible {
    -webkit-mask-image: none !important;
    mask-image: none !important;
  }

  .count-glow,
  .count-glow[data-contando="ok"],
  .count-glow.is-counted {
    animation: none !important;
    transform: none !important;
    filter: none !important;
  }

  .edge-light::before,
  .edge-light.is-visible::before {
    background-size: 100% 100% !important;
    transition: none !important;
  }

  .depth-stack > *,
  .depth-stack:hover > *,
  .depth-stack:focus-within > * {
    transform: none !important;
    opacity: 1 !important;
    transition: none !important;
  }

  .depth-stack {
    grid-auto-flow: row;
    gap: 16px;
    perspective: none !important;
  }

  .depth-stack > * { grid-area: auto; }

  .noise::after { opacity: .02 !important; }

  /* atalho do WhatsApp: mantém o botão, tira a pulsação */
  .wa-fab::before { animation: none !important; opacity: .35 !important; transform: none !important; }

  /* conteúdo sempre no estado final e legível */
  [data-reveal],
  [data-reveal].is-visible,
  .hero [data-hero-in],
  .wipe > * {
    opacity: 1 !important;
    transform: none !important;
  }

  .wipe,
  .wipe--down,
  .wipe--left { clip-path: none !important; }

  /* nada de loop: marquee, blobs, borda cônica, gradientes e pulsos */
  .marquee-track,
  .blobs::before,
  .blobs::after,
  .glow-border::before,
  .glow-border--rotate::before,
  .glow-border--aura::after,
  .slide-border::before,
  .gradient-text--animated,
  .data-lines path.data-pulse,
  .data-lines circle.data-node,
  .pulse-dot::after,
  .float,
  .btn--breathe,
  #contact-submit-btn.is-loading::before {
    animation: none !important;
  }

  .marquee-track { transform: none !important; }
  .blobs::before,
  .blobs::after { transform: none !important; filter: blur(90px) !important; }
  .glow-border--aura::after { filter: none !important; }
  .data-lines path.data-pulse { opacity: .6 !important; stroke-dashoffset: 0 !important; }
  .data-lines path.data-ring { opacity: 1 !important; }
  .pulse-dot::after { display: none !important; }

  /* estados de hover não deslocam nada */
  .lift:hover,
  .lift:focus-within,
  .lift--sm:hover,
  .marquee-item:hover,
  [data-page-nav] a:hover,
  [data-page-nav] button:hover { transform: none !important; }

  .zoom-media img { transform: none !important; }

  /* o brilho do botão fica parado fora do campo de visão */
  .btn-primary::after,
  .btn-success::after,
  .btn-primary:hover::after,
  .btn-success:hover::after { transform: translate3d(-130%, 0, 0) !important; }

  /* a barra de progresso completa na hora, sem correr */
  .is-active > .progress-line::after,
  [aria-selected="true"] > .progress-line::after {
    animation: none !important;
    transform: scaleX(1) !important;
  }

  /* ---- camada ligada ao scroll (21-22): estado final, parado ----
     Atenção: animação de timeline IGNORA animation-duration, então o
     bloco genérico lá de cima não a desliga. Tem de ser animation:none. */
  .sc-parallax,
  .sc-escala,
  .app-figure--parallax .app-shot {
    animation: none !important;
    transform: none !important;
    translate: none !important;
    opacity: 1 !important;
    will-change: auto !important;
  }

  .sc-desfoca {
    animation: none !important;
    filter: none !important;
  }

  .sc-progresso::after {
    animation: none !important;
    transform: scaleX(1) !important;
    opacity: 1 !important;
  }

  .app-figure--parallax::after {
    animation: none !important;
    transform: none !important;
    opacity: .45 !important;
    will-change: auto !important;
  }

  /* ---- cascata por contêiner (23): tudo visível, sem fila ---- */
  [data-stagger] > [data-reveal],
  [data-reveal][data-stagger="filhos"] > *,
  [data-reveal][data-stagger="filhos"].is-visible > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }

  /* ---- botão magnético (24): o rótulo não persegue ninguém ---- */
  .magnetico__rotulo,
  .magnetico:hover .magnetico__rotulo,
  .magnetico:focus-visible .magnetico__rotulo,
  [data-magnetico]:hover .magnetico__rotulo,
  [data-magnetico]:focus-visible .magnetico__rotulo {
    transform: none !important;
    transition: none !important;
    will-change: auto !important;
  }

  /* ---- linha do tempo (25): já chega desenhada ---- */
  [data-reveal].timeline--anima::before,
  [data-reveal].timeline--anima:not(.is-visible)::before,
  [data-reveal].timeline--anima.is-visible::before {
    transform: none !important;
    transition: none !important;
  }

  [data-reveal].timeline--anima .timeline-item::before,
  [data-reveal].timeline--anima:not(.is-visible) .timeline-item::before,
  [data-reveal].timeline--anima.is-visible .timeline-item::before {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
  }

  .timeline--anima .timeline-item::after,
  [data-reveal].timeline--anima.is-visible .timeline-item::after {
    display: none !important;
  }

  /* ---- número em destaque (26): destaque sem pulso ---- */
  .stat--destaque .stat-value,
  .stat--destaque .stat-value[data-contando="ok"],
  .stat--destaque [data-count][data-contando="ok"],
  .stat--destaque .stat-value.is-counted {
    animation: none !important;
    transform: none !important;
    filter: none !important;
  }

  .stat--destaque::after,
  .stat--destaque:has([data-contando="ok"])::after {
    animation: none !important;
    transform: none !important;
    opacity: .22 !important;
  }

  /* ---- borda de transição (27): linha acesa desde o início ---- */
  .borda-luz::before,
  [data-reveal].borda-luz:not(.is-visible)::before {
    animation: none !important;
    transition: none !important;
    transform: scaleX(1) !important;
    opacity: 1 !important;
  }

  /* ---- cartão em camadas (28): plano, legível, sem Z ---- */
  .camadas { perspective: none !important; }

  .camadas__frente,
  .camadas__fundo,
  .camadas:hover .camadas__frente,
  .camadas:hover .camadas__fundo,
  .camadas:focus-within .camadas__frente,
  .camadas:focus-within .camadas__fundo {
    transform: none !important;
    transition: none !important;
    opacity: 1 !important;
  }

  .camadas__fundo::after,
  .camadas:hover .camadas__fundo::after,
  .camadas:focus-within .camadas__fundo::after {
    transform: none !important;
    transition: none !important;
    opacity: .3 !important;
  }

  /* ---- cursor de precisão (29): não existe ---- */
  .alvo-cursor::after,
  .alvo-cursor:hover::after,
  .alvo-cursor:focus-within::after { display: none !important; }

  /* ---- desdobramento (30): sem passar do ponto ---- */
  [data-reveal="unfold"] { --unfold-overshoot: 1; }

  /* ---- rastro do cursor (33): não existe ---- */
  .rastro,
  .rastro--ativo,
  .rastro--pressionado { display: none !important; }

  /* ---- fundo reativo (34): fica, estático e centrado ----
     Fixar --mx/--my na própria camada sombreia o valor herdado do pai:
     o gradiente e as máscaras param no centro e não perseguem ninguém. */
  .fundo-reativo {
    --mx: 50% !important;
    --my: 50% !important;
    animation: none !important;
    transition: none !important;
  }

  .fundo-reativo,
  [data-fundo-reativo]:hover > .fundo-reativo,
  [data-fundo-reativo]:focus-within > .fundo-reativo {
    opacity: .38 !important;
  }

  .fundo-reativo--grade::after,
  .fundo-reativo--nos::after,
  [data-fundo-reativo]:hover > .fundo-reativo--grade::after,
  [data-fundo-reativo]:hover > .fundo-reativo--nos::after {
    opacity: .22 !important;
    transition: none !important;
  }

  /* ---- cena 3D e camadas de profundidade (35): entrada simples ---- */
  .cena-3d,
  .cena-3d:hover,
  .cena-3d:focus-within {
    perspective: none !important;
    transform-style: flat !important;
  }

  .profundidade-1,
  .profundidade-2,
  .profundidade-3,
  .profundidade-4,
  .cena-3d.is-visible .profundidade-1,
  .cena-3d.is-visible .profundidade-2,
  .cena-3d.is-visible .profundidade-3,
  .cena-3d.is-visible .profundidade-4 {
    transform: none !important;
    transition: none !important;
    opacity: 1 !important;
  }

  /* ---- carta 3D (35.3): plana, legível, sem giro nem sombra móvel ---- */
  .carta-3d,
  .carta-3d:hover,
  .carta-3d:focus-within {
    transform: none !important;
    transition: none !important;
    transform-style: flat !important;
  }

  .carta-3d::after,
  .carta-3d:hover::after,
  .carta-3d:focus-within::after { opacity: 0 !important; }

  .carta-3d::before,
  .carta-3d:hover::before,
  .carta-3d:focus-within::before {
    transform: none !important;
    transition: none !important;
    opacity: .3 !important;
  }

  .carta-3d__camada,
  .carta-3d:hover .carta-3d__camada,
  .carta-3d:focus-within .carta-3d__camada {
    transform: none !important;
    transition: none !important;
  }

  /* ---- contorno que percorre a borda (36.1): não corre ---- */
  .brilho-borda::before,
  .brilho-borda.is-visible::before {
    animation: none !important;
    opacity: 0 !important;
  }

  /* ---- título palavra a palavra (36.2): já chega montado ---- */
  [data-reveal].texto-revela > span,
  [data-reveal].texto-revela:not(.is-visible) > span,
  [data-reveal].texto-revela.is-visible > span,
  [data-reveal].texto-revela--suave > span,
  [data-reveal].texto-revela--suave:not(.is-visible) > span {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
    transition-delay: 0s !important;
  }

  /* ---- abertura cinematográfica (38): não existe ----
     O JS não deve nem criá-la; se criar, a camada sai do caminho e o
     site aparece pronto, no primeiro quadro. */
  .abertura,
  .abertura.p1,
  .abertura.p2,
  .abertura.p3,
  .abertura.p4,
  .abertura.p5,
  .abertura.saindo { display: none !important; }

  /* ---- o site se construindo (39): já chega construído ----
     Nada espera cortina nenhuma: o cromo está no lugar e o hero entra
     no ritmo normal (o bloco genérico acima já zerou os atrasos). */
  body.construindo .utility-bar,
  body.construido .utility-bar,
  body.construindo .navbar,
  body.construido .navbar {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }

  body.construindo .hero [data-hero-in],
  body.construido .hero [data-hero-in] {
    animation: none !important;
    animation-delay: 0s !important;
    opacity: 1 !important;
    transform: none !important;
  }

  /* a trava de rolagem do :has() nunca pega: sem abertura, sem trava */
  body.construindo,
  body.construido { overflow: visible !important; }

  /* ---- desdobramento extremo (37): a tela chega montada e reta ----
     As regras da seção 32 já zeram o transform da figura, da barra e
     dos filhos do corpo. Falta só desligar a varredura nova. */
  [data-reveal="unfold"] { perspective-origin: 50% 50% !important; }

  [data-reveal="unfold"] .app-shot__body > *,
  [data-reveal="unfold"].is-visible .app-shot__body > * {
    transition-delay: 0s !important;
  }
}
