/* ============================================================================
   Motion styles for /static/js/motion/ (anime.js 4.5.0).
   Start states apply only while html.has-motion is set (boot.js) and the
   user has no reduced-motion preference. JS adds .motion-done when an
   entrance ends and drops its inline styles, so the plain CSS takes over.
   Without JS, every element shows in its final state.
   Subtrees inside [data-motion-skip] belong to a page module: the shared
   start states below exclude them with :not([data-motion-skip] *).
   Decorative layers (the progress head, the mechanism packet, the divider
   lines, the video shade, the spotlight, the brand glow) are pseudo-elements
   that exist only while html.has-motion is set.
   ========================================================================== */

/* ── SCROLL PROGRESS BAR ── */
/* starts at scaleX(0): without JS or with reduced motion it never shows */
#scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 3px;
  z-index: 101; /* above the nav (z-index 100), below the menus and modals (200+) */
  background: var(--teal);
  transform: scaleX(0);
  transform-origin: left;
  pointer-events: none;
}

/* word mask: each word rises inside a clipped box. The padding keeps the
   descenders, the accents, and the italic overhang inside the clip; the
   equal negative margin keeps the layout of the heading unchanged */
.motion-word-mask {
  overflow: clip;
  padding: 0.15em 0.15em 0.3em;
  margin: -0.15em -0.15em -0.3em;
}

