/* ============================================================================
   MAXIMIZE — camada de movimento
   ----------------------------------------------------------------------------
   O movimento deste site tem UMA gramática, herdada do mundo do instrumento:
   nada "desliza"; as coisas ACENDEM e ASSENTAM, como fósforo excitado que
   depois decai. Curva de saída exponencial, sempre a partir de um estado já
   visível — conteúdo nunca depende da animação para existir.

   ---------------------------------------------------------------------------
   REGRA DE ARQUITETURA (aprendida quebrando o site)

   Existem dois tipos de efeito, e eles NÃO usam o mesmo motor:

   1. EFEITO COM ESTADO FINAL — revelar, desembaçar, abrir cortina.
      Motor: IntersectionObserver + transition.
      Por quê: `animation-timeline: view()` com um range em `entry` ou `cover`
      NUNCA COMPLETA quando o elemento é mais alto que a viewport, porque
      `entry 100%` exige que a borda inferior do elemento entre na tela. Numa
      seção de 1500px numa tela de 900px isso só acontece depois de rolar a
      seção inteira — e até lá o conteúdo fica embaçado enquanto o visitante
      tenta lê-lo. Foi exatamente esse o defeito de "seções que não desembaçam".

   2. EFEITO CONTÍNUO — parallax, deriva de gratícula, barra de progresso,
      crescimento de medidor.
      Motor: `animation-timeline` (scroll/view).
      Por quê: não têm estado final para ficar preso. Se o range não completar,
      o efeito apenas não chega ao extremo — ninguém percebe, nada some.

   Nunca mova um efeito do grupo 1 para o motor do grupo 2.
   ========================================================================== */

@property --angle {
  syntax: '<angle>';
  initial-value: 0deg;
  inherits: false;
}

/* ============================================================================
   1. CONTÍNUOS — motor de scroll timeline
   ========================================================================== */

/* progresso de leitura */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .scroll-progress {
      animation: progressGrow linear both;
      animation-timeline: scroll(root block);
    }
  }
}
@keyframes progressGrow {
  to {
    transform: scaleX(1);
  }
}

/* medidores e barras crescem conforme entram */
@media (prefers-reduced-motion: no-preference) {
  @supports (animation-timeline: view()) {
    html.has-sda .vs-fill,
    html.has-sda .meter > i {
      animation: barGrow var(--ease-out) both;
      animation-timeline: view();
      animation-range: entry 12% cover 45%;
    }
  }
}
@keyframes barGrow {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* ============================================================================
   2. COM ESTADO FINAL — motor IntersectionObserver
   ----------------------------------------------------------------------------
   O estado "antes" só existe depois que o JS confirma que vai revelar
   (classe .io no <html>). Sem JS, tudo nasce visível e legível.
   ========================================================================== */

html.io .reveal,
html.io .stagger > * {
  opacity: 0;
  transform: translateY(18px);
  filter: blur(5px);
  transition: opacity 0.7s var(--ease-out), transform 0.7s var(--ease-out),
    filter 0.7s var(--ease-out);
  transition-delay: var(--d, 0ms);
}
html.io .reveal-up {
  transform: translateY(44px);
}
html.io .reveal-scale {
  transform: translateY(22px) scale(0.968);
}

html.io .reveal.is-in,
html.io .stagger > *.is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* cortina: a banda sobe revelada por clip-path */
html.io .curtain {
  clip-path: inset(100% 0 0 0);
  transition: clip-path 0.95s var(--ease-out);
}
html.io .curtain.is-in {
  clip-path: inset(0% 0 0 0);
}

/* assentar: para faixas de largura total, onde escalar criaria barra
   horizontal. Assenta por luz e foco, nunca por tamanho. */
html.io .settle {
  opacity: 0.35;
  filter: blur(4px);
  transition: opacity 0.85s var(--ease-out), filter 0.85s var(--ease-out);
}
html.io .settle.is-in {
  opacity: 1;
  filter: blur(0);
}

/* aproximar: só para elementos CONTIDOS (figura, cartão, título). */
html.io .zoom-settle {
  opacity: 0;
  transform: scale(1.06);
  filter: blur(5px);
  transition: opacity 0.9s var(--ease-out), transform 0.9s var(--ease-out),
    filter 0.9s var(--ease-out);
}
html.io .zoom-settle.is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
}

/* Numa faixa de largura total, escalar 6% joga 3% da largura para FORA de cada
   lado e cria transbordo horizontal na página inteira. Faixa nunca muda de
   tamanho: assenta por luz e foco, como `.settle`. A regra vive aqui, e não
   no HTML, para que marcar uma seção com `zoom-settle` seja seguro por
   construção em vez de depender de quem escreveu o markup lembrar disso. */
