/**
 * Journey World — the design-preview system transplanted to the landing
 * (Plan-03, 2026-07-19). Styles ported from design-previews/journey-sandbox.html
 * <style> (b7–b15): header + light-line, loading veil, ride track sections,
 * About HUD (rail/stage/sheet). Auto-enqueued by the functions.php gfx glob.
 * Everything HUD-side is position:fixed — the document height must never change
 * or journey.js recomputes its scroll→clip stops mid-read.
 */

/* PURPLE BAR (MIKO note b, critic §7): 10-site-ground.css paints a fixed sitewide
   body::before with a purple radial glow — on the journey page the world owns the
   ground, so the site-ground layer stands down entirely. */
body:has(.vz-journey-track)::before { display: none; }

/* After the seam, regular content must read on solid ground — the fixed world canvas
   sits behind EVERYTHING at z:-1, so the post content gets an opaque floor.
   Light floor = the NO-WORLD case only (mobile/no-JS/failure). */
body:has(.vz-journey-track) main.wp-block-group {
  position: relative; z-index: 1;
  background: var(--wp--preset--color--base, #e4e4e7);
}

/* ── WORLD-ON SEAM — REWRITTEN (GFX 2026-07-23) ────────────────────────────────────
   The ride is now hero-only and freezes at the end of #ride-practices; the swallow
   sweeps that seam and LANDS on the restored 2D #about accordion (section.vz-about#about),
   which sits in flow right after the .vz-journey-track (NOT inside <main> — the old
   `main section#about` selector below matched nothing, and its side effect of making
   <main> transparent + pulling it -110vh was dead/wrong. Removed.)

   The frozen world canvas lives at z-index:-1 (12-scroll-journey.css), so from the
   accordion DOWN we need an OPAQUE floor or the frozen ride ghosts through the content
   (the .vz-about fluid backdrop is only opacity .22 — not a floor). Two floors:
     · the #about accordion is DARK-designed (light ink, 10-about.css) → deep-indigo floor
       (--ground #0B0620, literal — no theme token file), continuous with the swallow's
       dark resolve, so the curtain settles seamlessly into the accordion.
     · <main> (Projects/Contact) is LIGHT-designed → keep the base floor it was built on
       (the old world-on rule wrongly made main TRANSPARENT — that let the frozen ride
       ghost through Projects/Contact; restored to the opaque base here). */

/* ⚑ THE SWALLOW CONTAINS THE ABOUT (GFX 2026-08-07, Fabão ruling via MIKO: "o swallow
   precisa ser diretamente a propria sessao about … dentro do swallow e onde deveria residir
   a about section. centralizada ali. e verticalmente grande para que nao aparece o fundo").
   The curtain is the VECTOR that separates hero from About; About must live inside it, not
   arrive under it with a band of naked frozen world overhead (measured 1440×644: About top
   at y=233, 233px of nebula + vmark logo plate above it). Three coupled declarations — the
   wave retime in hero-field/world.js does the other half and its math ASSUMES all three:
     · min-height 100vh — the section must SEAL the frame. A content-height About can never
       cover the band the curtain has already released above it.
     · align-content: center — vertical centring of .sec-head + .accordion in that screen.
       Deliberately NOT `display:flex`: .vz-about's children carry `margin-inline:auto`
       (10-about.css), and an auto cross-axis margin BLOCKS flex stretch — .sec-head would
       shrink-wrap to the width of its own h2 and read as a centred title, and the
       sec-head↔accordion margins would stop collapsing (+27px). align-content on a block
       container centres the flow content and changes nothing else. Unsupported → content
       simply sits at the top of a still-sealing 100vh section: degraded, never broken.
       min-height (never height) so an opened accordion panel just grows the section and the
       centring no-ops. The #about-field canvas is position:absolute (10-about-fluid.css) —
       out of flow, untouched by either property, and the z:1 lift on non-canvas children
       still applies.
     · box-sizing + margin-block-start:0 — border-box so the 120px padding-block eats INTO
       the 100vh instead of adding to it (or the centred content sits 120px below the
       viewport centre); margin-block-start kills WP's blockGap (spacing-30) between the
       track and this section. With the runway collapsed to 0 (below), About's document top
       is then EXACTLY the hero height = 100vh, which is the `gap = 1.00vh` the wave math
       needs to be an identity rather than a measurement.
   WORLD-ON ONLY — the guard path (mobile ≤781px / reduced-motion / no-WebGL2 / failure) has
   no curtain to contain anything, so it keeps its content-height About and its blockGap.
   That is why About is NOT added to the shared blockGap zero-list further down: that list is
   unscoped and would move the guard layout too. */
html.vz-world-on body:has(.vz-journey-track) .vz-about#about {
  position: relative;
  /* TRANSPARENT (GFX 2026-08-07, Fabão ruling via MIKO — swallow-as-holder): the #0B0620
     opaque floor is GONE on the world path. The swallow curtain now PARKS at full cover
     (see the tick remap in hero-field/world.js) and its holder still
     (assets/images/about-swallow-backdrop.jpg) is the section's ONLY background —
     "apenas os textos sob o swallow". The guard path (html:not(.vz-world-on)) keeps its
     own fallback floor further down, untouched. */
  background: transparent;
  box-sizing: border-box;
  min-height: 100vh;
  align-content: center;
  margin-block-start: 0;
  /* Phase-2 rebalance (2026-09-06): the text block read top-heavy — big gap to
     the hero above, the butterfly sweep starting right at the section's end.
     80px top / 200px bottom re-centres the read between the two swallows.
     Padding is INTERNAL (border-box): the section's document top — the wave
     math's "gap = 1.00vh" identity — does not move. Overrides 10-about.css's
     symmetric 120px on the world path only. */
  padding-block: var(--vz-mu) calc(var(--vz-mu) * 2.5);
}
/* Live fluid backdrop retired from the world path (GFX 2026-08-07, same ruling): with the
   parked swallow as the section's surface, a second live fluid layer inside the section
   meant TWO backgrounds at once — the visible "square" seam Fabão vetoed. World-on only;
   ≤781px / reduced-motion already hide the canvas in 10-about-fluid.css, and the no-world
   desktop guard keeps it (no swallow exists there to replace it).
   NOTE for MIKO: 10-about-fluid.js still boots against the now-hidden canvas — harmless
   but wasted GPU; a world-on skip inside that JS is a follow-up in your seam. */
