/* ============================================================
   ZMS — motion layer
   Loaded after detailer.css. Owns everything tagged [data-anim].
   Nothing here uses the legacy [data-reveal] system.
   ============================================================ */

/* ---------- 1. line-masked type reveal ---------- */

[data-anim="lines"] { --ln-stagger: 90ms; }

.ln {
  display: block;
  overflow: hidden;
  padding-bottom: 0.04em;   /* room for descenders inside the mask */
  margin-bottom: -0.04em;
}
.js-anim .ln-i {
  display: block;
  transform: translate3d(0, 108%, 0);
  opacity: 0;
  will-change: transform, opacity;
}
.js-anim .is-lit .ln-i {
  transform: translate3d(0, 0, 0);
  opacity: 1;
  transition:
    transform 1000ms cubic-bezier(0.19, 1, 0.22, 1) var(--d, 0ms),
    opacity 620ms cubic-bezier(0.4, 0, 0.2, 1) var(--d, 0ms);
}

/* ---------- 2. soft rise for body copy and blocks ---------- */

.js-anim [data-anim="rise"] {
  opacity: 0;
  transform: translate3d(0, 26px, 0);
  will-change: transform, opacity;
}
.js-anim [data-anim="rise"].is-lit {
  opacity: 1;
  transform: none;
  transition:
    opacity 820ms cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0ms),
    transform 900ms cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0ms);
}

/* ---------- 3. media wipe ---------- */

.js-anim [data-anim="wipe"] {
  clip-path: inset(0 0 100% 0);
  will-change: clip-path;
}
.js-anim [data-anim="wipe"].is-lit {
  clip-path: inset(0 0 0% 0);
  transition: clip-path 1150ms cubic-bezier(0.77, 0, 0.175, 1) var(--d, 0ms);
}
.js-anim [data-anim="wipe"] img,
.js-anim [data-anim="wipe"] video {
  transform: scale(1.12);
  will-change: transform;
}
.js-anim [data-anim="wipe"].is-lit img,
.js-anim [data-anim="wipe"].is-lit video {
  transform: scale(1);
  transition: transform 1600ms cubic-bezier(0.16, 1, 0.3, 1) var(--d, 0ms);
}

/* ---------- 4. rules that draw ---------- */

.js-anim [data-anim="rule"] {
  height: 1px;
  background: var(--rule);
  transform: scaleX(0);
  transform-origin: left center;
  will-change: transform;
}
.js-anim [data-anim="rule"].is-lit {
  transform: scaleX(1);
  transition: transform 1100ms cubic-bezier(0.77, 0, 0.175, 1) var(--d, 0ms);
}

/* ---------- 5. the forty-minute scrub ---------- */

.scrub {
  border: 1px solid var(--rule);
  background: linear-gradient(150deg, rgba(255,255,255,.045), rgba(255,255,255,.012));
  padding: clamp(24px, 3.6vw, 44px);
  margin-top: clamp(28px, 3.6vw, 44px);
}
.scrub-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.scrub-clock {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(2.6rem, 9vw, 5.6rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--text);
  margin: 14px 0 6px;
  transition: color 400ms ease;
}
.scrub.is-hot .scrub-clock { color: var(--hivis); }
.scrub-track {
  position: relative;
  height: 2px;
  background: var(--rule-soft);
  margin-top: 20px;
  overflow: hidden;
}
.scrub-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: var(--text-faint);
  transition: background 400ms ease;
}
.scrub.is-hot .scrub-fill { background: var(--hivis); }
.scrub-legend {
  display: flex;
  justify-content: space-between;
  margin-top: 12px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.scrub-caption {
  margin-top: 18px;
  color: var(--text-soft);
  font-size: 0.97rem;
  min-height: 1.5em;
}
.scrub-caption b { color: var(--text); font-weight: 600; }

/* ---------- 6. the five-minute counter (the answer) ---------- */

.beat {
  display: flex;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  margin-top: clamp(22px, 3vw, 34px);
}
.beat-clock {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(2rem, 5.5vw, 3.4rem);
  line-height: 1;
  letter-spacing: -0.03em;
  color: var(--hivis);
}
.beat-label {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-faint);
  max-width: 26ch;
  line-height: 1.6;
}

/* ---------- 7. demo video panels ---------- */

