/* ═══════════════════════════════════════════════════════════════════════════
   rzv.studio — intro.css   (INTRO/CINEMATIC agent: the opening film + #hero)

   THE FILM RUNS ON THE HERO STAGE, NOT IN #loader. #loader is now nothing but
   an anti-flash plate: intro.js dismisses it on its first frame, once the
   runtime hidden states are set, and it fades out over black onto black. That
   is why there is almost nothing about it below.

   The consequence worth knowing: there is exactly ONE aurora and at most ONE
   pearl on the stage at a time. The aurora is revealed by a circular clip
   opening from the stage's centre — no second copy to cross-fade — and the
   pearl is a single element whose --pearl-size is written rather than scaled,
   so every inset shadow, the border and the iridescent band stay correct at
   each size it takes (25px through the film, then 9px as it lands in the brand
   slot). base.css parameterised .pearl by --pearl-size precisely so this could
   be one object.

   AND THE PEARL LEAVES. Haseeb: "no need for the pearl in hero." The film's
   pearl flies into the header at beat 7 and never comes back; the composed
   hero is the menu, the brow, the headline and the CTA on the settled aurora.
   Everything that held space or scroll for a resting hero pearl — the
   --hero-pearl-gap padding above the headline, and the drop that carried it to
   the viewport centre — has gone with it.

   THE ONE RULE THAT SHAPES THIS FILE: nothing that ships in index.html is
   pre-hidden here. base.css delivers the page composed and readable; intro.js
   sets every hidden state at RUNTIME and only when RZV.motionOK() is true, so
   a crashed or absent script can never leave a blank screen behind. The
   `opacity:0` rules below are only ever on elements intro.js itself created.

   TRAP 2: GSAP owns `transform` on everything it animates, so those elements
   are centred with margin offsets (or xPercent/yPercent, which GSAP owns too)
   — never with a CSS translate that GSAP's next write would delete. The
   exceptions are .rz-word, .hx-skip and .hero-inner, which GSAP never
   transforms.
   ═══════════════════════════════════════════════════════════════════════ */


/* ═══════════════════════════════════════════════════════════════════════════
   1 · THE ANTI-FLASH PLATE
   Black, over everything, for the ~0.9s it takes to fade — which is spent over
   the film's own opening black, so it is never seen going. It exists for the
   frames between first paint and intro.js setting .hero-copy's hidden state.
   ═══════════════════════════════════════════════════════════════════════ */
#loader{
  -webkit-user-select:none;user-select:none;pointer-events:none;
  /* base.css gives it a .9s fade, which was right when it was the curtain the
     whole film played behind. It is not the curtain any more — the film plays
     on the stage BENEATH it — so a .9s fade would dim the pearl's arrival by
     up to 65% for the first half-second. .22s fits inside the film's opening
     black and is gone before anything moves. */
  transition:opacity .22s linear, visibility 0s linear .22s;
}


/* ═══════════════════════════════════════════════════════════════════════════
   1b · THE HEADER LEAVES WITH THE HERO

   Haseeb: "After scroll down from hero header hides up." nav.js's
   RZV.headerAway() sets html.hdr-away, and intro.js calls it off the hero
   ScrollTrigger's own boundary — the same instant it parks the intro stage.
   The rule: the header is away for exactly as long as #hero is behind the
   reader, and it returns the moment they scroll back up into it.

   ⚠️ NOT html.hdr-veil, AND THAT IS THE WHOLE POINT. glass.js gates every
   autonomous glass solid on the home page on the veil being absent, so
   reusing it here would switch the glass off for the whole of the venn and the
   work grid. This class is unknown to glass.js.

   ⚠️ OPACITY AND TRANSFORM, NEVER display:none OR visibility. intro.js's
   flight and constellation.js's both measure #brandPearl with
   getBoundingClientRect through this state, and both suspend .site-header's
   transform INLINE for the read — inline beats this rule, and neither cares
   about opacity. Take the header out of layout and both measurements return a
   zero-width box.

   translateY(-100%) rather than the veil's -12px: this is the header LEAVING,
   not a chrome-free frame being held, and it has the whole header height to
   travel through. It rides base.css's existing .9s opacity+transform
   transition on .site-header, so it leaves and returns at the site's pace.

   This lives here rather than in base.css because base.css is another agent's
   file — and index.html is the only page that loads this stylesheet and the
   only page with a hero to be past, so the scoping is right either way.
   ═══════════════════════════════════════════════════════════════════════ */
