/* ==========================================================================
   TProM Team — motion primitives

   Every entrance is a CSS transition switched on by an `is-in` class that
   js/motion.js adds from an IntersectionObserver. Scroll-linked movement is
   driven by custom properties (`--p`, a 0→1 progress value) written by a
   single rAF loop, so the browser only ever animates transform and opacity.

   Nothing here hijacks scrolling. The page always scrolls at native speed.
   ========================================================================== */

/* ── Fade and rise (default) ──────────────────────────────────────────── */
/* Every hidden-then-revealed rule is scoped to `html.js`, which an inline
   script in the document head sets. Without scripting — or if the module
   fails to boot, in which case the same script removes the class again — the
   page renders as ordinary visible content instead of a blank screen. */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, var(--reveal-y, 22px), 0);
  transition: opacity var(--dur-slow) var(--ease) var(--reveal-delay, 0s),
    transform var(--dur-slow) var(--ease) var(--reveal-delay, 0s);
}
.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
}

/* ── Masked text entrance ─────────────────────────────────────────────── */
/* Wraps each line in an overflow-hidden band; the line rises into it. Used
   for the hero headline and section titles. Built by js/motion.js so the copy
   stays a single translatable string in the markup. */
.mask-line {
  display: block;
  overflow: hidden;
  /* Descenders and the serif italic need room or they get clipped. */
  padding-block-end: 0.12em;
  margin-block-end: -0.12em;
}
.js .mask-line > span {
  display: block;
  transform: translate3d(0, 108%, 0);
  transition: transform 1.05s var(--ease) var(--line-delay, 0s);
}
.js .is-in .mask-line > span,
.js .mask-line.is-in > span {
  transform: none;
}

/* ── Staggered children ──────────────────────────────────────────────── */
.js [data-stagger] > * {
  opacity: 0;
  transform: translate3d(0, 16px, 0);
  transition: opacity 0.75s var(--ease), transform 0.75s var(--ease);
}
.js [data-stagger].is-in > * {
  opacity: 1;
  transform: none;
}
/* Delays are set as inline custom properties by js/motion.js, but these
   defaults keep the effect if JS is slow to arrive. */
.js [data-stagger].is-in > *:nth-child(1) { transition-delay: 0s; }
.js [data-stagger].is-in > *:nth-child(2) { transition-delay: 0.055s; }
.js [data-stagger].is-in > *:nth-child(3) { transition-delay: 0.11s; }
.js [data-stagger].is-in > *:nth-child(4) { transition-delay: 0.165s; }
.js [data-stagger].is-in > *:nth-child(5) { transition-delay: 0.22s; }
.js [data-stagger].is-in > *:nth-child(6) { transition-delay: 0.275s; }
.js [data-stagger].is-in > *:nth-child(7) { transition-delay: 0.33s; }
.js [data-stagger].is-in > *:nth-child(8) { transition-delay: 0.385s; }
.js [data-stagger].is-in > *:nth-child(n + 9) { transition-delay: 0.44s; }

/* ── Plate reveal ────────────────────────────────────────────────────── */
/* The project's screen unmasks upward while what is inside it settles back to
   1:1 — the plate frame draws first and the screen fills in, which reads more
   deliberately than the whole thing wiping in.

   IMPORTANT: the clip goes on an element *inside* the observed one, never on
   [data-clip] itself. An IntersectionObserver measures the target's visible
   area, so clipping the target to zero height means it can never meet the
   threshold that would reveal it — the element hides itself permanently. */
.js [data-clip] .plate__screen {
  clip-path: inset(0 0 100% 0);
  transition: clip-path 1.15s var(--ease);
}
.js [data-clip].is-in .plate__screen {
  clip-path: inset(0 0 0 0);
}
.js [data-clip] .plate__shot,
.js [data-clip] .plate__mark {
  transform: scale(1.1);
  transition: transform 1.5s var(--ease);
}
.js [data-clip].is-in .plate__shot,
.js [data-clip].is-in .plate__mark {
  transform: scale(1);
}

/* ── Scroll-linked transforms ─────────────────────────────────────────── */
/* js/motion.js writes --p (0→1) on any [data-parallax] or [data-perspective]
   element while it is in view. --shift sets how far it travels.
   This is the flat default. Elements that sit inside a 3D scene take their
   transform from css/depth.css instead, which loads after this file and so
   wins — every 3D transform has exactly one owner, and it is that file. */
[data-parallax] {
  --p: 0;
  --shift: 40px;
  transform: translate3d(0, calc((0.5 - var(--p)) * var(--shift) * 2), 0);
  will-change: transform;
}

[data-perspective] {
  --p: 0;
}

/* ── Magnetic CTA ─────────────────────────────────────────────────────── */
/* Offsets are tiny by design: the button leans towards the cursor, it does
   not chase it. Pointer-fine only. */
[data-magnetic] {
  --mx: 0px;
  --my: 0px;
  transform: translate3d(var(--mx), var(--my), 0);
  transition: transform 0.4s var(--ease);
}
[data-magnetic].is-tracking {
  transition: transform 0.12s linear;
}
@media (pointer: coarse) {
  [data-magnetic] {
    --mx: 0px;
    --my: 0px;
  }
}

/* ── Counters and number rolls ────────────────────────────────────────── */
.tabular {
  font-variant-numeric: tabular-nums;
}

/* ── Reduced motion ───────────────────────────────────────────────────── */
/* Everything resolves to its final state. No transitions, no transforms, no
   scroll-linked work (js/motion.js also skips its rAF loop entirely). */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    transition-delay: 0s !important;
  }

  [data-reveal],
  [data-stagger] > * {
    opacity: 1 !important;
    transform: none !important;
  }

  .mask-line > span {
    transform: none !important;
  }

  [data-clip],
  [data-clip] .plate__screen {
    clip-path: none !important;
  }
  [data-clip] .plate__shot,
  [data-clip] .plate__mark {
    transform: none !important;
  }

  [data-parallax],
  [data-perspective],
  [data-magnetic],
  [data-tilt] {
    transform: none !important;
  }
}

/* Phones get the entrances but not the continuous scroll-linked work — it is
   the one thing that costs frames on mid-range hardware. js/motion.js makes
   the same call in script, this is the visual guarantee. */
@media (max-width: 860px) {
  [data-parallax],
  [data-perspective] {
    transform: none;
    will-change: auto;
  }
}
