/* MissionFi — approved animation layer (effects-lab prototype v6, verbatim).
   Progressive enhancement: every rule here is scoped under `html.fx`, a class
   assets/animations.js adds synchronously and only when prefers-reduced-motion
   is not "reduce". Without JS, or with reduced motion, none of the hidden /
   offset states apply and the page renders exactly like the base sheet. */

/* ============================================================
   MissionFi — FINAL effects layer
   The v2 "Intermedia" motion system in full, with one part
   swapped for its v3 "Expresiva" counterpart: the hero opening
   (H1 word masks + <em> underline + the phone's spring entrance).
   Everything v3 does outside the hero — 3D tilt, magnetic
   buttons, photo wipes, the CTA gradient breath, its general
   reveals — is deliberately absent.

   Progressive enhancement: every rule below is scoped under
   html.fx, a class the script at the end of this document adds
   synchronously, and only when prefers-reduced-motion is not
   "reduce". Without JS, or with reduced motion, none of this
   applies and the page renders exactly like v0-base — nothing
   here edits the base markup or the base <style> block above.
   ============================================================ */

/* ---------- reveal engine: fade + translateY(14px), once ---------- */
html.fx .fx-reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .5s ease-out, transform .5s ease-out;
  transition-delay: var(--fx-d, 0ms);
}
html.fx .fx-reveal.fx-in {
  opacity: 1;
  transform: translateY(0);
}

/* fade-only variant — used only where the script itself already
   drives this element's `transform` (the floating cards, via
   scroll parallax), so the reveal never fights that transform. */
html.fx .fx-fade {
  opacity: 0;
  transition: opacity .5s ease-out;
  transition-delay: var(--fx-d, 0ms);
}
html.fx .fx-fade.fx-in {
  opacity: 1;
}

/* ---------- progress chart bars: grow on reveal, no layout shift ---------- */
html.fx .mf-p-chart__bar {
  transform: scaleY(0);
  transform-origin: bottom;
  transition: transform .5s cubic-bezier(.22, .61, .36, 1);
}
html.fx .mf-fcard.fx-in .mf-p-chart__bar { transform: scaleY(1); }
html.fx .mf-p-chart__col:nth-child(1) .mf-p-chart__bar { transition-delay: 0ms; }
html.fx .mf-p-chart__col:nth-child(2) .mf-p-chart__bar { transition-delay: 100ms; }
html.fx .mf-p-chart__col:nth-child(3) .mf-p-chart__bar { transition-delay: 200ms; }

/* ---------- debt bar segments: grow from the left, in sequence ---------- */
html.fx .mf-p-bar span {
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .45s cubic-bezier(.22, .61, .36, 1);
}
html.fx .mf-fcard.fx-in .mf-p-bar span { transform: scaleX(1); }
html.fx .mf-p-bar__a { transition-delay: 0ms; }
html.fx .mf-p-bar__b { transition-delay: 120ms; }
html.fx .mf-p-bar__c { transition-delay: 240ms; }
html.fx .mf-p-bar__rest { transition-delay: 360ms; }

/* ---------- idle float on the floating data cards ----------
   Runs on the inner .mf-panel(s) rather than on .mf-fcard itself,
   so it never fights the scroll-parallax transform the script
   applies directly to .mf-fcard — two different elements, two
   independent transforms, nothing overwritten frame to frame. */
@keyframes mfIdleFloat {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}
html.fx .mf-panel { animation: mfIdleFloat 6s ease-in-out infinite; }
html.fx .mf-fcard--stand .mf-panel { animation-delay: -0.6s; }
html.fx .mf-fcard--prog  .mf-panel { animation-delay: -2.1s; }
html.fx .mf-fcard--subs  .mf-panel { animation-delay: -3.6s; }
html.fx .mf-fcard--debt  .mf-panel { animation-delay: -5.1s; }

/* ---------- nav shadow past 8px of scroll ---------- */
html.fx .mf-nav { transition: box-shadow .2s ease; }
html.fx .mf-nav.fx-nav-shadow { box-shadow: 0 8px 24px rgba(20, 40, 70, .12); }

/* ---------- hover polish, pointer devices only ---------- */
@media (hover: hover) {
  html.fx .mf-btn-primary,
  html.fx .mf-btn-pill,
  html.fx .mf-btn-white {
    transition: transform .18s ease, box-shadow .18s ease, background .15s ease;
  }
  html.fx .mf-btn-primary:hover,
  html.fx .mf-btn-pill:hover,
  html.fx .mf-btn-white:hover {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
  }

  html.fx .mf-feat__cta img,
  html.fx .mf-persona__cta img,
  html.fx .mf-btn-primary img,
  html.fx .mf-btn-white img {
    transition: transform .18s ease;
  }
  html.fx .mf-feat__cta:hover img,
  html.fx .mf-persona__cta:hover img,
  html.fx .mf-btn-primary:hover img,
  html.fx .mf-btn-white:hover img {
    transform: translateX(4px);
  }

  html.fx .mf-persona,
  html.fx .mf-mcard {
    transition: transform .22s ease, box-shadow .22s ease;
  }
  html.fx .mf-persona:hover,
  html.fx .mf-mcard:hover {
    transform: translateY(-3px);
    box-shadow: var(--shadow-card);
  }
}