html.hdr-away .site-header{
  opacity:0;transform:translateY(-100%);pointer-events:none;
}


/* ═══════════════════════════════════════════════════════════════════════════
   2 · THE WORDMARK — `RZV` + pearl, and the pearl is the FULL STOP

   The mark the film builds is `RZV.` — three caps with the pearl seated after
   the V as the period. It is NOT a split word with the pearl in the middle any
   more; there is no `studio` half, and there is therefore nothing to keep
   balanced either side of a gap.

   That kills the 1fr/auto/1fr grid this used to be, and the reasoning is worth
   keeping because it is what the grid was FOR. With `rzv` · pearl · `studio`
   the pearl had to sit on the screen's centre while the two halves weighed
   three characters against six, so a plain centred row carried the gap left of
   the pearl by half the difference. Forcing the outer columns equal put the
   middle column dead centre by construction. None of that applies to a mark
   whose dot is at the END: the composition is simply `RZV` followed by the
   dot's seat, and CENTRING THE WHOLE ROW is now the correct answer — the
   optical centre of `RZV.` is the centre of `RZV.`, not the centre of `RZV`.

   .rz-gap is the dot's SEAT — a real, laid-out box the pearl is flown into,
   not empty air. intro.js measures its centre and lands the pearl there, so
   the period's sidebearing is a CSS number in this file rather than a constant
   in a timeline. Its width is about one mono advance (0.6em) at every size,
   which is what a full stop occupies.

   `translate(-50%, calc(-50% + 0.05em))` — the nudge that agrees the letters'
   ink with the pearl rather than with their em box. It was 0.02em for
   `rzv.studio`, whose `t`, `d` and the tittle on the `i` pushed the ink most of
   the way up the box on its own. ALL CAPS puts it back: R Z V span baseline to
   cap height and nothing at all uses the descender space, so the ink now sits
   HIGH in the box by roughly the same margin it used to sit low, and the word
   has to come down to meet a pearl centred on the box.
   ═══════════════════════════════════════════════════════════════════════ */
.rz-word{
  position:absolute;left:50%;top:50%;
  transform:translate(-50%, calc(-50% + 0.05em));
  display:flex;align-items:center;justify-content:center;
  font-family:var(--mono);font-weight:400;line-height:1;
  font-size:clamp(34px,7.6vw,86px);letter-spacing:-0.02em;
  color:#FAFAFA;white-space:nowrap;pointer-events:none;opacity:0;
}
/* No transform of its own any more: the halves used to fly outward off the
   pearl, and nothing here moves but the characters inside their masks. */
.rz-w{display:flex}
/* The dot's seat. The middle term is 0.6em of the headline clamp — one Space
   Mono advance, i.e. exactly what a period occupies — so at desktop sizes the
   pearl sits with a real period's sidebearing after the V. The 34px FLOOR is
   the pearl itself: 0.6em of the 34px type is 20px, which is narrower than the
   25px pearl, and a dot wider than its own seat touches the V. 34 leaves ~4.5px
   of air either side, which is the least that still reads as punctuation. */
.rz-gap{display:block;width:clamp(34px,4.6vw,52px);flex:none}
/* One mask per character: the slide-up is a real reveal from behind an edge,
   not an opacity fade pretending to be one. The pearl DRAWS them out as it
   sweeps right past each box, so the mask is the whole gesture (intro.js
   glyphDelay). It is also how they leave — up, behind the same edge. */