html.io section.zoom-settle,
html.io .band.zoom-settle,
html.io .section.zoom-settle {
  opacity: 0.35;
  transform: none;
  filter: blur(4px);
  transition: opacity 0.85s var(--ease-out), filter 0.85s var(--ease-out);
}
html.io section.zoom-settle.is-in,
html.io .band.zoom-settle.is-in,
html.io .section.zoom-settle.is-in {
  opacity: 1;
  filter: blur(0);
}

/* leitura palavra a palavra */
html.io .read-in > span {
  opacity: 0.16;
  transition: opacity 0.5s var(--ease-out);
  transition-delay: calc(var(--wi, 0) * 26ms);
}
html.io .read-in.is-in > span {
  opacity: 1;
}

/* costura entre bandas: a linha de fósforo abre a partir do centro */
.seam::before {
  transform: scaleX(0.04);
  opacity: 0;
  transform-origin: 50% 50%;
  transition: transform 1s var(--ease-out), opacity 0.6s ease;
}
.seam.is-in::before {
  transform: scaleX(1);
  opacity: 0.55;
}
html:not(.io) .seam::before {
  transform: scaleX(1);
  opacity: 0.55;
}

/* ============================================================================
   3. ENTRADA DA BANDA — o movimento entre seções verticais
   ----------------------------------------------------------------------------
   Cada faixa não apenas "aparece": ela chega. Um deslocamento curto somado a
   um clareamento, orquestrado com a costura, dá ao scroll a sensação de
   passagem entre capítulos em vez de rolagem contínua de um documento.
   ========================================================================== */

html.io .band,
html.io .section {
  --band-shift: 22px;
}
@media (prefers-reduced-motion: no-preference) {
  html.io .band > .wrap,
  html.io .band > .wrap--wide,
  html.io .band > .wrap--narrow,
  html.io .section > .section-container {
    opacity: 0;
    transform: translateY(var(--band-shift));
    transition: opacity 0.75s var(--ease-out), transform 0.75s var(--ease-out);
  }
  html.io .band.is-in > .wrap,
  html.io .band.is-in > .wrap--wide,
  html.io .band.is-in > .wrap--narrow,
  html.io .section.is-in > .section-container {
    opacity: 1;
    transform: none;
  }
}

/* ============================================================================
   4. HERO — acende no carregamento, sem depender de scroll
   ========================================================================== */

@keyframes ignite {
  from {
    opacity: 0;
    transform: translateY(20px);
    filter: blur(6px);
  }
  to {
    opacity: 1;
    transform: none;
    filter: blur(0);
  }
}
/* O H1 é o elemento de LCP. Ele NUNCA parte de opacity: 0 — fazer isso adia a
   pintura pelo tempo inteiro da animação, e mediu 1 s de render delay num site
   cujo orçamento é LCP <= 1,5 s e cujo produto vende performance. A entrada
   dele é só transform, que não atrasa a métrica. */
@keyframes riseOnly {
  from {
    transform: translateY(26px);
  }
  to {
    transform: none;
  }
}
@media (prefers-reduced-motion: no-preference) {
  .hero h1 > span {
    display: block;
    animation: riseOnly 0.85s var(--ease-out) both;
  }
  .hero h1 > span:nth-child(1) {
    animation-delay: 0.02s;
  }
  .hero h1 > span:nth-child(2) {
    animation-delay: 0.1s;
  }
  .hero .status-line,
  .hero .eyebrow,
  .hero .lede,
  .hero .prose,
  .hero .hero-actions,
  .hero .btn-note,
  .hero .channels,
  .hero .channels-legend,
  .hero .gauges,
  .hero .perimeter,
  .hero .term,
  .hero .disk,
  .hero .price-hero,
  .hero .price-sub,
  .hero .doc-meta,
  .hero .jump {
    animation: ignite 0.9s var(--ease-out) both;
  }
  .hero .status-line,
  .hero .eyebrow {
    animation-delay: 0s;
  }
  .hero .lede,
  .hero .prose,
  .hero .price-hero {
    animation-delay: 0.26s;
  }
  .hero .hero-actions,
  .hero .price-sub {
    animation-delay: 0.36s;
  }
  .hero .btn-note,
  .hero .doc-meta,
  .hero .jump {
    animation-delay: 0.46s;
  }
  .hero .channels,
  .hero .gauges,
  .hero .perimeter,
  .hero .term,
  .hero .disk {
    animation-delay: 0.4s;
  }
  .hero .app {
    animation: ignite 1.1s var(--ease-out) 0.25s both;
  }
  .hero .channels-legend {
    animation-delay: 0.6s;
  }
}

/* varredura de fósforo no título — uma vez, ao carregar */
@media (prefers-reduced-motion: no-preference) {
  .scan-once {
    background-image: linear-gradient(
      100deg,
      currentColor 0%,
      currentColor 42%,
      var(--accent-core) 50%,
      currentColor 58%,
      currentColor 100%
    );
    background-size: 300% 100%;
    background-position: 100% 0;
    -webkit-background-clip: text;
    background-clip: text;
    animation: scanText 2.6s cubic-bezier(0.5, 0, 0.2, 1) 0.9s 1 both;
  }
}
@keyframes scanText {
  to {
    background-position: 0 0;
  }
}