html.vz-world-on body:has(.vz-journey-track) .vz-about canvas.field-gl { display: none; }
html.vz-world-on body:has(.vz-journey-track) main.wp-block-group {
  position: relative;
  background: var(--wp--preset--color--base, #e4e4e7);
}

/* Ride track — transparent sections the fixed canvas shows through. */
.vz-journey-track { position: relative; }
.vz-journey-track .vz-ride {
  min-height: 130vh;
  display: flex; align-items: flex-start;
  padding: 4rem 5vw;
}
/* Hero identity at BOTTOM-CENTER (Fabão 2026-08-12, clarity pass — his call mid-build:
   "a centralização precisa ser no centro inferior"): horizontally centered, anchored to
   the lower zone like the old composition, replacing the bottom-LEFT block. The 18vh
   lift replaces the 22vh the 3-line H1 carried — the 2-line identity is shorter, this
   keeps its optical center in the same lower-third band, still clear of the vmark. */
.vz-journey-track .vz-ride-hero {
  min-height: 100vh;
  align-items: flex-end; justify-content: center;
  text-align: center;
}
/* Swallow runway (GFX 2026-07-23b, ⚑ FABÃO — STRAIGHT-TO-ABOUT retune): the ONLY ride
   section after the hero (the #ride-about stop is cut). MIKO ruling 2026-07-23 (his words):
   "I want the about section to come straight after you scroll from the hero ... I don't want
   this long scrolling to arrive at the about section — it should be straight." So the runway
   is COLLAPSED 90vh → 20vh: no dead scroll stretch of static world between the hero exit and
   the accordion. It keeps a small beat (20vh) purely so the swallow curtain has a hair of
   scroll to resolve into as the About head lands (a literal-0 runway made the sweep read as a
   1px flicker — see world.js wave note). Total hero-top→about-top now ≈ 1 viewport + this beat.
   ⚑ FABÃO: with a ~20vh runway the #ride-practices↔#about anchor gap is too small to window
   the wave off — the wave `from` was RE-ANCHORED to .vz-ride-hero in world.js (see there).
   The #ride-practices id is KEPT (nav "Practices" href) but with near-zero height it now
   effectively anchors at the About top — acceptable placeholder until a real Practices ships.
   Guard state (html:not(.vz-world-on)) already collapses this to 0 further below — untouched.
   ⚑ GFX 2026-08-07 (Fabão ruling via MIKO, "swallow contains the About"): 20vh → 0. The beat
   this kept was pure dead scroll of naked frozen world between the hero exit and About's
   arrival — exactly the band Fabão can "ver perfeitamente o que estava no header" in. It is
   no longer needed to stop the sweep flickering either: the wave `from` has been anchored to
   .vz-ride-hero since 2026-07-23b, so the span comes from the hero's 100vh and not from this
   runway's height. Collapsing it (with About's blockGap zeroed above) makes About's document
   top EXACTLY 100vh — the `gap = 1.00vh` identity the retimed wave math in world.js rests on.
   The element and its id STAY in the DOM at height 0: "Practices" nav href + the ride-end
   anchor (data-vz-ride-end, stamped by 13-journey-world.js) both still resolve, and with
   rideEndT 0 the clip is pinned, so a zero-height ride-end anchor changes no camera travel. */
.vz-journey-track #ride-practices { min-height: 0; padding: 0; }
/* H1 = brand title + subtitle (Fabão 2026-08-12). Line-level styling lives on the SPANS
   (.hero-ttl / .hero-sub) because world.js rasterizes the H1 per-span for the GL swallow
   overlay — color must be a literal rgba (NOT opacity: the raster reads computed color,
   opacity would not survive the blit). Shadow stays on the h1 (raster hardcodes it). */
.vz-ride-h1 {
  font-family: "IBM Plex Sans", sans-serif; font-weight: 400;
  color: #F3F1FA; text-shadow: 0 1px 16px rgba(11, 6, 32, .9);
  margin: 0 0 18vh 0;
}
.vz-ride-h1 .hero-ttl {
  display: block;
  font-size: clamp(48px, 7vw, 104px); line-height: 1.05; letter-spacing: .03em;
}
.vz-ride-h1 .hero-sub {
  display: block; margin-top: clamp(10px, 1.4vh, 18px);
  font-size: clamp(15px, 1.3vw, 20px); line-height: 1.4; letter-spacing: .22em;
  color: rgba(243, 241, 250, .72);
}
/* (.vz-ride-about-fallback rules REMOVED — GFX 2026-07-23: the fallback copy block was
   deleted from the track; the #about accordion is now the single source of that copy.) */

/* World canvas fills the fixed journey stage. opacity forced: the shared gfx sheet
   parks `.vz-gfx-stage canvas` at 0 until the OLD loader's ready flag — this world
   manages its own reveal (the loading veil covers the boot). */
/* No world (mobile/reduced-motion/failure): the stage's legacy light-gray ground would
   sit fixed behind the whole page and kill the light-ink contrast (MIKO's mobile bug) —
   without a mounted world the stage serves nothing, so it stands down. */
html:not(.vz-world-on) body:has(.vz-journey-track) .vz-journey-stage { display: none; }
.vz-journey-stage.vz-world-live { display: block; }
.vz-journey-stage.vz-world-live canvas {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 1; transition: opacity .9s ease;
}

/* DARK STRIP FIX (MIKO 2026-07-19): WP's blockGap rule (.wp-site-blocks > * with
   margin-block-start) pushed the FIXED canvas stage 32px down, exposing a body-coloured
   strip across the top of the header (read as a "social placeholder bar"); the same gap
   shifted the light-line, HUD mount and ride track off their sandbox positions. These are
   fixed/full-bleed layers — no flow gap belongs on any of them. */
body:has(.vz-journey-track) .wp-site-blocks
  > :is(#vz-loading, #vz-header, .light-line, #vz-about-hud, .vz-journey-stage, .vz-journey-track, .vz-transition-holder) {
  margin-block-start: 0;
}

/* ── Header (sandbox b8): transparent bar, alpha-video logo centered, split nav. ── */
#vz-header {
  position: fixed; inset: 0 0 auto 0; z-index: 60;
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: center;
  column-gap: clamp(24px, 5vw, 72px);
  padding: 14px clamp(20px, 4vw, 56px);
  background: transparent; pointer-events: none;
}
#vz-header .brand, #vz-header .nav a { pointer-events: auto; }
#vz-header .nav { display: flex; gap: clamp(18px, 3vw, 44px); }
#vz-header .nav-left { justify-content: flex-end; }
#vz-header .nav-right { justify-content: flex-start; }
#vz-header .brand {
  display: block; line-height: 0; cursor: pointer;
  opacity: 0; transform: translateY(10px);
  transition: opacity .7s ease .1s, transform .7s cubic-bezier(.22, .9, .32, 1) .1s;
}
#vz-header .brand.in { opacity: 1; transform: none; }
#vz-header .brand-anim {
  height: 125px; width: 125px; display: block; object-fit: contain;
  filter: drop-shadow(0 1px 10px rgba(11, 6, 32, .95));
}
#vz-header .nav a {
  font-family: "IBM Plex Mono", monospace; font-size: 12px; font-weight: 500;
  letter-spacing: .15em; text-transform: uppercase; text-decoration: none;
  color: rgba(243, 241, 250, .82); text-shadow: 0 1px 14px rgba(11, 6, 32, .95);
  opacity: 0; transform: translateY(10px);
  transition: opacity .6s ease, transform .6s cubic-bezier(.22, .9, .32, 1), color .25s ease;
}
#vz-header .nav a.in { opacity: 1; transform: none; }
#vz-header .nav a:hover { color: #fff; }

