/*
 * Mandate D2.3 — the complete animation inventory, in CSS. No GSAP, no runtime framework: the motion
 * reads as considered because the easing and composition are disciplined, not because a dependency
 * shipped. Every effect is transform/opacity only, so nothing triggers layout.
 *
 * `prefers-reduced-motion: reduce` is honoured GLOBALLY at the bottom of this file: content is visible
 * by default, transforms are removed, and loops stop. Nothing here is the sole carrier of meaning, so
 * removing all of it loses no information.
 */

/* ── Hero entrance: transform + opacity, 480ms emphasized ─────────────────────────────────────── */

@keyframes rise {
  from {
    opacity: 0;
    transform: translate3d(0, 18px, 0);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

.rise {
  animation: rise var(--dur-hero) var(--ease-decel) both;
  /* A CSS variable drives per-child delay, so one rule staggers a whole group. */
  animation-delay: calc(var(--i, 0) * 70ms);
}

/* ── Scroll reveal: IntersectionObserver toggles .is-visible ──────────────────────────────────── */

/*
 * Scoped to .js-on, which the head script sets. If JavaScript is blocked, fails, or is still parsing,
 * `.reveal` never gets its opacity:0 and the page is simply VISIBLE. A scroll animation must not be
 * able to make content unreadable - that is a blank page, not a subtle degradation.
 */
/*
 * Only elements the observer is ACTIVELY watching get hidden - js/motion.js adds `.will-reveal` as it
 * registers each one, and a hard safety timeout reveals anything still hidden shortly after load. An
 * animation must never be able to leave the page blank: that is what happened when this was a bare
 * `.reveal { opacity: 0 }` plus a scroll-driven fallback that never advanced.
 */
.js-on .reveal.will-reveal {
  opacity: 0;
  transform: translate3d(0, 20px, 0);
  transition: opacity var(--dur-hero) var(--ease-decel),
    transform var(--dur-hero) var(--ease-decel);
  transition-delay: calc(var(--i, 0) * 60ms);
  will-change: opacity, transform;
}

.js-on .reveal.is-visible,
.js-on .reveal.will-reveal.is-visible {
  opacity: 1;
  transform: none;
}

/* ── Parallax depth: two transform-only layers, fine pointer + wide screens only ──────────────── */

.parallax {
  will-change: transform;
}

@media (pointer: fine) and (min-width: 1000px) {
  .parallax--slow {
    transform: translate3d(0, calc(var(--parallax) * 0.28), 0);
  }
  .parallax--fast {
    transform: translate3d(0, calc(var(--parallax) * -0.16), 0);
  }
}

/* ── Pointer tilt on the hero product canvas ──────────────────────────────────────────────────── */

@media (pointer: fine) and (min-width: 1000px) {
  .tilt {
    transform: perspective(1200px) rotateX(var(--tilt-x)) rotateY(var(--tilt-y));
    transition: transform var(--dur-standard) var(--ease-standard);
    transform-style: preserve-3d;
  }
}

/* ── Mesh / gradient: pseudo-elements, long transform+opacity loops, never an animated filter ──── */

.mesh {
  position: relative;
  isolation: isolate;
}

.mesh::before,
.mesh::after {
  content: "";
  position: absolute;
  z-index: -1;
  border-radius: 50%;
  /* A static blur is fine; animating a filter would repaint the whole surface every frame. */
  filter: blur(90px);
  pointer-events: none;
}

.mesh::before {
  inset-block-start: -12%;
  inset-inline-start: -8%;
  width: 62%;
  aspect-ratio: 1;
  background: radial-gradient(circle at 30% 30%, var(--mesh-a), transparent 68%);
  animation: drift-a 18s var(--ease-standard) infinite alternate;
}

.mesh::after {
  inset-block-start: 12%;
  inset-inline-end: -10%;
  width: 68%;
  aspect-ratio: 1;
  background: radial-gradient(circle at 70% 40%, var(--mesh-b), transparent 70%);
  animation: drift-b 14s var(--ease-standard) infinite alternate;
}

@keyframes drift-a {
  from {
    transform: translate3d(0, 0, 0) scale(1);
    opacity: 0.85;
  }
  to {
    transform: translate3d(4%, 6%, 0) scale(1.08);
    opacity: 1;
  }
}

@keyframes drift-b {
  from {
    transform: translate3d(0, 0, 0) scale(1.05);
    opacity: 1;
  }
  to {
    transform: translate3d(-5%, -4%, 0) scale(1);
    opacity: 0.8;
  }
}

/* ── Allocation ring: stroke-dashoffset driven by a CSS variable ──────────────────────────────── */

.ring__seg {
  /* --dash and --offset are set per segment in the markup; --draw animates 1 -> 0. */
  stroke-dasharray: var(--dash);
  stroke-dashoffset: calc(var(--dash) * var(--draw, 1));
  transition: stroke-dashoffset var(--dur-hero) var(--ease-decel);
  transition-delay: calc(var(--i, 0) * 90ms);
}

.ring.is-visible .ring__seg {
  --draw: 0;
}

/* ── Compare tray morph: transform + radius, JS only toggles a class ──────────────────────────── */

.tray {
  transform-origin: bottom center;
  transition: transform var(--dur-emphasized) var(--ease-decel),
    opacity var(--dur-emphasized) var(--ease-decel),
    border-radius var(--dur-emphasized) var(--ease-decel);
}

.tray[data-state="collapsed"] {
  transform: translate3d(0, 8px, 0) scale(0.97);
  opacity: 0.9;
  border-radius: var(--r-full);
}

.tray[data-state="expanded"] {
  transform: none;
  opacity: 1;
  border-radius: var(--r-18);
}

/* ── Number ticker: no animation of its own; JS writes textContent per frame ──────────────────── */

.ticker {
  font-variant-numeric: tabular-nums;
  font-feature-settings: var(--fv-tabular);
}

/* ── Reduced motion: remove all of it, globally (D2.3 right-hand column) ──────────────────────── */

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }

  /* Content is VISIBLE by default, never left mid-transform waiting for an observer. */
  .js-on .reveal,
  .js-on .reveal.will-reveal,
  .reveal,
  .rise {
    opacity: 1 !important;
    transform: none !important;
  }

  .parallax--slow,
  .parallax--fast,
  .tilt {
    transform: none !important;
  }

  /* The ring shows its final state immediately rather than drawing. */
  .ring__seg {
    --draw: 0;
  }

  .tray[data-state="collapsed"] {
    transform: none;
    opacity: 1;
  }
}
