/* ═══════════════════════════════════════════════════════════════════════════
   TopMark FX-Motion — cinematic, "Unseen"-grade micro-interactions.
   Additive layer on top of fx.css. Load AFTER fx.css, before fx-motion.js.

   Everything here is OPT-IN via data-attributes, GPU-only (transform/opacity),
   gated on `html.fx-anim` (added by fx.js) so if JS never runs, content is
   visible — same fail-open contract as [data-fx-reveal]. Uses TopMark tokens
   only: --fx-ink / --fx-accent / --fx-bg / --fx-serif etc. No new colours.

   Components:
     1. [data-fx-split]     — split-text reveal (words / chars / lines)
     2. [data-fx-magnetic]  — magnetic hover pull (JS-driven; base easing here)
     3. [data-fx-parallax]  — reveal-scale container + parallax-shift image
     4. .fx-curtain         — page-transition / intro grid mask
   ═══════════════════════════════════════════════════════════════════════════ */

:root {
  /* Premium easing curves (per the brief) — available as tokens so page-level
     tweaks stay on-system. The existing --fx-ease is out-quint (.22,1,.36,1). */
  --fx-ease-quart: cubic-bezier(0.25, 1, 0.5, 1);   /* snappy / premium (out-quart)  */
  --fx-ease-giga:  cubic-bezier(0.76, 0, 0.24, 1);  /* cinematic / sustained (in-out) */
  --fx-ease-expo:  cubic-bezier(0.16, 1, 0.3, 1);   /* magnetic / snappy (out-expo)   */

  /* Motion timing knobs — override per element with the matching data-attr. */
  --fx-split-stagger: 42ms;   /* delay between successive units */
  --fx-split-dur:     0.9s;   /* travel duration of each unit  */
  --fx-curtain-cols:  5;      /* curtain panel count           */
}

/* ─────────────────────── 1 · Split-text reveal ─────────────────────── */
/* JS wraps text in per-unit masks. Each unit sits below its clip line and
   slides up. Stagger is driven by a per-unit `--i` custom property so the
   whole thing is one CSS transition — no per-unit JS timers. */
html.fx-anim [data-fx-split] { --fx-split-i: 0; }

/* The mask: clips the unit as it travels up from below the baseline. */
.fx-split-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: top;
  /* the unit only ever travels UP from below, so we just need bottom room so
     descenders (g, y, p) and italic serif tails aren't clipped at rest */
  padding: 0.02em 0.03em 0.16em;
  margin: -0.02em -0.03em -0.16em;
}
.fx-split-line { display: block; }             /* line mode: one mask per line */
.fx-split-line .fx-split-inner { display: block; }

html.fx-anim [data-fx-split] .fx-split-inner {
  display: inline-block;
  transform: translate3d(0, 115%, 0);
  /* Out-expo, not in-out: the in-out curve held each unit invisible for the
     first ~150ms of its slot, which read as stutter across a stagger. Out-expo
     starts moving the instant its delay elapses, then settles long and soft. */
  transition:
    transform var(--fx-split-dur) var(--fx-ease-expo);
  transition-delay: calc(var(--i, 0) * var(--fx-split-stagger));
  will-change: transform;
}
/* optional gentle fade alongside the rise (add data-fx-split-fade) */
html.fx-anim [data-fx-split][data-fx-split-fade] .fx-split-inner {
  opacity: 0;
  transition:
    transform var(--fx-split-dur) var(--fx-ease-expo),
    opacity   calc(var(--fx-split-dur) * .6) var(--fx-ease-expo);
  transition-delay: calc(var(--i, 0) * var(--fx-split-stagger));
}
html.fx-anim [data-fx-split].is-in .fx-split-inner {
  transform: translate3d(0, 0, 0);
  opacity: 1;
}
/* once settled, drop will-change (JS also clears it after transitionend) */
html.fx-anim [data-fx-split].fx-split-done .fx-split-inner { will-change: auto; }

/* ─────────────────────── 2 · Magnetic hover ─────────────────────── */
/* Displacement is set inline by JS. We only own the SPRING-BACK easing here:
   while the pointer moves we follow instantly (JS sets a short transition),
   on leave we ease home with out-expo. Deliberately tiny — this is the
   difference between "premium pull" and the "chasing button" that was pulled
   from this site before. */