/* Light line under the header: dormant hairline, activation pulse, pointer glow. */
.light-line {
  position: fixed; top: 153px; left: 0; width: 100vw; height: 1px; z-index: 59;
  background: rgba(255, 255, 255, .12); pointer-events: none;
  opacity: 0; transition: opacity .8s ease .2s;
}
.light-line.in { opacity: 1; }
.light-line .pulse {
  position: absolute; inset: 0;
  background: #fff; box-shadow: 0 0 8px rgba(255, 255, 255, .8);
  opacity: 0; transform: scaleX(0); transform-origin: 50% 50%;
}
.light-line.activate .pulse { animation: vz-line-pulse 2.4s cubic-bezier(.45, 0, .25, 1) forwards; }
@keyframes vz-line-pulse {
  0%   { transform: scaleX(0); opacity: 0; }
  12%  { opacity: 1; }
  55%  { transform: scaleX(1); opacity: 1; }
  88%  { opacity: 1; }
  100% { transform: scaleX(0); opacity: 0; }
}
.light-line .glow {
  position: absolute; top: 0; left: 0; width: 260px; height: 1px;
  background: linear-gradient(90deg, transparent, #fff, transparent);
  box-shadow: 0 0 8px rgba(255, 255, 255, .8);
  opacity: 0; transform: translateX(calc(var(--mx, 50vw) - 130px));
  transition: opacity .35s ease, transform .16s ease-out;
}
.light-line.lit { background: rgba(255, 255, 255, .12); }
.light-line.lit .glow { opacity: 1; }

/* Social links — SPLIT to the corners (phase 2, 2026-09-06): Instagram far left,
   Email far right (first/second child of the same container), both vertically
   centered on the header's single grid row = the nav text line, in BOTH scroll
   states (the row height never changes). The stretched strip crosses the nav,
   so the container drops pointer-events and each anchor re-enables its own. */
#vz-header .vz-social {
  position: absolute;
  inset-inline: clamp(14px, 2.5vw, 30px);
  top: 50%; transform: translateY(-50%);
  display: flex; justify-content: space-between;
  pointer-events: none;
}
#vz-header .vz-social a {
  display: block; width: 16px; height: 16px;
  pointer-events: auto;
  color: rgba(243, 241, 250, .66);
  filter: drop-shadow(0 1px 8px rgba(11, 6, 32, .9));
  transition: color .25s ease;
}
#vz-header .vz-social a:hover { color: #fff; }
#vz-header .vz-social svg { width: 100%; height: 100%; display: block; }