.demo {
  position: relative;
  overflow: hidden;
  background: #000;
  border: 1px solid var(--rule-soft);
}
.demo video {
  width: 100%;
  display: block;
  aspect-ratio: 16 / 9;
  object-fit: cover;
}
.demo-tag {
  position: absolute;
  left: 0; bottom: 0;
  padding: 10px 16px;
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text);
  background: rgba(5,5,5,.72);
  backdrop-filter: blur(6px);
}
.demo-grid {
  display: grid;
  grid-template-columns: minmax(0,1.35fr) minmax(0,1fr);
  gap: clamp(14px, 2vw, 24px);
  margin-top: clamp(28px, 3.6vw, 48px);
}
.demo-grid .demo:first-child video { aspect-ratio: 16 / 10; }
@media (max-width: 780px) { .demo-grid { grid-template-columns: 1fr; } }

/* ---------- 8. sticky flow ---------- */

.flow-sticky { position: relative; }
.d-flow-step { transition: opacity 500ms ease, filter 500ms ease; }
.d-flow.is-seq .d-flow-step { opacity: 0.28; filter: saturate(0.4); }
.d-flow.is-seq .d-flow-step.on { opacity: 1; filter: none; }

/* ---------- 9. header state ---------- */

.site-header { transition: background 420ms ease, border-color 420ms ease; }
.site-header.is-stuck {
  background: rgba(6,6,6,.86);
  backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--rule-soft);
}

/* ---------- 10. pinned hero, scroll-assembled copy ----------
   The section is tall; the inner frame pins to the viewport. The video
   holds on screen while the copy builds line by line as the reader
   scrolls. Hidden states are applied only once JS confirms it is driving
   (.js-hero) — without JS everything renders normally. */

[data-hero-scroll] {
  height: 210vh;
  min-height: 210vh;
  position: relative;
  isolation: isolate;
  background: var(--bg-deep);
  display: block;
  padding-bottom: 0;
  /* must not clip: an overflow-hidden ancestor breaks the sticky child */
  overflow: visible;
}
.d-hero-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  padding-bottom: clamp(48px, 7vw, 96px);
}
.d-hero-sticky > .wrap { position: relative; z-index: 2; width: 100%; }

/* initial states — only while JS is driving */
.js-hero [data-hero-item] {
  opacity: 0;
  transform: translate3d(0, 30px, 0);
  transition: opacity 700ms cubic-bezier(0.16, 1, 0.3, 1),
              transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
  will-change: opacity, transform;
}
.js-hero [data-hero-item].on { opacity: 1; transform: none; }

.js-hero [data-hero-lines] .ln-i {
  transform: translate3d(0, 110%, 0);
  opacity: 0;
  transition: transform 1000ms cubic-bezier(0.19, 1, 0.22, 1),
              opacity 560ms cubic-bezier(0.4, 0, 0.2, 1);
}
.js-hero [data-hero-lines] .ln-i.on {
  transform: translate3d(0, 0, 0);
  opacity: 1;
}

/* the cue fades out once the copy is assembling */
.js-hero .hero-cue { transition: opacity 500ms ease; }
.js-hero.cue-off .hero-cue { opacity: 0 !important; animation: none; }

@media (prefers-reduced-motion: reduce) {
  [data-hero-scroll] { height: auto; min-height: 88vh; }
  .d-hero-sticky { position: static; height: auto; min-height: 88vh; }
  .js-hero [data-hero-item],
  .js-hero [data-hero-lines] .ln-i {
    opacity: 1 !important;
    transform: none !important;
    transition: none !important;
  }
}

/* ---------- 11. hero flourish ---------- */

.d-hero .d-eyebrow { overflow: hidden; }
.hero-cue {
  position: absolute;
  left: 50%;
  bottom: 22px;
  transform: translateX(-50%);
  width: 1px;
  height: 46px;
  background: linear-gradient(to bottom, transparent, var(--text-faint));
  opacity: 0;
  animation: cueIn 900ms ease 1400ms forwards, cueRun 2400ms ease-in-out 2300ms infinite;
}
@keyframes cueIn { to { opacity: 1; } }
@keyframes cueRun {
  0%   { transform: translateX(-50%) scaleY(0.3); transform-origin: top; }
  50%  { transform: translateX(-50%) scaleY(1);   transform-origin: top; }
  51%  { transform: translateX(-50%) scaleY(1);   transform-origin: bottom; }
  100% { transform: translateX(-50%) scaleY(0.3); transform-origin: bottom; }
}

/* ---------- reduced motion ---------- */

@media (prefers-reduced-motion: reduce) {
  .ln-i,
  [data-anim="rise"],
  [data-anim="wipe"],
  [data-anim="rule"] {
    opacity: 1 !important;
    transform: none !important;
    clip-path: none !important;
    transition: none !important;
  }
  [data-anim="wipe"] img,
  [data-anim="wipe"] video { transform: none !important; }
  .d-flow.is-seq .d-flow-step { opacity: 1; filter: none; }
  .hero-cue { animation: none; opacity: 1; }
}