@media (prefers-reduced-motion: no-preference) {
  /* ── REVEAL START STATES ── */
  /* the values match REVEAL_FROM in js/motion/shared.js. No transitions while
     JS drives the frames; hover transitions return with .motion-done */
  html.has-motion :is([data-motion^="reveal-"], [data-motion-stagger] > *):not(.motion-done, [data-motion-skip] *) {
    opacity: 0;
    transition: none;
  }

  html.has-motion :is(
    [data-motion="reveal-up"],
    [data-motion-stagger]:not([data-motion-stagger="left"], [data-motion-stagger="right"], [data-motion-stagger="scale"]) > *
  ):not(.motion-done, [data-motion-skip] *) {
    transform: translateY(60px);
  }

  html.has-motion :is([data-motion="reveal-left"], [data-motion-stagger="left"] > *):not(.motion-done, [data-motion-skip] *) {
    transform: translateX(-60px) rotate(-2deg);
  }

  html.has-motion :is([data-motion="reveal-right"], [data-motion-stagger="right"] > *):not(.motion-done, [data-motion-skip] *) {
    transform: translateX(60px) rotate(2deg);
  }

  html.has-motion :is([data-motion="reveal-scale"], [data-motion-stagger="scale"] > *):not(.motion-done, [data-motion-skip] *) {
    transform: scale(0.85);
  }

  /* ── SPLIT HEADINGS ── */
  /* hide the heading until splitText runs, then hide each word below its mask */
  html.has-motion [data-motion="split"]:not(.is-split, [data-motion-skip] *) {
    opacity: 0;
  }
  html.has-motion [data-motion="split"]:not(.motion-done, [data-motion-skip] *) [data-word] {
    transform: translateY(110%) rotate(4deg);
  }

  /* ── HERO ENTRANCE (#scanoair-experience) ── */
  /* the prototype faded these four blocks up 20px, 300 ms apart */
  html.has-motion [data-hero-item]:not(.motion-done) {
    opacity: 0;
    transform: translateY(20px);
  }

  /* ── PARALLAX (js/motion/home/parallax.js) ── */
  /* an inner image scales from its top edge, like its object-position */
  html.has-motion img[data-fx="parallax"] {
    transform-origin: center top;
  }

  /* ── CARD GROUPS (js/motion/home/cards.js) ── */
  /* the values match DESKTOP_PARAMS and MOBILE_PARAMS in cards.js */
  html.has-motion [data-fx^="cards-"] > :not(.motion-done) {
    opacity: 0;
    transition: none;
  }

  @media (min-width: 769px) {
    html.has-motion [data-fx="cards-cascade"] > :not(.motion-done) {
      transform: translateY(80px) rotate(4deg) scale(0.9);
    }
    html.has-motion [data-fx="cards-cascade"] > :nth-child(even):not(.motion-done) {
      transform: translateY(80px) rotate(-4deg) scale(0.9);
    }
    html.has-motion [data-fx="cards-grid"] > :not(.motion-done) {
      transform: rotate(-3deg) scale(0.7);
    }
    html.has-motion [data-fx="cards-grid"] > :nth-child(even):not(.motion-done) {
      transform: rotate(3deg) scale(0.7);
    }
    html.has-motion [data-fx="cards-pop"] > :not(.motion-done) {
      transform: scale(0);
    }
    /* the flips need depth: the parent gives its cards one vanishing point */
    html.has-motion :is([data-fx="cards-flip-x"], [data-fx="cards-flip-y"]) {
      perspective: 1200px;
    }
    html.has-motion [data-fx="cards-flip-x"] > :not(.motion-done) {
      transform: rotateX(70deg);
    }
    html.has-motion [data-fx="cards-flip-y"] > :not(.motion-done) {
      transform: rotateY(-80deg);
    }
    html.has-motion [data-fx="cards-slide"] > :not(.motion-done) {
      transform: translateX(-120px);
    }
    html.has-motion [data-fx="cards-slide"] > :nth-child(even):not(.motion-done) {
      transform: translateX(120px);
    }
  }

  @media (max-width: 768px) {
    html.has-motion [data-fx^="cards-"] > :not(.motion-done) {
      transform: translateY(30px);
    }
  }

  /* ── MECHANISM STEPS (js/motion/home/systemic.js) ── */
  /* the steps rise like the shared reveal-up */
  html.has-motion [data-fx="mech"] {
    position: relative;
  }
  html.has-motion [data-fx="mech"] > .sys-div4:not(.motion-done) {
    opacity: 0;
    transform: translateY(60px);
    transition: none;
  }

  /* the line runs through the badge centers (step padding 1.75rem/1.5rem,
     badge 38px) behind the steps. It draws from scale 0 with the scroll */
  html.has-motion .motion-mech-line {
    display: block;
    position: absolute;
    top: calc(1.75rem + 18px);
    left: calc(1.5rem + 19px);
    /* four columns, gap 1.25rem: first to last badge center */
    width: calc(75% + 0.9375rem);
    height: 2px;
    background: linear-gradient(90deg, var(--teal), var(--teal-glow));
    box-shadow: 0 0 10px var(--teal-glow), 0 0 24px rgba(0, 200, 216, 0.2);
    transform: scaleX(0);
    transform-origin: left center;
    pointer-events: none;
  }

  /* two columns (site.css switches the grid at 980px): first row only */
  @media (max-width: 980px) {
    html.has-motion .motion-mech-line {
      width: calc(50% + 0.625rem);
    }
  }

  /* mobile: a vertical line down the first column of badges */
  @media (max-width: 768px) {
    html.has-motion .motion-mech-line {
      top: calc(1.75rem + 19px);
      bottom: 0;
      left: calc(1.5rem + 18px);
      width: 2px;
      height: auto;
      background: linear-gradient(180deg, var(--teal), var(--teal-glow));
      transform: scaleY(0);
      transform-origin: center top;
    }
  }

  /* ── MECHANISM PACKET (js/motion/home/systemic.js) ── */
  /* a glowing dot rides the drawing head. The line scales with the scroll,
     so the dot divides that scale out again with --mech-progress; it fades
     in at the start and out at the end of the draw */
  html.has-motion .motion-mech-line::after {
    content: "";
    position: absolute;
    top: 50%;
    right: -5px;
    width: 10px;
    height: 10px;
    margin-top: -5px;
    border-radius: 50%;
    background: #e9fdff;
    box-shadow: 0 0 8px 2px var(--teal), 0 0 22px 6px var(--teal-glow);
    transform: scaleX(calc(1 / max(var(--mech-progress, 0), 0.001)));
    opacity: clamp(0, calc(min(var(--mech-progress, 0), 1 - var(--mech-progress, 0)) * 30), 1);
  }
  @media (max-width: 768px) {
    html.has-motion .motion-mech-line::after {
      top: auto;
      right: auto;
      bottom: -5px;
      left: 50%;
      margin: 0 0 0 -5px;
      transform: scaleY(calc(1 / max(var(--mech-progress, 0), 0.001)));
    }
  }

  /* ── SCROLL PROGRESS HEAD ── */
  /* a light at the moving end of the bar. The bar scales, so the dot divides
     that scale out with --progress (main.js); it shows once the bar moves */
  html.has-motion #scroll-progress::after {
    content: "";
    position: absolute;
    top: 50%;
    right: -4px;
    width: 8px;
    height: 8px;
    margin-top: -4px;
    border-radius: 50%;
    background: #e9fdff;
    box-shadow: 0 0 8px 2px var(--teal), 0 0 18px 5px var(--teal-glow);
    transform: scaleX(calc(1 / max(var(--progress, 0), 0.001)));
    opacity: clamp(0, calc(var(--progress, 0) * 40), 1);
  }

  /* ── MEDIA ZOOM (intro.js, curtains.js) ── */
  /* the CSS scale property composes with the parallax transform, so the
     zoom and the parallax never write the same property */
  html.has-motion .motion-zoom {
    scale: var(--motion-zoom, 1);
  }

  /* ── HERO INTRO (js/motion/home/intro.js) ── */
  html.has-motion :is([data-intro="eyebrow"], [data-intro="lead"], [data-intro="plate"]):not(.motion-done) {
    opacity: 0;
  }
  html.has-motion :is([data-intro="chips"], [data-intro="ctas"]):not(.motion-done) > * {
    opacity: 0;
    transition: none;
  }
  /* hide the heading until splitText runs, then hide each char below its mask */
  html.has-motion [data-intro="title"]:not(.is-split, .motion-done) {
    opacity: 0;
  }
  html.has-motion [data-intro="title"]:not(.motion-done) [data-char] {
    transform: translateY(110%) rotate(12deg);
  }
  html.has-motion [data-intro="portrait"]:not(.motion-done) {
    clip-path: inset(100% 0 0 0);
  }

  /* ── CURTAINS (js/motion/home/curtains.js) ── */
  html.has-motion [data-curtain="up"]:not(.motion-done) {
    clip-path: inset(100% 0 0 0);
  }
  html.has-motion [data-curtain="left"]:not(.motion-done) {
    clip-path: inset(0 100% 0 0);
  }

  /* ── EYEBROW LABELS (js/motion/home/eyebrows.js) ── */
  /* the dash draws from its outer end toward the text */
  html.has-motion [data-eyebrow]:not(.motion-done) .eyebrow-line {
    transform: scaleX(0);
    transform-origin: left center;
  }
  html.has-motion .eyebrow-center[data-eyebrow]:not(.motion-done) > .eyebrow-line:first-child {
    transform-origin: right center;
  }

  /* ── ACCENT SHIMMER (shared.js shimmerAccent) ── */
  /* one light sweep through the accent words. Both ends of the sweep show
     the solid accent color, and the class goes away on animationend */
  html.has-motion .motion-shimmer {
    background-image: linear-gradient(100deg, currentColor 38%, #f0feff 50%, currentColor 62%);
    background-size: 300% 100%;
    background-position: 100% 0;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    animation: motion-shimmer 900ms cubic-bezier(0.45, 0, 0.2, 1) var(--shimmer-delay, 0ms) both;
  }

  /* ── DIVIDERS (js/motion/home/dividers.js) ── */
  /* while a divider draws, its border hides and a pseudo-element line grows
     from the center. At --divider-draw 1 the glow is 0 and the line equals
     the border, so the handoff on .motion-done changes no pixel */
  html.has-motion [data-divider]:not(.motion-done) {
    position: relative;
  }
  html.has-motion :is([data-divider="top"], [data-divider="both"]):not(.motion-done) {
    border-top-color: transparent;
  }
  html.has-motion :is([data-divider="bottom"], [data-divider="both"]):not(.motion-done) {
    border-bottom-color: transparent;
  }
  html.has-motion :is([data-divider="top"], [data-divider="both"]):not(.motion-done)::before,
  html.has-motion :is([data-divider="bottom"], [data-divider="both"]):not(.motion-done)::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(0, 200, 216, var(--divider-glow, 0)) 50%, transparent), var(--line);
    transform: scaleX(var(--divider-draw, 0));
    transform-origin: center;
    pointer-events: none;
  }
  html.has-motion :is([data-divider="top"], [data-divider="both"]):not(.motion-done)::before {
    top: -1px;
  }
  html.has-motion :is([data-divider="bottom"], [data-divider="both"]):not(.motion-done)::after {
    bottom: -1px;
  }

  /* ── VIDEO EXIT (js/motion/home/hero.js, desktop) ── */
  /* a dark shade over the video, under the text (z-index 3) */
  @media (min-width: 769px) {
    html.has-motion [data-fx="video-exit"]::after {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 2;
      background: var(--bg);
      opacity: var(--exit-shade, 0);
      pointer-events: none;
    }
  }

  /* ── REVIEWS (js/motion/home/reviews.js) ── */
  /* the stars are inline text: inline-block lets them scale while they pop */
  html.has-motion [data-fx="stars"]:not(.motion-done) > * {
    display: inline-block;
    transform: scale(0);
  }
  html.has-motion [data-fx="review-card"]:not(.motion-done) {
    opacity: 0;
    transform: perspective(1200px) translateY(90px) rotateX(18deg);
    transform-origin: center bottom;
    transition: none;
  }

  /* ── FOOTER FINALE (js/motion/home/footer.js) ── */
  html.has-motion [data-fx="footer"] > :not(.motion-done) {
    opacity: 0;
    transform: translateY(50px);
    transition: none;
  }
  html.has-motion [data-fx="footer-brand"]:not(.motion-done) {
    position: relative;
    transform: scale(0.9);
  }
  /* one teal glow pulse around the logo chip, driven by --brand-glow */
  html.has-motion [data-fx="footer-brand"]:not(.motion-done)::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    box-shadow: 0 0 0 1px rgba(0, 200, 216, 0.6), 0 0 32px 8px var(--teal-glow);
    opacity: var(--brand-glow, 0);
    pointer-events: none;
  }

  /* ── POINTER DEPTH (js/motion/home/pointer.js) ── */
  @media (min-width: 769px) and (hover: hover) and (pointer: fine) {
    /* the parent gives its tilting cards one vanishing point */
    html.has-motion :is([data-tilt-group], :has(> [data-tilt])) {
      perspective: 1200px;
    }
    /* JS drives every frame while the pointer acts: no CSS transitions */
    html.has-motion .motion-pointer-active {
      position: relative;
      transition: none;
    }
    /* the spotlight follows --spot-x / --spot-y on the card itself */
    html.has-motion :is([data-tilt], [data-tilt-group] > *)::before {
      content: "";
      position: absolute;
      inset: 0;
      z-index: 1;
      border-radius: inherit;
      background: radial-gradient(380px circle at var(--spot-x, 50%) var(--spot-y, 50%), rgba(0, 200, 216, 0.16), transparent 60%);
      opacity: 0;
      transition: opacity 0.35s ease;
      pointer-events: none;
    }
    html.has-motion .motion-spot::before {
      opacity: 1;
    }
  }
}

/* the accent sweep ends on the solid accent color */
@keyframes motion-shimmer {
  from {
    background-position: 100% 0;
  }
  to {
    background-position: 0 0;
  }
}

/* a visually hidden copy of a decoding label, for screen readers */
.motion-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* the mechanism line is decoration that only makes sense animated:
   without motion it does not show */
.motion-mech-line {
  display: none;
}

/* ── REDUCED MOTION ── */
/* stop the looping decoration (marquee, rings, pulses, scroll cue) and show
   the hero underline at full width */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
  *,
  *::before,
  *::after {
    animation: none !important;
    transition-duration: 0.01ms !important;
  }
  .xp-underline {
    transform: scaleX(1);
  }
}