.rz-ch{display:block;overflow:hidden}
.rz-ch > i{display:block;font-style:normal;will-change:transform,opacity}

.hx-skip{
  position:absolute;left:50%;bottom:clamp(22px,5vh,46px);
  transform:translateX(-50%);
  color:var(--dim);opacity:0;pointer-events:none;
}


/* ═══════════════════════════════════════════════════════════════════════════
   3 · THE HERO STATEMENT

   base.css used to own .hero-inner / .hero-copy / .hero-title; those rules were
   purged in the cleanup sweep, so the layout lives here now — with the code
   that animates it.

   THE RESERVED AIR ABOVE THE HEADLINE IS GONE. --hero-pearl-gap held open
   clamp(58px,9vh,110px) of padding at the top of .hero-copy for the pearl to
   rest in, and intro.js's restOffset() measured the pearl's landing off exactly
   that computed value. With no pearl in the hero it was padding held for
   nothing, and it pushed the statement half its own height below the optical
   centre of a block that is centred by transform. Removed, so the sentence now
   sits dead centre. Nothing else read it: constellation.css names it in a
   comment but declares its own --cn-pearl-gap, and glass.js only quotes the
   number in prose.

   Live, .hero-inner is FIXED and viewport-centred. #hero carries ~130vh of
   empty runway for the takeover, and `justify-content:center` cannot centre
   against padding — so in flow the statement would sit at the top of a very
   tall box instead of in the middle of the screen. Fixed also means it holds
   still while the pearl grows through it, which is the point. Without
   html.intro-live it stays in normal flow and base.css's
   `#hero{justify-content:center}` centres it perfectly well.
   ═══════════════════════════════════════════════════════════════════════ */

.hero-inner{
  display:flex;flex-direction:column;align-items:center;text-align:center;
}
html.intro-live .hero-inner{
  position:fixed;left:50%;top:50%;
  transform:translate(-50%,-50%);
  z-index:1;
  /* ── AND A WIDTH, WHICH IT HAS NEVER HAD ──
     A fixed element with no width is shrink-to-fit, and its containing block is
     the VIEWPORT — so with left:50% and no right, the space it fits itself into
     is measured from the middle to the right edge and the -50% translate then
     hangs the overflow off BOTH sides. Nothing here was bounded by the screen
     at all: .hero-title's max-width is in `ch`, which is a function of the font
     size, not of the window, so at a large clamp value it exceeds the viewport
     and the headline runs off both edges — which is exactly what it was doing.
     Bounding the block is the fix, not shrinking the type: the h1 wraps inside
     a box that is now guaranteed narrower than the screen. */
  width:min(1100px, 92vw);
}
.hero-copy{
  display:flex;flex-direction:column;align-items:center;
  /* Was one 30–46px gap when .hero-copy held two children. It holds THREE now
     — the `Salam / Hello` overline, the headline, the CTA — and a single gap
     applies to both joints, which put 46px between a 10.5px mono label and the
     headline it belongs to. That reads as two unrelated blocks. The row gap is
     now the BROW's gap (14–20px, so the label sits on the headline), and the
     CTA buys its old separation back explicitly below. */
  gap:clamp(14px,1.5vw,20px);
}
/* 20 + 26 = the 46px the single gap used to give the button at full size, so
   the change above moves the brow and nothing else. */
.hero-copy .hero-cta{margin-top:clamp(16px,2.1vw,26px)}