/* ============================================================
   HERO ENTRANCE — the one block ported from v3 "Expresiva".
   Namespaced fx6-* so nothing collides with v2's fx-* hooks, but
   scoped under the same html.fx root so it lives and dies with
   the rest of this layer. Every class below is added by the
   script and removed again once the sequence settles: the hero
   at rest is the base markup, character for character.
   ============================================================ */
html.fx {
  --fx6-e:     cubic-bezier(.2, .9, .3, 1.05);   /* primary — a hair of overshoot */
  --fx6-e-out: cubic-bezier(.33, 1, .68, 1);     /* the underline draw */
}

/* opacity-only hold: the headline waits here until the script has
   split it into words, the phone until its transform is armed */
html.fx .fx6-hold { opacity: 0; }

/* ---------- hero H1: word masks ----------
   clip-path, NOT overflow:hidden — overflow would move the
   inline-block's baseline to its bottom margin edge and reflow
   the headline. clip-path masks without touching layout. */
html.fx .fx6-w {
  display: inline-block;
  position: relative;
  clip-path: inset(-45% -12% 0 -12%);
}
html.fx .fx6-wi {
  display: inline-block;
  transform: translate3d(0, 115%, 0);
  transition: transform .58s var(--fx6-e);
  transition-delay: var(--fx6-d, 0ms);
}
html.fx .fx6-w.fx6-in > .fx6-wi { transform: none; }

/* the <em> remate: a rule that draws under "next move." and then
   travels off to the right — a sweep, so it leaves no trace. */
html.fx .fx6-w--u::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: .10em;
  height: .058em; min-height: 2px;
  border-radius: 99px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
}
html.fx .fx6-w--u.fx6-draw::after {
  transform: scaleX(1);
  transition: transform .40s var(--fx6-e-out);
  transition-delay: var(--fx6-d2, 0ms);
}
html.fx .fx6-w--u.fx6-undraw::after {
  transform: scaleX(0);
  transform-origin: 100% 50%;
  transition: transform .34s cubic-bezier(.55, 0, .85, .25);
  transition-delay: var(--fx6-d3, 0ms);
}

/* ---------- hero phone: spring entrance ----------
   This animates the same <img> the scroll parallax below owns.
   The two never share a frame: the script keeps the parallax off
   the phone until this transition has ended and .fx6-phone has
   been removed, and both systems agree on translateY(0) at that
   instant, so the handoff is continuous.

   v3 put a `perspective` on the wrapper for its tilt stage; it is
   not carried over. The entrance is flat (translate3d with z = 0),
   so perspective changes nothing visually, but it does force the
   phone .webp into a 3D rendering context and resamples it a
   fraction differently — a ~4.5k-pixel drift from the base at
   620px. Dropping the rule puts the resting phone back to
   byte-for-byte base. */
html.fx .fx6-phone {
  opacity: 0;
  /* v3's spring is translate + scale(.94) + rotate; the scale is
     dropped here on purpose. Painting this .webp under ANY scale
     once — .99, .94, 1.06, all identical in the measurements —
     switches Chrome to a resampled decode of it and never switches
     back: the phone rests ~4.4k pixels softer than v0-base for the
     life of the page, at every width. Nothing on the CSS or JS side
     undoes it (re-decode, src swap, node clone, layer nudges were
     all tried). The 46px rise and the 1.4deg settle carry the same
     gesture and leave the resting phone identical to the base. */
  transform: translate3d(0, 46px, 0) rotate(-1.4deg);
  transition:
    opacity   .86s var(--fx6-e),
    transform .86s var(--fx6-e);
  transition-delay: var(--fx6-d, 0ms);
  will-change: transform, opacity;
}
html.fx .fx6-phone.fx6-in { opacity: 1; transform: none; }

/* The script never arms .fx6-phone below 620px (see the zoom note
   there). This is the belt for that braces: if the viewport is
   resized under the breakpoint while the spring is mid-flight, the
   image drops its layer immediately rather than carrying a resampled
   decode of itself for the rest of the session. */
@media (max-width: 619.98px) {
  html.fx .fx6-phone,
  html.fx .fx6-phone.fx6-in { transform: none; will-change: auto; }
}

/* ---------- reduced motion: defense in depth ----------
   The script never adds html.fx when prefers-reduced-motion is
   "reduce", so in practice this is a no-op; it only guards against
   html.fx staying on a page whose motion preference is honored by
   the OS mid-session. */
@media (prefers-reduced-motion: reduce) {
  html.fx .fx-reveal,
  html.fx .fx-fade,
  html.fx .mf-p-chart__bar,
  html.fx .mf-p-bar span,
  html.fx .mf-panel,
  html.fx .fx6-hold,
  html.fx .fx6-wi,
  html.fx .fx6-phone {
    transition: none !important;
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  html.fx .fx6-w--u::after { display: none !important; }
}