/* Hamburger (2026-09-06): hidden on desktop; mobile collapses the nav into the
   sitewide .mobile-nav overlay (assets/header/header.css) toggled by header.js.
   Same 3-line burger idiom as .site-header .menu-toggle, in this header's ink. */
#vz-header .menu-toggle {
  display: none;
  background: none; border: 0; cursor: pointer;
  width: 44px; height: 44px; padding: 0;
  pointer-events: auto;
}
#vz-header .menu-toggle .ln {
  display: block; width: 22px; height: 1px;
  background: rgba(243, 241, 250, .85);
  margin: 6px auto;
  filter: drop-shadow(0 1px 8px rgba(11, 6, 32, .9));
  transition: transform .3s ease, opacity .3s ease;
}
#vz-header .menu-toggle[aria-expanded="true"] .ln:nth-child(1) { transform: translateY(7px) rotate(45deg); }
#vz-header .menu-toggle[aria-expanded="true"] .ln:nth-child(2) { opacity: 0; }
#vz-header .menu-toggle[aria-expanded="true"] .ln:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* No world mounted (mobile/reduced-motion/failure): the entrance animation never runs,
   so the header must be readable WITHOUT the .in classes — force everything visible. */
html:not(.vz-world-on) #vz-header .nav a,
html:not(.vz-world-on) #vz-header .brand { opacity: 1; transform: none; }