/* ── THE HEADLINE'S SIZE ──
   base.css sizes every h1 at clamp(3rem,6vw,5.6rem) — 48 → 89.6px. That was
   drawn for the old five-word statement. The headline is now a full sentence
   in three authored .line spans, the longest of which is 37 characters, and at
   89.6px that sentence is a wall: ~1420px of ink against 1248px of usable wrap,
   so it wrapped again inside its own lines and arrived as five or six.

   Overridden here rather than in base.css, which this agent does not own and
   which is right for every other h1 on the site. clamp(1.7rem,4.3vw,3.5rem)
   caps at 56px — 37.5% down — which puts the longest line at ~890px, comfortably
   inside the wrap at every width above ~1000px and still display-scale type.
   The 1.7rem floor is the phone: 6vw of a 375px screen is 22px of headline,
   which is body copy, so the floor holds it at 27px and lets the sentence wrap.

   max-width is in ch so it tracks the size instead of fighting it. The longest
   line measures ~26.5ch in Inter 300 at −0.04em (37 characters at ~0.51em of
   lowercase advance, less 1.48em of tracking, over a 0.6em `0`); 30ch leaves
   ~13% of slack for fallback metrics before a fourth line falls out, and is
   still narrow enough that the sentence would break to three-ish lines on its
   own if the authored spans ever went away. 19ch — the old value, tuned to a
   short statement — forces this one to six lines at any size. ── */
.hero-title{
  font-size:clamp(1.7rem,4.3vw,3.5rem);
  max-width:30ch;
}
.hero-title .line{display:block}


/* ═══════════════════════════════════════════════════════════════════════════
   4 · THE STAGE — the pearl, the aurora, and the runway
   ═══════════════════════════════════════════════════════════════════════ */

html.intro-live #hero{
  /* ~85vh of runway. #hero has no content in flow once .hero-inner goes fixed,
     so its height IS this padding: every scroll pixel the hero's scrub gets is
     bought here deliberately.

     WHAT IT BUYS NOW: the statement letting go (0.00–0.34 of the scrub) and the
     aurora fading to night across the whole of it, plus the boundary at the end
     where the stage parks and the header goes away. It used to buy THE DROP as
     well — the pearl falling ~190px from the air above the headline to the
     viewport centre — and 85vh was chosen against that distance, about five
     parts scroll to one part travel.

     LEFT AT 85vh WITH THE DROP GONE, on purpose. The number now only decides
     how long the statement's exit and the aurora's fade are given, and both
     read well over roughly one screen; shortening it would also move the scroll
     position of `bottom top`, which is exactly where #constellation's sticky
     pins — retiming the venn to tidy up a runway nobody is watching.

     The scrub runs `top top → bottom top`, so it ends at the exact scroll
     position where #constellation's sticky pins. */
  padding-bottom:calc(clamp(48px,8vh,96px) + 85vh);
}
html.intro-live .hero-stage{position:fixed;inset:0}

/* Everything on the stage is centred on ONE point, and it has to be the SAME
   point .rz-word uses (`top:50%` of its fixed parent) — the pearl is the full
   stop in `RZV.` and is flown onto a seat measured out of that row, so a few px
   of vertical disagreement is a visibly crooked
   logo. Live, the stage is fixed and inset:0, so `top:50%` is the centre of the
   layout viewport and the two agree by construction. The 50svh default is for
   the static fallback only, where the stage is absolute inside a hero taller
   than the screen. (Not interchangeable on a phone: with the URL bar retracted
   50svh sits ~30px above the visual centre.) */
.hx-c{position:absolute;left:50%;top:50svh}
html.intro-live .hx-c{top:50%}

