/* ==========================================================================
   Landing page motion.

   Written by hand rather than pulling in p5.js: the page is 44KB of HTML+CSS
   today and a CDN canvas library would add roughly a megabyte to the first
   thing a customer ever loads. Motion here is CSS transitions driven by one
   IntersectionObserver, plus a ~2KB hand-written canvas field for the hero.

   Rules this file follows:
     - motion clarifies structure, it never competes with reading
     - nothing animates below the fold until it is actually scrolled to
     - every effect degrades to the static page if JS never runs
     - prefers-reduced-motion disables all of it, not just some
   ========================================================================== */

/* ---------- scroll reveal ----------
   Elements start slightly low and transparent, then settle when observed. The
   .is-in class is added by JS; without JS the :not(.motion-on) guard below means
   nothing is ever hidden, so the page stays fully readable. */

.motion-on .reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .62s cubic-bezier(.16, 1, .3, 1),
              transform .62s cubic-bezier(.16, 1, .3, 1);
  will-change: opacity, transform;
}
.motion-on .reveal.is-in {
  opacity: 1;
  transform: none;
}

/* Children stagger so a group resolves as a sequence, not a single block flash.
   Kept short — 60ms steps read as "settling", 200ms would read as "waiting". */
.motion-on .reveal-group > * {
  opacity: 0;
  transform: translateY(12px);
  transition: opacity .55s cubic-bezier(.16, 1, .3, 1),
              transform .55s cubic-bezier(.16, 1, .3, 1);
}
.motion-on .reveal-group.is-in > * { opacity: 1; transform: none; }
.motion-on .reveal-group.is-in > *:nth-child(1) { transition-delay: 0ms; }
.motion-on .reveal-group.is-in > *:nth-child(2) { transition-delay: 60ms; }
.motion-on .reveal-group.is-in > *:nth-child(3) { transition-delay: 120ms; }
.motion-on .reveal-group.is-in > *:nth-child(4) { transition-delay: 180ms; }
.motion-on .reveal-group.is-in > *:nth-child(5) { transition-delay: 240ms; }
.motion-on .reveal-group.is-in > *:nth-child(6) { transition-delay: 300ms; }

/* ---------- hero entrance ----------
   Runs immediately on load rather than on scroll: the hero is already in view,
   so waiting for an observer would show a blank hero for one frame. */

@keyframes lp-rise {
  from { opacity: 0; transform: translateY(16px); }
  to   { opacity: 1; transform: none; }
}

.motion-on .lp-eyebrow { animation: lp-rise .6s cubic-bezier(.16,1,.3,1) both; }
.motion-on .lp-h1      { animation: lp-rise .7s cubic-bezier(.16,1,.3,1) .08s both; }
.motion-on .lp-lede    { animation: lp-rise .7s cubic-bezier(.16,1,.3,1) .18s both; }
.motion-on .lp-hero .lp-cta-row { animation: lp-rise .7s cubic-bezier(.16,1,.3,1) .28s both; }
.motion-on .lp-microcopy { animation: lp-rise .7s cubic-bezier(.16,1,.3,1) .36s both; }
.motion-on .lp-proof   { animation: lp-rise .8s cubic-bezier(.16,1,.3,1) .46s both; }

/* The gradient in the headline drifts very slowly, so the hero is never fully
   static without ever pulling the eye off the words. */
@keyframes lp-hue-drift {
  0%   { background-position: 0% 50%; }
  100% { background-position: 100% 50%; }
}
.motion-on .lp-h1 em {
  background: linear-gradient(100deg, var(--accent-hi), #a5b4fc, var(--accent-hi));
  background-size: 220% 100%;
  -webkit-background-clip: text; background-clip: text;
  -webkit-text-fill-color: transparent;
  animation: lp-hue-drift 14s linear infinite alternate;
}

/* ---------- counting numbers ----------
   The proof strip animates from 0 to its real value. Tabular figures stop the
   width jitter that otherwise makes counters wobble mid-count. */

.lp-proof-n { font-variant-numeric: tabular-nums; }

/* ---------- hero field canvas ----------
   Sits behind the hero text, drawn by js/landing-motion.js. Fades in after the
   first frame so there is never a visible blank rectangle. */

.lp-field {
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.1s ease;
}
.lp-field.is-ready { opacity: 1; }
.lp-hero { position: relative; }
.lp-hero > *:not(.lp-field) { position: relative; z-index: 1; }

/* ---------- interactive polish ---------- */

/* Step numbers pulse once as their row arrives, so the sequence reads in order. */
@keyframes lp-step-pop {
  0%   { transform: scale(.82); opacity: 0; }
  60%  { transform: scale(1.06); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}
.motion-on .lp-steps.is-in .lp-step-n {
  animation: lp-step-pop .5s cubic-bezier(.34,1.56,.64,1) both;
}
.motion-on .lp-steps.is-in .lp-step:nth-child(2) .lp-step-n { animation-delay: .12s; }
.motion-on .lp-steps.is-in .lp-step:nth-child(3) .lp-step-n { animation-delay: .24s; }

/* Cards lift on hover — pointer only, so touch devices do not get a stuck
   hover state after a tap. */
@media (hover: hover) and (pointer: fine) {
  .lp-price {
    transition: transform .2s cubic-bezier(.16,1,.3,1),
                border-color .2s ease, background .2s ease;
  }
  .lp-price:hover {
    transform: translateY(-3px);
    border-color: rgba(255,255,255,.16);
  }
  .lp-price.is-lead:hover { border-color: rgba(113,112,255,.5); }
}

/* The closing CTA glow breathes slowly. Opacity only — no layout, no repaint
   of anything but this element. */
@keyframes lp-breathe {
  0%, 100% { opacity: .55; }
  50%      { opacity: 1; }
}
.motion-on .lp-close::before {
  content: "";
  position: absolute; inset: 0;
  border-radius: inherit;
  background: radial-gradient(120% 140% at 50% 0%, rgba(94,106,210,.16), transparent 62%);
  animation: lp-breathe 7s ease-in-out infinite;
  pointer-events: none;
}
.lp-close { position: relative; overflow: hidden; }
.lp-close > * { position: relative; z-index: 1; }

/* ---------- accessibility and fallbacks ----------
   One switch turns everything off. Anything added above must be listed here. */

@media (prefers-reduced-motion: reduce) {
  .motion-on .reveal,
  .motion-on .reveal-group > * {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
  .motion-on .lp-eyebrow,
  .motion-on .lp-h1,
  .motion-on .lp-lede,
  .motion-on .lp-hero .lp-cta-row,
  .motion-on .lp-microcopy,
  .motion-on .lp-proof,
  .motion-on .lp-steps.is-in .lp-step-n {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  .motion-on .lp-h1 em,
  .motion-on .lp-close::before { animation: none !important; }
  .lp-field { display: none !important; }
  .lp-price { transition: none !important; }
}