/* Mobile ≤781px — SANDWICH header (Fabão ruling 2026-09-06, replaces the stacked
   pure-CSS rows of critic fix #4: they ate a giant slice of the screen). Logo
   left, burger right; the four links + socials live in the .mobile-nav overlay
   (its own socials — the header's corner icons hide here). */
@media (max-width: 781px) {
  #vz-header {
    grid-template-columns: 1fr auto;
    padding: 10px 16px 8px; align-items: center; justify-items: start;
  }
  #vz-header .nav { display: none; }
  #vz-header .vz-social { display: none; }
  #vz-header .brand-anim { height: 68px; width: 68px; }
  #vz-header .menu-toggle { display: block; justify-self: end; }
  /* header is now one 68px row (+18px padding) instead of ~135px of stack */
  .light-line { top: 86px; }
}

/* Ground veil (2026-09-22, replaces the loading screen): a bare ink layer — no
   logo, no tag, nothing that reads as "loading" — that hides the guard→live
   layout switch while the world builds, then fades. Shown ONLY under the `.js`
   flag (vizaion_header_js_gate); every fallback path removes it (abort()). */
#vz-loading { display: none; }
.js #vz-loading {
  /* display must be re-asserted here — the cascade is per-property, so the
     no-JS `display:none` above would win and the ground would never paint. */
  display: block;
  position: fixed; inset: 0; z-index: 75; background: #070312;
  transition: opacity .7s ease;
}
#vz-loading.done { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
  /* The 5 .vz-ab-* selectors were dropped from this list — GFX 2026-08-05 (About 3D
     retired). The header / light-line / veil selectors are LIVE and stay. */
  #vz-header .nav a, #vz-header .brand, .light-line, .light-line .glow,
  #vz-loading { transition: none; }
  .light-line .pulse { animation: none; }
}

/* (About HUD block REMOVED — GFX 2026-08-05: veil + I/II/III rail + reading stage +
   the vz-vline-pulse keyframe, ~102 lines, all keyed to `#vz-about-hud` which the
   template stopped rendering on 2026-07-23. The --vz-mon-x / --vz-mon-right-x /
   --vz-rail-y custom properties died with it — world.js no longer writes them.
   The block still lives inline in design-previews/journey-sandbox.html.) */

