/* Motion layer.
 *
 * Everything here animates only `opacity` and `transform`. Those two are the
 * properties a browser can hand to the compositor, so they run off the main
 * thread and stay smooth while the page is scrolling. Animating width, top,
 * margin or box-shadow instead is what makes expressive pages stutter.
 *
 * Two guards wrap the whole file:
 *   .motion-ready          set by motion-init.js, so a JavaScript failure
 *                          leaves the page visible rather than blank.
 *   prefers-reduced-motion motion is skipped entirely for anyone who has
 *                          asked their system for less of it. Screen motion
 *                          triggers nausea and vertigo for some people, so
 *                          this is not optional.
 */

@media (prefers-reduced-motion: no-preference) {
  /* ---- Reveal on scroll -------------------------------------------------
     The selector list is mirrored in motion.js. It lives in CSS as well so
     the initial hidden state is painted with the first frame; setting it from
     JavaScript would show the content first and then snap it away. */
  .motion-ready .hero-copy > *,
  .motion-ready .section-heading > *,
  .motion-ready .product-showcase,
  .motion-ready .terminal-proof > div,
  .motion-ready .terminal-actions,
  .motion-ready .terminal-availability,
  .motion-ready .terminal-preview-stage,
  .motion-ready .terminal-preview-disclosure,
  .motion-ready .terminal-journal-copy > *,
  .motion-ready .terminal-journal-visual,
  .motion-ready .step-card,
  .motion-ready .feature-card,
  .motion-ready .price-card,
  .motion-ready .trust-item,
  .motion-ready .faq-list details,
  .motion-ready .final-cta .section-shell > * {
    opacity: 0;
    transform: translate3d(0, 28px, 0);
    /* A long, heavily-eased curve reads as "settling into place" rather than
       "sliding in", which is what separates considered motion from template
       motion. */
    transition:
      opacity 800ms cubic-bezier(0.16, 1, 0.3, 1),
      transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
    transition-delay: var(--reveal-delay, 0ms);
  }

  /* The class is doubled deliberately. The hiding rule above and a single
     .is-revealed have identical specificity, so which one wins would depend on
     source order alone — too fragile for a rule whose failure mode is an
     invisible page. Doubling raises specificity and settles it outright. */
  .motion-ready .is-revealed.is-revealed {
    opacity: 1;
    transform: none;
  }

  /* The hero is above the fold and must not wait for an observer. */
  .motion-ready .hero-copy > * {
    animation: motion-rise 900ms cubic-bezier(0.16, 1, 0.3, 1) both;
    animation-delay: var(--reveal-delay, 0ms);
    opacity: 1;
    transform: none;
    transition: none;
  }

  @keyframes motion-rise {
    from {
      opacity: 0;
      transform: translate3d(0, 30px, 0);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }

  /* ---- Hero: float -----------------------------------------------------
     `translate` is its own property, applied before `transform`, so the float
     composes with the parallax offset instead of overwriting it. Animating
     `transform` here would cancel the parallax outright. */
  @keyframes hero-float {
    0%,
    100% {
      translate: 0 0;
    }
    50% {
      translate: 0 -12px;
    }
  }

  .motion-ready .hero-stage {
    animation: hero-float 7.5s ease-in-out infinite;
  }

  /* ---- Drawn underline on section headings ---------------------------- */
  .motion-ready .section-heading h2 {
    position: relative;
  }

  .motion-ready .section-heading h2::after {
    content: "";
    position: absolute;
    left: 0;
    bottom: -10px;
    width: 68px;
    height: 3px;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--accent), var(--teal));
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 900ms cubic-bezier(0.16, 1, 0.3, 1) 220ms;
  }

  .motion-ready .section-heading h2.is-drawn::after {
    transform: scaleX(1);
  }

  /* One section heading is centred (.center-heading). Pinning the rule to
     left: 0 there left it hanging under the left edge of a centred block,
     which reads as a mistake rather than an accent. Centre it, and grow it
     from the middle so the draw matches the alignment. */
  .motion-ready .center-heading h2::after {
    left: 50%;
    transform: translateX(-50%) scaleX(0);
    transform-origin: center;
  }

  .motion-ready .center-heading h2.is-drawn::after {
    transform: translateX(-50%) scaleX(1);
  }

  /* ---- Parallax --------------------------------------------------------
     motion.js writes --parallax-y. Kept on its own transform so it composes
     with the reveal transform above instead of fighting it. */
  .motion-ready [data-parallax] {
    will-change: transform;
  }

  .motion-ready .product-shot img,
  .motion-ready .hero-stage {
    transform: translate3d(0, var(--parallax-y, 0px), 0);
  }

  /* ---- Hover depth -----------------------------------------------------
     Buttons already had this; the cards did not, which is why everything
     except the buttons felt dead to the cursor. */
  .motion-ready .price-card,
  .motion-ready .feature-card,
  .motion-ready .step-card {
    transition:
      transform 260ms cubic-bezier(0.16, 1, 0.3, 1),
      box-shadow 260ms cubic-bezier(0.16, 1, 0.3, 1),
      opacity 800ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .motion-ready .price-card:hover,
  .motion-ready .feature-card:hover,
  .motion-ready .step-card:hover {
    transform: translate3d(0, -6px, 0);
    box-shadow: 0 26px 60px rgba(0, 0, 0, 0.1);
  }

  .motion-ready .product-shot {
    overflow: hidden;
  }

  .motion-ready .product-shot img {
    transition: transform 700ms cubic-bezier(0.16, 1, 0.3, 1);
  }

  .motion-ready .product-panel:hover .product-shot img {
    transform: translate3d(0, var(--parallax-y, 0px), 0) scale(1.03);
  }

  /* Counting price digits shift width constantly; a tabular figure keeps the
     card from twitching while the number runs. */
  .motion-ready [data-price-display] span {
    font-variant-numeric: tabular-nums;
  }
}

/* ---- Parallax is desktop-only ------------------------------------------
   Touch scrolling is driven by the compositor and can outrun scroll events,
   so parallax on a phone reads as lag on the exact gesture people use most.
   motion.js also skips the maths below this width. */
@media (max-width: 900px) {
  .motion-ready .product-shot img,
  .motion-ready .hero-stage {
    transform: none;
  }

  .motion-ready .product-panel:hover .product-shot img {
    transform: scale(1.02);
  }
}

/* ---- Focus, for keyboard users -----------------------------------------
   Outside the reduced-motion guard: a visible focus ring is not decoration. */
.motion-ready a:focus-visible,
.motion-ready button:focus-visible,
.motion-ready summary:focus-visible,
.motion-ready input:focus-visible,
.motion-ready select:focus-visible,
.motion-ready textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 6px;
}