/* ============================================================================
   5. PERMANENTES E DE INTERAÇÃO
   ========================================================================== */

@media (prefers-reduced-motion: no-preference) {
  @supports (background: conic-gradient(from 0deg, red, blue)) {
    .btn--primary.btn--lg {
      isolation: isolate;
    }
    .btn--primary.btn--lg::before {
      content: '';
      position: absolute;
      inset: -2px;
      z-index: -1;
      border-radius: inherit;
      background: conic-gradient(
        from var(--angle),
        transparent 0deg,
        var(--accent-core) 36deg,
        transparent 100deg,
        transparent 360deg
      );
      /* PARADO em repouso. `--angle` é custom property registrada: animá-la NÃO
         é compositável — cada quadro invalida a pintura do botão. Rodando
         infinito em até 3 CTAs por página, isso taxa INP para sempre, e um anel
         girando sozinho lê como banner de anúncio, não como botão. Agora o anel
         está lá, imóvel, e só gira quando a pessoa demonstra intenção. */
      opacity: 0.55;
      transition: opacity 0.3s var(--ease-out);
    }
    .btn--primary.btn--lg:hover::before,
    .btn--primary.btn--lg:focus-visible::before {
      opacity: 0.9;
      animation: spinAngle 4.5s linear infinite;
    }
  }
}
@keyframes spinAngle {
  to {
    --angle: 360deg;
  }
}

@media (prefers-reduced-motion: no-preference) {
  .live-dot {
    animation: livePulse 2.2s ease-in-out infinite;
  }
}
@keyframes livePulse {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(47, 224, 138, 0.5);
  }
  70% {
    box-shadow: 0 0 0 8px rgba(47, 224, 138, 0);
  }
}

@media (prefers-reduced-motion: no-preference) {
  .bus-track {
    animation: busSlide 62s linear infinite;
  }
  .bus:hover .bus-track,
  .bus:focus-within .bus-track {
    animation-play-state: paused;
  }
}
@keyframes busSlide {
  to {
    transform: translate3d(-50%, 0, 0);
  }
}

html.fx-rich .tilt {
  transform: perspective(1000px) rotateX(var(--rx, 0deg)) rotateY(var(--ry, 0deg));
  transition: transform 0.5s var(--ease-out);
  transform-style: preserve-3d;
}
html.fx-rich .tilt.is-tilting {
  transition: transform 0.09s linear;
}

.fx-spot {
  position: fixed;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.6s ease;
  background: radial-gradient(
    420px circle at var(--mx, 50%) var(--my, 0%),
    rgba(34, 224, 232, 0.055),
    transparent 68%
  );
}
html.fx-rich .fx-spot {
  opacity: 1;
}

.panel {
  transition: border-color 0.35s var(--ease-out), transform 0.35s var(--ease-out);
}
.panel--hover:hover {
  border-color: var(--border-accent);
  transform: translateY(-3px);
}

@media (prefers-reduced-motion: no-preference) {
  .sw[aria-checked='true'] {
    box-shadow: 0 0 16px -4px var(--accent-glow);
  }
  .app-panel:not([hidden]) {
    animation: panelIn 0.4s var(--ease-out) both;
  }
}
@keyframes panelIn {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ============================================================================
   REDUÇÃO DE MOVIMENTO — tudo morre, nada some
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  html.io .reveal,
  html.io .stagger > *,
  html.io .curtain,
  html.io .settle,
  html.io .zoom-settle,
  html.io .read-in > span,
  html.io .band > .wrap,
  html.io .band > .wrap--wide,
  html.io .band > .wrap--narrow,
  html.io .section > .section-container {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    clip-path: none !important;
  }
  .seam::before {
    transform: scaleX(1) !important;
    opacity: 0.55 !important;
  }
  .hero h1 > span,
  .hero .status-line,
  .hero .eyebrow,
  .hero .lede,
  .hero .prose,
  .hero .hero-actions,
  .hero .btn-note,
  .hero .app,
  .hero .channels,
  .hero .channels-legend,
  .hero .gauges,
  .hero .perimeter,
  .hero .term,
  .hero .disk,
  .hero .price-hero,
  .hero .price-sub,
  .hero .doc-meta,
  .hero .jump {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    animation: none !important;
  }
  .bus-track,
  .live-dot,
  .scan-once,
  .btn--primary.btn--lg::before {
    animation: none !important;
  }
  .fx-spot {
    display: none;
  }
  .vs-fill,
  .meter > i {
    transform: none !important;
  }
  .scanlines::after {
    opacity: 0.25;
  }
}