/* ── the aurora ──
   A HOST, not the aurora itself: RZV.aurora() mounts base.css's six-blob
   .aurora inside it and drives the idle drift, and this wrapper carries the
   opacity intro.js animates: up on the burst, down to AURORA_REST as the film
   settles, and then all the way to zero across the hero's scrub, so the venn
   pins onto night. Two elements on purpose — base.css puts a .55s
   opacity transition on .aurora, and GSAP writing opacity every frame onto an
   element with a CSS transition on that same property smears each frame into
   the next (trap 2).

   The burst is a circular clip opening from the pearl's centre, written inline
   by intro.js and removed when it completes. Everything behind it is the page's
   own black, so the reveal needs no second layer to be revealed FROM.

   DELIBERATELY NOT base.css's `.au` host, which is otherwise the same box. `.au`
   carries `opacity:0; transition:opacity 1.2s` and reveals via `.on` — a CSS
   transition on the exact property this film animates frame by frame, which
   would smear every frame into the next (trap 2). `.au` is right for a host
   that fades in once and stays; this one is choreographed.

   ── THE BAND: THE AURORA ONLY LIVES AT THE TOP ──
   The glow is a band across the upper screen that falls off to nothing well
   before the fold, not a wash over the whole viewport. It is done with a MASK
   and the box is left full-size, which is the whole point of the choice:
   base.css's six blobs are sized and placed in PERCENTAGES of their own box
   (`.aurora{inset:-22%}`, `.ab.a1{width:124%;left:-41%}` and so on) and
   RZV.aurora() drives the idle drift in percentages too. Shrink the host to
   `height:60%` and every blob and every stride of drift shrinks with it — the
   field stops moving at the amplitude Haseeb signed off on last night ("needs
   to feel 100% like when i open the portal chat"). A mask changes only where
   the layer PAINTS; the motion inside it is untouched.

   The cost is a mask group per frame on a layer whose children move.
   constellation.js weighed exactly this and chose per-element opacity over a
   mask — but it had EIGHTEEN ether elements to mask. There is ONE layer here,
   already promoted, already grouped (`.aurora` carries a filter, so the
   mix-blend-mode:screen children are isolated into their own stacking context
   before the mask ever sees them — which is also why the mask cannot break the
   blending), and the mask itself never changes. One static mask on one
   composited layer is a rasterise-once cost, not a per-frame one.

   THE NUMBERS, against what sits underneath. Full strength to 22%, half at
   41%, gone by 60% — of the host, which live is exactly the viewport. The
   ramp is a SMOOTHSTEP written out as stops (the same 1−k²(3−2k) curve the
   scroll fade in syncHero uses), not a two-stop linear gradient: a linear ramp
   has a derivative kink at the top stop that reads as a faint horizontal seam,
   and the statement block sits centred — the seam would land across the type.
   Smoothstep is flat at both ends, so the band has no edge anywhere. 22% of the
   viewport is above the brow at every size, so the statement sits in the
   falloff rather than at the band's lip; 60% is below the CTA and a long way
   above the fold, which is what stops this being a full-screen wash again.
   (Both still hold with the reserved pearl gap removed: the statement block
   moved UP by half that padding, i.e. further into the falloff, not into the
   full-strength stop — at a 900px window 22% is 198px and the brow sits around
   360px.)

   mask-mode is `match-source`, and for a gradient that means ALPHA — hence
   black-with-alpha stops, not the white/black of a luminance mask. Same for
   the -webkit- pair, which Safari still needs. no-repeat, because `.aurora`
   overflows the host by 22% a side and repeat would tile the band into that
   overflow; off-screen today, but a lie waiting for a layout change. */
.hx-aurora{
  position:absolute;inset:0;z-index:0;pointer-events:none;opacity:0;
  -webkit-mask-image:linear-gradient(to bottom,
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 22%,
    rgba(0,0,0,0.95) 27%, rgba(0,0,0,0.83) 32%, rgba(0,0,0,0.50) 41%,
    rgba(0,0,0,0.17) 50%, rgba(0,0,0,0.05) 55%, rgba(0,0,0,0) 60%);
  mask-image:linear-gradient(to bottom,
    rgba(0,0,0,1) 0%, rgba(0,0,0,1) 22%,
    rgba(0,0,0,0.95) 27%, rgba(0,0,0,0.83) 32%, rgba(0,0,0,0.50) 41%,
    rgba(0,0,0,0.17) 50%, rgba(0,0,0,0.05) 55%, rgba(0,0,0,0) 60%);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-size:100% 100%;mask-size:100% 100%;
}