/* Mobile ≤781px (theme convention). The 5 About-HUD rules that shared this block were
   removed with the rest of the 3D About (GFX 2026-08-05); `.vz-ride-h1` is LIVE and is
   the only mobile rule for the hero H1 — the block is kept for it, not deleted. */
@media (max-width: 781px) {
  .vz-ride-h1 .hero-sub { letter-spacing: .16em; }
}

/* ═══════════════════════════════════════════════════════════════════════════
   GUARD-STATE FALLBACK  (Journey Fallback — Component Spec 2026-07-19, Option C)
   Every rule below is scoped to html:not(.vz-world-on) so the .vz-world-on live
   path is untouched. When the world DOES mount, .vz-world-on is set on <html> and
   none of this applies — the fixed canvas stage covers the same region and the
   H1 / About markup are the identical server-rendered nodes, so mount = zero CLS
   (the void is three fixed negative-z layers with NO layout box).

   Visual = Option C: pure-CSS void, zero image asset. Palette is the site-ground
   token layer (10-site-ground.css) + the 3D1 rim/disc tints — kept literal here
   because no theme-side token file exists (token → hex mapping in comments).
   ═══════════════════════════════════════════════════════════════════════════ */

/* 1 · The void field — one fixed full-viewport layer behind ALL guard content.
   z-index:-3 + fixed = no layout box, so the world canvas mounts on top with zero
   reflow. Cool cosmic palette (NOT the retired warm hero). Layers, top-most first:
     · soft cool aperture glow, upper-right          → --primary #5B39C9 @ .20
     · vertical ground wash deep→ground→earth→earth2 → --ground-deep #070312,
       --ground #0B0620, --earth #201324, --earth2 #2A1A20 (warm floor / content seam) */
html:not(.vz-world-on) body:has(.vz-journey-track)::after {
  content: "";
  position: fixed; inset: 0; z-index: -3; pointer-events: none;
  background:
    radial-gradient(120% 80% at 78% -8%, rgba(91, 57, 201, .20), transparent 55%),
    linear-gradient(180deg, #070312 0%, #0B0620 36%, #201324 84%, #2A1A20 100%);
}

/* The aperture: a ringed vmark void drawn purely in CSS (concentric radial rings,
   no image, no mask). rim = --rim rgba(211,222,255,.9) (3D1 rimTint [.84,.9,1]);
   disc = --disc rgba(107,159,255,.55) (3D1 disc tint [.42,.62,1]). Fixed with the
   field so it also carries zero layout box. Sits high (38% down) so the H1 anchored
   bottom-left of the first screen never collides with it. */
html:not(.vz-world-on) body:has(.vz-journey-track) .vz-journey-track::before {
  content: "";
  position: fixed; z-index: -2; pointer-events: none;
  top: 38%; left: 50%; transform: translate(-50%, -50%);
  width: min(46vw, 340px); aspect-ratio: 1; border-radius: 50%;
  background:
    /* dark disc core (the void body) — --ground #0B0620 @ .9 */
    radial-gradient(circle at 50% 50%, rgba(11, 6, 32, .9) 0%, rgba(11, 6, 32, .9) 46%, transparent 47%),
    /* bright rim */
    radial-gradient(circle at 50% 50%, transparent 46%, rgba(211, 222, 255, .9) 47%, transparent 49.5%),
    /* accretion ring */
    radial-gradient(circle at 50% 50%, transparent 60%, rgba(107, 159, 255, .55) 61%, transparent 62.5%),
    /* faint outer halo — disc tint @ .28 */
    radial-gradient(circle at 50% 50%, transparent 74%, rgba(107, 159, 255, .28) 75%, transparent 76%);
  filter: drop-shadow(0 0 30px rgba(91, 57, 201, .35));
}

/* Heavy vignette — the top-most of the three fixed void layers (still behind ALL
   content at negative z). Deterministic stack: field wash z:-3 → aperture z:-2 →
   vignette z:-1, all below the z:1 page content. Darkens the field toward corners
   and deepens the H1 backdrop for AA. */
html:not(.vz-world-on) body:has(.vz-journey-track) .vz-journey-track::after {
  content: "";
  position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(130% 100% at 50% 42%, transparent 42%, rgba(7, 3, 18, .55) 100%);
}

/* 2 · Collapse the ride runway in guard state (GFX 2026-07-23). The ride is hero-only
   now: hero = one clean first screen, then the #ride-practices runway COLLAPSES to zero
   (its world-on 120vh swallow distance is dead weight with no world to sweep) so the
   guard visitor flows straight from the void hero into the #about accordion below — no
   empty-scroll gap. (The old #ride-about collapse + About-fallback typography rules were
   removed: that section and its .vz-ride-about-fallback block no longer exist; the #about
   accordion, styled by 10-about.css, now carries the About content in the guard path too.) */
html:not(.vz-world-on) .vz-journey-track .vz-ride-hero {
  min-height: 100vh;                 /* one first screen: H1 + scroll cue */
  align-items: flex-end;             /* mirrors the world-on bottom-center identity (2026-08-12) */
  justify-content: center;
  text-align: center;
  position: relative;
}
html:not(.vz-world-on) .vz-journey-track #ride-practices {
  min-height: 0; padding: 0;         /* no trailing gap — collapse the swallow runway */
}

