/* ─── GS ODONTOLOGIA — ANIMAÇÕES ───────────────────────────────────
   Keyframes + estados iniciais das revelações de scroll.

   REGRA DE OURO: nada é escondido sem que o JS confirme que consegue
   revelar. A classe `.gs-anim` só entra no <html> quando o GSAP carrega
   (ver main.js). Sem JS, sem CDN ou com erro, o site aparece inteiro.
   ───────────────────────────────────────────────────────────────── */

/* ══ KEYFRAMES ═════════════════════════════════════════════════════ */

@keyframes gs-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes gs-pop-in {
  from { opacity: 0; transform: translateY(14px) scale(.97); }
  to   { opacity: 1; transform: none; }
}

/* seta do hero: respiração vertical curta */
@keyframes gs-bob {
  0%, 100% { transform: translateY(0);   opacity: .75; }
  50%      { transform: translateY(6px); opacity: 1; }
}

/* halo do botão flutuante do WhatsApp */
@keyframes gs-pulse {
  0%   { transform: scale(1);    opacity: .55; }
  70%  { transform: scale(1.65); opacity: 0; }
  100% { transform: scale(1.65); opacity: 0; }
}

/* brilho que atravessa o comparador na primeira aparição */
@keyframes gs-sweep {
  from { transform: translateX(-120%); }
  to   { transform: translateX(220%); }
}

/* ══ ESTADOS INICIAIS DAS REVELAÇÕES ═══════════════════════════════ */

.gs-anim [data-reveal] {
  opacity: 0;
  will-change: opacity, transform;
}

/* fade + slide (padrão) */
.gs-anim [data-reveal=""],
.gs-anim [data-reveal="up"] { transform: translateY(48px); }

/* scale reveal */
.gs-anim [data-reveal="scale"] { transform: scale(.92); }

/* text reveal: o próprio elemento fica visível; as linhas é que sobem */
.gs-anim [data-reveal="text"] { opacity: 1; }

.gs-anim [data-reveal-stagger] > * {
  opacity: 0;
  transform: translateY(40px);
  will-change: opacity, transform;
}

/* hero: cada elemento entra pela timeline de hero.js */
.gs-anim [data-hero-el] {
  opacity: 0;
  transform: translateY(32px);
}

.gs-anim [data-hero-line] > span {
  transform: translateY(105%);
}

/* liberado assim que a animação termina, para não travar composição */
.gs-anim .gs-is-revealed { will-change: auto; }


/* ══ PARTÍCULAS ════════════════════════════════════════════════════ */

.gs-hero__particles {
  opacity: 0;
  transition: opacity var(--dur-epic) var(--ease-smooth);
}
.gs-hero__particles.is-ready { opacity: .85; }


/* ══ SLIDESHOW DO HERO ═════════════════════════════════════════════
   Fade e zoom são CSS de propósito: o hero é a primeira coisa na tela
   e não pode ficar parado esperando a CDN do GSAP. O JS (hero.js) só
   troca as classes — o tempo de cada imagem está em INTERVALO, lá.

   Zoom em `animation` e não em `transition`: animação roda sozinha ao
   carregar a página, então a primeira imagem também respira, sem
   precisar de um clique ou de um segundo quadro para começar. */

@keyframes gs-kenburns-in {
  from { transform: scale(1); }
  to   { transform: scale(1.09); }
}

@keyframes gs-kenburns-out {
  from { transform: scale(1.09); }
  to   { transform: scale(1); }
}

.gs-hero__slide {
  opacity: 0;
  transition: opacity var(--dur-cinematic) var(--ease-smooth);
}

/* `is-leaving` = saindo de cena: segue com o zoom enquanto some. Sem
   ela a escala voltaria de um golpe no meio do fade, e o corte
   apareceria. */
.gs-hero__slide.is-active,
.gs-hero__slide.is-leaving {
  animation: gs-kenburns-in 7.6s var(--ease-smooth) forwards;
}

/* alternando o sentido, quatro imagens não viram um vaivém previsível */
.gs-hero__slide:nth-child(even).is-active,
.gs-hero__slide:nth-child(even).is-leaving {
  animation-name: gs-kenburns-out;
}

.gs-hero__slide.is-active { opacity: 1; }


/* ══ CTA INVISALIGN ════════════════════════════════════════════════
   Não há nada aqui: o fundo é um vídeo, e o movimento vem da filmagem.
   Os dois laços de CSS que existiam (`gs-cta-drift`, o zoom lento, e
   `gs-cta-bloom`, o pulso da luz) eram muletas para uma foto parada e
   saíram junto com ela. Quando pausar e quando baixar é com o
   `cta-invisalign.js`; o texto por cima entra por scroll
   (`data-reveal-stagger`, ver acima). */


/* ══ REVELAÇÃO HORIZONTAL POR CLIP-PATH ════════════════════════════ */

.gs-anim [data-clip-reveal] {
  clip-path: inset(0 100% 0 0);
}


/* ══ ACESSIBILIDADE ════════════════════════════════════════════════
   Com movimento reduzido nada é escondido nem animado: o conteúdo
   simplesmente está lá. Sobrepõe todos os estados iniciais acima. */

@media (prefers-reduced-motion: reduce) {
  .gs-anim [data-reveal],
  .gs-anim [data-reveal-stagger] > *,
  .gs-anim [data-hero-el] {
    opacity: 1 !important;
    transform: none !important;
  }

  .gs-anim [data-hero-line] > span { transform: none !important; }
  .gs-anim [data-clip-reveal] { clip-path: none !important; }

  .gs-hero__particles { display: none; }

  /* o slideshow não anda sozinho (ver hero.js); aqui só se garante que
     nenhuma imagem fique com zoom preso no meio do caminho */
  .gs-hero__slide.is-active,
  .gs-hero__slide.is-leaving { animation: none; }
  .gs-hero__scroll svg { animation: none; }

  /* O CTA do Invisalign não precisa de regra: o vídeo é o movimento, e
     quem decide não tocá-lo é o cta-invisalign.js, que sai fora antes
     de baixar o arquivo. Fica o `poster` — um frame do próprio vídeo. */

  .gs-fab__btn::before { animation: none; opacity: 0; }
  .gs-fab { opacity: 1; transform: none; pointer-events: auto; }
}
