/* ===========================================================================
   Animations — keyframes, scroll-reveal classes, and the reduced-motion
   contract.

   Everything here animates only `transform`, `opacity`, `filter` or
   `stroke-dashoffset`, so nothing in this file triggers layout.
   =========================================================================== */

/* ------------------------------------------------------------- keyframes */

@keyframes spin {
  to {
    transform: rotate(360deg);
  }
}

/* Expanding ring used by live dots and map pins. */
@keyframes ping {
  0% {
    transform: scale(0.85);
    opacity: 0.9;
  }
  70%,
  100% {
    transform: scale(1.9);
    opacity: 0;
  }
}

/* The live end of the journey timeline: a gold halo swelling under the marker,
   in step with the ring pinging out of it. */
@keyframes now-glow {
  0%,
  100% {
    box-shadow: 0 0 0 0 rgba(255, 198, 46, 0.34);
  }
  50% {
    box-shadow: 0 0 26px 6px rgba(255, 198, 46, 0.28);
  }
}

@keyframes float-y {
  0%,
  100% {
    transform: translateZ(70px) translateY(0);
  }
  50% {
    transform: translateZ(70px) translateY(-9px);
  }
}

/* A short segment running the length of a hero network link. */
@keyframes net-travel {
  from {
    stroke-dashoffset: 0;
  }
  to {
    stroke-dashoffset: -1005;
  }
}

@keyframes net-pulse {
  0%,
  100% {
    opacity: 0.45;
    scale: 0.8;
  }
  50% {
    opacity: 1;
    scale: 1.25;
  }
}

@keyframes marquee {
  from {
    transform: translateX(0);
  }
  to {
    transform: translateX(-50%);
  }
}

/* Mission / vision marks. `translate` rather than `transform` so the hover
   transform can take over without fighting the loop. */
@keyframes icon-float {
  0%,
  100% {
    translate: 0 0;
  }
  50% {
    translate: 0 -9px;
  }
}

/* The amber pool under a mission / vision icon, breathing in step with it. */
@keyframes glow-pulse {
  0%,
  100% {
    opacity: 0.55;
    scale: 0.94;
  }
  50% {
    opacity: 1;
    scale: 1.06;
  }
}

@keyframes bar-grow {
  0%,
  100% {
    transform: scaleY(0.65);
  }
  50% {
    transform: scaleY(1);
  }
}

/* Slow drift for ambient washes — long enough to read as light, not motion. */
@keyframes drift {
  0%,
  100% {
    transform: translate3d(0, 0, 0) scale(1);
  }
  50% {
    transform: translate3d(3%, -4%, 0) scale(1.08);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
  to {
    opacity: 1;
  }
}

.ambient::before {
  animation: drift 22s var(--ease-in-out) infinite;
}

.ambient::after {
  animation: drift 28s var(--ease-in-out) infinite reverse;
}

/* -------------------------------------------------------- scroll reveal */
/* animations.js adds .is-revealed via IntersectionObserver. The initial state
   is applied only when the observer is actually running (html.js-reveal), so
   a visitor with JS disabled sees fully-rendered content rather than a blank
   page. */

.js-reveal .reveal,
.js-reveal .reveal-up,
.js-reveal .reveal-scale,
.js-reveal .reveal-left,
.js-reveal .reveal-right {
  opacity: 0;
  transition: opacity var(--dur-reveal) var(--ease-out), transform var(--dur-reveal) var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
  will-change: transform, opacity;
}

.js-reveal .reveal-up {
  transform: translateY(26px);
}

.js-reveal .reveal-scale {
  transform: scale(0.965);
}

.js-reveal .reveal-left {
  transform: translateX(-26px);
}

.js-reveal .reveal-right {
  transform: translateX(26px);
}

.js-reveal .is-revealed {
  opacity: 1;
  transform: none;
  will-change: auto;
}

/* Stagger helper — set --reveal-delay per child from markup. */
.stagger > * {
  --reveal-delay: calc(var(--n, 0) * 80ms);
}

/* Hero entrance. Runs once on load, independent of the scroll observer, so
   the fold is never blank waiting for an intersection. */
.hero__copy > * {
  animation: hero-in 900ms var(--ease-out) backwards;
  animation-delay: calc(var(--n, 0) * 90ms + 120ms);
}

/* The badge and the headline get their own treatments; the rest of the column
   keeps the plain fade-up above. */
.hero__copy > .pill {
  animation: badge-in 700ms var(--ease-spring) 80ms backwards;
}

.underline-swoosh {
  animation: swoosh-draw 640ms var(--ease-out) 1020ms forwards;
}

.hero__visual {
  animation: hero-in 1100ms var(--ease-out) backwards;
  animation-delay: 320ms;
}

/* The headline wipes up from its own baseline instead of just fading — the
   clip is what makes it read as type being set rather than a block appearing. */
@keyframes headline-in {
  from {
    opacity: 0;
    transform: translateY(26px);
    clip-path: inset(0 0 110% 0);
  }
  to {
    opacity: 1;
    transform: none;
    clip-path: inset(0 0 -20% 0);
  }
}

/* The gold underline draws itself left to right once the headline has landed. */
@keyframes swoosh-draw {
  to {
    clip-path: inset(0 0 0 0);
  }
}

/* The hero badge arrives slightly scaled-down so it pops rather than slides. */
@keyframes badge-in {
  from {
    opacity: 0;
    transform: translateY(14px) scale(0.9);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

@keyframes hero-in {
  from {
    opacity: 0;
    transform: translateY(20px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ------------------------------------------------------- reduced motion */
/* Two levels: kill decorative loops entirely, and collapse transitions to a
   near-instant crossfade so state changes are still perceivable. */

@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Reveal targets must end up visible even though their transition is gone. */
  .js-reveal .reveal,
  .js-reveal .reveal-up,
  .js-reveal .reveal-scale,
  .js-reveal .reveal-left,
  .js-reveal .reveal-right {
    opacity: 1;
    transform: none;
  }

  /* Continuous loops are removed rather than sped up. */
  .marquee__track,
  .mv-card__icon,
  .mv-card .card__icon-glow,
  .hero-flow__pulse path,
  .hero-flow__orbit,
  .ambient::before,
  .ambient::after,
  .pill__dot--live::after,
  .masked-call__dial::after,
  .reach__pin::after,
  .journey__item--now .journey__marker,
  .journey__item--now .journey__marker::after,
  .hero-flow__sweep,
  .hero-flow__bars i,
  .bento__shield {
    animation: none !important;
  }

  /* The ping ring would otherwise sit frozen at its start scale on the marker. */
  .journey__item--now .journey__marker::after {
    display: none;
  }

  /* The hero entrance is decoration; land everything in its final state. */
  .hero__copy > *,
  .hero__copy > .pill,
  .hero__visual,
  .hero__title,
  .underline-swoosh {
    animation: none !important;
  }

  .underline-swoosh {
    clip-path: none;
  }

  .hero__title {
    clip-path: none;
  }

  .mv-card:hover .mv-card__icon {
    transform: none;
  }

  .steps__rail::after {
    transform: scaleX(1);
  }
}