/* 3 · H1 + scroll cue (spec §2 type block). The hero markup has ONLY the H1 (no
   sub-line, no scroll-cue node) and the constraint forbids adding DOM, so the H1 is
   anchored bottom-left and the scroll cue is a CSS ::after on the hero (generated UI
   chrome, not collective copy — no copy invented). Any About sub-line now lives only in
   the #about accordion below (single source of truth); not duplicated here. */
html:not(.vz-world-on) .vz-journey-track .vz-ride-h1 {
  color: #F3F1FA;                    /* --ink-light */
  text-shadow: 0 1px 16px rgba(11, 6, 32, .9), 0 0 4px rgba(11, 6, 32, .7);
  margin: 0 0 14vh 0;                /* lower-third band; the scroll cue keeps the floor */
}
html:not(.vz-world-on) .vz-journey-track .vz-ride-hero::after {
  content: "Scroll to enter";
  position: absolute; left: 50%; transform: translateX(-50%);
  bottom: clamp(24px, 5vh, 56px);
  font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 11px;
  letter-spacing: .24em; text-transform: uppercase;
  color: rgba(243, 241, 250, .62);   /* --ink-dim */
  text-shadow: 0 1px 12px rgba(11, 6, 32, .9);
  pointer-events: none;
}
/* MOBILE WORLD-ON SCROLL CUE (mobile pass 2026-09-05, REALOCAR): with the world
   now mounting ≤781px, the first screen is a full-viewport atmosphere with no
   scroll affordance at all — the guard path's cue above was the only invitation
   and it is scoped html:not(.vz-world-on). Same cue, world path, mobile only;
   desktop keeps its deliberate cue-less entry choreography. Absolute inside the
   hero, so it scrolls away with the first gesture — no JS carrier needed. */
@media (max-width: 781px) {
  html.vz-world-on .vz-journey-track .vz-ride-hero::after {
    content: "Scroll to enter";
    position: absolute; left: 50%; transform: translateX(-50%);
    bottom: clamp(24px, 5vh, 56px);
    font-family: "IBM Plex Mono", monospace; font-weight: 500; font-size: 11px;
    letter-spacing: .24em; text-transform: uppercase;
    color: rgba(243, 241, 250, .62);
    text-shadow: 0 1px 12px rgba(11, 6, 32, .9);
    pointer-events: none;
  }
}

/* reduced-motion: the whole fallback is a still by nature (no animation), so nothing
   to disable — the existing prefers-reduced-motion block above (lines ~232) already
   neutralises the header/HUD transitions the guard state inherits. */
