/* ══════════════════════════════════════════════════════════════════════════
   scroll-reveal.css — homepage scroll choreography

   Every element on the homepage arrives rather than appears: a short travel,
   a little blur burning off, and a stagger between siblings so a run of three
   films reads as a sequence instead of a flash. Lenis already makes the
   scroll itself smooth; this is what the page does while you move through it.

   The *hidden* half of the system is not in this file. It is injected by the
   inline script at the end of <head> in index.php, generated from one
   selector list that scroll-reveal.js reads back — which is what keeps the
   two halves from drifting apart, and what stops a section painting at full
   opacity for a frame before an external stylesheet arrives to hide it.

   That injected rule builds the transform out of custom properties:

       translate3d( --nh-x * --nh-dir , --nh-y + --nh-px , 0 ) scale( --nh-s )

   so three things compose on one element without fighting over `transform`:
   the reveal (--nh-x/--nh-y/--nh-s, zeroed by .nh-in), the writing direction
   (--nh-dir), and the scroll parallax (--nh-px, written every frame by JS).
   Nothing sets `transform` directly on a revealed element, anywhere.

   ── One rule about specificity, and it matters ──
   Everything that *configures* the hidden state is wrapped in :where(), so it
   carries no specificity at all: the injected rule, and every per-section
   block below. .nh-rv.nh-in is deliberately the only selector here with any
   weight, because it is the one that has to win. Written the obvious way,
   `html.nh-anim .piece .vwrap` ties with it and comes later in the file, so
   the film reaches full opacity and stays sitting 46px low forever. Half the
   page animating and half of it stuck is the failure this avoids.

   Everything is scoped to html.nh-anim. That class is set only when the
   visitor has not asked for reduced motion and the browser has an
   IntersectionObserver, so dropping it — or turning the preference on —
   returns the page to exactly what it rendered before any of this existed.
   ══════════════════════════════════════════════════════════════════════════ */

html.nh-anim          { --nh-dir:  1 }
html[dir="rtl"].nh-anim { --nh-dir: -1 }

/* ── The travel back to normal ──────────────────────────────────────────────
   --nh-delay is set from JS at reveal time rather than here: the stagger
   belongs to whichever group happens to cross the fold together, which CSS
   cannot know. */
html.nh-anim .nh-rv {
  /* Custom properties inherit, and --nh-px is written on the hero wrapper as
     an inline style. Without this every element inside the hero would read
     its parent's parallax offset as its own and travel twice as far, straight
     out of the block it belongs to. JS writes --nh-px inline on the elements
     that really do move, and an inline style outranks this. */
  --nh-px: 0px;

  transition-property: opacity, transform, filter;
  transition-duration: var(--nh-dur, .95s);
  transition-timing-function: var(--nh-ease, cubic-bezier(.22, .68, .31, 1));
  transition-delay: var(--nh-delay, 0ms);
}

/* Zeroing the inputs, not overwriting the transform — that is what leaves the
   parallax free to keep writing --nh-px into the same element afterwards. */
html.nh-anim .nh-rv.nh-in {
  --nh-x: 0px;
  --nh-y: 0px;
  --nh-s: 1;
  opacity: 1;
  filter: none;
}

/* Once an element has finished arriving it stops transitioning its transform.
   A parallax value is recomputed every frame; a 1.2s transition on top of it
   would turn a drift into a lag. */
html.nh-anim .nh-rv.nh-settled { transition-property: opacity, filter }
html.nh-anim .nh-px { will-change: transform }

/* ══ HERO ══════════════════════════════════════════════════════════════════
   The headline is the one place worth a longer, softer entrance. It is the
   first thing anyone reads and the tone of the whole page hangs off it. */
:where(html.nh-anim .c-hero .year)     { --nh-y: 14px; --nh-b: 4px; --nh-dur: .8s }
:where(html.nh-anim .c-hero h1)        { --nh-y: 40px; --nh-b: 9px; --nh-dur: 1.25s }
:where(html.nh-anim .c-hero .side > *) { --nh-y: 24px; --nh-b: 3px }

/* The hero block itself drifts up and dims as you leave it. It is not a
   reveal unit, so it owns its transform outright. */
html.nh-anim .c-hero .wrap {
  transform: translate3d(0, var(--nh-px, 0px), 0);
  opacity: var(--nh-fade, 1);
  will-change: transform, opacity;
}

/* ══ THE WORK ══════════════════════════════════════════════════════════════
   The piece number slides in from the margin, the film rises from just under
   full size. Both are small numbers on purpose: at this scale a frame that
   grows a visible amount reads as a slideshow, not as a studio. */