.hx-ring{overflow:visible;opacity:0;pointer-events:none;will-change:transform,opacity}
/* stroke-dasharray "0.5 13" + round caps = actual round dots, not dashes.
   var(--text) tracks the phase, so the rings are correct on black and white. */
.hx-ring circle{
  fill:none;stroke:var(--text);stroke-width:2;stroke-linecap:round;
  stroke-dasharray:0.5 13;opacity:0.30;
}

/* ── the three dots ── (behind the PLANETS flag in intro.js — see there)
   MONOCHROME. White on the night, black on the white, and nothing in between:
   the pearl is the ONE object on this site allowed to be iridescent, and the
   dots reading as flat 2D discs beside it is what makes that read as material
   rather than as decoration. */
.hx-planet{
  width:12px;height:12px;margin:-6px 0 0 -6px;border-radius:50%;
  background:#fff;opacity:0;pointer-events:none;
  will-change:transform,opacity;
}
/* <b> is the dot's glow — a separate, larger, blurred layer rather than a
   box-shadow, because the glow has to LEAVE on the same scrub that the ink
   arrives on, and a box-shadow cannot be faded independently of its element. */
.hx-planet > b{
  position:absolute;inset:-150%;border-radius:50%;filter:blur(3px);pointer-events:none;
  background:radial-gradient(circle,rgba(255,255,255,0.45),rgba(255,255,255,0.12) 42%,transparent 72%);
}
/* <i> is the ink for that dormant path. --ink IS body.phase-light's --text,
   stated once, here, and deliberately not var(--text) or var(--bg): both flip
   with the phase, and on the ⋯ path this crossfade ran well before the flip —
   a phase-tracking token would have put white ink on a white ground. Same
   reasoning as .hx-planet's white base: the values that must survive a phase
   change unchanged are the ones that stay literal. */