[data-fx-magnetic] {
  transform: translate3d(0, 0, 0);
  transition: transform 0.6s var(--fx-ease-expo);
}
[data-fx-magnetic-text] {
  display: inline-block;
  transform: translate3d(0, 0, 0);
  transition: transform 0.6s var(--fx-ease-expo);
}

/* ─────────────────────── 3 · Parallax reveal cards ─────────────────────── */
/* Container reveal-scales on enter; the inner image shifts on scroll at a
   different rate (JS drives --fx-py). Two separate elements = two separate
   transforms, so they never fight. */
html.fx-anim [data-fx-parallax] {
  overflow: hidden;
  transform: scale(0.94) translate3d(0, 0, 0);
  opacity: 0;
  transition:
    transform 1s var(--fx-ease-quart),
    opacity   1s var(--fx-ease-quart);
  will-change: transform, opacity;
}
html.fx-anim [data-fx-parallax].is-in {
  transform: scale(1) translate3d(0, 0, 0);
  opacity: 1;
}
html.fx-anim [data-fx-parallax].fx-parallax-settled { will-change: auto; }

/* the shifting layer — scale(1.16) gives bleed for the ±shift so no gaps show */
[data-fx-parallax] .fx-parallax-img,
[data-fx-parallax] > img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transform: translate3d(0, var(--fx-py, 0px), 0) scale(1.16);
}
[data-fx-parallax].fx-parallax-active .fx-parallax-img,
[data-fx-parallax].fx-parallax-active > img { will-change: transform; }

/* ─────────────────────── 4 · Page-transition curtain ─────────────────────── */
/* A grid of ink panels that sweep the viewport. Transform-only (translateY),
   so it composites cleanly. Idle = fully lifted & non-interactive. */
.fx-curtain {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  grid-template-columns: repeat(var(--fx-curtain-cols), 1fr);
  pointer-events: none;
  visibility: hidden;
}
.fx-curtain.is-active { visibility: visible; }
.fx-curtain__panel {
  background: var(--fx-ink);
  transform: translate3d(0, -101%, 0);   /* parked above the viewport */
  will-change: transform;
}
/* covering: panels drop in, staggered by column */
.fx-curtain.is-covering .fx-curtain__panel {
  transform: translate3d(0, 0, 0);
  transition: transform 0.62s var(--fx-ease-giga);
  transition-delay: calc(var(--p, 0) * 55ms);
}
/* revealing: panels lift out downward, staggered from the far edge */
.fx-curtain.is-revealing .fx-curtain__panel {
  transform: translate3d(0, 101%, 0);
  transition: transform 0.72s var(--fx-ease-giga);
  transition-delay: calc(var(--p, 0) * 55ms);
}
/* wordmark, only visible while the curtain covers */
.fx-curtain__mark {
  position: fixed;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.05em;
  font-family: var(--fx-serif);
  font-size: clamp(2rem, 7vw, 4.5rem);
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--fx-bg);
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s var(--fx-ease-quart);
}
.fx-curtain__mark b { font-weight: 500; }
.fx-curtain__mark i { color: var(--fx-accent); font-style: normal; }   /* the red tick */
.fx-curtain.is-covering .fx-curtain__mark,
.fx-curtain.is-covered  .fx-curtain__mark { opacity: 1; }

/* Pre-paint curtain hold — a plain ink sheet painted from the very first frame
   when arriving from a curtain navigation (set by an inline <head> script), so
   the outgoing view never flashes before the real curtain builds. Sits just
   UNDER the real curtain (z 9998 < 9999); arrive() removes the class once the
   real curtain is covering. */
html.tm-curtain-hold::before {
  content: '';
  position: fixed; inset: 0; z-index: 9998;
  background: var(--fx-ink);
}

/* ─────────────────────── Reduced motion ─────────────────────── */
/* Kill every travel; snap everything to its resting state. Mirrors how
   initReveal bails to revealAll() under prefers-reduced-motion. */
@media (prefers-reduced-motion: reduce) {
  html.fx-anim [data-fx-split] .fx-split-inner {
    transform: none !important;
    opacity: 1 !important;
    transition: none !important;
  }
  [data-fx-magnetic],
  [data-fx-magnetic-text] { transform: none !important; transition: none !important; }
  html.fx-anim [data-fx-parallax] {
    transform: none !important;
    opacity: 1 !important;
    transition: none !important;
  }
  [data-fx-parallax] .fx-parallax-img,
  [data-fx-parallax] > img { transform: scale(1) !important; }
  .fx-curtain { display: none !important; }
}