:where(html.nh-anim .piece .no) { --nh-x: -28px; --nh-y: 0px; --nh-dur: .85s }
:where(html.nh-anim .piece h2)  { --nh-y: 34px; --nh-b: 7px; --nh-dur: 1.1s }
:where(html.nh-anim .piece p)   { --nh-y: 22px; --nh-b: 2px }
:where(html.nh-anim .piece .vwrap),
:where(html.nh-anim .piece > .wrap > div > .frame) { --nh-y: 46px; --nh-s: .965; --nh-dur: 1.2s }

/* ══ THE STAT LINE ═════════════════════════════════════════════════════════ */
:where(html.nh-anim .stat-line .wrap) { --nh-y: 26px; --nh-b: 5px; --nh-dur: 1.1s }

/* ══ SECTION LABELS AND HEADINGS ═══════════════════════════════════════════ */
:where(html.nh-anim .lbl) { --nh-y: 12px; --nh-dur: .75s }
:where(html.nh-anim .hs-hd > *),
:where(html.nh-anim .hr > .wrap > h2),
:where(html.nh-anim .hf > .wrap > h2) { --nh-y: 30px; --nh-b: 5px; --nh-dur: 1.05s }

/* ══ HOW IT GOES ═══════════════════════════════════════════════════════════ */
:where(html.nh-anim .sec .two > div:first-child) { --nh-x: -24px; --nh-y: 20px }
:where(html.nh-anim .sec .list li)               { --nh-y: 18px; --nh-dur: .8s }

/* ══ SERVICES ══════════════════════════════════════════════════════════════
   The rows animate their own padding when they open, and the packages inside
   run a keyframe. Neither is touched here: the reveal lands on the row and
   ends with its inputs at zero, which is the state the accordion assumes. */
:where(html.nh-anim .hs-item) { --nh-y: 24px; --nh-dur: .85s }

/* ══ REVIEWS AND FAQ ═══════════════════════════════════════════════════════ */
:where(html.nh-anim .hr-q)      { --nh-y: 30px; --nh-s: .975; --nh-dur: .95s }
:where(html.nh-anim .hr-badges) { --nh-y: 18px }
:where(html.nh-anim .hf-i)      { --nh-y: 20px; --nh-dur: .8s }

/* ══ CLOSING CTA AND ITS FORM ══════════════════════════════════════════════
   The form arrives a field at a time, which is also the order it gets filled
   in. Shorter travel than the sections above: these are controls, and a
   control that slides a long way reads as unstable rather than considered. */
:where(html.nh-anim .cta > .wrap > h2) { --nh-y: 36px; --nh-b: 7px; --nh-dur: 1.15s }
:where(html.nh-anim .cta > .wrap > p)  { --nh-y: 22px }
:where(html.nh-anim .cf-row),
:where(html.nh-anim .cf-via),
:where(html.nh-anim .cf > .cf-f),
:where(html.nh-anim .cf-send) { --nh-y: 16px; --nh-dur: .8s }
:where(html.nh-anim .cf-alt)  { --nh-y: 10px; --nh-dur: .7s }

/* ══ FOOTER ════════════════════════════════════════════════════════════════ */
:where(html.nh-anim .footer-brand),
:where(html.nh-anim .footer-col)    { --nh-y: 26px; --nh-dur: .9s }
:where(html.nh-anim .footer-bottom) { --nh-y: 14px; --nh-dur: .8s }

/* ══ SECTION RULES ═════════════════════════════════════════════════════════
   The hairline between sections draws itself in from the leading edge as the
   section arrives. It is the quietest thing on the page and the one that
   makes the scroll feel authored rather than assembled. Applied only to the
   sections that already carry a border-top, so it lands on the line that is
   there instead of inventing a second one beside it. */
html.nh-anim .nh-sec { position: relative }

html.nh-anim .nh-sec::after {
  content: '';
  position: absolute;
  top: -1px;
  inset-inline-start: 0;
  width: 100%;
  height: 1px;
  background: linear-gradient(90deg, rgba(77, 124, 255, .8), rgba(77, 124, 255, 0));
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform 1.4s cubic-bezier(.22, .68, .31, 1);
  pointer-events: none;
}

html[dir="rtl"].nh-anim .nh-sec::after {
  transform-origin: right center;
  background: linear-gradient(270deg, rgba(77, 124, 255, .8), rgba(77, 124, 255, 0));
}

html.nh-anim .nh-sec.nh-sec-in::after { transform: scaleX(1) }

/* ── The escape hatch ──────────────────────────────────────────────────────
   Belt and braces. The head script already declines to set .nh-anim under a
   reduced-motion preference, but the preference can be turned on mid-visit,
   and by then the class is in the DOM and the elements are hidden. */
@media (prefers-reduced-motion: reduce) {
  html.nh-anim .nh-rv {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
  }
  html.nh-anim .c-hero .wrap { transform: none !important; opacity: 1 !important }
  html.nh-anim .nh-sec::after { transition: none; transform: scaleX(1) }
}