.hero-stage{--ink:#0A080A}
.hx-planet > i{position:absolute;inset:0;border-radius:50%;background:var(--ink);opacity:0}

/* ── THE PEARL ──
   ONE element, for the whole film and the whole hero. Its --pearl-size is
   written rather than its scale, so base.css's inset highlights, the iridescent
   band and the 1px border stay correct at every size it takes: a 25px pearl
   scaled is a smear, and the pearl is the one thing on this site that has to
   read as a material.

   ONE element, for the whole film — AND ONLY FOR THE FILM. It is born at 25px
   at the stage's centre, seats itself as the full stop in `RZV.`, flies into
   the brand slot at 9px, and is then held at opacity 0 for the rest of the
   page: the composed hero has no pearl in it. (The element is kept rather than
   removed because it is still a live tween target on the frame it fades out.)

   IT NO LONGER ARRIVES AS A MOON. It is born at 25px — the wordmark's dot, the
   logo pearl, one size — by scaling up from 0, and the only real size change
   left in this file is the last 25 → 9px of the flight into the brand slot.
   (The 25 → 22px taper went with the drop.) There is also no FACE any more: two
   eyes existed to give a 340px moon presence while it contracted, and both the
   moon and the contraction are gone. At 25px they were never legible.

   Centred with xPercent/yPercent from GSAP rather than margins, because its
   width changes as it flies into the header, and a fixed margin would walk it
   off centre as it shrank. */
.hx-pearl{--pearl-glow:0.55;opacity:0;will-change:transform,opacity}

/* The pearl's halo. DELIBERATELY NOT MASKED by the aurora's band, and it is a
   sibling of .hx-aurora rather than a child so the question does not even
   arise: it belongs to the OBJECT and travels with it, and the object crosses
   the whole width of the screen through a band that has already fallen to zero
   at the edges. Masking it would put the pearl out mid-sweep. It stays at
   GLOW_REST 0.85 rather than coming down with the aurora for the same reason —
   with the ambient field confined to the top, this is most of the light around
   a pearl that is moving.

   (It no longer has to survive a DROP to the viewport centre: there is no drop
   and no resting hero pearl. The halo's last act is to fade out with the
   flight, because what lands in the brand slot is a 9px logo pearl and a logo
   does not glow.)

   A separate layer rather than a tweened --pearl-glow,
   because that custom property feeds three box-shadows: animating it repaints
   the pearl every frame, while scaling and fading a pre-rasterised halo is two
   compositor properties. The loop still gives it a slow swell of its own while
   it travels — the one term in that loop that was never gated on the pearl
   being seated, so it outlived the lean and the breath that were. */
.hx-pearlglow{
  width:120px;height:120px;margin:-60px 0 0 -60px;border-radius:50%;
  opacity:0;pointer-events:none;filter:blur(6px);
  background:radial-gradient(circle,
    rgba(255,255,255,0.28) 0%,
    rgba(129,140,248,0.30) 22%,
    rgba(167,139,250,0.14) 44%,
    rgba(34,211,238,0.05) 66%,
    rgba(34,211,238,0) 80%);
  will-change:transform,opacity;
}

/* THERE IS NO FLOOD AND NO WHITE VEIL HERE ANY MORE. The hero used to end by
   growing the pearl to 130× and washing the screen white at phase('light');
   that moment moved — it now happens after the venn collapses, and the
   constellation owns it. This section is dark from its first frame to its last,
   and the pearl's last act here is to FLY INTO THE HEADER. Nothing of ours
   reaches the viewport centre, so the venn grows its own node rather than
   inheriting one. If you are looking for the takeover, it is not in this
   file. */


/* ═══════════════════════════════════════════════════════════════════════════
   5 · REDUCED MOTION / MOTION OFF — freeze, never remove (trap 3)

   With motion off intro.js builds the aurora — still, but built — and stops
   there: no film, no wordmark, no pearl, no header veil, no header hide. The
   statement is index.html's own markup, centred by base.css, and is never
   touched on this path, so the hero is composed and readable from CSS alone.

   The .hx-pearl / .hx-pearlglow rules below are the trap-3 freeze for the
   ORDERING WINDOW ONLY: html.rzv-no-motion is set by nav.js on the same event
   intro.js tears the stage down on, and whichever runs first, nothing may be
   left travelling with a compositor layer in the meantime. A moment later
   there is no pearl to match.

   The top-only band needs nothing here and deliberately gets nothing: it is a
   mask on .hx-aurora, which is the same element on both paths, so motion-off
   gets the same band rather than the full-screen wash everyone else lost. Its
   stops are percentages of the host, and on this path the host is
   `.hero-stage` inside a `min-height:100svh` #hero rather than the fixed
   viewport — near enough the same box that the band lands in the same place,
   and it tracks the box either way instead of assuming one.
   ═══════════════════════════════════════════════════════════════════════ */
@media(prefers-reduced-motion:reduce){
  .hx-pearl{animation:none}
  .rz-ch > i,.hx-ring,.hx-planet,.hx-pearl,.hx-pearlglow{will-change:auto}
}
html.rzv-no-motion .hx-pearl{animation:none}
html.rzv-no-motion .hx-ring,
html.rzv-no-motion .hx-planet,
html.rzv-no-motion .hx-pearl,
html.rzv-no-motion .hx-pearlglow{will-change:auto}


/* ═══════════════════════════════════════════════════════════════════════════
   6 · MOBILE (≤760px)
   A shorter runway, and a smaller halo. (No `--hero-pearl-gap` override here
   any more: the reserved air went with the hero's pearl, at every width. No
   `.hx-face` rule either — the face is gone from every width; see THE PEARL.)
   ═══════════════════════════════════════════════════════════════════════ */
@media(max-width:760px){
  html.intro-live #hero{padding-bottom:calc(clamp(48px,8vh,96px) + 70vh)}
  .hx-planet{width:10px;height:10px;margin:-5px 0 0 -5px}
  .hx-ring circle{stroke-dasharray:0.5 10;stroke-width:1.6}
  .hx-pearlglow{width:96px;height:96px;margin:-48px 0 0 -48px}
}
