:root{
  --c-dominant:#dddddd; --c-field:#dedede; --c-ink:#2e2e2e;
  --c-accent:#4d77d9; --c-texture:#2A2A2A;
  --f-display:'Inter', system-ui, sans-serif; --fw-display:500; --tr-display:-0.04em;
  --f-body:'Inter', system-ui, sans-serif; --fw-body:400;
  --f-utility:'Inter', system-ui, sans-serif;
  --s-hero:clamp(3rem, 10.5vw, 9.5rem); --s-h2:clamp(1.9rem, 4.4vw, 3.6rem); --s-body:1.0625rem;
  --ease:cubic-bezier(0.16,1,0.3,1);
  --dur:780ms;
  --stagger:80px;
  --radius:0px;
  --tex-opacity:0;
  --tex-url:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxODAiIGhlaWdodD0iMTgwIj48ZmlsdGVyIGlkPSJuIj48ZmVUdXJidWxlbmNlIHR5cGU9ImZyYWN0YWxOb2lzZSIgYmFzZUZyZXF1ZW5jeT0iMC44MiIgbnVtT2N0YXZlcz0iNCIvPjwvZmlsdGVyPjxyZWN0IHdpZHRoPSIxODAiIGhlaWdodD0iMTgwIiBmaWx0ZXI9InVybCgjbikiIG9wYWNpdHk9IjAuODUiLz48L3N2Zz4=");
  --media-chroma:1.18;
  --c-glow2:#5B8CFF;
  --c-glow3:#8AA6D8;
  --c-on-accent:#0b0b0e;
  --c-on-light:#0d0d10;
  --c-scrim:#2e2e2e;
  --c-on-scrim:#ffffff;
  --grid-offset:-8vw;
  --bleed:0px;
  --maxw:1440px;
}
/* ============================================================
   CINEMATIC MOTION ENGINE v1  |  zero dependencies
   Every value is a token. Archetypes never hardcode colour or size.
   ============================================================ */
*,*::before,*::after{box-sizing:border-box}
html{overflow-x:clip;-webkit-text-size-adjust:100%}
body{margin:0;background:var(--c-dominant);color:var(--c-ink);
  font-family:var(--f-body);font-weight:var(--fw-body);font-size:var(--s-body);
  line-height:1.55;overflow-x:hidden;
  transition:background-color 620ms var(--ease),color 620ms var(--ease)}
img,video,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
::selection{background:var(--c-accent);color:var(--c-on-accent,var(--c-dominant))}

.wrap{width:min(100% - 2.5rem,var(--maxw,1440px));margin-inline:auto}
.wrap--offset{transform:translateX(var(--grid-offset,0))}

/* ---------- typography ---------- */
.display{font-family:var(--f-display);font-weight:var(--fw-display);
  letter-spacing:var(--tr-display);line-height:.92;margin:0;text-wrap:balance}
.h-hero{font-size:var(--s-hero)}
.h-2{font-size:var(--s-h2)}
/* Text opacities sit on LLM-chosen palettes, so they carry a floor: nothing textual
   below .6, small utility text at .7+. Measured unreadable at the old values. */
/* A `text-shadow` ground halo on `.eyebrow,.gx-kicker` was tried here (2026-08-09) for the
   "utility-size copy lands on artwork" class — editorial-stack's hero eyebrow measures
   1.41:1 as cream --c-ink over the GL crystal's turquoise --c-glow3 face, reproducible 3/3.
   A/B'd on BYTE-IDENTICAL pages with the rule injected/removed: **the halo does not fix it**
   (still FAIL 2/2, same element, same frame), and it does not cause the other failures
   either. A no-op, so it was reverted rather than left in.
   Why it cannot work: the contrast gate computes declared-colour vs the modal background and
   only lets pixel structure override when spread >= 2.2, which a 7px shadow does not reach.
   Changing the perceived ground requires changing the GROUND — a scrim/plate behind the
   glyphs — not a shadow around them. See PROGRESS.md for the class and the open options. */
.eyebrow{font-family:var(--f-utility);font-size:.7rem;letter-spacing:.16em;
  text-transform:uppercase;opacity:.72;margin:0 0 1.1rem}
.lede{font-size:clamp(1.05rem,1.6vw,1.3rem);max-width:46ch;opacity:.85;margin:0}
.mono{font-family:var(--f-utility);font-variant-numeric:tabular-nums}

/* ---------- ambient glow canvas: the "expensive 3D site" layer ---------- */
.glow{position:fixed;inset:0;z-index:-3;pointer-events:none;filter:blur(60px) saturate(1.3);
  opacity:.55;transform:translateZ(0)}
.glow--hero{opacity:.8}

/* ---------- scroll progress hairline ---------- */
.prog{position:fixed;top:0;left:0;height:2px;width:100%;z-index:95;transform-origin:left;
  transform:scaleX(0);background:linear-gradient(90deg,var(--c-accent),color-mix(in srgb,var(--c-accent) 40%,var(--c-ink)))}

/* ---------- preloader: 900ms hard cap, skippable ---------- */
.pre{position:fixed;inset:0;z-index:200;background:var(--c-dominant);display:grid;
  place-items:center;transition:clip-path 780ms var(--ease)}
.pre.done{clip-path:inset(0 0 100% 0);pointer-events:none}
.pre__name{font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.4rem,4vw,2.6rem);letter-spacing:var(--tr-display);overflow:hidden}
.pre__name span{display:inline-block;transform:translateY(110%);
  animation:preUp .7s var(--ease) forwards}
.pre__bar{position:absolute;bottom:0;left:0;height:2px;width:100%;background:var(--c-accent);
  transform-origin:left;animation:preBar .82s var(--ease) forwards}
@keyframes preUp{to{transform:none}}
@keyframes preBar{from{transform:scaleX(0)}to{transform:scaleX(1)}}

/* ---------- rhythm: deliberately unequal ---------- */
main{perspective:1200px}
/* VERTICAL RHYTHM — viewport-relative, capped at the original flat values.
   These were flat rem (12rem default, 19rem loose = 192px / 304px top AND bottom at every
   viewport height), so on a 900px-tall screen a short section reserved 384-608px of pure
   padding — a third of the viewport empty before any content. User-reported 2026-08-10
   ("a lot of blank spaces in all three websites"), then measured: 26% of scroll frames on
   vantor-performance and 13% on mesnil-studio were near-empty viewports, and one section
   (`thesis`, data-rhythm="loose") was 1714px tall for a single paragraph. Padding accounted
   for ~4,350px of a ~21,000px document, about 20%.
   The vh term makes the rhythm scale with the screen it is actually on, and the max of each
   clamp is the ORIGINAL value, so very tall viewports are byte-identical to before and
   nothing anywhere gets more padding than it had. Measure with scripts/dead-space.py (frame
   emptiness) and scripts/layout-gaps.py (which section reserves height it does not fill). */
section{position:relative;padding-block:var(--rhythm,clamp(5rem,14vh,12rem));transform-style:preserve-3d}
/* deck: sections rise, settle flat, then recede — driven per-frame, never one-shot */
section[data-deck]{will-change:transform,opacity;
  transform:translateY(calc(var(--in,1) * 0px)) }
section[data-rhythm="tight"]{--rhythm:clamp(3rem,7vh,6rem)}
section[data-rhythm="loose"]{--rhythm:clamp(7rem,20vh,19rem)}

/* ---------- texture ---------- */
.tex{position:fixed;inset:-120%;pointer-events:none;z-index:60;
  opacity:var(--tex-opacity,.06);mix-blend-mode:overlay}
.tex--grain{background-image:var(--tex-url);background-size:180px}
.tex--scanline{background:repeating-linear-gradient(180deg,#fff 0 1px,transparent 1px 3px);opacity:calc(var(--tex-opacity) * .8)}
.tex--paper{background-image:var(--tex-url);background-size:420px;mix-blend-mode:multiply;opacity:calc(var(--tex-opacity) * 2)}
.is-animated .tex--grain{animation:grainDrift .5s steps(1) infinite}
@keyframes grainDrift{
  0%{transform:translate(0,0)}20%{transform:translate(-3%,2%)}40%{transform:translate(2%,-3%)}
  60%{transform:translate(-2%,-2%)}80%{transform:translate(3%,1%)}100%{transform:translate(0,0)}}

/* ---------- reveal primitives ---------- */
[data-char]{display:inline-block;transform-origin:50% 100%;
  transform:rotateX(-92deg) translateY(.35em);opacity:0;
  transition:transform var(--dur) var(--ease),opacity calc(var(--dur)*.6) var(--ease);
  transition-delay:var(--d,0ms)}
.in [data-char],.lit [data-char]{transform:none;opacity:1}
.chars{perspective:900px;transform-style:preserve-3d}

[data-line]{display:block;overflow:hidden}
[data-line] > span{display:block;transform:translateY(112%);
  transition:transform var(--dur) var(--ease);transition-delay:var(--d,0ms)}
.in [data-line] > span{transform:translateY(0)}

[data-rise]{opacity:0;transform:translateY(26px);
  transition:opacity calc(var(--dur) * .8) var(--ease),transform var(--dur) var(--ease);
  transition-delay:var(--d,0ms)}
.in [data-rise],[data-rise].in{opacity:1;transform:none}

[data-curtain]{clip-path:inset(0 0 100% 0);
  transition:clip-path calc(var(--dur) * 1.25) var(--ease);transition-delay:var(--d,0ms)}
.in [data-curtain],[data-curtain].in{clip-path:inset(0 0 0 0)}

/* ---------- exit choreography ---------- */
[data-exit]{will-change:transform,opacity,filter}
[data-exit="scale_out"] {transform:scale(calc(1 - var(--out,0) * .12));opacity:calc(1 - var(--out,0) * .75)}
[data-exit="drift_up"]  {transform:translateY(calc(var(--out,0) * -9vh));opacity:calc(1 - var(--out,0))}
[data-exit="blur_out"]  {filter:blur(calc(var(--out,0) * 7px));opacity:calc(1 - var(--out,0) * .85)}
[data-exit="desaturate"]{filter:saturate(calc(1 - var(--out,0)));opacity:calc(1 - var(--out,0) * .6)}

/* ---------- hero ---------- */
.hero{position:relative;min-height:100svh;display:grid;align-items:end;
  padding-block:0 clamp(3rem,9vh,7rem);overflow:hidden;isolation:isolate}
.hero__media{position:absolute;inset:0;z-index:-2;
  clip-path:inset(46% 0 46% 0);transition:clip-path 1000ms var(--ease) 380ms}
.hero.lit .hero__media{clip-path:inset(0 0 0 0)}
.hero__media video,.hero__media img{width:100%;height:100%;object-fit:cover;
  transform:scale(1.13);transition:transform 1400ms var(--ease) 380ms}
.hero.lit .hero__media video,.hero.lit .hero__media img{transform:scale(1)}
.hero.lit.amb-scale .hero__media video,.hero.lit.amb-scale .hero__media img{animation:slowScale 26s var(--ease) infinite alternate 1.6s}
@keyframes slowScale{to{transform:scale(1.07)}}
.hero__scrim{position:absolute;inset:0;z-index:-1;
  background:
    radial-gradient(120% 78% at 8% 88%, color-mix(in srgb,var(--c-dominant) 88%,transparent) 0%,
      color-mix(in srgb,var(--c-dominant) 52%,transparent) 34%, transparent 62%),
    linear-gradient(to top, color-mix(in srgb,var(--c-dominant) 74%,transparent) 0%, transparent 44%)}
/* Vivid footage must stay vivid. The scrim darkens only where type sits, never the whole frame. */
.hero__media video,.hero__media img{filter:saturate(var(--media-chroma,1.18)) contrast(1.04)}
.hero__inner{position:relative;display:grid;gap:1.6rem;max-width:min(56rem,86%)}
.hero--monument .hero__inner{max-width:none}
.hero__cta{justify-self:start;display:inline-flex;align-items:center;gap:.7rem;
  padding:1rem 0;font-family:var(--f-utility);font-size:.82rem;letter-spacing:.14em;
  text-transform:uppercase;position:relative;
  text-shadow:0 1px 4px color-mix(in srgb,var(--c-dominant) 65%,transparent),
    0 2px 14px color-mix(in srgb,var(--c-dominant) 85%,transparent)}
.hero__cta::after{content:"";position:absolute;left:0;right:0;bottom:.55rem;height:1px;
  background:var(--c-accent);transform-origin:left;transform:scaleX(.24);
  transition:transform 620ms var(--ease)}
.hero__cta:hover::after{transform:scaleX(1)}
.hero__cta i{width:.5rem;height:.5rem;background:var(--c-accent);border-radius:50%;
  animation:breathe 4s ease-in-out infinite}
@keyframes breathe{50%{opacity:.28}}
.hero__proof{display:flex;flex-wrap:wrap;gap:.6rem 2.2rem;font-family:var(--f-utility);
  font-size:.74rem;letter-spacing:.09em;text-transform:uppercase;opacity:.75;
  /* scrims cannot cover every bright patch of arbitrary footage — the halo can */
  text-shadow:0 1px 4px color-mix(in srgb,var(--c-dominant) 65%,transparent),
    0 2px 14px color-mix(in srgb,var(--c-dominant) 85%,transparent)}
.hero__cue{position:absolute;left:50%;bottom:1.6rem;width:1px;height:3.6rem;
  background:linear-gradient(var(--c-ink),transparent);opacity:0;
  transition:opacity 700ms var(--ease) 2400ms}
.hero.lit .hero__cue{opacity:.4;animation:cue 2.6s var(--ease) infinite 2.6s}
@keyframes cue{0%{transform:scaleY(.2);transform-origin:top}50%{transform:scaleY(1);transform-origin:top}
  51%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(.2);transform-origin:bottom}}

.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}
.hero--typemask{align-items:center;grid-template-rows:1fr auto}
.hero__media--tm{clip-path:inset(0 0 0 0)}
.hero--typemask .hero__scrim{background:linear-gradient(to top,
  color-mix(in srgb,var(--c-dominant) 92%,transparent) 0%, transparent 38%)}
.hero__plate{position:absolute;inset:0;z-index:-1;display:grid;place-items:center;
  clip-path:inset(0 0 100% 0);transition:clip-path 1150ms var(--ease) 320ms}
.hero.lit .hero__plate{clip-path:inset(0 0 0 0)}
.hero__plate svg{width:100%;height:100%}
.hero__inner--tm{align-self:end;padding-bottom:2vh}

/* typemask luminance guard: only when the mask holds VIDEO (hero--tm-guard).
   Real film is dim inside dark glyph cutouts — the footage lift is visible only
   through the letterforms, so it lights the words, never the frame. Side scrims
   plus a SOFT bottom scrim (a strong one drowns the lowest headline line), a
   drop-shadow rim on the glyph plate, and a blurred panel behind the copy. */
.hero--tm-guard .hero__media video{filter:brightness(1.52) saturate(1.3) contrast(1.06)}
.hero--tm-guard .hero__plate svg{filter:drop-shadow(0 0 22px color-mix(in srgb,var(--c-dominant) 82%,transparent))}
.hero--tm-guard::after{content:"";position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 86%,transparent) 0%,
    color-mix(in srgb,var(--c-dominant) 38%,transparent) 18%,transparent 36%),
  linear-gradient(to right,color-mix(in srgb,var(--c-dominant) 72%,transparent),transparent 14% 86%,
    color-mix(in srgb,var(--c-dominant) 72%,transparent))}
.hero--tm-guard .hero__inner--tm{position:relative;z-index:2;
  background:color-mix(in srgb,var(--c-dominant) 55%,transparent);
  -webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);
  padding:1.7rem 1.9rem 1.9rem;border-radius:20px;max-width:min(46rem,88%)}
.hero--tm-guard .hero__cue{z-index:2}

/* frame-scrub hero: pinned wrapper, sticky hero, canvas frames driven by the
   engine.js scrub module. Frame 1 is the instant-paint poster beneath the
   canvas; the canvas fades in only once the intro span is decoded. */
.gx-scrub-wrap{position:relative;min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 16svh))}
.gx-scrub-wrap .hero{position:sticky;top:0;height:100svh;min-height:0}
.gx-scrub-poster{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.gx-scrub-canvas{position:absolute;inset:0;width:100%;height:100%;opacity:0;transition:opacity .45s ease}
.gx-scrub-canvas.ready{opacity:1}
@media (max-width:900px),(prefers-reduced-motion:reduce){
  /* unpinned static poster: no canvas, no pin, the copy reads immediately */
  .gx-scrub-wrap{min-height:0}
  .gx-scrub-wrap .hero{position:relative;height:auto;min-height:100svh}
  .gx-scrub-canvas{display:none}
}

/* parallax depth layers */
.px{will-change:transform}

/* ---------- sticky stack ---------- */
.stack{display:grid;gap:0}
.stack__card{position:sticky;top:12vh;min-height:58vh;display:grid;align-content:center;
  gap:1rem;padding:clamp(2rem,5vw,4.5rem);border-radius:var(--radius);
  background:var(--c-field);border-top:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  transform-origin:top center}
.stack__n{font-family:var(--f-utility);font-size:.72rem;letter-spacing:.2em;opacity:.62}

/* ---------- horizontal pin ---------- */
.hpin{height:var(--hpin-h,320vh);position:relative}
.hpin__vp{position:sticky;top:0;height:100svh;display:flex;align-items:center;overflow:hidden}
.hpin__track{display:flex;gap:clamp(1.5rem,4vw,4rem);padding-inline:8vw;will-change:transform}
.hpin__panel{flex:0 0 min(62vw,720px);display:grid;gap:1rem;align-content:center}

/* ---------- scrub ---------- */
.scrub{height:var(--scrub-h,300vh);position:relative}
.scrub__vp{position:sticky;top:0;height:100svh;overflow:hidden;display:grid;place-items:center}
.scrub__media{position:absolute;inset:0}
.scrub__media video,.scrub__media img{width:100%;height:100%;object-fit:cover}
.scrub__beat{position:relative;text-align:center;max-width:34ch;opacity:0;
  transform:translateY(20px);transition:opacity 460ms var(--ease),transform 460ms var(--ease);
  grid-area:1/1}
.scrub__beat.on{opacity:1;transform:none}

/* ---------- gallery ---------- */
.gal{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1rem,2.4vw,2.6rem)}
.gal__fig{position:relative;overflow:hidden;border-radius:var(--radius)}
.gal__fig img{transform:scale(1.24);transition:transform 1300ms var(--ease)}
.gal__fig.in img{transform:scale(1)}
.gal__fig:nth-child(3n+1){grid-column:span 7}
.gal__fig:nth-child(3n+2){grid-column:span 5;margin-top:clamp(2rem,9vw,8rem)}
.gal__fig:nth-child(3n+3){grid-column:span 8;grid-column-start:4}
.gal__cap{font-family:var(--f-utility);font-size:.74rem;letter-spacing:.12em;
  text-transform:uppercase;opacity:.7;margin-top:.8rem}

/* ---------- cascade grid ---------- */
/* "Used by" client cards. Premium glass cards (were a flat 1px-gap spreadsheet grid that
   read as skeletal boxes over a GL field): real gap + rounded corners + an accent tick +
   hover lift, and semi-transparent glass so a particle background still glows through
   (GL-field decree). Works on dark GL packs and light packs alike. */
.casc{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));
  gap:14px;background:transparent}
.casc__cell{position:relative;overflow:hidden;
  background:color-mix(in srgb,var(--c-field) 52%,transparent);
  backdrop-filter:blur(14px) saturate(1.15);
  border:1px solid color-mix(in srgb,var(--c-ink) 13%,transparent);border-radius:14px;
  padding:1.7rem 1.5rem;min-height:8.4rem;
  display:grid;align-content:center;gap:.55rem;opacity:0;transform:translateY(18px) scale(.97);
  transition:opacity 640ms var(--ease),transform 760ms var(--ease),border-color 320ms var(--ease),background 320ms var(--ease);transition-delay:var(--d,0ms)}
.casc__cell::before{content:"";position:absolute;left:1.5rem;top:1.4rem;width:22px;height:2px;
  background:var(--c-accent);opacity:.85}
.casc__cell strong{font-size:1.05rem;letter-spacing:-.01em;line-height:1.15;padding-top:.9rem}
.casc__cell .mono{opacity:.5!important}
.casc__cell:hover{transform:translateY(-3px);
  border-color:color-mix(in srgb,var(--c-accent) 50%,transparent);
  background:color-mix(in srgb,var(--c-field) 66%,transparent)}
.casc__cell.in{opacity:1;transform:none}

/* ---------- counters ---------- */
.cnt{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  gap:clamp(2rem,5vw,5rem);align-items:end}
.cnt__v{font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(3rem,8vw,7rem);line-height:.85;font-variant-numeric:tabular-nums;
  letter-spacing:var(--tr-display)}
/* flap tiles are 1.18em tall — the .85 line box pushed them ~21px below the baseline */
.cnt__v:has(.flap){line-height:1.18}
.cnt__l{font-family:var(--f-utility);font-size:.74rem;letter-spacing:.14em;
  text-transform:uppercase;opacity:.7;margin-top:.9rem;border-top:1px solid currentColor;padding-top:.9rem}

/* ---------- pricing ---------- */
.price{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:clamp(1.2rem,2.6vw,2.4rem);align-items:stretch}
.price__card{display:grid;gap:1.1rem;align-content:start;padding:2.2rem 1.8rem;border-radius:var(--radius);
  border:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent);
  background:color-mix(in srgb,var(--c-dominant) 62%,transparent);backdrop-filter:blur(10px);
  opacity:0;transform:translateY(22px);transition:opacity 640ms var(--ease),transform 760ms var(--ease);transition-delay:var(--d,0ms)}
.price__card.in{opacity:1;transform:none}
.price__card--hot{border-color:var(--c-accent);box-shadow:0 0 0 1px var(--c-accent)}
.price__tier{font-size:.74rem;letter-spacing:.14em;text-transform:uppercase;opacity:.7}
.price__v{font-size:clamp(2rem,4.4vw,3.2rem);line-height:.95;font-variant-numeric:tabular-nums}
.price__ft{list-style:none;display:grid;gap:.55rem;padding:0;margin:0}
.price__ft li{padding-left:1.2rem;position:relative;font-size:.92rem;line-height:1.5}
.price__ft li::before{content:"";position:absolute;left:0;top:.52em;width:.55em;height:.55em;
  border-radius:50%;background:var(--c-accent)}

/* ---------- team ---------- */
.team{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:clamp(1.2rem,2.6vw,2.4rem)}
.team__card{display:grid;gap:.7rem;align-content:start;padding:2rem 1.6rem;border-radius:var(--radius);
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  /* frosted backing: on orbit/nebula the GL object glows through transparent cards
     (role text measured 1.12:1 over the bloom); invisible on flat tracks */
  background:color-mix(in srgb,var(--c-dominant) 62%,transparent);backdrop-filter:blur(10px);
  opacity:0;transform:translateY(20px);transition:opacity 640ms var(--ease),transform 760ms var(--ease);transition-delay:var(--d,0ms)}
.team__card.in{opacity:1;transform:none}
.team__ph{overflow:hidden;border-radius:var(--radius);aspect-ratio:4/5}
.team__ph img{width:100%;height:100%;object-fit:cover}
.team__mono{display:grid;place-items:center;aspect-ratio:4/5;border-radius:var(--radius);
  font-size:clamp(3rem,7vw,5rem);background:color-mix(in srgb,var(--c-accent) 14%,transparent);color:var(--c-accent)}
.team__role{font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;opacity:.7}

/* ---------- process steps ---------- */
.step{list-style:none;margin:0;padding:0;display:grid;gap:clamp(1.4rem,3vw,2.4rem);max-width:52rem}
.step__row{display:grid;grid-template-columns:auto 1fr;gap:1.6rem;align-items:start;
  padding:0 1rem clamp(1.4rem,3vw,2.4rem);border-bottom:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  background:color-mix(in srgb,var(--c-dominant) 55%,transparent);backdrop-filter:blur(8px);border-radius:var(--radius);
  opacity:0;transform:translateY(20px);transition:opacity 640ms var(--ease),transform 760ms var(--ease);transition-delay:var(--d,0ms)}
.step__row.in{opacity:1;transform:none}
.step__n{font-size:clamp(1.6rem,3.4vw,2.4rem);line-height:1;opacity:.45;font-variant-numeric:tabular-nums}
.step__txt{display:grid;gap:.6rem}

/* ---------- location + hours ---------- */
.hrs{display:grid;gap:1px;max-width:36rem;background:color-mix(in srgb,var(--c-ink) 12%,transparent);
  border-block:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent)}
.hrs__row{display:flex;justify-content:space-between;gap:2rem;padding:1.05rem 0;background:var(--c-dominant)}
.hrs__row span{opacity:.8}
.hrs__row b{font-variant-numeric:tabular-nums;letter-spacing:.06em}

/* ---------- media split ---------- */
.split{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.5rem,4vw,4rem);align-items:center}
.split__txt{grid-column:1/span 5;display:grid;gap:1.2rem}
.split__med{grid-column:6/span 8;overflow:hidden;border-radius:var(--radius);
  margin-right:calc(var(--bleed,0px) * -1)}

/* ---------- marquee ---------- */
.mq{overflow:hidden;white-space:nowrap;padding-block:1.4rem;
  border-block:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent)}
.mq__t{display:inline-block;will-change:transform;font-family:var(--f-display);
  font-size:clamp(2rem,5vw,4rem);letter-spacing:var(--tr-display);opacity:.85}

/* ---------- cursor ---------- */
.cur{position:fixed;top:0;left:0;width:14px;height:14px;border-radius:50%;
  background:var(--c-ink);mix-blend-mode:difference;pointer-events:none;z-index:99;
  transform:translate3d(-50px,-50px,0);transition:width .3s var(--ease),height .3s var(--ease)}
.cur.big{width:52px;height:52px}
@media (hover:none){.cur{display:none}}

/* ---------- contact ---------- */
.cta-block{display:grid;gap:2rem;justify-items:start}
.cta-big{font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.4rem,7vw,6rem);line-height:.9;letter-spacing:var(--tr-display);
  display:inline-block;will-change:transform}
footer{padding-block:4rem 3rem;border-top:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  font-family:var(--f-utility);font-size:.74rem;letter-spacing:.1em;text-transform:uppercase;opacity:.7}

/* ---------- responsive ---------- */
@media (max-width:900px){
  .split__txt,.split__med{grid-column:1/-1}
  .gal__fig,.gal__fig:nth-child(3n+1),.gal__fig:nth-child(3n+2),.gal__fig:nth-child(3n+3){grid-column:1/-1;grid-column-start:auto;margin-top:0}
  .hpin{height:auto}
  .hpin__vp{position:static;height:auto;display:block}
  .hpin__track{flex-direction:column;transform:none!important;padding-inline:0}
  .hpin__panel{flex:1 1 auto}
  .scrub{height:auto}
  .scrub__vp{position:static;height:auto;display:grid;gap:2rem;padding-block:4rem}
  .scrub__beat{position:static;opacity:1;transform:none;grid-area:auto;text-align:left}
  .scrub__media{position:relative;inset:auto;height:60svh}
  .stack__card{position:static;min-height:0}
  section{--rhythm:6.5rem}
  section[data-rhythm="loose"]{--rhythm:8rem}
}

/* ---------- reduced motion: still beautiful, just still ---------- */
@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition-duration:180ms!important;transition-delay:0ms!important}
  [data-line] > span,[data-rise],[data-curtain],.casc__cell,.scrub__beat{transform:none!important;opacity:1!important;clip-path:none!important}
  .hero__media{clip-path:none!important}
  .hero__media video,.hero__media img{transform:none!important}
  [data-exit]{transform:none!important;filter:none!important;opacity:1!important}
  .cur{display:none}
}


/* ============================================================
   EMAIL CAPTURE
   ============================================================ */
.opt{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.4rem,3vw,3.4rem);align-items:end}
.opt__txt{grid-column:1/span 6;display:grid;gap:1.1rem}
.opt__form{grid-column:8/span 5;display:grid;gap:.9rem}
.opt__incentive{display:inline-flex;align-items:center;gap:.6rem;font-family:var(--f-utility);
  font-size:.72rem;letter-spacing:.14em;text-transform:uppercase;color:var(--c-accent)}
.opt__incentive::before{content:"";width:1.6rem;height:1px;background:currentColor}
.opt__row{display:grid;gap:.55rem}
.opt__label{font-family:var(--f-utility);font-size:.68rem;letter-spacing:.14em;
  text-transform:uppercase;opacity:.72}
.opt__in{width:100%;padding:1rem 0;background:transparent;border:0;
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 26%,transparent);
  color:var(--c-ink);font-family:var(--f-body);font-size:1.05rem;outline:0;
  transition:border-color 420ms var(--ease)}
.opt__in::placeholder{color:color-mix(in srgb,var(--c-ink) 55%,transparent)}
.opt__in:focus-visible{border-bottom-color:var(--c-accent)}
.opt__in[aria-invalid="true"]{border-bottom-color:var(--c-accent);animation:nudge .32s var(--ease)}
@keyframes nudge{25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
.opt__btn{justify-self:start;margin-top:.6rem;padding:1.05rem 2.2rem;border:0;cursor:pointer;
  background:var(--c-accent);color:var(--c-on-accent,var(--c-dominant));border-radius:var(--radius);
  font-family:var(--f-utility);font-size:.78rem;letter-spacing:.16em;text-transform:uppercase;
  position:relative;overflow:hidden;transition:transform 420ms var(--ease)}
.opt__btn span{position:relative;z-index:1}
.opt__btn::before{content:"";position:absolute;inset:0;background:var(--c-ink);
  transform:translateY(101%);transition:transform 520ms var(--ease)}
.opt__btn:hover::before{transform:translateY(0)}
.opt__btn:hover span{color:var(--c-dominant)}
.opt__btn[disabled]{opacity:.5;cursor:progress}
.opt__consent{font-size:.76rem;opacity:.62;max-width:38ch;line-height:1.5}
.opt__hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.opt__done{display:none;gap:.8rem;align-content:center}
.opt.sent .opt__form{display:none}
.opt.sent .opt__done{display:grid}
.opt__tick{width:2.6rem;height:2.6rem;border-radius:50%;border:1px solid var(--c-accent);
  display:grid;place-items:center;color:var(--c-accent)}
.opt__tick svg{width:1rem;height:1rem}
.opt__err{font-size:.8rem;color:var(--c-accent);min-height:1.1rem}

/* ============================================================
   SITE ASSISTANT
   ============================================================ */
.bot{position:fixed;right:clamp(1rem,3vw,2.2rem);bottom:clamp(1rem,3vw,2.2rem);z-index:80;
  display:grid;justify-items:end;gap:.9rem;font-family:var(--f-body)}
.bot__fab{display:inline-flex;align-items:center;gap:.7rem;padding:.95rem 1.35rem;border:0;
  cursor:pointer;background:var(--c-accent);color:var(--c-on-accent,var(--c-dominant));border-radius:999px;
  font-family:var(--f-utility);font-size:.74rem;letter-spacing:.13em;text-transform:uppercase;
  box-shadow:0 14px 40px -12px color-mix(in srgb,var(--c-accent) 70%,transparent);
  transition:transform 420ms var(--ease),opacity 300ms var(--ease)}
.bot__fab:hover{transform:translateY(-2px)}
/* On phones the labelled pill is wide enough to sit over hero stats/headings in
   the bottom corner; collapse it to a compact icon-only circle (the label is the
   business name, redundant with the panel header once opened). */
@media (max-width:560px){
  .bot__fab{padding:0;width:3.2rem;height:3.2rem;justify-content:center}
  .bot__fab .bot__label{display:none}
  .bot__dot{width:.7rem;height:.7rem}
}
.bot__dot{width:.5rem;height:.5rem;border-radius:50%;background:currentColor;
  animation:breathe 3.2s ease-in-out infinite}
.bot__panel{width:min(23rem,calc(100vw - 2rem));background:var(--c-field);
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);border-radius:var(--radius);
  box-shadow:0 34px 90px -30px rgba(0,0,0,.75);overflow:hidden;
  transform-origin:bottom right;transform:translateY(14px) scale(.94);opacity:0;
  pointer-events:none;transition:transform 520ms var(--ease),opacity 320ms var(--ease)}
.bot.open .bot__panel{transform:none;opacity:1;pointer-events:auto}
.bot.open .bot__fab{opacity:0;pointer-events:none}
.bot__head{display:flex;align-items:center;justify-content:space-between;gap:1rem;
  padding:1.05rem 1.2rem;border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent)}
.bot__who{display:grid;gap:.15rem}
.bot__name{font-family:var(--f-utility);font-size:.74rem;letter-spacing:.14em;text-transform:uppercase}
.bot__status{font-size:.68rem;opacity:.65;display:flex;align-items:center;gap:.4rem}
.bot__status i{width:.4rem;height:.4rem;border-radius:50%;background:var(--c-accent)}
.bot__x{background:0;border:0;color:inherit;cursor:pointer;font-size:1.3rem;line-height:1;opacity:.55}
.bot__x:hover{opacity:1}
.bot__log{padding:1.1rem 1.2rem;display:grid;gap:.75rem;max-height:19rem;overflow-y:auto;
  scrollbar-width:thin}
.bot__msg{max-width:88%;padding:.75rem .95rem;border-radius:var(--radius);font-size:.9rem;
  line-height:1.5;opacity:0;transform:translateY(8px);animation:msgIn .42s var(--ease) forwards}
@keyframes msgIn{to{opacity:1;transform:none}}
.bot__msg--bot{background:color-mix(in srgb,var(--c-ink) 8%,transparent);justify-self:start}
.bot__msg--me{background:var(--c-accent);color:var(--c-on-accent,var(--c-dominant));justify-self:end}
.bot__msg a{color:var(--c-accent);text-decoration:underline;text-underline-offset:3px}
.bot__msg--me a{color:inherit}
.bot__typing{display:inline-flex;gap:.28rem;padding:.85rem .95rem}
.bot__typing i{width:.34rem;height:.34rem;border-radius:50%;background:currentColor;opacity:.4;
  animation:blink 1.25s infinite}
.bot__typing i:nth-child(2){animation-delay:.18s}
.bot__typing i:nth-child(3){animation-delay:.36s}
@keyframes blink{40%{opacity:1;transform:translateY(-2px)}}
.bot__chips{display:flex;flex-wrap:wrap;gap:.45rem;padding:0 1.2rem 1.1rem}
.bot__chip{padding:.5rem .85rem;cursor:pointer;font-size:.78rem;
  background:transparent;color:inherit;border-radius:999px;
  border:1px solid color-mix(in srgb,var(--c-ink) 22%,transparent);
  transition:border-color 300ms var(--ease),color 300ms var(--ease)}
.bot__chip:hover{border-color:var(--c-accent);color:var(--c-accent)}
.bot__foot{padding:.85rem 1.2rem;border-top:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  font-size:.7rem;opacity:.6}

@media (max-width:900px){
  .opt__txt,.opt__form{grid-column:1/-1}
  .bot__panel{width:calc(100vw - 2rem)}
}
@media (prefers-reduced-motion:reduce){
  .bot__panel{transition-duration:150ms}
  .bot__msg{animation:none;opacity:1;transform:none}
  .bot__dot,.bot__typing i{animation:none}
}


/* ============================================================
   3D DEPTH LAYER
   ============================================================ */
[data-tilt]{transform-style:preserve-3d;will-change:transform;
  transition:box-shadow 500ms var(--ease)}
[data-tilt]:hover{box-shadow:0 40px 90px -30px rgba(0,0,0,.7)}
[data-tilt] > *{transform:translateZ(28px)}
.tilt-wrap{perspective:1000px}

/* giant ghost type behind sections: the classic expensive-site move */
.ghost{position:absolute;top:50%;left:50%;z-index:-1;pointer-events:none;white-space:nowrap;
  font-family:var(--f-display);font-weight:var(--fw-display);letter-spacing:var(--tr-display);
  font-size:clamp(8rem,26vw,24rem);line-height:1;
  color:transparent;-webkit-text-stroke:1px color-mix(in srgb,var(--c-ink) 13%,transparent);
  will-change:transform;transform:translate(-50%,-50%)}

/* continuous heading drift: headings float against the scroll */
[data-float]{will-change:transform}

/* media enters through 3D space, then breathes with scroll */
.m3d{overflow:hidden;border-radius:var(--radius);transform-style:preserve-3d}
.m3d img,.m3d video{will-change:transform;transform:scale(1.18);object-fit:cover}

/* velocity skew: type leans into scroll speed */
[data-skew]{will-change:transform}

/* marquee divider between acts */
.mq--ghost .mq__t{color:transparent;-webkit-text-stroke:1px color-mix(in srgb,var(--c-ink) 30%,transparent)}
.mq em{font-style:normal;color:var(--c-accent);-webkit-text-stroke:0}

/* accent orb that follows scroll through the page */
.orb{position:fixed;z-index:-2;width:34vmin;height:34vmin;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-accent) 55%,transparent),transparent 70%);
  filter:blur(30px);opacity:.5;will-change:transform}

/* counters get accent sweep */
.cnt__v{position:relative;background:linear-gradient(90deg,var(--c-ink) 45%,var(--c-accent) 50%,var(--c-ink) 55%);
  background-size:280% 100%;background-position:100% 0;-webkit-background-clip:text;background-clip:text;
  -webkit-text-fill-color:transparent}
.cnt.in .cnt__v{animation:sweep 1.6s var(--ease) .3s forwards}
@keyframes sweep{to{background-position:0 0}}
/* LAST-RESORT PAINT GUARANTEE. The sweep above clips its gradient to the text, and
   the gradient is the ELEMENT'S box — so a glyph pushed outside that box has no
   background to clip and renders invisible, not merely overflowing. The engine
   shrinks over-wide values to fit first; if a value still cannot fit at the scale
   floor, this class drops the clip entirely and paints the digits directly, which
   cannot depend on the box. A digit is then unable to fall outside the paint at any
   viewport width or value length. */
.cnt__v--solid{background:none;-webkit-background-clip:border-box;background-clip:border-box;
  -webkit-text-fill-color:var(--c-ink);color:var(--c-ink);animation:none!important}
.cnt.in .cnt__v--solid{animation:none!important}
/* flap-tile digits are ::before pseudos: the clipped-to-text background above cannot
   paint into them, so the inherited transparent fill rendered digits invisible */
.cnt__v .flap i::before{-webkit-text-fill-color:var(--c-ink);color:var(--c-ink)}

/* stack cards live in 3D */
.stack{perspective:1200px}
.stack__card{transform-style:preserve-3d;box-shadow:0 -18px 60px -30px rgba(0,0,0,.6)}

/* CTA block: giant type distorts toward cursor */
.cta-big{transform-style:preserve-3d}

@media (max-width:900px){
  .ghost{font-size:clamp(5rem,30vw,10rem)}
  .glow{filter:blur(40px)}
  [data-tilt]{transform:none!important}
  [data-tilt] > *{transform:none}
}
@media (prefers-reduced-motion:reduce){
  .glow,.orb{display:none}
  .pre{display:none}
  [data-char]{transform:none!important;opacity:1!important}
  .ghost{transform:translate(-50%,-50%)!important}
  [data-float],[data-skew],.m3d img,.m3d video{transform:none!important}
  .cnt__v{background:none;-webkit-text-fill-color:currentColor;animation:none}
}


/* ============================================================
   FIXED NAV: logo + CTA. Its absence is what made pages read as a blog.
   ============================================================ */
.nav{pointer-events:none;position:fixed;top:0;left:0;right:0;z-index:90;display:flex;align-items:center;
  justify-content:space-between;padding:1.1rem clamp(1.2rem,3vw,2.6rem);
  transition:background 500ms var(--ease),backdrop-filter 500ms var(--ease)}
.nav.scrolled,body.on-accent .nav{background:color-mix(in srgb,var(--c-dominant) 62%,transparent);
  backdrop-filter:blur(14px) saturate(1.2);
  /* the glass is dominant-tinted, so nav text pairs with ink regardless of what colour
     the body morphed to underneath (dominant-on-dominant measured 1.58:1 on persona-bolt) */
  color:var(--c-ink)}
.nav__brand{display:flex;align-items:center;gap:.75rem}
.nav__logo{height:2.1rem;width:auto;display:block}
.nav__name{font-family:var(--f-utility);font-size:.78rem;letter-spacing:.18em;text-transform:uppercase;
  text-shadow:0 1px 4px color-mix(in srgb,var(--c-dominant) 65%,transparent),
    0 2px 14px color-mix(in srgb,var(--c-dominant) 85%,transparent)}
.nav__cta{padding:.7rem 1.4rem;border:1px solid color-mix(in srgb,var(--c-ink) 30%,transparent);
  border-radius:999px;font-family:var(--f-utility);font-size:.7rem;letter-spacing:.15em;
  text-transform:uppercase;transition:border-color 400ms var(--ease),background 400ms var(--ease),color 400ms var(--ease);
  /* glass fill: the pill floats over arbitrary footage, so it carries its own backing */
  background:color-mix(in srgb,var(--c-dominant) 45%,transparent);backdrop-filter:blur(10px)}
.nav__cta:hover{border-color:var(--c-accent);background:var(--c-accent);color:var(--c-on-accent,var(--c-dominant))}
/* over an accent-morphed body the glass pill tints toward the accent and the flipped
   body colour lands near it (measured 1.18:1) — go solid while the morph holds */
body.on-accent .nav__cta{background:var(--c-ink);color:var(--c-dominant);border-color:transparent}

/* ============================================================
   TRACK 1 — CINEMA. Every section is a full-viewport SCENE on media.
   ============================================================ */
.scene{min-height:100svh;display:grid;align-items:end;padding-block:0 clamp(3rem,8vh,6rem);
  overflow:hidden;isolation:isolate}
.scene--center{align-items:center;text-align:center;justify-items:center}
.scene__bg{position:absolute;inset:0;z-index:-2;clip-path:inset(0 0 0 0)}
.scene__bg video,.scene__bg img{width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.18)) contrast(1.04);will-change:transform}
.scene__scrim{position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 86%,transparent) 0%,
    color-mix(in srgb,var(--c-dominant) 30%,transparent) 42%,transparent 72%)}
.scene--center .scene__scrim{background:radial-gradient(90% 90% at 50% 60%,
    color-mix(in srgb,var(--c-dominant) 58%,transparent) 0%,transparent 78%)}
.scene__inner{display:grid;gap:1.4rem;max-width:min(62rem,90%);
  text-shadow:0 1px 5px color-mix(in srgb,var(--c-dominant) 70%,transparent),
    0 3px 18px color-mix(in srgb,var(--c-dominant) 88%,transparent)}
.scene--center .scene__inner{justify-items:center}
.scene__h{font-size:clamp(2.6rem,7.5vw,7rem);line-height:.92}
.scene__idx{position:absolute;top:clamp(5rem,10vh,7rem);right:clamp(1.2rem,3vw,2.6rem);
  font-family:var(--f-utility);font-size:.72rem;letter-spacing:.22em;opacity:.7}
.scene .cnt__v{font-size:clamp(4rem,12vw,11rem)}
.scene .cnt{width:100%}

/* ============================================================
   TRACK 2 — MACHINE. Persistent HUD grid, mono readouts, side panels.
   ============================================================ */
.hud{position:fixed;inset:0;z-index:-1;pointer-events:none;opacity:.5}
.hud__grid{position:absolute;inset:0;
  background:
    linear-gradient(color-mix(in srgb,var(--c-ink) 5%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 5%,transparent) 1px,transparent 1px);
  background-size:8.33vw 8.33vw}
.hud__tick{position:absolute;font-family:var(--f-utility);font-size:.62rem;
  letter-spacing:.18em;opacity:.8;color:var(--c-accent)}
.hud__frame{position:absolute;inset:clamp(.8rem,2vw,1.6rem);
  border:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent)}
.hud__frame::before,.hud__frame::after{content:"";position:absolute;width:14px;height:14px;
  border:1px solid var(--c-accent)}
.hud__frame::before{top:-1px;left:-1px;border-right:0;border-bottom:0}
.hud__frame::after{bottom:-1px;right:-1px;border-left:0;border-top:0}
.panel{display:grid;grid-template-columns:repeat(12,1fr);gap:clamp(1.5rem,3vw,3rem);
  min-height:76svh;align-items:center}
.panel__txt{grid-column:2/span 5;display:grid;gap:1.2rem}
.panel--r .panel__txt{grid-column:7/span 5}
.panel__med{grid-column:8/span 5;position:relative}
.panel--r .panel__med{grid-column:1/span 6}
.panel__med .m3d{border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent)}
.panel__readout{position:absolute;bottom:-1.6rem;left:0;font-family:var(--f-utility);
  font-size:.62rem;letter-spacing:.2em;color:var(--c-accent);opacity:.85}
.spec-line{display:flex;justify-content:space-between;gap:2rem;padding:1.1rem 0;
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  font-size:.95rem}
.spec-line b{font-family:var(--f-utility);font-weight:500;font-variant-numeric:tabular-nums;
  color:var(--c-accent)}
.scrub__hud{position:absolute;inset:clamp(1rem,3vw,2rem);pointer-events:none;
  border:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent)}
.scrub__hud i{position:absolute;font-family:var(--f-utility);font-size:.62rem;
  letter-spacing:.2em;font-style:normal;color:var(--c-accent);background:var(--c-dominant);
  padding:.2rem .5rem}
.scrub__hud i:nth-child(1){top:-0.6rem;left:1rem}
.scrub__hud i:nth-child(2){bottom:-0.6rem;right:1rem}

/* ============================================================
   TRACK 3 — FULL-SCREEN CARD DECK (multiple card styles)
   ============================================================ */
.stack--fs{display:block;padding-bottom:8vh}
.stack--fs .stack__card{position:sticky;min-height:52svh;
  border-radius:26px 26px 0 0;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent);
  box-shadow:0 -30px 70px -30px rgba(0,0,0,.65);
  display:grid;grid-template-columns:repeat(12,1fr);align-items:center;align-content:center;
  gap:clamp(1.5rem,3vw,3rem);padding:clamp(3.5rem,7vh,5.5rem) clamp(1.5rem,5vw,5rem)}
.stack--fs .stack__card:nth-child(1){top:calc(10vh + 0rem)}
.stack--fs .stack__card:nth-child(2){top:calc(10vh + 3.2rem)}
.stack--fs .stack__card:nth-child(3){top:calc(10vh + 6.4rem)}
.stack--fs .stack__card:nth-child(4){top:calc(10vh + 9.6rem)}
.stack--fs .stack__card:nth-child(5){top:calc(10vh + 12.8rem)}
.stack--fs .stack__card:nth-child(6){top:calc(10vh + 16rem)}
.stack--fs .stack__card:nth-child(7){top:calc(10vh + 19.2rem)}
.stack--fs .stack__card:nth-child(8){top:calc(10vh + 22.4rem)}
.stack--fs .stack__card:nth-child(odd){background:var(--c-field)}
.stack--fs .stack__card:nth-child(even){background:color-mix(in srgb,var(--c-field) 60%,var(--c-dominant))}
.stack--fs .stack__n{grid-column:1/-1;font-size:.72rem}
.stack--fs .fcard__txt{grid-column:1/span 6;display:grid;gap:1.2rem}
.stack--fs .fcard__med{grid-column:8/span 5}
.stack--fs .stack__card:nth-child(even) .fcard__txt{grid-column:7/span 6}
.stack--fs .stack__card:nth-child(even) .fcard__med{grid-column:1/span 5;grid-row:2}
.stack--fs .fcard__big{font-size:clamp(2.4rem,6vw,5.2rem);line-height:.94}
.stack--fs .fcard__tag{font-family:var(--f-utility);font-size:4.4rem;line-height:1;
  opacity:.12;font-weight:700}

/* grid cards: dense responsive service grid (catalog card_style "grid"). Columns are
   BOUNDED (not 1fr) and the track group is centred, so an item count that doesn't fill the
   last row (e.g. 6 items in a 4-wide row) centres the orphans instead of left-weighting them
   with empty tracks beside — the "widgets not aligned" defect on the horizon capability
   section (2026-08-14). max-width caps keep cards from ballooning on wide monitors. */
.stack--grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,360px));
  justify-content:center;gap:clamp(1rem,2vw,1.8rem);perspective:none}
.stack--grid .stack__card{position:static;min-height:0;top:auto;display:grid;align-content:start;
  gap:.9rem;padding:clamp(1.6rem,2.6vw,2.4rem);border-radius:var(--radius);
  background:var(--c-field);border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  box-shadow:none;transition:transform 420ms var(--ease),border-color 420ms var(--ease)}
.stack--grid .stack__card:hover{transform:translateY(-6px);border-color:var(--c-accent)}
.gcard__n{font-family:var(--f-utility);font-size:.72rem;letter-spacing:.2em;color:var(--c-accent);
  display:inline-flex;align-items:center;gap:.5rem}
.gcard__med{border-radius:calc(var(--radius) - 4px);overflow:hidden;aspect-ratio:16/10}
.gcard__med img{width:100%;height:100%;object-fit:cover;filter:saturate(var(--media-chroma,1.18))}
/* Grid-card titles get line-height >= 1: the shared .display line-height is ~0.88 (tight
   display setting), which on a TWO-LINE grid-card title made the lines overlap each other and
   pushed the first line's ascent up into the image above it (the "01 Private estate sales
   overlapping" defect, only visible on image cards with wrapping titles, at wide/4K widths).
   1.12 keeps it condensed but non-overlapping; margin-top guarantees clearance from the image. */
.stack--grid .h-2{font-size:clamp(1.15rem,1.9vw,1.6rem);line-height:1.12;margin-top:.3rem}
.stack--grid .lede{font-size:.95rem}
body[data-track="nebula"] .stack--grid .stack__card,
body[data-track="orbit"] .stack--grid .stack__card{
  background:color-mix(in srgb,var(--c-field) 70%,transparent);backdrop-filter:blur(12px)}

/* split cards: media half / text half, alternating */
/* sticky section heading: it pins near the top and STAYS on screen while the cards scroll/stack
   beneath it (the cards pin lower, so they never overlap it). Only used by the split deck. */
.stk-head{position:sticky;top:clamp(78px,11vh,116px);z-index:6;padding-bottom:1.2rem}
.stack--split{margin-top:clamp(2.5rem,7vh,5rem)}
.stack--split .stack__card{position:sticky;top:clamp(210px,37vh,400px);min-height:50svh;
  display:grid;grid-template-columns:1fr 1fr;gap:0;padding:0;overflow:hidden;border-radius:24px;
  background:linear-gradient(155deg,color-mix(in srgb,var(--c-ink) 13%,var(--c-field)),color-mix(in srgb,var(--c-ink) 3.5%,var(--c-field)));
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  box-shadow:inset 0 1px 0 color-mix(in srgb,var(--c-ink) 18%,transparent),0 44px 110px -52px color-mix(in srgb,var(--c-dominant) 92%,black)}
.stack--split .sc__med{position:relative;overflow:hidden;background:color-mix(in srgb,var(--c-dominant) 70%,var(--c-field))}
.stack--split .sc__med::after{content:"";position:absolute;inset:0;pointer-events:none;box-shadow:inset -1px 0 0 color-mix(in srgb,var(--c-ink) 8%,transparent)}
.stack--split .sc__med img,.stack--split .sc__med video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.stack--split .sc__txt{display:grid;align-content:center;gap:1.1rem;padding:clamp(2rem,4vw,4rem)}
.stack--split .stack__card:nth-child(even) .sc__med{order:2}

@media (max-width:900px){
  .panel__txt,.panel--r .panel__txt{grid-column:1/-1}
  .panel__med,.panel--r .panel__med{grid-column:1/-1}
  .stack--fs .fcard__txt,.stack--fs .fcard__med,
  .stack--fs .stack__card:nth-child(even) .fcard__txt,
  .stack--fs .stack__card:nth-child(even) .fcard__med{grid-column:1/-1;grid-row:auto}
  .stack--split .stack__card{grid-template-columns:1fr;min-height:0}
  .stack--split .sc__med{min-height:44svh;position:relative}
  .scene{align-items:end}
  .hud__grid{background-size:25vw 25vw}
}
@media (prefers-reduced-motion:reduce){
  .scene__bg video{display:none}
}

/* ---------- CARD SKIN LIBRARY (src/cards.js is the contract) ----------
   Future skin CSS lands HERE, one block per skin; scripts/check-catalog.js
   asserts every registry skin's class literally exists in this file.
   Deck skins:    .stack.stack--<id>; full-height decks also carry .stack--tall
                  (engine.js skips the section rise transform for
                  .stack--fs/.stack--tall, the takeover marker).
   Static skins:  .cardset.cardset--<id> non-.stack container; cards keep
                  .stack__card for the reveal observer and reset the inherited
                  sticky (model: .stack--grid above).
   Takeover skins: pinned ~320svh, full-bleed breakout of .wrap
                  (width:100vw; margin-left:calc(50% - 50vw)), reverted in the
                  mobile static fallback.
   Rules: colors via --c-* tokens only — light/accent surfaces use
   --c-on-light/--c-on-accent; mobile collapse <=900px; every animated
   property gets a reduced-motion still state.
   LINT REGION: scripts/check-catalog.js statically gates everything between
   this banner and the next slash-star ---------- banner (raw hex, opaque
   stage/sticky/scene gradients, takeover full-bleed breakout). Skin CSS must
   land inside this region and nowhere else. */

/* ---- ported-skin shared scaffold (Phase 3a, proven grafts P4-P8) ----
   Takeover scenes: pinned, full-bleed, GL-transparent (token radials <=15%
   only — the page's GL field must glow through). All color via --c-* tokens. */
.gx-scene{--scene-progress:0;position:relative;isolation:isolate;
  --gx-panel-strong:color-mix(in srgb,var(--c-field) 92%,transparent);
  --gx-panel-deep:color-mix(in srgb,var(--c-dominant) 94%,transparent);
  --gx-line:color-mix(in srgb,var(--c-ink) 16%,transparent);
  --gx-line-strong:color-mix(in srgb,var(--c-ink) 30%,transparent);
  --gx-muted:color-mix(in srgb,var(--c-ink) 62%,transparent);
  --gx-shadow:0 34px 90px -30px rgba(0,0,0,.65)}
/* Takeover atmosphere (2026-08-15): the ported takeover scenes sit transparent over the
   pack's page-wide GL field, so on dark packs the area a card does not cover reads as a black
   void — the "blank space" the user flagged. Fill that space with token-derived atmospheric
   depth: an upper accent bloom plus two lower-corner glows in the pack's glow temperatures, so
   the empty zone reads as intentional composition, not a hole. All token-only and capped at
   <=13% alpha per the GL-field decree (never an opaque backdrop), so it adds depth on dark
   packs and only a faint wash on light ones. */
.gx-scene::before{position:absolute;inset:0;z-index:-1;content:"";pointer-events:none;
  background:
    radial-gradient(120% 78% at 50% 20%,color-mix(in srgb,var(--c-accent) 13%,transparent),transparent 60%),
    radial-gradient(85% 66% at 82% 96%,color-mix(in srgb,var(--c-glow2,var(--c-accent)) 11%,transparent),transparent 62%),
    radial-gradient(85% 66% at 16% 92%,color-mix(in srgb,var(--c-glow3,var(--c-accent)) 9%,transparent),transparent 62%)}
.gx-sticky{position:sticky;top:0;display:grid;min-height:100svh;align-items:center;
  padding:14px 0 20px;overflow:clip}
.gx-stage{position:relative;width:100%;min-height:clamp(540px,calc(100svh - 200px),780px);
  perspective:1500px;transform-style:preserve-3d}
.gx-glass{position:relative;margin:0;border:0;
  background:linear-gradient(145deg,var(--gx-panel-strong),var(--gx-panel-deep));
  box-shadow:var(--gx-shadow),inset 0 1px 0 var(--gx-line);
  -webkit-backdrop-filter:none;backdrop-filter:none}
.gx-glass h3{margin-top:0;margin-bottom:10px;letter-spacing:-.03em;line-height:1.06;
  color:var(--c-ink);font-family:var(--f-display);font-weight:var(--fw-display);
  -webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-glass p{margin:0;color:var(--gx-muted);font-size:.95rem;backface-visibility:hidden}
/* de-AI baked: kicker has NO glowing-dot bullet (P6/P8 decree) */
.gx-kicker{display:inline-block;width:fit-content;margin-bottom:12px;color:var(--c-accent);
  font-family:var(--f-utility);font-size:.73rem;font-weight:700;letter-spacing:.13em;text-transform:uppercase}
.gx-cinematic-index{position:absolute;top:20px;right:25px;z-index:3;color:var(--gx-line-strong);
  font-family:var(--f-utility);font-size:5rem;font-weight:800;letter-spacing:-.09em;line-height:1}
.gx-stars{display:block;margin-bottom:10px;color:var(--c-accent);font-size:.95rem;letter-spacing:.35em}
/* On a LIGHT-theme pack a bright accent (e.g. sc-barbell blood orange #FF4A1C) fails
   contrast as small TEXT on the light ground — a testimonial kicker measured 1.47:1.
   Darken the accent toward ink for accent-COLOURED TEXT only (hue stays branded);
   decorative glows, borders and dots keep the full accent. Scoped to data-theme=light
   so dark packs are untouched. Covers the base kicker/stars + the process step-accent
   copy kickers, which cycle accent/glow the same way. */
body[data-theme="light"] .gx-kicker,
body[data-theme="light"] .gx-stars,
body[data-theme="light"] .gxt-role,
body[data-theme="light"] .gxp-mtn-altitude b,
body[data-theme="light"] .gxp-wheel-copy .gx-kicker,
body[data-theme="light"] .gxp-road-copy .gx-kicker,
body[data-theme="light"] .gxp-mtn-copy .gx-kicker{
  color:color-mix(in srgb,var(--member-accent,var(--step-accent,var(--c-accent))) 56%,var(--c-ink))}
.gx-card-cta{margin-top:16px;display:inline-flex;padding:.6rem 0}
/* per-card deck affordance = QUIET text link. It must never read as the loud primary-CTA
   pill nor repeat the booking label — that lives in the hero, nav, contact + footer (and
   pricing tiers, which keep the prominent .gxpr-cta). A deck row otherwise stamped N
   identical bordered "Book a table" buttons across the cards (user: repeated skins). The
   bordered tracks (nebula/orbit) add a pill border to .hero__cta — strip it for card CTAs. */
.gx-card-cta:not(.gxpr-cta){font-size:.72rem;opacity:.9;padding:.45rem 0}
.gx-card-cta:not(.gxpr-cta):hover{opacity:1}
body[data-track="nebula"] .gx-card-cta:not(.gxpr-cta),
body[data-track="orbit"] .gx-card-cta:not(.gxpr-cta){border:none;padding:.45rem 0}
/* shared no-asset media pane (Phase 3b gallery family) — token gradient +
   monogram, the P3 team precedent: media:required skins keep their layout when
   a single item has no image; an empty img src is never emitted */
.gx-pane-fallback{display:grid;width:100%;height:100%;min-height:inherit;place-items:center;
  background:
    radial-gradient(circle at 24% 22%,color-mix(in srgb,var(--c-accent) 24%,transparent),transparent 52%),
    radial-gradient(circle at 78% 80%,color-mix(in srgb,var(--c-glow2) 16%,transparent),transparent 55%),
    linear-gradient(150deg,color-mix(in srgb,var(--c-field) 96%,transparent),color-mix(in srgb,var(--c-dominant) 97%,transparent));
  color:color-mix(in srgb,var(--c-ink) 26%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(4rem,10vw,9rem);letter-spacing:-.06em;line-height:1;user-select:none}

/* ---- skin: tunnel (cards-01, proven P4/P5) — enlarged cards (user: "too
   small"), bottom-anchored h3, single index numeral per card (de-AI) ---- */
.gx-tunnel{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-tunnel .gx-stage{display:grid;place-items:center}
.gx-tunnel-rings{position:absolute;inset:50%;width:0;height:0;transform-style:preserve-3d}
.gx-tunnel-ring{position:absolute;top:0;left:0;width:calc(340px + var(--ring-index) * 120px);
  aspect-ratio:1;border:1px solid var(--gx-line);border-radius:50%;
  transform:translate(-50%,-50%) translateZ(calc(var(--ring-index) * -120px))}
.gx-tunnel-card{position:absolute;top:50%;left:50%;width:min(940px,80vw);
  min-height:clamp(440px,58svh,600px);padding:48px 52px;border-radius:34px;
  display:flex;flex-direction:column;justify-content:flex-end;gap:10px;
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,opacity,filter}
.gx-tunnel-card h3{max-width:660px;margin-top:auto;font-size:clamp(2.5rem,4.3vw,4.4rem)}
.gx-tunnel-card p{max-width:560px;font-size:clamp(1rem,1.25vw,1.2rem)}
.gx-tunnel-card .gx-index{position:absolute;top:20px;right:28px;color:var(--gx-line-strong);
  font-family:var(--f-utility);font-size:9rem;font-weight:800;letter-spacing:-.08em;line-height:1}

/* ---- skin: fan (cards-02, proven P6) — opaque enlarged cards, round chip is
   the single numbering (de-AI: kicker counters removed) ---- */
.gx-fan{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-fan .gx-stage{display:grid;place-items:center;overflow:hidden;padding-inline:16px}
.gx-fan-card{position:absolute;top:50%;left:50%;
  width:clamp(268px,23vw,352px);min-height:clamp(470px,60vh,570px);
  padding:clamp(26px,2.2vw,38px);border-radius:26px;display:flex;flex-direction:column;
  background:linear-gradient(155deg,color-mix(in srgb,var(--c-field) 97%,transparent),color-mix(in srgb,var(--c-dominant) 98%,transparent));
  opacity:var(--card-opacity,1);
  filter:brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateZ(var(--card-rz,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-origin:50% 110%;transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-fan-icon{display:grid;width:52px;aspect-ratio:1;margin-bottom:clamp(30px,5vh,56px);
  place-items:center;border:1px solid var(--gx-line-strong);border-radius:50%;
  background:color-mix(in srgb,var(--c-accent) 12%,transparent);
  color:var(--c-accent);font-family:var(--f-utility);font-weight:780;font-size:.82rem}
.gx-fan-card h3{font-size:clamp(1.55rem,2vw,2.35rem);line-height:1.04}
.gx-fan-card p{font-size:clamp(.92rem,1.02vw,1.08rem);line-height:1.5}
.gx-fan-cta{margin-top:auto;font-size:.82rem}

/* ---- skin: swipe (cards-09, proven P7) — stage de-boxed per box checklist
   (wash/border/::before frame stripped); cards carry the look ---- */
.gx-swipe{min-height:min(270svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-swipe .gx-stage{display:grid;place-items:center;overflow:hidden}
.gx-swipe-card{position:absolute;top:50%;left:50%;width:min(820px,72vw);min-height:540px;
  padding:32px;overflow:hidden;border-radius:var(--radius);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-origin:50% 105%;transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-swipe-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(180deg,transparent 23%,color-mix(in srgb,var(--c-dominant) 94%,transparent) 86%),
    radial-gradient(circle at 18% 15%,color-mix(in srgb,var(--c-accent) 22%,transparent),transparent 29%),
    radial-gradient(circle at 82% 72%,color-mix(in srgb,var(--c-glow2) 16%,transparent),transparent 34%)}
.gx-swipe-content{position:absolute;right:30px;bottom:29px;left:30px;z-index:2;
  transform:translateZ(42px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-swipe-card h3{max-width:560px;margin-bottom:12px;font-size:clamp(2rem,3.8vw,3.55rem);line-height:.98}
.gx-swipe-card p{max-width:520px}

/* ---- skin: runway (cards-15, proven P8) — stage de-boxed; diagonal runway
   line + floor grid ::before KEPT as texture (ink-mix, masked) ---- */
.gx-runway{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-runway .gx-stage{display:grid;place-items:center;overflow:hidden;perspective:1800px}
.gx-runway .gx-stage::before{position:absolute;top:12%;left:50%;width:115%;height:95%;content:"";
  opacity:.5;pointer-events:none;
  background:
    linear-gradient(90deg,transparent 49.8%,color-mix(in srgb,var(--c-ink) 12%,transparent) 50%,transparent 50.2%),
    repeating-linear-gradient(0deg,color-mix(in srgb,var(--c-ink) 7%,transparent) 0 1px,transparent 1px 78px);
  transform:translateX(-50%) perspective(760px) rotateX(67deg) rotateZ(-9deg);
  transform-origin:center top;
  mask-image:linear-gradient(rgba(0,0,0,1),transparent 88%);-webkit-mask-image:linear-gradient(rgba(0,0,0,1),transparent 88%)}
.gx-runway-card{position:absolute;top:50%;left:50%;width:min(690px,60vw);min-height:430px;
  padding:34px;overflow:hidden;border-radius:var(--radius);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-runway-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(180deg,transparent 23%,color-mix(in srgb,var(--c-dominant) 94%,transparent) 86%),
    radial-gradient(circle at 18% 15%,color-mix(in srgb,var(--c-accent) 20%,transparent),transparent 29%),
    radial-gradient(circle at 82% 72%,color-mix(in srgb,var(--c-glow2) 14%,transparent),transparent 34%)}
.gx-runway-content{position:absolute;right:30px;bottom:29px;left:30px;z-index:2;max-width:560px;
  transform:translateZ(42px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-runway-card h3{max-width:520px;margin-bottom:12px;font-size:clamp(1.9rem,3.4vw,3.2rem);line-height:.98}
.gx-runway-card p{max-width:500px}

/* ---- skin: coverflow (cards-07, proven P2/P3/P4/P5) — testimonials carrier;
   stage wash REMOVED (gray box on light hosts), floor shadow pulled inward ---- */
.gx-coverflow{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-coverflow .gx-stage{display:grid;place-items:center;overflow:hidden;
  background:radial-gradient(circle at 50% 46%,color-mix(in srgb,var(--c-accent) 9%,transparent),transparent 34%)}
.gx-coverflow .gx-stage::after{position:absolute;right:18%;bottom:16%;left:18%;height:70px;
  border-radius:50%;content:"";pointer-events:none;
  background:radial-gradient(ellipse,color-mix(in srgb,var(--c-dominant) 40%,transparent),transparent 67%);
  filter:blur(12px);transform:translateZ(-180px)}
.gx-coverflow-card{position:absolute;top:50%;left:50%;width:min(610px,58vw);min-height:500px;
  padding:30px;overflow:hidden;border-radius:34px;
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-coverflow-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:linear-gradient(180deg,transparent 23%,color-mix(in srgb,var(--c-dominant) 94%,transparent) 86%),
    radial-gradient(circle at 18% 15%,color-mix(in srgb,var(--c-glow2) 27%,transparent),transparent 29%),
    radial-gradient(circle at 82% 72%,color-mix(in srgb,var(--c-accent) 20%,transparent),transparent 34%)}
.gx-coverflow-content{position:absolute;right:30px;bottom:29px;left:30px;z-index:2;
  transform:translateZ(42px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-coverflow-card h3{max-width:390px;margin-bottom:12px;font-size:clamp(1.35rem,2vw,1.9rem);line-height:1.18}
.gx-coverflow-card .gx-kicker{margin-bottom:0}

/* ---- skin: wave (cards-12, proven P6) — testimonials carrier; stage de-boxed,
   perspective floor grid KEPT as texture (masked ink, not a box) ---- */
.gx-wave{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-wave .gx-stage{display:grid;place-items:center;overflow:hidden;perspective:1800px}
.gx-wave .gx-stage::before{position:absolute;right:-8%;bottom:7%;left:-8%;height:42%;content:"";opacity:.6;
  background:
    repeating-linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 6%,transparent) 0 1px,transparent 1px 72px),
    repeating-linear-gradient(0deg,color-mix(in srgb,var(--c-ink) 5%,transparent) 0 1px,transparent 1px 55px);
  transform:perspective(700px) rotateX(68deg) translateY(40%);transform-origin:center bottom;
  -webkit-mask-image:linear-gradient(transparent,rgba(0,0,0,1));mask-image:linear-gradient(transparent,rgba(0,0,0,1))}
.gx-wave-card{position:absolute;top:50%;left:50%;overflow:hidden;border-radius:26px;
  width:min(560px,54vw);min-height:420px;padding:30px 34px;
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-wave-card::after{position:absolute;inset:0;content:"";pointer-events:none;border-radius:inherit;
  background:linear-gradient(180deg,transparent 20%,color-mix(in srgb,var(--c-dominant) 78%,transparent) 87%),
    radial-gradient(circle at 16% 14%,color-mix(in srgb,var(--c-accent) 20%,transparent),transparent 30%)}
.gx-cin-index{position:absolute;top:18px;right:24px;z-index:1;color:var(--gx-line-strong);
  font-family:var(--f-utility);font-size:4.6rem;font-weight:800;letter-spacing:-.08em;line-height:1}
.gx-cin-content{position:absolute;right:30px;bottom:28px;left:30px;z-index:2;max-width:520px;
  transform:translateZ(52px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-wave-card h3{margin:0 0 12px;font-size:clamp(1.35rem,1.9vw,2.05rem);line-height:1.18;letter-spacing:-.03em}
.gx-wave-card .gx-stars{margin-bottom:0}

/* ---- skin: cylinder (cards-13, proven P7) — testimonials carrier; stage
   de-boxed, ellipse floor rings kept as glow lines ---- */
.gx-cylinder{min-height:min(280svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-cylinder .gx-stage{display:grid;place-items:center;overflow:hidden;perspective:1900px}
.gx-cylinder .gx-stage::before,.gx-cylinder .gx-stage::after{position:absolute;top:50%;left:50%;
  width:72%;height:34%;border:1px solid color-mix(in srgb,var(--c-ink) 8%,transparent);
  border-radius:50%;content:"";transform:translate(-50%,-50%) rotateX(76deg)}
.gx-cylinder .gx-stage::after{width:48%;opacity:.65}
.gx-cyl-card{position:absolute;top:50%;left:50%;width:min(650px,58vw);min-height:470px;
  padding:34px;overflow:hidden;border-radius:var(--radius);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-cyl-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(180deg,transparent 20%,color-mix(in srgb,var(--c-dominant) 95%,transparent) 87%),
    radial-gradient(circle at 16% 14%,color-mix(in srgb,var(--c-accent) 24%,transparent),transparent 30%),
    radial-gradient(circle at 84% 70%,color-mix(in srgb,var(--c-glow2) 18%,transparent),transparent 34%)}
.gx-cyl-content{position:absolute;right:34px;bottom:32px;left:34px;z-index:2;max-width:560px;
  transform:translateZ(52px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-cyl-card h3{margin:0 0 14px;font-size:clamp(1.45rem,2.2vw,2.15rem);line-height:1.18;letter-spacing:-.02em}
.gx-cyl-card .gx-stars{margin-bottom:0}

/* ---- skin: portal (cards-14, proven P8) — testimonials carrier; stage
   de-boxed (token radial glows only), 5 rings kept as glow lines with the
   baked visibility bump (ink 22% / accent shadow 9%) ---- */
.gx-portal{min-height:min(280svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-portal .gx-stage{display:grid;place-items:center;overflow:hidden;perspective:1900px;
  background:
    radial-gradient(circle at center,color-mix(in srgb,var(--c-accent) 10%,transparent),transparent 18%),
    radial-gradient(circle at center,color-mix(in srgb,var(--c-glow2) 9%,transparent),transparent 48%)}
.gx-portal-rings{position:absolute;inset:50%;width:0;height:0;pointer-events:none;
  transform-style:preserve-3d;transform:rotateZ(var(--portal-spin,0deg))}
.gx-portal-ring{position:absolute;top:0;left:0;width:calc(180px + var(--ring) * 130px);aspect-ratio:1;
  border:1px solid color-mix(in srgb,var(--c-ink) calc(22% - var(--ring) * 2%),transparent);
  border-radius:50%;
  box-shadow:0 0 32px color-mix(in srgb,var(--c-accent) 9%,transparent);
  transform:translate(-50%,-50%) translateZ(calc(var(--ring) * -170px))}
.gx-portal-card{position:absolute;top:50%;left:50%;width:min(830px,72vw);min-height:480px;
  padding:36px;overflow:hidden;border-radius:var(--radius);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,filter,opacity}
.gx-portal-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(180deg,transparent 20%,color-mix(in srgb,var(--c-dominant) 95%,transparent) 87%),
    radial-gradient(circle at 16% 14%,color-mix(in srgb,var(--c-accent) 22%,transparent),transparent 30%),
    radial-gradient(circle at 84% 70%,color-mix(in srgb,var(--c-glow2) 16%,transparent),transparent 34%)}
.gx-portal-content{position:absolute;right:34px;bottom:32px;left:34px;z-index:2;max-width:640px;
  transform:translateZ(52px);-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-portal-card h3{margin:0 0 14px;font-size:clamp(1.4rem,2.2vw,2.1rem);line-height:1.2;letter-spacing:-.02em}
.gx-portal-card .gx-kicker{margin-bottom:0;color:var(--c-ink);opacity:.72}

/* ---- skin: imagesplit (cards-17, proven P2/P3/P4/P5) — gallery family;
   editorial split chapters, photo beside a bottom-anchored copy panel, sides
   alternating by parity. Non-photo items keep their pane (monogram fallback,
   P3 team precedent). Stage keeps a translucent dominant wash (62% mix — the
   GL field still reads through); P5's stacked-row variant is a graft-side
   option only, the engine port is the full split stage ---- */
.gx-imagesplit{min-height:min(260svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-imagesplit .gx-stage{min-height:clamp(590px,calc(100svh - 235px),820px);overflow:hidden;
  isolation:isolate;perspective:none;
  background:color-mix(in srgb,var(--c-dominant) 62%,transparent)}
.gx-imagesplit .gx-stage img{display:block;width:100%;height:100%;object-fit:cover;
  -webkit-user-drag:none;user-select:none;backface-visibility:hidden}
.gx-imagesplit-card{position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,1.28fr) minmax(390px,.72fr);
  opacity:var(--panel-opacity,0);pointer-events:none;z-index:var(--panel-z,1)}
.gx-imagesplit-card:nth-child(even){grid-template-columns:minmax(390px,.72fr) minmax(0,1.28fr)}
.gx-imagesplit-card:nth-child(even) .gx-imagesplit-media{order:2}
.gx-imagesplit-card:nth-child(even) .gx-imagesplit-panel{order:1}
.gx-imagesplit-media{position:relative;overflow:hidden;
  transform:translate3d(var(--media-x,0px),0,0);will-change:transform}
.gx-imagesplit-media::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:linear-gradient(90deg,transparent 50%,color-mix(in srgb,var(--c-dominant) 34%,transparent))}
.gx-imagesplit-card:nth-child(even) .gx-imagesplit-media::after{
  background:linear-gradient(-90deg,transparent 50%,color-mix(in srgb,var(--c-dominant) 34%,transparent))}
.gx-imagesplit-media img{transform:scale(var(--image-scale,1.08)) translate3d(var(--image-pan,0px),0,0);
  will-change:transform}
.gx-imagesplit-panel{position:relative;z-index:4;display:flex;flex-direction:column;
  justify-content:flex-end;padding:clamp(34px,5vw,86px);color:var(--c-ink);
  background:var(--gx-panel-strong);box-shadow:var(--gx-shadow);
  transform:translate3d(var(--copy-x,0px),0,0);will-change:transform,opacity;
  -webkit-font-smoothing:antialiased}
.gx-imagesplit-panel h3{margin:0 0 14px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2.35rem,5vw,5.8rem);letter-spacing:-.058em;line-height:.93}
.gx-imagesplit-panel p{max-width:620px;margin:0;color:var(--gx-muted);
  font-size:clamp(.95rem,1.15vw,1.12rem);line-height:1.62}

/* ---- skin: filmstrip (cards-19, proven P7) — gallery family; oversized
   frames travel sideways, the centred frame turns face-on. Stage backdrop
   stripped per box checklist (host bg + GL show between frames) ---- */
.gx-filmstrip{min-height:min(260svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-filmstrip .gx-stage{min-height:clamp(590px,calc(100svh - 200px),820px);overflow:hidden;
  isolation:isolate;perspective:1900px}
.gx-film-track{position:absolute;top:50%;left:50%;display:flex;gap:clamp(24px,3vw,54px);
  align-items:center;transform:translate3d(var(--track-x,0px),-50%,0);
  transform-style:preserve-3d;will-change:transform}
.gx-film-card{position:relative;flex:0 0 min(78vw,1120px);height:clamp(470px,66vh,650px);
  overflow:hidden;
  opacity:var(--card-opacity,1);
  filter:brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate3d(0,var(--card-y,0px),var(--card-z,0px)) rotateY(var(--card-ry,0deg))
    scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,opacity,filter}
.gx-film-card img{display:block;width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.18));user-select:none;-webkit-user-drag:none}
.gx-film-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:linear-gradient(0deg,color-mix(in srgb,var(--c-dominant) 92%,transparent),transparent 64%)}
.gx-film-copy{position:absolute;right:clamp(24px,4vw,70px);bottom:clamp(24px,5vh,58px);
  left:clamp(24px,4vw,70px);z-index:4;color:var(--c-ink);opacity:var(--copy-opacity,0);
  transform:translate3d(var(--copy-x,0px),var(--copy-y,24px),0);will-change:transform,opacity}
.gx-film-copy h3{max-width:850px;margin:0;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);letter-spacing:-.058em;line-height:.93;
  font-size:clamp(2.2rem,4.6vw,4.8rem)}
.gx-film-copy .gx-kicker{margin-bottom:14px}

/* ---- skin: photomosaic (cards-21, proven P6) — gallery family; six-tile
   editorial grid, one tile expands into the spotlight. Stage TRANSPARENT —
   the page's GL field glows through the grid gaps (P6 decree); captions ride
   a stable plane (no counters — de-AI baked) ---- */
.gx-photomosaic{min-height:min(260svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-photomosaic .gx-stage{min-height:clamp(590px,calc(100svh - 200px),820px);overflow:hidden;
  isolation:isolate;background:transparent;perspective:none}
.gx-mosaic-grid{position:absolute;inset:clamp(18px,4vw,64px);display:grid;
  grid-template-columns:repeat(3,minmax(0,1fr));grid-template-rows:repeat(2,minmax(0,1fr));
  gap:clamp(10px,1.4vw,24px);perspective:1600px}
.gx-photo-tile{position:relative;overflow:hidden;border-radius:14px;
  border:1px solid var(--gx-line);
  opacity:var(--card-opacity,1);
  filter:brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-origin:center;will-change:transform,filter,opacity;z-index:var(--card-z-index,1)}
.gx-photo-tile img{display:block;width:100%;height:100%;object-fit:cover;
  transform:scale(var(--image-scale,1.04));-webkit-user-drag:none;user-select:none}
.gx-mosaic-captions{position:absolute;inset:0;z-index:30;pointer-events:none}
.gx-mosaic-caption{position:absolute;left:clamp(22px,5vw,86px);bottom:clamp(24px,6vh,72px);
  width:min(640px,calc(100% - 44px));padding:clamp(22px,2.6vw,38px);border-radius:14px;
  opacity:var(--caption-opacity,0);transform:translate3d(0,var(--caption-y,34px),0);
  background:color-mix(in srgb,var(--c-dominant) 94%,transparent);
  border:1px solid var(--gx-line);box-shadow:var(--gx-shadow);z-index:var(--caption-z,1);
  -webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-mosaic-caption h3{margin:0 0 10px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(1.7rem,3vw,3.2rem);letter-spacing:-.05em;line-height:.95}
.gx-mosaic-caption p{max-width:560px;margin:0;color:var(--gx-muted);
  font-size:clamp(.9rem,1.05vw,1.02rem);line-height:1.6}
.gx-mosaic-caption .gx-kicker{margin-bottom:10px}

/* ---- skin: curtain (cards-20, proven P8) — gallery family; six vertical
   shutters assemble one photo per slide, slides crossfade, copy slides in
   after the image lands. ASPECT FIX (ledger, mandatory): each strip shows its
   slice via a ::before cover layer at width:600% offset by --strip-index —
   never a stretched background-size. Stage chrome stripped: host bg + GL show
   through the crossfade. Cap raised for tall viewports (P8 review) ---- */
.gx-curtain{min-height:min(300svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-curtain .gx-stage{min-height:clamp(500px,calc(100svh - 260px),780px);overflow:hidden;
  isolation:isolate;perspective:none}
.gx-curtain-slide{position:absolute;inset:0;pointer-events:none;
  opacity:var(--slide-opacity,0);z-index:var(--slide-z,1)}
.gx-curtain-grid{position:absolute;inset:0;display:grid;grid-template-columns:repeat(6,1fr);overflow:hidden}
.gx-curtain-strip{position:relative;overflow:hidden}
.gx-curtain-strip::before{content:"";position:absolute;top:0;left:calc(var(--strip-index) * -100%);
  width:600%;height:100%;
  background-image:var(--curtain-image);background-size:cover;background-position:center;
  background-repeat:no-repeat;filter:saturate(var(--media-chroma,1.18))}
.gx-curtain-noimg .gx-curtain-grid{grid-template-columns:1fr}
.gx-curtain-slide::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 74%,transparent),transparent 68%),
    linear-gradient(0deg,color-mix(in srgb,var(--c-dominant) 76%,transparent),transparent 55%)}
.gx-curtain-copy{position:absolute;left:clamp(24px,6vw,105px);bottom:clamp(28px,7vh,78px);z-index:4;
  width:min(720px,calc(100% - 48px));padding:clamp(24px,3vw,46px);
  color:var(--c-ink);background:color-mix(in srgb,var(--c-dominant) 88%,transparent);
  box-shadow:0 30px 90px rgba(0,0,0,.32);
  opacity:var(--copy-opacity,0);transform:translate3d(var(--copy-x,-50px),0,0);will-change:transform,opacity}
.gx-curtain-copy h3{margin:0 0 14px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2.1rem,4.4vw,4.6rem);letter-spacing:-.058em;line-height:.93}
.gx-curtain-copy p{max-width:620px;margin:0;color:var(--gx-muted);
  font-size:clamp(.95rem,1.15vw,1.08rem);line-height:1.62}
.gx-curtain-copy .gx-kicker{margin-bottom:12px}
/* Copy over a real IMAGE follows the text-over-media doctrine — a dark scrim panel + light
   text, independent of the pack palette. The default `--c-ink` on a `--c-dominant` panel went
   dark-on-dark on warm-dark packs (hearth measured 1.11:1). The no-image variant keeps the
   theme colours (light-theme cards need dark text). */
.gx-curtain-slide:not(.gx-curtain-noimg) .gx-curtain-copy{background:color-mix(in srgb,var(--c-scrim) 80%,transparent);color:var(--c-on-scrim)}
.gx-curtain-slide:not(.gx-curtain-noimg) .gx-curtain-copy h3{color:var(--c-on-scrim)}
.gx-curtain-slide:not(.gx-curtain-noimg) .gx-curtain-copy p{color:color-mix(in srgb,var(--c-on-scrim) 84%,transparent)}

/* ---- ported skins: static collapse <=900px ---- */
@media (max-width:900px){
  .gx-tunnel{min-height:auto;width:100%;margin-left:0}
  .gx-fan{min-height:auto;width:100%;margin-left:0}
  .gx-fan .gx-stage{padding-inline:14px;overflow:visible}
  .gx-fan-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-fan-icon{margin-bottom:16px}
  .gx-swipe{min-height:auto;width:100%;margin-left:0}
  .gx-swipe .gx-stage{overflow:visible}
  .gx-swipe-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-swipe-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-runway{min-height:auto;width:100%;margin-left:0}
  .gx-runway .gx-stage{overflow:visible;perspective:none}
  .gx-runway .gx-stage::before{display:none}
  .gx-runway-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-runway-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-coverflow{min-height:auto;width:100%;margin-left:0}
  .gx-coverflow .gx-stage{overflow:visible;background:transparent}
  .gx-coverflow .gx-stage::after{display:none}
  .gx-coverflow-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-coverflow-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-wave{min-height:auto;width:100%;margin-left:0}
  .gx-wave .gx-stage{overflow:visible;perspective:none}
  .gx-wave .gx-stage::before{display:none}
  .gx-wave-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-cin-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-cin-index{position:relative;top:auto;right:auto;font-size:2.6rem}
  .gx-cylinder{min-height:auto;width:100%;margin-left:0}
  .gx-cylinder .gx-stage{overflow:visible;perspective:none}
  .gx-cylinder .gx-stage::before,.gx-cylinder .gx-stage::after{display:none}
  .gx-cyl-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-cyl-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-portal{min-height:auto;width:100%;margin-left:0}
  .gx-portal .gx-stage{overflow:visible;perspective:none;background:transparent}
  .gx-portal-rings{display:none}
  .gx-portal-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-portal-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-scene .gx-stage{min-height:auto;perspective:none;display:grid;gap:14px;padding:0 14px;
    overflow:visible;place-items:stretch}
  .gx-tunnel-rings{display:none}
  .gx-tunnel-card{position:relative;top:auto;left:auto;width:100%;min-height:260px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-tunnel-card h3{margin-top:72px;font-size:2rem}
  .gx-tunnel-card .gx-index{font-size:4.8rem}
  .gx-imagesplit{min-height:auto;width:100%;margin-left:0}
  .gx-imagesplit .gx-stage{background:transparent}
  .gx-imagesplit-card,.gx-imagesplit-card:nth-child(even){position:relative;inset:auto;
    display:grid;grid-template-columns:1fr;pointer-events:auto;
    opacity:1!important;transform:none!important}
  .gx-imagesplit-card:nth-child(even) .gx-imagesplit-media,
  .gx-imagesplit-card:nth-child(even) .gx-imagesplit-panel{order:initial}
  .gx-imagesplit-media{min-height:310px}
  .gx-imagesplit-media,.gx-imagesplit-media img,
  .gx-imagesplit-panel{transform:none!important;opacity:1!important}
  .gx-imagesplit-panel{padding:26px 20px 30px}
  .gx-imagesplit-panel h3{font-size:clamp(2rem,8vw,3rem)}
  .gx-filmstrip{min-height:auto;width:100%;margin-left:0}
  .gx-filmstrip .gx-stage{perspective:none}
  .gx-film-track{position:relative;top:auto;left:auto;display:grid;gap:14px;transform:none!important}
  .gx-film-card{width:100%;flex:none;height:390px;transform:none!important;opacity:1!important;filter:none!important}
  .gx-film-copy{opacity:1!important;transform:none!important}
  .gx-photomosaic{min-height:auto;width:100%;margin-left:0}
  .gx-mosaic-grid{position:relative;inset:auto;grid-template-columns:1fr 1fr;grid-template-rows:auto;perspective:none}
  .gx-photo-tile{transform:none!important;opacity:1!important;filter:none!important;aspect-ratio:4/3}
  .gx-mosaic-captions{position:relative;inset:auto;pointer-events:auto;display:grid;gap:0;margin-top:18px}
  .gx-mosaic-caption{position:relative;left:auto;bottom:auto;width:100%;
    opacity:1!important;transform:none!important;border:0;box-shadow:none;background:transparent;
    padding:18px 0;border-radius:0;border-bottom:1px solid var(--gx-line)}
  .gx-curtain{min-height:auto;width:100%;margin-left:0}
  .gx-curtain-slide{position:relative;inset:auto;display:grid;grid-template-columns:1fr;
    pointer-events:auto;opacity:1!important}
  .gx-curtain-grid{position:relative;min-height:310px}
  .gx-curtain-strip{transform:none!important;opacity:1!important}
  .gx-curtain-copy{position:relative;inset:auto;left:auto;bottom:auto;width:100%;
    padding:26px 20px 30px;opacity:1!important;transform:none!important}
  .gx-curtain-copy h3{font-size:clamp(2rem,9vw,3.2rem)}
}
/* ---- ported skins: reduced-motion still state (same static collapse) ---- */
@media (prefers-reduced-motion:reduce){
  .gx-tunnel{min-height:auto;width:100%;margin-left:0}
  .gx-fan{min-height:auto;width:100%;margin-left:0}
  .gx-fan .gx-stage{padding-inline:14px;overflow:visible}
  .gx-fan-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-fan-icon{margin-bottom:16px}
  .gx-swipe{min-height:auto;width:100%;margin-left:0}
  .gx-swipe .gx-stage{overflow:visible}
  .gx-swipe-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-swipe-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-runway{min-height:auto;width:100%;margin-left:0}
  .gx-runway .gx-stage{overflow:visible;perspective:none}
  .gx-runway .gx-stage::before{display:none}
  .gx-runway-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-runway-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-coverflow{min-height:auto;width:100%;margin-left:0}
  .gx-coverflow .gx-stage{overflow:visible;background:transparent}
  .gx-coverflow .gx-stage::after{display:none}
  .gx-coverflow-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-coverflow-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-wave{min-height:auto;width:100%;margin-left:0}
  .gx-wave .gx-stage{overflow:visible;perspective:none}
  .gx-wave .gx-stage::before{display:none}
  .gx-wave-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-cin-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-cin-index{position:relative;top:auto;right:auto;font-size:2.6rem}
  .gx-cylinder{min-height:auto;width:100%;margin-left:0}
  .gx-cylinder .gx-stage{overflow:visible;perspective:none}
  .gx-cylinder .gx-stage::before,.gx-cylinder .gx-stage::after{display:none}
  .gx-cyl-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-cyl-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-portal{min-height:auto;width:100%;margin-left:0}
  .gx-portal .gx-stage{overflow:visible;perspective:none;background:transparent}
  .gx-portal-rings{display:none}
  .gx-portal-card{position:relative;top:auto;left:auto;width:100%;min-height:390px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-portal-content{position:relative;right:auto;bottom:auto;left:auto;transform:none;margin-top:14px}
  .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-scene .gx-stage{min-height:auto;perspective:none;display:grid;gap:14px;padding:0 14px;
    overflow:visible;place-items:stretch}
  .gx-tunnel-rings{display:none}
  .gx-tunnel-card{position:relative;top:auto;left:auto;width:100%;min-height:260px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-tunnel-card h3{margin-top:72px;font-size:2rem}
  .gx-tunnel-card .gx-index{font-size:4.8rem}
  .gx-imagesplit{min-height:auto;width:100%;margin-left:0}
  .gx-imagesplit .gx-stage{background:transparent}
  .gx-imagesplit-card,.gx-imagesplit-card:nth-child(even){position:relative;inset:auto;
    display:grid;grid-template-columns:1fr;pointer-events:auto;
    opacity:1!important;transform:none!important}
  .gx-imagesplit-card:nth-child(even) .gx-imagesplit-media,
  .gx-imagesplit-card:nth-child(even) .gx-imagesplit-panel{order:initial}
  .gx-imagesplit-media{min-height:310px}
  .gx-imagesplit-media,.gx-imagesplit-media img,
  .gx-imagesplit-panel{transform:none!important;opacity:1!important}
  .gx-imagesplit-panel{padding:26px 20px 30px}
  .gx-imagesplit-panel h3{font-size:clamp(2rem,8vw,3rem)}
  .gx-filmstrip{min-height:auto;width:100%;margin-left:0}
  .gx-filmstrip .gx-stage{perspective:none}
  .gx-film-track{position:relative;top:auto;left:auto;display:grid;gap:14px;transform:none!important}
  .gx-film-card{width:100%;flex:none;height:390px;transform:none!important;opacity:1!important;filter:none!important}
  .gx-film-copy{opacity:1!important;transform:none!important}
  .gx-photomosaic{min-height:auto;width:100%;margin-left:0}
  .gx-mosaic-grid{position:relative;inset:auto;grid-template-columns:1fr 1fr;grid-template-rows:auto;perspective:none}
  .gx-photo-tile{transform:none!important;opacity:1!important;filter:none!important;aspect-ratio:4/3}
  .gx-mosaic-captions{position:relative;inset:auto;pointer-events:auto;display:grid;gap:0;margin-top:18px}
  .gx-mosaic-caption{position:relative;left:auto;bottom:auto;width:100%;
    opacity:1!important;transform:none!important;border:0;box-shadow:none;background:transparent;
    padding:18px 0;border-radius:0;border-bottom:1px solid var(--gx-line)}
  .gx-curtain{min-height:auto;width:100%;margin-left:0}
  .gx-curtain-slide{position:relative;inset:auto;display:grid;grid-template-columns:1fr;
    pointer-events:auto;opacity:1!important}
  .gx-curtain-grid{position:relative;min-height:310px}
  .gx-curtain-strip{transform:none!important;opacity:1!important}
  .gx-curtain-copy{position:relative;inset:auto;left:auto;bottom:auto;width:100%;
    padding:26px 20px 30px;opacity:1!important;transform:none!important}
  .gx-curtain-copy h3{font-size:clamp(2rem,9vw,3.2rem)}
  .gx-scene *,.gx-scene *::before,.gx-scene *::after{
    animation-duration:.001ms!important;transition-duration:.001ms!important}
}

/* ---- Phase 3c: PROCESS + HOURS family skins (proven grafts P4-P8) ----
   Full-viewport step theatres. Every sticky stays GL-transparent (token
   radials at <=15% only); per-step color rides the token accent cycle via
   the inline --step-accent custom property. */

/* ---- skin: chapters (ps-01, proven P8 — the flagship) — copy left / orbit
   art right, functional step-rail nav (kept: rail dots indicate the ACTIVE
   step), symbol tile de-filled (border + radial glow, no slab), sticky wash
   is the accent radial only (GL-field transparency, P8 fix) ---- */
.gx-chapters{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-chapters .gx-sticky{padding:0;
  background:radial-gradient(circle at 72% 42%,color-mix(in srgb,var(--active-accent,var(--c-accent)) 12%,transparent),transparent 30rem);
  transition:background .7s ease}
.gx-chapters .gx-stage{min-height:100svh;perspective:none}
.gxp-chap-ambient{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none}
.gxp-chap-ambient::before,.gxp-chap-ambient::after{position:absolute;content:"";border-radius:50%;filter:blur(2px)}
.gxp-chap-ambient::before{width:38vw;aspect-ratio:1;right:-10vw;top:-16vw;
  border:1px solid color-mix(in srgb,var(--c-ink) 7%,transparent);
  box-shadow:0 0 0 8vw color-mix(in srgb,var(--c-ink) 2%,transparent),0 0 0 16vw color-mix(in srgb,var(--c-ink) 1%,transparent)}
.gxp-chap-ambient::after{width:28vw;aspect-ratio:1;left:-12vw;bottom:-14vw;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-glow3) 9%,transparent),transparent 67%)}
.gxp-chap-stack{position:absolute;inset:0;z-index:1}
.gxp-chap-step{--local:0;--delta:0;position:absolute;inset:0;
  display:grid;grid-template-columns:minmax(0,.9fr) minmax(480px,1.1fr);gap:clamp(30px,6vw,90px);
  align-items:center;padding:clamp(80px,8vw,130px) clamp(24px,7vw,120px) 130px;
  opacity:clamp(0,calc(var(--local) * 1.35),1);pointer-events:none;
  transform:translate3d(0,calc(var(--delta) * 22vh),calc((1 - var(--local)) * -220px)) scale(calc(.9 + var(--local) * .1));
  transition:opacity .08s linear;will-change:transform,opacity}
.gxp-chap-step.is-active{pointer-events:auto}
.gxp-chap-copy{position:relative;z-index:3;max-width:680px}
.gxp-chap-num{display:block;margin-left:-.08em;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 78%,var(--c-ink));
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(6rem,15vw,14.5rem);letter-spacing:-.1em;line-height:.68;opacity:.24}
.gxp-chap-cap{display:inline-block;margin:32px 0 14px;color:var(--step-accent,var(--c-accent));
  font-family:var(--f-utility);font-size:.74rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase}
.gxp-chap-copy h3{max-width:620px;margin:0 0 18px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.5rem,5.2vw,5.4rem);letter-spacing:-.06em;line-height:.91}
.gxp-chap-copy p{max-width:600px;margin:0;color:var(--gx-muted);font-size:clamp(1rem,1.45vw,1.2rem)}
.gxp-chap-art{position:relative;width:min(40vw,620px);aspect-ratio:1;margin:auto;
  transform:perspective(1200px) rotateX(calc((1 - var(--local)) * 18deg)) rotateY(calc(var(--delta) * -14deg)) scale(calc(.8 + var(--local) * .2));
  transform-style:preserve-3d}
.gxp-chap-orbit{position:absolute;inset:50%;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 40%,transparent);
  transform:translate(-50%,-50%)}
.gxp-chap-orbit--outer{width:86%;aspect-ratio:1;
  box-shadow:0 0 80px color-mix(in srgb,var(--step-accent,var(--c-accent)) 18%,transparent),
    inset 0 0 60px color-mix(in srgb,var(--c-ink) 3%,transparent);
  animation:gxp-chap-orbit 18s linear infinite}
.gxp-chap-orbit--inner{width:54%;aspect-ratio:1;border-style:dashed;animation:gxp-chap-orbit-r 12s linear infinite}
/* symbol tile DE-FILLED per box checklist: border + radial glow, no opaque slab */
.gxp-chap-glyph{position:absolute;inset:50% auto auto 50%;display:grid;width:34%;aspect-ratio:1;
  place-items:center;border-radius:32%;
  border:1px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 55%,transparent);
  background:radial-gradient(circle at 50% 40%,color-mix(in srgb,var(--step-accent,var(--c-accent)) 16%,transparent),transparent 74%);
  box-shadow:0 0 70px color-mix(in srgb,var(--step-accent,var(--c-accent)) 22%,transparent);
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 80%,var(--c-ink));
  font-size:clamp(2.6rem,5.4vw,6rem);
  transform:translate(-50%,-50%) translateZ(100px) rotate(calc(var(--delta) * 11deg))}
.gxp-chap-scan{position:absolute;inset:5%;opacity:.45;
  background:
    linear-gradient(90deg,transparent 49.8%,color-mix(in srgb,var(--c-ink) 8%,transparent) 50%,transparent 50.2%),
    linear-gradient(transparent 49.8%,color-mix(in srgb,var(--c-ink) 6%,transparent) 50%,transparent 50.2%);
  mask-image:radial-gradient(circle,rgba(0,0,0,1) 0 56%,transparent 57%);
  -webkit-mask-image:radial-gradient(circle,rgba(0,0,0,1) 0 56%,transparent 57%)}
/* step-rail nav — FUNCTIONAL active-step indicator (de-AI carve-out: kept) */
.gxp-chap-rail{position:absolute;right:clamp(24px,5vw,78px);bottom:38px;left:clamp(24px,5vw,78px);
  z-index:12;display:grid;gap:18px}
.gxp-chap-rail::before{position:absolute;top:8px;right:0;left:0;height:1px;content:"";
  background:color-mix(in srgb,var(--c-ink) 12%,transparent)}
.gxp-chap-rail-item{--nav-local:0;position:relative;padding-top:21px;
  color:color-mix(in srgb,var(--c-ink) calc(32% + var(--nav-local) * 68%),transparent);
  transition:color .15s linear}
.gxp-chap-rail-item::before{position:absolute;top:3px;left:0;width:11px;aspect-ratio:1;border-radius:50%;content:"";
  background:color-mix(in srgb,var(--active-accent,var(--c-accent)) calc(var(--nav-local) * 100%),color-mix(in srgb,var(--c-ink) 24%,var(--c-field)));
  box-shadow:0 0 calc(var(--nav-local) * 24px) var(--active-accent,var(--c-accent));
  transform:scale(calc(.7 + var(--nav-local) * .55))}
.gxp-chap-rail-item span{display:block;font-family:var(--f-utility);font-size:.78rem;font-weight:700}
.gxp-chap-rail-item strong{display:block;margin-top:4px;font-size:.72rem;font-weight:650;
  letter-spacing:.08em;text-transform:uppercase}
@keyframes gxp-chap-orbit{to{transform:translate(-50%,-50%) rotate(360deg)}}
@keyframes gxp-chap-orbit-r{to{transform:translate(-50%,-50%) rotate(-360deg)}}

/* ---- skin: wheel (ps-04, proven P6) — radial dial + copy crossfade stack;
   sticky wash stripped to the warm accent radial (box checklist); hub keeps
   its glow ring, no slab. Dial count is semantic (3 steps OK) ---- */
.gx-wheel{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-wheel .gx-sticky{padding:0;
  background:radial-gradient(circle at 27% 57%,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 31rem)}
.gx-wheel .gx-stage{min-height:100svh;perspective:none}
.gxp-wheel-layout{position:absolute;inset:10vh clamp(24px,5vw,84px) 9vh;display:grid;
  grid-template-columns:minmax(400px,1.02fr) minmax(340px,.98fr);
  gap:clamp(30px,5vw,90px);align-items:center}
.gxp-wheel-visual{position:relative;width:min(46vw,620px);aspect-ratio:1;justify-self:center}
.gxp-wheel-ring{position:absolute;inset:50%;border-radius:50%;transform:translate(-50%,-50%)}
.gxp-wheel-ring--outer{width:96%;aspect-ratio:1;border:1px solid var(--gx-line);
  box-shadow:0 0 110px color-mix(in srgb,var(--c-accent) 16%,transparent)}
.gxp-wheel-ring--middle{width:72%;aspect-ratio:1;
  border:1px dashed color-mix(in srgb,var(--c-accent) 30%,transparent);
  animation:gxp-wheel-spin 30s linear infinite}
.gxp-wheel-ring--inner{width:38%;aspect-ratio:1;border:1px solid var(--gx-line-strong);
  box-shadow:inset 0 0 60px color-mix(in srgb,var(--c-accent) 6%,transparent)}
.gxp-wheel-ticks{position:absolute;inset:2%;border-radius:50%;
  background:repeating-conic-gradient(from 0deg,color-mix(in srgb,var(--c-ink) 28%,transparent) 0 .35deg,transparent .35deg 6deg);
  -webkit-mask:radial-gradient(transparent 0 92%,black 92.3%);mask:radial-gradient(transparent 0 92%,black 92.3%)}
.gxp-wheel-node{--nav-local:0;position:absolute;top:50%;left:50%;z-index:6;display:grid;
  width:84px;aspect-ratio:1;place-items:center;border-radius:50%;
  color:color-mix(in srgb,var(--c-ink) calc(42% + var(--nav-local) * 58%),transparent);
  background:color-mix(in srgb,var(--c-dominant) 88%,transparent);
  box-shadow:0 0 calc(12px + var(--nav-local) * 40px) color-mix(in srgb,var(--step-accent,var(--c-accent)) calc(var(--nav-local) * 42%),transparent);
  transform:translate(-50%,-50%);will-change:transform}
.gxp-wheel-node::before{position:absolute;inset:-8px;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) calc(var(--nav-local) * 100%),var(--gx-line));
  content:"";transform:scale(calc(.85 + var(--nav-local) * .2))}
.gxp-wheel-node span{font-family:var(--f-utility);font-size:1.4rem;font-weight:850;letter-spacing:-.06em}
.gxp-wheel-node strong{position:absolute;top:105%;left:50%;font-family:var(--f-utility);
  font-size:.62rem;letter-spacing:.12em;text-transform:uppercase;white-space:nowrap;transform:translateX(-50%)}
.gxp-wheel-core{position:absolute;top:50%;left:50%;z-index:4;display:grid;width:28%;aspect-ratio:1;
  place-content:center;text-align:center;border-radius:50%;
  background:radial-gradient(circle at 35% 28%,color-mix(in srgb,var(--c-ink) 88%,transparent),color-mix(in srgb,var(--c-accent) 55%,var(--c-dominant)) 9%,color-mix(in srgb,var(--c-field) 82%,transparent) 43%,color-mix(in srgb,var(--c-dominant) 85%,transparent) 72%);
  box-shadow:0 0 90px color-mix(in srgb,var(--c-accent) 35%,transparent);transform:translate(-50%,-50%)}
.gxp-wheel-core small{color:var(--gx-muted);font-family:var(--f-utility);font-size:.62rem;letter-spacing:.12em;text-transform:uppercase}
.gxp-wheel-core strong{color:var(--c-ink);font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(3rem,6vw,6rem);letter-spacing:-.1em;line-height:1}
.gxp-wheel-lock{position:absolute;top:50%;left:-3%;z-index:9;width:96px;height:64px;transform:translateY(-50%)}
.gxp-wheel-lock::before{position:absolute;top:50%;left:0;width:56px;height:2px;content:"";
  background:linear-gradient(90deg,var(--c-ink),color-mix(in srgb,var(--c-accent) 10%,transparent));
  box-shadow:0 0 16px var(--c-accent)}
.gxp-wheel-lock span{position:absolute;top:50%;right:0;width:18px;aspect-ratio:1;border-radius:50%;
  background:var(--c-ink);
  box-shadow:0 0 0 9px color-mix(in srgb,var(--c-accent) 12%,transparent),0 0 32px var(--c-accent);
  transform:translateY(-50%)}
.gxp-wheel-stack{position:relative;height:min(54vh,520px)}
.gxp-wheel-copy{--local:0;--delta:0;position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;
  opacity:var(--local);
  transform:translate3d(calc((1 - var(--local)) * 70px),calc(var(--delta) * 12vh),calc((1 - var(--local)) * -120px)) scale(calc(.94 + var(--local) * .06));
  pointer-events:none}
.gxp-wheel-copy.is-active{pointer-events:auto}
.gxp-wheel-number{position:absolute;right:0;top:0;color:color-mix(in srgb,var(--c-ink) 7%,transparent);
  font-family:var(--f-display);font-size:clamp(8rem,15vw,15rem);font-weight:900;letter-spacing:-.12em;line-height:.75}
.gxp-wheel-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gxp-wheel-copy h3{max-width:560px;margin:12px 0 16px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.3rem,4.4vw,4.6rem);letter-spacing:-.06em;line-height:.92}
.gxp-wheel-copy p{max-width:520px;margin:0;color:var(--gx-muted);font-size:clamp(.95rem,1.2vw,1.1rem)}
.gxp-wheel-progress{position:absolute;right:clamp(24px,5vw,84px);bottom:26px;left:clamp(24px,5vw,84px);
  display:flex;align-items:center;gap:14px;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.7rem;letter-spacing:.13em;text-transform:uppercase}
.gxp-wheel-progress i{flex:1;height:1px;
  background:linear-gradient(90deg,var(--c-accent) calc(var(--wheel-progress,0) * 100%),var(--gx-line) 0)}
@keyframes gxp-wheel-spin{to{transform:translate(-50%,-50%) rotate(360deg)}}

/* ---- skin: road (ps-06, proven P5) — SVG ribbon road + runner driven by
   scene progress; kit sticky wash stripped (host bg + GL carry the depth);
   masked ink floor grid is texture, not a box ---- */
.gx-road{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-road .gx-sticky{padding:0}
.gx-road .gx-stage{min-height:100svh;perspective:none}
.gxp-road-world{position:absolute;inset:6vh 0 9vh}
.gxp-road-world::before{position:absolute;inset:0;content:"";
  background:linear-gradient(color-mix(in srgb,var(--c-ink) 3%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 3%,transparent) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:linear-gradient(180deg,transparent,rgba(0,0,0,1) 20%,rgba(0,0,0,1) 80%,transparent);
  mask-image:linear-gradient(180deg,transparent,rgba(0,0,0,1) 20%,rgba(0,0,0,1) 80%,transparent)}
.gxp-road-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gxp-road-shadow,.gxp-road-base,.gxp-road-active{fill:none;vector-effect:non-scaling-stroke}
.gxp-road-shadow{stroke:color-mix(in srgb,var(--c-dominant) 80%,transparent);stroke-width:60;
  filter:blur(16px);transform:translateY(18px)}
.gxp-road-base{stroke:color-mix(in srgb,var(--c-ink) 6%,transparent);stroke-width:42}
.gxp-road-active{stroke:url(#gxpRoadGrad);stroke-width:17;stroke-linecap:round;
  stroke-dasharray:1;stroke-dashoffset:calc(1 - var(--road-progress,0));filter:url(#gxpRoadGlow)}
.gxp-road-horizon{position:absolute;right:0;bottom:6%;left:0;height:1px;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--c-ink) 13%,transparent),transparent)}
.gxp-road-runner{position:absolute;z-index:12;width:50px;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle at 35% 30%,var(--c-ink),var(--c-accent) 14%,
    color-mix(in srgb,var(--c-accent) 34%,var(--c-dominant)) 50%,color-mix(in srgb,var(--c-dominant) 96%,transparent) 74%);
  box-shadow:0 0 0 12px color-mix(in srgb,var(--c-accent) 9%,transparent),
    0 0 56px color-mix(in srgb,var(--c-accent) 50%,transparent);
  transform:translate(-50%,-50%)}
.gxp-road-runner span{position:absolute;inset:50% auto auto 50%;width:11px;height:28px;
  border-radius:8px 8px 3px 3px;background:var(--c-ink);transform:translate(-50%,-70%)}
.gxp-road-runner i{position:absolute;top:72%;left:50%;width:6px;height:22px;
  background:linear-gradient(var(--c-accent),transparent);filter:blur(1px);transform:translateX(-50%)}
.gxp-road-marker{--nav-local:0;position:absolute;z-index:10;min-width:72px;
  color:var(--c-ink);text-align:center;transform:translate(-50%,-50%)}
.gxp-road-marker::before{display:block;width:15px;aspect-ratio:1;margin:0 auto 7px;
  border-radius:50%;content:"";
  background:color-mix(in srgb,var(--step-accent,var(--c-glow2)) calc(var(--nav-local) * 100%),var(--c-texture));
  box-shadow:0 0 calc(var(--nav-local) * 30px) var(--step-accent,var(--c-glow2));
  transform:scale(calc(.7 + var(--nav-local) * .45))}
.gxp-road-marker span,.gxp-road-marker strong{opacity:calc(.38 + var(--nav-local) * .62)}
.gxp-road-marker span{display:block;font-family:var(--f-utility);font-size:.92rem;font-weight:800}
.gxp-road-marker strong{display:block;font-family:var(--f-utility);font-size:.56rem;
  letter-spacing:.12em;text-transform:uppercase}
.gxp-road-stack{position:absolute;top:11%;right:clamp(16px,3vw,44px);left:clamp(16px,3vw,44px);height:44vh}
.gxp-road-copy{--local:0;--delta:0;position:absolute;inset:0;display:flex;flex-direction:column;
  justify-content:flex-start;opacity:var(--local);
  transform:translate3d(calc((1 - var(--local)) * 60px),calc(var(--delta) * 14vh),0);pointer-events:none}
.gxp-road-copy.is-active{pointer-events:auto}
.gxp-road-number{position:absolute;right:0;top:-14%;
  color:color-mix(in srgb,var(--c-ink) 6%,transparent);
  font-family:var(--f-utility);font-size:clamp(6rem,9vw,9.5rem);font-weight:900;letter-spacing:-.1em;line-height:1}
.gxp-road-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gxp-road-copy h3{margin:8px 0 12px;font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.9rem,3vw,3.2rem);letter-spacing:-.048em;line-height:.94;color:var(--c-ink);
  text-shadow:0 2px 18px var(--c-dominant)}
.gxp-road-copy p{max-width:470px;margin:0;color:var(--gx-muted);
  font-size:clamp(.92rem,1.05vw,1.05rem);line-height:1.55;text-shadow:0 1px 12px var(--c-dominant)}
.gxp-road-scale{position:absolute;right:clamp(16px,3vw,44px);bottom:22px;left:clamp(16px,3vw,44px);
  display:flex;align-items:center;gap:12px;
  color:color-mix(in srgb,var(--c-ink) 42%,transparent);
  font-family:var(--f-utility);font-size:.62rem;letter-spacing:.13em;text-transform:uppercase}
.gxp-road-scale i{flex:1;height:1px;
  background:linear-gradient(90deg,var(--c-accent),var(--c-glow2));opacity:.45}

/* ---- skin: mountain (ps-10, proven P7) — clip-path ridge silhouettes (no
   slab), SVG route + runner on scene progress; copy on the EMPTY left side,
   slide-in from the left (ledger fix). Donor's opaque sky gradient dropped —
   the sticky keeps only the accent radial (GL-field transparency) ---- */
.gx-mountain{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-mountain .gx-sticky{padding:0;
  background:radial-gradient(circle at 76% 12%,color-mix(in srgb,var(--c-accent) 12%,transparent),transparent 34rem)}
.gx-mountain .gx-stage{min-height:100svh;perspective:none}
.gxp-mtn-moon{position:absolute;top:11vh;right:10vw;width:clamp(110px,14vw,220px);aspect-ratio:1;
  border-radius:50%;background:color-mix(in srgb,var(--c-ink) 82%,var(--c-accent));
  box-shadow:0 0 0 30px color-mix(in srgb,var(--c-accent) 10%,transparent),
    0 0 110px color-mix(in srgb,var(--c-accent) 34%,transparent);
  transform:translateY(calc(var(--mountain-progress,0) * -10vh))}
.gxp-mtn-cloud{position:absolute;height:42px;border-radius:999px;
  background:color-mix(in srgb,var(--c-ink) 9%,transparent);
  transform:translateX(calc(var(--mountain-progress,0) * var(--cloud-travel)))}
.gxp-mtn-cloud::before,.gxp-mtn-cloud::after{position:absolute;bottom:0;content:"";border-radius:50%;background:inherit}
.gxp-mtn-cloud::before{left:18%;width:76px;aspect-ratio:1}
.gxp-mtn-cloud::after{right:12%;width:54px;aspect-ratio:1}
.gxp-mtn-cloud--one{--cloud-travel:18vw;top:19vh;left:-8vw;width:240px}
.gxp-mtn-cloud--two{--cloud-travel:-22vw;top:32vh;right:-8vw;width:200px;opacity:.7}
.gxp-mtn-layer{position:absolute;right:-8vw;bottom:-18vh;left:-8vw;height:78vh;
  transform-origin:50% 100%;will-change:transform}
.gxp-mtn-layer--far{background:color-mix(in srgb,var(--c-ink) 14%,var(--c-field));
  clip-path:polygon(0 73%,17% 39%,28% 62%,44% 24%,55% 54%,70% 31%,84% 58%,100% 18%,100% 100%,0 100%);
  transform:translateY(calc(var(--mountain-progress,0) * 5vh)) scale(calc(1 + var(--mountain-progress,0) * .05))}
.gxp-mtn-layer--mid{background:color-mix(in srgb,var(--c-ink) 7%,var(--c-field));
  clip-path:polygon(0 82%,14% 57%,27% 72%,43% 40%,52% 64%,67% 43%,78% 70%,91% 38%,100% 60%,100% 100%,0 100%);
  transform:translateY(calc(var(--mountain-progress,0) * 9vh)) scale(calc(1 + var(--mountain-progress,0) * .08))}
.gxp-mtn-layer--front{background:color-mix(in srgb,var(--c-ink) 3%,var(--c-dominant));
  clip-path:polygon(0 92%,13% 72%,24% 84%,39% 58%,51% 82%,65% 62%,76% 83%,89% 60%,100% 76%,100% 100%,0 100%);
  transform:translateY(calc(var(--mountain-progress,0) * 14vh)) scale(calc(1 + var(--mountain-progress,0) * .12))}
.gxp-mtn-world{position:absolute;inset:14vh 3vw 5vh;z-index:9}
.gxp-mtn-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gxp-mtn-route-base,.gxp-mtn-route-active{fill:none;stroke-linecap:round;stroke-linejoin:round;vector-effect:non-scaling-stroke}
.gxp-mtn-route-base{stroke:color-mix(in srgb,var(--c-ink) 28%,transparent);stroke-width:12}
.gxp-mtn-route-active{stroke:var(--c-accent);stroke-width:7;stroke-dasharray:1;
  stroke-dashoffset:calc(1 - var(--mountain-progress,0));filter:url(#gxpMtnGlow)}
.gxp-mtn-runner{position:absolute;z-index:15;width:32px;aspect-ratio:1;
  border:6px solid color-mix(in srgb,var(--c-ink) 92%,transparent);border-radius:50%;
  background:var(--c-accent);
  box-shadow:0 0 0 11px color-mix(in srgb,var(--c-accent) 18%,transparent),0 10px 30px rgba(0,0,0,.45);
  transform:translate(-50%,-50%)}
.gxp-mtn-node{--nav-local:0;position:absolute;z-index:13;display:grid;width:clamp(50px,4.6vw,68px);
  aspect-ratio:1;place-items:center;
  border:3px solid color-mix(in srgb,var(--c-ink) 60%,transparent);border-radius:50%;
  color:color-mix(in srgb,var(--c-ink) calc(100% - var(--nav-local) * 100%),var(--c-on-accent));
  background:color-mix(in srgb,var(--c-field) calc(100% - var(--nav-local) * 70%),var(--c-accent));
  box-shadow:0 12px 30px rgba(0,0,0,.35),0 0 calc(var(--nav-local) * 34px) color-mix(in srgb,var(--c-accent) 65%,transparent);
  font-family:var(--f-utility);font-size:.78rem;font-weight:700;
  transform:translate(-50%,-50%) scale(calc(.78 + var(--nav-local) * .35))}
.gxp-mtn-stack{position:absolute;top:24vh;left:clamp(24px,5vw,86px);z-index:19;
  width:min(44vw,690px);height:330px;pointer-events:none}
.gxp-mtn-copy{--local:0;--delta:0;position:absolute;inset:0;opacity:var(--local);
  transform:translate3d(calc(var(--delta) * -75px),calc((1 - var(--local)) * 30px),0)}
.gxp-mtn-copy.is-active{pointer-events:auto}
.gxp-mtn-number{display:block;color:var(--step-accent,var(--c-accent));
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(5rem,10vw,10rem);letter-spacing:-.11em;line-height:.68;opacity:.85}
.gxp-mtn-copy .gx-kicker{display:inline-block;margin-top:14px;color:var(--step-accent,var(--c-accent))}
.gxp-mtn-copy h3{margin:20px 0 12px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);letter-spacing:-.06em;line-height:.9;
  font-size:clamp(2.4rem,4.4vw,4.8rem);
  text-shadow:0 2px 30px rgba(0,0,0,.5)}
.gxp-mtn-copy p{max-width:560px;margin:0;color:color-mix(in srgb,var(--c-ink) 74%,transparent);
  font-size:clamp(.98rem,1.25vw,1.12rem);text-shadow:0 1px 18px rgba(0,0,0,.5)}
.gxp-mtn-altitude{position:absolute;right:clamp(150px,12vw,210px);bottom:34px;z-index:20;display:flex;
  align-items:center;gap:12px;color:color-mix(in srgb,var(--c-ink) 55%,transparent);
  font-family:var(--f-utility);font-size:.7rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase}
.gxp-mtn-altitude b{font-size:1rem;color:var(--c-accent)}

/* ---- skin: depthstair (ps-16, proven P4) — perspective stairwell; platform
   slab DE-FILLED + edges masked (glow line / numeral / texture carry it);
   sticky keeps the accent radial only ---- */
.gx-depthstair{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-depthstair .gx-sticky{padding:0;perspective:1500px;
  background:radial-gradient(circle at 70% 56%,color-mix(in srgb,var(--c-accent) 14%,transparent),transparent 32rem)}
.gx-depthstair .gx-stage{min-height:100svh;perspective:none}
.gxp-stair-grid{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px);
  background-size:64px 64px;
  -webkit-mask-image:linear-gradient(145deg,transparent 4%,black 44%,transparent 94%);
  mask-image:linear-gradient(145deg,transparent 4%,black 44%,transparent 94%);
  transform:perspective(850px) rotateX(64deg) scale(1.7);transform-origin:69% 58%}
.gxp-stair-glow{position:absolute;top:56%;left:69%;width:48vw;aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-accent) 13%,transparent),transparent 66%);
  filter:blur(8px);transform:translate(-50%,-50%)}
.gxp-stair-stage{position:absolute;inset:0;perspective:1150px;perspective-origin:69% 57%;transform-style:preserve-3d}
.gxp-stair-step{--local:0;--delta:0;position:absolute;inset:0;pointer-events:none;
  visibility:var(--step-vis,visible);transform-style:preserve-3d}
.gxp-stair-step.is-active{pointer-events:auto}
.gxp-stair-platform{position:absolute;top:57%;left:69%;width:min(68vw,1080px);height:min(32vh,305px);
  overflow:hidden;
  /* stage-backdrop doctrine: no filled slab, no hard edges — the glowing
     horizon line, numeral and floor texture carry the look; boundaries fade */
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--step-accent,var(--c-accent)) 16%,transparent),
    transparent 72%);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 14% 86%,transparent),
    linear-gradient(180deg,#000 55%,transparent);
  -webkit-mask-composite:source-in;
  mask-image:linear-gradient(90deg,transparent,#000 14% 86%,transparent),
    linear-gradient(180deg,#000 55%,transparent);
  mask-composite:intersect;
  opacity:var(--plat-opacity,1);
  filter:brightness(var(--plat-brightness,1)) blur(var(--plat-blur,0px));
  transform:translate(-50%,-50%)
    translate3d(var(--plat-x,0px),var(--plat-y,0px),var(--plat-z,0px))
    rotateX(63deg) rotateZ(var(--plat-rz,0deg));
  transform-style:preserve-3d;will-change:transform,opacity,filter}
.gxp-stair-platform::before{position:absolute;inset:0;content:"";
  background:linear-gradient(90deg,transparent 49.9%,color-mix(in srgb,var(--c-ink) 12%,transparent) 50%,transparent 50.1%),
    repeating-linear-gradient(90deg,transparent 0 9%,color-mix(in srgb,var(--c-ink) 6%,transparent) 9.1% 9.25%);
  opacity:.55}
.gxp-stair-platform::after{position:absolute;top:0;right:10%;left:10%;height:3px;content:"";
  background:linear-gradient(90deg,transparent,var(--step-accent,var(--c-accent)),var(--c-ink),var(--step-accent,var(--c-accent)),transparent);
  box-shadow:0 0 28px var(--step-accent,var(--c-accent))}
.gxp-stair-number{position:absolute;right:8%;bottom:-.1em;z-index:3;
  color:color-mix(in srgb,var(--c-ink) 20%,transparent);
  font-family:var(--f-utility);font-size:clamp(10rem,22vw,24rem);font-weight:800;letter-spacing:-.14em;line-height:.68;
  text-shadow:0 0 38px color-mix(in srgb,var(--step-accent,var(--c-accent)) 30%,transparent)}
.gxp-stair-label{position:absolute;top:13%;left:14%;z-index:4;color:var(--c-ink);
  font-family:var(--f-utility);font-size:.72rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase}
.gxp-stair-label::before{display:inline-block;width:34px;height:2px;margin-right:12px;vertical-align:middle;content:"";
  background:var(--step-accent,var(--c-accent));box-shadow:0 0 12px var(--step-accent,var(--c-accent))}
.gxp-stair-copy{position:absolute;bottom:12vh;left:clamp(24px,7vw,125px);z-index:45;width:min(40vw,620px);
  opacity:var(--local);transform:translate3d(calc(var(--delta) * -7vw),calc(var(--delta) * 8vh),0);
  will-change:opacity,transform;-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gxp-stair-copy-number{display:block;color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 18%,transparent);
  font-family:var(--f-utility);font-size:clamp(6rem,11vw,11rem);font-weight:800;letter-spacing:-.12em;line-height:.56}
.gxp-stair-copy .gx-kicker{display:inline-block;margin:25px 0 12px;color:var(--step-accent,var(--c-accent))}
.gxp-stair-copy h3{max-width:650px;margin:0 0 18px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.4rem,4.8vw,4.8rem);letter-spacing:-.045em;line-height:.92;text-wrap:balance}
.gxp-stair-copy p{max-width:600px;margin:0;color:var(--gx-muted);font-size:clamp(.98rem,1.3vw,1.15rem)}
.gxp-stair-nav{position:absolute;right:clamp(24px,5vw,82px);bottom:31px;left:clamp(24px,5vw,82px);z-index:70;
  display:flex;justify-content:space-between;gap:14px}
.gxp-stair-nav span{--nav-local:0;display:flex;align-items:center;gap:10px;
  color:color-mix(in srgb,var(--c-ink) calc(28% + var(--nav-local) * 72%),transparent);
  font-family:var(--f-utility);font-size:.68rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase}
.gxp-stair-nav span::before{width:11px;aspect-ratio:1;border:2px solid currentColor;border-radius:2px;content:"";
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 100%),transparent);
  box-shadow:0 0 calc(var(--nav-local) * 24px) var(--c-accent);
  transform:rotate(45deg) scale(calc(.72 + var(--nav-local) * .35))}
.gxp-stair-readout{position:absolute;top:50%;right:28px;z-index:70;
  color:color-mix(in srgb,var(--c-ink) 42%,transparent);
  font-family:var(--f-utility);font-size:.64rem;font-weight:700;letter-spacing:.15em;text-transform:uppercase;
  writing-mode:vertical-rl;transform:translateY(-50%)}
.gxp-stair-readout strong{color:var(--c-ink)}
@media (min-width:901px) and (max-width:1180px){
  .gxp-stair-platform{left:70%;width:min(74vw,880px)}
  .gxp-stair-copy{left:38px;width:43vw}
}

/* ---- skin: accordion (cards-06, proven P2/P3/P4) — process carrier;
   vertical spines swell open on the walk. Optional media: full-bleed image
   behind a token scrim as first child, fallback numeral otherwise ---- */
.gx-accordion{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-accordion .gx-stage{display:flex;gap:12px;align-items:stretch;padding:12px 16px;perspective:none}
.gx-accordion-card{position:relative;min-width:76px;padding:22px;overflow:hidden;border:0;
  border-radius:28px;background:linear-gradient(145deg,var(--gx-panel-strong),var(--gx-panel-deep));
  box-shadow:var(--gx-shadow);
  opacity:var(--card-opacity,1);
  filter:brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  flex-grow:var(--card-grow,1);flex-basis:0;
  transform:perspective(1000px) translateY(var(--card-y,0px)) rotateY(var(--card-ry,0deg));
  transform-style:preserve-3d;will-change:flex-grow,transform,filter,opacity}
.gx-accordion-card::before{position:absolute;inset:0;content:"";pointer-events:none;z-index:1;
  background:radial-gradient(circle at 20% 20%,color-mix(in srgb,var(--c-accent) 28%,transparent),transparent 28%),
    radial-gradient(circle at 80% 72%,color-mix(in srgb,var(--c-glow2) 17%,transparent),transparent 31%)}
.gx-accordion-media{position:absolute;inset:0;z-index:0;overflow:hidden;border-radius:inherit}
.gx-accordion-media img{width:100%;height:100%;object-fit:cover;filter:saturate(.92)}
.gx-accordion-media::after{position:absolute;inset:0;content:"";
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-dominant) 14%,transparent),
    color-mix(in srgb,var(--c-dominant) 88%,transparent) 76%)}
.gx-accordion-index{position:relative;z-index:2;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.78rem;font-weight:700;letter-spacing:.13em}
.gx-accordion-fallback{position:absolute;top:16%;left:50%;z-index:1;
  transform:translateX(-50%);color:var(--gx-line-strong);
  font-family:var(--f-utility);font-size:clamp(4rem,8vw,7.5rem);font-weight:800;
  letter-spacing:-.06em;line-height:1;opacity:.55;pointer-events:none}
.gx-accordion-label{position:absolute;bottom:22px;left:50%;z-index:2;color:var(--c-ink);
  font-family:var(--f-utility);font-size:.82rem;font-weight:700;letter-spacing:.11em;
  text-transform:uppercase;writing-mode:vertical-rl;
  transform:translateX(-50%) rotate(180deg);opacity:var(--label-opacity,1)}
.gx-accordion-content{position:absolute;right:24px;bottom:24px;left:24px;z-index:2;min-width:300px;
  opacity:var(--content-opacity,0);transform:translateY(var(--content-y,22px));
  will-change:opacity,transform;-webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-accordion-content h3{margin:0 0 10px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2rem,4vw,4rem);letter-spacing:-.03em;line-height:.98}
.gx-accordion-content p{max-width:560px;margin:0;color:var(--gx-muted);font-size:.92rem}

/* ---- skin: temporal (hours-v5 temporal-bands, proven P8) — HOURS family,
   gxh- namespace. Donor's 6-colour neon :root fully re-derived to the token
   accent cycle at graft time; the status pill's glowing dot is FUNCTIONAL
   (open/closed — de-AI carve-out). .gxh-today marks the venue clock's day;
   .active is the scroll tour's walking emphasis — two distinct treatments,
   one clock source (engine.js) ---- */
.gx-temporal{min-height:min(190vh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-temporal .gx-sticky{padding:0;
  background:radial-gradient(circle at 18% 22%,color-mix(in srgb,var(--c-accent) 6%,transparent),transparent 26rem)}
.gx-temporal .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}
.gxh-layout{display:grid;grid-template-columns:420px 1fr;gap:32px;align-items:center;
  width:min(1320px,calc(100vw - 36px));margin:0 auto}
.gxh-info{position:relative;z-index:3;padding:24px 0}
/* Copy over a SIGNATURE scene needs its own ground.
   A pack that owns its background fills the entire frame, so the takeover-scene rule
   "the copy block sits on the composition's EMPTY side" has no empty side to use:
   measured on out-real/gym__whitespace-run, the whitespace_gates corridor crosses the
   address block, every hours row and the day chips. The copy still paints ON TOP
   (.gxh-info is z-index:3; the scene layers are z-index -1 and -3, verified with
   elementsFromPoint) — what it lacks is the token-derived scrim the doctrine requires
   whenever text sits over artwork. The hours column's own plate is calibrated for a
   quiet background and is too weak here.
   Scoped to body[data-gl-module], so a pack with no signature module stays
   byte-identical, and feathered with a mask so it is a scrim and never a slab. */
body[data-gl-module] .gxh-info::before{position:absolute;z-index:-1;content:"";
  inset:-20px -28px -14px -28px;
  background:radial-gradient(125% 105% at 24% 46%,
    color-mix(in srgb,var(--c-field) 88%,transparent) 0%,
    color-mix(in srgb,var(--c-field) 70%,transparent) 48%,
    transparent 80%);
  -webkit-mask:radial-gradient(125% 105% at 24% 46%,#000 52%,transparent 88%);
  mask:radial-gradient(125% 105% at 24% 46%,#000 52%,transparent 88%)}
body[data-gl-module] .gxh-hours-card{
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-field) 80%,transparent),
    color-mix(in srgb,var(--c-field) 66%,transparent))}
/* ...and the rest of the class (user decision 2026-08-08: per-family scrims, not a
   change to the signature module's behaviour). The containers below are the ones
   MEASURED to carry copy with no ground of their own on a signature-pack page — found by
   walking every text node in every takeover scene and checking whether any ancestor
   inside the scene paints a background, not by reading CSS:

     stairsteps  .gxp-ts-copy    12 text nodes, no ground
     lightwell   .gxpr-plan      name / pitch / features / pill, no ground
     staircase   .gxf-item       only `[open]` items get a plate; closed ones have none

   `comparewipe` and `cylinder` are NOT here: their copy already sits on a card face, and
   the same walk confirms it. `.gxf-st-word` is not here either — it is the ghost word at
   5% ink, decorative by design, and scrimming it would defeat the effect.
   Same recipe throughout: feathered, masked, token-derived, no hard box, and it inherits
   each skin's own copy fade because it is a child of the faded element. */
body[data-gl-module] .gx-stairsteps .gxp-ts-copy::before,
body[data-gl-module] .gx-lightwell .gxpr-plan::before,
body[data-gl-module] .gxf-item::before{position:absolute;z-index:-1;content:"";
  inset:-16px -22px;pointer-events:none;
  background:radial-gradient(120% 108% at 40% 50%,
    color-mix(in srgb,var(--c-field) 86%,transparent) 0%,
    color-mix(in srgb,var(--c-field) 66%,transparent) 50%,
    transparent 82%);
  -webkit-mask:radial-gradient(120% 108% at 40% 50%,#000 50%,transparent 88%);
  mask:radial-gradient(120% 108% at 40% 50%,#000 50%,transparent 88%)}
/* status pill: the glowing dot is FUNCTIONAL (open/closed) */
.gxh-status{display:inline-flex;gap:10px;align-items:center;margin-bottom:18px;padding:8px 12px;
  border:1px solid var(--gx-line);border-radius:999px;
  background:color-mix(in srgb,var(--c-ink) 4%,transparent);
  color:var(--c-ink);font-family:var(--f-utility);font-size:.8rem;font-weight:700}
.gxh-status::before{content:"";width:8px;height:8px;border-radius:50%;
  background:color-mix(in srgb,var(--c-ink) 30%,transparent);box-shadow:none}
.gxh-status.gxh-open::before{background:var(--c-accent);
  box-shadow:0 0 15px color-mix(in srgb,var(--c-accent) 80%,transparent)}
.gxh-address{margin-bottom:20px;color:color-mix(in srgb,var(--c-ink) 88%,transparent);
  font-size:.96rem;line-height:1.6}
.gxh-address strong{display:block;margin-bottom:4px;color:var(--c-ink);
  font-family:var(--f-utility);font-size:.8rem;letter-spacing:.15em;text-transform:uppercase}
.gxh-hours-card{padding:16px 16px 10px;border:1px solid var(--gx-line);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 6%,transparent),color-mix(in srgb,var(--c-ink) 3%,transparent))}
.gxh-hours-title{margin-bottom:6px;color:var(--gx-muted);font-family:var(--f-utility);
  font-size:.72rem;letter-spacing:.14em;text-transform:uppercase}
.gxh-hours-list{list-style:none;margin:0;padding:0}
.gxh-hours-list li{display:grid;grid-template-columns:1fr auto;gap:18px;align-items:center;
  padding:10px 10px;border:1px solid transparent;
  color:color-mix(in srgb,var(--c-ink) 78%,transparent);transition:all .25s ease}
.gxh-hours-list li + li{margin-top:5px}
.gxh-hours-list li.active{color:var(--c-ink);
  background:color-mix(in srgb,var(--c-accent) 8%,transparent)}
.gxh-hours-list li.gxh-today{color:var(--c-ink);
  border-left:2px solid var(--c-accent)}
.gxh-hours-list .gxh-day{font-weight:700}
.gxh-hours-list .gxh-time{font-family:var(--f-utility);font-weight:700;letter-spacing:-.01em}
.gxh-visual{position:relative;height:72vh;min-height:560px;overflow:hidden}
.gxh-frame{position:absolute;inset:28px 22px 26px 74px;
  border-left:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent)}
.gxh-axis{position:absolute;left:-58px;top:42px;bottom:28px;display:flex;flex-direction:column;
  justify-content:space-between;color:var(--gx-muted);font-family:var(--f-utility);
  font-size:.65rem;font-weight:700}
.gxh-bands{position:absolute;inset:0;display:grid;grid-auto-columns:1fr;grid-auto-flow:column;gap:10px}
.gxh-col{position:relative;display:grid;grid-template-rows:34px 1fr;gap:8px;transition:opacity .25s ease}
.gxh-band-day{text-align:center;color:color-mix(in srgb,var(--c-ink) 66%,transparent);
  font-family:var(--f-utility);font-size:.7rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase}
.gxh-col.gxh-today .gxh-band-day{color:var(--c-accent);
  border-bottom:2px solid var(--c-accent);padding-bottom:3px}
.gxh-track{position:relative;
  border-left:1px solid color-mix(in srgb,var(--c-ink) 7%,transparent);
  border-right:1px solid color-mix(in srgb,var(--c-ink) 4%,transparent);
  background:repeating-linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 0 1px,transparent 1px 25%)}
.gxh-window{position:absolute;left:12%;right:12%;display:grid;place-items:center;
  border:1px solid color-mix(in srgb,var(--c-accent) 40%,transparent);font-style:normal;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 78%,transparent),color-mix(in srgb,var(--c-accent) 18%,transparent));
  color:var(--c-on-accent);transition:opacity .25s ease,transform .25s ease}
.gxh-window b{font-family:var(--f-utility);font-size:.7rem}
.gxh-closed{position:absolute;left:12%;right:12%;top:44%;padding:8px 3px;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.62rem;font-style:normal;text-align:center}
.gxh-col.active .gxh-band-day{color:var(--c-ink)}
.gxh-col.gxh-today.active .gxh-band-day{color:var(--c-accent)}
.gxh-col.active .gxh-window{transform:scaleX(1.08);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 55%,var(--c-ink)),var(--c-accent))}
/* the time cursor is a NOW indicator — engine.js pins it to the venue clock */
.gxh-cursor{position:absolute;left:0;right:0;top:42px;height:1px;pointer-events:none;
  background:color-mix(in srgb,var(--c-glow3) 72%,transparent)}
.gxh-cursor span{position:absolute;left:8px;top:-24px;color:var(--c-glow3);
  font-family:var(--f-utility);font-size:.67rem;font-weight:800;letter-spacing:.08em}
/* short-desktop compression (P8 ledger, screenshot-caught): at 100svh the
   info column (pill+address+7 rows) clips on ~900px-tall desktops without it */
@media (min-width:901px) and (max-height:980px){
  .gxh-info{padding:8px 0}
  .gxh-status{margin-bottom:10px;padding:6px 10px;font-size:.72rem}
  .gxh-address{font-size:.82rem;line-height:1.4;margin-bottom:12px}
  .gxh-address strong{font-size:.66rem;margin-bottom:2px}
  .gxh-hours-card{padding:9px 10px 6px}
  .gxh-hours-title{margin-bottom:3px;font-size:.64rem}
  .gxh-hours-list li{padding:5px 8px;font-size:.8rem}
  .gxh-hours-list li + li{margin-top:2px}
  .gxh-visual{height:80vh;min-height:500px}
}
@media (min-width:901px) and (max-width:1180px){
  .gxh-layout{grid-template-columns:350px 1fr}
  .gxh-visual{height:64vh;min-height:480px}
}
@media (min-width:901px) and (max-width:1180px){
  .gxp-chap-step{grid-template-columns:1fr 1fr;padding-inline:38px}
  .gxp-chap-art{width:min(46vw,500px)}
}

/* ---- Phase 3c skins: static collapse <=900px ---- */
@media (max-width:900px){
  .gx-chapters{min-height:auto;width:100%;margin-left:0}
  .gx-chapters .gx-sticky{padding:24px 0 30px}
  .gx-chapters .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-chap-ambient{display:none}
  .gxp-chap-stack{position:relative;inset:auto;display:grid;gap:16px}
  .gxp-chap-step{position:relative;inset:auto;display:grid;grid-template-columns:1fr;gap:16px;
    min-height:0;padding:25px 0 38px;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-chap-num{font-size:6rem}
  .gxp-chap-cap{margin-top:20px}
  .gxp-chap-copy h3{font-size:2.5rem}
  .gxp-chap-art{width:100%;max-width:390px;order:-1;transform:none!important}
  .gxp-chap-rail{display:none}
  .gx-wheel{min-height:auto;width:100%;margin-left:0}
  .gx-wheel .gx-sticky{padding:24px 0 30px}
  .gx-wheel .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-wheel-layout{position:relative;inset:auto;display:block}
  .gxp-wheel-visual{width:min(86vw,390px);margin:26px auto}
  .gxp-wheel-node{width:56px}
  .gxp-wheel-node strong{display:none}
  .gxp-wheel-stack{height:auto;display:grid;gap:0}
  .gxp-wheel-copy{position:relative;inset:auto;min-height:0;padding:28px 0;pointer-events:auto;
    border-bottom:1px solid var(--gx-line);opacity:1!important;transform:none!important}
  .gxp-wheel-number{font-size:6rem}
  .gxp-wheel-copy h3{font-size:2.3rem}
  .gxp-wheel-progress{display:none}
  .gx-road{min-height:auto;width:100%;margin-left:0}
  .gx-road .gx-sticky{padding:24px 0 40px}
  .gx-road .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-road-world{position:relative;inset:auto;height:400px;margin:12px 0 0}
  .gxp-road-stack{position:relative;inset:auto;display:grid;height:auto;margin:18px 0 0}
  .gxp-road-copy{position:relative;inset:auto;min-height:0;padding:22px 0;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-road-number{font-size:4.6rem;top:0}
  .gxp-road-copy h3{font-size:1.9rem}
  .gxp-road-scale{display:none}
  .gxp-road-marker strong{display:none}
  .gx-mountain{min-height:auto;width:100%;margin-left:0}
  .gx-mountain .gx-sticky{padding:24px 0 30px}
  .gx-mountain .gx-stage{display:block;min-height:auto;padding:0}
  .gx-mountain{--mountain-progress:1}
  .gxp-mtn-world{position:relative;inset:auto;height:440px;margin:0}
  .gxp-mtn-runner,.gxp-mtn-altitude{display:none}
  .gxp-mtn-moon{top:24px}
  .gxp-mtn-layer{bottom:auto;top:110px;height:420px}
  .gxp-mtn-stack{position:relative;inset:auto;display:grid;width:auto;height:auto;padding:0 20px 40px}
  .gxp-mtn-copy{position:relative;inset:auto;min-height:0;padding:26px 0;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-mtn-number{font-size:5rem}
  .gxp-mtn-copy h3{font-size:2.4rem}
  .gx-depthstair{min-height:auto;width:100%;margin-left:0}
  .gx-depthstair .gx-sticky{padding:12px 0 0;perspective:none;background:none}
  .gx-depthstair .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-stair-grid,.gxp-stair-glow,.gxp-stair-nav,.gxp-stair-readout{display:none}
  .gxp-stair-stage{position:relative;inset:auto;perspective:none}
  .gxp-stair-step{position:relative;inset:auto;display:grid;min-height:0;padding:24px 0 52px;
    border-bottom:1px solid var(--gx-line);opacity:1!important;visibility:visible!important;pointer-events:auto}
  .gxp-stair-platform{position:relative;top:auto;left:auto;width:min(100%,410px);height:220px;
    margin:10px auto 24px;opacity:1!important;filter:none!important;
    transform:perspective(680px) rotateX(44deg)!important}
  .gxp-stair-number{font-size:8.5rem}
  .gxp-stair-copy{position:relative;inset:auto;bottom:auto;left:auto;width:auto;
    opacity:1!important;transform:none!important}
  .gxp-stair-copy-number{font-size:5.2rem}
  .gxp-stair-copy h3{font-size:2.5rem}
  .gx-accordion{min-height:auto;width:100%;margin-left:0}
  .gx-accordion .gx-stage{display:grid;gap:14px;min-height:auto;padding:0 14px}
  .gx-accordion-card{min-height:310px;min-width:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-accordion-label{display:none}
  .gx-accordion-content{min-width:0;opacity:1!important;transform:none!important}
  .gx-temporal{min-height:auto;width:100%;margin-left:0}
  .gx-temporal .gx-sticky{padding:26px 0 36px}
  .gx-temporal .gx-stage{display:block;min-height:auto;padding:0}
  .gxh-layout{grid-template-columns:1fr;gap:20px;width:min(calc(100vw - 24px),1000px)}
  .gxh-info{order:1}
  .gxh-visual{order:2;height:420px;min-height:420px}
  .gxh-frame{inset:16px 8px 18px 46px}
  .gxh-bands{gap:4px}
  .gxh-band-day{font-size:.54rem}
  .gxh-window b{font-size:.52rem}
  .gxh-axis{left:-40px;font-size:.55rem}
}
/* ---- Phase 3c skins: reduced-motion still state ---- */
@media (prefers-reduced-motion:reduce){
  .gx-chapters{min-height:auto;width:100%;margin-left:0}
  .gx-chapters .gx-sticky{padding:24px 0 30px}
  .gx-chapters .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-chap-ambient{display:none}
  .gxp-chap-stack{position:relative;inset:auto;display:grid;gap:16px}
  .gxp-chap-step{position:relative;inset:auto;display:grid;grid-template-columns:1fr;gap:16px;
    min-height:0;padding:25px 0 38px;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-chap-num{font-size:6rem}
  .gxp-chap-cap{margin-top:20px}
  .gxp-chap-copy h3{font-size:2.5rem}
  .gxp-chap-art{width:100%;max-width:390px;order:-1;transform:none!important}
  .gxp-chap-orbit--outer,.gxp-chap-orbit--inner{animation:none}
  .gxp-chap-rail{display:none}
  .gx-wheel{min-height:auto;width:100%;margin-left:0}
  .gx-wheel .gx-sticky{padding:24px 0 30px}
  .gx-wheel .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-wheel-layout{position:relative;inset:auto;display:block}
  .gxp-wheel-visual{width:min(86vw,390px);margin:26px auto}
  .gxp-wheel-node{width:56px}
  .gxp-wheel-node strong{display:none}
  .gxp-wheel-ring--middle{animation:none}
  .gxp-wheel-stack{height:auto;display:grid;gap:0}
  .gxp-wheel-copy{position:relative;inset:auto;min-height:0;padding:28px 0;pointer-events:auto;
    border-bottom:1px solid var(--gx-line);opacity:1!important;transform:none!important}
  .gxp-wheel-number{font-size:6rem}
  .gxp-wheel-copy h3{font-size:2.3rem}
  .gxp-wheel-progress{display:none}
  .gx-road{min-height:auto;width:100%;margin-left:0}
  .gx-road .gx-sticky{padding:24px 0 40px}
  .gx-road .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-road-world{position:relative;inset:auto;height:400px;margin:12px 0 0}
  .gxp-road-stack{position:relative;inset:auto;display:grid;height:auto;margin:18px 0 0}
  .gxp-road-copy{position:relative;inset:auto;min-height:0;padding:22px 0;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-road-active{stroke-dashoffset:0}
  .gxp-road-number{font-size:4.6rem;top:0}
  .gxp-road-copy h3{font-size:1.9rem}
  .gxp-road-scale{display:none}
  .gxp-road-marker strong{display:none}
  .gx-mountain{min-height:auto;width:100%;margin-left:0}
  .gx-mountain .gx-sticky{padding:24px 0 30px}
  .gx-mountain .gx-stage{display:block;min-height:auto;padding:0}
  .gx-mountain{--mountain-progress:1}
  .gxp-mtn-world{position:relative;inset:auto;height:440px;margin:0}
  .gxp-mtn-runner,.gxp-mtn-altitude{display:none}
  .gxp-mtn-moon{top:24px}
  .gxp-mtn-layer{bottom:auto;top:110px;height:420px}
  .gxp-mtn-stack{position:relative;inset:auto;display:grid;width:auto;height:auto;padding:0 20px 40px}
  .gxp-mtn-copy{position:relative;inset:auto;min-height:0;padding:26px 0;pointer-events:auto;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
    opacity:1!important;transform:none!important}
  .gxp-mtn-number{font-size:5rem}
  .gxp-mtn-copy h3{font-size:2.4rem}
  .gx-depthstair{min-height:auto;width:100%;margin-left:0}
  .gx-depthstair .gx-sticky{padding:12px 0 0;perspective:none;background:none}
  .gx-depthstair .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gxp-stair-grid,.gxp-stair-glow,.gxp-stair-nav,.gxp-stair-readout{display:none}
  .gxp-stair-stage{position:relative;inset:auto;perspective:none}
  .gxp-stair-step{position:relative;inset:auto;display:grid;min-height:0;padding:24px 0 52px;
    border-bottom:1px solid var(--gx-line);opacity:1!important;visibility:visible!important;pointer-events:auto}
  .gxp-stair-platform{position:relative;top:auto;left:auto;width:min(100%,410px);height:220px;
    margin:10px auto 24px;opacity:1!important;filter:none!important;
    transform:perspective(680px) rotateX(44deg)!important}
  .gxp-stair-number{font-size:8.5rem}
  .gxp-stair-copy{position:relative;inset:auto;bottom:auto;left:auto;width:auto;
    opacity:1!important;transform:none!important}
  .gxp-stair-copy-number{font-size:5.2rem}
  .gxp-stair-copy h3{font-size:2.5rem}
  .gx-accordion{min-height:auto;width:100%;margin-left:0}
  .gx-accordion .gx-stage{display:grid;gap:14px;min-height:auto;padding:0 14px}
  .gx-accordion-card{min-height:310px;min-width:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-accordion-label{display:none}
  .gx-accordion-content{min-width:0;opacity:1!important;transform:none!important}
  .gx-temporal{min-height:auto;width:100%;margin-left:0}
  .gx-temporal .gx-sticky{padding:26px 0 36px}
  .gx-temporal .gx-stage{display:block;min-height:auto;padding:0}
  .gxh-layout{grid-template-columns:1fr;gap:20px;width:min(calc(100vw - 24px),1000px)}
  .gxh-info{order:1}
  .gxh-visual{order:2;height:420px;min-height:420px}
  .gxh-frame{inset:16px 8px 18px 46px}
  .gxh-bands{gap:4px}
  .gxh-band-day{font-size:.54rem}
  .gxh-window b{font-size:.52rem}
  .gxh-axis{left:-40px;font-size:.55rem}
  .gxh-hours-list li.active{transform:none}
}

/* ==== HOURS FAMILY SKINS (Library 7, hours-v5 kits, gxh- namespace) ====
   The nine remaining hours-v5 designs, ported 2026-08-07 alongside the
   original .gx-temporal above. All ten render on the SAME hoursShell (the
   info column, status pill, address block and seven-row hours card styled
   globally with the temporal block) — only the .gxh-visual composition is
   per-skin, so every rule below is scoped .gx-<skin> .gxh-*.
   Takeover scenes: each carries its own full-bleed breakout and its own two
   static collapses (<=900px and reduced-motion), because each rests at a
   different composition. Stages/stickies stay GL-transparent; all colour
   derives from --c-* tokens. ==== */

/* ---- skin: sunline (hours-v5 location-hours-sunline) — HOURS family,
   gxh- namespace, rendered on the shared hoursShell (info column, status pill,
   address block and the seven-row hours card are styled globally; only the
   scene box and .gxh-visual's own composition live here).

   The week as a sun arc. Motion splits in two, deliberately:
   - CONTINUOUS: the sun and its glow ride the arc off --scene-progress, which
     the frame loop already writes on the scene element — one rotating-arm
     transform per element (rotate(-70deg -> +70deg) about the arc centre,
     translate out by the radius, then counter-rotate so the disc's own
     highlight stays upright). Zero extra custom-property writes per frame.
   - QUANTISED: the day chips take .active from hoursWalk (index-coupled to the
     info column's rows) and .gxh-today from the venue clock. The horizon haze
     warms on --gxh-t, which hoursWalk publishes only when the day changes.

   Donor decisions re-taken at port: the donor painted an opaque dark-navy
   stage gradient plus an opaque sky and horizon — all three were
   re-derived to translucent token washes (GL-field transparency decree), so the
   page's own field glows through. The donor's fixed 620px visual cap is dropped
   in favour of the family's vh-based .gxh-visual. The donor's mountain
   silhouette is dropped (a filled slab with hard edges, box checklist). Every
   chip readout prints a spec row via hoursShort() — a day with no parsable
   hours prints an em-dash, never an invented "Closed" (audit rule 13b). ---- */
.gx-sunline{min-height:min(200svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-sunline .gx-sticky{padding:0;
  background:radial-gradient(circle at 74% 26%,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 30rem)}
.gx-sunline .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}
/* the arc geometry is one pivot (--gxh-cx/--gxh-cy) plus one radius, so the sun
   and its glow share a single arm expression and stay on the drawn curve at
   every viewport size */
.gx-sunline .gxh-sunfield{position:absolute;inset:0;overflow:hidden;
  border:1px solid var(--gx-line);border-radius:28px;
  --gxh-r:clamp(140px,25vh,290px);
  --gxh-cx:50%;
  --gxh-cy:80%;
  --gxh-ang:calc(-70deg + var(--scene-progress,0) * 140deg);
  --gxh-sunx:calc(14% + var(--scene-progress,0) * 72%)}
.gx-sunline .gxh-sky{position:absolute;inset:0;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-glow2) 18%,transparent) 0%,
    color-mix(in srgb,var(--c-accent) 7%,transparent) 58%,transparent 100%)}
/* top / left / right borders draw -135deg..135deg of the circle, so the sun
   never rides off the end of the line at the extremes of its 140deg sweep */
.gx-sunline .gxh-arc{position:absolute;left:var(--gxh-cx);top:var(--gxh-cy);
  width:calc(var(--gxh-r) * 2);height:calc(var(--gxh-r) * 2);border-radius:50%;
  border:1px solid transparent;
  border-top-color:color-mix(in srgb,var(--c-ink) 22%,transparent);
  border-right-color:color-mix(in srgb,var(--c-ink) 11%,transparent);
  border-left-color:color-mix(in srgb,var(--c-ink) 11%,transparent);
  transform:translate(-50%,-50%)}
.gx-sunline .gxh-sun-glow{position:absolute;left:var(--gxh-cx);top:var(--gxh-cy);
  width:calc(var(--gxh-r) * 1.2);height:calc(var(--gxh-r) * 1.2);border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-accent) 28%,transparent) 0%,
    color-mix(in srgb,var(--c-accent) 11%,transparent) 38%,transparent 68%);
  transform:translate(-50%,-50%) rotate(var(--gxh-ang)) translateY(calc(var(--gxh-r) * -1)) rotate(calc(var(--gxh-ang) * -1))}
.gx-sunline .gxh-sun{position:absolute;left:var(--gxh-cx);top:var(--gxh-cy);
  width:clamp(34px,5vh,58px);height:clamp(34px,5vh,58px);border-radius:50%;
  background:radial-gradient(circle at 50% 40%,color-mix(in srgb,var(--c-accent) 86%,var(--c-glow2)) 0%,
    var(--c-accent) 46%,color-mix(in srgb,var(--c-accent) 70%,transparent) 100%);
  box-shadow:0 0 44px color-mix(in srgb,var(--c-accent) 55%,transparent);
  transform:translate(-50%,-50%) rotate(var(--gxh-ang)) translateY(calc(var(--gxh-r) * -1)) rotate(calc(var(--gxh-ang) * -1))}
/* haze, not a slab: the fill fades to transparent upward and never reaches
   full opacity, so the page field still reads through the bottom of the box */
.gx-sunline .gxh-horizon{position:absolute;right:0;bottom:0;left:0;height:36%;pointer-events:none;
  background:linear-gradient(180deg,transparent 0%,
    color-mix(in srgb,var(--c-dominant) 42%,transparent) 44%,
    color-mix(in srgb,var(--c-dominant) 70%,transparent) 100%)}
.gx-sunline .gxh-horizon::before{position:absolute;inset:-34% 0 0;content:"";
  background:radial-gradient(ellipse 46% 100% at var(--gxh-sunx) 100%,
    color-mix(in srgb,var(--c-accent) 24%,transparent),transparent 72%);
  opacity:calc(.45 + var(--gxh-t,0) * .55)}
.gx-sunline .gxh-horizon::after{position:absolute;top:0;right:0;left:0;height:1px;content:"";
  background:linear-gradient(90deg,transparent 0%,
    color-mix(in srgb,var(--c-accent) 44%,transparent) var(--gxh-sunx),transparent 100%)}
/* the week, as seven readouts under the horizon — count-general (auto-flow
   column), so a future non-seven hours shape still lays out */
.gx-sunline .gxh-weekday-stack{position:absolute;right:18px;bottom:16px;left:18px;z-index:3;
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:6px}
.gx-sunline .gxh-sun-day{display:grid;gap:3px;padding:8px 4px 7px;border-radius:12px;
  border:1px solid var(--gx-line);
  background:color-mix(in srgb,var(--c-ink) 5%,transparent);text-align:center;
  transition:background .25s ease,border-color .25s ease,color .25s ease,transform .25s ease}
.gx-sunline .gxh-sun-day span{color:color-mix(in srgb,var(--c-ink) 74%,transparent);
  font-family:var(--f-utility);font-size:.62rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase}
.gx-sunline .gxh-sun-day b{color:color-mix(in srgb,var(--c-ink) 90%,transparent);
  font-family:var(--f-utility);font-size:.74rem;font-weight:700;letter-spacing:-.01em}
/* the walking emphasis: accent stays a WASH so the chip's copy keeps reading
   against --c-ink on every palette (never ink-on-accent by accident) */
.gx-sunline .gxh-sun-day.active{transform:translateY(-6px);
  border-color:color-mix(in srgb,var(--c-accent) 55%,transparent);
  background:color-mix(in srgb,var(--c-accent) 18%,transparent);
  box-shadow:0 12px 30px -14px color-mix(in srgb,var(--c-accent) 70%,transparent)}
.gx-sunline .gxh-sun-day.active span{color:var(--c-accent)}
.gx-sunline .gxh-sun-day.active b{color:var(--c-ink)}
/* .gxh-today is the venue clock's day — a separate, non-scroll treatment
   (temporal precedent); inset shadow keeps it out of the layout */
.gx-sunline .gxh-sun-day.gxh-today{box-shadow:inset 0 -2px 0 var(--c-accent)}
.gx-sunline .gxh-sun-day.gxh-today.active{
  box-shadow:inset 0 -2px 0 var(--c-accent),0 12px 30px -14px color-mix(in srgb,var(--c-accent) 70%,transparent)}
/* short-desktop compression (temporal's P8 lesson): the family shrinks
   .gxh-visual to 80vh here, so the arc and the chips come down with it */
@media (min-width:901px) and (max-height:980px){
  .gx-sunline .gxh-sunfield{--gxh-r:clamp(120px,22vh,240px)}
  .gx-sunline .gxh-weekday-stack{right:12px;bottom:12px;left:12px;gap:5px}
  .gx-sunline .gxh-sun-day{padding:6px 3px 5px}
  .gx-sunline .gxh-sun-day span{font-size:.56rem}
  .gx-sunline .gxh-sun-day b{font-size:.66rem}
}
/* ---- sunline: static collapse <=900px — breakout reverted, scene unpinned,
   the sun parked at the apex of its arc (--scene-progress:.5 declared on the
   field wins over the inherited scene value) so the resting composition is a
   readable noon arc with all seven days legible, never a blank box ---- */
@media (max-width:900px){
  .gx-sunline{min-height:auto;width:100%;margin-left:0}
  .gx-sunline .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;background:none}
  .gx-sunline .gx-stage{display:block;min-height:auto;padding:0}
  .gx-sunline .gxh-sunfield{--scene-progress:.5;--gxh-r:clamp(105px,19vh,180px);--gxh-cy:76%;
    border-radius:18px}
  .gx-sunline .gxh-sun,.gx-sunline .gxh-sun-glow,.gx-sunline .gxh-sun-day{transition:none}
  .gx-sunline .gxh-weekday-stack{right:9px;bottom:9px;left:9px;gap:4px}
  .gx-sunline .gxh-sun-day{padding:6px 2px 5px;border-radius:9px}
  .gx-sunline .gxh-sun-day span{font-size:.56rem;letter-spacing:.04em}
  .gx-sunline .gxh-sun-day b{font-size:.63rem}
  .gx-sunline .gxh-sun-day.active{transform:none}
}
/* ---- sunline: reduced-motion still state — same resting noon composition,
   every transition dropped; the venue clock still marks today (a clock is not
   motion, temporal precedent) ---- */
@media (prefers-reduced-motion:reduce){
  .gx-sunline{min-height:auto;width:100%;margin-left:0}
  .gx-sunline .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;background:none}
  .gx-sunline .gx-stage{display:block;min-height:auto;padding:0}
  .gx-sunline .gxh-sunfield{--scene-progress:.5;--gxh-r:clamp(105px,19vh,190px);--gxh-cy:76%}
  .gx-sunline .gxh-sky,.gx-sunline .gxh-arc,.gx-sunline .gxh-sun-glow,
  .gx-sunline .gxh-sun,.gx-sunline .gxh-horizon,.gx-sunline .gxh-sun-day{transition:none;animation:none}
  .gx-sunline .gxh-sun-day.active{transform:none}
}

/* ---- skin: transit-ribbon (hours-01, hours-v5 kit) — HOURS family, gxh-
   namespace. A route drawn through the week. The SVG ribbon reveals with a
   stroke-dashoffset driven by --gxh-route (the road precedent); road's paths
   carry pathLength="1", this renderer's do not, so --gxh-len is the MEASURED
   user-unit length of the emitted d= curve (792.46, rounded up to 800 — a
   dasharray longer than the path only means the reveal completes a hair
   early, never a repeated second dash). 1px == 1 user unit inside the
   viewBox, so the constant tracks the curve at every viewport.
   The .gxh-day-pointer stops and the .gxh-route-dot are seated by seatPath /
   hoursRoute in engine.js — this file NEVER hardcodes a stop position; the
   dot's left/top here are only the path's first point, the resting value the
   animator overwrites on its first frame.
   Donor chrome dropped per doctrine: the .gxh-t1 stage gradient and the
   .gxh-visual panel wash (GL-field transparency), the two-stop path gradient
   (no defs in the emitted markup) re-derived to the accent plus a glow
   drop-shadow, the neon :root re-derived to host tokens ---- */
.gx-transit-ribbon{--gxh-len:800px;min-height:min(205svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-transit-ribbon .gx-sticky{padding:0;
  background:radial-gradient(circle at 76% 24%,color-mix(in srgb,var(--c-accent) 7%,transparent),transparent 30rem)}
.gx-transit-ribbon .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}

/* the scene box: everything absolute inside .gxh-visual (the layout's right
   cell), so the info column is never overlapped at any progress */
.gx-transit-ribbon .gxh-ribbon{position:absolute;inset:0;isolation:isolate}
.gx-transit-ribbon .gxh-ribbon::before{position:absolute;inset:0;z-index:0;content:"";
  background:radial-gradient(circle at 18% 84%,color-mix(in srgb,var(--c-glow2) 12%,transparent),transparent 46%),
    radial-gradient(circle at 78% 16%,color-mix(in srgb,var(--c-accent) 11%,transparent),transparent 44%);
  -webkit-mask-image:radial-gradient(circle at 50% 50%,rgba(0,0,0,1) 40%,transparent 82%);
  mask-image:radial-gradient(circle at 50% 50%,rgba(0,0,0,1) 40%,transparent 82%)}
/* the one continuous motion: a signal haze travelling the route. It reads
   --scene-progress, which the frame loop already writes on the scene element,
   so the ride costs the animator zero extra writes. */
.gx-transit-ribbon .gxh-ribbon::after{position:absolute;inset:0;z-index:1;content:"";pointer-events:none;
  background:radial-gradient(circle at 12% 86%,color-mix(in srgb,var(--c-glow3) 14%,transparent),transparent 22%);
  translate:calc(var(--scene-progress,0) * 64%) calc(var(--scene-progress,0) * -66%);
  opacity:calc(.35 + var(--scene-progress,0) * .5)}

.gx-transit-ribbon .gxh-ribbon svg{position:absolute;inset:0;z-index:2;width:100%;height:100%}
.gx-transit-ribbon .gxh-path-base,
.gx-transit-ribbon .gxh-path-active{fill:none;stroke-linecap:round;stroke-linejoin:round}
.gx-transit-ribbon .gxh-path-base{stroke:color-mix(in srgb,var(--c-ink) 11%,transparent);stroke-width:11px}
.gx-transit-ribbon .gxh-path-active{stroke:var(--c-accent);stroke-width:13px;
  stroke-dasharray:var(--gxh-len);
  stroke-dashoffset:calc(var(--gxh-len) * (1 - var(--gxh-route,0)));
  filter:drop-shadow(0 0 12px color-mix(in srgb,var(--c-glow2) 55%,transparent))}

/* day stops — position:absolute only; seatPath owns left/top */
.gx-transit-ribbon .gxh-day-pointers{position:absolute;inset:0;z-index:3;pointer-events:none}
.gx-transit-ribbon .gxh-day-pointer{position:absolute;display:grid;justify-items:center;gap:7px;
  transform:translate(-50%,-50%);
  transition:opacity .28s ease,filter .28s ease}
.gx-transit-ribbon .gxh-dot{width:14px;height:14px;border-radius:50%;
  border:2px solid color-mix(in srgb,var(--c-ink) 34%,transparent);
  background:color-mix(in srgb,var(--c-dominant) 88%,var(--c-ink));
  transition:background .24s ease,border-color .24s ease,transform .24s ease}
.gx-transit-ribbon .gxh-tag{display:grid;justify-items:center;gap:2px;
  padding:5px 10px;border:1px solid var(--gx-line);border-radius:999px;
  background:color-mix(in srgb,var(--c-dominant) 74%,transparent);
  color:color-mix(in srgb,var(--c-ink) 92%,transparent);
  font-family:var(--f-utility);font-size:.66rem;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;white-space:nowrap;
  transition:background .24s ease,border-color .24s ease,color .24s ease}
.gx-transit-ribbon .gxh-tag b{font-size:.62rem;font-weight:700;letter-spacing:.04em;
  color:color-mix(in srgb,var(--c-ink) 74%,transparent)}
/* .active is the scroll walk's emphasis (hoursWalk); every stop stays legible
   at 30/60/90% — the inactive treatment dims the ring, never the copy */
.gx-transit-ribbon .gxh-day-pointer.active{z-index:4}
.gx-transit-ribbon .gxh-day-pointer.active .gxh-dot{
  border-color:var(--c-accent);background:var(--c-accent);transform:scale(1.2);
  box-shadow:0 0 0 6px color-mix(in srgb,var(--c-accent) 14%,transparent)}
.gx-transit-ribbon .gxh-day-pointer.active .gxh-tag{
  border-color:color-mix(in srgb,var(--c-accent) 60%,transparent);
  background:var(--c-accent);color:var(--c-on-accent)}
.gx-transit-ribbon .gxh-day-pointer.active .gxh-tag b{color:var(--c-on-accent)}
/* the venue clock's today marker is a separate treatment from the walk */
.gx-transit-ribbon .gxh-day-pointer.gxh-today .gxh-tag{
  border-color:color-mix(in srgb,var(--c-accent) 52%,transparent)}

/* the travelling marker — hoursRoute writes left/top each frame; the values
   below are the curve's first point, so a scene that has not been walked yet
   still reads as parked at the start of the route rather than in a corner */
.gx-transit-ribbon .gxh-route-dot{position:absolute;z-index:5;left:7.917%;top:82.222%;
  width:20px;height:20px;border-radius:50%;
  background:radial-gradient(circle at 34% 30%,var(--c-on-accent),var(--c-accent) 46%,
    color-mix(in srgb,var(--c-accent) 62%,var(--c-glow2)) 100%);
  box-shadow:0 0 0 9px color-mix(in srgb,var(--c-accent) 11%,transparent),
    0 0 46px color-mix(in srgb,var(--c-accent) 52%,transparent);
  transform:translate(-50%,-50%)}

/* the address plate — spec copy, never invented */
.gx-transit-ribbon .gxh-arrival-board{position:absolute;z-index:6;
  right:clamp(12px,2.2vw,32px);bottom:clamp(12px,2.6vh,28px);
  max-width:min(320px,68%);padding:13px 16px;
  border:1px solid var(--gx-line);border-radius:18px;
  background:color-mix(in srgb,var(--c-dominant) 84%,transparent);
  backdrop-filter:blur(7px);-webkit-backdrop-filter:blur(7px)}
.gx-transit-ribbon .gxh-arrival-board h4{margin:0 0 5px;color:var(--c-accent);
  font-family:var(--f-utility);font-size:.66rem;font-weight:800;letter-spacing:.16em;
  text-transform:uppercase}
.gx-transit-ribbon .gxh-arrival-board p{margin:0;color:var(--c-ink);
  font-size:.95rem;font-weight:700;line-height:1.4}

@media (min-width:901px) and (max-width:1180px){
  .gx-transit-ribbon .gxh-tag{padding:4px 8px;font-size:.58rem}
  .gx-transit-ribbon .gxh-tag b{font-size:.55rem}
  .gx-transit-ribbon .gxh-dot{width:12px;height:12px}
  .gx-transit-ribbon .gxh-arrival-board{max-width:min(260px,74%);padding:11px 13px}
}

/* ---- static collapse (<=900px): unpinned, full-bleed reverted, the route
   drawn to its end and the week read as a chip grid. seatPath never runs here
   (the frame loop stands down), so nothing may depend on JS seating — the
   stops are re-flowed to a static grid with left/top defeated. ---- */
@media (max-width:900px){
  .gx-transit-ribbon{min-height:auto;width:100%;margin-left:0}
  .gx-transit-ribbon .gx-sticky{padding:26px 0 36px}
  .gx-transit-ribbon .gx-stage{display:block;min-height:auto;padding:0}
  .gx-transit-ribbon .gxh-visual{order:2;height:auto;min-height:0;overflow:visible}
  .gx-transit-ribbon .gxh-ribbon{--gxh-route:1;position:relative;inset:auto;display:grid;gap:12px}
  .gx-transit-ribbon .gxh-ribbon::before{position:absolute;inset:0;
    -webkit-mask-image:none;mask-image:none;opacity:.7}
  .gx-transit-ribbon .gxh-ribbon::after{display:none}
  .gx-transit-ribbon .gxh-ribbon svg{position:relative;inset:auto;z-index:2;
    display:block;width:100%;height:190px}
  .gx-transit-ribbon .gxh-path-active{filter:none}
  .gx-transit-ribbon .gxh-route-dot{display:none}
  .gx-transit-ribbon .gxh-day-pointers{position:relative;inset:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(92px,1fr));gap:8px;pointer-events:auto}
  .gx-transit-ribbon .gxh-day-pointer{position:relative!important;
    left:auto!important;top:auto!important;transform:none;opacity:1}
  .gx-transit-ribbon .gxh-day-pointer .gxh-dot{display:none}
  .gx-transit-ribbon .gxh-tag{width:100%;padding:7px 8px;font-size:.6rem}
  .gx-transit-ribbon .gxh-arrival-board{position:relative;z-index:6;
    right:auto;bottom:auto;max-width:none;backdrop-filter:none;-webkit-backdrop-filter:none}
}

/* ---- static collapse (reduced motion): identical resting composition, all
   transitions stood down ---- */
@media (prefers-reduced-motion:reduce){
  .gx-transit-ribbon{min-height:auto;width:100%;margin-left:0}
  .gx-transit-ribbon .gx-sticky{padding:26px 0 36px}
  .gx-transit-ribbon .gx-stage{display:block;min-height:auto;padding:0}
  .gx-transit-ribbon .gxh-visual{order:2;height:auto;min-height:0;overflow:visible}
  .gx-transit-ribbon .gxh-ribbon{--gxh-route:1;position:relative;inset:auto;display:grid;gap:12px}
  .gx-transit-ribbon .gxh-ribbon::before{position:absolute;inset:0;
    -webkit-mask-image:none;mask-image:none;opacity:.7}
  .gx-transit-ribbon .gxh-ribbon::after{display:none}
  .gx-transit-ribbon .gxh-ribbon svg{position:relative;inset:auto;z-index:2;
    display:block;width:100%;height:190px}
  .gx-transit-ribbon .gxh-path-active{filter:none}
  .gx-transit-ribbon .gxh-route-dot{display:none}
  .gx-transit-ribbon .gxh-day-pointers{position:relative;inset:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(92px,1fr));gap:8px;pointer-events:auto}
  .gx-transit-ribbon .gxh-day-pointer{position:relative!important;
    left:auto!important;top:auto!important;transform:none;opacity:1;transition:none}
  .gx-transit-ribbon .gxh-day-pointer .gxh-dot{display:none}
  .gx-transit-ribbon .gxh-tag{width:100%;padding:7px 8px;font-size:.6rem;transition:none}
  .gx-transit-ribbon .gxh-arrival-board{position:relative;z-index:6;
    right:auto;bottom:auto;max-width:none;backdrop-filter:none;-webkit-backdrop-filter:none}
}

/* ---- skin: neon-columns (hours-v5 location-hours-neon-columns) — HOURS
   family, gxh- namespace. Seven light columns; each .gxh-neon-col holds an
   <i> whose height is that day's own --gxh-fill (open-span percentage,
   computed at render — a day with no parsable span renders a dim stub, never
   an invented value). The .gxh-runner sweeps across the columns off the walk
   index (--gxh-t, published by hoursWalk on the SCENE element only on
   change) and .gxh-trace is its rail; the rail's spark is the only
   continuous motion and rides --scene-progress, which the frame loop already
   writes — zero extra property writes. .gxh-column-caption reads out the
   active day and its real hours via [data-gxh-out] (spec copy, never
   formatted guesses). .active is the scroll walk's emphasis, .gxh-today is
   the venue clock's day — two distinct treatments, one clock source.
   Donor's neon :root (gold/green/pink plus a near-black stage wash) re-derived
   to --c-* tokens; the donor's opaque stage gradient is DROPPED per the
   GL-field transparency decree and replaced by an 8% accent radial. Info
   column, status pill, address and hours card are the shared hoursShell and
   are styled globally — this block owns only the scene box and .gxh-visual's
   own composition, which sits on the layout's empty right side. ---- */
.gx-neon-columns{min-height:min(200svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw);
  --gxh-t:0;
  --gxn-pad-x:30px;    /* side inset of the column field, trace and runner */
  --gxn-top:96px;      /* clear zone the caption owns — bars never reach it */
  --gxn-bottom:22px;   /* field bottom inset */
  --gxn-label:30px;    /* day-label strip at the foot of every column */
  --gxn-base:52px}     /* = --gxn-bottom + --gxn-label: the bar baseline */
.gx-neon-columns .gx-sticky{padding:0;
  background:radial-gradient(circle at 78% 26%,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 30rem)}
.gx-neon-columns .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}
.gx-neon-columns .gxh-neonfield{position:absolute;inset:0}

/* caption — prints the active day's spec row (data-gxh-out), no filled plate */
.gx-neon-columns .gxh-column-caption{position:absolute;left:var(--gxn-pad-x);top:26px;z-index:3;
  display:flex;align-items:baseline;gap:14px;padding-bottom:9px;
  max-width:calc(100% - var(--gxn-pad-x) * 2);
  border-bottom:1px solid color-mix(in srgb,var(--c-accent) 32%,transparent)}
.gx-neon-columns .gxh-column-day{color:var(--gx-muted);font-family:var(--f-utility);
  font-size:.76rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gx-neon-columns .gxh-column-time{color:var(--c-ink);font-family:var(--f-utility);
  font-size:clamp(1.02rem,1.6vw,1.34rem);font-weight:800;letter-spacing:-.01em}

/* the rail the runner walks, plus its one continuous spark */
.gx-neon-columns .gxh-trace{position:absolute;left:var(--gxn-pad-x);right:var(--gxn-pad-x);
  bottom:var(--gxn-base);height:1px;
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 40%,transparent),
    color-mix(in srgb,var(--c-glow2) 46%,transparent),
    color-mix(in srgb,var(--c-accent) 40%,transparent))}
.gx-neon-columns .gxh-trace::after{content:"";position:absolute;left:0;top:-3px;bottom:-3px;width:14%;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--c-glow2) 62%,transparent),transparent);
  transform:translateX(calc(var(--scene-progress,0) * 614%))}

/* runner — the BOX spans the whole column track, so translateX percentages
   resolve against the track width and the sweep stays on the compositor;
   the visible 2px line and its head are the pseudo-elements. Column centres
   for seven equal, gapless columns are (i + .5) / 7, and --gxh-t is
   idx / (n - 1), hence (t * 6 + .5) / 7. */
.gx-neon-columns .gxh-runner{position:absolute;left:var(--gxn-pad-x);right:var(--gxn-pad-x);
  top:var(--gxn-top);bottom:var(--gxn-base);z-index:2;pointer-events:none;
  transform:translateX(calc((var(--gxh-t,0) * 6 + .5) / 7 * 100%));
  transition:transform .5s cubic-bezier(.22,.61,.36,1)}
.gx-neon-columns .gxh-runner::before{content:"";position:absolute;left:-1px;top:0;bottom:0;width:2px;
  background:linear-gradient(180deg,transparent,
    color-mix(in srgb,var(--c-glow3) 76%,transparent) 26%,
    color-mix(in srgb,var(--c-accent) 92%,transparent) 74%,transparent);
  box-shadow:0 0 18px color-mix(in srgb,var(--c-accent) 42%,transparent)}
.gx-neon-columns .gxh-runner::after{content:"";position:absolute;left:-6px;bottom:-6px;
  width:12px;height:12px;border-radius:50%;background:var(--c-accent);
  box-shadow:0 0 22px color-mix(in srgb,var(--c-accent) 66%,transparent)}

/* the seven columns — gapless tracks keep the runner maths exact; the visual
   gap is the bar's own left/right inset */
.gx-neon-columns .gxh-columns{position:absolute;
  inset:var(--gxn-top) var(--gxn-pad-x) var(--gxn-bottom);
  display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:0;align-items:stretch}
.gx-neon-columns .gxh-neon-col{position:relative;padding-bottom:var(--gxn-label)}
.gx-neon-columns .gxh-neon-col i{position:absolute;left:7px;right:7px;bottom:var(--gxn-label);
  height:var(--gxh-fill,8%);min-height:10px;max-height:calc(100% - 34px);
  border-radius:14px 14px 3px 3px;font-style:normal;transform-origin:50% 100%;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 60%,transparent),
    color-mix(in srgb,var(--c-accent) 10%,transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--c-ink) 10%,transparent),
    0 0 26px color-mix(in srgb,var(--c-accent) 14%,transparent);
  transition:transform .32s ease,background .32s ease,box-shadow .32s ease}
.gx-neon-columns .gxh-neon-col span{position:absolute;left:0;right:0;bottom:6px;text-align:center;
  color:color-mix(in srgb,var(--c-ink) 66%,transparent);font-family:var(--f-utility);
  font-size:.72rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  transition:color .3s ease}
.gx-neon-columns .gxh-neon-col.active i{transform:translateY(-12px) scaleX(1.06);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 94%,transparent),
    color-mix(in srgb,var(--c-glow3) 28%,transparent));
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--c-ink) 18%,transparent),
    0 0 46px color-mix(in srgb,var(--c-accent) 36%,transparent)}
.gx-neon-columns .gxh-neon-col.active span{color:var(--c-ink)}
/* venue clock's day — accent, deliberately distinct from the walk emphasis */
.gx-neon-columns .gxh-neon-col.gxh-today span{color:var(--c-accent)}

/* short-desktop compression (the P8 lesson: at 100svh the field crowds the
   caption on ~900px-tall desktops) */
@media (min-width:901px) and (max-height:980px){
  .gx-neon-columns{--gxn-top:74px;--gxn-base:46px;--gxn-label:26px}
  .gx-neon-columns .gxh-column-caption{top:15px;padding-bottom:7px}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-neon-columns{--gxn-pad-x:20px}
  .gx-neon-columns .gxh-neon-col i{left:5px;right:5px}
}

/* ---- static collapse <=900px: unpinned, breakout reverted, resting
   composition = every column at its real fill, the first day read out by the
   server-rendered caption and marked as the resting emphasis ---- */
@media (max-width:900px){
  .gx-neon-columns{min-height:auto;width:100%;margin-left:0;
    --gxn-pad-x:14px;--gxn-top:64px;--gxn-bottom:16px;--gxn-label:24px;--gxn-base:40px}
  .gx-neon-columns .gx-sticky{position:relative;padding:26px 0 36px;background:none}
  .gx-neon-columns .gx-stage{display:block;min-height:auto;padding:0}
  .gx-neon-columns .gxh-column-caption{top:14px;gap:10px;padding-bottom:7px}
  .gx-neon-columns .gxh-column-time{font-size:1.02rem}
  .gx-neon-columns .gxh-trace::after{transform:translateX(307%)}
  .gx-neon-columns .gxh-runner{transition:none}
  .gx-neon-columns .gxh-neon-col i{left:4px;right:4px;max-height:calc(100% - 28px);
    transform:none!important;transition:none}
  .gx-neon-columns .gxh-neon-col span{bottom:5px;font-size:.56rem;letter-spacing:.05em;transition:none}
  .gx-neon-columns .gxh-neon-col:first-child i{
    background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 94%,transparent),
      color-mix(in srgb,var(--c-glow3) 28%,transparent))}
  .gx-neon-columns .gxh-neon-col:first-child span{color:var(--c-ink)}
}
/* ---- reduced-motion still state: same resting composition, no transitions,
   the rail spark parked mid-track ---- */
@media (prefers-reduced-motion:reduce){
  .gx-neon-columns{min-height:auto;width:100%;margin-left:0;
    --gxn-pad-x:14px;--gxn-top:64px;--gxn-bottom:16px;--gxn-label:24px;--gxn-base:40px}
  .gx-neon-columns .gx-sticky{position:relative;padding:26px 0 36px;background:none}
  .gx-neon-columns .gx-stage{display:block;min-height:auto;padding:0}
  .gx-neon-columns .gxh-column-caption{top:14px;gap:10px;padding-bottom:7px}
  .gx-neon-columns .gxh-column-time{font-size:1.02rem}
  .gx-neon-columns .gxh-trace::after{transform:translateX(307%)}
  .gx-neon-columns .gxh-runner{transition:none}
  .gx-neon-columns .gxh-neon-col i{left:4px;right:4px;max-height:calc(100% - 28px);
    transform:none!important;transition:none}
  .gx-neon-columns .gxh-neon-col span{bottom:5px;font-size:.56rem;letter-spacing:.05em;transition:none}
  .gx-neon-columns .gxh-neon-col:first-child i{
    background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 94%,transparent),
      color-mix(in srgb,var(--c-glow3) 28%,transparent))}
  .gx-neon-columns .gxh-neon-col:first-child span{color:var(--c-ink)}
}

/* ---- skin: frequency-board (hours-v5 location-hours-frequency-board) — HOURS
   family, gxh- namespace, built on hoursShell() so the info column (status
   pill / address / seven-row hours card) is the shared one and only
   .gxh-visual differs. A DEPARTURES BOARD: .gxh-ticker-track carries the week
   TWICE and slides -50% on a pure-CSS marquee (seamless because the second
   copy starts at exactly half the track width — spans carry padding-right
   instead of a flex gap, which would leave half a gap of drift at the wrap);
   .gxh-frequency-focus is the plate for the active day, .gxh-board-rows is the
   per-day list that takes .active from hoursWalk. Motion is CSS-only: the
   marquee is a keyframe, the parallax and the sweep read --scene-progress
   (already written on the scene element by the frame loop), so the animator
   writes nothing beyond the shared walk. Donor's opaque dominant stage
   gradient stripped (GL-field transparency decree) — only a <=15% accent
   radial remains; the focus plate keeps its own copy scrim so text never
   borrows contrast from the ticker behind it. ---- */
.gx-frequency-board{min-height:min(210svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-frequency-board .gx-sticky{padding:0;
  background:radial-gradient(circle at 76% 30%,color-mix(in srgb,var(--c-accent) 7%,transparent),transparent 28rem)}
.gx-frequency-board .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}

.gx-frequency-board .gxh-board{position:absolute;inset:0;overflow:hidden}
/* progress sweep — a single hairline riding --scene-progress (zero JS writes) */
.gx-frequency-board .gxh-board::after{content:"";position:absolute;top:0;bottom:0;width:1px;
  left:clamp(0px,calc(var(--scene-progress,0) * 100%),calc(100% - 1px));pointer-events:none;z-index:1;
  background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--c-accent) 34%,transparent) 42%,transparent)}

/* the marquee band */
.gx-frequency-board .gxh-ticker{position:absolute;left:0;right:0;top:50%;
  display:grid;align-content:center;height:clamp(150px,26vh,260px);overflow:hidden;
  border-top:1px solid var(--gx-line);border-bottom:1px solid var(--gx-line);
  translate:0 calc(-50% + (var(--scene-progress,0) - .5) * -26px)}
.gx-frequency-board .gxh-ticker-track{display:flex;flex-wrap:nowrap;white-space:nowrap;
  width:max-content;will-change:transform;animation:gxh-board-run 46s linear infinite}
.gx-frequency-board .gxh-ticker-track span{padding-right:clamp(26px,3.4vw,58px);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2rem,5.4vw,5.2rem);line-height:1;letter-spacing:-.06em;
  color:color-mix(in srgb,var(--c-ink) 13%,transparent)}
@keyframes gxh-board-run{from{transform:translateX(0)}to{transform:translateX(-50%)}}

/* the active-day plate — sits over the ticker on the visual's LEFT half, and
   the visual is the layout's right column, so it never reaches .gxh-info */
.gx-frequency-board .gxh-frequency-focus{position:absolute;left:0;right:37%;top:50%;
  transform:translateY(-50%);z-index:2;padding:26px 20px 26px 6%}
.gx-frequency-board .gxh-frequency-focus::before{content:"";position:absolute;z-index:-1;
  inset:-14% -18% -14% -22%;filter:blur(10px);
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 72%,transparent),
    color-mix(in srgb,var(--c-dominant) 46%,transparent) 58%,transparent)}
.gx-frequency-board .gxh-focus-day{display:block;font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2.4rem,5.6vw,5.4rem);line-height:.9;
  letter-spacing:-.07em;color:var(--c-ink)}
.gx-frequency-board .gxh-focus-time{display:block;margin-top:14px;font-family:var(--f-utility);
  font-size:clamp(1.1rem,2.1vw,2.1rem);font-weight:700;letter-spacing:-.01em;
  color:color-mix(in srgb,var(--c-accent) 66%,var(--c-ink))}
.gx-frequency-board .gxh-frequency-focus small{display:block;margin-top:12px;max-width:36ch;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.74rem;
  letter-spacing:.12em;text-transform:uppercase;line-height:1.55}

/* the week as a departures list, right edge of the visual */
.gx-frequency-board .gxh-board-rows{position:absolute;right:0;top:50%;transform:translateY(-50%);
  z-index:3;width:min(34%,300px);display:grid;gap:2px}
.gx-frequency-board .gxh-board-row{display:grid;grid-template-columns:1fr auto;gap:14px;
  align-items:baseline;padding:9px 12px;border-left:2px solid transparent;
  font-family:var(--f-utility);font-size:.82rem;font-weight:700;
  color:color-mix(in srgb,var(--c-ink) 64%,transparent);
  transition:color .25s ease,background .25s ease,border-color .25s ease,translate .25s ease}
.gx-frequency-board .gxh-board-row span{letter-spacing:.14em;text-transform:uppercase}
.gx-frequency-board .gxh-board-row b{font-weight:800;letter-spacing:-.01em}
.gx-frequency-board .gxh-board-row.active{color:var(--c-ink);translate:-7px 0;
  border-left-color:var(--c-accent);
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 14%,transparent),transparent)}
.gx-frequency-board .gxh-board-row.active b{color:color-mix(in srgb,var(--c-accent) 72%,var(--c-ink))}
/* .gxh-today is the venue clock's day — a distinct treatment from the walk */
.gx-frequency-board .gxh-board-row.gxh-today span{color:var(--c-accent)}

/* short-desktop compression (temporal precedent): at 100svh the info column
   clips on ~900px-tall desktops, and the plate needs to shrink with it */
@media (min-width:901px) and (max-height:980px){
  .gx-frequency-board .gxh-ticker{height:clamp(120px,22vh,200px)}
  .gx-frequency-board .gxh-focus-day{font-size:clamp(2rem,4.4vw,4rem)}
  .gx-frequency-board .gxh-focus-time{margin-top:8px;font-size:clamp(1rem,1.8vw,1.6rem)}
  .gx-frequency-board .gxh-frequency-focus small{margin-top:8px;font-size:.68rem}
  .gx-frequency-board .gxh-board-row{padding:6px 10px;font-size:.75rem}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-frequency-board .gxh-frequency-focus{right:41%;padding-left:4%}
  .gx-frequency-board .gxh-board-rows{width:min(38%,240px)}
}

/* ---- static collapse <=900px: unpin, revert the breakout, stack the board
   into a readable resting composition (ticker strip, plate, full week) ---- */
@media (max-width:900px){
  .gx-frequency-board{min-height:auto;width:100%;margin-left:0}
  .gx-frequency-board .gx-sticky{padding:24px 0 32px}
  .gx-frequency-board .gx-stage{display:block;min-height:auto;padding:0}
  .gx-frequency-board .gxh-visual{height:auto;min-height:0;overflow:visible}
  .gx-frequency-board .gxh-board{position:relative;inset:auto;display:grid;gap:18px}
  .gx-frequency-board .gxh-board::after{display:none}
  .gx-frequency-board .gxh-ticker{position:relative;left:auto;right:auto;top:auto;
    height:auto;padding:10px 0;translate:none}
  .gx-frequency-board .gxh-ticker-track{animation:none;transform:none}
  .gx-frequency-board .gxh-ticker-track span{font-size:clamp(1.4rem,7vw,2.4rem);
    padding-right:22px;color:color-mix(in srgb,var(--c-ink) 22%,transparent)}
  .gx-frequency-board .gxh-frequency-focus{position:relative;left:auto;right:auto;top:auto;
    transform:none;padding:0}
  .gx-frequency-board .gxh-frequency-focus::before{display:none}
  .gx-frequency-board .gxh-focus-day{font-size:clamp(2.2rem,10vw,3.4rem)}
  .gx-frequency-board .gxh-board-rows{position:relative;right:auto;top:auto;transform:none;
    width:100%}
  .gx-frequency-board .gxh-board-row{padding:8px 10px;font-size:.8rem}
  .gx-frequency-board .gxh-board-row.active{translate:none}
}

/* ---- static collapse under reduced motion: identical resting composition,
   marquee and sweep both stopped ---- */
@media (prefers-reduced-motion: reduce){
  .gx-frequency-board{min-height:auto;width:100%;margin-left:0}
  .gx-frequency-board .gx-sticky{padding:24px 0 32px}
  .gx-frequency-board .gx-stage{display:block;min-height:auto;padding:0}
  .gx-frequency-board .gxh-visual{height:auto;min-height:0;overflow:visible}
  .gx-frequency-board .gxh-board{position:relative;inset:auto;display:grid;gap:18px}
  .gx-frequency-board .gxh-board::after{display:none}
  .gx-frequency-board .gxh-ticker{position:relative;left:auto;right:auto;top:auto;
    height:auto;padding:10px 0;translate:none}
  .gx-frequency-board .gxh-ticker-track{animation:none;transform:none}
  .gx-frequency-board .gxh-ticker-track span{font-size:clamp(1.4rem,4vw,2.4rem);
    padding-right:22px;color:color-mix(in srgb,var(--c-ink) 22%,transparent)}
  .gx-frequency-board .gxh-frequency-focus{position:relative;left:auto;right:auto;top:auto;
    transform:none;padding:0}
  .gx-frequency-board .gxh-frequency-focus::before{display:none}
  .gx-frequency-board .gxh-board-rows{position:relative;right:auto;top:auto;transform:none;
    width:100%}
  .gx-frequency-board .gxh-board-row{padding:8px 10px;font-size:.8rem}
  .gx-frequency-board .gxh-board-row.active{translate:none}
}

/* ---- skin: metro-route (hours-v5 #05 "metro route") — HOURS family, gxh-
   namespace. The week as a transit line: .gxh-metro-line-base is the whole
   route, .gxh-metro-line-active draws to --gxh-route (published by
   hoursRoute), .gxh-route-dot rides the curve by getPointAtLength and each
   .gxh-metro-station chip prints the SPEC row's day + compact hours
   (hoursShort — an unparsable row prints an em-dash, never an invented
   "Closed"). Station seating is seatPath discipline: position:absolute, JS
   owns left/top. The :nth-child seeds below are the arc-length points of the
   renderer's OWN path (viewBox 960x720, length 1630.6u, sampled at i/6) so
   the composition is already correct before the first frame — and in both
   static collapses, where the frame loop stands down and nothing is ever
   seated. Colour is token-only; the sticky carries a 7% accent radial and
   nothing else (GL-field transparency decree). SPLICE NOTE: this block goes
   in the CARD SKIN LIBRARY region NEXT TO .gx-temporal (i.e. after the shared
   `.gx-scene .gx-stage` collapse rules ~line 1226) — same requirement the
   temporal collapse rules already have. ---- */
.gx-metro-route{min-height:min(200svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw);
  --gxh-len:1631px;--gxh-route:0}
.gx-metro-route .gx-sticky{padding:0;
  background:radial-gradient(circle at 74% 26%,color-mix(in srgb,var(--c-accent) 7%,transparent),transparent 32rem)}
.gx-metro-route .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}

/* the route box: same box for the svg, the stations and the dot, so the
   percentage seating the animator writes lines up with the stretched path */
.gx-metro-route .gxh-metro{position:absolute;inset:18px 48px}
/* atmosphere only — a glow field, never a filled plane */
.gx-metro-route .gxh-metro::before{content:"";position:absolute;inset:-8%;pointer-events:none;
  background:
    radial-gradient(ellipse at 30% 22%,color-mix(in srgb,var(--c-glow2) 10%,transparent),transparent 62%),
    radial-gradient(ellipse at 78% 76%,color-mix(in srgb,var(--c-glow3) 9%,transparent),transparent 60%);
  opacity:calc(.5 + var(--scene-progress,0) * .5)}
.gx-metro-route .gxh-metro svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gx-metro-route .gxh-metro-line-base{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:12;
  stroke:color-mix(in srgb,var(--c-ink) 13%,transparent)}
.gx-metro-route .gxh-metro-line-active{fill:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:6;
  stroke:var(--c-accent);
  stroke-dasharray:var(--gxh-len);
  /* a 6% head of colour is always drawn, so the route reads as a route at 0 */
  stroke-dashoffset:calc(var(--gxh-len) * (1 - (.06 + var(--gxh-route,0) * .94)));
  filter:drop-shadow(0 0 9px color-mix(in srgb,var(--c-accent) 55%,transparent))}

/* the travelling dot — the animator writes left/top; the seed is the path's
   first point so it never sits in the corner on the first paint */
.gx-metro-route .gxh-route-dot{position:absolute;left:6.67%;top:82.50%;z-index:4;pointer-events:none;
  width:clamp(13px,1.3vw,18px);aspect-ratio:1;border-radius:50%;transform:translate(-50%,-50%);
  background:var(--c-accent);
  box-shadow:0 0 0 4px color-mix(in srgb,var(--c-accent) 20%,transparent),
             0 0 26px color-mix(in srgb,var(--c-accent) 55%,transparent)}
.gx-metro-route .gxh-route-dot::after{content:"";position:absolute;inset:-150%;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-accent) 34%,transparent);
  opacity:calc(.2 + var(--scene-progress,0) * .55)}

/* stations: day + compact hours, seated ON the curve */
.gx-metro-route .gxh-metro-stations{position:absolute;inset:0;z-index:3;pointer-events:none}
.gx-metro-route .gxh-metro-station{position:absolute;transform:translate(-50%,-50%);
  min-width:74px;padding:8px 10px;border-radius:12px;text-align:center;
  border:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent);
  background:color-mix(in srgb,var(--c-field) 62%,transparent);
  transition:background .28s ease,border-color .28s ease,box-shadow .28s ease,scale .28s ease}
.gx-metro-route .gxh-metro-station span{display:block;font-family:var(--f-utility);
  font-size:.62rem;font-weight:800;letter-spacing:.14em;text-transform:uppercase;
  color:color-mix(in srgb,var(--c-ink) 72%,transparent)}
.gx-metro-route .gxh-metro-station b{display:block;margin-top:3px;font-family:var(--f-utility);
  font-size:.86rem;font-weight:800;letter-spacing:-.01em;
  color:color-mix(in srgb,var(--c-ink) 96%,transparent)}
/* .gxh-today is the venue clock's FUNCTIONAL marker (never the scroll walk) */
.gx-metro-route .gxh-metro-station.gxh-today{
  border-color:color-mix(in srgb,var(--c-glow3) 60%,transparent);
  box-shadow:0 0 0 3px color-mix(in srgb,var(--c-glow3) 15%,transparent)}
/* .active is the scroll tour's walking emphasis (hoursWalk) */
.gx-metro-route .gxh-metro-station.active{scale:1.1;
  background:color-mix(in srgb,var(--c-accent) 94%,var(--c-ink));
  border-color:color-mix(in srgb,var(--c-accent) 72%,transparent);
  box-shadow:0 0 30px color-mix(in srgb,var(--c-accent) 38%,transparent)}
.gx-metro-route .gxh-metro-station.active span{color:color-mix(in srgb,var(--c-on-accent) 86%,transparent)}
.gx-metro-route .gxh-metro-station.active b{color:var(--c-on-accent)}
.gx-metro-route .gxh-metro-station.active.gxh-today{
  box-shadow:0 0 0 3px color-mix(in srgb,var(--c-glow3) 34%,transparent),
             0 0 30px color-mix(in srgb,var(--c-accent) 38%,transparent)}

/* arc-length seeds for the renderer's path (i/6 of 1630.6u). The animator
   overwrites these inline the moment the scene is measured; they exist so the
   scene is never a pile of chips in the corner. */
.gx-metro-route .gxh-metro-station:nth-child(1){left:6.67%;top:82.50%}
.gx-metro-route .gxh-metro-station:nth-child(2){left:19.82%;top:51.01%}
.gx-metro-route .gxh-metro-station:nth-child(3){left:34.30%;top:20.13%}
.gx-metro-route .gxh-metro-station:nth-child(4){left:52.10%;top:44.85%}
.gx-metro-route .gxh-metro-station:nth-child(5){left:62.96%;top:79.41%}
.gx-metro-route .gxh-metro-station:nth-child(6){left:80.43%;top:57.19%}
.gx-metro-route .gxh-metro-station:nth-child(7){left:93.75%;top:25.28%}

/* short-desktop compression (P8 ledger precedent): the info column is the
   shared shell's, but the route box needs the same breathing room */
@media (min-width:901px) and (max-height:980px){
  .gx-metro-route .gxh-metro{inset:12px 40px}
  .gx-metro-route .gxh-metro-station{min-width:66px;padding:6px 8px}
  .gx-metro-route .gxh-metro-station b{font-size:.78rem}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-metro-route .gxh-metro{inset:12px 36px}
  .gx-metro-route .gxh-metro-station{min-width:60px;padding:6px 7px}
  .gx-metro-route .gxh-metro-station span{font-size:.55rem;letter-spacing:.1em}
  .gx-metro-route .gxh-metro-station b{font-size:.74rem}
}

/* ---- static collapse <=900px: unpinned, unpositioned, fully drawn ----
   The frame loop stands down here, so NOTHING is ever seated: the stations go
   position:static (the animator's inline left/top become inert) and lay out as
   a legible seven-chip grid under a fully-drawn line. */
@media (max-width:900px){
  .gx-metro-route{min-height:auto;width:100%;margin-left:0}
  .gx-metro-route .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;overflow:visible}
  .gx-metro-route .gx-stage{display:block;min-height:auto;padding:0}
  .gx-metro-route .gxh-visual{order:2;height:auto;min-height:0;overflow:visible}
  .gx-metro-route .gxh-metro{position:relative;inset:auto;display:grid;gap:14px}
  .gx-metro-route .gxh-metro::before{display:none}
  .gx-metro-route .gxh-metro svg{position:relative;inset:auto;width:100%;height:104px}
  .gx-metro-route .gxh-metro-line-base{stroke-width:16}
  .gx-metro-route .gxh-metro-line-active{stroke-width:9;stroke-dasharray:none;stroke-dashoffset:0}
  /* the dot marks travel; with the whole route drawn there is nothing to mark */
  .gx-metro-route .gxh-route-dot{display:none}
  .gx-metro-route .gxh-metro-stations{position:relative;inset:auto;
    display:grid;grid-template-columns:repeat(auto-fit,minmax(84px,1fr));gap:8px}
  .gx-metro-route .gxh-metro-station,
  .gx-metro-route .gxh-metro-station:nth-child(1),
  .gx-metro-route .gxh-metro-station:nth-child(2),
  .gx-metro-route .gxh-metro-station:nth-child(3),
  .gx-metro-route .gxh-metro-station:nth-child(4),
  .gx-metro-route .gxh-metro-station:nth-child(5),
  .gx-metro-route .gxh-metro-station:nth-child(6),
  .gx-metro-route .gxh-metro-station:nth-child(7){position:static;left:auto;top:auto;
    transform:none;scale:1;min-width:0;padding:9px 6px}
  .gx-metro-route .gxh-metro-station.active{scale:1;box-shadow:none}
}

/* ---- static collapse: reduced motion — same resting composition ---- */
@media (prefers-reduced-motion:reduce){
  .gx-metro-route{min-height:auto;width:100%;margin-left:0}
  .gx-metro-route .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;overflow:visible}
  .gx-metro-route .gx-stage{display:block;min-height:auto;padding:0}
  .gx-metro-route .gxh-visual{order:2;height:auto;min-height:0;overflow:visible}
  .gx-metro-route .gxh-metro{position:relative;inset:auto;display:grid;gap:14px}
  .gx-metro-route .gxh-metro::before{display:none}
  .gx-metro-route .gxh-metro svg{position:relative;inset:auto;width:100%;height:104px}
  .gx-metro-route .gxh-metro-line-base{stroke-width:16}
  .gx-metro-route .gxh-metro-line-active{stroke-width:9;stroke-dasharray:none;stroke-dashoffset:0}
  .gx-metro-route .gxh-route-dot{display:none}
  .gx-metro-route .gxh-metro-stations{position:relative;inset:auto;
    display:grid;grid-template-columns:repeat(auto-fit,minmax(84px,1fr));gap:8px}
  .gx-metro-route .gxh-metro-station,
  .gx-metro-route .gxh-metro-station:nth-child(1),
  .gx-metro-route .gxh-metro-station:nth-child(2),
  .gx-metro-route .gxh-metro-station:nth-child(3),
  .gx-metro-route .gxh-metro-station:nth-child(4),
  .gx-metro-route .gxh-metro-station:nth-child(5),
  .gx-metro-route .gxh-metro-station:nth-child(6),
  .gx-metro-route .gxh-metro-station:nth-child(7){position:static;left:auto;top:auto;
    transform:none;scale:1;min-width:0;padding:9px 6px;transition:none}
  .gx-metro-route .gxh-metro-station.active{scale:1;box-shadow:none}
}

/* ---- skin: meridian-dial (hours-v5 "meridian dial" / stage handler
   latitude-rings) — HOURS family, gxh- namespace. A RADIAL week: the seven
   days sit on the outer ring, a sweep hand rotates to the active one and the
   core prints that day's real spec hours. Everything the shell already owns
   (.gxh-layout / .gxh-info / .gxh-status / .gxh-address / .gxh-hours-card /
   .gxh-hours-list / .gxh-visual) is styled globally with temporal — this
   block styles ONLY the scene box and the dial inside .gxh-visual, which is
   the layout's RIGHT column, so the info column is never overlapped.

   Donor re-derivation: the kit's 6-colour neon :root is gone — every value
   here comes from --c-accent / --c-ink / --c-field / --c-glow2 /
   --c-on-accent / --gx-line / --gx-muted through color-mix(). The donor's
   filled core disc (var(--gxh-c28), an opaque slab over the page) is a
   translucent token radial with a ring border instead (GL-field transparency
   decree + the box checklist), and its fixed 580/430/270px ring caps became
   one viewport-relative --gxh-d so the dial never reads as a thin strip on a
   tall monitor.

   MOTION: the animator is a one-line delegation to hoursWalk. The step is
   CSS — hoursWalk publishes --gxh-a (active-day angle) on the SCENE element
   only when the index changes, and the hand + the active-sector wedge each
   rotate to it under a transition. The two continuous elements (topographic
   drift, dashed ring counter-rotation) read --scene-progress, which the frame
   loop already writes on the scene element, so this skin adds ZERO per-frame
   custom-property writes.

   Two distinct day treatments, same as temporal: .active is the scroll
   tour's walking emphasis, .gxh-today is the venue clock's real day. ---- */
.gx-meridian-dial{min-height:min(205svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-meridian-dial .gx-sticky{padding:0;
  background:radial-gradient(circle at 74% 46%,color-mix(in srgb,var(--c-accent) 7%,transparent),transparent 30rem)}
.gx-meridian-dial .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}

/* one working diameter drives every ring, the hand length, the core and the
   day radius — viewport-relative so the dial scales instead of stretching */
.gx-meridian-dial .gxh-meridian-field{position:absolute;inset:0;overflow:hidden;
  --gxh-d:clamp(258px,min(50vh,42vw),540px)}
/* active-day sector: a masked conic wedge that ROTATES to --gxh-a (transform
   is animatable — a transitioned gradient would snap). Masked hollow in the
   middle so it never sits behind the core readout's copy. */
.gx-meridian-dial .gxh-meridian-field::after{content:"";position:absolute;z-index:0;
  left:50%;top:50%;width:var(--gxh-d);height:var(--gxh-d);border-radius:50%;
  pointer-events:none;
  background:conic-gradient(from calc(-.5 * var(--gxh-step,51.43deg)),
    color-mix(in srgb,var(--c-accent) 26%,transparent),
    color-mix(in srgb,var(--c-accent) 5%,transparent) var(--gxh-step,51.43deg),
    transparent var(--gxh-step,51.43deg));
  -webkit-mask:radial-gradient(circle closest-side,transparent 46%,var(--c-ink) 62%,
    color-mix(in srgb,var(--c-ink) 25%,transparent) 88%,transparent 100%);
  mask:radial-gradient(circle closest-side,transparent 46%,var(--c-ink) 62%,
    color-mix(in srgb,var(--c-ink) 25%,transparent) 88%,transparent 100%);
  transform:translate(-50%,-50%) rotate(var(--gxh-a,0deg));
  transition:transform .55s cubic-bezier(.22,1,.36,1);will-change:transform}
/* contour texture — continuous drift straight off --scene-progress */
.gx-meridian-dial .gxh-topographic-lines{position:absolute;z-index:0;inset:6%;opacity:.42;
  pointer-events:none;
  background:repeating-radial-gradient(ellipse at 52% 48%,transparent 0 24px,
    color-mix(in srgb,var(--c-ink) 9%,transparent) 25px 26px);
  transform:rotate(calc(-8deg + var(--scene-progress,0) * 12deg)) scale(1.18);
  will-change:transform}
.gx-meridian-dial .gxh-meridian-ring{position:absolute;z-index:1;left:50%;top:50%;
  aspect-ratio:1;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  transform:translate(-50%,-50%)}
.gx-meridian-dial .gxh-ring-a{width:var(--gxh-d);
  border-color:color-mix(in srgb,var(--c-accent) 28%,transparent)}
.gx-meridian-dial .gxh-ring-b{width:calc(var(--gxh-d) * .72);border-style:dashed;
  border-color:color-mix(in srgb,var(--c-ink) 18%,transparent);
  transform:translate(-50%,-50%) rotate(calc(var(--scene-progress,0) * 110deg));
  will-change:transform}
.gx-meridian-dial .gxh-ring-c{width:calc(var(--gxh-d) * .46);
  border-color:color-mix(in srgb,var(--c-glow2) 26%,transparent)}
.gx-meridian-dial .gxh-meridian-axis{position:absolute;z-index:1;left:50%;top:50%;
  background:color-mix(in srgb,var(--c-ink) 10%,transparent);
  transform:translate(-50%,-50%)}
.gx-meridian-dial .gxh-axis-x{width:calc(var(--gxh-d) * 1.14);height:1px}
.gx-meridian-dial .gxh-axis-y{height:calc(var(--gxh-d) * 1.14);width:1px}
/* the hand: pivot is its own bottom-centre, translated onto the dial centre */
.gx-meridian-dial .gxh-meridian-sweep{position:absolute;z-index:2;left:50%;top:50%;
  width:2px;height:calc(var(--gxh-d) * .5);transform-origin:50% 100%;
  background:linear-gradient(180deg,var(--c-accent),
    color-mix(in srgb,var(--c-accent) 10%,transparent));
  transform:translate(-50%,-100%) rotate(var(--gxh-a,0deg));
  transition:transform .55s cubic-bezier(.22,1,.36,1);will-change:transform}
.gx-meridian-dial .gxh-meridian-sweep::after{content:"";position:absolute;left:50%;top:-5px;
  width:10px;height:10px;border-radius:50%;transform:translateX(-50%);
  background:var(--c-accent);
  box-shadow:0 0 18px color-mix(in srgb,var(--c-accent) 62%,transparent)}
/* core readout — prints the spec row (hoursWalk writes [data-gxh-out]) */
.gx-meridian-dial .gxh-meridian-core{position:absolute;z-index:3;left:50%;top:50%;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:5px;
  width:clamp(170px,calc(var(--gxh-d) * .46),252px);aspect-ratio:1;padding:16px;
  border-radius:50%;text-align:center;
  border:1px solid color-mix(in srgb,var(--c-accent) 30%,transparent);
  background:radial-gradient(circle at 50% 40%,color-mix(in srgb,var(--c-accent) 12%,transparent),
    color-mix(in srgb,var(--c-ink) 4%,transparent) 72%);
  transform:translate(-50%,-50%)}
.gx-meridian-dial .gxh-meridian-core > *{max-width:100%}
.gx-meridian-dial .gxh-core-day{color:var(--c-accent);font-family:var(--f-utility);
  font-size:.66rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gx-meridian-dial .gxh-core-time{color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(1rem,1.7vw,1.5rem);
  letter-spacing:-.02em;line-height:1.08}
.gx-meridian-dial .gxh-meridian-core small{color:var(--gx-muted);font-size:.68rem;line-height:1.35}
/* the week seated on the outer ring — each chip carries its own --gxh-a and
   counter-rotates so the label stays upright at any day count */
.gx-meridian-dial .gxh-radial-days{position:absolute;z-index:4;inset:0;pointer-events:none}
.gx-meridian-dial .gxh-radial-day{position:absolute;left:50%;top:50%;padding:6px 10px;
  border-radius:9px;border:1px solid var(--gx-line);
  background:color-mix(in srgb,var(--c-field) 82%,transparent);
  color:color-mix(in srgb,var(--c-ink) 82%,transparent);
  font-family:var(--f-utility);font-size:.64rem;font-weight:800;letter-spacing:.12em;
  text-transform:uppercase;white-space:nowrap;
  transform:translate(-50%,-50%) rotate(var(--gxh-a)) translateY(calc(var(--gxh-d) * -.5))
    rotate(calc(-1 * var(--gxh-a)));
  transition:background .3s ease,color .3s ease,border-color .3s ease,box-shadow .3s ease}
.gx-meridian-dial .gxh-radial-day span{display:block}
/* .gxh-today = the venue clock's real day (outline) */
.gx-meridian-dial .gxh-radial-day.gxh-today{
  border-color:color-mix(in srgb,var(--c-accent) 62%,transparent);
  color:var(--c-ink)}
/* .active = the scroll tour's walking emphasis (fill) */
.gx-meridian-dial .gxh-radial-day.active{background:var(--c-accent);color:var(--c-on-accent);
  border-color:var(--c-accent);
  box-shadow:0 0 22px color-mix(in srgb,var(--c-accent) 42%,transparent);
  transform:translate(-50%,-50%) rotate(var(--gxh-a)) translateY(calc(var(--gxh-d) * -.5))
    rotate(calc(-1 * var(--gxh-a))) scale(1.14)}
/* short-desktop compression (temporal precedent): at 100svh the info column
   clips on ~900px-tall desktops; the dial follows the shrunken visual */
@media (min-width:901px) and (max-height:980px){
  .gx-meridian-dial .gxh-meridian-field{--gxh-d:clamp(240px,min(46vh,40vw),470px)}
  .gx-meridian-dial .gxh-meridian-core{padding:13px}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-meridian-dial .gxh-meridian-field{--gxh-d:clamp(238px,min(46vh,38vw),430px)}
}
/* ---- static collapse <=900px: unpinned, full-width, dial at rest.
   .gxh-layout / .gxh-info / .gxh-visual and .gx-sticky are already collapsed
   by the shared hours + scene rules; this restates only what the dial needs.
   The hand and the wedge sit at 0deg (Monday, straight up) which is exactly
   the day the core is server-rendered with, and the first chip carries the
   resting highlight — so the resting frame is a complete, readable dial. ---- */
@media (max-width:900px){
  .gx-meridian-dial{min-height:auto;width:100%;margin-left:0}
  .gx-meridian-dial .gx-sticky{padding:26px 0 36px;background:none}
  .gx-meridian-dial .gx-stage{display:block;min-height:auto;padding:0}
  .gx-meridian-dial .gxh-meridian-field{--gxh-d:min(330px,72vw)}
  .gx-meridian-dial .gxh-topographic-lines{transform:rotate(-8deg) scale(1.12)}
  .gx-meridian-dial .gxh-ring-b{transform:translate(-50%,-50%)}
  .gx-meridian-dial .gxh-meridian-field::after{transform:translate(-50%,-50%) rotate(0deg)}
  .gx-meridian-dial .gxh-meridian-sweep{transform:translate(-50%,-100%) rotate(0deg)}
  .gx-meridian-dial .gxh-meridian-core{width:auto;max-width:min(280px,72%);aspect-ratio:auto;
    border-radius:14px;padding:12px 15px}
  .gx-meridian-dial .gxh-core-time{font-size:1.15rem}
  .gx-meridian-dial .gxh-radial-day{font-size:.6rem;padding:5px 8px}
  .gx-meridian-dial .gxh-radial-day:first-child{background:var(--c-accent);
    color:var(--c-on-accent);border-color:var(--c-accent)}
}
/* ---- reduced-motion still state: same resting dial, no drift, no swing ---- */
@media (prefers-reduced-motion:reduce){
  .gx-meridian-dial{min-height:auto;width:100%;margin-left:0}
  .gx-meridian-dial .gx-sticky{padding:26px 0 36px;background:none}
  .gx-meridian-dial .gx-stage{display:block;min-height:auto;padding:0}
  .gx-meridian-dial .gxh-meridian-field{--gxh-d:min(330px,72vw)}
  .gx-meridian-dial .gxh-topographic-lines{transform:rotate(-8deg) scale(1.12)}
  .gx-meridian-dial .gxh-ring-b{transform:translate(-50%,-50%)}
  .gx-meridian-dial .gxh-meridian-field::after{transform:translate(-50%,-50%) rotate(0deg);
    transition:none}
  .gx-meridian-dial .gxh-meridian-sweep{transform:translate(-50%,-100%) rotate(0deg);
    transition:none}
  .gx-meridian-dial .gxh-meridian-core{width:auto;max-width:min(280px,72%);aspect-ratio:auto;
    border-radius:14px;padding:12px 15px}
  .gx-meridian-dial .gxh-core-time{font-size:1.15rem}
  .gx-meridian-dial .gxh-radial-day{font-size:.6rem;padding:5px 8px;transition:none}
  .gx-meridian-dial .gxh-radial-day:first-child{background:var(--c-accent);
    color:var(--c-on-accent);border-color:var(--c-accent)}
}

/* ---- skin: flip-clock (hours-v5 location-hours-flip-clock) — HOURS family,
   gxh- namespace. A split-flap departure pair: Opens / Closes for the day the
   scroll walk has selected. Donor's opaque near-black board + 6-colour neon root
   fully re-derived to host tokens (GL-field transparency decree: the stage and
   sticky paint nothing but a <=15% accent radial, and the flap plates are
   ink-mixed translucency, never a slab).

   PORT NOTES
   - The donor animated four layers (static-top/static-bottom/top-flap/
     bottom-flap) through a rotateX hinge driven by setSplit() JS. The engine
     renderer emits only the TWO static halves, and both ALWAYS carry text —
     no half is ever rendered empty (the blank-flap sampling class). So the
     change is expressed as a slide+fade of the half's text, clipped by the
     half's own overflow, which reads as a flap without ever emptying one.
   - The animation is restarted by the alternating .gxh-flip-a / .gxh-flip-b
     classes hoursWalk() toggles on [data-gxh-flip]; two IDENTICAL keyframe
     rules (gxhFlapA / gxhFlapB) let the restart happen on a class swap with no
     forced reflow. Direction per half comes from a STATIC custom property
     (--gxh-dir), not from JS.
   - Text opacity never falls below .55 in the keyframes: a sampled frame mid
     flip must still read, which is the whole point of the blank-flap rule.
   - Continuous motion (the board's travelling light) reads --scene-progress,
     already written on the scene element by the frame loop — zero extra
     writes.
   - .gxh-flip-rail is the per-day tick strip: .active is the scroll walk's
     day, .gx-flip-clock .gxh-today is the venue clock's day (two distinct treatments, the
     temporal precedent). It is the scene's ONE index readout — no numerals
     anywhere else in this visual (de-AI, one numbering format per element). */
.gx-flip-clock{min-height:min(210svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-flip-clock .gx-sticky{padding:0;
  background:radial-gradient(circle at 74% 26%,color-mix(in srgb,var(--c-accent) 7%,transparent),transparent 27rem)}
.gx-flip-clock .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}
/* travelling board light — the scene's only continuous motion, driven by the
   frame loop's --scene-progress (inherited from .gx-scene), 13% accent */
.gx-flip-clock .gxh-visual::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(circle at calc(10% + var(--scene-progress,0) * 80%) 48%,
    color-mix(in srgb,var(--c-accent) 13%,transparent),transparent 44%)}
.gx-flip-clock .gxh-flipboard{position:absolute;inset:0;z-index:2;display:grid;
  grid-template-rows:auto 1fr auto;gap:clamp(12px,2.2vh,24px);
  padding:clamp(16px,3vh,32px) clamp(8px,1.8vw,26px)}
.gx-flip-clock .gxh-flip-dayline{display:flex;justify-content:space-between;align-items:baseline;gap:16px;
  padding-bottom:clamp(8px,1.4vh,14px);border-bottom:1px solid var(--gx-line)}
.gx-flip-clock .gxh-flip-dayline small{color:var(--gx-muted);font-family:var(--f-utility);font-size:.68rem;
  font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gx-flip-clock .gxh-flip-day{color:var(--c-ink);font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.1rem,2vw,1.7rem);letter-spacing:-.02em;line-height:1}
.gx-flip-clock .gxh-split-grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(10px,1.8vw,22px);align-items:center}
.gx-flip-clock .gxh-split-display{position:relative;min-width:0}
.gx-flip-clock .gxh-split-label{display:block;margin-bottom:9px;color:var(--gx-muted);font-family:var(--f-utility);
  font-size:.7rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gx-flip-clock .gxh-split-value{position:relative;height:clamp(140px,29vh,250px);overflow:hidden;
  border:1px solid var(--gx-line);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 7%,transparent),
    color-mix(in srgb,var(--c-ink) 3%,transparent));
  box-shadow:0 26px 70px -34px color-mix(in srgb,var(--c-dominant) 78%,transparent),
    inset 0 0 0 1px color-mix(in srgb,var(--c-accent) 9%,transparent)}
/* the split hinge — a rule, not a filled bar */
.gx-flip-clock .gxh-split-value::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;z-index:4;
  background:color-mix(in srgb,var(--c-ink) 26%,transparent)}
.gx-flip-clock .gxh-split-half{position:absolute;left:0;right:0;height:50%;overflow:hidden}
.gx-flip-clock .gxh-split-half span{position:absolute;left:0;right:0;height:200%;display:grid;place-items:center;
  padding-inline:6px;text-align:center;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.5rem,3.4vw,3.2rem);letter-spacing:-.05em;line-height:1}
.gx-flip-clock .gxh-static-top{top:0;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 12%,transparent),
    color-mix(in srgb,var(--c-ink) 5%,transparent));
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent)}
.gx-flip-clock .gxh-static-top span{top:0;--gxh-dir:-1}
.gx-flip-clock .gxh-static-bottom{bottom:0;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 4%,transparent),
    color-mix(in srgb,var(--c-ink) 9%,transparent))}
.gx-flip-clock .gxh-static-bottom span{bottom:0;--gxh-dir:1;color:color-mix(in srgb,var(--c-ink) 92%,transparent)}
/* two IDENTICAL keyframe rules: hoursWalk alternates the class, so the
   animation restarts on a day change without a forced reflow */
.gx-flip-clock .gxh-split-value.gxh-flip-a .gxh-split-half span{animation:gxhFlapA .4s cubic-bezier(.22,1,.36,1)}
.gx-flip-clock .gxh-split-value.gxh-flip-b .gxh-split-half span{animation:gxhFlapB .4s cubic-bezier(.22,1,.36,1)}
.gx-flip-clock .gxh-split-value.gxh-flip-a,.gx-flip-clock .gxh-split-value.gxh-flip-b{
  border-color:color-mix(in srgb,var(--c-accent) 30%,var(--gx-line))}
@keyframes gxhFlapA{
  0%{opacity:.55;transform:translateY(calc(var(--gxh-dir,-1) * 24%))}
  46%{opacity:1}
  100%{opacity:1;transform:translateY(0)}
}
@keyframes gxhFlapB{
  0%{opacity:.55;transform:translateY(calc(var(--gxh-dir,-1) * 24%))}
  46%{opacity:1}
  100%{opacity:1;transform:translateY(0)}
}
/* per-day tick strip — the scene's ONE index readout */
.gx-flip-clock .gxh-flip-rail{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;gap:6px;align-items:center;
  padding-top:clamp(6px,1.2vh,12px);border-top:1px solid var(--gx-line)}
.gx-flip-clock .gxh-flip-rail i{display:block;height:3px;
  background:color-mix(in srgb,var(--c-ink) 18%,transparent);
  transition:height .28s ease,background .28s ease,box-shadow .28s ease}
.gx-flip-clock .gxh-flip-rail i.gxh-today{background:color-mix(in srgb,var(--c-glow3) 62%,transparent)}
.gx-flip-clock .gxh-flip-rail i.active,.gx-flip-clock .gxh-flip-rail i.gxh-today.active{height:7px;background:var(--c-accent);
  box-shadow:0 0 14px color-mix(in srgb,var(--c-accent) 62%,transparent)}
/* short-desktop compression (temporal precedent: at 100svh the info column
   clips on ~900px-tall desktops, and the board must shrink with it) */
@media (min-width:901px) and (max-height:980px){
  .gx-flip-clock .gxh-flipboard{padding:14px 10px;gap:10px}
  .gx-flip-clock .gxh-split-value{height:clamp(120px,24vh,190px)}
  .gx-flip-clock .gxh-split-half span{font-size:clamp(1.3rem,2.8vw,2.5rem)}
  .gx-flip-clock .gxh-flip-dayline{padding-bottom:8px}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-flip-clock .gxh-split-grid{gap:12px}
  .gx-flip-clock .gxh-split-half span{font-size:clamp(1.3rem,2.9vw,2.6rem)}
}

/* ---- static collapse 1/2: splice into the existing @media (max-width:900px)
   block. Breakout reverted, scene unpinned, board reflows to a readable
   resting composition (both flaps show their real text, rail seated). ---- */
@media (max-width:900px){
  .gx-flip-clock{min-height:auto;width:100%;margin-left:0}
  .gx-flip-clock .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;overflow:visible}
  .gx-flip-clock .gx-stage{display:block;min-height:auto;padding:0}
  .gx-flip-clock .gxh-visual{height:auto;min-height:0;overflow:visible}
  .gx-flip-clock .gxh-visual::before{display:none}
  .gx-flip-clock .gxh-flipboard{position:relative;inset:auto;display:grid;gap:14px;padding:16px 0 0}
  .gx-flip-clock .gxh-flip-dayline{padding-bottom:10px}
  .gx-flip-clock .gxh-split-grid{gap:10px}
  .gx-flip-clock .gxh-split-value{height:150px}
  .gx-flip-clock .gxh-split-half span{font-size:clamp(1.3rem,7vw,2.3rem);
    animation:none!important;opacity:1!important;transform:none!important}
  .gx-flip-clock .gxh-flip-rail i{height:4px}
  .gx-flip-clock .gxh-flip-rail i.active{height:8px}
}

/* ---- static collapse 2/2: splice into the existing
   @media (prefers-reduced-motion:reduce) block. Same resting composition —
   never a blank or dead scene. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-flip-clock{min-height:auto;width:100%;margin-left:0}
  .gx-flip-clock .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 36px;overflow:visible}
  .gx-flip-clock .gx-stage{display:block;min-height:auto;padding:0}
  .gx-flip-clock .gxh-visual{height:auto;min-height:0;overflow:visible}
  .gx-flip-clock .gxh-visual::before{display:none}
  .gx-flip-clock .gxh-flipboard{position:relative;inset:auto;display:grid;gap:14px;padding:16px 0 0}
  .gx-flip-clock .gxh-flip-dayline{padding-bottom:10px}
  .gx-flip-clock .gxh-split-grid{gap:12px}
  .gx-flip-clock .gxh-split-value{height:160px}
  .gx-flip-clock .gxh-split-half span{font-size:clamp(1.4rem,6vw,2.4rem);
    animation:none!important;opacity:1!important;transform:none!important}
  .gx-flip-clock .gxh-flip-rail i{height:4px}
  .gx-flip-clock .gxh-flip-rail i.active{height:8px}
}

/* ---- skin: orbital-week-ring (hours-v5 location-hours-orbital-week-ring)
   HOURS family, gxh- namespace, shares hoursShell()'s info column with every
   other hours skin (.gxh-layout / .gxh-info / .gxh-status / .gxh-address /
   .gxh-hours-card / .gxh-hours-list / .gxh-visual are styled globally beside
   .gx-temporal — nothing below re-declares them).

   The week is a ring. .gxh-orbit-ring is the track, .gxh-orbit-core the centre
   readout (prints the ACTIVE day's spec row via [data-gxh-out] — never invented
   copy), .gxh-planet the marker that rides the inner track to var(--gxh-a).
   Every .gxh-orbit-day carries its own inline --gxh-a from the renderer and is
   seated with the same rotate / translate / counter-rotate trick, so the labels
   stay upright at every angle.

   Motion: hoursWalk() publishes --gxh-i / --gxh-t / --gxh-a on the SCENE element
   and only when the active day changes; the planet, the spoke and the active
   chip are pure CSS transitions off that one property. The ring's continuous
   sweep reads --scene-progress, which the frame loop already writes on the scene
   element, so this skin costs zero extra per-frame writes.

   Donor chrome dropped by doctrine: the stage's two-stop opaque near-black
   linear wash (GL-field transparency decree — only a <=15% token radial
   survives), the
   .gxh-visual border-radius+clip box, the fixed 560px/620px px caps (viewport
   clamps instead) and the donor's raw violet planet/glow hexes (token-derived
   accent/glow2 now). ---- */
.gx-orbital-week-ring{min-height:min(200svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-orbital-week-ring .gx-sticky{padding:0;
  background:radial-gradient(circle at 74% 46%,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 32rem)}
.gx-orbital-week-ring .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}

/* the orbit field. --gxh-d is the track diameter, --gxh-r its radius: both
   viewport-relative so the ring never reads as a thin strip on a tall monitor */
.gx-orbital-week-ring .gxh-orbit-wrap{--gxh-d:clamp(240px,min(46vh,32vw),520px);
  --gxh-r:calc(var(--gxh-d) / 2);
  position:absolute;inset:0;display:grid;place-items:center}
/* centre glow — token radial, well under the 15% ceiling */
.gx-orbital-week-ring .gxh-orbit-wrap::before{content:"";position:absolute;left:50%;top:50%;
  width:calc(var(--gxh-d) * 1.25);height:calc(var(--gxh-d) * 1.25);border-radius:50%;
  transform:translate(-50%,-50%);pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-glow2) 13%,transparent),transparent 66%)}
/* spoke from the core out to the active day — rides --gxh-a */
.gx-orbital-week-ring .gxh-orbit-wrap::after{content:"";position:absolute;left:50%;top:50%;z-index:1;
  width:1px;height:var(--gxh-r);pointer-events:none;transform-origin:50% 100%;
  transform:translate(-50%,-100%) rotate(var(--gxh-a,0deg));
  background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--c-accent) 62%,transparent));
  transition:transform .55s cubic-bezier(.22,.61,.36,1)}

.gx-orbital-week-ring .gxh-orbit-ring{position:relative;width:var(--gxh-d);height:var(--gxh-d);
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);border-radius:50%}
.gx-orbital-week-ring .gxh-orbit-ring::before{content:"";position:absolute;inset:14%;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent)}
/* the continuous sweep: a bright arc of the track rotating on --scene-progress */
.gx-orbital-week-ring .gxh-orbit-ring::after{content:"";position:absolute;inset:-1px;border-radius:50%;
  border:1px solid transparent;
  border-top-color:color-mix(in srgb,var(--c-accent) 58%,transparent);
  border-right-color:color-mix(in srgb,var(--c-accent) 20%,transparent);
  transform:rotate(calc(var(--scene-progress,0) * 360deg))}

/* seven day ticks on the track — --gxh-step is the renderer's 360deg/n */
.gx-orbital-week-ring .gxh-week-orbit{position:absolute;inset:0;z-index:2}
.gx-orbital-week-ring .gxh-week-orbit::before{content:"";position:absolute;left:50%;top:50%;
  width:var(--gxh-d);height:var(--gxh-d);border-radius:50%;transform:translate(-50%,-50%);pointer-events:none;
  background:repeating-conic-gradient(from calc(var(--gxh-step,51.429deg) * -.5),
    color-mix(in srgb,var(--c-ink) 26%,transparent) 0deg .4deg,
    transparent .4deg var(--gxh-step,51.429deg));
  -webkit-mask-image:radial-gradient(circle,transparent calc(50% - 9px),var(--c-ink) calc(50% - 9px),var(--c-ink) 50%,transparent 50%);
  mask-image:radial-gradient(circle,transparent calc(50% - 9px),var(--c-ink) calc(50% - 9px),var(--c-ink) 50%,transparent 50%)}

/* a day chip: rotate to its own angle, push out to the track, counter-rotate
   so the label stays upright */
.gx-orbital-week-ring .gxh-orbit-day{position:absolute;left:50%;top:50%;
  display:grid;justify-items:center;gap:1px;padding:6px 11px;white-space:nowrap;
  border:1px solid color-mix(in srgb,var(--c-ink) 13%,transparent);border-radius:999px;
  background:color-mix(in srgb,var(--c-ink) 6%,transparent);
  font-family:var(--f-utility);font-size:.68rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  color:color-mix(in srgb,var(--c-ink) 88%,transparent);
  transform:translate(-50%,-50%) rotate(var(--gxh-a,0deg)) translateY(calc(var(--gxh-r) * -1)) rotate(calc(var(--gxh-a,0deg) * -1));
  transition:color .3s ease,background-color .3s ease,border-color .3s ease}
.gx-orbital-week-ring .gxh-orbit-day b{font-size:.62rem;font-weight:700;letter-spacing:.03em;text-transform:none;
  color:color-mix(in srgb,var(--c-ink) 70%,transparent)}
.gx-orbital-week-ring .gxh-orbit-day.active{background:var(--c-accent);border-color:var(--c-accent);
  color:var(--c-on-accent);
  box-shadow:0 0 26px color-mix(in srgb,var(--c-accent) 45%,transparent)}
.gx-orbital-week-ring .gxh-orbit-day.active b{color:color-mix(in srgb,var(--c-on-accent) 84%,transparent)}
/* the venue clock's today marker — a distinct treatment from the walk's .active */
.gx-orbital-week-ring .gxh-orbit-day.gxh-today{border-color:color-mix(in srgb,var(--c-accent) 62%,transparent)}

/* the marker: rides the INNER track so it never sits under a day chip */
.gx-orbital-week-ring .gxh-planet{position:absolute;left:50%;top:50%;z-index:3;
  width:16px;height:16px;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle at 34% 32%,color-mix(in srgb,var(--c-ink) 92%,transparent),
    var(--c-accent) 46%,color-mix(in srgb,var(--c-glow2) 88%,transparent));
  box-shadow:0 0 24px color-mix(in srgb,var(--c-accent) 55%,transparent);
  transform:translate(-50%,-50%) rotate(var(--gxh-a,0deg)) translateY(calc(var(--gxh-r) * -.72));
  transition:transform .55s cubic-bezier(.22,.61,.36,1)}

/* centre readout — prints the ACTIVE day's spec row (hoursWalk fills
   [data-gxh-out]); server-rendered with row 1 so it is never empty */
.gx-orbital-week-ring .gxh-orbit-core{position:absolute;left:50%;top:50%;z-index:4;
  display:grid;justify-items:center;gap:7px;width:min(calc(var(--gxh-d) * .58),220px);
  text-align:center;pointer-events:none;transform:translate(-50%,-50%)}
.gx-orbital-week-ring .gxh-orbit-core span{font-family:var(--f-utility);font-size:.72rem;font-weight:800;
  letter-spacing:.2em;text-transform:uppercase;color:var(--c-accent)}
.gx-orbital-week-ring .gxh-orbit-core strong{font-size:clamp(1.05rem,1.7vw,1.55rem);line-height:1.16;
  letter-spacing:-.015em;color:var(--c-ink)}

/* short-desktop compression (temporal's P8 precedent): at 100svh a ~900px-tall
   desktop has to give the ring back some room */
@media (min-width:901px) and (max-height:980px){
  .gx-orbital-week-ring .gxh-orbit-wrap{--gxh-d:clamp(220px,min(42vh,30vw),420px)}
  .gx-orbital-week-ring .gxh-orbit-core strong{font-size:1.05rem}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-orbital-week-ring .gxh-orbit-wrap{--gxh-d:clamp(210px,min(40vh,34vw),400px)}
  .gx-orbital-week-ring .gxh-orbit-day{padding:5px 9px;font-size:.62rem}
}

/* ---- static collapse: <=900px ----
   Breakout reverted, scene unpinned, ring kept but small and still — the whole
   week reads at rest (seven chips + the row-1 core readout), never a dead box. */
@media (max-width:900px){
  .gx-orbital-week-ring{min-height:auto;width:100%;margin-left:0}
  .gx-orbital-week-ring .gx-sticky{padding:26px 0 36px}
  .gx-orbital-week-ring .gx-stage{display:block;min-height:auto;padding:0}
  .gx-orbital-week-ring .gxh-orbit-wrap{--gxh-d:clamp(190px,min(62vw,300px),320px)}
  .gx-orbital-week-ring .gxh-orbit-wrap::after{transition:none}
  .gx-orbital-week-ring .gxh-orbit-ring::after{transform:none}
  .gx-orbital-week-ring .gxh-orbit-day{padding:4px 8px;font-size:.56rem;letter-spacing:.08em;transition:none}
  .gx-orbital-week-ring .gxh-orbit-day b{font-size:.54rem}
  .gx-orbital-week-ring .gxh-planet{width:13px;height:13px;transition:none}
  .gx-orbital-week-ring .gxh-orbit-core{width:min(calc(var(--gxh-d) * .62),180px);gap:5px}
  .gx-orbital-week-ring .gxh-orbit-core span{font-size:.62rem;letter-spacing:.16em}
  .gx-orbital-week-ring .gxh-orbit-core strong{font-size:1rem}
}
/* ---- static collapse: reduced motion ---- */
@media (prefers-reduced-motion:reduce){
  .gx-orbital-week-ring{min-height:auto;width:100%;margin-left:0}
  .gx-orbital-week-ring .gx-sticky{padding:26px 0 36px}
  .gx-orbital-week-ring .gx-stage{display:block;min-height:auto;padding:0}
  /* the collapsed .gxh-visual is a 420px band — keep the track inside it so no
     chip is clipped (half a chip rides outside the ring by design) */
  .gx-orbital-week-ring .gxh-orbit-wrap{--gxh-d:clamp(200px,min(52vw,44vh),330px)}
  .gx-orbital-week-ring .gxh-orbit-wrap::after{transition:none}
  .gx-orbital-week-ring .gxh-orbit-ring::after{transform:none;
    border-right-color:color-mix(in srgb,var(--c-accent) 20%,transparent)}
  .gx-orbital-week-ring .gxh-orbit-day{padding:5px 9px;font-size:.6rem;transition:none}
  .gx-orbital-week-ring .gxh-planet{transition:none}
  .gx-orbital-week-ring .gxh-orbit-core strong{font-size:1.05rem}
  .gx-orbital-week-ring .gxh-hours-list li.active{transform:none}
}

/* ---- skin: foldout-atlas (hours-v5 #10 "foldout atlas") — HOURS family,
   gxh- namespace. A folded map sheet that opens as the scene advances: the two
   flaps hinge on the sheet's OUTER edges and their rotateY rides
   --scene-progress directly. The frame loop already writes --scene-progress on
   the scene element, so the whole unfold, the pin bloom and the meridian line
   cost ZERO extra per-frame writes — the animator is a plain hoursWalk
   delegation and everything continuous lives here.
   Port decisions (donor -> engine):
   - donor stage wash (the three near-black linear-gradient stops) DROPPED; the
     sticky keeps only a <=15% token radial (GL-field transparency decree).
   - the three .gxh-panel slabs lost their filled near-black boards and are
     translucent ink washes with hairline edges (box checklist).
   - donor's fixed 900x620px sheet cap became viewport-relative (min()/%), so it
     is not a thin strip on a 1600x1200 monitor.
   - donor's 6-colour neon :root re-derived: accent for the pins/active day,
     glow2/glow3 for the map blooms, ink mixes for paper and creases.
   - .gxh-atlas-rows and .gxh-atlas-plate print SPEC copy only (hoursShort() /
     s.body); a day with no parsable hours prints an em-dash, never a fake
     "Closed" (audit rule 13b).
   - .active is the scroll tour's walking emphasis, .gxh-today is the venue
     clock's day — two treatments, one clock source (engine.js).
   The visual lives in .gxh-visual (the layout's RIGHT cell) and that cell is
   overflow:hidden, so an opened flap can never reach the info column. ---- */
.gx-foldout-atlas{
  /* the single continuous driver: 0 until 6% of the scene, fully open by 52% */
  --gxh-open:clamp(0,calc((var(--scene-progress,0) - .06) / .46),1);
  --gxh-lift:calc((var(--scene-progress,0) - .5) * -14px);
  min-height:min(206svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-foldout-atlas .gx-sticky{padding:0;
  background:radial-gradient(circle at 72% 32%,color-mix(in srgb,var(--c-glow2) 9%,transparent),transparent 30rem)}
.gx-foldout-atlas .gx-stage{display:grid;align-items:center;min-height:100svh;perspective:none}
.gx-foldout-atlas .gxh-visual{perspective:1600px;perspective-origin:50% 42%}
.gx-foldout-atlas .gxh-atlas{position:absolute;inset:0;transform-style:preserve-3d}
.gx-foldout-atlas .gxh-sheet{position:absolute;left:50%;top:4%;
  width:min(580px,80%);height:min(48%,420px);
  transform:translate(-50%,var(--gxh-lift));transform-style:preserve-3d}

/* the sheet itself — paper, not a slab: translucent ink wash + hairline edge */
.gx-foldout-atlas .gxh-panel{position:absolute;inset:0;
  border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  background:linear-gradient(168deg,color-mix(in srgb,var(--c-ink) 7%,transparent),
    color-mix(in srgb,var(--c-ink) 3%,transparent));
  box-shadow:0 30px 78px -34px rgba(0,0,0,.55)}
/* the printed map: survey grid + three token blooms under the authored pins */
.gx-foldout-atlas .gxh-panel.gxh-grid::before{content:"";position:absolute;inset:18px;
  background:
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 9%,transparent) 1px,transparent 1px) 0 0/48px 48px,
    linear-gradient(color-mix(in srgb,var(--c-ink) 9%,transparent) 1px,transparent 1px) 0 0/48px 48px,
    radial-gradient(circle at 24% 30%,color-mix(in srgb,var(--c-glow2) 13%,transparent),transparent 14%),
    radial-gradient(circle at 62% 54%,color-mix(in srgb,var(--c-accent) 14%,transparent),transparent 15%),
    radial-gradient(circle at 44% 72%,color-mix(in srgb,var(--c-glow3) 12%,transparent),transparent 14%)}
/* meridian line — steps to the active day off --gxh-t (hoursWalk writes it on
   the scene element only when the day changes), fades in with the unfold */
.gx-foldout-atlas .gxh-panel.gxh-grid::after{content:"";position:absolute;top:18px;bottom:18px;
  left:calc(18px + var(--gxh-t,0) * (100% - 36px));width:1px;opacity:var(--gxh-open);
  background:linear-gradient(180deg,transparent,color-mix(in srgb,var(--c-accent) 60%,transparent),transparent);
  transition:left .45s cubic-bezier(.22,.7,.24,1)}

/* the two flaps: hinged on the outer edges, unfolding on --scene-progress */
.gx-foldout-atlas .gxh-fold-left,
.gx-foldout-atlas .gxh-fold-right{top:0;bottom:0;width:50%;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 9%,transparent),
    color-mix(in srgb,var(--c-ink) 3%,transparent));
  border:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent)}
.gx-foldout-atlas .gxh-fold-left{inset:0 auto 0 0;transform-origin:left center;
  transform:rotateY(calc(var(--gxh-open) * -128deg))}
.gx-foldout-atlas .gxh-fold-right{inset:0 0 0 auto;transform-origin:right center;
  transform:rotateY(calc(var(--gxh-open) * 128deg))}

/* pins at the authored percentages; each blooms in on its own progress window */
.gx-foldout-atlas .gxh-map-pin{position:absolute;z-index:3;width:22px;height:22px;
  margin:-11px 0 0 -11px;border-radius:50% 50% 50% 0;
  --gxh-pin:clamp(0,calc((var(--scene-progress,0) - var(--gxh-pin-at,.3)) / .14),1);
  background:linear-gradient(135deg,var(--c-accent),color-mix(in srgb,var(--c-accent) 45%,var(--c-ink)));
  box-shadow:0 0 26px color-mix(in srgb,var(--c-accent) 34%,transparent);
  opacity:var(--gxh-pin);
  transform:rotate(-45deg) scale(calc(.5 + var(--gxh-pin) * .5))}
.gx-foldout-atlas .gxh-map-pin::after{content:"";position:absolute;inset:6px;border-radius:50%;
  background:color-mix(in srgb,var(--c-dominant) 88%,transparent)}
.gx-foldout-atlas .gxh-map-pin:nth-of-type(1){--gxh-pin-at:.28}
.gx-foldout-atlas .gxh-map-pin:nth-of-type(2){--gxh-pin-at:.40}
.gx-foldout-atlas .gxh-map-pin:nth-of-type(3){--gxh-pin-at:.52}

/* the week, printed under the sheet — count-general (auto-flow column), always
   legible: it sits ABOVE the flaps in z so no fold state can hide the hours */
.gx-foldout-atlas .gxh-atlas-rows{position:absolute;left:0;right:0;z-index:4;
  top:calc(100% + clamp(14px,2.6vh,30px));
  display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);gap:clamp(4px,.7vw,9px)}
.gx-foldout-atlas .gxh-atlas-row{display:grid;gap:3px;justify-items:center;padding:9px 4px;
  border:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
  background:color-mix(in srgb,var(--c-ink) 4%,transparent);
  color:color-mix(in srgb,var(--c-ink) 80%,transparent);
  transition:background .3s ease,color .3s ease,border-color .3s ease,transform .35s ease}
.gx-foldout-atlas .gxh-atlas-row span{font-family:var(--f-utility);font-size:.62rem;font-weight:800;
  letter-spacing:.12em;text-transform:uppercase}
.gx-foldout-atlas .gxh-atlas-row b{font-family:var(--f-utility);font-size:.74rem;font-weight:700;
  letter-spacing:-.01em;white-space:nowrap}
.gx-foldout-atlas .gxh-atlas-row.active{color:var(--c-ink);transform:translateY(-4px);
  border-color:color-mix(in srgb,var(--c-accent) 52%,transparent);
  background:color-mix(in srgb,var(--c-accent) 16%,transparent)}
/* the venue clock's day — a distinct treatment from the tour's .active */
.gx-foldout-atlas .gxh-atlas-row.gxh-today{color:var(--c-ink);
  border-bottom:2px solid var(--c-accent)}

/* the address plate: the pins' anchor, not decoration. The day label is a
   [data-gxh-out] readout, so it prints the active spec row verbatim. */
.gx-foldout-atlas .gxh-atlas-plate{position:absolute;left:0;right:0;bottom:0;z-index:4;
  display:grid;gap:5px;padding:12px 16px;
  border-left:2px solid var(--c-accent);
  background:color-mix(in srgb,var(--c-ink) 5%,transparent)}
.gx-foldout-atlas .gxh-atlas-plate strong{color:var(--c-accent);font-family:var(--f-utility);
  font-size:.68rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gx-foldout-atlas .gxh-atlas-plate span{color:color-mix(in srgb,var(--c-ink) 86%,transparent);
  font-size:.9rem;line-height:1.5}

/* short-desktop compression (P8 ledger precedent): at 100svh the info column
   already tightens globally — the sheet has to give room back too */
@media (min-width:901px) and (max-height:980px){
  .gx-foldout-atlas .gxh-sheet{top:2%;height:min(45%,350px)}
  .gx-foldout-atlas .gxh-atlas-row{padding:6px 3px}
  .gx-foldout-atlas .gxh-atlas-row b{font-size:.68rem}
  .gx-foldout-atlas .gxh-atlas-plate{padding:9px 13px}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-foldout-atlas .gxh-sheet{width:min(520px,88%)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned, and the
   sheet rests as a READ map — flaps become the tri-fold creases, pins and the
   full week are visible, the plate carries the address. Never blank. ---- */
@media (max-width:900px){
  .gx-foldout-atlas{min-height:auto;width:100%;margin-left:0}
  .gx-foldout-atlas .gx-sticky{position:relative;min-height:auto;padding:26px 0 36px}
  .gx-foldout-atlas .gx-stage{display:block;min-height:auto;padding:0}
  .gx-foldout-atlas .gxh-visual{perspective:none}
  .gx-foldout-atlas .gxh-atlas{transform-style:flat}
  .gx-foldout-atlas .gxh-sheet{top:0;width:min(560px,96%);height:40%;
    transform:translateX(-50%);transform-style:flat}
  .gx-foldout-atlas .gxh-panel.gxh-grid::before{inset:12px;background-size:34px 34px,34px 34px,auto,auto,auto}
  .gx-foldout-atlas .gxh-panel.gxh-grid::after{left:50%;opacity:1}
  .gx-foldout-atlas .gxh-fold-left,
  .gx-foldout-atlas .gxh-fold-right{width:34%;background:none;border:0;
    box-shadow:none;opacity:1;transform:none}
  .gx-foldout-atlas .gxh-fold-left{border-right:1px dashed color-mix(in srgb,var(--c-ink) 20%,transparent)}
  .gx-foldout-atlas .gxh-fold-right{border-left:1px dashed color-mix(in srgb,var(--c-ink) 20%,transparent)}
  .gx-foldout-atlas .gxh-map-pin{opacity:1;transform:rotate(-45deg)}
  .gx-foldout-atlas .gxh-atlas-rows{top:calc(100% + 12px);gap:4px;
    grid-auto-flow:row;grid-template-columns:repeat(4,minmax(0,1fr))}
  .gx-foldout-atlas .gxh-atlas-row{padding:6px 3px;transform:none}
  .gx-foldout-atlas .gxh-atlas-row span{font-size:.56rem}
  .gx-foldout-atlas .gxh-atlas-row b{font-size:.64rem}
  .gx-foldout-atlas .gxh-atlas-plate{padding:9px 12px}
  .gx-foldout-atlas .gxh-atlas-plate span{font-size:.82rem}
}

/* ---- static collapse B: reduced motion. Same resting composition; the fold
   never animates, so nothing depends on --scene-progress here. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-foldout-atlas{min-height:auto;width:100%;margin-left:0}
  .gx-foldout-atlas .gx-sticky{position:relative;min-height:auto;padding:26px 0 36px}
  .gx-foldout-atlas .gx-stage{display:block;min-height:auto;padding:0}
  .gx-foldout-atlas .gxh-visual{perspective:none}
  .gx-foldout-atlas .gxh-atlas{transform-style:flat}
  .gx-foldout-atlas .gxh-sheet{top:0;width:min(560px,96%);height:40%;
    transform:translateX(-50%);transform-style:flat}
  .gx-foldout-atlas .gxh-panel.gxh-grid::before{inset:12px;background-size:34px 34px,34px 34px,auto,auto,auto}
  .gx-foldout-atlas .gxh-panel.gxh-grid::after{left:50%;opacity:1}
  .gx-foldout-atlas .gxh-fold-left,
  .gx-foldout-atlas .gxh-fold-right{width:34%;background:none;border:0;
    box-shadow:none;opacity:1;transform:none}
  .gx-foldout-atlas .gxh-fold-left{border-right:1px dashed color-mix(in srgb,var(--c-ink) 20%,transparent)}
  .gx-foldout-atlas .gxh-fold-right{border-left:1px dashed color-mix(in srgb,var(--c-ink) 20%,transparent)}
  .gx-foldout-atlas .gxh-map-pin{opacity:1;transform:rotate(-45deg)}
  .gx-foldout-atlas .gxh-atlas-rows{top:calc(100% + 12px);gap:4px;
    grid-auto-flow:row;grid-template-columns:repeat(4,minmax(0,1fr))}
  .gx-foldout-atlas .gxh-atlas-row{padding:6px 3px;transform:none}
  .gx-foldout-atlas .gxh-atlas-row span{font-size:.56rem}
  .gx-foldout-atlas .gxh-atlas-row b{font-size:.64rem}
  .gx-foldout-atlas .gxh-atlas-plate{padding:9px 12px}
  .gx-foldout-atlas .gxh-atlas-plate span{font-size:.82rem}
  .gx-foldout-atlas .gxh-atlas-row.active{transform:none}
}

/* ==== PRICING FAMILY SKINS (Library 4, pricing-v5 kits, gxpr- namespace) ====
   Takeover 220svh scenes, GL-transparent stages (token washes <=15% only).
   Shared plan anatomy below; each skin adds its scene visuals. All geometry is
   count-general: --plan-count (renderer), --plan-i (per plan), and the
   animator-written --active-center/--active-width stage percents, MEASURED
   from the real plan boxes (the donor's px syncPlanGeometry kept — the
   width-capped centred grid means lane fractions of the stage miss the outer
   lanes on wide viewports). Prices render verbatim in one block — never split
   into currency/amount/period. */
.gxpr-plans{position:relative;z-index:3;display:grid;
  grid-template-columns:repeat(var(--plan-count,3),minmax(0,1fr));
  width:min(1280px,calc(100vw - clamp(28px,6vw,96px)));margin-inline:auto;
  align-items:stretch}
.gxpr-plan{position:relative;display:flex;flex-direction:column;align-items:flex-start;
  min-height:clamp(430px,56svh,560px);padding:30px clamp(20px,2.8vw,44px) 28px;
  opacity:.9;transform:translateY(8px);
  transition:opacity .45s ease,transform .6s cubic-bezier(.22,.7,.24,1),filter .45s ease}
.gxpr-plan.is-active{opacity:1;transform:translateY(-9px)}
.gxpr-plan::after{position:absolute;right:8%;bottom:0;left:8%;height:2px;content:"";
  background:linear-gradient(90deg,transparent,var(--plan-accent,var(--c-accent)),transparent);
  opacity:0;transform:scaleX(.4);transition:opacity .4s ease,transform .6s cubic-bezier(.22,.7,.24,1)}
.gxpr-plan.is-active::after{opacity:1;transform:scaleX(1)}
.gxpr-name{margin:0;color:var(--c-ink);font-family:var(--f-utility);font-size:.79rem;
  font-weight:800;letter-spacing:.17em;text-transform:uppercase}
.gxpr-price{margin:14px 0 12px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2rem,3.4vw,3.7rem);
  letter-spacing:-.045em;line-height:.92;overflow-wrap:anywhere}
.gxpr-pitch{margin:0 0 16px;color:var(--gx-muted);font-size:.92rem;line-height:1.5}
.gxpr-features{display:grid;gap:9px;margin:0 0 20px;padding:0;list-style:none}
.gxpr-features li{position:relative;padding-left:17px;
  color:color-mix(in srgb,var(--c-ink) 80%,transparent);font-size:.86rem;line-height:1.4}
.gxpr-features li::before{position:absolute;top:.66em;left:0;width:7px;height:1px;content:"";
  background:var(--plan-accent,var(--c-accent));opacity:.75}
.gxpr-cta{margin-top:auto;font-size:.8rem}
.gxpr-pill{display:inline-flex;width:max-content;margin-bottom:12px;padding:5px 10px;
  border:1px solid color-mix(in srgb,var(--c-accent) 55%,transparent);border-radius:999px;
  color:var(--c-accent);font-family:var(--f-utility);font-size:.63rem;font-weight:800;
  letter-spacing:.13em;text-transform:uppercase}
/* ghost price numerals — digits from the REAL plan prices, active one visible */
.gxpr-ghosts{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none}
.gxpr-ghost{position:absolute;bottom:-.12em;left:50%;transform:translateX(-50%);
  color:color-mix(in srgb,var(--c-ink) 7%,transparent);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(10rem,20vw,21rem);letter-spacing:-.1em;
  line-height:.8;white-space:nowrap;opacity:0;transition:opacity .6s ease}
.gxpr-ghost.is-active{opacity:1}

/* ---- skin: aperture (pricing-01) — grid field + concentric lens circle
   gliding to the active lane ---- */
.gx-aperture{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-aperture .gx-stage{display:grid;align-content:center}
.gxpr-ap-field{position:absolute;inset:0;z-index:0;pointer-events:none}
.gxpr-ap-field::before{position:absolute;inset:0;content:"";
  background:
    linear-gradient(color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px);
  background-size:54px 54px;
  mask-image:linear-gradient(transparent,rgba(0,0,0,1) 20%,rgba(0,0,0,1) 82%,transparent);
  -webkit-mask-image:linear-gradient(transparent,rgba(0,0,0,1) 20%,rgba(0,0,0,1) 82%,transparent)}
.gxpr-ap-lens{position:absolute;top:52%;left:var(--active-center,50%);width:min(32vw,470px);
  aspect-ratio:1;border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);border-radius:50%;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-accent) 12%,transparent),
    color-mix(in srgb,var(--c-accent) 3%,transparent) 46%,transparent 70%);
  transform:translate(-50%,-50%);transition:left .72s cubic-bezier(.22,.7,.24,1)}
.gxpr-ap-lens::before,.gxpr-ap-lens::after{position:absolute;inset:12%;content:"";
  border:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent);border-radius:50%}
.gxpr-ap-lens::after{inset:29%;border-color:color-mix(in srgb,var(--c-accent) 22%,transparent)}
.gx-aperture .gxpr-plans{border-top:1px solid var(--gx-line);border-bottom:1px solid var(--gx-line)}
.gx-aperture .gxpr-plan{border-right:1px solid color-mix(in srgb,var(--c-ink) 8%,transparent)}
.gx-aperture .gxpr-plan:last-child{border-right:0}

/* ---- skin: lightwell (pricing-02) — vertical light beam + perspective floor
   grid + floor ring + real-price ghost; "Tier 0N" kickers dropped (de-AI) ---- */
.gx-lightwell{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-lightwell .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-lw-floor{position:absolute;right:-12%;bottom:-14%;left:-12%;height:48%;z-index:0;
  transform:perspective(620px) rotateX(67deg);transform-origin:center bottom;
  background:
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 7%,transparent) 1px,transparent 1px) 0 0/7.142% 100%,
    linear-gradient(color-mix(in srgb,var(--c-ink) 7%,transparent) 1px,transparent 1px) 0 0/100% 18%;
  mask-image:linear-gradient(rgba(0,0,0,1),transparent 86%);
  -webkit-mask-image:linear-gradient(rgba(0,0,0,1),transparent 86%)}
.gxpr-lw-beam{position:absolute;top:-12%;left:var(--active-center,50%);z-index:0;
  width:min(38vw,580px);height:120%;clip-path:polygon(47% 0,53% 0,100% 100%,0 100%);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 14%,transparent),
    color-mix(in srgb,var(--c-accent) 8%,transparent) 43%,
    color-mix(in srgb,var(--c-glow2) 4%,transparent) 76%,transparent);
  transform:translateX(-50%);transition:left .76s cubic-bezier(.22,.7,.24,1)}
.gxpr-lw-ring{position:absolute;bottom:5%;left:var(--active-center,50%);z-index:0;
  width:min(27vw,410px);height:58px;border:1px solid color-mix(in srgb,var(--c-accent) 30%,transparent);
  border-radius:50%;background:radial-gradient(ellipse,color-mix(in srgb,var(--c-accent) 13%,transparent),transparent 64%);
  transform:translateX(-50%);transition:left .76s cubic-bezier(.22,.7,.24,1)}
.gx-lightwell .gxpr-ghost{left:var(--active-center,50%);transition:opacity .6s ease,left .76s cubic-bezier(.22,.7,.24,1)}
.gx-lightwell .gxpr-plans{border-top:1px solid var(--gx-line);border-bottom:1px solid var(--gx-line)}
.gx-lightwell .gxpr-plan{border-right:1px solid color-mix(in srgb,var(--c-ink) 8%,transparent)}
.gx-lightwell .gxpr-plan:last-child{border-right:0}
.gx-lightwell .gxpr-plan:not(.is-active){filter:saturate(.72)}

/* ---- skin: monument (pricing-03) — stepped-skyline horizon DE-FILLED per
   box checklist (translucent token gradient, masked base — mountain
   precedent); plans rise on count-general --plan-i steps; drifting haze ---- */
.gx-monument{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-monument .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-mo-haze{position:absolute;inset:0;z-index:0;
  background:linear-gradient(112deg,transparent 0 38%,color-mix(in srgb,var(--c-accent) 6%,transparent) 50%,transparent 62%);
  transform:translateX(calc((var(--scene-progress,0) - .5) * 30%))}
.gxpr-mo-horizon{position:absolute;right:0;bottom:0;left:0;height:50%;z-index:0;
  clip-path:polygon(0 76%,33.33% 76%,33.33% 52%,66.66% 52%,66.66% 26%,100% 26%,100% 100%,0 100%);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 10%,transparent),
    color-mix(in srgb,var(--c-accent) 4%,transparent) 64%,transparent);
  mask-image:linear-gradient(rgba(0,0,0,1) 40%,transparent 96%);
  -webkit-mask-image:linear-gradient(rgba(0,0,0,1) 40%,transparent 96%)}
.gx-monument .gxpr-plan{padding-top:calc(28px + var(--plan-i,0) * clamp(22px,4.5vh,50px))}
.gx-monument .gxpr-ghost{left:auto;right:.02em;transform:none;
  translate:0 calc((var(--scene-progress,0) - .5) * -30px)}

/* ---- skin: prism (pricing-04) — translucent prism object (donor's filled
   triangle de-filled) + one refraction ray PER PLAN on the token accent
   cycle (donor r1-r3 trio generalized) ---- */
.gx-prism{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-prism .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-pr-sky{position:absolute;inset:0;z-index:0;overflow:hidden;pointer-events:none}
.gxpr-pr-object{position:absolute;top:46%;left:50%;width:min(44vw,640px);height:min(38vw,540px);
  clip-path:polygon(50% 0,100% 100%,0 100%);
  background:linear-gradient(145deg,color-mix(in srgb,var(--c-ink) 9%,transparent),
    color-mix(in srgb,var(--c-accent) 13%,transparent));
  transform:translate(-50%,-50%) rotate(calc((var(--scene-progress,0) - .5) * 10deg))}
.gxpr-pr-object::before{position:absolute;inset:2px;content:"";clip-path:inherit;
  background:linear-gradient(145deg,color-mix(in srgb,var(--c-ink) 5%,transparent),
    transparent 48%,color-mix(in srgb,var(--c-glow2) 8%,transparent))}
.gxpr-pr-ray{position:absolute;top:48%;left:50%;width:64vw;height:16%;
  transform-origin:left center;clip-path:polygon(0 43%,100% 0,100% 100%,0 57%);
  background:linear-gradient(90deg,color-mix(in srgb,var(--plan-accent,var(--c-accent)) 24%,transparent),transparent 74%);
  transform:translate(0,-50%) rotate(var(--ray-rotate,0deg));
  opacity:.12;transition:opacity .45s ease}
.gxpr-pr-ray.is-active{opacity:.36}
.gx-prism .gxpr-plan::before{position:absolute;top:0;left:50%;width:1px;height:76px;content:"";
  background:linear-gradient(var(--plan-accent,var(--c-accent)),transparent);
  opacity:.2;transition:height .55s cubic-bezier(.22,.7,.24,1),opacity .35s ease}
.gx-prism .gxpr-plan.is-active::before{height:128px;opacity:.8}
.gx-prism .gxpr-plan:not(.is-active){filter:saturate(.55)}

/* ---- skin: strata (pricing-05) — serif editorial stage re-derived to host
   tokens by hand (kit's cream->ink bridge was the flagged miss; lens
   light-theme decree — serif voice, not cream paint, carries the identity):
   derived display backword, accent rule under the active plan, Roman
   data-edition ghosts (the plan's one numbering) ---- */
.gx-strata{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-strata .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-st-word{position:absolute;bottom:-.1em;left:-.03em;z-index:0;
  font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(11rem,24vw,29rem);line-height:.8;letter-spacing:-.095em;white-space:nowrap;
  color:color-mix(in srgb,var(--c-ink) 6%,transparent);
  transform:translateX(calc(var(--scene-progress,0) * -5%))}
.gxpr-st-rule{position:absolute;right:0;bottom:9%;left:0;z-index:1;height:1px;
  background:color-mix(in srgb,var(--c-ink) 16%,transparent)}
.gxpr-st-rule::after{position:absolute;bottom:-2px;left:var(--active-center,50%);
  width:calc(var(--active-width,33.3%) * .9);height:4px;content:"";background:var(--c-accent);
  transform:translateX(-50%);transition:left .7s cubic-bezier(.22,.7,.24,1)}
.gx-strata .gxpr-plans{border-top:1px solid color-mix(in srgb,var(--c-ink) 15%,transparent)}
.gx-strata .gxpr-plan{border-right:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent)}
.gx-strata .gxpr-plan:last-child{border-right:0}
.gx-strata .gxpr-plan::before{position:absolute;top:16px;right:17px;content:attr(data-edition);
  font-family:Georgia,"Times New Roman",serif;font-size:clamp(2.2rem,4vw,4.4rem);line-height:1;
  color:color-mix(in srgb,var(--c-ink) 16%,transparent)}
.gx-strata .gxpr-name,.gx-strata .gxpr-price{font-family:Georgia,"Times New Roman",serif}
.gx-strata .gxpr-name{font-weight:700;letter-spacing:.12em}
.gx-strata .gxpr-plan::after{background:linear-gradient(90deg,transparent,var(--c-accent),transparent)}

/* ---- skin: auroralane (pricing-06) — aurora ridge silhouette (translucent
   clip-path, no slab) + travelling vertical beam over the active lane ---- */
.gx-auroralane{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-auroralane .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-au-ridge{position:absolute;right:-8%;bottom:10%;left:-8%;height:42%;z-index:0;
  clip-path:polygon(0 65%,14% 55%,27% 60%,40% 47%,51% 56%,66% 42%,80% 50%,100% 35%,100% 100%,0 100%);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 12%,transparent),
    color-mix(in srgb,var(--c-glow2) 5%,transparent) 60%,transparent);
  mask-image:linear-gradient(rgba(0,0,0,1) 30%,transparent 96%);
  -webkit-mask-image:linear-gradient(rgba(0,0,0,1) 30%,transparent 96%)}
.gxpr-au-beam{position:absolute;top:8%;left:var(--active-center,50%);z-index:0;
  width:calc(var(--active-width,33.3%) * .82);height:84%;transform:translateX(-50%);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 13%,transparent),
    color-mix(in srgb,var(--c-glow2) 4%,transparent) 70%,transparent);
  transition:left .7s cubic-bezier(.22,.7,.24,1),width .7s cubic-bezier(.22,.7,.24,1)}
.gxpr-au-beam::before{position:absolute;top:0;bottom:0;left:50%;width:1px;content:"";
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 55%,transparent),transparent 70%)}

/* ---- skin: celestial (pricing-07) — orbital ellipse rings sized to the
   plans grid + glowing sun disc travelling to the active plan ---- */
.gx-celestial{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-celestial .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-ce-orbit{position:absolute;top:56%;left:50%;z-index:0;
  width:min(78vw,1150px);height:min(32vw,520px);border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  border-radius:50%;transform:translate(-50%,-50%)}
.gxpr-ce-orbit::before,.gxpr-ce-orbit::after{position:absolute;inset:12% 10%;content:"";
  border:1px solid color-mix(in srgb,var(--c-ink) 8%,transparent);border-radius:50%}
.gxpr-ce-orbit::after{inset:26% 22%;border-color:color-mix(in srgb,var(--c-accent) 16%,transparent)}
.gxpr-ce-sun{position:absolute;top:24%;left:var(--active-center,50%);z-index:0;width:54px;height:54px;
  border-radius:50%;transform:translate(-50%,-50%);
  background:radial-gradient(circle at 35% 35%,color-mix(in srgb,var(--c-ink) 40%,var(--c-accent)),
    var(--c-accent) 48%,color-mix(in srgb,var(--c-accent) 45%,transparent));
  box-shadow:0 0 45px color-mix(in srgb,var(--c-accent) 30%,transparent);
  transition:left .7s cubic-bezier(.22,.7,.24,1)}
.gxpr-ce-sun::after{position:absolute;inset:-14px;content:"";border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-accent) 35%,transparent)}
.gx-celestial .gxpr-plan::before{position:absolute;top:0;left:50%;width:1px;height:94px;content:"";
  background:linear-gradient(color-mix(in srgb,var(--c-accent) 60%,transparent),transparent);
  opacity:.25;transition:height .6s cubic-bezier(.22,.7,.24,1),opacity .25s ease}
.gx-celestial .gxpr-plan.is-active::before{height:136px;opacity:.78}

/* ---- skin: ribbonglow (pricing-08) — skewed glowing ribbon + travelling
   bordered highlight panel (translucent glow frame, box-checklist reviewed)
   + real-price ghost ---- */
.gx-ribbonglow{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-ribbonglow .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-rb-band{position:absolute;top:52%;right:-10%;left:-10%;z-index:0;
  height:clamp(120px,18vh,190px);transform:translateY(-50%) skewY(-6deg);
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 10%,transparent),
    color-mix(in srgb,var(--c-glow2) 14%,transparent),
    color-mix(in srgb,var(--c-accent) 10%,transparent));
  mask-image:linear-gradient(90deg,transparent,rgba(0,0,0,1) 12%,rgba(0,0,0,1) 88%,transparent);
  -webkit-mask-image:linear-gradient(90deg,transparent,rgba(0,0,0,1) 12%,rgba(0,0,0,1) 88%,transparent)}
.gxpr-rb-panel{position:absolute;top:16%;left:var(--active-center,50%);z-index:0;height:68%;
  width:calc(var(--active-width,33.3%) * .92);transform:translateX(-50%) skewY(-3deg);
  border:1px solid color-mix(in srgb,var(--c-accent) 40%,transparent);border-radius:24px;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent);
  transition:left .7s cubic-bezier(.22,.7,.24,1),width .7s cubic-bezier(.22,.7,.24,1)}
.gx-ribbonglow .gxpr-ghost{left:var(--active-center,50%);
  transition:opacity .6s ease,left .7s cubic-bezier(.22,.7,.24,1)}

/* ---- skin: spotlight (pricing-09) — perspective floor wedge (masked
   translucent gradient) + horizon line + soft radial spotlight; the
   calmest/luxury variant ---- */
.gx-spotlight{min-height:min(220svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-spotlight .gx-stage{display:grid;align-content:center;overflow:hidden}
.gxpr-sp-floor{position:absolute;right:0;bottom:0;left:0;height:44%;z-index:0;
  clip-path:polygon(0 22%,100% 0,100% 100%,0 100%);
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 8%,transparent),transparent 80%);
  mask-image:linear-gradient(rgba(0,0,0,1) 30%,transparent 96%);
  -webkit-mask-image:linear-gradient(rgba(0,0,0,1) 30%,transparent 96%)}
.gxpr-sp-horizon{position:absolute;top:34%;right:0;left:0;z-index:0;height:1px;
  background:color-mix(in srgb,var(--c-ink) 16%,transparent)}
.gxpr-sp-spot{position:absolute;top:34%;left:var(--active-center,50%);z-index:0;
  width:min(30vw,420px);aspect-ratio:1;transform:translate(-50%,-50%);
  background:radial-gradient(circle at 50% 55%,color-mix(in srgb,var(--c-accent) 14%,transparent),transparent 55%);
  transition:left .7s cubic-bezier(.22,.7,.24,1)}
.gxpr-sp-spot::after{position:absolute;top:50%;left:50%;width:72px;height:72px;content:"";
  border:1px solid color-mix(in srgb,var(--c-accent) 35%,transparent);border-radius:50%;
  transform:translate(-50%,-50%)}

/* ==== FAQ FAMILY SKINS (Library 5, faq-v3 kits, gxf- namespace) ====
   Takeover 240svh scenes on NATIVE <details>/<summary> (family hard
   invariant): the summary is the question row, `open` replaces the donor's
   is-active, scroll/click toggle it via the engine animator (click owns the
   state 1.8s — donor manualUntil, rebuilt). Shared item anatomy below. */
.gxf-item{position:relative;border-bottom:1px solid var(--gx-line)}
.gxf-q{display:grid;grid-template-columns:54px minmax(0,1fr) 26px;gap:16px;align-items:center;
  padding:17px 0;cursor:pointer;list-style:none;
  color:color-mix(in srgb,var(--c-ink) 78%,transparent)}
.gxf-q::-webkit-details-marker{display:none}
.gxf-q::marker{content:""}
.gxf-index{font-family:var(--f-utility);font-size:.7rem;font-weight:800;letter-spacing:.16em;
  font-variant-numeric:tabular-nums;color:color-mix(in srgb,var(--c-ink) 42%,transparent)}
.gxf-qt{font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.2rem,2.05vw,2.05rem);line-height:1.1;letter-spacing:-.035em}
.gxf-plus{position:relative;width:22px;height:22px}
.gxf-plus::before,.gxf-plus::after{position:absolute;top:50%;left:50%;width:14px;height:1px;content:"";
  background:currentColor;transform:translate(-50%,-50%);transition:transform .35s ease}
.gxf-plus::after{transform:translate(-50%,-50%) rotate(90deg)}
.gxf-item[open]>.gxf-q .gxf-plus::after{transform:translate(-50%,-50%) rotate(0deg)}
.gxf-item[open]>.gxf-q{color:var(--c-ink)}
.gxf-item[open]>.gxf-q .gxf-index{color:var(--c-accent)}
.gxf-answer{padding:0 48px 20px 70px}
.gxf-answer p{margin:0;max-width:760px;color:var(--gx-muted);line-height:1.65}
.gxf-item[open] .gxf-answer{animation:gxf-reveal .5s ease both}
@keyframes gxf-reveal{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ---- skin: ledger (faq-01) — serif editorial ledger re-derived to host
   tokens (kit cream bridge miss re-derived; lens light-theme decree); aside
   numeral/short-label/progress rule are animator surfaces ---- */
.gx-ledger{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-ledger .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-lg-word{position:absolute;bottom:-.12em;left:-.04em;z-index:0;
  font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(12rem,27vw,29rem);line-height:.78;letter-spacing:-.11em;white-space:nowrap;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent);
  transform:translateX(var(--word-x,-5%))}
.gxf-lg-layout{position:relative;z-index:2;display:grid;
  grid-template-columns:minmax(220px,.55fr) minmax(0,1.45fr);
  gap:clamp(35px,7vw,110px);align-items:start;width:min(1280px,100%);margin-inline:auto}
.gxf-lg-aside{position:relative;min-height:clamp(320px,44svh,430px);padding-top:8px}
.gxf-lg-num{font-family:Georgia,"Times New Roman",serif;
  font-size:clamp(8rem,16vw,15rem);line-height:.75;letter-spacing:-.1em;color:var(--c-accent)}
.gxf-lg-label{margin-top:30px;font-family:var(--f-utility);font-size:.72rem;font-weight:800;
  letter-spacing:.18em;text-transform:uppercase;color:color-mix(in srgb,var(--c-ink) 48%,transparent)}
.gxf-lg-label strong{display:block;margin-top:8px;color:var(--c-ink);font-size:1rem;
  letter-spacing:0;text-transform:none}
.gxf-lg-rule{position:absolute;right:0;bottom:0;left:0;height:1px;
  background:color-mix(in srgb,var(--c-ink) 14%,transparent)}
.gxf-lg-rule i{position:absolute;top:-1px;left:0;height:3px;width:var(--rule-w,16%);
  background:var(--c-accent);transition:width .55s cubic-bezier(.22,.7,.24,1)}
.gxf-lg-list{border-top:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent)}
.gx-ledger .gxf-qt{font-family:Georgia,"Times New Roman",serif;font-weight:700}

/* ---- skin: bands (faq-02) — full-width bands, open band gains the accent
   wash; scan line tracks the active row; giant ghost counter is the scene's
   ONE counter (band-keyword chips dropped — not in the manifest) ---- */
.gx-bands{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-bands .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-bd-ghost{position:absolute;right:-.04em;bottom:-.16em;z-index:0;
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(14rem,30vw,34rem);line-height:1;letter-spacing:-.12em;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent)}
.gxf-bd-list{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto;
  border-top:1px solid var(--gx-line)}
.gx-bands .gxf-item{transition:background .35s ease}
.gx-bands .gxf-item[open]{background:linear-gradient(90deg,
  color-mix(in srgb,var(--c-accent) 10%,transparent),
  color-mix(in srgb,var(--c-accent) 4%,transparent),transparent)}
.gx-bands .gxf-q{grid-template-columns:60px minmax(0,1fr) 26px;gap:18px}
.gx-bands .gxf-answer{padding-left:78px}
.gxf-bd-scan{position:absolute;right:0;left:0;top:var(--scan-y,0px);height:1px;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--c-accent) 55%,transparent),transparent);
  transition:top .6s cubic-bezier(.22,.7,.24,1)}

/* ---- skin: staircase (faq-03) — stair-indented list (count-general --gx-i
   indent) + functional rail marker and "01 / 0N" count, kept as the ONLY
   counter (item indexes dropped — one numbering per element) ---- */
.gx-staircase{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-staircase .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-st-word{position:absolute;right:-.06em;bottom:-.16em;z-index:0;
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(13rem,30vw,34rem);line-height:1;letter-spacing:-.13em;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent);
  transform:translateX(var(--word-x,0))}
.gxf-st-layout{position:relative;z-index:2;display:grid;
  grid-template-columns:150px minmax(0,1fr);gap:clamp(28px,5vw,72px);align-items:start;
  width:min(1280px,100%);margin-inline:auto}
.gxf-st-rail{position:relative;height:clamp(320px,46svh,440px);margin-top:8px}
.gxf-st-rail::before{position:absolute;top:0;bottom:0;left:50%;width:1px;content:"";
  background:color-mix(in srgb,var(--c-ink) 14%,transparent)}
.gxf-st-marker{position:absolute;top:var(--marker-y,8%);left:50%;width:36px;height:4px;
  background:var(--c-accent);transform:translate(-50%,-50%);
  transition:top .5s cubic-bezier(.22,.7,.24,1)}
.gxf-st-count{position:absolute;right:0;left:0;top:var(--marker-y,8%);
  transform:translateY(16px);text-align:center;font-family:var(--f-utility);font-size:.72rem;
  font-weight:800;letter-spacing:.16em;font-style:normal;
  color:color-mix(in srgb,var(--c-ink) 55%,transparent);
  transition:top .5s cubic-bezier(.22,.7,.24,1)}
.gxf-st-list{border-top:1px solid var(--gx-line)}
.gx-staircase .gxf-item{margin-left:calc(var(--gx-i,0) * clamp(10px,2.2vw,30px));
  transform:translateX(var(--stair-shift,0px));
  transition:transform .45s cubic-bezier(.22,.7,.24,1),background .3s ease}
.gx-staircase .gxf-item[open]{background:linear-gradient(90deg,
  color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 70%)}
.gx-staircase .gxf-q{grid-template-columns:minmax(0,1fr) 26px;padding-left:14px}
.gx-staircase .gxf-answer{padding-left:14px}

/* ---- skin: columns (faq-04) — vertical flex columns; the open details IS
   the expanded column (~2.45x, accent wash), count-general ---- */
.gx-columns{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-columns .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-co-track{position:relative;z-index:2;display:flex;width:min(1280px,100%);margin-inline:auto;
  min-height:clamp(400px,52svh,540px);
  border-top:1px solid var(--gx-line);border-bottom:1px solid var(--gx-line)}
.gx-columns .gxf-item{flex:1 1 0%;min-width:0;overflow:hidden;padding:24px 18px;
  border-bottom:0;border-right:1px solid var(--gx-line);opacity:.55;
  transition:flex-grow .68s cubic-bezier(.22,.7,.24,1),background .35s ease,opacity .35s ease}
.gx-columns .gxf-item:last-child{border-right:0}
.gx-columns .gxf-item[open]{flex-grow:2.45;opacity:1;
  background:linear-gradient(180deg,color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 70%)}
.gx-columns .gxf-q{display:grid;grid-template-columns:1fr;align-content:start;gap:0;padding:0}
.gx-columns .gxf-index{margin-bottom:22px}
.gx-columns .gxf-qt{font-size:clamp(1rem,1.5vw,1.5rem);line-height:1.08}
.gx-columns .gxf-item[open] .gxf-qt{font-size:clamp(1.5rem,2.6vw,2.9rem);max-width:11ch}
.gx-columns .gxf-plus{margin-top:22px}
.gx-columns .gxf-answer{margin-top:16px;padding:0 8px 0 0}
.gx-columns .gxf-answer p{max-width:520px;font-size:.98rem}
.gxf-co-base{position:absolute;right:0;bottom:10%;left:0;z-index:0;height:1px;
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--c-accent) 40%,transparent),transparent)}

/* ---- skin: typewall (faq-05) — serif question wall + measured travelling
   cursor frame; the mirror below is the desktop VISUAL reading surface
   (in-wall answers visually clipped on ANIMATED desktop only — the
   ledger-sanctioned pattern, but they stay in the accessibility tree; native
   disclosure carries mobile, reduced-motion AND assistive tech). LIGHT donor
   re-derived to host tokens (lens light-theme decree). ---- */
.gx-typewall{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-typewall .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-tw-word{position:absolute;top:40%;left:-.03em;z-index:0;
  font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(12rem,26vw,28rem);line-height:.8;letter-spacing:-.12em;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent);
  transform:translate(var(--word-x,-4%),-50%)}
.gxf-tw-wrap{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto}
.gxf-tw-grid{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));
  border-top:1px solid color-mix(in srgb,var(--c-ink) 16%,transparent);
  border-left:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent)}
.gx-typewall .gxf-item{min-height:clamp(120px,16svh,165px);padding:20px;border-bottom:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
  border-right:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);transition:background .3s ease}
.gx-typewall .gxf-item[open]{background:color-mix(in srgb,var(--c-accent) 7%,transparent)}
.gx-typewall .gxf-q{grid-template-columns:36px minmax(0,1fr);gap:14px;align-items:start;padding:0}
.gx-typewall .gxf-qt{font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(1.1rem,1.7vw,1.75rem)}
.gx-typewall .gxf-plus{display:none}
.gxf-tw-cursor{position:absolute;top:var(--cursor-y,0);left:var(--cursor-x,0);z-index:3;
  width:var(--cursor-w,33.3%);height:var(--cursor-h,150px);border:2px solid var(--c-accent);
  pointer-events:none;font-style:normal;
  transition:left .5s cubic-bezier(.22,.7,.24,1),top .5s cubic-bezier(.22,.7,.24,1),
    width .5s cubic-bezier(.22,.7,.24,1),height .5s cubic-bezier(.22,.7,.24,1)}
.gxf-tw-mirror{position:relative;z-index:2;display:grid;
  grid-template-columns:minmax(90px,130px) minmax(0,1fr);gap:30px;
  width:min(1280px,100%);margin-inline:auto;padding:25px 0 0}
.gxf-tw-num{font-family:Georgia,"Times New Roman",serif;
  font-size:clamp(3.6rem,7vw,7rem);line-height:.8;color:var(--c-accent)}
.gxf-tw-copy h3{margin:0 0 10px;font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(1.7rem,2.9vw,3.2rem);line-height:1;letter-spacing:-.045em;color:var(--c-ink)}
.gxf-tw-copy p{margin:0;max-width:760px;color:var(--gx-muted);line-height:1.65}
@media (min-width:901px) and (prefers-reduced-motion:no-preference){
  /* visually-hidden clip, NEVER display:none: the mirror below is aria-hidden
     visual chrome, so the native answers must stay in the accessibility tree —
     toggling a summary has to disclose real content to AT */
  .gx-typewall .gxf-answer{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
}

/* ---- skin: faqchapters (faq-06) — active question at viewport scale (mirror
   surface) over the giant ghost chapter numeral; details nav strip along the
   bottom. Donor "Question 0N" mirror-index dropped (de-AI: ghost = artwork
   numbering, nav indexes = nav — rail precedent). ---- */
.gx-faqchapters{min-height:min(240svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-faqchapters .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxf-ch-ghost{position:absolute;right:-.05em;bottom:-.16em;z-index:0;
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(16rem,34vw,38rem);line-height:1;letter-spacing:-.13em;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent)}
.gxf-ch-wipe{position:absolute;top:0;bottom:0;left:var(--wipe-x,8%);z-index:0;width:20%;
  transform:translateX(-50%);
  background:linear-gradient(90deg,transparent,color-mix(in srgb,var(--c-accent) 5%,transparent),transparent)}
.gxf-ch-copy{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto;
  padding:10px 0 clamp(130px,18svh,180px);max-width:1050px}
.gxf-ch-copy h3{margin:0 0 22px;font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.6rem,6vw,6.6rem);line-height:.92;letter-spacing:-.07em;max-width:14ch;
  color:var(--c-ink)}
.gxf-ch-copy p{margin:0;max-width:62ch;color:var(--gx-muted);line-height:1.7;
  font-size:clamp(1rem,1.4vw,1.16rem)}
.gxf-ch-nav{position:absolute;right:clamp(16px,4vw,52px);bottom:14px;left:clamp(16px,4vw,52px);
  z-index:2;display:grid;grid-auto-flow:column;grid-auto-columns:minmax(0,1fr);
  border-top:1px solid var(--gx-line)}
.gx-faqchapters .gxf-item{padding:15px 12px 11px;border-bottom:0;
  border-right:1px solid var(--gx-line)}
.gx-faqchapters .gxf-item:last-child{border-right:0}
.gx-faqchapters .gxf-item[open]{box-shadow:inset 0 3px var(--c-accent)}
.gx-faqchapters .gxf-q{display:block;padding:0}
.gx-faqchapters .gxf-index{display:block;margin-bottom:8px}
.gx-faqchapters .gxf-qt{display:block;font-family:var(--f-utility);font-size:.78rem;
  font-weight:700;line-height:1.2;letter-spacing:0}
.gx-faqchapters .gxf-plus{display:none}
@media (min-width:901px) and (prefers-reduced-motion:no-preference){
  /* visually-hidden clip, NEVER display:none — same AT contract as typewall */
  .gx-faqchapters .gxf-answer{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
    overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
}

/* ---- pricing + faq skins: static collapse <=900px ---- */
@media (max-width:900px){
  .gx-aperture,.gx-lightwell,.gx-monument,.gx-prism,.gx-strata,.gx-auroralane,
  .gx-celestial,.gx-ribbonglow,.gx-spotlight{min-height:auto;width:100%;margin-left:0}
  .gxpr-ap-field,.gxpr-lw-floor,.gxpr-lw-beam,.gxpr-lw-ring,.gxpr-mo-haze,
  .gxpr-mo-horizon,.gxpr-pr-sky,.gxpr-st-word,.gxpr-st-rule,.gxpr-au-ridge,
  .gxpr-au-beam,.gxpr-ce-orbit,.gxpr-ce-sun,.gxpr-rb-band,.gxpr-rb-panel,
  .gxpr-sp-floor,.gxpr-sp-horizon,.gxpr-sp-spot,.gxpr-ghosts{display:none}
  .gxpr-plans{grid-template-columns:1fr;width:100%;border:0}
  .gxpr-plan{min-height:0;padding:26px 0 30px!important;border-right:0!important;
    border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important;filter:none!important}
  .gxpr-plan::after{display:none}
  .gx-ledger,.gx-bands,.gx-staircase,.gx-columns,.gx-typewall,.gx-faqchapters{
    min-height:auto;width:100%;margin-left:0}
  .gxf-lg-word,.gxf-lg-aside,.gxf-bd-ghost,.gxf-bd-scan,.gxf-st-word,.gxf-st-rail,
  .gxf-co-base,.gxf-tw-word,.gxf-tw-cursor,.gxf-tw-mirror,.gxf-ch-ghost,
  .gxf-ch-wipe,.gxf-ch-copy{display:none}
  .gxf-lg-layout,.gxf-st-layout{display:block}
  .gxf-item{min-height:0;margin-left:0!important;padding:0!important;
    border-right:0!important;border-bottom:1px solid var(--gx-line)!important;
    transform:none!important;background:none!important;opacity:1!important;box-shadow:none!important}
  .gxf-co-track{display:block;min-height:0;border-bottom:0}
  .gxf-tw-grid{grid-template-columns:1fr;border-left:0}
  .gxf-ch-nav{position:relative;right:auto;bottom:auto;left:auto;display:block}
  .gxf-q{display:grid!important;grid-template-columns:42px minmax(0,1fr) 24px!important;
    gap:14px;align-items:center;padding:16px 0!important}
  .gx-staircase .gxf-q{grid-template-columns:minmax(0,1fr) 24px!important}
  .gxf-qt{font-size:1.15rem!important;max-width:none!important}
  .gxf-index{margin:0!important}
  .gxf-plus{display:block!important;margin:0!important}
  .gxf-answer{display:block!important;margin:0!important;padding:0 8px 18px 56px!important}
  .gx-staircase .gxf-answer{padding-left:14px!important}
}
/* ---- pricing + faq skins: reduced-motion still state (same collapse; the
   animator stands down, so mirror/cursor surfaces hide and native details
   disclosure carries the reading) ---- */
@media (prefers-reduced-motion:reduce){
  .gx-aperture,.gx-lightwell,.gx-monument,.gx-prism,.gx-strata,.gx-auroralane,
  .gx-celestial,.gx-ribbonglow,.gx-spotlight{min-height:auto;width:100%;margin-left:0}
  .gxpr-ap-field,.gxpr-lw-floor,.gxpr-lw-beam,.gxpr-lw-ring,.gxpr-mo-haze,
  .gxpr-mo-horizon,.gxpr-pr-sky,.gxpr-st-word,.gxpr-st-rule,.gxpr-au-ridge,
  .gxpr-au-beam,.gxpr-ce-orbit,.gxpr-ce-sun,.gxpr-rb-band,.gxpr-rb-panel,
  .gxpr-sp-floor,.gxpr-sp-horizon,.gxpr-sp-spot,.gxpr-ghosts{display:none}
  .gxpr-plans{grid-template-columns:1fr;width:100%;border:0}
  .gxpr-plan{min-height:0;padding:26px 0 30px!important;border-right:0!important;
    border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important;filter:none!important}
  .gxpr-plan::after{display:none}
  .gx-ledger,.gx-bands,.gx-staircase,.gx-columns,.gx-typewall,.gx-faqchapters{
    min-height:auto;width:100%;margin-left:0}
  .gxf-lg-word,.gxf-lg-aside,.gxf-bd-ghost,.gxf-bd-scan,.gxf-st-word,.gxf-st-rail,
  .gxf-co-base,.gxf-tw-word,.gxf-tw-cursor,.gxf-tw-mirror,.gxf-ch-ghost,
  .gxf-ch-wipe,.gxf-ch-copy{display:none}
  .gxf-lg-layout,.gxf-st-layout{display:block}
  .gxf-item{min-height:0;margin-left:0!important;padding:0!important;
    border-right:0!important;border-bottom:1px solid var(--gx-line)!important;
    transform:none!important;background:none!important;opacity:1!important;box-shadow:none!important}
  .gxf-item[open] .gxf-answer{animation:none}
  .gxf-co-track{display:block;min-height:0;border-bottom:0}
  .gxf-tw-grid{grid-template-columns:1fr;border-left:0}
  .gxf-ch-nav{position:relative;right:auto;bottom:auto;left:auto;display:block}
  .gxf-q{display:grid!important;grid-template-columns:42px minmax(0,1fr) 24px!important;
    gap:14px;align-items:center;padding:16px 0!important}
  .gx-staircase .gxf-q{grid-template-columns:minmax(0,1fr) 24px!important}
  .gxf-qt{font-size:1.15rem!important;max-width:none!important}
  .gxf-index{margin:0!important}
  .gxf-plus{display:block!important;margin:0!important}
  .gxf-answer{display:block!important;margin:0!important;padding:0 8px 18px 56px!important}
  .gx-staircase .gxf-answer{padding-left:14px!important}
}

/* ==== TEAM FAMILY SKINS (Library 6, team-v3 kits, gxt- namespace) ====
   Takeover ~230svh scenes, GL-transparent stages (token radials <=15% only).
   team_grid is brief-verbatim and UNFLOORED: every layout below is
   count-general from 2 people (velo) to 8. Portraits ride items[].asset_ref;
   the no-photo branch is the shared gx-pane-fallback monogram (P3 team
   precedent — never an empty <img>). Shared member anatomy below; NO member
   counters anywhere (people are not a sequence — de-AI); member-jump nav is
   functional (rail precedent). */
.gxt-portrait{position:relative;display:block;margin:0;width:100%;height:100%;overflow:hidden;
  background:color-mix(in srgb,var(--c-field) 55%,transparent)}
.gxt-portrait img{width:100%;height:100%;display:block;object-fit:cover;object-position:center 22%}
.gxt-name{margin:0;font-family:var(--f-display);font-weight:var(--fw-display);color:var(--c-ink);
  font-size:clamp(1.7rem,3.1vw,3.5rem);line-height:.98;letter-spacing:-.05em}
.gxt-role{margin:8px 0 10px;color:var(--c-accent);font-family:var(--f-utility);
  font-size:.73rem;font-weight:800;letter-spacing:.16em;text-transform:uppercase}
.gxt-bio{margin:0;max-width:46ch;color:var(--gx-muted);
  font-size:clamp(.92rem,1.15vw,1.05rem);line-height:1.6}
.gxt-nav{display:grid;gap:4px}
.gxt-nav button{border:0;background:none;padding:6px 0;cursor:pointer;
  color:color-mix(in srgb,var(--c-ink) 44%,transparent);font-family:var(--f-utility);
  font-size:.68rem;font-weight:800;letter-spacing:.12em;text-transform:uppercase;
  transition:color .3s ease,transform .3s ease}
.gxt-nav button.is-active{color:var(--c-ink)}

/* ---- skin: teamchapters (team-01) — one member fills the stage; portrait
   clip reveal, copy right, name nav on the right edge, progress hairline on
   --scene-progress. Donor stage gradient stripped (GL transparency). ---- */
.gx-teamchapters{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-teamchapters .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-ch-scene{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto;
  height:clamp(470px,61svh,650px)}
.gxt-ch-member{position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,1.06fr) minmax(300px,.72fr);
  gap:clamp(30px,5vw,76px);align-items:center;
  opacity:0;visibility:hidden;pointer-events:none;
  transition:opacity .3s ease,visibility 0s .35s}
.gxt-ch-member.is-active{opacity:1;visibility:visible;pointer-events:auto;transition-delay:0s}
.gxt-ch-media{height:100%;clip-path:inset(14% 12% 0);transform:translateY(12%) scale(.94);
  transition:clip-path .8s cubic-bezier(.2,.8,.2,1),transform .8s cubic-bezier(.2,.8,.2,1)}
.gxt-ch-member.is-active .gxt-ch-media{clip-path:inset(0);transform:none}
/* C2: the copy sits directly on a team PORTRAIT with no ground of its own. Measured across 21
   scroll positions it currently reads at 14.05 / 7.79 / 5.94:1 (name / role / bio) — fine, but
   only because those photographs happen to be dark exactly where the copy lands. It is one
   bright portrait away from the `contactsheet` failure that measured 1.22:1 on a grey chef's
   coat, and a portrait is user-replaceable, so the pack cannot rely on the photo staying dark.
   Same recipe as the scrims already shipped: feathered, masked, token-derived, no hard box, and
   `z-index:-1` so it sits under the text and over the photo. */
.gxt-ch-copy{position:relative;opacity:0;transform:translateY(30px);padding-right:clamp(0px,8vw,150px);
  transition:opacity .25s ease,transform .65s cubic-bezier(.2,.8,.2,1)}
.gxt-ch-copy::before{position:absolute;z-index:-1;content:"";pointer-events:none;
  inset:-18px -26px -22px -30px;
  background:radial-gradient(115% 105% at 22% 50%,
    color-mix(in srgb,var(--c-dominant) 82%,transparent) 0%,
    color-mix(in srgb,var(--c-dominant) 58%,transparent) 52%,
    transparent 84%);
  -webkit-mask:radial-gradient(115% 105% at 22% 50%,#000 48%,transparent 88%);
  mask:radial-gradient(115% 105% at 22% 50%,#000 48%,transparent 88%)}
.gxt-ch-member.is-active .gxt-ch-copy{opacity:1;transform:none}
.gx-teamchapters .gxt-name{font-size:clamp(2.6rem,5vw,5.4rem)}
.gx-teamchapters .gxt-nav{position:absolute;right:clamp(16px,4vw,52px);top:50%;z-index:5;
  transform:translateY(-50%)}
.gx-teamchapters .gxt-nav button{text-align:right;padding:7px 0 7px 18px}
.gx-teamchapters .gxt-nav button::after{content:"";display:inline-block;width:18px;height:1px;
  margin-left:9px;background:currentColor;vertical-align:middle}
.gx-teamchapters .gxt-nav button.is-active{transform:translateX(-8px)}
.gxt-ch-progress{position:absolute;left:clamp(16px,4vw,52px);top:14%;bottom:14%;width:1px;
  background:color-mix(in srgb,var(--c-ink) 9%,transparent)}
.gxt-ch-progress::after{content:"";position:absolute;inset:0;background:var(--c-accent);
  transform-origin:top;transform:scaleY(var(--scene-progress,0))}

/* ---- skin: teamstrip (team-02) — horizontal portrait reel; measured track
   glide; per-card giant numeral = the single cinematic index ---- */
.gx-teamstrip{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-teamstrip .gx-stage{display:grid;align-items:center;overflow:hidden;perspective:1500px}
.gxt-fs-track{position:relative;z-index:2;display:flex;gap:24px;align-items:center;
  width:max-content;margin-block:auto;
  transform:translate3d(var(--track-x,0px),0,0);will-change:transform}
.gxt-fs-member{position:relative;flex:0 0 min(47vw,640px);height:clamp(420px,56svh,650px);
  overflow:hidden;transform:perspective(1200px) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,.86));
  opacity:var(--card-opacity,.52);
  filter:brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform-style:preserve-3d;will-change:transform,opacity,filter}
.gxt-fs-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 43%,color-mix(in srgb,var(--c-dominant) 92%,transparent))}
.gxt-fs-copy{position:absolute;right:24px;bottom:22px;left:24px;z-index:2;
  opacity:var(--copy-opacity,0);transform:translateY(var(--copy-y,18px))}
.gx-teamstrip .gxt-name{font-size:clamp(2rem,4vw,4rem)}
.gx-teamstrip .gxt-bio{max-width:36ch}
.gxt-fs-num{position:absolute;right:22px;top:14px;z-index:3;
  font-family:var(--f-utility);font-size:clamp(3.2rem,8vw,7rem);font-weight:800;
  letter-spacing:-.08em;line-height:1;color:color-mix(in srgb,var(--c-ink) 13%,transparent)}

/* ---- skin: depthseq (team-03) — z-axis portrait corridor; giant side
   numeral is the scene's ONE counter (donor "/ 0N" tail dropped) ---- */
.gx-depthseq{min-height:min(180svh,calc(100svh + (var(--gx-n,6) - 1) * 20svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-depthseq .gx-stage{display:grid;place-items:center;overflow:hidden;
  perspective:1700px;transform-style:preserve-3d}
.gxt-dp-axis{position:absolute;left:50%;top:6%;bottom:6%;width:1px;z-index:0;
  background:linear-gradient(transparent,color-mix(in srgb,var(--c-ink) 16%,transparent),transparent)}
.gxt-dp-counter{position:absolute;right:clamp(14px,3vw,44px);top:50%;z-index:1;
  transform:translateY(-50%);font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(5rem,10vw,9rem);letter-spacing:-.09em;line-height:1;
  color:color-mix(in srgb,var(--c-ink) 14%,transparent)}
.gxt-dp-member{position:absolute;top:50%;left:50%;width:min(42vw,590px);height:clamp(400px,56svh,610px);
  overflow:hidden;opacity:var(--card-opacity,1);z-index:2;
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),0,var(--card-z,0px))
    rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,opacity}
.gxt-dp-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 42%,color-mix(in srgb,var(--c-dominant) 90%,transparent))}
.gxt-dp-copy{position:absolute;right:25px;bottom:24px;left:25px;z-index:2;
  opacity:.12;transition:opacity .28s ease}
.gxt-dp-member.is-active .gxt-dp-copy{opacity:1}
.gx-depthseq .gxt-name{font-size:clamp(2rem,4vw,4rem)}
.gx-depthseq .gxt-bio{max-width:34ch}

/* ---- skin: coverrelay (team-04) — magazine cover wipes; serif backword =
   the member's real first name; translucent color-mix cover panel (never an
   opaque slab); numeric round nav (nav numbering — rail precedent) ---- */
.gx-coverrelay{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-coverrelay .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-cv-stage{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto;
  height:clamp(480px,63svh,680px);overflow:hidden;
  background:color-mix(in srgb,var(--c-dominant) 55%,transparent)}
.gxt-cv-member{position:absolute;inset:0;overflow:hidden;opacity:0;visibility:hidden;
  pointer-events:none;clip-path:inset(0 100% 0 0);
  background:color-mix(in srgb,var(--member-accent,var(--c-accent)) 7%,transparent);
  transition:clip-path .78s cubic-bezier(.2,.8,.2,1),opacity .2s ease,visibility 0s .8s}
.gxt-cv-member.is-active{opacity:1;visibility:visible;pointer-events:auto;
  clip-path:inset(0);transition-delay:0s}
.gxt-cv-word{position:absolute;left:3%;top:50%;z-index:0;transform:translateY(-56%);
  font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(7rem,17vw,16rem);line-height:.75;letter-spacing:-.1em;white-space:nowrap;
  color:color-mix(in srgb,var(--member-accent,var(--c-accent)) 20%,transparent)}
.gxt-cv-image{position:absolute;right:0;top:0;bottom:0;width:56%;
  clip-path:polygon(13% 0,100% 0,100% 100%,0 100%)}
.gxt-cv-image .gxt-portrait{transform:scale(var(--kb-s,1.03)) translateX(var(--kb-x,0%));
  will-change:transform}
.gxt-cv-image::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 85%,transparent) 0,transparent 30%)}
.gxt-cv-copy{position:absolute;left:5%;bottom:8%;z-index:2;width:min(46%,520px)}
.gx-coverrelay .gxt-name{font-size:clamp(2.8rem,5.6vw,6.2rem)}
.gx-coverrelay .gxt-role{color:var(--member-accent,var(--c-accent))}
.gx-coverrelay .gxt-nav{position:absolute;right:22px;bottom:20px;z-index:5;
  display:flex;gap:8px}
.gx-coverrelay .gxt-nav button{width:34px;height:34px;padding:0;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  background:color-mix(in srgb,var(--c-dominant) 60%,transparent);letter-spacing:0}
.gx-coverrelay .gxt-nav button.is-active{background:var(--c-ink);color:var(--c-dominant)}

/* ---- skin: monoindex (team-05) — LIGHT editorial donor re-derived to host
   tokens by hand (lens light-theme decree): GL-transparent stage, serif
   voice. Measured cursor frame (typewall precedent, count-general); backdrop
   initials follow the active member ---- */
.gx-monoindex{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-monoindex .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-mi-back{position:absolute;right:-.02em;bottom:-.12em;z-index:0;
  font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(12rem,26vw,28rem);line-height:.78;letter-spacing:-.11em;
  color:color-mix(in srgb,var(--c-ink) 5%,transparent);
  transform:translateX(var(--mono-x,0%))}
.gxt-mi-list{position:relative;z-index:2;display:grid;
  grid-template-columns:repeat(2,minmax(0,1fr));width:min(1280px,100%);margin-inline:auto;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 20%,transparent)}
.gxt-mi-member{position:relative;min-height:clamp(150px,22svh,210px);padding:24px 28px;
  display:grid;grid-template-columns:86px minmax(0,1fr);gap:20px;align-items:start;
  border-right:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  opacity:.62;transition:opacity .35s ease}
.gxt-mi-member:nth-child(even){border-right:0}
.gxt-mi-member.is-active{opacity:1}
.gxt-mi-tile{width:86px;height:104px;overflow:hidden}
.gxt-mi-mark{display:block;font-family:Georgia,"Times New Roman",serif;
  font-size:3.2rem;line-height:1;color:color-mix(in srgb,var(--c-ink) 24%,transparent)}
.gxt-mi-member.is-active .gxt-mi-mark{color:var(--c-accent)}
.gx-monoindex .gxt-name{font-family:Georgia,"Times New Roman",serif;font-weight:700;
  font-size:clamp(1.4rem,2.2vw,2.2rem)}
.gx-monoindex .gxt-bio{font-size:.92rem}
.gxt-mi-cursor{position:absolute;top:var(--cursor-y,0);left:var(--cursor-x,0);z-index:3;
  width:var(--cursor-w,50%);height:var(--cursor-h,170px);border:2px solid var(--c-accent);
  pointer-events:none;font-style:normal;
  transition:left .55s cubic-bezier(.2,.8,.2,1),top .55s cubic-bezier(.2,.8,.2,1),
    width .55s cubic-bezier(.2,.8,.2,1),height .55s cubic-bezier(.2,.8,.2,1)}

/* ---- skin: shutters (team-06) — vertical portrait columns; the active
   shutter expands to full colour, others hold as dimmed slivers with
   vertical names; copy over the token scrim ---- */
.gx-shutters{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-shutters .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-sh-row{position:relative;z-index:2;display:flex;width:min(1280px,100%);margin-inline:auto;
  height:clamp(440px,59svh,640px);overflow:hidden}
.gxt-sh-member{position:relative;flex:1;min-width:0;overflow:hidden;
  border-right:1px solid color-mix(in srgb,var(--c-ink) 8%,transparent);
  transition:flex .75s cubic-bezier(.2,.8,.2,1)}
.gxt-sh-member:last-child{border-right:0}
.gxt-sh-member.is-active{flex:4}
.gxt-sh-member .gxt-portrait{filter:saturate(.6) brightness(.74);
  transition:filter .45s ease,transform .75s cubic-bezier(.2,.8,.2,1)}
.gxt-sh-member.is-active .gxt-portrait{filter:none;transform:scale(1.04)}
.gxt-sh-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 35%,color-mix(in srgb,var(--c-dominant) 90%,transparent))}
.gxt-sh-name{position:absolute;left:50%;bottom:22px;z-index:2;white-space:nowrap;
  writing-mode:vertical-rl;transform:translateX(-50%) rotate(180deg);
  font-family:var(--f-display);font-weight:var(--fw-display);color:var(--c-ink);
  font-size:clamp(1rem,1.9vw,2rem);letter-spacing:-.02em;transition:opacity .25s ease}
.gxt-sh-member.is-active .gxt-sh-name{opacity:0}
.gxt-sh-copy{position:absolute;right:28px;bottom:28px;left:28px;z-index:3;max-width:470px;
  opacity:0;transform:translateY(24px);
  transition:opacity .35s ease .18s,transform .55s cubic-bezier(.2,.8,.2,1) .18s}
.gxt-sh-member.is-active .gxt-sh-copy{opacity:1;transform:none}
.gx-shutters .gxt-name{font-size:clamp(1.8rem,3.4vw,3.6rem)}

/* ---- skin: portraitstack (team-07) — fanned portrait deck glides past the
   camera (custom-prop transforms only); numeric nav dots bottom-left ---- */
.gx-portraitstack{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-portraitstack .gx-stage{display:grid;place-items:center;overflow:hidden;
  perspective:1700px;transform-style:preserve-3d}
.gxt-ps-member{position:absolute;top:50%;left:50%;width:min(52vw,720px);height:clamp(420px,56svh,620px);
  overflow:hidden;opacity:var(--card-opacity,1);z-index:2;
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateZ(var(--card-rz,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,opacity}
.gxt-ps-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 45%,color-mix(in srgb,var(--c-dominant) 88%,transparent))}
.gxt-ps-copy{position:absolute;right:28px;bottom:26px;left:28px;z-index:2;
  opacity:0;transition:opacity .28s ease}
.gxt-ps-member.is-active .gxt-ps-copy{opacity:1}
.gx-portraitstack .gxt-name{font-size:clamp(2.1rem,4.4vw,4.6rem)}
.gx-portraitstack .gxt-nav{position:absolute;left:clamp(16px,4vw,52px);bottom:18px;z-index:5;
  display:flex;gap:7px}
.gx-portraitstack .gxt-nav button{width:38px;height:38px;padding:0;border-radius:50%;
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  background:color-mix(in srgb,var(--c-dominant) 60%,transparent);letter-spacing:0}
.gx-portraitstack .gxt-nav button.is-active{background:var(--c-ink);color:var(--c-dominant)}

/* ---- skin: contactsheet (team-08) — editorial proof-sheet grid, every
   member visible; MEASURED accent focus frame over the active tile; column
   count from the head count (count-general); donor's opaque grid slab
   dropped — the 2px gaps show the page field ---- */
.gx-contactsheet{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-contactsheet .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-cs-grid{position:relative;z-index:2;display:grid;
  grid-template-columns:repeat(var(--sheet-cols,3),minmax(0,1fr));grid-auto-rows:1fr;
  width:min(1280px,100%);margin-inline:auto;height:clamp(460px,61svh,680px);gap:2px}
.gxt-cs-member{position:relative;overflow:hidden;
  background:color-mix(in srgb,var(--c-field) 55%,transparent)}
.gxt-cs-member .gxt-portrait{filter:saturate(.55) brightness(.72);transform:scale(1.02);
  transition:filter .45s ease,transform .7s cubic-bezier(.2,.8,.2,1)}
.gxt-cs-member.is-active .gxt-portrait{filter:none;transform:scale(1.08)}
.gxt-cs-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,transparent 45%,color-mix(in srgb,var(--c-dominant) 85%,transparent))}
.gxt-cs-label{position:absolute;right:16px;bottom:14px;left:16px;z-index:2}
/* The label needs its OWN ground, not the card-wide gradient's tail.
   `.gxt-cs-member::after` ramps to 85%-alpha dominant only at the card's very bottom
   edge, and the label sits at 89-95% of the card height — measured. On an ACTIVE member
   the portrait also loses its `brightness(.72)` dim, so the coat behind the copy gets
   brighter at exactly the moment the scrim is thinnest: `CHEF & FOUNDER` in accent
   crimson measured **1.22:1** over a gray chef's coat on
   out-real/restaurant__persona-studio (verify-visual at 1600x1200, crop saved). The
   `.gxt-name` above it is ink-on-image and survives; an ACCENT-coloured label over
   mid-tone photography cannot. Doctrine: text never borrows contrast from imagery.
   A local feathered scrim makes it deterministic whatever the photo does. */
.gxt-cs-label::before{position:absolute;z-index:-1;content:"";pointer-events:none;
  inset:-14px -16px -18px -16px;
  background:linear-gradient(180deg,transparent,
    color-mix(in srgb,var(--c-dominant) 62%,transparent) 38%,
    color-mix(in srgb,var(--c-dominant) 88%,transparent));
  -webkit-mask:linear-gradient(180deg,transparent,#000 34%);
  mask:linear-gradient(180deg,transparent,#000 34%)}
.gx-contactsheet .gxt-name{font-size:clamp(1.2rem,2vw,2.2rem)}
.gx-contactsheet .gxt-role{margin:4px 0 0}
.gx-contactsheet .gxt-bio{max-height:0;overflow:hidden;opacity:0;font-size:.88rem;line-height:1.3;
  transition:max-height .55s cubic-bezier(.2,.8,.2,1),opacity .35s ease}
.gxt-cs-member.is-active .gxt-bio{max-height:64px;opacity:1;margin-top:6px}
.gxt-cs-focus{position:absolute;z-index:5;border:2px solid var(--c-accent);pointer-events:none;
  font-style:normal;left:var(--fx,0);top:var(--fy,0);width:var(--fw,33.33%);height:var(--fh,50%);
  transition:left .58s cubic-bezier(.2,.8,.2,1),top .58s cubic-bezier(.2,.8,.2,1),
    width .58s cubic-bezier(.2,.8,.2,1),height .58s cubic-bezier(.2,.8,.2,1)}

/* ---- skin: rolebands (team-09) — full-width bands, the active band expands
   with its portrait on the right; copy on the EMPTY left over a token scrim
   (readability scrim, sanctioned); donor index chip dropped ---- */
.gx-rolebands{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-rolebands .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-rb-list{position:relative;z-index:2;display:flex;flex-direction:column;
  width:min(1280px,100%);margin-inline:auto;height:clamp(460px,62svh,680px);overflow:hidden}
.gxt-rb-member{position:relative;flex:1;min-height:0;overflow:hidden;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent);
  transition:flex .72s cubic-bezier(.2,.8,.2,1)}
.gxt-rb-member:last-child{border-bottom:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent)}
.gxt-rb-member.is-active{flex:4}
.gxt-rb-media{position:absolute;inset:0 0 0 55%;opacity:.44;
  transition:opacity .4s ease,transform .72s cubic-bezier(.2,.8,.2,1)}
.gxt-rb-member.is-active .gxt-rb-media{opacity:1;transform:scale(1.04)}
.gxt-rb-member::after{content:"";position:absolute;inset:0;
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 88%,transparent) 0 43%,
    color-mix(in srgb,var(--c-dominant) 55%,transparent) 58%,transparent)}
.gxt-rb-copy{position:absolute;left:22px;top:50%;z-index:2;width:min(47%,620px);
  transform:translateY(-50%)}
.gx-rolebands .gxt-name{font-size:clamp(1.5rem,2.9vw,3.2rem)}
.gx-rolebands .gxt-bio{max-height:0;opacity:0;overflow:hidden;
  transition:max-height .5s cubic-bezier(.2,.8,.2,1),opacity .35s ease}
.gxt-rb-member.is-active .gxt-bio{max-height:100px;opacity:1;margin-top:8px}

/* ---- skin: verticalreel (team-10) — full-height member frames on a
   vertical reel, MEASURED glide against the window height; side label
   derives from the section eyebrow (donor demo label dropped) ---- */
.gx-verticalreel{min-height:min(230svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-verticalreel .gx-stage{display:grid;align-content:center;overflow:hidden;
  padding-inline:clamp(16px,4vw,52px)}
.gxt-vr-window{position:relative;z-index:2;width:min(1280px,100%);margin-inline:auto;
  height:clamp(470px,64svh,680px);overflow:hidden;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent);
  border-bottom:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent)}
.gxt-vr-track{position:absolute;inset:0;transform:translateY(var(--reel-y,0px));
  will-change:transform}
.gxt-vr-member{height:100%;display:grid;grid-template-columns:.8fr 1.2fr;gap:40px;
  align-items:center}
.gxt-vr-copy{padding-left:clamp(20px,4vw,60px)}
.gx-verticalreel .gxt-name{font-size:clamp(2.6rem,5.6vw,6.2rem)}
.gxt-vr-media{height:100%;clip-path:polygon(12% 0,100% 0,100% 100%,0 100%)}
.gxt-vr-side{position:absolute;right:12px;top:50%;z-index:5;writing-mode:vertical-rl;
  transform:translateY(-50%);font-family:var(--f-utility);font-size:.66rem;font-weight:800;
  letter-spacing:.18em;color:color-mix(in srgb,var(--c-ink) 45%,transparent)}

/* ---- team skins: static collapse <=900px — stacked member cards, all
   chrome hidden, every member visible with portrait + copy ---- */
@media (max-width:900px){
  .gx-teamchapters,.gx-teamstrip,.gx-depthseq,.gx-coverrelay,.gx-monoindex,
  .gx-shutters,.gx-portraitstack,.gx-contactsheet,.gx-rolebands,.gx-verticalreel{
    min-height:auto;width:100%;margin-left:0}
  .gxt-nav,.gxt-ch-progress,.gxt-dp-axis,.gxt-dp-counter,.gxt-fs-num,
  .gxt-cv-word,.gxt-mi-back,.gxt-mi-cursor,.gxt-cs-focus,.gxt-sh-name,
  .gxt-vr-side{display:none}
  .gxt-ch-scene,.gxt-cv-stage,.gxt-fs-track,.gxt-sh-row,.gxt-rb-list,
  .gxt-cs-grid,.gxt-mi-list,.gxt-vr-track{position:relative;display:grid;
    grid-template-columns:1fr;gap:18px;height:auto;min-height:0;width:100%;
    overflow:visible;transform:none!important;border:0;background:none}
  .gxt-vr-window{height:auto;overflow:visible;border:0}
  .gxt-ch-member,.gxt-fs-member,.gxt-dp-member,.gxt-cv-member,.gxt-mi-member,
  .gxt-sh-member,.gxt-ps-member,.gxt-cs-member,.gxt-rb-member,.gxt-vr-member{
    position:relative;inset:auto;top:auto;left:auto;display:block;width:100%;
    height:auto;min-height:0;flex:none;padding:0;margin:0;
    opacity:1!important;visibility:visible!important;pointer-events:auto;
    transform:none!important;filter:none!important;clip-path:none!important;
    border:0;background:none;transition:none;z-index:auto!important}
  .gx-depthseq .gx-stage,.gx-portraitstack .gx-stage{display:grid;
    grid-auto-rows:auto;gap:18px;place-items:stretch;padding-block:10px}
  .gxt-ch-member::after,.gxt-fs-member::after,.gxt-dp-member::after,
  .gxt-sh-member::after,.gxt-ps-member::after,.gxt-cs-member::after,
  .gxt-rb-member::after,.gxt-cv-image::after{display:none}
  .gxt-portrait{height:340px;filter:none!important;transform:none!important}
  .gxt-ch-media{height:340px;clip-path:none;transform:none}
  .gxt-cv-image,.gxt-rb-media,.gxt-vr-media,.gxt-mi-tile{position:relative;
    inset:auto;width:100%;height:auto;opacity:1!important;clip-path:none;
    transform:none!important}
  .gxt-mi-tile{width:86px}
  .gxt-mi-tile .gxt-portrait,.gxt-mi-tile{height:104px}
  .gxt-ch-copy,.gxt-fs-copy,.gxt-dp-copy,.gxt-cv-copy,.gxt-sh-copy,
  .gxt-ps-copy,.gxt-cs-label,.gxt-rb-copy,.gxt-vr-copy{position:relative;
    inset:auto;width:auto;max-width:none;padding:16px 0 6px;margin:0;
    opacity:1!important;transform:none!important;transition:none}
  .gxt-mi-member{grid-template-columns:86px minmax(0,1fr);padding:16px 0;
    border-right:0;border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
    opacity:1}
  .gxt-name{font-size:1.5rem!important}
  .gxt-bio{max-height:none!important;opacity:1!important;overflow:visible;margin-top:6px}
}
/* ---- team skins: reduced-motion still state (same collapse; the animator
   stands down, native document order carries the reading) ---- */
@media (prefers-reduced-motion:reduce){
  .gx-teamchapters,.gx-teamstrip,.gx-depthseq,.gx-coverrelay,.gx-monoindex,
  .gx-shutters,.gx-portraitstack,.gx-contactsheet,.gx-rolebands,.gx-verticalreel{
    min-height:auto;width:100%;margin-left:0}
  .gxt-nav,.gxt-ch-progress,.gxt-dp-axis,.gxt-dp-counter,.gxt-fs-num,
  .gxt-cv-word,.gxt-mi-back,.gxt-mi-cursor,.gxt-cs-focus,.gxt-sh-name,
  .gxt-vr-side{display:none}
  .gxt-ch-scene,.gxt-cv-stage,.gxt-fs-track,.gxt-sh-row,.gxt-rb-list,
  .gxt-cs-grid,.gxt-mi-list,.gxt-vr-track{position:relative;display:grid;
    grid-template-columns:1fr;gap:18px;height:auto;min-height:0;width:100%;
    overflow:visible;transform:none!important;border:0;background:none}
  .gxt-vr-window{height:auto;overflow:visible;border:0}
  .gxt-ch-member,.gxt-fs-member,.gxt-dp-member,.gxt-cv-member,.gxt-mi-member,
  .gxt-sh-member,.gxt-ps-member,.gxt-cs-member,.gxt-rb-member,.gxt-vr-member{
    position:relative;inset:auto;top:auto;left:auto;display:block;width:100%;
    height:auto;min-height:0;flex:none;padding:0;margin:0;
    opacity:1!important;visibility:visible!important;pointer-events:auto;
    transform:none!important;filter:none!important;clip-path:none!important;
    border:0;background:none;transition:none;z-index:auto!important}
  .gx-depthseq .gx-stage,.gx-portraitstack .gx-stage{display:grid;
    grid-auto-rows:auto;gap:18px;place-items:stretch;padding-block:10px}
  .gxt-ch-member::after,.gxt-fs-member::after,.gxt-dp-member::after,
  .gxt-sh-member::after,.gxt-ps-member::after,.gxt-cs-member::after,
  .gxt-rb-member::after,.gxt-cv-image::after{display:none}
  .gxt-portrait{height:340px;filter:none!important;transform:none!important}
  .gxt-ch-media{height:340px;clip-path:none;transform:none}
  .gxt-cv-image,.gxt-rb-media,.gxt-vr-media,.gxt-mi-tile{position:relative;
    inset:auto;width:100%;height:auto;opacity:1!important;clip-path:none;
    transform:none!important}
  .gxt-mi-tile{width:86px}
  .gxt-mi-tile .gxt-portrait,.gxt-mi-tile{height:104px}
  .gxt-ch-copy,.gxt-fs-copy,.gxt-dp-copy,.gxt-cv-copy,.gxt-sh-copy,
  .gxt-ps-copy,.gxt-cs-label,.gxt-rb-copy,.gxt-vr-copy{position:relative;
    inset:auto;width:auto;max-width:none;padding:16px 0 6px;margin:0;
    opacity:1!important;transform:none!important;transition:none}
  .gxt-mi-member{grid-template-columns:86px minmax(0,1fr);padding:16px 0;
    border-right:0;border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
    opacity:1}
  .gxt-name{font-size:1.5rem!important}
  .gxt-bio{max-height:none!important;opacity:1!important;overflow:visible;margin-top:6px}
}

/* ---- half-column host carve-out (specimen spec-flow, P5 precedent) ----
   The 100vw breakout centers a scene on its PARENT (calc(50% - 50vw)); the
   specimen track's spec-flow column is not viewport-centered, so a blind
   breakout shifts every takeover half a viewport left (screenshot-caught:
   mountain/road copy clipped at the viewport edge). P5 shipped its specimen
   grafts column-scoped — scenes stay inside the column here, with compact
   layouts where a fixed column floor would not fit ~50vw. */
.spec-flow .gx-scene{width:100%;margin-left:0}
.spec-flow .gxp-chap-step{grid-template-columns:1fr;padding:clamp(40px,6vh,70px) clamp(20px,4vw,44px) 120px;align-content:center}
.spec-flow .gxp-chap-art{display:none}
.spec-flow .gxp-wheel-layout{grid-template-columns:1fr;inset:5vh clamp(18px,3vw,40px) 8vh;align-content:start;gap:10px}
.spec-flow .gxp-wheel-visual{width:min(56%,330px)}
.spec-flow .gxp-wheel-node{width:56px}
.spec-flow .gxp-wheel-node strong{display:none}
.spec-flow .gxp-wheel-stack{height:min(38vh,360px)}
.spec-flow .gxp-wheel-copy h3{font-size:clamp(1.7rem,2.6vw,2.6rem)}
.spec-flow .gxp-mtn-stack{width:min(60%,430px)}
.spec-flow .gxp-mtn-copy h3{font-size:clamp(1.8rem,2.8vw,2.8rem)}
.spec-flow .gxp-road-copy h3{font-size:clamp(1.6rem,2.4vw,2.4rem)}
.spec-flow .gxp-road-copy p{max-width:380px}
.spec-flow .gxp-stair-copy{left:24px;width:min(52%,420px)}
.spec-flow .gxp-stair-copy h3{font-size:clamp(1.8rem,2.8vw,2.8rem)}
.spec-flow .gxp-stair-platform{left:62%;width:92%}
.spec-flow .gx-accordion .gx-stage{gap:8px;padding:12px 10px}
.spec-flow .gx-accordion-content{min-width:0;right:16px;left:16px;bottom:16px}
.spec-flow .gx-accordion-content h3{font-size:clamp(1.5rem,2.6vw,2.6rem)}
.spec-flow .gxh-layout{grid-template-columns:minmax(230px,290px) 1fr;gap:16px;width:calc(100% - 20px)}
.spec-flow .gxh-frame{inset:20px 10px 20px 52px}
.spec-flow .gxh-axis{left:-42px;font-size:.56rem}
.spec-flow .gxh-bands{gap:5px}
.spec-flow .gxh-band-day{font-size:.56rem}
.spec-flow .gxh-window b{font-size:.54rem}
.spec-flow .gxh-hours-list li{padding:6px 8px;font-size:.85rem}
.spec-flow .gxh-address{font-size:.85rem;margin-bottom:12px}
.spec-flow .gxh-status{font-size:.72rem;margin-bottom:12px}
@media (max-width:900px){
  .spec-flow .gxp-chap-art{display:block}
  .spec-flow .gxh-layout{grid-template-columns:1fr}
}


/* ---- skin: flipcards (cards-v8 template-03 "Sequential flip cards") ----
   STACK family, takeover. Donor identity kept: a two-column wall of cards whose
   inner plate turns on rotateY in READING ORDER as the scroll advances, front
   face = the pitch, reverse = the detail list.

   PORT NOTES
   - Donor shipped 4 items in one static 2-col grid; the engine hands this skin
     5-8. The grid therefore WALKS: a two-row window (mask-faded top/bottom)
     with the deck stepping down as the focus ladder descends. The step is pure
     CSS off --scene-progress (the frame loop already writes it on the scene
     element), so nothing but the cards themselves is touched per frame; the one
     per-scene constant (--gxf-rowmax) is a STATIC inline value the renderer
     derives from the item count.
   - Donor's inline per-item accent hexes become a token cycle: the renderer
     writes --gxf-accent per card from the shared stepAccent() ladder, and every
     accented part of the card (index numeral, kicker, list rule, corner bloom,
     reverse tint, single-face head rail) reads it. Zero raw hex outside
     shadows/mask stencils.
   - The donor's opaque reverse slab (#11131d under an accent wash) is gone: the
     reverse is the shared .gx-glass panel with a token wash over it, and the
     stage/sticky/scene paint NOTHING — the pack's own GL field shows through.
   - ONE per-frame value drives the whole deck: --flip-t, the 0..1 turn amount
     of that card. A card WITH item.features spends it on the donor hinge
     (rotateY 0->180deg); a card WITHOUT features (the mock emits none on ~1 in
     3 stack items, so this is a common designed state, not an edge case) is
     single-face and spends the same value on a head rail that wipes across and
     brightens, an opening corner bloom and an accent hairline. Once the
     playhead has passed, EVERY card is accent-marked — the two paths read as
     one system side by side in the same deck.
   - Nothing in this skin sets z-index in a 3D context: the deck is flattened
     (.gx-stage is pinned back to transform-style:flat / perspective:none) and
     grid cards never overlap — the largest RELATIVE y-shift between adjacent
     rows is 6px against a >=14px gutter — so there is no z-order to fight.
   - Depth read: opacity falls below the .55 reading floor past ONE row of
     depth, and blur only starts at 1.15 rows (opacity is already .54 there), so
     dimmed glyph structure is never smeared while still readable.
   - Donor's glowing-dot list bullet is replaced by a short accent rule
     (de-AI decree); the card's ONE numbering format is the front-face
     .gx-index numeral — no counters anywhere else in the scene. */
.gx-flipcards{min-height:min(260svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw);
  --gxf-card-h:clamp(238px,30svh,320px);
  --gxf-gap:clamp(14px,1.6vw,22px);
  --gxf-pad:clamp(16px,2.4vh,30px);
  --gxf-step:calc(var(--gxf-card-h) + var(--gxf-gap))}
/* the window: exactly two rows plus a fade gutter, and the gutter is the mask
   ramp — so a row at rest is never faded, only a row travelling out of frame */
.gx-flipcards .gx-stage{display:block;width:min(1180px,86vw);margin-inline:auto;
  padding:var(--gxf-pad) 0;min-height:0;
  height:min(calc(var(--gxf-card-h) * 2 + var(--gxf-gap) + var(--gxf-pad) * 2),calc(100svh - 130px));
  overflow:hidden;perspective:none;transform-style:flat;
  -webkit-mask-image:linear-gradient(180deg,transparent 0,black var(--gxf-pad),black calc(100% - var(--gxf-pad)),transparent 100%);
  mask-image:linear-gradient(180deg,transparent 0,black var(--gxf-pad),black calc(100% - var(--gxf-pad)),transparent 100%)}
/* THE WALK. --gxf-rowmax = rows - 2 = the number of row-steps the deck can take
   before its last row sits in the lower window slot, and the deck spends ALL of
   them across the whole scroll: offset = progress * rowmax, saturating only at
   progress 1. The animator's focus ladder is the same shape one row taller
   (progress * (rows - 1)), so the focused row's position inside the two-row
   window is progress * step — it enters in the upper slot, lands in the lower
   slot at progress 1, and is never outside the window in between. Nothing here
   freezes before the section ends. */
.gx-flipcards .gx-flipcards-deck{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));
  grid-auto-rows:var(--gxf-card-h);gap:var(--gxf-gap);align-content:start;
  transform:translate3d(0,calc(-1 * var(--scene-progress,0) * var(--gxf-rowmax,0) * var(--gxf-step)),0);
  will-change:transform}
.gx-flipcards .gx-flipcards-deck > .gx-flipcards-card:last-child:nth-child(odd){grid-column:1 / -1}
.gx-flipcards .gx-flipcards-card{position:relative;min-height:var(--gxf-card-h);perspective:1200px;
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1));
  transform:translateY(var(--card-y,0px)) scale(var(--card-scale,1));
  will-change:transform,opacity,filter}
.gx-flipcards .gx-flipcards-inner{position:relative;width:100%;height:100%;
  min-height:var(--gxf-card-h);transform:rotateY(calc(var(--flip-t,0) * 180deg));
  transform-style:preserve-3d;will-change:transform}
.gx-flipcards .gx-flipcards-face{position:absolute;inset:0;display:flex;flex-direction:column;
  padding:clamp(20px,1.9vw,30px);overflow:hidden;
  border:1px solid var(--gx-line);border-radius:24px;
  backface-visibility:hidden;-webkit-backface-visibility:hidden}
.gx-flipcards .gx-flipcards-face::after{position:absolute;right:-45px;bottom:-55px;width:170px;
  aspect-ratio:1;border-radius:50%;content:"";pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--gxf-accent,var(--c-accent)) 28%,transparent),transparent 68%)}
.gx-flipcards .gx-flipcards-back{transform:rotateY(180deg);
  background:
    linear-gradient(145deg,color-mix(in srgb,var(--gxf-accent,var(--c-accent)) 20%,transparent),color-mix(in srgb,var(--gxf-accent,var(--c-accent)) 5%,transparent)),
    linear-gradient(145deg,var(--gx-panel-strong),var(--gx-panel-deep))}
.gx-flipcards .gx-flipcards-face .gx-kicker{position:relative;z-index:2;margin-bottom:10px;
  color:var(--gxf-accent,var(--c-accent))}
.gx-flipcards .gx-flipcards-face h3{position:relative;z-index:2;margin:0 0 8px;
  font-size:clamp(1.2rem,1.5vw,1.68rem);line-height:1.06}
.gx-flipcards .gx-flipcards-front h3{margin-top:auto}
.gx-flipcards .gx-flipcards-face p{position:relative;z-index:2;
  font-size:clamp(.88rem,.95vw,1rem);line-height:1.5}
.gx-flipcards .gx-flipcards-front .gx-index{position:absolute;top:clamp(14px,1.4vw,20px);
  right:clamp(18px,1.7vw,26px);z-index:1;
  color:color-mix(in srgb,var(--gxf-accent,var(--c-accent)) 40%,transparent);
  font-family:var(--f-utility);font-size:clamp(2.5rem,3.4vw,4.1rem);font-weight:800;
  letter-spacing:-.08em;line-height:1}
/* the reverse mirrors the front's bottom-anchored block (donor h3{margin-top:auto}) */
.gx-flipcards .gx-flipcards-list{position:relative;z-index:2;display:grid;gap:9px;
  padding:0;margin:auto 0 0;list-style:none}
.gx-flipcards .gx-flipcards-list li{display:flex;gap:10px;align-items:baseline;
  color:color-mix(in srgb,var(--c-ink) 84%,transparent);
  font-size:clamp(.84rem,.92vw,.96rem);line-height:1.35}
.gx-flipcards .gx-flipcards-list li::before{content:"";flex:0 0 auto;width:14px;height:1px;
  transform:translateY(-.3em);background:var(--gxf-accent,var(--c-accent))}

/* ---- the single-face plate (item with no usable item.features) ----
   The hinge is PINNED so a one-faced card can never be caught mid-turn showing
   a blank back — and instead of sitting inert while its row-mate turns, it
   spends the same --flip-t on its own beat: a head rail that wipes left to
   right and brightens, a corner bloom that opens, and a hairline that takes the
   accent. At rest (progress 0, and the state a mis-keyed animator would leave)
   the rail is a short dim tick that declares the variant without shouting. */
.gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-inner{transform:none}
.gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front{
  border-color:color-mix(in srgb,var(--gxf-accent,var(--c-accent)) calc(var(--flip-t,0) * 40%),var(--gx-line))}
.gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::before{position:absolute;
  top:0;left:0;right:0;height:2px;content:"";pointer-events:none;
  transform:scaleX(calc(.32 + var(--flip-t,0) * .68));transform-origin:0 50%;
  background:color-mix(in srgb,var(--gxf-accent,var(--c-accent)) calc(16% + var(--flip-t,0) * 62%),transparent)}
.gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::after{
  opacity:calc(.46 + var(--flip-t,0) * .54);transform:scale(calc(.8 + var(--flip-t,0) * .32))}

/* static collapse — narrow. The wall unfolds: one column, both faces stacked in
   normal flow so the reverse copy is READ, not hidden behind a dead hinge. */
@media (max-width:900px){
  .gx-flipcards{min-height:auto;width:auto;margin-left:0}
  .gx-flipcards .gx-sticky{position:relative;top:auto;min-height:0;padding:0;overflow:visible}
  .gx-flipcards .gx-stage{width:auto;height:auto;min-height:0;overflow:visible;
    -webkit-mask-image:none;mask-image:none}
  .gx-flipcards .gx-flipcards-deck{grid-template-columns:minmax(0,1fr);grid-auto-rows:auto;
    transform:none;will-change:auto}
  .gx-flipcards .gx-flipcards-deck > .gx-flipcards-card:last-child:nth-child(odd){grid-column:auto}
  .gx-flipcards .gx-flipcards-card{min-height:0;opacity:1;filter:none;transform:none;
    perspective:none;will-change:auto}
  .gx-flipcards .gx-flipcards-inner{display:grid;gap:var(--gxf-gap);height:auto;min-height:0;
    transform:none;transform-style:flat;will-change:auto}
  .gx-flipcards .gx-flipcards-face{position:relative;inset:auto;min-height:0;transform:none;
    backface-visibility:visible;-webkit-backface-visibility:visible}
  .gx-flipcards .gx-flipcards-front{padding-right:clamp(66px,20vw,92px)}
  .gx-flipcards .gx-flipcards-front h3{margin-top:0}
  .gx-flipcards .gx-flipcards-list{margin-top:14px}
  .gx-flipcards .gx-flipcards-face::after{width:110px;right:-30px;bottom:-38px}
  .gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::after{transform:none;opacity:1}
  .gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::before{transform:none}
  .gx-flipcards .gx-flipcards-front .gx-index{font-size:clamp(2.2rem,9vw,3.2rem)}
}

/* static collapse — reduced motion. Same unfold, two columns kept: every card
   shows its pitch AND its detail list at rest, nothing pinned, nothing moving.
   The full-bleed breakout is reverted HERE TOO — a desktop reader with
   prefers-reduced-motion is still on a 100vw section otherwise, which shifts
   the unpinned reading stack half a scrollbar off centre against
   html{overflow-x:clip}. */
@media (prefers-reduced-motion:reduce){
  .gx-flipcards{min-height:auto;width:auto;margin-left:0}
  .gx-flipcards .gx-sticky{position:relative;top:auto;min-height:0;padding:0;overflow:visible}
  .gx-flipcards .gx-stage{height:auto;min-height:0;overflow:visible;
    -webkit-mask-image:none;mask-image:none}
  .gx-flipcards .gx-flipcards-deck{grid-auto-rows:auto;transform:none;will-change:auto}
  .gx-flipcards .gx-flipcards-card{min-height:0;opacity:1;filter:none;transform:none;
    perspective:none;will-change:auto}
  .gx-flipcards .gx-flipcards-inner{display:grid;gap:var(--gxf-gap);height:auto;min-height:0;
    transform:none;transform-style:flat;will-change:auto}
  .gx-flipcards .gx-flipcards-face{position:relative;inset:auto;min-height:0;transform:none;
    backface-visibility:visible;-webkit-backface-visibility:visible}
  .gx-flipcards .gx-flipcards-front{padding-right:clamp(72px,7vw,104px)}
  .gx-flipcards .gx-flipcards-front h3{margin-top:0}
  .gx-flipcards .gx-flipcards-list{margin-top:14px}
  .gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::after{transform:none;opacity:1}
  .gx-flipcards .gx-flipcards-card.is-flat .gx-flipcards-front::before{transform:none}
}


/* ---- skin: horizontal (cards-04 "Vertical scroll, horizontal cards") ----
   A filmstrip pulled sideways by VERTICAL scroll. Donor behaviour kept verbatim:
   an absolutely-positioned flex track anchored at the window's centre line,
   translated on X, cards carrying per-card opacity / brightness / saturation /
   translateY / rotateY / scale.

   COUNT-GENERAL TRAVEL, AND THE COUNT IS NOWHERE IN THIS FILE. The donor
   library shipped a 5-item scene; every 5 left in this file is part of a length
   or a percentage. The travel calc needs (n-1)*(w+gap), and that quantity is
   exactly `100% - w` measured against the track's own max-content border box —
   a percentage inside `translate` resolves against the element itself — so the
   CSS alone lands card `--scene-progress * (n-1)` on the window centre for ANY
   n with no item count written anywhere. The animator then overrides --track-x
   with measured offsetLeft geometry (sub-pixel exact, and correct even if a
   host stylesheet changes the card width). A dead or filtered-out animator
   therefore still yields a correctly-framed strip rather than a half-off-screen
   pile — verified at n = 5/6/7/8 and p = 0/.25/.5/.75/1, JS vs pure CSS within
   0.63px worst case (see NOTES.md).

   Ported per invariants: token-only colour (the donor's inline per-item
   --step-accent hexes become the nth-child cycle below, mirroring the
   process-family STEP_ACCENTS sequence), full-bleed breakout, no stage/sticky/
   scene backdrop (the pack's GL field glows through), both static collapses,
   single index numeral per card (donor's glowing-dot kicker bullet and its
   scene-progress readout are de-AI chrome and are gone). */
.gx-horizontal{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw);
  --gx-horizontal-w:clamp(272px,31vw,500px);
  --gx-horizontal-gap:clamp(14px,1.7vw,26px)}
.gx-horizontal .gx-stage{overflow:hidden;perspective:1500px}

/* the window is the donor's overflow frame; its hard clip is replaced by an
   edge mask so the strip dissolves instead of guillotining a card */
.gx-horizontal-window{position:absolute;inset:0;overflow:hidden;
  -webkit-mask-image:linear-gradient(90deg,transparent,rgba(0,0,0,1) 11%,rgba(0,0,0,1) 89%,transparent);
  mask-image:linear-gradient(90deg,transparent,rgba(0,0,0,1) 11%,rgba(0,0,0,1) 89%,transparent)}
/* filmstrip sprocket ticks — pure CSS lateral parallax off --scene-progress
   (the frame loop already writes it on the scene element), so the scene reads
   as travelling even before a card resolves. Texture, not a telemetry readout. */
.gx-horizontal-window::before{position:absolute;top:50%;left:-12%;z-index:0;
  width:124%;height:clamp(430px,62svh,620px);content:"";pointer-events:none;opacity:.42;
  background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 9%,transparent) 0 1px,transparent 1px 104px);
  transform:translate(calc(var(--scene-progress,0) * -260px),-50%);
  -webkit-mask-image:radial-gradient(ellipse 58% 72% at 50% 50%,rgba(0,0,0,.9),transparent 76%);
  mask-image:radial-gradient(ellipse 58% 72% at 50% 50%,rgba(0,0,0,.9),transparent 76%)}

.gx-horizontal-track{position:absolute;top:50%;left:50%;z-index:1;
  display:flex;align-items:center;gap:var(--gx-horizontal-gap);width:max-content;
  /* CSS-ONLY TRAVEL, WITH NO ITEM COUNT ANYWHERE. Earlier drafts carried the
     donor's 5 as the fallback of an item-count property the renderer had to inline;
     that is the donor constant this port exists to delete, and it mis-frames a
     6/7/8-card strip the moment the inline style is lost. It is not needed at
     all: a percentage inside translate resolves against the element's OWN border
     box, and the track is width:max-content, so 100% IS n*w + (n-1)*gap. Wanted
     travel is  -(progress * (n-1) * (w+gap) + w/2), and (n-1)*(w+gap) == 100% - w.
     Hence: -progress*100% + progress*w - w/2. No n, exact for every count. */
  --gx-horizontal-autox:calc(var(--scene-progress,0) * -100%
    + var(--scene-progress,0) * var(--gx-horizontal-w) - var(--gx-horizontal-w) / 2);
  transform:translate3d(var(--track-x,var(--gx-horizontal-autox)),-50%,0);
  will-change:transform}

.gx-horizontal-card{position:relative;flex:0 0 auto;
  width:var(--gx-horizontal-w);min-height:clamp(430px,62svh,620px);
  padding:clamp(22px,2vw,32px);overflow:hidden;border-radius:30px;
  --gx-horizontal-accent:var(--c-accent);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:perspective(1200px) translateY(var(--card-y,0px))
    rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-origin:50% 50%;z-index:var(--card-order,1);
  will-change:transform,opacity,filter}
/* donor per-item accent hexes -> token-derived CYCLE (STEP_ACCENTS sequence) */
.gx-horizontal-card:nth-child(6n+2){--gx-horizontal-accent:color-mix(in srgb,var(--c-accent) 62%,var(--c-ink))}
.gx-horizontal-card:nth-child(6n+3){--gx-horizontal-accent:var(--c-glow2)}
.gx-horizontal-card:nth-child(6n+4){--gx-horizontal-accent:color-mix(in srgb,var(--c-accent) 55%,var(--c-glow2))}
.gx-horizontal-card:nth-child(6n+5){--gx-horizontal-accent:var(--c-glow3)}
.gx-horizontal-card:nth-child(6n+6){--gx-horizontal-accent:color-mix(in srgb,var(--c-glow2) 60%,var(--c-ink))}
.gx-horizontal-card::before{position:absolute;top:0;left:0;z-index:4;width:100%;height:3px;
  content:"";pointer-events:none;
  background:linear-gradient(90deg,var(--gx-horizontal-accent),transparent 66%)}
.gx-horizontal-card::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(180deg,transparent 26%,color-mix(in srgb,var(--c-dominant) 92%,transparent) 86%),
    radial-gradient(circle at 18% 16%,color-mix(in srgb,var(--c-accent) 22%,transparent),transparent 30%),
    radial-gradient(circle at 84% 78%,color-mix(in srgb,var(--c-glow2) 15%,transparent),transparent 36%)}
/* is-active APPENDS the accent ring; it must re-state the shared .gx-glass list
   (var(--gx-shadow) + the inset top hairline) because box-shadow does not merge —
   dropping the inset would strip the highlight from the one card being read */
.gx-horizontal-card.is-active{box-shadow:var(--gx-shadow),
  inset 0 1px 0 var(--gx-line),
  0 0 0 1px color-mix(in srgb,var(--gx-horizontal-accent) 42%,transparent)}

.gx-horizontal-content{position:absolute;z-index:3;
  right:clamp(22px,2vw,32px);bottom:clamp(22px,2vw,32px);left:clamp(22px,2vw,32px);
  -webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-horizontal-card h3{max-width:340px;margin-bottom:10px;
  font-size:clamp(1.5rem,2.3vw,2.35rem);line-height:1.02}
.gx-horizontal-card p{max-width:340px;font-size:clamp(.9rem,1vw,1.02rem);line-height:1.55}
.gx-horizontal-card .gx-index{position:absolute;z-index:4;
  top:clamp(14px,1.4vw,22px);right:clamp(18px,1.8vw,28px);
  color:color-mix(in srgb,var(--gx-horizontal-accent) 34%,var(--gx-line-strong));
  font-family:var(--f-utility);font-size:clamp(3.4rem,5vw,5.4rem);font-weight:800;
  letter-spacing:-.08em;line-height:1}
.gx-horizontal-cta{margin-top:14px;font-size:.82rem}

/* ---- static collapse <=900px: the strip becomes a readable vertical list
       (the donor's own <=760px fallback, rebased) ---- */
@media (max-width:900px){
  .gx-horizontal{min-height:auto;width:100%;margin-left:0}
  .gx-horizontal .gx-stage{overflow:visible;perspective:none}
  .gx-horizontal-window{position:relative;inset:auto;min-height:auto;overflow:visible;
    -webkit-mask-image:none;mask-image:none}
  .gx-horizontal-window::before{display:none}
  .gx-horizontal-track{position:relative;top:auto;left:auto;display:grid;gap:14px;
    width:100%;transform:none!important}
  .gx-horizontal-card{width:100%;min-height:330px;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-horizontal-card h3{max-width:none;font-size:1.9rem}
  .gx-horizontal-card p{max-width:60ch}
  .gx-horizontal-content{position:relative;right:auto;bottom:auto;left:auto;margin-top:92px}
  .gx-horizontal-card .gx-index{font-size:4.2rem}
}

/* ---- reduced motion: the same still composition, no parallax, no travel ---- */
@media (prefers-reduced-motion:reduce){
  .gx-horizontal{min-height:auto;width:100%;margin-left:0}
  .gx-horizontal .gx-stage{overflow:visible;perspective:none}
  .gx-horizontal-window{position:relative;inset:auto;min-height:auto;overflow:visible;
    -webkit-mask-image:none;mask-image:none}
  .gx-horizontal-window::before{display:none}
  .gx-horizontal-track{position:relative;top:auto;left:auto;display:grid;gap:14px;
    width:100%;transform:none!important}
  .gx-horizontal-card{width:100%;min-height:330px;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-horizontal-card h3{max-width:none;font-size:1.9rem}
  .gx-horizontal-card p{max-width:60ch}
  .gx-horizontal-content{position:relative;right:auto;bottom:auto;left:auto;margin-top:92px}
  .gx-horizontal-card .gx-index{font-size:4.2rem}
}


/* ---- skin: parallax (cards-v8 template-05 "Layered scroll parallax") ----
   STACK family, takeover container, media policy none (fallback numeral).

   DONOR ADAPTATION (MASTER-PLAN 6.4): the donor stage holds three decorative
   parallax layers (ambient wash / organic blob / floating cube) plus THREE
   feature cards at fixed absolute positions. `sticky_stack` is floored at 6 by
   the schema, so this skin always receives 6-8 items and a 3-position layout
   would collapse. The adaptation keeps the donor's two motion ideas verbatim
   and generalises the composition:
     1. the decorative field still drifts at three different rates — but off
        --scene-progress in pure CSS (the frame loop already writes it on the
        scene element), so the animator writes NOTHING for it;
     2. every ITEM is now a layer of the same parallax field. Its depth is
        --layer-depth = index / (count - 1) over the REAL count, written once by
        the renderer. Depth drives (a) the resting receding ladder below, which
        is what the scene looks like before the first frame and with no JS, and
        (b) the per-layer travel RATE in the animator (.70 -> 1.28 per slot of
        scroll) — differing rates per depth IS the donor's parallax signature.

   NO-COLLISION AT 8 LAYERS: positions are per-SLOT, never per-index, so the
   geometry is identical at 6, 7 and 8 items — only the number of far, parked
   layers changes. One slot of separation is x = xStep * rate, y = -.63 * xStep
   * rate and a 520px z drop, and the copy block's own alpha is tied to
   --card-focus, so a neighbour's title never competes with the focused card's.
   Separation is by OCCLUSION, not by lateral clearance: --card-z is a function
   of |delta| alone and strictly decreasing in it, so the plate with the
   smallest |delta| — which is exactly the one the animator marks .is-active —
   always carries the front-most plane. The full argument, including why the
   yaw coefficient is solved (4.41deg/slot) rather than styled, is in
   animator.js; do not change --card-rx, --card-ry or the .63 y/x ratio
   independently of each other, they are one interlocked constraint.

   DONOR BOXES STRIPPED (box checklist): the donor's `.gx-parallax-stage` was a
   filled, bordered, 36px-radius slab — the fill is gone and the frame survives
   only as a mask-faded hairline on .gx-stage::before, so the pack's own GL
   field shows through. The donor's fixed 285/370px card height cap is replaced
   by clamp() with an svh term. The donor's inline per-item accent hexes become
   the token-derived --plate-accent cycle below. ---- */
.gx-parallax{min-height:min(265svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
/* GL WASH BUDGET — the whole skin, not just this declaration. engine.css
   .gx-scene::before spends 7% of the 15% composite ceiling on EVERY takeover,
   so the skin's own share is 8%. This skin has TWO washes, not one: the sticky
   radial below AND the donor's decorative field (ambient/orb/slab), and where
   they overlap they COMPOSITE. Measured in Chromium — accent family forced to
   white over a black base, plates hidden, page really scrolled through the
   pinned section at 1400x900 and 1024x768 — the peak alphas are:
       engine .gx-scene::before alone .. 7.06%
       this sticky radial alone ....... 5.88%   (declared 6%)
       decorative field alone ......... 6.67-7.06%
       SKIN ONLY (sticky + field) ..... 7.06%   <= the 8% budget
       FULL COMPOSITE ................. 9.80-10.59%  <= the 15% ceiling
   The previous cut declared 11% here and left the field's radials at 12-16%,
   which measured 20.0-20.8% composite — over the ceiling, and invisible to
   skin-lint because that rule reads one declaration at a time. Do not raise any
   of these without re-running the composite measurement. */
.gx-parallax .gx-sticky{
  background:radial-gradient(circle at 68% 24%,color-mix(in srgb,var(--c-glow2) 6%,transparent),transparent 34rem)}
.gx-parallax .gx-stage{display:grid;place-items:center;perspective:1700px}
/* de-boxed donor frame: border only, edges masked away (runway/wave precedent) */
.gx-parallax .gx-stage::before{position:absolute;inset:0;z-index:0;content:"";pointer-events:none;
  border:1px solid var(--gx-line);border-radius:clamp(20px,2.4vw,36px);
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 13%,#000 87%,transparent);
  mask-image:linear-gradient(180deg,transparent,#000 13%,#000 87%,transparent)}

/* ---- the decorative field: donor ambient / blob / cube, three drift rates,
   all of it CSS off --scene-progress (zero per-frame property writes) ----

   ARMATURE PARKS BEHIND EVERY CARD. `.gx-stage` is transform-style:preserve-3d
   (engine.css), so inside it paint order is 3D DEPTH and z-index is ignored —
   the `z-index:0` this rule used to carry bought nothing, and at high
   --scene-progress the orb (drifting +132px right, at translateZ(20px)) and the
   slab (drifting -154px left, at translateZ(120px)) crossed IN FRONT of the
   focused plate on mid-width stages.

   THE NUMBER THAT MATTERS IS THE CORNER, NOT THE CENTRE. An earlier version of
   this comment parked the field at translateZ(-1900px) and called it "behind
   the deepest a plate ever reaches (-1768px)". -1768px is the deepest plate
   CENTRE (3.4-slot clamp x -520/slot). A parked plate is rotated
   rotateX(-23.8deg) / rotateY(14.99deg) and scaled .898, which swings its far
   corner another 130-143px back: measured in Chromium across 6,416 frames at
   n = 5,6,7,8 and four viewports, the deepest CORNER any plate reaches is
   -1911.2px (worst case 1600x1200, where the plate is at its 450px min-height
   cap) — which was BEHIND the field's slab child (-1780px) and orb child
   (-1880px). The claim was wrong and the geometry was wrong with it.

   Fixed by parking the field at translateZ(-2200px) — its nearest child, the
   slab at +120px relative, therefore sits at -2080px, 168.8px behind the
   deepest plate corner (a plate would have to grow past 1,380px TALL to eat
   that headroom, inside a stage capped at 780px) — and scaling by
   1 + 2200/1700 = 2.294118, the exact inverse of
   the perspective divide at that depth. The compensation is EXACT for the
   field's own plane, so drift amplitude, border weight and blur radii all land
   back where they were. The donor's -80/+20/+120 relative depth stack is kept
   verbatim on the children, and each child sits slightly off its own inverse:
   measured apparent scales are .980 / 1.005 / 1.032 where the donor's were
   .955 / 1.012 / 1.076, i.e. the three planes still read as three planes but
   their depth spread is compressed from 12.6% to 5.4%, and the ambient wash
   shifts <=27px. That is the price of parking the armature behind the cards,
   and it is paid on a set of soft decorative gradients — not on anything
   anyone reads. */
.gx-parallax-field{position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d;
  transform:translateZ(-2200px) scale(2.294118)}
.gx-parallax-drift{position:absolute;display:block;will-change:transform}
.gx-parallax-ambient{inset:-18%;
  background:
    radial-gradient(circle at 22% 20%,color-mix(in srgb,var(--c-glow2) 5%,transparent),transparent 26%),
    radial-gradient(circle at 78% 38%,color-mix(in srgb,var(--c-glow3) 4%,transparent),transparent 23%),
    radial-gradient(circle at 55% 86%,color-mix(in srgb,var(--c-accent) 5%,transparent),transparent 29%);
  transform:
    translate3d(calc(var(--scene-progress,0) * -72px + 36px),calc(var(--scene-progress,0) * 60px - 30px),-80px)
    scale(calc(1 + var(--scene-progress,0) * .1))}
/* donor blob — its hand-tuned organic radius is kept verbatim (donor identity) */
.gx-parallax-orb{top:12%;left:7%;width:clamp(180px,23vw,300px);aspect-ratio:1;
  border:1px solid var(--gx-line-strong);
  border-radius:42% 58% 60% 40% / 44% 42% 58% 56%;
  background:color-mix(in srgb,var(--c-ink) 4%,transparent);
  /* the donor's inset rim light was rgba(255,255,255,.05) — a WHITE shadow is a
     GLOW, i.e. a colour choice, and it is invisible on a light pack. Tokenised. */
  box-shadow:inset 0 0 55px color-mix(in srgb,var(--c-glow2) 5%,transparent),0 30px 85px rgba(0,0,0,.24);
  transform:
    translate3d(calc(var(--scene-progress,0) * 132px),calc(var(--scene-progress,0) * -98px),20px)
    rotate(calc(var(--scene-progress,0) * 26deg))}
/* donor cube — the fastest, nearest layer */
.gx-parallax-slab{right:11%;bottom:16%;width:clamp(84px,9vw,124px);aspect-ratio:1;
  border:1px solid var(--gx-line-strong);border-radius:26px;
  background:linear-gradient(145deg,color-mix(in srgb,var(--c-ink) 12%,transparent),color-mix(in srgb,var(--c-glow2) 5%,transparent));
  box-shadow:0 28px 75px rgba(0,0,0,.3);
  transform:
    translate3d(calc(var(--scene-progress,0) * -154px + 62px),calc(var(--scene-progress,0) * 120px - 52px),120px)
    rotate(calc(-14deg + var(--scene-progress,0) * 54deg))}

/* ---- the item layers ----
   No z-index: this is a preserve-3d stage, so the only paint order is --card-z,
   which the animator keeps negative for every non-focused layer. ---- */
.gx-parallax-plate{position:absolute;top:50%;left:50%;
  display:flex;flex-direction:column;
  width:min(580px,52vw);min-height:clamp(330px,42svh,450px);
  padding:clamp(26px,2.4vw,40px);border-radius:30px;
  --plate-accent:var(--c-accent);
  --layer-depth:0;--layer-index:0;
  /* DEAD-SCENE FLOOR — the composition before the first frame, with no JS, and
     the one a page shows if the ANIMATORS key is ever mis-keyed (nothing in the
     gate set opens engine.js, MASTER-PLAN §2.1). The first cut spread 8 plates
     over 200px of x / 150px of y — a 28px step under a 580px plate, i.e. a
     centred pile of colliding headlines. This is no longer a decorative guess:
     it is the ANIMATOR'S progress-0 FRAME reproduced in calc(), with
     --layer-index (written once by the renderer) standing in for the slot delta
     that pos=0 produces. Layer 0 lands centred at opacity 1 / focus 1, layer 1
     at .800 fully occluded behind it, layer 2 at .160, layers 3+ at the .050
     floor — the same invariant-8 falloff the animated state obeys. It is also
     the exact frame the animator writes on its first tick, so there is no jump.
     (The one approximation: the animator's xStep is Math.min(252, stageW*.17)
     off the measured stage; clamp(120px,17vw,252px) is the same curve against
     the viewport, and the stage of a full-bleed takeover IS 100vw.)
     --gx-xstep exists so the y step stays DERIVED from the x step at the .63
     ratio the depth contract in animator.js is solved against. */
  --gx-xstep:clamp(120px,17vw,252px);
  --gx-rate:calc(.7 + var(--layer-depth) * .58);
  --card-x:calc(var(--layer-index) * var(--gx-xstep) * var(--gx-rate));
  --card-y:calc(var(--layer-index) * var(--gx-xstep) * -.63 * var(--gx-rate));
  --card-z:calc(min(var(--layer-index),3.4) * -520px);
  --card-rx:calc(min(var(--layer-index),3.4) * -7deg);
  --card-ry:calc(min(var(--layer-index),3.4) * 4.41deg);
  --card-scale:max(.84,1 - var(--layer-index) * .03);
  --card-opacity:max(.05,1 - var(--layer-index) * .2 - max(0,var(--layer-index) - 1) * .44);
  --card-blur:min(6px,max(0px,(var(--layer-index) - 1.4) * 2px));
  --card-focus:max(0,1 - var(--layer-index));
  --card-brightness:calc(.6 + var(--card-focus) * .45);
  --card-saturation:calc(.7 + var(--card-focus) * .34);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-style:preserve-3d;will-change:transform,opacity,filter}
/* donor per-item accent hexes -> token-derived cycle */
.gx-parallax-plate:nth-of-type(4n+2){--plate-accent:color-mix(in srgb,var(--c-accent) 55%,var(--c-glow2))}
.gx-parallax-plate:nth-of-type(4n+3){--plate-accent:var(--c-glow2)}
.gx-parallax-plate:nth-of-type(4n+4){--plate-accent:color-mix(in srgb,var(--c-glow3) 68%,var(--c-accent))}
.gx-parallax-plate::before{position:absolute;top:0;right:clamp(26px,2.4vw,40px);left:clamp(26px,2.4vw,40px);
  height:1px;content:"";pointer-events:none;
  opacity:calc(.22 + var(--card-focus,1) * .62);
  background:linear-gradient(90deg,var(--plate-accent),transparent 74%)}
.gx-parallax-plate.is-active{
  box-shadow:0 44px 116px -34px rgba(0,0,0,.72),inset 0 1px 0 var(--gx-line-strong)}
.gx-parallax-copy{position:relative;z-index:1;margin-top:auto}
.gx-parallax-plate .gx-index{position:absolute;top:clamp(16px,1.7vw,26px);right:clamp(22px,2.2vw,36px);
  color:color-mix(in srgb,var(--plate-accent) 32%,transparent);
  font-family:var(--f-utility);font-size:clamp(3.2rem,5.8vw,6rem);font-weight:800;
  letter-spacing:-.08em;line-height:1}
.gx-parallax-plate .gx-kicker{margin-bottom:10px;color:var(--plate-accent)}
.gx-parallax-plate h3{max-width:min(84%,460px);font-size:clamp(1.7rem,2.7vw,2.75rem)}
/* copy alpha rides the focus so an out-of-focus layer never competes for
   reading attention with the layer in front of it */
.gx-parallax-plate p{max-width:min(90%,490px);font-size:clamp(.94rem,1.1vw,1.06rem);line-height:1.58;
  opacity:calc(.3 + var(--card-focus,1) * .7)}

/* ---- parallax: static collapse <=900px — breakout reverted, scene unpinned,
   the decorative drift field dropped, every layer flattened into a readable
   single-column reading stack at full opacity ---- */
@media (max-width:900px){
  .gx-parallax{min-height:auto;width:100%;margin-left:0}
  .gx-parallax .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;
    overflow:visible;background:none}
  .gx-parallax .gx-stage{display:grid;gap:14px;min-height:auto;padding:0 14px;
    overflow:visible;perspective:none;place-items:stretch}
  .gx-parallax .gx-stage::before{display:none}
  /* the depth-park transform is a 3D compensation pair; with perspective:none
     above, the scale would no longer be divided back down. Both go. */
  .gx-parallax-field{display:none;transform:none}
  .gx-parallax-plate{position:relative;top:auto;left:auto;width:100%;min-height:280px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-parallax-plate::before{opacity:.6}
  .gx-parallax-plate .gx-index{font-size:3.2rem}
  .gx-parallax-plate h3{max-width:100%;font-size:clamp(1.6rem,7vw,2.3rem)}
  .gx-parallax-plate p{max-width:100%;opacity:1}
}

/* ---- parallax: reduced-motion still state — same flattened reading stack;
   the ambient wash stays as ONE frozen parked frame (a still gradient is not
   motion, sunline precedent) while the two moving donor shapes are dropped ---- */
@media (prefers-reduced-motion:reduce){
  .gx-parallax{min-height:auto;width:100%;margin-left:0}
  .gx-parallax .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 34px;
    overflow:visible;background:none}
  .gx-parallax .gx-stage{display:grid;gap:16px;min-height:auto;padding:0 14px;
    overflow:visible;perspective:none;place-items:stretch}
  .gx-parallax .gx-stage::before{display:none}
  /* transform:none is REQUIRED here, not tidiness: translateZ(-2200px) is only
     harmless because the 3D stage divides it back down by scale(2.294118), and
     this collapse sets perspective:none. Left in, the ambient wash would render
     2.29x oversized. Verified: ambient renders 1904px = 1400 x 1.36, i.e. the
     unscaled inset:-18% box. */
  .gx-parallax-field{--scene-progress:.42;transform:none}
  .gx-parallax-drift{will-change:auto;transition:none;animation:none}
  .gx-parallax-orb,.gx-parallax-slab{display:none}
  .gx-parallax-plate{position:relative;top:auto;left:auto;width:100%;min-height:280px;flex:none;
    will-change:auto;transition:none;animation:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-parallax-plate::before{opacity:.6}
  .gx-parallax-plate h3{max-width:100%}
  .gx-parallax-plate p{max-width:100%;opacity:1}
}


/* ---- skin: bloom (cards-v8 template-08 "Radial card bloom") ----
   STACK family, takeover container, media none. The donor is a lit core with a
   ring of petal cards around it; the port keeps that composition and throws away
   the donor's FIVE HARDCODED SEATS. The seat angle is 360deg / item count in
   BOTH places it is needed: the renderer writes --gx-bloom-step on the ring
   field (the tick ring), the animator derives every card angle from
   sc.cards.length. 5-8 items all bloom correctly.

   Geometry: a disc lying back from the camera. The animator writes the seat
   position as --card-x / --card-y / --card-z (x = R sin0, y = .30 R cos0 so the
   focus seat sits LOW and near, z = .78 R cos0 so it sits FORWARD), plus
   --card-rz / --card-ry petal lean and the usual opacity/blur/brightness/
   saturation depth cues. The ring radius arrives as --gx-bloom-r on the scene
   element (written only when the stage is resized, never per frame) so the
   decorative rings track the real card orbit instead of guessing.

   Continuous motion that CSS can own reads --scene-progress, which the frame
   loop already writes on the scene element: the core breathes open and the tick
   ring counter-rotates with zero extra per-frame writes.

   Donor chrome dropped by doctrine:
   - the donor stage slab (border + 38px radius + two opaque radial washes over
     a flat ink fill) — the pack's own GL field must show through,
     so the wash survives only as ONE <=15% token radial on .gx-sticky and the
     slab's border/fill is gone. Edges are masked, not boxed.
   - the donor's raw violet core hexes (#151825 / #08090e / #fff / rgba violet)
     -> token-derived accent/ink/dominant mixes.
   - the fixed 350px/420px min-height card caps and the 118px core -> viewport
     clamps (thin-strip-on-a-tall-monitor box-checklist flag).
   - the scene-note / scene-progress-label / scene-number telemetry chrome —
     it reads as AI-made and the numbered dot is already the ONE numbering
     format per card.
   - the donor core label "One system" — invented copy. The core is graphic
     only; nothing in this skin prints a string that is not a spec field. ---- */
.gx-bloom{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
/* The donor's opaque stage slab, reduced to ONE token radial. Cut 11% -> 6%:
   every takeover already gets engine.css's `.gx-scene::before` radial at
   `--c-accent 7%`, and skin-lint only ever inspects ONE declaration at a time,
   so the two washes stacked to ~18% at the centre — over the GL-transparency
   decree's 15% ceiling, invisibly. 7% + 6% = 13% worst case (and the two are
   not concentric: 50%/34% at 31rem vs 50%/47% at 34rem). */
.gx-bloom .gx-sticky{background:radial-gradient(circle at 50% 47%,color-mix(in srgb,var(--c-glow2) 6%,transparent),transparent 34rem)}
.gx-bloom .gx-stage{display:grid;place-items:center;perspective:1500px}

/* ---- the orbit field: three concentric ellipses + a tick ring ----
   --gx-bloom-r is published by the animator (resize only). The fallback keeps
   the field sane for the single frame before the first rAF tick.

   ARMATURE DEPTH. .gx-stage is transform-style:preserve-3d, so paint order here
   is 3D DEPTH and z-index is IGNORED — the old `z-index:0` was inert and the
   ring lines were drawing straight across the body copy of any card sitting
   behind the z=0 plane (measured: cards at --card-z down to -251px painted
   under RINGS at 4 of the 5 progress samples). The whole field is parked at
   translateZ(-420px), below the deepest possible card (-.78 x radius-max 400 =
   -312px), so it can never come in front of a card. scale(1.28) is the exact
   perspective compensation — 1500/(1500+420) = .78125, and 1/.78125 = 1.28 —
   so the field's APPARENT size, ring weight and tick spacing are unchanged. */
.gx-bloom-rings{position:absolute;inset:0;pointer-events:none;
  transform:translateZ(-420px) scale(1.28)}
.gx-bloom-ring{position:absolute;top:50%;left:50%;
  width:calc(var(--gx-bloom-r,300px) * 2 * (.46 + var(--ring-index,0) * .27));
  height:calc(var(--gx-bloom-r,300px) * .6 * (.46 + var(--ring-index,0) * .27));
  border:1px solid var(--gx-line);border-radius:50%;
  transform:translate(-50%,-50%)}
/* one tick per SEAT — the tick spacing is var(--gx-bloom-step), which the
   renderer computes as 360deg / item count. Edges masked, never boxed. */
.gx-bloom-rings::after{content:"";position:absolute;top:50%;left:50%;
  width:calc(var(--gx-bloom-r,300px) * 2.04);aspect-ratio:1;border-radius:50%;
  transform:translate(-50%,-50%) scaleY(.3) rotate(calc(var(--scene-progress,0) * -1turn));
  background:repeating-conic-gradient(from calc(var(--gx-bloom-step,72deg) * -.5),
    color-mix(in srgb,var(--c-ink) 24%,transparent) 0deg .4deg,
    transparent .4deg var(--gx-bloom-step,72deg));
  -webkit-mask-image:radial-gradient(circle,transparent 43%,var(--c-ink) 60%,var(--c-ink) 78%,transparent 100%);
  mask-image:radial-gradient(circle,transparent 43%,var(--c-ink) 60%,var(--c-ink) 78%,transparent 100%)}

/* ---- the core ----
   Graphic only (the donor's "One system" label was invented copy). Breathes
   open on --scene-progress: pure CSS, zero per-frame writes.

   The core is ARMATURE, so it rides on the same -420px plane as the ring field
   and never paints in front of a card. The old `z-index:5` was inert (preserve-3d
   ignores it); the old translateZ(0px) put a filled, glowing disc in the middle
   of the card orbit, in front of every card with cos0 < 0. The breathe scale is
   multiplied by the same 1.28 perspective compensation, so the core's apparent
   diameter and its 72px glow are byte-for-byte what they were at z=0. The core
   sits ON the perspective origin (stage centre), so the compensation is exact. */
.gx-bloom-core{position:absolute;top:50%;left:50%;
  width:clamp(78px,9vw,124px);aspect-ratio:1;border-radius:50%;pointer-events:none;
  border:1px solid var(--gx-line-strong);
  background:radial-gradient(circle at 34% 28%,
    color-mix(in srgb,var(--c-ink) 84%,transparent),
    color-mix(in srgb,var(--c-accent) 80%,transparent) 24%,
    color-mix(in srgb,var(--c-dominant) 90%,transparent) 60%,
    color-mix(in srgb,var(--c-dominant) 97%,transparent));
  box-shadow:0 0 72px color-mix(in srgb,var(--c-accent) 32%,transparent);
  transform:translate(-50%,-50%) translateZ(-420px)
    scale(calc((.74 + var(--scene-progress,0) * .28) * 1.28));
  will-change:transform}
.gx-bloom-core::after{content:"";position:absolute;inset:-42%;border-radius:50%;pointer-events:none;
  background:radial-gradient(circle,color-mix(in srgb,var(--c-accent) 13%,transparent),transparent 68%)}

/* ---- the petals ---- */
.gx-bloom-card{position:absolute;top:50%;left:50%;
  width:clamp(228px,21vw,320px);min-height:clamp(282px,34svh,398px);
  padding:clamp(20px,1.7vw,30px);border-radius:28px;
  display:flex;flex-direction:column;
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateZ(var(--card-rz,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  transform-origin:50% 50%;
  will-change:transform,filter,opacity}
/* The card is deliberately NOT transform-style:preserve-3d. It carries a
   `filter`, which forces flattening and a containing block anyway, so the
   declaration was inert — and leaving it there made the ::after rim's `z-index`
   read as if it might be a 3D-depth write. Inside this flat card z-index is the
   real thing; depth ACROSS cards is --card-z and nothing else. */
/* petal edge: a soft accent rim on the side that faces the core */
.gx-bloom-card::after{position:absolute;inset:0;z-index:-1;content:"";pointer-events:none;
  border-radius:inherit;
  background:radial-gradient(circle at 50% 108%,color-mix(in srgb,var(--c-accent) 18%,transparent),transparent 62%)}
/* DEAD-SCENE FLOOR, CSS half. If the ANIMATORS key is ever mis-keyed, no
   --card-* is ever written and every card falls back to x=y=z=0 / scale 1 /
   opacity 1 — n cards in perfect register, and .gx-glass is only ~92% opaque,
   so 8% of the card below ghosts through the top card's body copy. Defaulting
   every card BUT THE LAST to the deep-seat alpha makes that state one clean
   readable card instead. It has to be the LAST one, not the first: the cards
   are coplanar at z=0 there, so paint order falls back to tree order and the
   last article is the one on top — dimming the others leaves the reader fully
   opaque with the ghosts both dimmed to .05 AND occluded by its own ~92% fill
   (measured residual 0.4%, was 8%). It is also the seat the animator focuses at
   progress 1, so the two resting states agree. Count-general (:nth-last-of-type,
   never a hardcoded seat) and inert the moment the animator runs, because then
   --card-opacity is always defined and the fallback is never consulted. Both
   collapses override with opacity:1!important, so they are unaffected. */
.gx-bloom-card:not(:nth-last-of-type(1)){opacity:var(--card-opacity,.05)}
.gx-bloom-card h3{font-size:clamp(1.32rem,1.75vw,2.05rem);line-height:1.05}
.gx-bloom-card p{margin-top:8px;font-size:clamp(.9rem,1vw,1.02rem);line-height:1.5}
.gx-bloom-cta{margin-top:auto}
/* the ONE numbering format on this card — no kicker counter, no "0N / 0M" */
.gx-bloom-dot{display:grid;width:48px;aspect-ratio:1;margin-bottom:clamp(18px,3.6vh,42px);
  place-items:center;border:1px solid var(--gx-line-strong);border-radius:50%;
  background:color-mix(in srgb,var(--c-accent) 12%,transparent);
  color:var(--c-accent);font-family:var(--f-utility);font-size:.82rem;font-weight:780;
  transition:background-color .3s ease,color .3s ease,border-color .3s ease}
/* is-active is the animator's ONLY class write (the focus seat) */
.gx-bloom-card.is-active .gx-bloom-dot{background:var(--c-accent);border-color:var(--c-accent);
  color:var(--c-on-accent);
  box-shadow:0 0 26px color-mix(in srgb,var(--c-accent) 45%,transparent)}

/* short-desktop compression (temporal/orbital P8 precedent): a ~900px-tall
   desktop has to give the orbit back some room or the petals clip the sticky */
@media (min-width:901px) and (max-height:860px){
  .gx-bloom-card{width:clamp(206px,19vw,278px);min-height:clamp(248px,31svh,326px);border-radius:24px}
  .gx-bloom-dot{width:42px;margin-bottom:clamp(14px,2.6vh,26px)}
  .gx-bloom-card h3{font-size:clamp(1.2rem,1.5vw,1.7rem)}
}
@media (min-width:901px) and (max-width:1180px){
  .gx-bloom-card{width:clamp(200px,26vw,266px);min-height:clamp(250px,32svh,330px)}
}

/* ---- static collapse 1/2: <=900px ----
   Breakout reverted, scene unpinned, orbit chrome retired, every card is a
   full-width readable panel in source order. Never a blank or collapsed pile. */
@media (max-width:900px){
  .gx-bloom{min-height:auto;width:100%;margin-left:0}
  .gx-bloom .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;
    overflow:visible;background:none}
  .gx-bloom .gx-stage{display:grid;gap:14px;min-height:auto;padding:0 14px;
    perspective:none;place-items:stretch;overflow:visible}
  .gx-bloom-rings,.gx-bloom-core{display:none}
  .gx-bloom-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-bloom-card::after{display:none}
  .gx-bloom-dot{width:42px;margin-bottom:14px;transition:none}
  .gx-bloom-card h3{font-size:1.6rem}
}
/* ---- static collapse 2/2: reduced motion ----
   Same resting composition, but this one also has to read on a WIDE desktop
   (reduced-motion is not a width query), so the petals land in an auto-fit
   grid instead of one very long column. */
@media (prefers-reduced-motion:reduce){
  .gx-bloom{min-height:auto;width:100%;margin-left:0}
  .gx-bloom .gx-sticky{position:relative;top:auto;min-height:auto;padding:26px 0 34px;
    overflow:visible;background:none}
  .gx-bloom .gx-stage{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
    gap:16px;min-height:auto;padding:0 16px;perspective:none;place-items:stretch;overflow:visible}
  .gx-bloom-rings,.gx-bloom-core{display:none}
  .gx-bloom-card{position:relative;top:auto;left:auto;width:100%;min-height:0;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-bloom-card::after{display:none}
  .gx-bloom-dot{width:42px;margin-bottom:14px;transition:none}
}


/* ---- skin: panorama (cards-v8 template-10 "Folding card panorama") — STACK
   family, takeover. A folding screen: the panels are hinged at their SHARED
   edges and concertina across the stage; the panel you are reading unfolds to
   face the camera while its neighbours fold away into depth. The animator walks
   the hinge chain and writes only custom properties — every transform below is
   assembled here.

   Port decisions (donor -> engine):
   - THE SLAB IS GONE. The donor's `.gx-panorama-window` was a filled, bordered,
     38px-radius rectangle (linear-gradient + radial, `overflow:hidden`,
     `perspective:1800px`) sitting under the fold — exactly the "platform" bug
     class this project has already paid for twice. Its fill, border and
     rectangular edges are dropped entirely; the perspective moved onto
     `.gx-stage`, and what survives of it is `.gx-panorama-rail`: a 1px
     token-glow horizon line masked to nothing at both ends (no hard edges).
     The pack's own GL field shows straight through the scene.
   - donor `overflow:hidden` on that window is NOT reproduced — it would have
     forced `transform-style:flat` and killed the fold. `.gx-sticky` (shared,
     `overflow:clip`) is the clipper, one level above the 3D context.
   - donor `.gx-card-kicker::before` glowing dot bullet DROPPED (de-AI decree);
     the donor's scene-progress readout / scene-note chrome was already stripped
     at extraction and is not reinstated. One numbering format per panel: the
     shared `.gx-index` numeral, nothing else.
   - donor per-item accent hexes and the `:root` `--gx-c1..c15` bridge block are
     replaced by host tokens via color-mix; the scene declares no custom
     properties outside its own subtree.
   - donor fixed `min-height:480px/545px` and `width:min(430px,64vw)` card cap
     became viewport-relative clamps so the panorama is not a thin strip on a
     1600x1200 monitor.
   - donor `--hinge` (per-item transform-origin) is NOT used: the hinge chain is
     solved in the animator against centre-origin rotation, which keeps the
     shared edges mathematically joined at any panel count. See NOTES.md.
   Continuous motion that CSS can own is owned here: the rail's light rides
   `--scene-progress` (written by the frame loop on the scene element), so it
   costs zero extra per-frame writes. ---- */
.gx-panorama{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-panorama .gx-sticky{padding:10px 0 18px}
.gx-panorama .gx-stage{position:relative;display:block;
  min-height:clamp(560px,calc(100svh - 150px),820px);
  perspective:1800px;perspective-origin:50% 46%;transform-style:preserve-3d}

/* the horizon — all that is left of the donor's window: a masked token hairline
   whose highlight travels on --scene-progress. Never a plane, never an edge.

   DEPTH, NOT z-index (adversarial review, blocker). `.gx-stage` is
   `transform-style:preserve-3d`, so paint order inside it is 3D DEPTH; the old
   `z-index:0` + no translateZ left the rail at z=0 while folded panels sit as
   deep as -214px, i.e. the hairline drew a line straight across their body copy
   (measured: 219-576 rail pixels on opaque card interior on 38 of 40 traced
   frames, counts 5-8 x progress 0/.25/.5/.75/1 x 1440x900 and 1600x1200, worst
   576px at 1600x1200 n=8 p=.75 over panels at opacity .67 and .87). No z-index
   value fixes that — see the panel block below. The rail is armature, so it takes
   a depth BELOW the deepest panel geometry. Deepest panel CORNER =
   z_centre - (w/2)·|sin ry| - (h/2)·|sin rx| = -0.986·w - 13px, i.e. -407px at
   the 400px width cap (worst case n=5, progress .31). Clearance measured, not
   assumed: Chrome's 3D sort still leaks a ~4x2px seam at a folded panel's outer
   silhouette at -460px, and is clean from -480px down, so -700px is used — 293px
   of clearance, ~4x the measured requirement. It stays clean until a pack
   widens `.gx-panorama-card` past ~620px (see NOTES §5).

   Perspective compensation. At `perspective:1800px`, a plane at -700px renders
   at 1800/(1800+700) = 0.72 scale about `perspective-origin:50% 46%`, so the
   geometry below is pre-divided by that factor (×1.3889) to land the rendered
   hairline exactly where the un-shifted one was: full stage width, 79% down.
     left/right  -19.44%  -> rendered width  100.0% of the stage (browser: 1440/1440)
     top          91.83%  -> 46% + 45.83%·0.72 = 79.0% (browser-measured 79.1%)
     height        1.39px -> rendered 1.00px
     box-shadow      36px -> rendered 25.9px (the authored 26px halo)
   All of them are STATIC — no per-frame variable touches a layout property. */
.gx-panorama-rail{position:absolute;left:-19.44%;right:-19.44%;top:91.83%;height:1.39px;
  transform:translateZ(-700px);
  pointer-events:none;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--c-glow2) 30%,transparent),
    color-mix(in srgb,var(--c-accent) 66%,transparent) calc(18% + var(--scene-progress,0) * 64%),
    color-mix(in srgb,var(--c-glow3) 28%,transparent));
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 17%,#000 83%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 17%,#000 83%,transparent);
  box-shadow:0 0 36px color-mix(in srgb,var(--c-accent) 20%,transparent)}

/* the panels. Centre-origin rotation is what the hinge-chain math assumes —
   do not add a transform-origin here.
   NO z-index: these are children of the preserve-3d `.gx-stage`, so their paint
   order is DEPTH. Measured (1600x1200, n=6, progress .25, rail put back at its
   old z=0 geometry): the rail lands on 573 opaque-card-interior pixels at
   z-index none / 0 / -1 / -9999 alike — identical to the pixel, so no z-index
   value can order armature behind a panel. Among the PANELS z-index still
   breaks ties where the hinged planes intersect (their shared edge), which is
   all the old `--card-layer` was buying: restoring it changes 100 px of 1.92M
   at that frame, all of it in the ~1px hinge seam, with the focused panel's
   body identical (0 px) either way. So the old `z-index:var(--card-layer)` and
   the animator's per-frame `--card-layer` write are gone together — one fewer
   custom-property write per card per frame, and no pretence that z-index is
   doing the ordering.
   `transform-style:flat`: the panel is a single plane. The `filter` below
   already forces the used value to `flat` (a filter is a grouping property), so
   this only says out loud what the browser does — but it is what guarantees the
   numeral / copy / crease inside a panel can never be depth-interleaved with a
   NEIGHBOURING panel's plane, which matters here because hinged panels share an
   edge and their planes therefore intersect. */
.gx-panorama-card{position:absolute;top:50%;left:50%;
  --gxp-w:clamp(268px,26vw,400px);
  width:var(--gxp-w);min-height:clamp(430px,56svh,588px);
  padding:clamp(24px,2vw,34px);
  display:flex;flex-direction:column;
  border:1px solid var(--gx-line);border-radius:24px;
  /* ---- DEAD-SCENE FLOOR ----------------------------------------------------
     The composition before the first frame, with JS off, and the one EVERY
     visitor would see permanently if the `ANIMATORS` key were ever mis-keyed —
     a state no gate in this project catches (MASTER-PLAN §2.1: check-catalog
     never opens engine.js, simulate is markup-only, verify-visual's ghost-text
     warning is appended after ok= and cannot fail a page).
     Without it every panel fell back to bare `translate(-50%,-50%)` at
     opacity 1 with `--card-focus` defaulting to 1, i.e. n headlines exactly
     superimposed at stage centre (measured: 8 panels, one identical
     `matrix(1,0,0,1,-182,-270)`).
     This is NOT a decorative guess. It is the animator's own progress-0 frame
     transcribed into calc(), with `--card-i` (the renderer's loop integer,
     written once) standing in for the slot delta that pos=0 produces:
       flat[0]=1, flat[i>=1]=0            -> --card-focus = max(0, 1 - i)
       a_i = ±64°·(1-flat[i])             -> --gxp-fold alternates by :nth-of-type
       chain walk at those angles gives
         cx[0]-ox = 0,  cx[i>=1]-ox = (.280814 + .438371·i)·w   [.438371=cos64°]
         cz[0]-oz = 0,  cz[i>=1]-oz = -.449397·w                [.449397=½sin64°]
     so the panels land JOINED edge-to-edge in a fan — panel 01 square to the
     camera and fully lit, its neighbours folded away at 64°, opacity stepping
     1 / .830 / .200 / .050 down the same invariant-8 ladder the animated state
     obeys. Because it IS frame 0, there is also no jump when the loop starts.
     `--gxp-w` is the same clamp() the width uses, so the chain math sees the
     same panel width the animator measures via offsetWidth.
     Every one of these is overridden by the animator's INLINE write on tick 1. */
  --card-i:0;--gxp-fold:-1;
  --card-focus:max(0,1 - var(--card-i));
  --card-x:calc((min(var(--card-i),1) * .280814 + var(--card-i) * .438371) * var(--gxp-w));
  --card-y:calc((1 - var(--card-focus)) * 13px);
  --card-z:calc(var(--card-focus) * 46px - min(var(--card-i),1) * .449397 * var(--gxp-w));
  --card-ry:calc((1 - var(--card-focus)) * var(--gxp-fold) * 64deg);
  --card-rx:calc((1 - var(--card-focus)) * 2.6deg);
  --card-opacity:max(.05,1 - var(--card-i) * .17 - max(0,var(--card-i) - 1) * .46);
  --card-blur:min(6px,max(0px,(var(--card-i) - 1.5) * 2.4px));
  --card-brightness:calc(.60 + var(--card-focus) * .44);
  --card-saturation:calc(.68 + var(--card-focus) * .35);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateY(var(--card-ry,0deg)) rotateX(var(--card-rx,0deg));
  transform-style:flat;will-change:transform,opacity,filter}
/* Concertina direction for the floor above — the animator's `(i % 2 ? 1 : -1)` —
   plus a SELECTOR-SIDE copy of the slot ordinal.
   `:nth-of-type`, not `:nth-child`: the `.gx-panorama-rail` armature is a
   <span> and the panels are <article>, so type-counting keeps the phase even
   though the rail is the stage's first child. Panel 0 is nth-of-type(1) (odd,
   -1) and its fold is 0° regardless, because `1 - --card-focus` is 0 there.
   Why BOTH these and the renderer's inline `--card-i`: the inline value is
   count-general (it is the loop integer, so it keeps working if maxItems is
   ever raised), but it is also the single point of failure for the whole floor
   — strip the style attribute and every panel takes the rule default
   `--card-i:0` and the pile comes straight back. The ladder below covers the
   registry's declared range (minItems 5 .. maxItems 8) from the stylesheet
   alone, so the dead-scene composition survives a renderer that lost the
   attribute. Inline wins over both when it is present; the two agree by
   construction, so which one wins is unobservable. */
.gx-panorama-card:nth-of-type(even){--gxp-fold:1}
.gx-panorama-card:nth-of-type(2){--card-i:1}
.gx-panorama-card:nth-of-type(3){--card-i:2}
.gx-panorama-card:nth-of-type(4){--card-i:3}
.gx-panorama-card:nth-of-type(5){--card-i:4}
.gx-panorama-card:nth-of-type(6){--card-i:5}
.gx-panorama-card:nth-of-type(7){--card-i:6}
.gx-panorama-card:nth-of-type(8){--card-i:7}

/* the hinge itself: a glow crease on the panel's trailing edge, brightest on
   the panel that is currently flat to the camera (donor: glow lines kept). */
.gx-panorama-crease{position:absolute;top:7%;bottom:7%;right:-1px;width:1px;z-index:4;
  pointer-events:none;
  background:linear-gradient(180deg,transparent,
    color-mix(in srgb,var(--c-accent) 58%,transparent),transparent);
  opacity:calc(.24 + var(--card-focus,1) * .76)}

/* the one numbering format on this card — stays legible while the panel is
   folded, which is what makes a half-turned panel still read as a chapter */
.gx-panorama-card .gx-index{position:absolute;top:18px;right:24px;z-index:3;
  color:var(--gx-line-strong);font-family:var(--f-utility);
  font-size:clamp(3.2rem,4.6vw,5.4rem);font-weight:800;letter-spacing:-.08em;line-height:1;
  opacity:calc(.42 + var(--card-focus,1) * .58)}

/* copy sits at the foot of the panel and fades with the fold, so glyphs are
   never asked to be read at a steep oblique angle */
.gx-panorama-content{position:relative;z-index:2;margin-top:auto;
  display:flex;flex-direction:column;gap:9px;
  opacity:calc(.14 + var(--card-focus,1) * .86)}
.gx-panorama-card h3{font-size:clamp(1.5rem,1.85vw,2.15rem);line-height:1.05}
.gx-panorama-card p{font-size:clamp(.9rem,.98vw,1.05rem);line-height:1.5}

/* short-desktop + narrow-desktop compression (P8 ledger precedent): the fold
   needs horizontal room before it needs panel height */
@media (min-width:901px) and (max-height:900px){
  .gx-panorama .gx-stage{min-height:clamp(470px,calc(100svh - 130px),700px)}
  .gx-panorama-card{min-height:clamp(392px,60svh,540px);padding:22px}
  .gx-panorama-card .gx-index{font-size:clamp(2.8rem,4vw,4.4rem)}
}
/* narrow desktop overrides `--gxp-w`, not `width`, so the dead-scene chain math
   above stays fed with the width the panel actually renders at */
@media (min-width:901px) and (max-width:1180px){
  .gx-panorama-card{--gxp-w:clamp(244px,30vw,330px)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned, the fold
   opens out flat: every panel is a full-width read with its numeral, its copy
   and the crease laid down as a foot rule. Never blank, never a pile. ---- */
@media (max-width:900px){
  .gx-panorama{min-height:auto;width:100%;margin-left:0}
  .gx-panorama .gx-sticky{position:relative;min-height:auto;padding:16px 0 30px;overflow:visible}
  /* .gx-panorama.gx-scene, not .gx-panorama, so this beats the shared
     `.gx-scene .gx-stage` collapse rule that lives in the same @media block
     whatever order the splice lands in (both are 0,2,0 otherwise) */
  .gx-panorama.gx-scene .gx-stage{display:grid;grid-template-columns:minmax(0,1fr);
    gap:14px;min-height:auto;padding:0 14px;perspective:none;transform-style:flat;
    place-items:stretch;overflow:visible}
  /* left/right/height/box-shadow/transform are all reset: the base rule
     pre-divides them for the -700px perspective projection, and there is no
     perspective here. `background` is reset too, for the same reason collapse B
     resets it: the base gradient's middle stop is
     `calc(18% + var(--scene-progress,0) * 64%)`, and the frame loop stands down
     in BOTH collapses — leaving it in place kept one scroll-driven paint alive
     on mobile, frozen at whatever --scene-progress was last written. The two
     collapses were inconsistent about this; they no longer are. */
  .gx-panorama-rail{position:relative;top:auto;left:auto;right:auto;height:1px;
    transform:none!important;margin-bottom:4px;
    box-shadow:0 0 26px color-mix(in srgb,var(--c-accent) 20%,transparent);
    background:linear-gradient(90deg,
      color-mix(in srgb,var(--c-glow2) 30%,transparent),
      color-mix(in srgb,var(--c-accent) 58%,transparent),
      color-mix(in srgb,var(--c-glow3) 28%,transparent))}
  /* !important on the animated three, matching the four shipped stack skins
     (engine.css:1103/1188 etc) and the batch-mates. Without it the base rule and
     this rule are both (0,1,0) and only SOURCE ORDER decides — and the frame
     loop stands down here, so any stale inline --card-* left over from a desktop
     -> mobile resize would otherwise pile the panels at stage centre. */
  .gx-panorama-card{position:relative;top:auto;left:auto;
    width:100%;min-height:0;padding:22px 22px 26px;border-radius:18px;
    opacity:1!important;filter:none!important;transform:none!important;
    transform-style:flat;will-change:auto}
  .gx-panorama-content{opacity:1!important;gap:8px}
  .gx-panorama-card h3{font-size:clamp(1.35rem,5.4vw,1.9rem)}
  .gx-panorama-card p{font-size:.95rem}
  .gx-panorama-card .gx-index{position:static;display:block;margin-bottom:10px;
    font-size:2.6rem;opacity:1!important}
  .gx-panorama-crease{top:auto;right:22px;bottom:0;left:22px;width:auto;height:1px;
    opacity:1!important;
    background:linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 52%,transparent),transparent)}
}

/* ---- static collapse B: reduced motion. The same resting composition at every
   width — the fold never animates, so nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-panorama{min-height:auto;width:100%;margin-left:0}
  .gx-panorama .gx-sticky{position:relative;min-height:auto;padding:16px 0 30px;overflow:visible}
  /* same specificity bump as collapse A — and a real gutter, because at desktop
     width the scene sits OUTSIDE .wrap and the shared collapse rule only
     budgets 14px */
  .gx-panorama.gx-scene .gx-stage{display:grid;grid-template-columns:repeat(auto-fit,minmax(282px,1fr));
    gap:16px;min-height:auto;padding:0 clamp(14px,3vw,44px);perspective:none;
    transform-style:flat;place-items:stretch;overflow:visible}
  /* same resets as collapse A: no perspective here, so the -700px pre-division
     of left/right/height/box-shadow/transform must come back off */
  .gx-panorama-rail{position:relative;top:auto;left:auto;right:auto;height:1px;
    transform:none!important;grid-column:1/-1;margin-bottom:4px;
    box-shadow:0 0 26px color-mix(in srgb,var(--c-accent) 20%,transparent);
    background:linear-gradient(90deg,
      color-mix(in srgb,var(--c-glow2) 30%,transparent),
      color-mix(in srgb,var(--c-accent) 58%,transparent),
      color-mix(in srgb,var(--c-glow3) 28%,transparent))}
  /* !important for the same reason as collapse A — this is the state a page
     shows when the frame loop never runs at all, so it must not depend on where
     the splice puts the block */
  .gx-panorama-card{position:relative;top:auto;left:auto;
    width:100%;min-height:0;padding:24px 24px 28px;border-radius:20px;
    opacity:1!important;filter:none!important;transform:none!important;
    transform-style:flat;will-change:auto}
  .gx-panorama-content{opacity:1!important;gap:9px}
  .gx-panorama-card h3{font-size:clamp(1.4rem,2.4vw,1.95rem)}
  .gx-panorama-card p{font-size:.97rem}
  .gx-panorama-card .gx-index{position:static;display:block;margin-bottom:10px;
    font-size:2.8rem;opacity:1!important}
  .gx-panorama-crease{top:auto;right:24px;bottom:0;left:24px;width:auto;height:1px;
    opacity:1!important;
    background:linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 52%,transparent),transparent)}
}


/* ---- skin: mosaicwall (cards-16 "Mosaic focus wall") ----
   A TYPOGRAPHIC focus wall. The donor ships ZERO image slots (manifest
   imageCount:0), so this is a wall of TYPE panels, never a photo wall —
   media policy none / fallback numeral. Two rows hold the complete system in
   view while the focused panel detaches toward the camera.

   ⚠ RENAME: every donor class is re-prefixed `gx-mosaicwall-*`. The shipped
   `photomosaic` skin owns GLOBAL, UNSCOPED `.gx-mosaic{-grid,-caption*}`
   rules in engine.css (repeated in BOTH collapse blocks); the donor
   skeleton's `gx-mosaic{-stage,-card}` would have silently restyled it.
   Every `gx-mosaic` substring in this file is part of `gx-mosaicwall`.

   Box checklist resolved: the donor `.mosaic-stage` shipped a filled wash
   (radial + linear over the whole stage), a 1px border at 40px radius, and an
   inset ::before slab — ALL THREE STRIPPED. The stage keeps only a masked
   hairline lattice (runway floor-grid precedent) and a receding hairline
   frame, so the pack's own GL field shows through. Donor fixed px caps
   (670px stage / 270px card) rebased on clamp() with svh terms.

   Colour: the donor carries no per-item accent hexes, so the wall gets a
   token-derived accent CYCLE (`--gx-mw-accent`, the STEP_ACCENTS ladder) —
   5-8 panels must not read as one flat block. Zero raw hex outside the mask
   stencil. Continuous drift is pure CSS off `--scene-progress`. */
.gx-mosaicwall{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
/* ⚠ NO `overflow` ON THE STAGE — this is load-bearing, do not "tidy" it back in.
   `overflow` other than `visible` is a CSS Transforms GROUPING PROPERTY: it
   forces the element's `transform-style` to compute to `flat`. The shared
   .gx-stage declares transform-style:preserve-3d (engine.css:724-725), so the
   donor's `overflow:hidden` silently DESTROYED the 3D rendering context —
   --card-z stopped ordering anything and the cards fell back to plain DOM
   paint order, i.e. every LATER card painted over the focused card's copy.
   Measured in Chromium 148 with the focused <h3> flooded magenta: at n=7 /
   progress 0 / 1440x900 only 2.4% of the focused headline's own box survived;
   with overflow removed it is 98.7%. Traced 5-8 items x progress 0/.25/.5/
   .75/1 at 1440x900 and 1600x1200: no card ever leaves the stage box (max
   escape 0px), so the clip was never doing work for the cards, and .gx-sticky
   carries overflow:clip above us for the lattice bleed. */
.gx-mosaicwall .gx-stage{display:grid;place-items:center;perspective:1700px;
  min-height:clamp(560px,calc(100svh - 190px),840px)}
/* the wall itself: a masked hairline lattice, drifting off --scene-progress
   (CSS owns the continuous motion — the animator never writes this) */
.gx-mosaicwall .gx-stage::before{position:absolute;inset:-8% -5%;content:"";pointer-events:none;
  opacity:.55;
  /* GL WASH BUDGET: engine.css `.gx-scene::before` already spends 7% of the 15%
     composite ceiling on EVERY takeover, so a skin's own stage/sticky/scene wash
     stays at or below 8%. Both lattice inks are at or under 8% (and the element's
     own opacity:.55 plus the radial mask stencil take the delivered ink to
     <=4.4% at the centre and 0% past the 84% stop). */
  background:
    repeating-linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 8%,transparent) 0 1px,transparent 1px 152px),
    repeating-linear-gradient(0deg,color-mix(in srgb,var(--c-ink) 7%,transparent) 0 1px,transparent 1px 128px);
  -webkit-mask-image:radial-gradient(ellipse 76% 70% at 50% 50%,#000 24%,transparent 84%);
  mask-image:radial-gradient(ellipse 76% 70% at 50% 50%,#000 24%,transparent 84%);
  transform:translate3d(calc(var(--scene-progress,0) * -44px),calc(var(--scene-progress,0) * -20px),-260px)}
/* donor inner slab -> hairline frame only (no fill), receding behind the wall.
   REVIEW FIX (box checklist): a closed rounded rectangle IS a box even with no
   fill. Ink dropped 16% (--gx-line) -> the donor's own 6% (--gx-c21), and the
   frame now carries the SAME radial mask stencil the lattice above it uses, so
   it fades out at the corners (~1.3% effective ink) and only survives as a
   depth hairline at the mid-edges (~4.8%). Never a receding rectangle. */
.gx-mosaicwall .gx-stage::after{position:absolute;inset:9%;content:"";pointer-events:none;
  border:1px solid color-mix(in srgb,var(--c-ink) 6%,transparent);border-radius:34px;
  -webkit-mask-image:radial-gradient(ellipse 92% 88% at 50% 50%,#000 0 46%,transparent 88%);
  mask-image:radial-gradient(ellipse 92% 88% at 50% 50%,#000 0 46%,transparent 88%);
  transform:translateZ(-250px) scale(calc(1 + var(--scene-progress,0) * .05))}

.gx-mosaicwall-card{position:absolute;top:50%;left:50%;
  width:min(384px,26vw);min-height:clamp(238px,26vh,278px);padding:24px 26px;
  display:flex;flex-direction:column;justify-content:flex-end;
  border:1px solid var(--gx-line);border-radius:28px;overflow:hidden;
  --gx-mw-accent:var(--c-accent);
  /* NO z-index — and none is needed. With the stage's `overflow` gone (see the
     block above) the cards really are in a 3D rendering context, where paint
     order IS depth and z-index would be ignored anyway. Ordering is --card-z
     alone: -110px for every card a slot or more away, +210px for the focused
     one, monotone in |delta| in between (see animator.js). Both armature layers
     sit at -250px and -260px, behind the deepest card. The former
     z-index:var(--card-zi) write was a per-frame write for a property the
     browser discards.

     ⚠ NO rotateX / rotateY — this is load-bearing (round 3, R3-B). The transform
     chain below leaves every card's plane normal at exactly (0,0,1) at every
     frame, so no two card quads can ever be non-parallel, so Chromium can never
     SPLIT them: --card-z is a strict total paint order and nothing else is
     needed to make it one. The donor's per-cell rotateX/rotateY tilt was safe
     only inside the donor's own FLATTENED stage (its overflow:hidden — see the
     banner above), where z-index ordered everything; in a real preserve-3d
     context two oppositely tilted panels that overlap on screen intersect, and
     this wall's rows and columns DO overlap. rotateZ is kept: an in-plane
     rotation leaves the normal on +z, so it cannot introduce an intersection.

     NO transform-style either. Declaring preserve-3d here would be a dead
     declaration: `overflow:hidden`, the `filter` and `opacity<1` on this same
     rule are each grouping properties that force the used value back to `flat`.
     The card's children are therefore flattened into the card's own plane and
     ordered by ordinary z-index — which is exactly what .gx-mosaicwall-content
     needs, and why it carries no translateZ. */
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateZ(var(--card-rz,0deg))
    scale(var(--card-scale,1));
  will-change:transform,opacity,filter}
/* ---- DEAD-SCENE FLOOR ----
   MASTER-PLAN §2.1: a missing/mis-keyed ANIMATORS key is filtered out at
   engine.js:2240, so no --card-* is ever written AND --scene-progress never
   ticks. Without these defaults every panel would sit at translate(-50%,-50%)
   — a centred pile of 5-8 colliding headlines. These class-level declarations
   make the resting composition a static two-row wall instead: every panel
   opacity 1, unblurred, non-overlapping at 5, 6, 7 and 8 items. The animator
   writes through setVars() -> element.style, i.e. INLINE, so it overrides all
   of them on its very first frame; this changes nothing on the live path.
   It is also what a --shots frame captured far above the scene now shows. */
.gx-mosaicwall-card{--gx-mw-fb:min(300px,21vw);--card-scale:.72}
.gx-mosaicwall-card:nth-child(1){--card-x:calc(var(--gx-mw-fb) * -1.5);--card-y:-115px}
.gx-mosaicwall-card:nth-child(2){--card-x:calc(var(--gx-mw-fb) * -.5);--card-y:-115px}
.gx-mosaicwall-card:nth-child(3){--card-x:calc(var(--gx-mw-fb) * .5);--card-y:-115px}
.gx-mosaicwall-card:nth-child(4){--card-x:calc(var(--gx-mw-fb) * 1.5);--card-y:-115px}
.gx-mosaicwall-card:nth-child(5){--card-x:calc(var(--gx-mw-fb) * -1.5);--card-y:115px}
.gx-mosaicwall-card:nth-child(6){--card-x:calc(var(--gx-mw-fb) * -.5);--card-y:115px}
.gx-mosaicwall-card:nth-child(7){--card-x:calc(var(--gx-mw-fb) * .5);--card-y:115px}
.gx-mosaicwall-card:nth-child(8){--card-x:calc(var(--gx-mw-fb) * 1.5);--card-y:115px}
/* token-derived accent cycle (donor had none — 5-8 flat panels read as a slab) */
.gx-mosaicwall-card:nth-child(6n+2){--gx-mw-accent:color-mix(in srgb,var(--c-accent) 62%,var(--c-ink))}
.gx-mosaicwall-card:nth-child(6n+3){--gx-mw-accent:var(--c-glow2)}
.gx-mosaicwall-card:nth-child(6n+4){--gx-mw-accent:color-mix(in srgb,var(--c-accent) 55%,var(--c-glow2))}
.gx-mosaicwall-card:nth-child(6n+5){--gx-mw-accent:var(--c-glow3)}
.gx-mosaicwall-card:nth-child(6n){--gx-mw-accent:color-mix(in srgb,var(--c-glow2) 60%,var(--c-ink))}
/* accent tab — brightens with the card's own focus (no glowing-dot bullets) */
.gx-mosaicwall-card::before{position:absolute;top:0;left:26px;width:46px;height:2px;content:"";
  border-radius:0 0 2px 2px;background:var(--gx-mw-accent);
  opacity:calc(.22 + var(--card-focus,0) * .78);
  transform:scaleX(calc(.55 + var(--card-focus,0) * .45));transform-origin:left center}
/* donor cinematic-surface scrim replaced: no imagery to darken, so the panel
   keeps only two token corner glows that lift as the panel takes focus */
.gx-mosaicwall-card::after{position:absolute;inset:0;content:"";pointer-events:none;border-radius:inherit;
  opacity:calc(.42 + var(--card-focus,0) * .58);
  background:
    radial-gradient(circle at 16% 10%,color-mix(in srgb,var(--gx-mw-accent) 20%,transparent),transparent 44%),
    radial-gradient(circle at 86% 88%,color-mix(in srgb,var(--c-glow2) 12%,transparent),transparent 48%)}
.gx-mosaicwall-card.is-active{border-color:color-mix(in srgb,var(--gx-mw-accent) 42%,transparent)}
/* z-index:2 only — NO translateZ. The card is `flat` (see above), so a translateZ
   here would compute to a dead declaration; z-index is the real mechanism that
   keeps the copy above the ::after corner glow. */
.gx-mosaicwall-content{position:relative;z-index:2;
  -webkit-font-smoothing:antialiased;backface-visibility:hidden}
.gx-mosaicwall-card h3{margin-bottom:9px;font-size:clamp(1.34rem,1.9vw,1.95rem);
  line-height:1.01;letter-spacing:-.032em}
.gx-mosaicwall-card p{font-size:.88rem;line-height:1.46}
/* REVIEW FIX: the cycle puts --c-glow2 / --c-glow3 on three of six panels, and
   audit rule 11b (src/audit/rules.js:269-296) never contrast-checks those two
   tokens — they only get an unscored temperature advisory (rules.js:476-482).
   TEXT therefore does not ride the raw glow step: it is pulled 22% toward
   --c-ink, the same construct engine.css:1301 uses for .gxp-chap-num. Ink is
   by definition the readable extreme against --c-field/--c-dominant, so the
   mix moves every cycle step TOWARD legibility on light and dark packs alike.
   The raw cycle still drives the tab, the border and the corner glow, which
   are decoration and are contrast-irrelevant.
   THE RATIO IS 50/50 (round 3 — the 55/45 this file used to ship, and the
   "bottoms out at 4.69:1" it used to claim, were both wrong). Re-measured in
   Chromium against the REAL composited .gx-glass panel — panel-strong =
   --c-field at 92% over the page's --c-dominant, panel-deep = --c-dominant —
   plus the ::after corner glow, on ALL SIXTEEN packs in fixtures/style-packs/
   (palette taken from the site each pack generated) x all six cycle steps x the
   focused card's own filter brightness(1.08) saturate(1.06):
     raw cycle   worst 1.715:1  (persona-studio, --c-glow2 #F4A261)
     78/22       worst 2.520:1
     55/45       worst 4.295:1  <- MISSES the 4.5:1 AA floor for small text
     50/50       worst 4.838:1  <- shipped (independent analytic model: 4.787:1)
     45/55       worst 5.524:1
   The kicker is .67rem = 10.72px, i.e. small text under WCAG 2.2, so 4.5:1 is
   the applicable floor and 50/50 is the smallest mix that clears it on every
   pack. Mixing toward --c-ink is monotone-safe because ink is by definition the
   readable extreme against --c-field/--c-dominant, but it is NOT a proof for an
   arbitrary future palette: a glow token that landed exactly ON --c-field would
   bottom out at 3.03:1 even at 50/50. Nothing in the shipped library is close —
   the worst real step clears by 7.5%. The raw cycle still drives the tab, the
   active border and the corner glow, which carry no text. */
.gx-mosaicwall-card .gx-kicker{margin-bottom:9px;font-size:.67rem;
  color:color-mix(in srgb,var(--gx-mw-accent) 50%,var(--c-ink))}
.gx-mosaicwall-card .gx-cinematic-index{top:13px;right:19px;font-size:3.1rem;
  color:color-mix(in srgb,var(--gx-mw-accent) 28%,var(--gx-line-strong))}

/* ---- mosaicwall: <=900px static collapse — the wall becomes a readable
   single/twin-column stack; full-bleed breakout reverted so nothing is
   clipped by the host container ---- */
@media (max-width:900px){
  .gx-mosaicwall{min-height:auto;width:100%;margin-left:0}
  .gx-mosaicwall .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-mosaicwall .gx-stage{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
    gap:14px;min-height:auto;padding:0 14px;overflow:visible;place-items:stretch;perspective:none}
  .gx-mosaicwall .gx-stage::before,.gx-mosaicwall .gx-stage::after{display:none}
  /* perspective:none on the stage + transform:none on the card is the whole
     collapse: the card declares no transform-style (it is `flat` everywhere, see
     the base rule), so .gx-mosaicwall-content's z-index:2 keeps the copy above
     the ::after corner glow here exactly as it does on the animated path. */
  .gx-mosaicwall-card{position:relative;top:auto;left:auto;width:auto;min-height:210px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-mosaicwall-card::before{opacity:1;transform:none}
  .gx-mosaicwall-card::after{opacity:.7}
  .gx-mosaicwall-card h3{font-size:clamp(1.4rem,6vw,2rem)}
  .gx-mosaicwall-card .gx-cinematic-index{top:12px;right:16px;font-size:2.5rem}
}

/* ---- mosaicwall: reduced-motion still state — the SAME resting composition,
   but kept as a true multi-column wall at desktop widths (the donor's whole
   point is that the complete system stays visible) ---- */
@media (prefers-reduced-motion:reduce){
  .gx-mosaicwall{min-height:auto;width:100%;margin-left:0}
  .gx-mosaicwall .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-mosaicwall .gx-stage{display:grid;grid-template-columns:repeat(auto-fit,minmax(272px,1fr));
    gap:16px;min-height:auto;padding:0 14px;overflow:visible;place-items:stretch;perspective:none}
  .gx-mosaicwall .gx-stage::before,.gx-mosaicwall .gx-stage::after{display:none}
  .gx-mosaicwall-card{position:relative;top:auto;left:auto;width:auto;min-height:220px;flex:none;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-mosaicwall-card::before{opacity:1;transform:none}
  .gx-mosaicwall-card::after{opacity:.7}
  .gx-mosaicwall-card .gx-cinematic-index{top:12px;right:16px;font-size:2.6rem}
}


/* ---- skin: imagechapters (cards-v8 template-18 "Full-bleed image chapters",
   handler `imageChapters`) — GALLERY family, takeover container, media
   REQUIRED (monogram fallback). Edge-to-edge photographic chapters: one
   chapter owns the whole stage, the next fades in over it while its own
   photograph settles out of an overscan and the copy slab rises from the
   bottom-left.

   Donor decisions re-taken at port:
   - the donor's OPAQUE stage fill (`background:#06070b`) is DROPPED — the
     stage carries no backdrop at all; the shared `.gx-scene::before` token
     radial (7% accent) is the only wash, so the pack's GL field reads around
     the pinned stage exactly like every other takeover (GL-field decree).
   - the donor's demo chrome (scene-note / scene-number / scene-progress rail)
     was stripped at extraction and is NOT reintroduced — a progress readout is
     telemetry chrome (de-AI). The scene carries NO counters at all: the
     gallery family has no "0N / 0M" numbering (de-AI decree), so this skin has
     exactly zero numbering formats.
   - the donor kicker's glowing-dot `::before` bullet
     (`box-shadow:0 0 15px rgba(72,217,255,.75)`) is dropped — the engine's
     shared `.gx-kicker` has no bullet (P6/P8 decree); the kicker only takes
     this skin's per-chapter accent colour.
   - the donor's hard-coded per-item accent becomes a FOUR-STEP TOKEN CYCLE
     (`--gxic-accent`), so 4-8 chapters never repeat two accents in a row.
   - the donor's scrim pair (horizontal wash + bottom-up wash) is kept as the
     composition it is, re-derived to `--c-dominant` mixes, and MIRRORED on
     even chapters so consecutive chapters do not read as the same frame.
   - the copy slab keeps its fill (curtain / imagesplit precedent): it is the
     text carrier over photography and the contrast floor demands it. It is a
     card, not a stage/sticky/scene backdrop.
   - continuous motion: the photographs ken-burns off `--scene-progress` (the
     frame loop already writes it on the scene element) — a scale term inside
     the existing scale(), so it costs zero extra per-frame writes.  ---- */
.gx-imagechapters{min-height:min(270svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-imagechapters .gx-stage{min-height:clamp(590px,calc(100svh - 210px),840px);
  overflow:hidden;isolation:isolate;perspective:none;transform-style:flat}

/* per-chapter accent cycle — token-derived (donor's inline per-item hexes).
   `isolation:isolate` makes each chapter its own stacking context, so the
   copy slab can never paint over a LATER chapter and DOM order alone gives
   the stack order (no z-index, no per-frame z write). */
.gx-imagechapters-chapter{position:absolute;inset:0;isolation:isolate;
  pointer-events:none;opacity:var(--panel-opacity,0);
  --gxic-accent:var(--c-accent)}
/* DEAD-SCENE FLOOR (adversarial review, 2026-08-07). Nothing in the gate set
   opens engine.js: a missing or mis-keyed ANIMATORS entry is filtered out at
   engine.js:2236, which ALSO kills the `--scene-progress` write at :2366 — so
   without this rule the resting state of a full-bleed takeover is a blank
   section behind four green gates. These are exactly the values the animator
   writes for chapter 1 at progress 0 (traced in NOTES.md), and an inline
   animator write always outranks them. Chapters 2..n stay at --panel-opacity 0,
   so the resting composition is ONE readable chapter, never a pile.
   Precedent: the comparewipe skin's own first-pair floor rule. (Spelled out
   rather than quoted so a naive cross-skin class grep cannot false-positive.) */
.gx-imagechapters-chapter:first-child{--panel-opacity:1;--image-scale:1.02;
  --image-pan:0px;--image-brightness:.84;--image-saturation:1.02;
  --copy-opacity:1;--copy-y:0px}
.gx-imagechapters-chapter:nth-child(4n+2){--gxic-accent:var(--c-glow2)}
.gx-imagechapters-chapter:nth-child(4n+3){--gxic-accent:var(--c-glow3)}
.gx-imagechapters-chapter:nth-child(4n+4){
  --gxic-accent:color-mix(in srgb,var(--c-accent) 52%,var(--c-glow3))}

.gx-imagechapters-media{position:absolute;inset:0;overflow:hidden}
.gx-imagechapters-media img{display:block;width:100%;height:100%;object-fit:cover;
  -webkit-user-drag:none;user-select:none;backface-visibility:hidden;
  transform:translate3d(var(--image-pan,0px),0,0)
    scale(calc(var(--image-scale,1.14) + var(--scene-progress,0) * .04));
  filter:brightness(var(--image-brightness,.72)) saturate(var(--image-saturation,.9));
  will-change:transform,filter}
/* no-asset chapters keep their slot on the shared monogram pane (P3 team
   precedent) — this renderer never emits an empty src */
.gx-imagechapters-media .gx-pane-fallback{width:100%;height:100%;min-height:100%;
  transform:scale(calc(var(--image-scale,1.14) + var(--scene-progress,0) * .04));
  filter:brightness(var(--image-brightness,.72))}

/* donor scrim pair, token-derived; mirrored on even chapters */
.gx-imagechapters-chapter::after{position:absolute;inset:0;content:"";pointer-events:none;
  background:
    linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 84%,transparent),
      color-mix(in srgb,var(--c-dominant) 18%,transparent) 58%,
      color-mix(in srgb,var(--c-dominant) 46%,transparent)),
    linear-gradient(0deg,color-mix(in srgb,var(--c-dominant) 82%,transparent),transparent 52%)}
.gx-imagechapters-chapter:nth-child(even)::after{
  background:
    linear-gradient(-90deg,color-mix(in srgb,var(--c-dominant) 84%,transparent),
      color-mix(in srgb,var(--c-dominant) 18%,transparent) 58%,
      color-mix(in srgb,var(--c-dominant) 46%,transparent)),
    linear-gradient(0deg,color-mix(in srgb,var(--c-dominant) 82%,transparent),transparent 52%)}

.gx-imagechapters-copy{position:absolute;left:clamp(22px,6vw,110px);
  bottom:clamp(28px,7vh,86px);z-index:4;width:min(760px,calc(100% - 44px));
  padding:clamp(26px,3.5vw,50px);color:var(--c-ink);
  border-left:2px solid var(--gxic-accent);
  background:color-mix(in srgb,var(--c-dominant) 92%,transparent);
  box-shadow:0 30px 90px rgba(0,0,0,.34);
  opacity:var(--copy-opacity,0);transform:translate3d(0,var(--copy-y,45px),0);
  will-change:transform,opacity;-webkit-font-smoothing:antialiased}
.gx-imagechapters-copy h3{margin:0 0 14px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2.1rem,4.4vw,4.6rem);
  letter-spacing:-.058em;line-height:.93}
/* The shared --gx-muted is ink at 62%. Traced at the real glyph band of this
   slab (1440x900, worst light pack lens-cinema, crushed-black photograph under
   the weakest corner of both scrims) that lands on 4.59:1 — over the 4.5 AA
   floor, but with no margin left for a longer body that pushes the row further
   up into the thinner part of the bottom scrim. This slab is the ONE place in
   the skin where text sits over live photography, so it takes a skin-local
   step of ink instead: 5.94:1 worst case, same token, same construct. */
.gx-imagechapters-copy p{max-width:620px;margin:0;
  color:color-mix(in srgb,var(--c-ink) 70%,transparent);
  font-size:clamp(.95rem,1.15vw,1.1rem);line-height:1.62}
/* The accent CYCLE drives the slab's left rule (a border, not text). The KICKER
   is TEXT, and --c-glow2/--c-glow3 are never contrast-checked: audit rule 11b
   (rules.js:269-296) reads ink/dominant, ink/field and accent/dominant only,
   and the glow pair gets a temperature advisory that is explicitly not scored
   (rules.js:476-482). Measured over this slab across the 14 distinct palettes
   in out/: the RAW cycle bottoms out at 1.87:1 (persona-studio glow2 #F4A261 on
   a #FCFAF7 slab) and 3 of 14 packs fall under the 3.0:1 the engine contracts
   for accent-on-dominant. The review's suggested 78% mix only reaches 2.84:1
   and still misses on 2 packs, so the mix is 55/45: worst case 4.71:1, which
   clears AA small text on every pack and beats the shipped family's own raw
   --c-accent kicker (3.61:1 on whitespace-run). Same construct as
   engine.css:1301 (.gxp-chap-num), a heavier ratio because this text sits over
   live photography instead of a panel. */
.gx-imagechapters-copy .gx-kicker{margin-bottom:12px;
  color:color-mix(in srgb,var(--gxic-accent) 55%,var(--c-ink))}

/* ---- imagechapters: static collapse <=900px — breakout reverted, the scene
   unpinned, every chapter laid out as a readable stacked photo-essay entry
   (photo above, copy below on the page's own surface). Nothing is hidden and
   nothing is piled: 4-8 chapters all read. ---- */
@media (max-width:900px){
  .gx-imagechapters{min-height:auto;width:100%;margin-left:0}
  .gx-imagechapters .gx-sticky{position:relative;top:auto;min-height:auto;
    padding:22px 0 30px;overflow:visible;background:none}
  .gx-imagechapters .gx-stage{display:grid;gap:26px;min-height:auto;padding:0 14px;
    overflow:visible;perspective:none}
  .gx-imagechapters-chapter{position:relative;inset:auto;isolation:auto;
    pointer-events:auto;opacity:1!important}
  .gx-imagechapters-chapter::after,
  .gx-imagechapters-chapter:nth-child(even)::after{content:none}
  .gx-imagechapters-media{position:relative;inset:auto;height:clamp(220px,46vw,320px);
    border-radius:16px}
  .gx-imagechapters-media img,
  .gx-imagechapters-media .gx-pane-fallback{height:100%;
    transform:none!important;filter:none!important}
  .gx-imagechapters-copy{position:relative;left:auto;bottom:auto;z-index:auto;
    width:100%;padding:16px 0 0 14px;background:none;box-shadow:none;
    opacity:1!important;transform:none!important}
  .gx-imagechapters-copy h3{font-size:clamp(1.8rem,7.4vw,2.8rem)}
}

/* ---- imagechapters: reduced-motion still state — identical readable stacked
   composition, every animated property frozen at its resting value and every
   transition/animation dropped ---- */
@media (prefers-reduced-motion:reduce){
  .gx-imagechapters{min-height:auto;width:100%;margin-left:0}
  .gx-imagechapters .gx-sticky{position:relative;top:auto;min-height:auto;
    padding:22px 0 30px;overflow:visible;background:none}
  .gx-imagechapters .gx-stage{display:grid;gap:26px;min-height:auto;padding:0 14px;
    overflow:visible;perspective:none}
  .gx-imagechapters-chapter{position:relative;inset:auto;isolation:auto;
    pointer-events:auto;opacity:1!important;transition:none;animation:none}
  .gx-imagechapters-chapter::after,
  .gx-imagechapters-chapter:nth-child(even)::after{content:none}
  .gx-imagechapters-media{position:relative;inset:auto;height:clamp(240px,42vw,420px);
    border-radius:16px}
  .gx-imagechapters-media img,
  .gx-imagechapters-media .gx-pane-fallback{height:100%;
    transform:none!important;filter:none!important;transition:none;animation:none}
  .gx-imagechapters-copy{position:relative;left:auto;bottom:auto;z-index:auto;
    width:100%;padding:16px 0 0 14px;background:none;box-shadow:none;
    opacity:1!important;transform:none!important;transition:none;animation:none}
  .gx-imagechapters-copy h3{font-size:clamp(1.9rem,5vw,3rem)}
}


/* ---- skin: comparewipe (cards-v8 template-22, ported as an IMAGE WIPE REVEAL) ----
   GALLERY family, takeover container, media required + monogram fallback.

   FRAMING DECISION (review, 2026-08-07): the donor calls its two panes
   "Before" and "After". A spec item carries ONE asset_ref and no pairing
   semantics, so consecutive gallery items are INDEPENDENT captions — calling
   them before/after invents a relationship the brief never stated, and the
   donor's permanent grayscale drain asserted that invented meaning visually
   (it also survived into both static collapses, printing half a customer's
   portfolio in gray). The before/after framing is therefore REMOVED:
     · no Before/After labels, no "then/now" copy, NO grayscale anywhere;
     · the two panes are EQUALS — one image wipes across to reveal the next;
     · the classes are `-base` (the plane underneath) and `-reveal` (the
       clipped plane on top), never "before"/"after".
   The donor's WIPE MOTION is what is kept.

   STRUCTURAL PORT: the donor needs TWO images per unit but a spec item carries
   exactly ONE asset_ref, so each wipe is built from a PAIR OF CONSECUTIVE
   ITEMS — (0,1), (2,3), (4,5)… — and `data-card` rides the PAIR, never the
   item. An odd trailing item renders as `.gx-comparewipe-solo`: one full-bleed
   pane with no seam, never half a broken wipe. Both halves come from
   galleryPane(), so a missing asset degrades to the shared .gx-pane-fallback
   monogram instead of an empty src.

   Motion: the donor's pointer-dragged handle is dead weight inside a scroll
   takeover, so --wipe is scroll-driven (animator) and clamped to the donor's
   own 8%-92% bounds so the seam node is never clipped against the stage edge.
   The seam survives as a visible motion affordance — a thin accent rule with a
   round node — but carries no "drag me" glyph and no telemetry chrome.

   Port fixes baked: donor #06070b / #05060a stage + compare-stage fills DROPPED
   (the pack's GL field shows through — .gx-stage stays transparent); donor
   inline per-item accent hexes become the token-derived --gxcw-accent cycle
   below; donor rgba(255,255,255,.46) seam glow becomes an accent mix (a white
   glow is invisible on a light pack); donor 260svh compressed to 300svh for a
   per-pair wipe; the demo scene-note / scene-progress-label telemetry strip is
   not ported. */
.gx-comparewipe{min-height:min(300svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-comparewipe .gx-stage{min-height:clamp(600px,calc(100svh - 200px),980px);
  overflow:hidden;isolation:isolate;perspective:none}
/* continuous CSS-driven ambience: reads the scene's own --scene-progress, so
   the light rakes without a single per-frame JS write. It lives on the STICKY
   — ONE viewport — not on the 300svh scene root: --scene-progress is rewritten
   every frame, and a moving gradient on the root repaints a three-viewport,
   100vw layer every frame for a band of pixels the stage covers anyway.
   GL WASH BUDGET: 8%, not 12%. The ceiling is on the COMPOSITE, and engine.css
   `.gx-scene::before` already paints --c-accent at 7% on every takeover scene
   (rev 2 read lint's per-declaration <=15% check as the whole rule; it is not).
   MEASURED as pixels, --c-accent forced to pure red over black and the page
   actually scrolled so the pinned sticky travels the scene: the two radial
   peaks come closest at progress 0.275, where rev 2's 12% composited to
   16.08% -- over the ceiling. At 8% that same peak measures 12.94%, under it.
   (Straight alpha-over of two coincident peaks would be 18.16% / 14.44%; the
   peaks are not coincident, so the real composite is lower than that bound in
   both cases.) See NOTES.md section 5e for the full sweep. */
.gx-comparewipe .gx-sticky{
  background:radial-gradient(120% 62% at calc(16% + var(--scene-progress,0) * 66%) 48%,
    color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 68%)}

/* token-derived accent cycle — replaces the donor's inline per-item hexes */
.gx-comparewipe-pair{--gxcw-accent:var(--c-accent)}
.gx-comparewipe-pair:nth-child(4n+2){--gxcw-accent:var(--c-glow2)}
.gx-comparewipe-pair:nth-child(4n+3){--gxcw-accent:color-mix(in srgb,var(--c-accent) 55%,var(--c-glow3))}
.gx-comparewipe-pair:nth-child(4n+4){--gxcw-accent:var(--c-glow3)}

/* DOM ORDER IS THE STACK ORDER. No z-index is written per frame and none is
   needed: each pair is its own stacking context, the incoming pair fades in ON
   TOP of the one beneath (which holds opacity 1 for a full slot), so the
   composite is opaque at every scroll position and the handover is a real
   crossfade instead of an instantaneous swap. */
/* container-type gives the seam an exact inline-size reference (100cqw == the
   stage's own width) so the seam can be a NARROW promoted box instead of a
   100%-wide one — see the seam block. The pair is ALREADY a stacking context
   (isolation:isolate), so the stacking context container-type implies changes
   nothing: measured 0 differing pixels over 9 progress stops at 1440x900.
   will-change:opacity is KEPT and it is the one promotion this skin cannot
   give up — the pair is the crossfading full-bleed plane. Measured cost of
   dropping it: -1.0Mpx of 32.25 (3 of 4 pairs stay promoted by Overlap
   anyway) and +13 paint events over 300 frames. */
.gx-comparewipe-pair{position:absolute;inset:0;pointer-events:none;isolation:isolate;
  container-type:inline-size;
  opacity:var(--pair-opacity,0);will-change:opacity}
/* dead-scene floor: if the animator never runs (missing ANIMATORS key, JS
   error) the first pair still reads as a finished composition mid-wipe rather
   than a blank stage */
.gx-comparewipe-pair:first-child{--pair-opacity:1;--copy-opacity:1;--copy-y:0px;--wipe:.46}

.gx-comparewipe-media{position:absolute;inset:0;overflow:hidden}
.gx-comparewipe-base,.gx-comparewipe-reveal{position:absolute;inset:0}
.gx-comparewipe-media img{display:block;width:100%;height:100%;object-fit:cover;
  image-rendering:auto;backface-visibility:hidden;user-select:none;-webkit-user-drag:none}
/* both panes are treated IDENTICALLY — neither is a drained "before" state.
   The monogram fallback keeps the shared pane's own display:grid centring; it
   only inherits the settle + grade. */
.gx-comparewipe-media img,.gx-comparewipe-media .gx-pane-fallback{
  transform:scale(var(--media-scale,1));
  filter:brightness(var(--media-brightness,1))
    saturate(calc(var(--media-saturation,1) * var(--media-chroma,1.18)))}
/* NO will-change here. `will-change:clip-path` promotes NOTHING in Chromium —
   measured: the reveal's only compositing reason is `Overlap`, never
   `WillChangeClipPath`, and deleting the declaration moved the layer count,
   the layer area and the rendered pixels by exactly 0. `clip-path:inset()`
   already establishes the stacking context, so nothing else depended on it. */
.gx-comparewipe-reveal{clip-path:inset(0 calc(100% - var(--wipe,0) * 100%) 0 0)}

/* The seam is a plane translated by the wipe fraction, not an absolutely-
   positioned rule with `left:var(--wipe)`: `left` is a LAYOUT property, and
   driving one off a per-frame variable forces layout on every frame of the
   scroll (the `helix` lesson). A transform is the same geometry, free.

   COMPOSITOR BUDGET: rev 2 made this plane 100% WIDE so the percentage in the
   translate resolved against the stage. That is correct geometry and a bad
   layer: a 1440x700 promoted texture per pair to carry a 2px rule and a 46px
   disc, and it pushed its own ::before out into a second Overlap-promoted
   layer (74x772). The box is now only as wide as the disc and the percentage
   resolves against the PAIR's container inline-size instead (100cqw == the
   stage width, exactly what 100% of a full-width box gave).
   MEASURED per seam, CDP LayerTree at 1440x900: 1440x700 (1,008,000px2) plus a
   74x772 ::before layer (57,128px2) -> ONE 75x772 layer (57,900px2, the 46px
   box union its 24px glow). Across 4 pairs 4,260,512px2 -> 231,600px2, -94.6%.
   The seam centre still lands on the clip boundary to 0.000px at all 56
   seam-bearing samples (items 5/6/7/8 x progress 0/.25/.5/.75/1). */
.gx-comparewipe-seam{position:absolute;top:0;bottom:0;left:0;z-index:3;
  width:clamp(34px,3.2vw,48px);
  pointer-events:none;transform:translate3d(calc(var(--wipe,0) * 100cqw - 50%),0,0);
  will-change:transform}
.gx-comparewipe-seam::before{position:absolute;top:0;bottom:0;left:calc(50% - 1px);content:"";
  display:block;width:2px;
  background:linear-gradient(180deg,
    color-mix(in srgb,var(--c-ink) 26%,transparent),
    color-mix(in srgb,var(--gxcw-accent) 92%,transparent) 20%,
    color-mix(in srgb,var(--gxcw-accent) 92%,transparent) 80%,
    color-mix(in srgb,var(--c-ink) 26%,transparent));
  box-shadow:0 0 24px color-mix(in srgb,var(--gxcw-accent) 30%,transparent)}
.gx-comparewipe-seam::after{position:absolute;top:50%;left:50%;content:"";
  display:block;width:100%;aspect-ratio:1;border-radius:50%;
  transform:translate(-50%,-50%);
  border:1px solid color-mix(in srgb,var(--c-ink) 34%,transparent);
  background:color-mix(in srgb,var(--gxcw-accent) 88%,transparent);
  box-shadow:0 12px 34px rgba(0,0,0,.45)}

/* copy-legibility scrim (curtain precedent) — this is over MEDIA, not over the
   stage/sticky/scene, so the GL-field transparency rule is untouched */
.gx-comparewipe-pair::after{position:absolute;inset:0;z-index:2;content:"";pointer-events:none;
  background:linear-gradient(0deg,color-mix(in srgb,var(--c-dominant) 80%,transparent),transparent 54%)}

/* will-change is KEPT here and the measurement is why: the copy block sits on
   top of the composited media, so Chromium promotes it by `Overlap` with or
   without the hint — dropping the declaration removes 0 layers and 0px2. What
   it DOES change is text rasterisation: 25,488 pixels of headline/body flip
   antialiasing mode (max channel delta 223) because an explicitly promoted
   layer takes grayscale AA while an Overlap-promoted one can still take LCD
   subpixel AA, which then fringes as the block's own opacity animates. All
   cost, no saving — so it stays. Both static collapses stand it back down. */
.gx-comparewipe-copy{position:absolute;right:clamp(20px,5vw,80px);bottom:clamp(22px,6vh,68px);
  left:clamp(20px,5vw,80px);z-index:6;
  opacity:var(--copy-opacity,0);transform:translate3d(0,var(--copy-y,28px),0);
  will-change:opacity,transform}
.gx-comparewipe-copy h3{margin:0 0 12px;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(2rem,4.2vw,4.3rem);
  letter-spacing:-.055em;line-height:.94}
.gx-comparewipe-copy p{max-width:640px;margin:0;
  color:color-mix(in srgb,var(--c-ink) 68%,transparent);
  font-size:clamp(.95rem,1.15vw,1.08rem);line-height:1.6}
/* the accent cycle can land on --c-glow2/--c-glow3, which rule 11b never
   contrast-checks; mixed toward ink (the .gxp-chap-num construct) it cannot
   drop under the floor over live photography */
.gx-comparewipe-copy .gx-kicker{margin-bottom:10px;
  color:color-mix(in srgb,var(--gxcw-accent) 78%,var(--c-ink))}
/* the pair's SECOND item is a full peer, not a truncated pill: its title rides
   at sub-headline weight above its own body copy */
.gx-comparewipe-alt{margin-top:16px;padding-left:14px;
  border-left:2px solid color-mix(in srgb,var(--gxcw-accent) 60%,transparent)}
.gx-comparewipe-alt-title{display:block;margin-bottom:5px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.05rem,1.7vw,1.6rem);letter-spacing:-.03em;line-height:1.1}
.gx-comparewipe-alt p{color:color-mix(in srgb,var(--c-ink) 60%,transparent)}

/* ---- static collapse <=900px: the wipe becomes a printed side-by-side ---- */
@media (max-width:900px){
  .gx-comparewipe{min-height:auto;width:100%;margin-left:0}
  .gx-comparewipe .gx-sticky{background:none}
  .gx-comparewipe .gx-stage{min-height:0;overflow:visible;perspective:none;
    display:grid;gap:clamp(28px,6vw,46px)}
  /* NOTHING animates in a static collapse, so nothing stays promoted: the
     will-change hints and the img compositing hint are stood down here or the
     skin would hold n full-width compositor layers on the most memory-
     constrained device it ships to. Measured (CDP LayerTree, items=8, layers
     that DRAW): 800x900 collapse 14 layers / 7.74Mpx (29.5MB) -> 2 / 4.25Mpx
     (16.2MB); reduced-motion at 1440x900 14 / 21.63Mpx (82.5MB) -> 2 /
     11.48Mpx (43.8MB). */
  .gx-comparewipe-pair{position:relative;inset:auto;display:block;pointer-events:auto;
    container-type:normal;will-change:auto;opacity:1!important}
  .gx-comparewipe-pair::after{display:none}
  .gx-comparewipe-media{position:relative;inset:auto;display:grid;
    grid-template-columns:1fr 1fr;gap:2px;min-height:250px}
  .gx-comparewipe-solo .gx-comparewipe-media{grid-template-columns:1fr}
  .gx-comparewipe-base,.gx-comparewipe-reveal{position:relative;inset:auto;
    min-height:250px;clip-path:none!important}
  /* both panes reset to their true colour: no drain survives into print */
  .gx-comparewipe-media img,.gx-comparewipe-media .gx-pane-fallback{
    backface-visibility:visible;
    transform:none!important;filter:saturate(var(--media-chroma,1.18))!important}
  .gx-comparewipe-seam{display:none}
  .gx-comparewipe-copy{position:relative;inset:auto;right:auto;bottom:auto;left:auto;
    margin-top:14px;will-change:auto;opacity:1!important;transform:none!important}
  .gx-comparewipe-copy h3{font-size:clamp(1.9rem,8vw,3rem)}
}

/* ---- reduced-motion still state: the same static composition ---- */
@media (prefers-reduced-motion:reduce){
  .gx-comparewipe{min-height:auto;width:100%;margin-left:0}
  .gx-comparewipe .gx-sticky{background:none}
  .gx-comparewipe .gx-stage{min-height:0;overflow:visible;perspective:none;
    display:grid;gap:clamp(28px,6vw,46px)}
  /* NOTHING animates in a static collapse, so nothing stays promoted: the
     will-change hints and the img compositing hint are stood down here or the
     skin would hold n full-width compositor layers on the most memory-
     constrained device it ships to. Measured (CDP LayerTree, items=8, layers
     that DRAW): 800x900 collapse 14 layers / 7.74Mpx (29.5MB) -> 2 / 4.25Mpx
     (16.2MB); reduced-motion at 1440x900 14 / 21.63Mpx (82.5MB) -> 2 /
     11.48Mpx (43.8MB). */
  .gx-comparewipe-pair{position:relative;inset:auto;display:block;pointer-events:auto;
    container-type:normal;will-change:auto;opacity:1!important}
  .gx-comparewipe-pair::after{display:none}
  .gx-comparewipe-media{position:relative;inset:auto;display:grid;
    grid-template-columns:1fr 1fr;gap:2px;min-height:250px}
  .gx-comparewipe-solo .gx-comparewipe-media{grid-template-columns:1fr}
  .gx-comparewipe-base,.gx-comparewipe-reveal{position:relative;inset:auto;
    min-height:250px;clip-path:none!important}
  .gx-comparewipe-media img,.gx-comparewipe-media .gx-pane-fallback{
    backface-visibility:visible;
    transform:none!important;filter:saturate(var(--media-chroma,1.18))!important}
  .gx-comparewipe-seam{display:none}
  .gx-comparewipe-copy{position:relative;inset:auto;right:auto;bottom:auto;left:auto;
    margin-top:14px;will-change:auto;opacity:1!important;transform:none!important}
  .gx-comparewipe-copy h3{font-size:clamp(1.9rem,8vw,3rem)}
}


/* ---- skin: helix (cards-v8 template-11 "Vertical 3D card helix") — STACK
   family, takeover container, media policy none (numeral fallback).

   The deck rides ONE helical column: each card sits at its own angle around a
   vertical axis and at its own height on that axis, so scrolling walks the
   camera up the helix. Motion splits in two, deliberately:
   - PER-CARD (JS): the animator writes ELEVEN custom properties —
     --card-x/-y/-z/-rx/-ry/-scale/-opacity/-blur/-brightness/-saturation/
     -focus. There is no --card-layer and no z-index anywhere in this skin;
     ordering is 3D depth (see the armature note below). The angular sweep per
     slot is derived from sc.cards.length every frame, so 5 and 8 items describe
     the same helix — nothing here hardcodes the donor's 5. The VERTICAL pitch
     is derived from the live card box instead (round 4): it is .86 x the card's
     own height at every count, because the front-rank hand-over is a geometry
     event and the step that clears it is a property of the card, not of n. See
     the animator's pitch note for the measured threshold.
   - CONTINUOUS (CSS): the armature — the donor's two rotateX(72deg) ellipses
     and its centre line — drifts off --scene-progress, which the frame loop
     already writes on the scene element. Zero extra per-frame property writes.

   Donor decisions re-taken at port (box checklist + port invariants):
   - the donor's .gx-helix-stage was a FILLED BOX (1px border, 38px radius, an
     ink slab, a hard 50%-stop centre-line gradient and a 15% radial). The fill,
     border and radius are stripped (swipe/runway precedent) so the pack's own
     GL field shows through; the centre line survives as .gx-helix-spine, a
     hairline that fades out at both ends instead of butting into hard edges,
     and the two ellipses survive as real line work on .gx-helix-armature.
   - the donor's ::before card glow was MOUSE-driven (--card-glow/--glow-x/y).
     There is no pointer contract in a scroll takeover, so it is re-derived as a
     SCROLL glow off --card-focus.
   - the donor's fixed 330/410px card cap becomes clamp() with svh terms.
   - the donor's inline per-item accent hexes become a token-derived CYCLE
     (--gxhx-accent, six steps, mirroring STEP_ACCENTS in archetypes.js).
   - the donor's per-card eyebrow + its separate scene counter are dropped to
     ONE numbering format per card (the .gx-index numeral); the kicker survives
     only when the item carries a semantic value. ---- */
.gx-helix{min-height:min(250svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-helix .gx-stage{display:grid;place-items:center}
/* armature — line work only, never a slab; the pack field reads through it.
   ORDERED BY DEPTH, NOT BY z-index. `.gx-stage` is transform-style:preserve-3d
   (engine.css:724-725), and inside a preserve-3d context paint order is 3D
   depth — `z-index` is ignored outright. The armature therefore lives on ONE
   flat plane parked at translateZ(-1400px).

   -1400 is set against the deepest CARD CORNER, not the deepest --card-z. The
   earlier -1000 was derived from the card CENTRE (-864.5px) and was wrong: a
   card is a rotated 560x470 quad, so rotateY (capped 58deg) and rotateX (capped
   15deg) push its far corner well behind its own centre. Measured in Chromium
   off the real computed matrices, over viewports 901x760 -> 3840x2160 x n=5-8 x
   41 progress stops: the deepest centre is -1024.46px and the deepest CORNER is
   -1204.37px (n=8, progress 1, card 0 at delta=-7, any viewport >=1428x1022 —
   card 560x470, scale .62, corner reach 179.91px behind its centre). Against
   the old -1000 plane the round-2 depths already put card corners through it
   from |delta| = 5.295 outward (deepest corner then: -1044.37px), and the
   round-3 recession makes the outgoing side cross earlier still. They no longer
   can: -1400 leaves 195.63px of clearance below the worst corner. Verified in
   Chromium: every card flooded opaque, the quad union eroded 1px, armature
   toggled -> 0 armature pixels in front of any card over n=5-8 x progress
   0/.25/.5/.75/1 at 1920x1200 and 1440x1200.

   The plane itself is Z-FLAT (that part of the old note was true and is kept):
   the donor's `rotateX(72deg) rotateZ(a)` is written as its own projection,
   `scaleY(cos 72deg) rotate(a)`, so no ring or spine has any Z extent of its
   own. To keep the composition identical the plane is pre-scaled by the inverse
   of its own perspective divide: 1500/(1500+1400) = .51724, so scale(1.93333)
   lands it back pixel-for-pixel. perspective-origin is the stage centre and the
   armature is inset:0 of that same stage, so the two centres coincide and the
   compensation is exact. */
.gx-helix-armature{position:absolute;inset:0;pointer-events:none;
  transform:translateZ(-1400px) scale(1.93333)}
/* the donor's two tilted ellipses, generalised to a --ring-index set: each ring
   carries a BRIGHT top arc and faint sides, so the --scene-progress rotation
   drift is legible as rotation instead of an invisible symmetric circle.
   The donor's `rotateX(72deg) rotateZ(a)` is expressed as its own projection,
   `scaleY(cos 72deg) rotate(a)` — identical for every shape under an ortho
   projection, and the reason the armature plane can be Z-flat. */
.gx-helix-ring{position:absolute;top:50%;left:50%;
  width:calc(30% + var(--ring-index) * 13%);
  height:calc(44% + var(--ring-index) * 13%);
  border:1px solid transparent;border-radius:50%;
  border-top-color:color-mix(in srgb,var(--c-accent) 34%,transparent);
  border-right-color:color-mix(in srgb,var(--c-ink) 13%,transparent);
  border-bottom-color:color-mix(in srgb,var(--c-ink) 6%,transparent);
  border-left-color:color-mix(in srgb,var(--c-ink) 13%,transparent);
  opacity:calc(.9 - var(--ring-index) * .18);
  transform:translate(-50%,-50%) scaleY(.30902)
    rotate(calc(var(--scene-progress,0) * 200deg + var(--ring-index) * 42deg))}
/* the donor centre line, de-slabbed: a hairline masked to nothing at both ends */
.gx-helix-spine{position:absolute;top:0;bottom:0;left:50%;width:1px;
  transform:translateX(-50%);
  background:linear-gradient(180deg,transparent 0%,
    color-mix(in srgb,var(--c-ink) 20%,transparent) 18%,
    color-mix(in srgb,var(--c-ink) 20%,transparent) 82%,transparent 100%)}
/* the travelling node. --scene-progress drives a TRANSFORM, never `top`: the
   frame loop rewrites --scene-progress on the scene element every rAF
   (engine.js:2366, unconditional), so `top:calc(var(--scene-progress)*100%)`
   dirtied layout on every single frame. The node is a full-height rail — its
   OWN height is exactly the travel distance — so translateY(progress * 100%),
   a percentage of its own border box, reproduces the old geometry to the pixel
   while staying on the compositor. No container query, no hardcoded stage
   height: the rail inherits the stage's clamp() height through the spine. */
.gx-helix-node{position:absolute;top:0;left:0;width:1px;height:100%;
  transform:translateY(calc(var(--scene-progress,0) * 100%))}
.gx-helix-node::before{position:absolute;top:0;left:50%;
  width:9px;height:9px;margin:-4px 0 0 -4px;border-radius:50%;content:"";
  background:var(--c-accent);
  box-shadow:0 0 24px 5px color-mix(in srgb,var(--c-accent) 45%,transparent)}
/* the card: donor transform chain kept verbatim (translate(-50%,-50%) then
   translate3d + rotateX + rotateY + scale), donor filter chain kept verbatim.
   No z-index and no --card-layer: inside the preserve-3d stage those do
   nothing. Ordering is real 3D depth, and --card-z is deliberately ASYMMETRIC
   about focus — an outgoing card carries up to 160px of extra recession that an
   incoming card at the same |delta| does not, so the incoming card takes the
   front rank slightly early and a passed card never comes back toward the
   camera. That asymmetry is NOT what keeps the copy clear: rounds 2 and 3 tried
   to make it so and could not, because the front rank must hand over and two
   planes must therefore meet at equal depth. What keeps the copy clear is the
   PITCH — .86 x the card's own height per slot, which puts the neighbour's
   whole box clear of the front card's h3+p block through the hand-over. The
   recession now only decides the thin residual EDGE band (card padding and the
   top-right numeral) cleanly instead of 50/50. See the animator. */
.gx-helix-card{position:absolute;top:50%;left:50%;
  display:flex;flex-direction:column;
  /* Card height is declared ONCE and everything derives from it. The dead-scene
     rise below is .86 x this value, and it used to be a hand-evaluated copy
     (clamp(275.2px,39.56svh,404.2px)) that would silently drift the moment
     anyone retuned the card — a duplicate constant with no gate. calc() does the
     arithmetic now, so drift is structurally impossible rather than merely
     documented. */
  --gxhx-card-h:clamp(320px,46svh,470px);
  width:min(560px,62vw);min-height:var(--gxhx-card-h);
  padding:clamp(26px,2.4vw,38px);overflow:hidden;border-radius:30px;
  border:1px solid var(--gx-line);
  --gxhx-accent:var(--c-accent);
  /* DEAD-SCENE FLOOR. These four are the resting composition, keyed off
     --card-i, and the animator's INLINE writes override all of them on frame 1
     — so they only ever render if the ANIMATORS key is missing or mis-spliced,
     which no gate in this project catches (MASTER-PLAN §2.1). Without them a
     dead scene stacked all 8 cards on the exact same rect: 6,491,520px2 of
     superimposed headlines. With them it degrades to a legible receding column
     — card 0 crisp and centred, 1-2 behind it, the rest at opacity <=0 and
     therefore silent.
     The rise is the SAME .86 x card-height pitch the animator uses (round 4),
     derived from --gxhx-card-h by calc() so the two cannot drift. At the old
     flat 190px the dead scene reproduced exactly the overlap the live scene was
     being fixed for. */
  --card-y:calc(var(--card-i,0) * var(--gxhx-card-h) * .86);
  --card-z:calc(var(--card-i,0) * -190px);
  --card-scale:calc(1 - var(--card-i,0) * .07);
  --card-opacity:calc(1 - var(--card-i,0) * .35);
  opacity:var(--card-opacity,1);
  filter:blur(var(--card-blur,0px)) brightness(var(--card-brightness,1)) saturate(var(--card-saturation,1));
  transform:translate(-50%,-50%)
    translate3d(var(--card-x,0px),var(--card-y,0px),var(--card-z,0px))
    rotateX(var(--card-rx,0deg)) rotateY(var(--card-ry,0deg)) scale(var(--card-scale,1));
  will-change:transform,filter,opacity}
/* NB: no `transform-style:preserve-3d` here. `overflow:hidden` above forces the
   used value of transform-style to `flat` (CSS Transforms 2 §grouping), so the
   declaration was silently dead — the same rule panorama cites as its reason
   for refusing the donor's overflow:hidden. The card has no 3D descendants, so
   flat is what it wants; shipping both would have misled whoever adds one. */
/* SELECTOR-SIDE COPY OF THE SLOT ORDINAL — the dead-scene floor's backstop, the
   same construction panorama ships (its skin.css:165-171). The floor above keys
   entirely off --card-i, and --card-i's only other source is the renderer's
   inline `style="--card-i:N"`. That inline value is count-general (it is the
   loop integer, so it keeps working if maxItems is ever raised) but it is also a
   single point of failure: strip the style attribute — a re-assembly path, a
   sanitiser, edit-site.js — and every card falls back to the rule default
   --card-i:0, which is the 8-cards-on-one-rect pile the floor exists to
   prevent. The ladder covers the registry's declared range (minItems 5 ..
   maxItems 8) from the stylesheet alone. :nth-of-type, not :nth-child: the
   .gx-helix-armature is a <div> and the cards are <article>, so type-counting
   keeps the phase even though the armature is the stage's first child. Card 0
   is nth-of-type(1) and wants --card-i:0, which is already the rule default.
   Inline wins over these when present; the two agree by construction, so which
   one wins is unobservable. */
.gx-helix-card:nth-of-type(2){--card-i:1}
.gx-helix-card:nth-of-type(3){--card-i:2}
.gx-helix-card:nth-of-type(4){--card-i:3}
.gx-helix-card:nth-of-type(5){--card-i:4}
.gx-helix-card:nth-of-type(6){--card-i:5}
.gx-helix-card:nth-of-type(7){--card-i:6}
.gx-helix-card:nth-of-type(8){--card-i:7}
/* token-derived accent cycle (donor shipped inline per-item hexes); :nth-of-type
   so the armature div never shifts the phase */
.gx-helix-card:nth-of-type(6n+2){--gxhx-accent:color-mix(in srgb,var(--c-accent) 62%,var(--c-ink))}
.gx-helix-card:nth-of-type(6n+3){--gxhx-accent:var(--c-glow2)}
.gx-helix-card:nth-of-type(6n+4){--gxhx-accent:color-mix(in srgb,var(--c-accent) 55%,var(--c-glow2))}
.gx-helix-card:nth-of-type(6n+5){--gxhx-accent:var(--c-glow3)}
.gx-helix-card:nth-of-type(6n){--gxhx-accent:color-mix(in srgb,var(--c-glow2) 60%,var(--c-ink))}
/* donor mouse glow, re-derived as the scroll focus glow */
.gx-helix-card::before{position:absolute;inset:0;z-index:0;border-radius:inherit;content:"";
  pointer-events:none;opacity:var(--card-focus,0);
  background:radial-gradient(circle at 24% 10%,
    color-mix(in srgb,var(--gxhx-accent) 24%,transparent),transparent 48%)}
/* the accent cycle's one visible edge — a hairline, not a filled band */
.gx-helix-card::after{position:absolute;top:0;right:24%;left:24%;height:1px;content:"";
  pointer-events:none;opacity:calc(.3 + var(--card-focus,0) * .7);
  background:linear-gradient(90deg,transparent,var(--gxhx-accent),transparent)}
.gx-helix-content{position:relative;z-index:2;display:flex;flex-direction:column;margin-top:auto}
.gx-helix-card h3{max-width:24ch;font-size:clamp(1.6rem,2.3vw,2.6rem);line-height:1.04}
.gx-helix-card p{max-width:46ch;font-size:clamp(.95rem,1.05vw,1.1rem);line-height:1.55}
.gx-helix-card .gx-kicker{color:var(--gxhx-accent)}
/* the ONE numbering format on this card (donor eyebrow counter dropped) */
.gx-helix-card .gx-index{position:absolute;top:18px;right:26px;z-index:1;
  color:color-mix(in srgb,var(--gxhx-accent) 22%,var(--gx-line-strong));
  font-family:var(--f-utility);font-size:5.2rem;font-weight:800;
  letter-spacing:-.08em;line-height:1}
/* ---- helix: static collapse <=900px — breakout reverted, scene unpinned, the
   helix flattened to a plain readable card column (armature hidden: rings that
   no longer surround anything read as decoration). Every animated property is
   neutralised so a dead frame loop can never leave a card at opacity 0. ---- */
@media (max-width:900px){
  .gx-helix{min-height:auto;width:100%;margin-left:0}
  .gx-helix .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-helix .gx-stage{display:grid;gap:14px;width:min(100%,1180px);margin-inline:auto;
    min-height:auto;padding:0 14px;overflow:visible;perspective:none;place-items:stretch}
  .gx-helix-armature{display:none}
  .gx-helix-card{position:relative;top:auto;left:auto;flex:none;
    width:100%;min-height:250px;padding:24px 20px 26px;border-radius:22px;
    transform:none!important;opacity:1!important;filter:none!important}
  .gx-helix-card::before{opacity:.3}
  .gx-helix-card::after{opacity:.55}
  .gx-helix-card h3{font-size:1.85rem}
  .gx-helix-card p{max-width:none}
  .gx-helix-card .gx-index{top:14px;right:18px;font-size:4.2rem}
}
/* ---- helix: reduced-motion still state — the same flattened column at every
   width, with the --scene-progress-driven armature dropped rather than frozen
   mid-drift ---- */
@media (prefers-reduced-motion:reduce){
  .gx-helix{min-height:auto;width:100%;margin-left:0}
  .gx-helix .gx-sticky{position:relative;top:auto;min-height:auto;padding:24px 0 30px;overflow:visible}
  .gx-helix .gx-stage{display:grid;gap:14px;width:min(100%,1180px);margin-inline:auto;
    min-height:auto;padding:0 14px;overflow:visible;perspective:none;place-items:stretch}
  .gx-helix-armature{display:none}
  .gx-helix-card{position:relative;top:auto;left:auto;flex:none;
    width:100%;min-height:250px;padding:24px 20px 26px;border-radius:22px;
    transform:none!important;opacity:1!important;filter:none!important;
    transition:none;animation:none}
  .gx-helix-card::before{opacity:.3}
  .gx-helix-card::after{opacity:.55}
  .gx-helix-card h3{font-size:clamp(1.5rem,4vw,2.2rem)}
  .gx-helix-card .gx-index{top:14px;right:18px;font-size:4.2rem}
}


/* ---- skin: path (process-v4 process-step-02 "Blueprint journey path") —
   PROCESS family, takeover. A surveyor's route drawn across a blueprint grid:
   numbered checkpoints alternate above and below a winding line, the route
   inks itself in as the section scrolls, and the checkpoint you are reading
   blooms while its card lifts into focus.

   Port decisions (donor -> engine):
   - Donor `.ps-path .ps-sticky` painted an OPAQUE `#071421` plate plus two
     full-bleed washes. Both are gone (GL-transparency decree): the sticky is
     transparent and the only surviving backdrop is `.gxp-pt-grid`, a token
     hairline grid at 6% masked to nothing at the edges — the pack's own GL
     field reads straight through it.
   - Donor `.path-runner` (a free-floating dot riding getPointAtLength) is
     DROPPED. Seating it needs per-frame `left`/`top` writes, which are layout
     properties — the exact class of defect skin-lint rule 7 exists to stop
     (`left:var(--wipe)` cost +211 LayoutCount over 300 frames). The route's own
     ink reveal (`stroke-dashoffset`, a paint property) plus the checkpoint
     bloom carry the same motion for zero layout.
   - Donor `.path-card p{max-height:calc(var(--local) * 90px)}` — a per-frame
     LAYOUT property on every card, every frame — is replaced by an opacity
     fade. Nothing here animates geometry off `--local`/`--delta`.
   - Donor per-item hexes (#61e8ff/#b08cff/#ffbc65) and the `pathGradient` stops
     become the host token cycle; the route gradient reads --c-glow2/accent/glow3.
   - Donor `.path-kicker` text was the literal string "Checkpoint 01" — invented
     copy AND a second numbering format on a card that already carries a numeral.
     Dropped; the kicker is `stepKicker()` (semantic `item.value` only) and the
     node numeral is the card's ONE numbering format.
   - Donor fixed `min-height:560px` map became viewport-relative so the route is
     not a thin strip on a 1600x1200 monitor.
   Count-generality: checkpoints are seated at render time from the item index
   (x spread evenly, y alternating), and the route `d` is built through those
   same points — so 5, 6, 7 and 8 items all produce a correctly joined curve.
   `--pt-w` divides the available width by the live count, so the cards cannot
   collide as the count grows (same-side neighbours are 2 slots apart). ---- */
.gx-path{min-height:min(320svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-path .gx-sticky{padding:0}
.gx-path .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 110px),900px)}

/* blueprint field — all that survives of the donor's opaque plate */
.gx-path .gxp-pt-grid{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(color-mix(in srgb,var(--c-accent) 6%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-accent) 6%,transparent) 1px,transparent 1px);
  background-size:46px 46px;
  -webkit-mask-image:radial-gradient(ellipse at 50% 50%,#000,transparent 82%);
  mask-image:radial-gradient(ellipse at 50% 50%,#000,transparent 82%)}

/* the map holds the route and every checkpoint. `--pt-n` is the live item
   count, written ONCE by the renderer (never per frame) — it is what keeps the
   card width count-general. */
.gx-path .gxp-pt-map{--pt-n:5;--pt-w:clamp(146px,calc(80vw / var(--pt-n)),268px);
  position:absolute;inset:clamp(26px,5vh,64px) clamp(24px,10vw,168px) clamp(58px,7vh,86px)}
.gx-path .gxp-pt-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gx-path .gxp-pt-base{fill:none;stroke:var(--gx-line-strong);stroke-width:2;
  stroke-dasharray:4 12;vector-effect:non-scaling-stroke}
/* the ink reveal. stroke-dashoffset is a PAINT property, so this is continuous
   motion CSS owns outright — the animator writes nothing for it. */
.gx-path .gxp-pt-active{fill:none;stroke:url(#gxpPathGrad);stroke-width:3;
  stroke-dasharray:1;stroke-dashoffset:calc(1 - var(--scene-progress,0));
  vector-effect:non-scaling-stroke;
  filter:drop-shadow(0 0 10px color-mix(in srgb,var(--c-accent) 52%,transparent))}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The composition before the first frame, with JS off, and the one every
   visitor would see permanently if the `ANIMATORS` key were ever mis-keyed — a
   state no gate in this project catches (MASTER-PLAN §2.1). It is not a guess:
   it is the animator's own progress-0 frame transcribed into calc(). At
   progress 0 `stepWalk` computes pos=0, so delta_i = i and
   local_i = clamp(1 - i * 1.15) — which is exactly what the two lines below
   say, with `--step-i` standing in for the slot ordinal. Checkpoint 01 is
   therefore lit and readable and the rest are dimmed, instead of every card
   sitting at the CSS default (`--local:0`, i.e. a scene with nothing lit).
   Both the renderer's inline `--step-i` and the `:nth-of-type` ladder below
   set it: the inline value is count-general (it is the loop integer, so it
   survives a raised maxItems), the ladder covers the registry's declared range
   from the stylesheet alone so the floor survives a renderer that lost the
   style attribute. They agree by construction. */
.gx-path .gxp-pt-step{--step-i:0;--pt-side:1;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.15);
  position:absolute;width:var(--pt-w);pointer-events:none;
  transform:translate(-50%,-50%)}
.gx-path .gxp-pt-step:nth-of-type(2){--step-i:1}
.gx-path .gxp-pt-step:nth-of-type(3){--step-i:2}
.gx-path .gxp-pt-step:nth-of-type(4){--step-i:3}
.gx-path .gxp-pt-step:nth-of-type(5){--step-i:4}
.gx-path .gxp-pt-step:nth-of-type(6){--step-i:5}
.gx-path .gxp-pt-step:nth-of-type(7){--step-i:6}
.gx-path .gxp-pt-step:nth-of-type(8){--step-i:7}

.gx-path .gxp-pt-node{position:absolute;top:50%;left:50%;display:grid;
  width:clamp(44px,3.6vw,58px);aspect-ratio:1;place-items:center;border-radius:50%;
  color:var(--c-on-accent);font-family:var(--f-utility);font-size:.85rem;font-weight:800;
  background:color-mix(in srgb,var(--c-accent) calc(28% + var(--local) * 62%),var(--c-dominant));
  box-shadow:0 0 calc(6px + var(--local) * 34px) color-mix(in srgb,var(--c-accent) 62%,transparent),
    0 0 0 1px var(--gx-line);
  transform:translate(-50%,-50%) scale(calc(.8 + var(--local) * .34))}

/* the card sits on the OPPOSITE side of the route from its neighbours
   (`--pt-side`, written once at render time), so a card never lands on the
   line and same-side neighbours are two slots apart. */
.gx-path .gxp-pt-card{position:absolute;top:50%;left:50%;width:100%;
  padding:15px 18px 17px;border:1px solid var(--gx-line);border-radius:18px;
  background:linear-gradient(150deg,var(--gx-panel-strong),var(--gx-panel-deep));
  box-shadow:0 24px 60px -28px rgba(0,0,0,.6);
  opacity:calc(.3 + var(--local) * .7);
  transform:translate(-50%,-50%)
    translateY(calc(var(--pt-side) * clamp(82px,9vh,118px)))
    translateY(calc((1 - var(--local)) * 14px))
    scale(calc(.93 + var(--local) * .07))}
.gx-path .gxp-pt-card h3{margin:0;color:var(--c-ink);font-family:var(--f-display);
  font-weight:var(--fw-display);font-size:clamp(1rem,1.35vw,1.34rem);
  letter-spacing:-.03em;line-height:1.08}
.gx-path .gxp-pt-card p{margin:7px 0 0;color:var(--gx-muted);
  font-size:clamp(.76rem,.86vw,.87rem);line-height:1.45;
  opacity:clamp(0,calc(var(--local) * 1.6 - .25),1);
  display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:3;overflow:hidden}
.gx-path .gxp-pt-card .gx-kicker{margin-bottom:7px;font-size:.63rem;
  color:var(--step-accent,var(--c-accent))}

/* the survey scale — the one piece of scene chrome, and its bar is driven by
   --scene-progress through a TRANSFORM (never width) */
.gx-path .gxp-pt-scale{position:absolute;right:clamp(24px,5vw,80px);
  bottom:clamp(18px,2.4vh,30px);left:clamp(24px,5vw,80px);z-index:6;
  display:flex;align-items:center;gap:13px;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase}
.gx-path .gxp-pt-scale i{position:relative;flex:1;height:1px;background:var(--gx-line)}
.gx-path .gxp-pt-scale i::after{position:absolute;inset:0;content:"";
  background:linear-gradient(90deg,var(--c-glow2),var(--c-accent),var(--c-glow3));
  transform:scaleX(var(--scene-progress,0));transform-origin:left}
.gx-path .gxp-pt-scale strong{color:var(--c-ink)}

@media (min-width:901px) and (max-height:820px){
  .gx-path .gx-stage{min-height:clamp(470px,calc(100svh - 92px),720px)}
  .gx-path .gxp-pt-card{padding:12px 15px 14px}
  .gx-path .gxp-pt-card p{-webkit-line-clamp:2}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the
   route becomes a vertical rule and every checkpoint is a full-width read.
   Never blank, never a pile. ---- */
@media (max-width:900px){
  .gx-path{min-height:auto;width:100%;margin-left:0}
  .gx-path .gx-sticky{position:relative;min-height:auto;padding:10px 0 26px;overflow:visible}
  .gx-path.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-path .gxp-pt-grid,.gx-path .gxp-pt-svg,.gx-path .gxp-pt-scale{display:none}
  .gx-path .gxp-pt-map{position:relative;inset:auto;display:grid;gap:16px;
    padding-left:44px}
  .gx-path .gxp-pt-map::before{position:absolute;top:14px;bottom:14px;left:21px;
    width:2px;content:"";
    background:linear-gradient(var(--c-glow2),var(--c-accent),var(--c-glow3))}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --local/--delta left by a desktop -> mobile resize must never
     be able to dim or displace a card (shipped-skin precedent, engine.css). */
  /* `top`/`left` MUST be neutralised, and with !important. The renderer writes
     the checkpoint's seat as an INLINE `top:X%;left:Y%`, which is absolute
     placement while the step is `position:absolute` — but on a
     `position:relative` flow item those same properties become OFFSETS from the
     normal position, so every step was displaced by a percentage of its own box.
     Measured at 380px, n=8: steps 181px tall sitting only 104px apart, 18.1%
     card-on-card overlap. Inline styles beat any non-!important rule, which is
     why `!important` is required here rather than merely `top:auto`. */
  .gx-path .gxp-pt-step{position:relative;width:100%;transform:none!important;
    top:auto!important;left:auto!important;padding:2px 0 2px 8px}
  .gx-path .gxp-pt-node{position:absolute;top:14px;left:-44px;
    width:38px;transform:none!important;
    background:color-mix(in srgb,var(--c-accent) 82%,var(--c-dominant));
    box-shadow:0 0 0 1px var(--gx-line)}
  .gx-path .gxp-pt-card{position:relative;top:auto;left:auto;width:100%;
    padding:16px 18px 18px;opacity:1!important;transform:none!important}
  .gx-path .gxp-pt-card p{opacity:1!important;-webkit-line-clamp:none;overflow:visible;
    font-size:.92rem}
  .gx-path .gxp-pt-card h3{font-size:1.16rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress, because the frame loop that
   writes it never runs. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-path{min-height:auto;width:100%;margin-left:0}
  .gx-path .gx-sticky{position:relative;min-height:auto;padding:10px 0 30px;overflow:visible}
  .gx-path.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-path .gxp-pt-grid,.gx-path .gxp-pt-svg,.gx-path .gxp-pt-scale{display:none}
  .gx-path .gxp-pt-map{position:relative;inset:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(266px,1fr));gap:18px;padding-left:0}
  /* same inline top/left neutralisation as the <=900px collapse above */
  .gx-path .gxp-pt-step{position:relative;width:100%;transform:none!important;
    top:auto!important;left:auto!important;
    display:grid;grid-template-columns:auto minmax(0,1fr);gap:14px;align-items:start}
  .gx-path .gxp-pt-node{position:relative;top:auto;left:auto;width:40px;
    transform:none!important;
    background:color-mix(in srgb,var(--c-accent) 82%,var(--c-dominant));
    box-shadow:0 0 0 1px var(--gx-line)}
  .gx-path .gxp-pt-card{position:relative;top:auto;left:auto;width:100%;
    padding:16px 18px 18px;opacity:1!important;transform:none!important}
  .gx-path .gxp-pt-card p{opacity:1!important;-webkit-line-clamp:none;overflow:visible;
    font-size:.93rem}
  .gx-path .gxp-pt-card h3{font-size:1.18rem}
}


/* ---- skin: stairsteps (process-v4 process-step-03 "Giant typographic
   staircase") — PROCESS family, takeover. The step number IS the architecture:
   a colossal numeral climbs away from the camera on a diagonal while its copy
   block holds the lower right, each step rotating slightly as it recedes so the
   run reads as a flight of stairs rather than a stack.

   Inner namespace is `gxp-ts-`, NOT `gxp-st-`: `gxp-stair-` already belongs to
   the shipped `depthstair` skin and a two-letter prefix there would be a
   maintenance trap even though CSS class matching is exact.

   Port decisions (donor -> engine):
   - Donor `.ps-staircase .ps-sticky` was an opaque cream plate
     (`linear-gradient(145deg,#f1eee7,#e6e0d5,#d8d0c3)`) with `#141519` ink —
     a LIGHT theme hard-coded into the widget. Both are gone: the sticky is
     transparent, ink is `--c-ink`, and the only backdrop is `.gxp-ts-grid`, a
     token hairline grid at 5% masked on the diagonal (donor's `.stair-grid`
     geometry kept, its `rgba(20,21,25,.055)` colour re-derived). The skin now
     reads correctly on dark AND light packs instead of on cream alone.
   - Donor per-item hexes (#9a4c31 kicker, #a14d31 meter dot, #57544f body)
     become the host token cycle / `--gx-muted`.
   - Donor `.stair-edge` was a decorative vertical word with no spec field
     behind it — dropped (never invent copy, §2.10).
   - Donor `.ps-scroll-cue` (an animated "scroll" hint) is demo chrome and was
     already stripped at extraction; it is not reinstated.
   - Donor `.stair-meter` is kept: it is a functional active-step indicator, the
     explicit de-AI carve-out the `chapters` rail established. Its `01..0N` is a
     SEPARATE element from the step's giant numeral, so each element still
     carries exactly one numbering format.
   Count-generality: every geometric term is `--step-i` (or the animator's
   `--delta`) times a constant, so 5, 6, 7 and 8 items all produce the same
   evenly-pitched flight; the meter's column count comes from the live count. ---- */
.gx-stairsteps{min-height:min(330svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-stairsteps .gx-sticky{padding:0}
.gx-stairsteps .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 100px),920px)}

.gx-stairsteps .gxp-ts-grid{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(color-mix(in srgb,var(--c-ink) 5%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 5%,transparent) 1px,transparent 1px);
  background-size:56px 56px;
  -webkit-mask-image:linear-gradient(135deg,#000,transparent 74%);
  mask-image:linear-gradient(135deg,#000,transparent 74%)}

/* the flight. perspective lives here, not on `.gx-stage`, so the meter and the
   grid stay flat while only the steps recede.
   `--ts-pitch` compresses the flight past 6 treads so the last one still lands
   on the stage; the renderer writes it ONCE from the live count using the same
   expression the animator computes, so the floor and the animated state cannot
   drift apart. */
.gx-stairsteps .gxp-ts-stack{--ts-pitch:1;position:absolute;inset:0;
  perspective:1600px;perspective-origin:32% 62%;transform-style:preserve-3d}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame, transcribed into calc(). At progress 0
   `stepWalk` gives pos=0, so delta_i = i, and every term below is the animator's
   expression with `--step-i` substituted for the slot delta. Without it every
   step falls back to `--local:0` / no transform — n colossal numerals exactly
   superimposed at stage centre with NOTHING lit, which is what a mis-keyed
   ANIMATORS entry would ship permanently and what no gate in this project
   catches (MASTER-PLAN §2.1).
   `--step-i` is set twice on purpose: the renderer's inline value is
   count-general (it is the loop integer, so it survives a raised maxItems), the
   `:nth-of-type` ladder covers the declared range from the stylesheet alone so
   the floor survives a renderer that lost the style attribute. They agree by
   construction, so which one wins is unobservable. */
.gx-stairsteps .gxp-ts-step{--step-i:0;
  --delta:var(--step-i);
  --local:max(0,1 - var(--step-i) * 1.25);
  --ts-x:calc(var(--step-i) * 116px * var(--ts-pitch,1));
  --ts-y:calc(var(--step-i) * -94px * var(--ts-pitch,1));
  --ts-z:calc(var(--step-i) * -262px * var(--ts-pitch,1));
  --ts-r:calc(var(--step-i) * -2.4deg);
  --ts-scale:max(.6,calc(1 - var(--step-i) * .1));
  --ts-opacity:max(.04,calc(1 - var(--step-i) * .34 - max(0,var(--step-i) - 1) * .4));
  position:absolute;top:52%;left:50%;width:min(78vw,1120px);height:min(56vh,540px);
  pointer-events:none;transform-origin:20% 80%;
  opacity:var(--ts-opacity);
  transform:translate(-50%,-50%)
    translate3d(var(--ts-x),var(--ts-y),var(--ts-z))
    rotateZ(var(--ts-r)) scale(var(--ts-scale));
  will-change:transform,opacity}
.gx-stairsteps .gxp-ts-step:nth-of-type(2){--step-i:1}
.gx-stairsteps .gxp-ts-step:nth-of-type(3){--step-i:2}
.gx-stairsteps .gxp-ts-step:nth-of-type(4){--step-i:3}
.gx-stairsteps .gxp-ts-step:nth-of-type(5){--step-i:4}
.gx-stairsteps .gxp-ts-step:nth-of-type(6){--step-i:5}
.gx-stairsteps .gxp-ts-step:nth-of-type(7){--step-i:6}
.gx-stairsteps .gxp-ts-step:nth-of-type(8){--step-i:7}
.gx-stairsteps .gxp-ts-step.is-active{pointer-events:auto}

/* the numeral is the architecture — it stays legible while dim, which is what
   lets a receded step still read as a stair tread */
.gx-stairsteps .gxp-ts-number{position:absolute;bottom:-.12em;left:0;
  color:var(--c-ink);font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(11rem,29vw,30rem);letter-spacing:-.13em;line-height:.68;
  opacity:calc(.09 + var(--local) * .74)}
.gx-stairsteps .gxp-ts-number::after{position:absolute;right:.06em;bottom:.22em;
  width:.34em;height:2px;content:"";
  background:linear-gradient(90deg,var(--step-accent,var(--c-accent)),transparent);
  opacity:var(--local)}

.gx-stairsteps .gxp-ts-copy{position:absolute;right:3%;bottom:9%;z-index:2;
  width:min(32vw,450px);opacity:var(--local);
  transform:translateY(calc((1 - var(--local)) * 46px))}
.gx-stairsteps .gxp-ts-copy .gx-kicker{color:var(--step-accent,var(--c-accent));
  margin-bottom:9px}
.gx-stairsteps .gxp-ts-copy h3{margin:0 0 14px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.4rem,5.4vw,5.4rem);letter-spacing:-.075em;line-height:.86;
  text-wrap:balance}
.gx-stairsteps .gxp-ts-copy p{max-width:520px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.3vw,1.14rem);line-height:1.5}

/* functional active-step meter (chapters-rail precedent: rail dots ARE the
   active-step indicator, the one de-AI carve-out) */
.gx-stairsteps .gxp-ts-meter{--ts-n:5;position:absolute;
  right:clamp(24px,5vw,82px);bottom:clamp(20px,3vh,36px);left:clamp(24px,5vw,82px);
  z-index:25;display:grid;grid-template-columns:repeat(var(--ts-n),minmax(0,1fr));gap:16px}
.gx-stairsteps .gxp-ts-meter::before{position:absolute;top:7px;right:0;left:0;
  height:1px;content:"";background:var(--gx-line)}
.gx-stairsteps .gxp-ts-meter span{--nav-local:0;position:relative;padding-top:20px;
  color:color-mix(in srgb,var(--c-ink) calc(34% + var(--nav-local) * 66%),transparent);
  font-family:var(--f-utility)}
.gx-stairsteps .gxp-ts-meter span::before{position:absolute;top:2px;left:0;
  width:11px;aspect-ratio:1;border-radius:50%;content:"";
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 100%),var(--gx-line-strong));
  box-shadow:0 0 calc(var(--nav-local) * 22px) color-mix(in srgb,var(--c-accent) 55%,transparent)}
.gx-stairsteps .gxp-ts-meter b{display:block;font-size:.75rem;font-weight:800;
  letter-spacing:.06em}
.gx-stairsteps .gxp-ts-meter i{display:block;margin-top:3px;font-style:normal;
  font-size:.66rem;font-weight:700;letter-spacing:.1em;text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

@media (min-width:901px) and (max-height:820px){
  .gx-stairsteps .gx-stage{min-height:clamp(470px,calc(100svh - 88px),740px)}
  .gx-stairsteps .gxp-ts-step{height:min(60vh,470px)}
  .gx-stairsteps .gxp-ts-number{font-size:clamp(9rem,24vw,23rem)}
  .gx-stairsteps .gxp-ts-copy h3{font-size:clamp(2.1rem,4.4vw,4.2rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned, the
   flight opens out into a stack of full-width treads. ---- */
@media (max-width:900px){
  .gx-stairsteps{min-height:auto;width:100%;margin-left:0}
  .gx-stairsteps .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-stairsteps.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-stairsteps .gxp-ts-grid,.gx-stairsteps .gxp-ts-meter{display:none}
  .gx-stairsteps .gxp-ts-stack{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --ts-x or --local left by a desktop -> mobile resize must never
     be able to displace or blank a tread (shipped-skin precedent). */
  .gx-stairsteps .gxp-ts-step{position:relative;top:auto;left:auto;
    width:100%;height:auto;min-height:0;padding:30px 0 34px;
    border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important;transform-origin:50% 50%;
    will-change:auto}
  .gx-stairsteps .gxp-ts-number{position:relative;bottom:auto;
    font-size:7.4rem;opacity:.22!important;line-height:.8}
  .gx-stairsteps .gxp-ts-number::after{display:none}
  .gx-stairsteps .gxp-ts-copy{position:relative;right:auto;bottom:auto;
    width:100%;margin-top:6px;opacity:1!important;transform:none!important}
  .gx-stairsteps .gxp-ts-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-stairsteps .gxp-ts-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-stairsteps{min-height:auto;width:100%;margin-left:0}
  .gx-stairsteps .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-stairsteps.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-stairsteps .gxp-ts-grid,.gx-stairsteps .gxp-ts-meter{display:none}
  .gx-stairsteps .gxp-ts-stack{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  .gx-stairsteps .gxp-ts-step{position:relative;top:auto;left:auto;
    width:100%;height:auto;min-height:0;padding:34px 0 38px;
    border-bottom:1px solid var(--gx-line);
    display:grid;grid-template-columns:minmax(0,.34fr) minmax(0,1fr);
    gap:clamp(18px,4vw,54px);align-items:center;
    opacity:1!important;transform:none!important;transform-origin:50% 50%;
    will-change:auto}
  .gx-stairsteps .gxp-ts-number{position:relative;bottom:auto;
    font-size:clamp(5rem,12vw,9rem);opacity:.3!important;line-height:.8}
  .gx-stairsteps .gxp-ts-number::after{display:none}
  .gx-stairsteps .gxp-ts-copy{position:relative;right:auto;bottom:auto;
    width:100%;opacity:1!important;transform:none!important}
  .gx-stairsteps .gxp-ts-copy h3{font-size:clamp(1.9rem,3.6vw,3rem)}
  .gx-stairsteps .gxp-ts-copy p{font-size:.97rem}
}


/* ---- skin: elevator (process-v4 process-step-05 "Process elevator") —
   PROCESS family, takeover. A glass shaft on the left with one marked landing
   per step; the car rides the shaft as the section scrolls and its doors part
   as it settles on a landing, while the copy for that step crossfades in on the
   right.

   Port decisions (donor -> engine):
   - Donor `.ps-elevator .ps-sticky` painted a HARD SPLIT plate
     (`linear-gradient(90deg,#090d13 0 43%,#eee9df 43% 100%)`) — an opaque
     two-tone box, and the exact "filled plane with hard rectangular edges" bug
     class the ledger has already paid for twice. Gone. The split survives only
     as `.gxp-ev-well`, a token wash at 7% whose right edge is MASKED to
     transparent, so there is no visible cut line and the pack's GL field reads
     through both halves.
   - Donor `.elevator-car{will-change:top}` and the JS that wrote `top` per
     frame are replaced by a transform: `--ev-car` is a 0-1 fraction multiplied
     by `--ev-h`, the shaft's own height token. Nothing here animates a layout
     property (skin-lint rule 7).
   - Donor hexes (#ffbc65 dot, #a34e31 kicker, #d9dde2/#6d737b door metal,
     #fff1cd lamp) become host tokens; the doors are `--c-ink`-derived metal so
     they read on light packs as well as dark.
   - Donor `.elevator-car__top` carried a hard-coded floor-indicator readout in
     a monospace face; it is kept as the ONE live readout (`data-gxp-active`),
     fed from the walk, not from invented copy.
   - Donor fixed `min-height:560px` shaft became viewport-relative so the shaft
     is not a short stub on a 1600x1200 monitor.
   Count-generality: landings are seated at render time from the loop index as a
   percentage of the shaft, and the car's travel is the same 0-1 fraction, so
   5-8 steps all land the car exactly on its marks. ---- */
.gx-elevator{min-height:min(330svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-elevator .gx-sticky{padding:0}
.gx-elevator .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 100px),920px)}

/* all that survives of the donor's opaque split plate: a masked token wash */
.gx-elevator .gxp-ev-well{position:absolute;inset:0;pointer-events:none;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--c-dominant) 7%,transparent),
    color-mix(in srgb,var(--c-dominant) 7%,transparent) 40%,
    transparent 62%);
  -webkit-mask-image:linear-gradient(90deg,#000,#000 38%,transparent 68%);
  mask-image:linear-gradient(90deg,#000,#000 38%,transparent 68%)}

.gx-elevator .gxp-ev-world{position:absolute;
  inset:clamp(20px,4vh,54px) clamp(20px,4vw,72px) clamp(56px,7vh,84px);
  display:grid;grid-template-columns:minmax(0,.42fr) minmax(0,.58fr);
  gap:clamp(18px,4vw,60px);align-items:center}

/* the shaft. `--ev-h` is the ONE length the landings and the car both measure
   against, so the car cannot drift off its marks at any viewport. */
.gx-elevator .gxp-ev-shaft{--ev-h:clamp(400px,58svh,600px);
  position:relative;width:min(64%,286px);height:var(--ev-h);margin:auto;
  border-left:1px solid var(--gx-line);border-right:1px solid var(--gx-line);
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--c-ink) 3%,transparent),
    color-mix(in srgb,var(--c-ink) 7%,transparent),
    color-mix(in srgb,var(--c-ink) 3%,transparent))}
.gx-elevator .gxp-ev-cable{position:absolute;top:-14%;bottom:0;left:50%;width:1px;
  background:linear-gradient(color-mix(in srgb,var(--c-ink) 6%,transparent),
    color-mix(in srgb,var(--c-accent) 34%,transparent));
  transform:translateX(-50%)}

/* landings — seated at render time (static inline `top`), lit by the walk */
.gx-elevator .gxp-ev-floor{--nav-local:0;position:absolute;right:0;left:0;height:1px;
  background:var(--gx-line);transform:translateY(-50%)}
.gx-elevator .gxp-ev-floor i{position:absolute;top:50%;right:-11px;
  width:20px;aspect-ratio:1;border-radius:50%;
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 100%),var(--gx-line-strong));
  box-shadow:0 0 calc(var(--nav-local) * 26px) color-mix(in srgb,var(--c-accent) 62%,transparent);
  transform:translateY(-50%) scale(calc(.72 + var(--nav-local) * .34))}
.gx-elevator .gxp-ev-floor b{position:absolute;top:50%;right:calc(100% + 18px);
  color:color-mix(in srgb,var(--c-ink) calc(38% + var(--nav-local) * 62%),transparent);
  font-family:var(--f-utility);font-size:1.05rem;font-weight:800;
  transform:translateY(-50%)}
.gx-elevator .gxp-ev-floor strong{position:absolute;top:50%;left:calc(100% + 22px);
  max-width:min(11vw,104px);
  color:color-mix(in srgb,var(--c-ink) calc(30% + var(--nav-local) * 60%),transparent);
  font-family:var(--f-utility);font-size:.64rem;font-weight:700;letter-spacing:.13em;
  text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  transform:translateY(-50%)}

/* the car. `--ev-car` is a 0-1 fraction of `--ev-h` (1 = bottom landing), so
   this is a transform, never a `top` write. */
.gx-elevator .gxp-ev-car{--ev-car:1;--ev-doors:0;
  position:absolute;top:0;left:50%;z-index:6;
  width:min(76%,238px);height:clamp(110px,15svh,158px);overflow:hidden;
  border:2px solid var(--gx-line-strong);
  background:color-mix(in srgb,var(--c-dominant) 88%,var(--c-ink));
  box-shadow:0 22px 60px -24px rgba(0,0,0,.6),
    0 0 46px color-mix(in srgb,var(--c-accent) 16%,transparent);
  transform:translate(-50%,-50%) translateY(calc(var(--ev-car) * var(--ev-h)));
  will-change:transform}
.gx-elevator .gxp-ev-plate{position:absolute;right:0;bottom:100%;left:0;height:30px;
  display:grid;place-items:center;color:var(--c-accent);
  border:1px solid var(--gx-line);
  background:color-mix(in srgb,var(--c-dominant) 82%,transparent);
  font-family:var(--f-utility);font-size:.74rem;font-weight:800;letter-spacing:.18em}
.gx-elevator .gxp-ev-door{position:absolute;top:0;bottom:0;width:50%;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--c-ink) 22%,var(--c-field)),
    color-mix(in srgb,var(--c-ink) 52%,var(--c-field)) 48%,
    color-mix(in srgb,var(--c-ink) 16%,var(--c-field)))}
.gx-elevator .gxp-ev-door--left{left:0;transform:translateX(calc(var(--ev-doors) * -96%))}
.gx-elevator .gxp-ev-door--right{right:0;transform:translateX(calc(var(--ev-doors) * 96%))}
.gx-elevator .gxp-ev-lamp{position:absolute;top:11px;left:50%;width:42px;height:3px;
  background:var(--c-accent);
  box-shadow:0 0 18px color-mix(in srgb,var(--c-accent) 75%,transparent);
  transform:translateX(-50%)}

/* the copy stack — absolutely stacked so only the active step reads */
.gx-elevator .gxp-ev-stack{position:relative;height:clamp(360px,52svh,520px)}
/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(). At progress 0 `stepWalk`
   gives pos=0, so delta_i = i and local_i = clamp(1 - i * 1.35); the car sits
   at `--ev-car:1` (the bottom landing) with its doors shut, which is exactly
   what the animator writes on frame 1 at progress 0. Without it every copy
   block falls back to `--local:0` and the scene reads as an empty shaft with no
   step showing at all — the state a mis-keyed ANIMATORS entry would ship
   permanently, which no gate in this project catches (MASTER-PLAN §2.1).
   The `:nth-of-type` ladder duplicates the renderer's inline `--step-i` so the
   floor survives a renderer that lost the style attribute; they agree by
   construction. */
.gx-elevator .gxp-ev-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.35);
  position:absolute;inset:0;display:flex;flex-direction:column;justify-content:center;
  pointer-events:none;opacity:var(--local);
  transform:translateY(calc(var(--delta) * 18vh))}
.gx-elevator .gxp-ev-copy:nth-of-type(2){--step-i:1}
.gx-elevator .gxp-ev-copy:nth-of-type(3){--step-i:2}
.gx-elevator .gxp-ev-copy:nth-of-type(4){--step-i:3}
.gx-elevator .gxp-ev-copy:nth-of-type(5){--step-i:4}
.gx-elevator .gxp-ev-copy:nth-of-type(6){--step-i:5}
.gx-elevator .gxp-ev-copy:nth-of-type(7){--step-i:6}
.gx-elevator .gxp-ev-copy:nth-of-type(8){--step-i:7}
.gx-elevator .gxp-ev-copy.is-active{pointer-events:auto}
.gx-elevator .gxp-ev-ghost{position:absolute;top:0;right:0;
  color:color-mix(in srgb,var(--c-ink) 8%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(8rem,18vw,18rem);letter-spacing:-.12em;line-height:.76}
.gx-elevator .gxp-ev-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gx-elevator .gxp-ev-copy h3{max-width:640px;margin:8px 0 16px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.3rem,5.2vw,5.2rem);letter-spacing:-.075em;line-height:.86;
  text-wrap:balance}
.gx-elevator .gxp-ev-copy p{max-width:560px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.3vw,1.13rem);line-height:1.5}

.gx-elevator .gxp-ev-altimeter{position:absolute;right:clamp(24px,5vw,80px);
  bottom:clamp(18px,2.4vh,32px);left:46%;z-index:8;
  display:flex;align-items:center;gap:13px;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase}
.gx-elevator .gxp-ev-altimeter i{position:relative;flex:1;height:1px;background:var(--gx-line)}
.gx-elevator .gxp-ev-altimeter i::after{position:absolute;inset:0;content:"";
  background:linear-gradient(90deg,var(--c-glow2),var(--c-accent));
  transform:scaleX(var(--scene-progress,0));transform-origin:left}
.gx-elevator .gxp-ev-altimeter strong{color:var(--c-ink)}

@media (min-width:901px) and (max-height:820px){
  .gx-elevator .gx-stage{min-height:clamp(460px,calc(100svh - 88px),740px)}
  .gx-elevator .gxp-ev-shaft{--ev-h:clamp(330px,54svh,470px)}
  .gx-elevator .gxp-ev-stack{height:clamp(300px,48svh,420px)}
  .gx-elevator .gxp-ev-copy h3{font-size:clamp(2rem,4.2vw,4rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the
   shaft becomes a plain diagram above a stack of full-width step reads. ---- */
@media (max-width:900px){
  .gx-elevator{min-height:auto;width:100%;margin-left:0}
  .gx-elevator .gx-sticky{position:relative;min-height:auto;padding:10px 0 26px;overflow:visible}
  .gx-elevator.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-elevator .gxp-ev-well,.gx-elevator .gxp-ev-altimeter{display:none}
  .gx-elevator .gxp-ev-world{position:relative;inset:auto;display:block}
  .gx-elevator .gxp-ev-shaft{--ev-h:320px;width:min(70%,240px);margin:8px auto 26px}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --ev-car or --local left by a desktop -> mobile resize must
     never be able to strand the car or blank a step. */
  .gx-elevator .gxp-ev-car{transform:translate(-50%,-50%) translateY(var(--ev-h))!important;
    will-change:auto}
  .gx-elevator .gxp-ev-stack{position:relative;height:auto;display:grid;gap:0}
  .gx-elevator .gxp-ev-copy{position:relative;inset:auto;min-height:0;
    padding:26px 0 30px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-elevator .gxp-ev-ghost{position:relative;top:auto;right:auto;display:block;
    font-size:5.4rem;opacity:1}
  .gx-elevator .gxp-ev-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-elevator .gxp-ev-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-elevator{min-height:auto;width:100%;margin-left:0}
  .gx-elevator .gx-sticky{position:relative;min-height:auto;padding:10px 0 30px;overflow:visible}
  .gx-elevator.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-elevator .gxp-ev-well,.gx-elevator .gxp-ev-altimeter{display:none}
  .gx-elevator .gxp-ev-world{position:relative;inset:auto;display:block}
  .gx-elevator .gxp-ev-shaft{--ev-h:340px;width:min(60%,250px);margin:8px auto 30px}
  .gx-elevator .gxp-ev-car{transform:translate(-50%,-50%) translateY(var(--ev-h))!important;
    will-change:auto}
  .gx-elevator .gxp-ev-stack{position:relative;height:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px}
  .gx-elevator .gxp-ev-copy{position:relative;inset:auto;min-height:0;
    padding:24px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-elevator .gxp-ev-ghost{position:relative;top:auto;right:auto;display:block;
    font-size:clamp(4rem,9vw,7rem);opacity:1}
  .gx-elevator .gxp-ev-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-elevator .gxp-ev-copy p{font-size:.97rem}
}


/* ---- skin: constellation (process-v4 process-step-07 "Constellation
   journey") — PROCESS family, takeover. The steps are stars: a dashed
   asterism is traced between them as the section scrolls, each star blooming
   with its short label as the line reaches it, while the full copy for that
   step reads from the lower left.

   Port decisions (donor -> engine):
   - Donor `.ps-constellation .ps-sticky` painted two coloured washes over an
     opaque `linear-gradient(180deg,#05070d,#080b14)` plate. The plate is gone
     (GL-transparency decree). The star FIELD survives as `.gxp-cn-stars`,
     re-derived from tokens, because on this widget the field is the subject
     rather than demo chrome — but it is masked and capped so the pack's own GL
     particles still read through it.
   - Donor `.constellation-runner` (a dot riding getPointAtLength) is DROPPED:
     seating it costs a per-frame `left`/`top` write, i.e. layout every frame
     (skin-lint rule 7). The asterism's own ink reveal — `stroke-dashoffset`, a
     paint property — plus the star bloom carry the same motion for free.
   - Donor `.constellation-status b{width:calc(var(--...) * 100%)}` animated
     WIDTH off scroll progress. It is a `transform:scaleX()` here.
   - Donor hexes (#67e8ff, #ae8cff, #ffbd69) become the host token cycle; the
     asterism gradient reads --c-glow2/accent/glow3.
   - Donor node markup carried an invented `<strong>` caption per star; it is
     `stepLabel(item.title)` here — a real spec value, trimmed, never invented.
   Count-generality: the stars are seated at render time from a closed-form
   scatter (even x spread, sinusoidal y), and the asterism polyline is built
   through those exact points — so 5, 6, 7 and 8 steps all yield a joined
   figure with the line passing exactly through every star. ---- */
.gx-constellation{min-height:min(325svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-constellation .gx-sticky{padding:0}
.gx-constellation .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 100px),920px)}

.gx-constellation .gxp-cn-stars{position:absolute;inset:0;pointer-events:none;
  background-image:
    radial-gradient(circle,color-mix(in srgb,var(--c-ink) 55%,transparent) 0 1px,transparent 1.6px),
    radial-gradient(circle,color-mix(in srgb,var(--c-glow2) 48%,transparent) 0 1px,transparent 1.6px),
    radial-gradient(circle,color-mix(in srgb,var(--c-ink) 26%,transparent) 0 1px,transparent 1.6px);
  background-size:97px 89px,163px 137px,67px 71px;
  background-position:13px 17px,44px 61px,25px 8px;
  -webkit-mask-image:linear-gradient(180deg,#000,rgba(0,0,0,.45));
  mask-image:linear-gradient(180deg,#000,rgba(0,0,0,.45))}

.gx-constellation .gxp-cn-world{position:absolute;
  inset:clamp(20px,4vh,52px) clamp(20px,4vw,70px) 40%}
.gx-constellation .gxp-cn-svg{position:absolute;inset:0;width:100%;height:100%;
  overflow:visible}
.gx-constellation .gxp-cn-base{fill:none;stroke:var(--gx-line-strong);stroke-width:1.5;
  stroke-dasharray:3 10;vector-effect:non-scaling-stroke}
.gx-constellation .gxp-cn-active{fill:none;stroke:url(#gxpConstGrad);stroke-width:3;
  stroke-dasharray:1;stroke-dashoffset:calc(1 - var(--scene-progress,0));
  vector-effect:non-scaling-stroke;
  filter:drop-shadow(0 0 11px color-mix(in srgb,var(--c-accent) 55%,transparent))}

/* the stars. Seated at render time (static inline left/top), lit by the walk. */
.gx-constellation .gxp-cn-node{--nav-local:0;position:absolute;z-index:4;
  min-width:104px;text-align:center;font-family:var(--f-utility);
  color:color-mix(in srgb,var(--c-ink) calc(36% + var(--nav-local) * 64%),transparent);
  transform:translate(-50%,-50%)}
.gx-constellation .gxp-cn-node::before{display:block;width:16px;aspect-ratio:1;
  margin:0 auto 9px;border-radius:50%;content:"";
  background:color-mix(in srgb,var(--c-accent) calc(30% + var(--nav-local) * 70%),var(--c-ink));
  box-shadow:0 0 calc(7px + var(--nav-local) * 38px) color-mix(in srgb,var(--c-accent) 70%,transparent);
  transform:scale(calc(.7 + var(--nav-local) * .56))}
.gx-constellation .gxp-cn-node::after{position:absolute;top:8px;left:50%;width:58px;
  aspect-ratio:1;border-radius:50%;content:"";
  border:1px solid color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 48%),transparent);
  transform:translate(-50%,-50%) scale(calc(.4 + var(--nav-local) * .62))}
.gx-constellation .gxp-cn-node b{display:block;font-size:.95rem;font-weight:800}
.gx-constellation .gxp-cn-node i{display:block;font-style:normal;font-size:.61rem;
  font-weight:700;letter-spacing:.12em;text-transform:uppercase;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}

/* the copy sits on the composition's EMPTY side (lower left) — the asterism
   climbs across the upper right of the world (ledger: copy never overlaps the
   route). The stack is absolutely positioned so only the active step reads. */
.gx-constellation .gxp-cn-stack{position:absolute;bottom:6%;
  left:clamp(20px,6vw,110px);z-index:6;
  width:min(44vw,660px);height:clamp(230px,28vh,300px);pointer-events:none}
/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(): at progress 0 `stepWalk`
   gives pos=0, so delta_i = i and local_i = clamp(1 - i * 1.3). Without it
   every copy block takes `--local:0` and the scene is a star map with no step
   showing — the permanent state of a mis-keyed ANIMATORS entry, which no gate
   in this project catches (MASTER-PLAN §2.1). The `:nth-of-type` ladder
   duplicates the renderer's inline `--step-i` so the floor survives a renderer
   that lost the style attribute; the two agree by construction. */
.gx-constellation .gxp-cn-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.3);
  position:absolute;inset:0;display:flex;flex-direction:column;justify-content:flex-end;
  pointer-events:none;opacity:var(--local);
  transform:translateY(calc(var(--delta) * 15vh))}
.gx-constellation .gxp-cn-copy:nth-of-type(2){--step-i:1}
.gx-constellation .gxp-cn-copy:nth-of-type(3){--step-i:2}
.gx-constellation .gxp-cn-copy:nth-of-type(4){--step-i:3}
.gx-constellation .gxp-cn-copy:nth-of-type(5){--step-i:4}
.gx-constellation .gxp-cn-copy:nth-of-type(6){--step-i:5}
.gx-constellation .gxp-cn-copy:nth-of-type(7){--step-i:6}
.gx-constellation .gxp-cn-copy:nth-of-type(8){--step-i:7}
.gx-constellation .gxp-cn-ghost{position:absolute;right:0;bottom:-.1em;
  color:color-mix(in srgb,var(--c-ink) 6%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(8rem,17vw,17rem);letter-spacing:-.12em;line-height:.7}
.gx-constellation .gxp-cn-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gx-constellation .gxp-cn-copy h3{margin:6px 0 14px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.3rem,5vw,5rem);letter-spacing:-.072em;line-height:.88;
  text-wrap:balance}
.gx-constellation .gxp-cn-copy p{max-width:540px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.28vw,1.13rem);line-height:1.5}

.gx-constellation .gxp-cn-status{position:absolute;right:clamp(24px,5vw,80px);
  bottom:clamp(16px,2.2vh,28px);left:52%;z-index:8;
  display:flex;align-items:center;gap:12px;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.66rem;letter-spacing:.13em;text-transform:uppercase}
.gx-constellation .gxp-cn-status i{position:relative;flex:1;height:1px;background:var(--gx-line)}
.gx-constellation .gxp-cn-status i::after{position:absolute;inset:0;content:"";
  background:linear-gradient(90deg,var(--c-glow2),var(--c-accent),var(--c-glow3));
  box-shadow:0 0 12px color-mix(in srgb,var(--c-accent) 45%,transparent);
  transform:scaleX(var(--scene-progress,0));transform-origin:left}
.gx-constellation .gxp-cn-status strong{color:var(--c-ink)}

@media (min-width:901px) and (max-height:820px){
  .gx-constellation .gx-stage{min-height:clamp(460px,calc(100svh - 88px),740px)}
  .gx-constellation .gxp-cn-stack{height:clamp(200px,26vh,260px)}
  .gx-constellation .gxp-cn-copy h3{font-size:clamp(2rem,4vw,3.8rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the star
   map becomes a plain numbered list of full-width reads. ---- */
@media (max-width:900px){
  .gx-constellation{min-height:auto;width:100%;margin-left:0}
  .gx-constellation .gx-sticky{position:relative;min-height:auto;padding:10px 0 26px;overflow:visible}
  .gx-constellation.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-constellation .gxp-cn-stars,.gx-constellation .gxp-cn-svg,
  .gx-constellation .gxp-cn-node,.gx-constellation .gxp-cn-status{display:none}
  .gx-constellation .gxp-cn-world{position:relative;inset:auto;min-height:0}
  /* !important on the animated pair: the frame loop stands down here, so a
     stale inline --local/--delta left by a desktop -> mobile resize must never
     be able to blank or displace a step. */
  .gx-constellation .gxp-cn-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;gap:0}
  .gx-constellation .gxp-cn-copy{position:relative;inset:auto;
    padding:26px 0 30px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-constellation .gxp-cn-ghost{position:relative;right:auto;bottom:auto;
    display:block;font-size:5.2rem;line-height:.9}
  .gx-constellation .gxp-cn-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-constellation .gxp-cn-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-constellation{min-height:auto;width:100%;margin-left:0}
  .gx-constellation .gx-sticky{position:relative;min-height:auto;padding:10px 0 30px;overflow:visible}
  .gx-constellation.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-constellation .gxp-cn-stars,.gx-constellation .gxp-cn-svg,
  .gx-constellation .gxp-cn-node,.gx-constellation .gxp-cn-status{display:none}
  .gx-constellation .gxp-cn-world{position:relative;inset:auto;min-height:0}
  .gx-constellation .gxp-cn-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:20px}
  .gx-constellation .gxp-cn-copy{position:relative;inset:auto;
    padding:24px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-constellation .gxp-cn-ghost{position:relative;right:auto;bottom:auto;
    display:block;font-size:clamp(4rem,9vw,7rem);line-height:.9}
  .gx-constellation .gxp-cn-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-constellation .gxp-cn-copy p{font-size:.97rem}
}


/* ---- skin: editorial (process-v4 process-step-08 "Editorial scene wipe") —
   PROCESS family, takeover. A magazine spread per step: colossal numeral,
   colossal headline, and a single hard geometric mark on the right. Each step
   WIPES in over the last from the right-hand edge rather than fading, which is
   what gives the section its editorial cut.

   Port decisions (donor -> engine):
   - Donor `.editorial-step` painted a full-bleed OPAQUE `--step-bg` per step
     (its `.ps-editorial .ps-sticky` base was `#f3efe7`, i.e. the widget shipped
     a light theme inside itself). That is exactly the "filled plane with hard
     rectangular edges" the ledger strips, and it would black out the pack's GL
     field for the whole scene. The spread survives as `.gxp-ed-veil`: a graded
     token veil — accent tint at 9%, dominant at 22% falling to nothing across
     the plate — so the wipe still reads as a change of ground while the GL
     particles stay visible through it. This is a deliberate, documented
     deviation from the donor's look; see NOTES §2.
   - The wipe itself is a `clip-path:inset()` driven by `--delta`. clip-path is
     a paint/compositing property, so the mechanic costs no layout (the donor
     declared `will-change:clip-path` and did the same thing).
   - Donor `.editorial-counter` / `.editorial-nav` used `mix-blend-mode:difference`
     to stay legible over an unknown ground. Over a live GL field that is
     unpredictable, so both are token-coloured here instead.
   - Donor per-step `--step-accent` hexes become the host token cycle. The disc
     shape cycle (circle / squircle / triangle / rotated square / ring) is kept
     verbatim — it is the widget's typographic personality — and cycles by
     `--step-i` so it is correct at any item count.
   - The disc carries NO numeral: the spread already has one giant numeral, and
     one numbering format per element is a hard rule.
   Count-generality: the wipe and the shape cycle are pure functions of the slot
   index, so 5, 6, 7 and 8 steps behave identically. ---- */
.gx-editorial{min-height:min(340svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-editorial .gx-sticky{padding:0}
.gx-editorial .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,100svh,1000px)}

.gx-editorial .gxp-ed-stack{position:absolute;inset:0}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(). At progress 0 `stepWalk`
   gives pos=0, so delta_i = i: step 01 is unclipped and lit, every later step
   is clipped to zero width — precisely the animated state at the top of the
   scene. Without it every step falls back to `--delta:0` and ALL of them are
   unclipped and stacked at opacity 1, i.e. n colossal headlines superimposed
   with only the last one legible. That is the state a mis-keyed ANIMATORS
   entry ships permanently and no gate in this project catches it
   (MASTER-PLAN §2.1).
   The `:nth-of-type` ladder duplicates the renderer's inline `--step-i` so the
   floor survives a renderer that lost the style attribute; the two agree by
   construction. */
.gx-editorial .gxp-ed-step{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.2);
  position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,.78fr) minmax(0,1.22fr);
  gap:clamp(24px,5vw,100px);align-items:center;
  padding:clamp(72px,9vh,128px) clamp(24px,6vw,110px) clamp(74px,9vh,108px);
  overflow:hidden;pointer-events:none;
  /* The wipe runs LEFT-TO-RIGHT: an incoming spread reveals from its left edge
     rightwards, so its headline — which lives in the left column — is the FIRST
     thing to appear, and a passed spread is covered starting where its headline
     is. The donor clipped from the other side. Measured at 1440x900, n=6,
     progress .75: the ACTIVE spread carried `inset(0 0 0 25.11%)`, which cut
     275 of its headline's 466px — 59% of the live headline clipped away by its
     own wipe, with the previous step's headline showing through underneath.
     Reversing the inset makes the copy lead the reveal instead of trailing it.
     The PASSED spread is clipped from the left by the same amount, so mid-wipe
     the two tile the viewport exactly — outgoing mark on the right, incoming
     copy on the left — instead of superimposing two headlines in the same
     column. Measured before the change: 100% of the active headline overlapped
     by another step's copy, against 57-64% for `mountain`, the worst SHIPPED
     process skin on that metric. */
  clip-path:inset(0 clamp(0%,calc(var(--delta) * 100%),100%)
                   0 clamp(0%,calc(var(--delta) * -100%),100%));
  /* A PASSED spread must not linger. The veil is translucent by decree, so an
     unclipped passed spread would show THROUGH the one on top of it — n sets of
     colossal copy accumulating behind the current one. It cannot simply fade
     with distance either: a 50%-faded spread under a 50%-revealed one leaves a
     half-lit left region. So it stays FULLY opaque until the next spread has
     completely covered it (delta = -1) and then goes out at once. The x40 makes
     that cut-off sharp; exactly one or two spreads are ever painted. */
  opacity:clamp(0,calc((1 + var(--delta)) * 40),1);
  will-change:clip-path,opacity}
.gx-editorial .gxp-ed-step:nth-of-type(2){--step-i:1}
.gx-editorial .gxp-ed-step:nth-of-type(3){--step-i:2}
.gx-editorial .gxp-ed-step:nth-of-type(4){--step-i:3}
.gx-editorial .gxp-ed-step:nth-of-type(5){--step-i:4}
.gx-editorial .gxp-ed-step:nth-of-type(6){--step-i:5}
.gx-editorial .gxp-ed-step:nth-of-type(7){--step-i:6}
.gx-editorial .gxp-ed-step:nth-of-type(8){--step-i:7}
.gx-editorial .gxp-ed-step.is-active{pointer-events:auto}

/* the graded veil — all that survives of the donor's opaque spread */
.gx-editorial .gxp-ed-veil{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    linear-gradient(90deg,
      color-mix(in srgb,var(--step-accent,var(--c-accent)) 9%,transparent),
      transparent 62%),
    linear-gradient(255deg,
      color-mix(in srgb,var(--c-dominant) 22%,transparent),
      transparent 74%)}
.gx-editorial .gxp-ed-rule{position:absolute;top:0;bottom:0;left:0;width:2px;z-index:2;
  pointer-events:none;
  background:linear-gradient(180deg,transparent,
    color-mix(in srgb,var(--step-accent,var(--c-accent)) 70%,transparent),transparent)}

.gx-editorial .gxp-ed-copy{position:relative;z-index:4;max-width:720px;
  opacity:calc(.2 + var(--local) * .8);
  transform:translateY(calc((1 - var(--local)) * 42px))}
.gx-editorial .gxp-ed-number{display:block;margin:0 0 14px;
  color:var(--step-accent,var(--c-accent));
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(4.4rem,11vw,11rem);letter-spacing:-.105em;line-height:.64}
.gx-editorial .gxp-ed-copy .gx-kicker{margin-bottom:clamp(20px,4vh,54px)}
.gx-editorial .gxp-ed-copy h3{max-width:720px;margin:0 0 20px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.6rem,6.6vw,7.4rem);letter-spacing:-.074em;line-height:.85;
  text-wrap:balance}
.gx-editorial .gxp-ed-copy p{max-width:620px;margin:0;color:var(--gx-muted);
  font-size:clamp(1rem,1.4vw,1.22rem);line-height:1.5}

/* the mark. Shape cycles by slot; every fill is token-derived. */
.gx-editorial .gxp-ed-mark{position:relative;z-index:3;
  width:min(38vw,560px);aspect-ratio:1;margin:auto;
  transform:rotate(calc(var(--delta) * 7deg)) scale(calc(.8 + var(--local) * .2))}
.gx-editorial .gxp-ed-disc{position:absolute;inset:11%;border-radius:50%;
  background:color-mix(in srgb,var(--step-accent,var(--c-accent)) 82%,var(--c-dominant));
  box-shadow:0 42px 90px -40px rgba(0,0,0,.55)}
.gx-editorial .gxp-ed-disc::before{position:absolute;inset:16%;border-radius:50%;
  content:"";border:2px solid color-mix(in srgb,var(--c-field) 74%,transparent)}
.gx-editorial .gxp-ed-disc::after{position:absolute;right:-16%;left:-16%;top:50%;
  height:3px;content:"";background:var(--c-ink);opacity:.5;
  transform:rotate(-24deg);transform-origin:center}
.gx-editorial .gxp-ed-step:nth-of-type(2) .gxp-ed-disc{border-radius:12% 50% 12% 50%}
.gx-editorial .gxp-ed-step:nth-of-type(3) .gxp-ed-disc{border-radius:0;
  clip-path:polygon(50% 0,100% 100%,0 100%)}
.gx-editorial .gxp-ed-step:nth-of-type(4) .gxp-ed-disc{border-radius:0;
  transform:rotate(45deg) scale(.74)}
.gx-editorial .gxp-ed-step:nth-of-type(5) .gxp-ed-disc{background:none;
  border:clamp(24px,3.6vw,58px) solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 82%,var(--c-dominant))}
.gx-editorial .gxp-ed-step:nth-of-type(7) .gxp-ed-disc{border-radius:12% 50% 12% 50%}
.gx-editorial .gxp-ed-step:nth-of-type(8) .gxp-ed-disc{border-radius:0;
  clip-path:polygon(50% 0,100% 100%,0 100%)}

.gx-editorial .gxp-ed-counter{position:absolute;top:clamp(18px,3vh,34px);
  right:clamp(24px,5vw,78px);z-index:20;display:flex;align-items:center;gap:14px;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.7rem;
  font-weight:800;letter-spacing:.14em;text-transform:uppercase}
.gx-editorial .gxp-ed-counter b{color:var(--c-ink);font-size:1.05rem}
.gx-editorial .gxp-ed-nav{--ed-n:5;position:absolute;right:clamp(24px,5vw,78px);
  bottom:clamp(20px,3vh,36px);left:clamp(24px,5vw,78px);z-index:20;
  display:grid;grid-template-columns:repeat(var(--ed-n),minmax(0,1fr));gap:10px}
.gx-editorial .gxp-ed-nav span{--nav-local:0;position:relative;height:3px;
  background:var(--gx-line-strong)}
.gx-editorial .gxp-ed-nav span::before{position:absolute;inset:0;content:"";
  background:var(--c-accent);transform:scaleX(var(--nav-local));transform-origin:left}
.gx-editorial .gxp-ed-nav span::after{position:absolute;top:10px;left:0;
  content:attr(data-label);font-family:var(--f-utility);font-size:.64rem;
  font-weight:700;letter-spacing:.11em;text-transform:uppercase;
  color:color-mix(in srgb,var(--c-ink) calc(34% + var(--nav-local) * 62%),transparent);
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:100%}

@media (min-width:901px) and (max-width:1180px){
  .gx-editorial .gxp-ed-step{grid-template-columns:1fr 1fr;padding-inline:38px}
  .gx-editorial .gxp-ed-mark{width:min(44vw,430px)}
}
@media (min-width:901px) and (max-height:820px){
  .gx-editorial .gxp-ed-number{font-size:clamp(3.4rem,8vw,7.4rem)}
  .gx-editorial .gxp-ed-copy h3{font-size:clamp(2.2rem,5vw,5.4rem)}
  .gx-editorial .gxp-ed-mark{width:min(30vw,400px)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned, the wipe
   opens out: every spread is a full-width read with its mark above the copy. ---- */
@media (max-width:900px){
  .gx-editorial{min-height:auto;width:100%;margin-left:0}
  .gx-editorial .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-editorial.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-editorial .gxp-ed-counter,.gx-editorial .gxp-ed-nav{display:none}
  .gx-editorial .gxp-ed-stack{position:relative;inset:auto;display:grid}
  /* !important on the clipped/animated three: the frame loop stands down here,
     so a stale inline --delta or --local left by a desktop -> mobile resize must
     never be able to clip a spread away or dim its copy. */
  .gx-editorial .gxp-ed-step{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,1fr);gap:18px;
    padding:34px 0 40px;border-bottom:1px solid var(--gx-line);
    clip-path:none!important;opacity:1!important;will-change:auto}
  .gx-editorial .gxp-ed-veil{display:none}
  .gx-editorial .gxp-ed-rule{top:34px;bottom:34px}
  .gx-editorial .gxp-ed-copy{opacity:1!important;transform:none!important;
    padding-left:18px}
  .gx-editorial .gxp-ed-number{font-size:5.2rem}
  .gx-editorial .gxp-ed-copy h3{font-size:clamp(1.9rem,8.4vw,2.8rem)}
  .gx-editorial .gxp-ed-copy p{font-size:.95rem}
  .gx-editorial .gxp-ed-mark{order:-1;width:min(62vw,240px);margin:0 0 6px 18px;
    transform:none!important}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-editorial{min-height:auto;width:100%;margin-left:0}
  .gx-editorial .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-editorial.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-editorial .gxp-ed-counter,.gx-editorial .gxp-ed-nav{display:none}
  .gx-editorial .gxp-ed-stack{position:relative;inset:auto;display:grid}
  .gx-editorial .gxp-ed-step{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,1.1fr) minmax(0,.6fr);gap:clamp(20px,4vw,60px);
    align-items:center;padding:36px 0 42px;border-bottom:1px solid var(--gx-line);
    clip-path:none!important;opacity:1!important;will-change:auto}
  .gx-editorial .gxp-ed-veil{display:none}
  .gx-editorial .gxp-ed-rule{top:36px;bottom:36px}
  .gx-editorial .gxp-ed-copy{opacity:1!important;transform:none!important;
    padding-left:20px}
  .gx-editorial .gxp-ed-number{font-size:clamp(3rem,7vw,5.6rem)}
  .gx-editorial .gxp-ed-copy h3{font-size:clamp(1.9rem,3.8vw,3.1rem)}
  .gx-editorial .gxp-ed-copy p{font-size:.97rem}
  .gx-editorial .gxp-ed-mark{width:min(26vw,230px);margin:auto;
    transform:none!important}
}


/* ---- skin: infinity (process-v4 process-step-09 "Infinity loop journey") —
   PROCESS family, takeover. The steps are stations on a lemniscate: the ribbon
   inks itself around the figure-eight as the section scrolls, each station
   blooming as the ink reaches it, with a ghosted step counter sitting in the
   crossing and the copy reading across the foot of the scene.

   Port decisions (donor -> engine):
   - Donor `.ps-infinity .ps-sticky` painted a violet wash over an opaque
     `linear-gradient(150deg,#05050b,#0b0a18,#05060b)` plate. The plate is gone
     (GL-transparency decree); the only backdrop is the token halo at 8% behind
     the crossing, which is inside the composite budget the engine leaves a skin
     (`.gx-scene::before` already spends 7% of the 15% ceiling on every
     takeover).
   - Donor `.infinity-runner` (a dot riding getPointAtLength) is DROPPED:
     seating it costs a per-frame `left`/`top` write, i.e. layout every frame
     (skin-lint rule 7). The ribbon's own ink reveal — `stroke-dashoffset`, a
     paint property — plus the station bloom carry the same motion for free.
   - Donor hexes (#8d73ff, #a991ff, #bfaeff) become the host token cycle.
   - Donor `.infinity-readout small` carried a hard-coded caption; the readout
     here is the live `active / count` pair, never invented copy.
   Count-generality: stations are seated at render time from the closed-form
   lemniscate x = sin t, y = sin t cos t at t = (i + .5)/n * 2pi, and the ribbon
   is a 160-sample polyline of the SAME curve — so 5, 6, 7 and 8 steps all sit
   exactly on the ribbon and stay evenly spaced around it. The half-step offset
   keeps a station off the crossing point, where two labels would collide. ---- */
.gx-infinity{min-height:min(340svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-infinity .gx-sticky{padding:0}
.gx-infinity .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 90px),960px)}

.gx-infinity .gxp-if-halo{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(circle at 50% 44%,
    color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 34rem)}

.gx-infinity .gxp-if-world{position:absolute;
  inset:clamp(14px,3vh,44px) clamp(16px,3vw,58px) clamp(190px,29vh,290px)}
.gx-infinity .gxp-if-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gx-infinity .gxp-if-bed{fill:none;stroke:color-mix(in srgb,var(--c-ink) 9%,transparent);
  stroke-width:26;stroke-linecap:round;vector-effect:non-scaling-stroke}
.gx-infinity .gxp-if-base{fill:none;stroke:var(--gx-line-strong);stroke-width:1.5;
  stroke-dasharray:4 14;vector-effect:non-scaling-stroke}
.gx-infinity .gxp-if-active{fill:none;stroke:url(#gxpInfGrad);stroke-width:8;
  stroke-linecap:round;stroke-dasharray:1;
  stroke-dashoffset:calc(1 - var(--scene-progress,0));vector-effect:non-scaling-stroke;
  filter:drop-shadow(0 0 14px color-mix(in srgb,var(--c-accent) 52%,transparent))}

/* stations — seated at render time (static inline left/top), lit by the walk */
.gx-infinity .gxp-if-node{--nav-local:0;position:absolute;z-index:6;display:grid;
  width:clamp(50px,4.8vw,72px);aspect-ratio:1;place-items:center;border-radius:50%;
  font-family:var(--f-utility);font-size:.8rem;font-weight:800;
  color:color-mix(in srgb,var(--c-ink) calc(46% + var(--nav-local) * 54%),transparent);
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 46%),var(--c-dominant));
  box-shadow:0 0 calc(var(--nav-local) * 38px) color-mix(in srgb,var(--c-accent) 72%,transparent),
    0 0 0 1px var(--gx-line);
  transform:translate(-50%,-50%) scale(calc(.78 + var(--nav-local) * .3))}

/* the ghosted counter sits in the crossing — the one place on a lemniscate
   nothing else occupies */
.gx-infinity .gxp-if-readout{position:absolute;top:50%;left:50%;z-index:4;
  color:color-mix(in srgb,var(--c-ink) 12%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(6rem,16vw,17rem);letter-spacing:-.12em;line-height:1;
  pointer-events:none;transform:translate(-50%,-50%)}
.gx-infinity .gxp-if-readout small{display:block;margin-top:-.2em;text-align:center;
  color:color-mix(in srgb,var(--c-ink) 42%,transparent);
  font-family:var(--f-utility);font-size:.06em;font-weight:800;letter-spacing:.22em;
  text-transform:uppercase}

.gx-infinity .gxp-if-stack{position:absolute;right:clamp(20px,6vw,120px);
  bottom:clamp(30px,5vh,68px);left:clamp(20px,6vw,120px);z-index:8;
  height:clamp(170px,22vh,240px);pointer-events:none}
/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(): at progress 0 `stepWalk`
   gives pos=0, so delta_i = i and local_i = clamp(1 - i * 1.3). Without it
   every copy row takes `--local:0` and the scene reads as a ribbon with no
   step showing at all — the permanent state of a mis-keyed ANIMATORS entry,
   which no gate in this project catches (MASTER-PLAN §2.1). The
   `:nth-of-type` ladder duplicates the renderer's inline `--step-i` so the
   floor survives a renderer that lost the style attribute. */
.gx-infinity .gxp-if-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.3);
  position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,.3fr) minmax(0,1fr);
  gap:clamp(20px,4vw,72px);align-items:end;
  opacity:var(--local);transform:translateY(calc(var(--delta) * 58px))}
.gx-infinity .gxp-if-copy:nth-of-type(2){--step-i:1}
.gx-infinity .gxp-if-copy:nth-of-type(3){--step-i:2}
.gx-infinity .gxp-if-copy:nth-of-type(4){--step-i:3}
.gx-infinity .gxp-if-copy:nth-of-type(5){--step-i:4}
.gx-infinity .gxp-if-copy:nth-of-type(6){--step-i:5}
.gx-infinity .gxp-if-copy:nth-of-type(7){--step-i:6}
.gx-infinity .gxp-if-copy:nth-of-type(8){--step-i:7}
.gx-infinity .gxp-if-number{color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 62%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(4.4rem,10vw,11rem);letter-spacing:-.11em;line-height:.68}
.gx-infinity .gxp-if-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gx-infinity .gxp-if-copy h3{max-width:820px;margin:0 0 11px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2rem,4.4vw,4.6rem);letter-spacing:-.065em;line-height:.9;
  text-wrap:balance}
.gx-infinity .gxp-if-copy p{max-width:640px;margin:0;color:var(--gx-muted);
  font-size:clamp(.96rem,1.28vw,1.12rem);line-height:1.48}

@media (min-width:901px) and (max-height:820px){
  .gx-infinity .gx-stage{min-height:clamp(470px,calc(100svh - 80px),760px)}
  .gx-infinity .gxp-if-world{inset:12px clamp(16px,3vw,58px) clamp(160px,26vh,230px)}
  .gx-infinity .gxp-if-stack{height:clamp(150px,20vh,200px)}
  .gx-infinity .gxp-if-copy h3{font-size:clamp(1.8rem,3.6vw,3.4rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the loop
   becomes a plain numbered list of full-width reads. ---- */
@media (max-width:900px){
  .gx-infinity{min-height:auto;width:100%;margin-left:0}
  .gx-infinity .gx-sticky{position:relative;min-height:auto;padding:10px 0 26px;overflow:visible}
  .gx-infinity.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-infinity .gxp-if-halo,.gx-infinity .gxp-if-svg,
  .gx-infinity .gxp-if-node,.gx-infinity .gxp-if-readout{display:none}
  .gx-infinity .gxp-if-world{position:relative;inset:auto;min-height:0}
  /* !important on the animated pair: the frame loop stands down here, so a
     stale inline --local/--delta left by a desktop -> mobile resize must never
     be able to blank or displace a step. */
  .gx-infinity .gxp-if-stack{position:relative;inset:auto;right:auto;bottom:auto;
    left:auto;height:auto;display:grid;gap:0}
  .gx-infinity .gxp-if-copy{position:relative;inset:auto;display:block;
    padding:26px 0 30px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-infinity .gxp-if-number{display:block;font-size:5rem;line-height:.85}
  .gx-infinity .gxp-if-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-infinity .gxp-if-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-infinity{min-height:auto;width:100%;margin-left:0}
  .gx-infinity .gx-sticky{position:relative;min-height:auto;padding:10px 0 30px;overflow:visible}
  .gx-infinity.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-infinity .gxp-if-halo,.gx-infinity .gxp-if-svg,
  .gx-infinity .gxp-if-node,.gx-infinity .gxp-if-readout{display:none}
  .gx-infinity .gxp-if-world{position:relative;inset:auto;min-height:0}
  .gx-infinity .gxp-if-stack{position:relative;inset:auto;right:auto;bottom:auto;
    left:auto;height:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(310px,1fr));gap:20px}
  .gx-infinity .gxp-if-copy{position:relative;inset:auto;display:block;
    padding:24px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-infinity .gxp-if-number{display:block;font-size:clamp(3.4rem,8vw,6rem);line-height:.85}
  .gx-infinity .gxp-if-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-infinity .gxp-if-copy p{font-size:.97rem}
}


/* ---- skin: relay (process-v4 process-step-11 "Kinetic split relay") —
   PROCESS family, takeover. The stage is split down the middle and the steps
   hand off across it: copy on the left for one step, on the right for the next,
   with a baton running the centre track from the first hand to the last.

   Port decisions (donor -> engine):
   - Donor `.relay-half--left{background:var(--relay-a)}` / `--right` painted
     two OPAQUE full-height plates (`#ff4f3e` and `#101118` — a hard-coded
     palette, and a filled plane with a hard vertical edge down the middle of
     the viewport). Both are gone. The split survives as two token washes at 7%
     whose inner edges are MASKED to transparent, so there is no cut line and
     the pack's GL field reads through both halves.
   - Donor `.relay-baton{left:var(--baton-x);will-change:left}` animated a
     LAYOUT property every frame. Here the baton is a `translateX` of
     `--rl-w`, the track's own explicit width token, so the travel is exact and
     costs no layout (skin-lint rule 7).
   - Donor `--relay-a` recoloured the whole left half on every step change. That
     survives as `--rl-accent`, written by the animator ONLY when the active
     step changes (a change-gated write, not a per-frame one) and consumed by
     the centre track and the baton rather than by a full-bleed plate.
   - Donor hexes (#ff4f3e, #101118, rgba(255,255,255,...) chrome) become host
     tokens throughout, so the skin reads on light packs as well as dark.
   - Side alternation is `:nth-of-type(even)`, not a `data-side` attribute the
     donor's JS had to maintain — static CSS, correct at any item count.
   Count-generality: every geometric term is the slot index times a constant and
   the baton's travel is a 0-1 fraction of the track, so 5, 6, 7 and 8 steps
   behave identically. ---- */
.gx-relay{min-height:min(340svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-relay .gx-sticky{padding:0}
.gx-relay .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,100svh,1000px)}

/* the split — two masked token washes where the donor had two opaque plates */
.gx-relay .gxp-rl-half{position:absolute;top:0;bottom:0;width:50%;pointer-events:none}
.gx-relay .gxp-rl-half--left{left:0;
  background:linear-gradient(90deg,
    color-mix(in srgb,var(--c-accent) 7%,transparent),transparent);
  -webkit-mask-image:linear-gradient(90deg,#000,transparent);
  mask-image:linear-gradient(90deg,#000,transparent)}
.gx-relay .gxp-rl-half--right{right:0;
  background:linear-gradient(270deg,
    color-mix(in srgb,var(--c-dominant) 7%,transparent),transparent);
  -webkit-mask-image:linear-gradient(270deg,#000,transparent);
  mask-image:linear-gradient(270deg,#000,transparent)}
.gx-relay .gxp-rl-grid{position:absolute;inset:0;pointer-events:none;
  background:
    linear-gradient(color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px);
  background-size:70px 70px;
  -webkit-mask-image:radial-gradient(ellipse at 50% 50%,#000,transparent 84%);
  mask-image:radial-gradient(ellipse at 50% 50%,#000,transparent 84%)}

/* the track and the baton. `--rl-w` is an EXPLICIT length, which is what lets
   the baton's travel be a transform instead of the donor's `left` write. */
/* the track sits at 79%, NOT the donor's 50%: the steps are `align-items:center`
   so their copy is centred vertically, and a 3px rule with a baton running
   through the middle of it is exactly the "copy over the artwork" defect the
   ledger forbids. At 79% the baton runs below the copy block and above the nav. */
.gx-relay .gxp-rl-track{--rl-w:min(84vw,1400px);--rl-accent:var(--c-accent);
  position:absolute;top:79%;left:50%;z-index:12;
  width:var(--rl-w);height:3px;background:var(--gx-line-strong);
  transform:translate(-50%,-50%)}
.gx-relay .gxp-rl-track::before,.gx-relay .gxp-rl-track::after{position:absolute;top:50%;
  width:14px;aspect-ratio:1;border:3px solid var(--gx-line-strong);border-radius:50%;
  content:"";transform:translateY(-50%)}
.gx-relay .gxp-rl-track::before{left:0}
.gx-relay .gxp-rl-track::after{right:0}
.gx-relay .gxp-rl-baton{--rl-baton:0;position:absolute;top:50%;left:0;z-index:14;
  width:clamp(44px,4.6vw,72px);aspect-ratio:1;border-radius:50%;
  border:4px solid color-mix(in srgb,var(--c-ink) 40%,transparent);
  background:color-mix(in srgb,var(--rl-accent) 62%,var(--c-dominant));
  box-shadow:0 0 0 11px color-mix(in srgb,var(--rl-accent) 12%,transparent),
    0 18px 48px -20px rgba(0,0,0,.6);
  transform:translate(-50%,-50%)
    translateX(calc(var(--rl-baton) * var(--rl-w)))
    rotate(calc(var(--rl-baton) * 720deg));
  will-change:transform}

.gx-relay .gxp-rl-stack{position:absolute;inset:0;z-index:8}
/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(): at progress 0 `stepWalk`
   gives pos=0, so delta_i = i and local_i = clamp(1 - i * 1.35); the baton
   rests at `--rl-baton:0`, the left end of the track, which is exactly what
   the animator writes at progress 0. Without it every step takes `--local:0`
   and the scene reads as an empty split with no step showing — the permanent
   state of a mis-keyed ANIMATORS entry, which no gate in this project catches
   (MASTER-PLAN §2.1). The `:nth-of-type` ladder duplicates the renderer's
   inline `--step-i` so the floor survives a renderer that lost the style
   attribute. */
.gx-relay .gxp-rl-step{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.35);
  position:absolute;inset:0;display:grid;grid-template-columns:1fr 1fr;
  align-items:center;
  padding:clamp(74px,9vh,132px) clamp(24px,6vw,112px) clamp(76px,9vh,110px);
  pointer-events:none;opacity:var(--local)}
.gx-relay .gxp-rl-step:nth-of-type(2){--step-i:1}
.gx-relay .gxp-rl-step:nth-of-type(3){--step-i:2}
.gx-relay .gxp-rl-step:nth-of-type(4){--step-i:3}
.gx-relay .gxp-rl-step:nth-of-type(5){--step-i:4}
.gx-relay .gxp-rl-step:nth-of-type(6){--step-i:5}
.gx-relay .gxp-rl-step:nth-of-type(7){--step-i:6}
.gx-relay .gxp-rl-step:nth-of-type(8){--step-i:7}
.gx-relay .gxp-rl-step.is-active{pointer-events:auto}

.gx-relay .gxp-rl-copy{grid-column:1;grid-row:1;align-self:center;max-width:640px;
  padding-right:clamp(24px,5vw,92px);
  transform:translate3d(calc(var(--delta) * -8vw),calc((1 - var(--local)) * 36px),0)}
.gx-relay .gxp-rl-number{grid-column:2;grid-row:1;justify-self:center;
  color:color-mix(in srgb,var(--c-ink) 15%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(8rem,22vw,26rem);letter-spacing:-.13em;line-height:.7;
  transform:translate3d(calc(var(--delta) * 10vw),0,0) rotate(calc(var(--delta) * 4deg))}
/* the hand-off: every other step swaps sides */
.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-copy{grid-column:2;
  padding-right:0;padding-left:clamp(24px,5vw,92px);
  transform:translate3d(calc(var(--delta) * 8vw),calc((1 - var(--local)) * 36px),0)}
.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-number{grid-column:1;
  transform:translate3d(calc(var(--delta) * -10vw),0,0) rotate(calc(var(--delta) * -4deg))}

.gx-relay .gxp-rl-copy .gx-kicker{margin-bottom:20px;
  color:var(--step-accent,var(--c-accent))}
.gx-relay .gxp-rl-copy h3{margin:0 0 18px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.5rem,6.2vw,7rem);letter-spacing:-.074em;line-height:.85;
  text-wrap:balance}
.gx-relay .gxp-rl-copy p{max-width:580px;margin:0;color:var(--gx-muted);
  font-size:clamp(1rem,1.4vw,1.2rem);line-height:1.5}

.gx-relay .gxp-rl-header{position:absolute;top:clamp(18px,3vh,34px);
  right:clamp(24px,5vw,78px);left:clamp(24px,5vw,78px);z-index:20;
  display:flex;align-items:center;justify-content:space-between;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.7rem;
  font-weight:800;letter-spacing:.15em;text-transform:uppercase}
.gx-relay .gxp-rl-header strong{color:var(--c-ink);font-size:1rem}
.gx-relay .gxp-rl-nav{position:absolute;right:clamp(24px,5vw,78px);
  bottom:clamp(20px,3vh,34px);left:clamp(24px,5vw,78px);z-index:20;
  display:flex;justify-content:space-between;gap:16px}
.gx-relay .gxp-rl-nav span{--nav-local:0;display:flex;align-items:center;gap:9px;
  font-family:var(--f-utility);font-size:.68rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;min-width:0;
  color:color-mix(in srgb,var(--c-ink) calc(36% + var(--nav-local) * 62%),transparent)}
.gx-relay .gxp-rl-nav span::before{flex:none;width:10px;aspect-ratio:1;border-radius:50%;
  content:"";border:2px solid var(--gx-line-strong);
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 100%),transparent);
  box-shadow:0 0 calc(var(--nav-local) * 22px) color-mix(in srgb,var(--c-accent) 60%,transparent);
  transform:scale(calc(.75 + var(--nav-local) * .34))}
.gx-relay .gxp-rl-nav b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (min-width:901px) and (max-height:820px){
  .gx-relay .gxp-rl-number{font-size:clamp(6rem,17vw,17rem)}
  .gx-relay .gxp-rl-copy h3{font-size:clamp(2.1rem,4.8vw,5rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the split
   opens out into a stack of full-width hand-offs. ---- */
@media (max-width:900px){
  .gx-relay{min-height:auto;width:100%;margin-left:0}
  .gx-relay .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-relay.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-relay .gxp-rl-half,.gx-relay .gxp-rl-grid,.gx-relay .gxp-rl-track,
  .gx-relay .gxp-rl-header,.gx-relay .gxp-rl-nav{display:none}
  .gx-relay .gxp-rl-stack{position:relative;inset:auto;display:grid}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --local/--delta left by a desktop -> mobile resize must never
     be able to blank or displace a hand-off. */
  .gx-relay .gxp-rl-step{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,1fr);padding:26px 0 30px;
    border-bottom:1px solid var(--gx-line);opacity:1!important}
  .gx-relay .gxp-rl-copy,.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-copy{
    grid-column:1;grid-row:auto;padding:0;transform:none!important}
  .gx-relay .gxp-rl-number,.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-number{
    grid-column:1;grid-row:auto;order:-1;justify-self:start;
    font-size:5.4rem;transform:none!important}
  .gx-relay .gxp-rl-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-relay .gxp-rl-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-relay{min-height:auto;width:100%;margin-left:0}
  .gx-relay .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-relay.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-relay .gxp-rl-half,.gx-relay .gxp-rl-grid,.gx-relay .gxp-rl-track,
  .gx-relay .gxp-rl-header,.gx-relay .gxp-rl-nav{display:none}
  .gx-relay .gxp-rl-stack{position:relative;inset:auto;display:grid}
  .gx-relay .gxp-rl-step{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,.34fr) minmax(0,1fr);gap:clamp(18px,4vw,54px);
    align-items:center;padding:26px 0 30px;
    border-bottom:1px solid var(--gx-line);opacity:1!important}
  .gx-relay .gxp-rl-copy,.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-copy{
    grid-column:2;grid-row:1;padding:0;transform:none!important}
  .gx-relay .gxp-rl-number,.gx-relay .gxp-rl-step:nth-of-type(even) .gxp-rl-number{
    grid-column:1;grid-row:1;justify-self:start;
    font-size:clamp(3.6rem,9vw,7rem);transform:none!important}
  .gx-relay .gxp-rl-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-relay .gxp-rl-copy p{font-size:.97rem}
}


/* ---- skin: corridor (process-v4 process-step-12 "Architectural depth
   corridor") — PROCESS family, takeover. The steps are portals down a
   perspective corridor: a numbered frame per step recedes toward a vanishing
   point off to the right, the reader flies through them one at a time, and the
   copy for the frame at the head of the corridor reads from the empty lower
   left.

   Port decisions (donor -> engine):
   - Donor `.ps-corridor .ps-sticky` painted a violet wash over an opaque
     `linear-gradient(145deg,#04050a,#080b14,#05060b)` plate. The plate is gone
     (GL-transparency decree); the atmosphere grid survives at 3.5%, masked
     radially around the vanishing point exactly as the donor had it.
   - The frames were already de-filled in the donor (border + faint inner
     gradient, no opaque slab), so they port intact — only their colour moves
     from `--corridor-color` hexes to the host token cycle.
   - DEPTH, NOT z-index. `.gxp-cr-stage` is `transform-style:preserve-3d`, so
     paint order inside it is 3D depth and a z-index there is ignored (three
     cards-v8 skins learned this the hard way). Frames are ordered purely by
     `--cr-z`. The copy stack and the chrome are deliberately SIBLINGS of that
     stage inside a FLAT `.gx-stage`, so ordinary z-index does order them above
     the corridor — which is why a frame flying toward the camera can never
     paint over the copy.
   - Donor `.corridor-copy` sat bottom-left and the frames occupy the right —
     the copy is already on the composition's empty side (ledger rule) and
     stays there.
   Count-generality: frame depth is the slot delta times a constant, so 5, 6, 7
   and 8 steps all produce the same corridor with the same spacing. ---- */
.gx-corridor{min-height:min(340svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-corridor .gx-sticky{padding:0}
.gx-corridor .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,100svh,1000px)}

.gx-corridor .gxp-cr-atmosphere{position:absolute;inset:0;overflow:hidden;pointer-events:none;
  background:
    linear-gradient(color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px);
  background-size:74px 74px;
  -webkit-mask-image:radial-gradient(circle at 69% 53%,#000 0 18%,rgba(0,0,0,.9) 42%,transparent 86%);
  mask-image:radial-gradient(circle at 69% 53%,#000 0 18%,rgba(0,0,0,.9) 42%,transparent 86%)}
.gx-corridor .gxp-cr-atmosphere::before,.gx-corridor .gxp-cr-atmosphere::after{
  position:absolute;top:53%;left:69%;width:140vw;height:1px;content:"";
  background:linear-gradient(90deg,transparent,
    color-mix(in srgb,var(--c-accent) 22%,transparent),transparent);
  transform-origin:0 50%}
.gx-corridor .gxp-cr-atmosphere::before{transform:rotate(27deg)}
.gx-corridor .gxp-cr-atmosphere::after{transform:rotate(-27deg)}

/* the corridor itself — the ONLY preserve-3d context in this skin */
.gx-corridor .gxp-cr-stage{position:absolute;inset:0;
  perspective:1150px;perspective-origin:69% 53%;transform-style:preserve-3d}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(). At progress 0 `stepWalk`
   gives pos=0, so delta_i = i (never negative), and every term below is the
   animator's d >= 0 branch with `--step-i` substituted: frame 01 is at the head
   of the corridor, square and lit, and the rest recede at -420px per slot.
   Without it every frame falls back to `--cr-z:0` at opacity 1 — n frames
   exactly superimposed at the vanishing point, which is what a mis-keyed
   ANIMATORS entry would ship permanently and what no gate in this project
   catches (MASTER-PLAN §2.1). The `:nth-of-type` ladder duplicates the
   renderer's inline `--step-i` so the floor survives a renderer that lost the
   style attribute; the two agree by construction. */
.gx-corridor .gxp-cr-step{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.25);
  --cr-z:calc(var(--step-i) * -420px);
  --cr-op:max(.05,calc(1 - var(--step-i) * .3 - max(0,var(--step-i) - 1) * .4));
  position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d}
.gx-corridor .gxp-cr-step:nth-of-type(2){--step-i:1}
.gx-corridor .gxp-cr-step:nth-of-type(3){--step-i:2}
.gx-corridor .gxp-cr-step:nth-of-type(4){--step-i:3}
.gx-corridor .gxp-cr-step:nth-of-type(5){--step-i:4}
.gx-corridor .gxp-cr-step:nth-of-type(6){--step-i:5}
.gx-corridor .gxp-cr-step:nth-of-type(7){--step-i:6}
.gx-corridor .gxp-cr-step:nth-of-type(8){--step-i:7}

.gx-corridor .gxp-cr-frame{position:absolute;top:53%;left:72%;
  width:min(52vw,820px);aspect-ratio:16/10;overflow:hidden;
  border:2px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 72%,transparent);
  background:
    linear-gradient(145deg,color-mix(in srgb,var(--step-accent,var(--c-accent)) 7%,transparent),transparent 40%),
    linear-gradient(180deg,color-mix(in srgb,var(--c-ink) 2%,transparent),transparent);
  box-shadow:0 0 74px color-mix(in srgb,var(--step-accent,var(--c-accent)) 16%,transparent),
    inset 0 0 90px color-mix(in srgb,var(--step-accent,var(--c-accent)) 6%,transparent);
  /* NO `filter` here on purpose: a filter is a grouping property, so it would
     force this element's used `transform-style` to flat and collapse the
     translateZ pops on the index and the crosshair below. Depth falloff is
     carried by opacity and by perspective scale alone. */
  opacity:var(--cr-op);
  transform:translate(-50%,-50%) translateZ(var(--cr-z));
  transform-style:preserve-3d;will-change:transform,opacity}
.gx-corridor .gxp-cr-frame::before{position:absolute;inset:7%;content:"";
  border:1px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 52%,transparent)}
.gx-corridor .gxp-cr-frame::after{position:absolute;inset:0;content:"";opacity:.5;
  background:
    linear-gradient(90deg,transparent 49.88%,color-mix(in srgb,var(--step-accent,var(--c-accent)) 38%,transparent) 50%,transparent 50.12%),
    linear-gradient(transparent 49.88%,color-mix(in srgb,var(--step-accent,var(--c-accent)) 28%,transparent) 50%,transparent 50.12%)}
.gx-corridor .gxp-cr-index{position:absolute;top:50%;left:50%;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 62%,var(--c-ink));
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(6rem,17vw,19rem);letter-spacing:-.14em;line-height:.65;
  text-shadow:0 0 55px color-mix(in srgb,var(--step-accent,var(--c-accent)) 42%,transparent);
  transform:translate(-50%,-50%) translateZ(80px)}
.gx-corridor .gxp-cr-crosshair{position:absolute;top:50%;left:50%;
  width:22px;aspect-ratio:1;border-radius:50%;
  border:2px solid color-mix(in srgb,var(--c-ink) 62%,transparent);
  box-shadow:0 0 25px color-mix(in srgb,var(--step-accent,var(--c-accent)) 70%,transparent);
  transform:translate(-50%,-50%) translateZ(110px)}
.gx-corridor .gxp-cr-crosshair::before,.gx-corridor .gxp-cr-crosshair::after{
  position:absolute;top:50%;left:50%;content:"";
  background:color-mix(in srgb,var(--c-ink) 55%,transparent);
  transform:translate(-50%,-50%)}
.gx-corridor .gxp-cr-crosshair::before{width:44px;height:1px}
.gx-corridor .gxp-cr-crosshair::after{width:1px;height:44px}

/* the copy — a SIBLING of the 3D stage, so z-index really does order it above
   the corridor (see the note at the top of this file) */
.gx-corridor .gxp-cr-copy-stack{position:absolute;bottom:11vh;
  left:clamp(20px,6vw,120px);z-index:20;
  width:min(34vw,520px);height:clamp(260px,34vh,380px);pointer-events:none}
.gx-corridor .gxp-cr-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.25);
  position:absolute;inset:0;display:flex;flex-direction:column;justify-content:flex-end;
  opacity:var(--local);
  transform:translate3d(calc(var(--delta) * -7vw),calc(var(--delta) * 6vh),0)}
.gx-corridor .gxp-cr-copy:nth-of-type(2){--step-i:1}
.gx-corridor .gxp-cr-copy:nth-of-type(3){--step-i:2}
.gx-corridor .gxp-cr-copy:nth-of-type(4){--step-i:3}
.gx-corridor .gxp-cr-copy:nth-of-type(5){--step-i:4}
.gx-corridor .gxp-cr-copy:nth-of-type(6){--step-i:5}
.gx-corridor .gxp-cr-copy:nth-of-type(7){--step-i:6}
.gx-corridor .gxp-cr-copy:nth-of-type(8){--step-i:7}
.gx-corridor .gxp-cr-copy-number{display:block;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 18%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(5rem,12vw,13rem);letter-spacing:-.12em;line-height:.56}
.gx-corridor .gxp-cr-copy .gx-kicker{margin:22px 0 11px;
  color:var(--step-accent,var(--c-accent))}
.gx-corridor .gxp-cr-copy h3{max-width:640px;margin:0 0 16px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.2rem,5.4vw,6rem);letter-spacing:-.072em;line-height:.86;
  text-wrap:balance}
.gx-corridor .gxp-cr-copy p{max-width:580px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.36vw,1.18rem);line-height:1.5}

.gx-corridor .gxp-cr-nav{--cr-n:5;position:absolute;right:clamp(24px,5vw,82px);
  bottom:clamp(18px,2.8vh,32px);left:clamp(24px,5vw,82px);z-index:24;
  display:grid;grid-template-columns:repeat(var(--cr-n),minmax(0,1fr));gap:12px}
.gx-corridor .gxp-cr-nav span{--nav-local:0;position:relative;padding-top:17px;
  font-family:var(--f-utility);font-size:.66rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  color:color-mix(in srgb,var(--c-ink) calc(30% + var(--nav-local) * 66%),transparent)}
.gx-corridor .gxp-cr-nav span::before{position:absolute;top:0;right:0;left:0;height:2px;
  content:"";background:var(--gx-line-strong)}
.gx-corridor .gxp-cr-nav span::after{position:absolute;top:0;left:0;width:100%;height:2px;
  content:"";background:var(--c-accent);
  box-shadow:0 0 16px color-mix(in srgb,var(--c-accent) 60%,transparent);
  transform:scaleX(var(--nav-local));transform-origin:left}
.gx-corridor .gxp-cr-readout{position:absolute;top:50%;right:26px;z-index:24;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.63rem;font-weight:800;
  letter-spacing:.15em;text-transform:uppercase;writing-mode:vertical-rl;
  transform:translateY(-50%)}
.gx-corridor .gxp-cr-readout strong{color:var(--c-ink)}

@media (min-width:901px) and (max-width:1180px){
  .gx-corridor .gxp-cr-frame{left:73%;width:min(54vw,640px)}
  .gx-corridor .gxp-cr-copy-stack{left:36px;width:34vw}
}
@media (min-width:901px) and (max-height:820px){
  .gx-corridor .gxp-cr-copy-stack{height:clamp(220px,30vh,300px);bottom:9vh}
  .gx-corridor .gxp-cr-copy-number{font-size:clamp(3.6rem,9vw,9rem)}
  .gx-corridor .gxp-cr-copy h3{font-size:clamp(1.9rem,4.2vw,4.4rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the
   corridor opens out into a stack of framed, full-width reads. ---- */
@media (max-width:900px){
  .gx-corridor{min-height:auto;width:100%;margin-left:0}
  .gx-corridor .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-corridor.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-corridor .gxp-cr-atmosphere,.gx-corridor .gxp-cr-nav,
  .gx-corridor .gxp-cr-readout,.gx-corridor .gxp-cr-crosshair{display:none}
  .gx-corridor .gxp-cr-stage{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --cr-z or --local left by a desktop -> mobile resize must
     never be able to send a frame to the vanishing point or blank its copy. */
  .gx-corridor .gxp-cr-step{position:relative;inset:auto;padding:12px 0 8px;
    transform-style:flat}
  .gx-corridor .gxp-cr-frame{position:relative;top:auto;left:auto;
    width:min(92vw,420px);margin:0 auto;opacity:1!important;
    transform:none!important;transform-style:flat;will-change:auto}
  .gx-corridor .gxp-cr-index{font-size:6.4rem;transform:translate(-50%,-50%)!important}
  .gx-corridor .gxp-cr-copy-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;gap:0}
  .gx-corridor .gxp-cr-copy{position:relative;inset:auto;
    padding:22px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-corridor .gxp-cr-copy-number{font-size:5rem}
  .gx-corridor .gxp-cr-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-corridor .gxp-cr-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-corridor{min-height:auto;width:100%;margin-left:0}
  .gx-corridor .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-corridor.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-corridor .gxp-cr-atmosphere,.gx-corridor .gxp-cr-nav,
  .gx-corridor .gxp-cr-readout,.gx-corridor .gxp-cr-crosshair{display:none}
  .gx-corridor .gxp-cr-stage{position:relative;inset:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px;
    perspective:none;transform-style:flat}
  .gx-corridor .gxp-cr-step{position:relative;inset:auto;padding:10px 0;
    transform-style:flat}
  .gx-corridor .gxp-cr-frame{position:relative;top:auto;left:auto;
    width:100%;margin:0;opacity:1!important;
    transform:none!important;transform-style:flat;will-change:auto}
  .gx-corridor .gxp-cr-index{font-size:clamp(4rem,9vw,7rem);
    transform:translate(-50%,-50%)!important}
  .gx-corridor .gxp-cr-copy-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px}
  .gx-corridor .gxp-cr-copy{position:relative;inset:auto;
    padding:20px 0 26px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-corridor .gxp-cr-copy-number{font-size:clamp(3.4rem,8vw,5.6rem)}
  .gx-corridor .gxp-cr-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-corridor .gxp-cr-copy p{font-size:.97rem}
}


/* ---- skin: signal (process-v4 process-step-13 "Luminous signal wave") —
   PROCESS family, takeover. The process is a transmission: a waveform crosses
   the scene, each step is a station sitting exactly on it, and the signal
   lights up along the wave as the section scrolls. The copy rides the TOP of
   the scene — the empty half — so nothing ever sits on the wave.

   Port decisions (donor -> engine):
   - Donor `.ps-signal .ps-sticky` painted a pink wash over an opaque
     `linear-gradient(180deg,#090711,#100b1b,#08070c)` plate. The plate is gone
     (GL-transparency decree); the only backdrop is a token halo at 8%, inside
     the composite budget the engine leaves a skin (`.gx-scene::before` already
     spends 7% of the 15% ceiling on every takeover).
   - Donor `.signal-runner` (a dot riding getPointAtLength) is DROPPED: seating
     it costs a per-frame `left`/`top` write, i.e. layout every frame
     (skin-lint rule 7). The wave's own ink reveal — `stroke-dashoffset`, a
     paint property — plus the station bloom carry the motion for free.
   - Donor hexes (#ff69ad, #ff4e9f) become the host token cycle.
   - Donor `.signal-node small` carried an invented per-station caption; it is
     `stepLabel(item.title)` here — a real spec value, trimmed, never invented.
   Count-generality: stations are seated at render time by evaluating the SAME
   closed-form wave the ribbon is sampled from, so 5, 6, 7 and 8 steps all sit
   exactly on the curve and stay evenly spaced along it. ---- */
.gx-signal{min-height:min(335svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-signal .gx-sticky{padding:0}
.gx-signal .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,calc(100svh - 90px),960px)}

.gx-signal .gxp-sg-halo{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(circle at 72% 30%,
    color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 30rem)}

.gx-signal .gxp-sg-world{position:absolute;
  inset:clamp(210px,32vh,330px) 0 clamp(50px,7vh,86px);z-index:3}
.gx-signal .gxp-sg-svg{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.gx-signal .gxp-sg-bed{fill:none;stroke:color-mix(in srgb,var(--c-ink) 8%,transparent);
  stroke-width:24;stroke-linecap:round;vector-effect:non-scaling-stroke}
.gx-signal .gxp-sg-base{fill:none;stroke:var(--gx-line-strong);stroke-width:1.5;
  stroke-dasharray:3 15;vector-effect:non-scaling-stroke}
.gx-signal .gxp-sg-active{fill:none;stroke:url(#gxpSigGrad);stroke-width:8;
  stroke-linecap:round;stroke-dasharray:1;
  stroke-dashoffset:calc(1 - var(--scene-progress,0));vector-effect:non-scaling-stroke;
  filter:drop-shadow(0 0 15px color-mix(in srgb,var(--c-accent) 55%,transparent))}

/* stations — seated at render time (static inline left/top), lit by the walk */
.gx-signal .gxp-sg-node{--nav-local:0;position:absolute;z-index:6;display:grid;
  min-width:74px;justify-items:center;text-align:center;font-family:var(--f-utility);
  font-size:.7rem;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:color-mix(in srgb,var(--c-ink) calc(34% + var(--nav-local) * 64%),transparent);
  transform:translate(-50%,-50%)}
.gx-signal .gxp-sg-node::before{width:14px;aspect-ratio:1;margin-bottom:8px;
  border-radius:50%;content:"";
  border:3px solid color-mix(in srgb,var(--c-ink) 54%,transparent);
  background:color-mix(in srgb,var(--c-accent) calc(var(--nav-local) * 100%),transparent);
  box-shadow:0 0 calc(var(--nav-local) * 34px) color-mix(in srgb,var(--c-accent) 75%,transparent);
  transform:scale(calc(.72 + var(--nav-local) * .44))}
.gx-signal .gxp-sg-node small{display:block;margin-top:3px;font-size:.56rem;
  letter-spacing:.11em;max-width:12ch;
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  color:color-mix(in srgb,var(--c-ink) calc(24% + var(--nav-local) * 58%),transparent)}

/* the copy rides the TOP band — the empty half of the composition (the wave
   owns the lower two thirds). Ledger rule: copy never sits on the artwork. */
.gx-signal .gxp-sg-stack{position:absolute;top:clamp(16px,3vh,44px);
  right:clamp(20px,6vw,110px);left:clamp(20px,6vw,110px);z-index:8;
  height:clamp(180px,27vh,270px);pointer-events:none}
/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(): at progress 0 `stepWalk`
   gives pos=0, so delta_i = i and local_i = clamp(1 - i * 1.3). Without it
   every copy row takes `--local:0` and the scene reads as a bare waveform with
   no step showing — the permanent state of a mis-keyed ANIMATORS entry, which
   no gate in this project catches (MASTER-PLAN §2.1). The `:nth-of-type`
   ladder duplicates the renderer's inline `--step-i` so the floor survives a
   renderer that lost the style attribute. */
.gx-signal .gxp-sg-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.3);
  position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,.3fr) minmax(0,1fr);
  gap:clamp(22px,4vw,80px);align-items:end;
  opacity:var(--local);transform:translateY(calc(var(--delta) * -9vh))}
.gx-signal .gxp-sg-copy:nth-of-type(2){--step-i:1}
.gx-signal .gxp-sg-copy:nth-of-type(3){--step-i:2}
.gx-signal .gxp-sg-copy:nth-of-type(4){--step-i:3}
.gx-signal .gxp-sg-copy:nth-of-type(5){--step-i:4}
.gx-signal .gxp-sg-copy:nth-of-type(6){--step-i:5}
.gx-signal .gxp-sg-copy:nth-of-type(7){--step-i:6}
.gx-signal .gxp-sg-copy:nth-of-type(8){--step-i:7}
.gx-signal .gxp-sg-number{color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 22%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(4.6rem,12vw,13rem);letter-spacing:-.12em;line-height:.62}
.gx-signal .gxp-sg-copy .gx-kicker{color:var(--step-accent,var(--c-accent))}
.gx-signal .gxp-sg-copy h3{max-width:840px;margin:10px 0 13px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.1rem,4.4vw,4.8rem);letter-spacing:-.04em;line-height:.98;
  text-wrap:balance}
.gx-signal .gxp-sg-copy p{max-width:640px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.36vw,1.18rem);line-height:1.48}

.gx-signal .gxp-sg-readout{position:absolute;right:clamp(24px,5vw,82px);
  bottom:clamp(16px,2.4vh,30px);z-index:12;color:var(--gx-muted);
  font-family:var(--f-utility);font-size:.66rem;font-weight:800;letter-spacing:.15em;
  text-transform:uppercase}
.gx-signal .gxp-sg-readout strong{color:var(--c-ink)}

@media (min-width:901px) and (max-height:820px){
  .gx-signal .gx-stage{min-height:clamp(470px,calc(100svh - 80px),760px)}
  .gx-signal .gxp-sg-stack{height:clamp(150px,24vh,220px)}
  .gx-signal .gxp-sg-world{inset:clamp(170px,28vh,250px) 0 clamp(42px,6vh,70px)}
  .gx-signal .gxp-sg-copy h3{font-size:clamp(1.9rem,4.2vw,4.2rem)}
  .gx-signal .gxp-sg-number{font-size:clamp(3.4rem,9vw,8rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the wave
   becomes a plain numbered list of full-width reads. ---- */
@media (max-width:900px){
  .gx-signal{min-height:auto;width:100%;margin-left:0}
  .gx-signal .gx-sticky{position:relative;min-height:auto;padding:10px 0 26px;overflow:visible}
  .gx-signal.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-signal .gxp-sg-halo,.gx-signal .gxp-sg-svg,
  .gx-signal .gxp-sg-node,.gx-signal .gxp-sg-readout{display:none}
  .gx-signal .gxp-sg-world{position:relative;inset:auto;min-height:0}
  /* !important on the animated pair: the frame loop stands down here, so a
     stale inline --local/--delta left by a desktop -> mobile resize must never
     be able to blank or displace a step. */
  .gx-signal .gxp-sg-stack{position:relative;inset:auto;top:auto;right:auto;left:auto;
    height:auto;display:grid;gap:0}
  .gx-signal .gxp-sg-copy{position:relative;inset:auto;display:block;
    padding:26px 0 30px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-signal .gxp-sg-number{display:block;font-size:5rem;line-height:.85}
  .gx-signal .gxp-sg-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-signal .gxp-sg-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-signal{min-height:auto;width:100%;margin-left:0}
  .gx-signal .gx-sticky{position:relative;min-height:auto;padding:10px 0 30px;overflow:visible}
  .gx-signal.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-signal .gxp-sg-halo,.gx-signal .gxp-sg-svg,
  .gx-signal .gxp-sg-node,.gx-signal .gxp-sg-readout{display:none}
  .gx-signal .gxp-sg-world{position:relative;inset:auto;min-height:0}
  .gx-signal .gxp-sg-stack{position:relative;inset:auto;top:auto;right:auto;left:auto;
    height:auto;display:grid;grid-template-columns:repeat(auto-fit,minmax(310px,1fr));gap:20px}
  .gx-signal .gxp-sg-copy{position:relative;inset:auto;display:block;
    padding:24px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-signal .gxp-sg-number{display:block;font-size:clamp(3.4rem,8vw,6rem);line-height:.85}
  .gx-signal .gxp-sg-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-signal .gxp-sg-copy p{font-size:.97rem}
}


/* ---- skin: zoomportal (process-v4 process-step-14 "Portal zoom sequence") —
   PROCESS family, takeover. Each step is a ring-gate hanging in space; the
   camera flies straight through them one after another, the passed gate blowing
   past the lens while the next one grows out of the dark. The copy holds the
   empty lower left and never crosses a gate.

   Port decisions (donor -> engine):
   - Donor `.ps-portal .ps-sticky` painted a violet wash over an opaque
     `linear-gradient(145deg,#04040a,#090818,#04050a)` plate. The plate is gone
     (GL-transparency decree); the gates' own concentric radial rings survive
     verbatim, re-derived from tokens, and a single 8% halo marks the throat.
   - DEPTH, NOT z-index. `.gxp-pz-stage` is `transform-style:preserve-3d`, so
     paint order inside it is 3D depth and a z-index there is ignored (three
     cards-v8 skins learned this the hard way). Gates are ordered purely by
     `--pz-z`, and the fly-through works BECAUSE of that: the passed gate is at
     positive z, genuinely in front of the camera plane. The copy stack and the
     chrome are deliberately SIBLINGS of that stage inside a FLAT `.gx-stage`,
     so ordinary z-index does order them above the gates — which is why a gate
     blowing past can never paint over the copy.
   - Donor hexes (#815dff, #b19cff) become the host token cycle.
   - Donor `.portal-glyph` was a per-step numeral inside the gate AND the copy
     block carried no numeral, so the numeral is kept exactly once per element:
     the gate glyph is the artwork's numbering, the copy has its own
     (depthstair precedent — one numbering FORMAT per element).
   - The donor's `ps-orbit` keyframe on the gate's dashed ring is kept as
     `gxpPortalOrbit`, and is switched OFF in the reduced-motion collapse: an
     infinite CSS animation keeps running there otherwise, which the static
     collapse contract forbids.
   Count-generality: gate depth is the slot delta times a constant, so 5, 6, 7
   and 8 steps all fly through at the same cadence. ---- */
.gx-zoomportal{min-height:min(345svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-zoomportal .gx-sticky{padding:0}
.gx-zoomportal .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,100svh,1000px)}

.gx-zoomportal .gxp-pz-halo{position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(circle at 71% 50%,
    color-mix(in srgb,var(--c-accent) 8%,transparent),transparent 34rem)}

/* the flight — the ONLY preserve-3d context in this skin */
.gx-zoomportal .gxp-pz-stage{position:absolute;inset:0;
  perspective:900px;perspective-origin:68% 50%;transform-style:preserve-3d}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   The animator's own progress-0 frame in calc(). At progress 0 `stepWalk`
   gives pos=0, so delta_i = i (never negative), and the two terms below are
   the animator's d >= 0 branch with `--step-i` substituted: gate 01 sits in the
   throat, full size and lit, and the rest recede at -520px per slot. Without it
   every gate falls back to `--pz-z:0` at opacity 1 — n rings exactly
   superimposed, which is what a mis-keyed ANIMATORS entry would ship
   permanently and what no gate in this project catches (MASTER-PLAN §2.1).
   The `:nth-of-type` ladder duplicates the renderer's inline `--step-i` so the
   floor survives a renderer that lost the style attribute. */
.gx-zoomportal .gxp-pz-step{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.22);
  --pz-z:calc(var(--step-i) * -520px);
  --pz-op:max(.04,calc(1 - var(--step-i) * .32 - max(0,var(--step-i) - 1) * .42));
  position:absolute;inset:0;pointer-events:none;transform-style:preserve-3d}
.gx-zoomportal .gxp-pz-step:nth-of-type(2){--step-i:1}
.gx-zoomportal .gxp-pz-step:nth-of-type(3){--step-i:2}
.gx-zoomportal .gxp-pz-step:nth-of-type(4){--step-i:3}
.gx-zoomportal .gxp-pz-step:nth-of-type(5){--step-i:4}
.gx-zoomportal .gxp-pz-step:nth-of-type(6){--step-i:5}
.gx-zoomportal .gxp-pz-step:nth-of-type(7){--step-i:6}
.gx-zoomportal .gxp-pz-step:nth-of-type(8){--step-i:7}

.gx-zoomportal .gxp-pz-gate{position:absolute;top:50%;left:71%;
  width:min(50vw,720px);aspect-ratio:1;border-radius:50%;
  border:2px solid color-mix(in srgb,var(--step-accent,var(--c-accent)) 76%,transparent);
  background:radial-gradient(circle,
    transparent 0 36%,
    color-mix(in srgb,var(--step-accent,var(--c-accent)) 7%,transparent) 36.5% 37%,
    transparent 37.5% 54%,
    color-mix(in srgb,var(--step-accent,var(--c-accent)) 9%,transparent) 54.5% 55%,
    transparent 55.5%);
  box-shadow:0 0 70px color-mix(in srgb,var(--step-accent,var(--c-accent)) 20%,transparent),
    inset 0 0 90px color-mix(in srgb,var(--step-accent,var(--c-accent)) 10%,transparent);
  /* no `filter`: a filter is a grouping property and would force this
     element's used transform-style to flat, collapsing the glyph's translateZ */
  opacity:var(--pz-op);
  transform:translate(-50%,-50%) translateZ(var(--pz-z));
  transform-style:preserve-3d;will-change:transform,opacity}
.gx-zoomportal .gxp-pz-gate::before{position:absolute;inset:9%;border-radius:50%;content:"";
  border:1px dashed color-mix(in srgb,var(--step-accent,var(--c-accent)) 62%,transparent);
  animation:gxpPortalOrbit 18s linear infinite}
.gx-zoomportal .gxp-pz-gate::after{position:absolute;inset:23%;border-radius:50%;content:"";
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent)}
@keyframes gxpPortalOrbit{to{transform:rotate(360deg)}}
.gx-zoomportal .gxp-pz-glyph{position:absolute;top:50%;left:50%;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 70%,var(--c-ink));
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(5rem,14vw,15rem);letter-spacing:-.12em;line-height:.7;
  text-shadow:0 0 55px color-mix(in srgb,var(--step-accent,var(--c-accent)) 50%,transparent);
  transform:translate(-50%,-50%) translateZ(80px)}

/* the copy — a SIBLING of the 3D stage, so z-index really does order it above
   the gates (see the note at the top of this file) */
.gx-zoomportal .gxp-pz-copy-stack{position:absolute;bottom:12vh;
  left:clamp(20px,6vw,120px);z-index:20;
  width:min(34vw,520px);height:clamp(240px,32vh,360px);pointer-events:none}
.gx-zoomportal .gxp-pz-copy{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.22);
  position:absolute;inset:0;display:flex;flex-direction:column;justify-content:flex-end;
  opacity:var(--local);
  transform:translate3d(calc(var(--delta) * -7vw),calc(var(--delta) * 7vh),0)}
.gx-zoomportal .gxp-pz-copy:nth-of-type(2){--step-i:1}
.gx-zoomportal .gxp-pz-copy:nth-of-type(3){--step-i:2}
.gx-zoomportal .gxp-pz-copy:nth-of-type(4){--step-i:3}
.gx-zoomportal .gxp-pz-copy:nth-of-type(5){--step-i:4}
.gx-zoomportal .gxp-pz-copy:nth-of-type(6){--step-i:5}
.gx-zoomportal .gxp-pz-copy:nth-of-type(7){--step-i:6}
.gx-zoomportal .gxp-pz-copy:nth-of-type(8){--step-i:7}
.gx-zoomportal .gxp-pz-copy-number{display:block;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 17%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(4.6rem,11vw,12rem);letter-spacing:-.12em;line-height:.58}
.gx-zoomportal .gxp-pz-copy .gx-kicker{margin:20px 0 11px;
  color:var(--step-accent,var(--c-accent))}
.gx-zoomportal .gxp-pz-copy h3{max-width:640px;margin:0 0 16px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.2rem,5.4vw,6rem);letter-spacing:-.072em;line-height:.86;
  text-wrap:balance}
.gx-zoomportal .gxp-pz-copy p{max-width:580px;margin:0;color:var(--gx-muted);
  font-size:clamp(.98rem,1.36vw,1.18rem);line-height:1.5}

.gx-zoomportal .gxp-pz-nav{--pz-n:5;position:absolute;right:clamp(24px,5vw,82px);
  bottom:clamp(20px,3vh,36px);left:clamp(24px,5vw,82px);z-index:24;
  display:grid;grid-template-columns:repeat(var(--pz-n),minmax(0,1fr));gap:12px}
.gx-zoomportal .gxp-pz-nav span{--nav-local:0;position:relative;padding-top:17px;
  font-family:var(--f-utility);font-size:.66rem;font-weight:800;letter-spacing:.1em;
  text-transform:uppercase;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
  color:color-mix(in srgb,var(--c-ink) calc(32% + var(--nav-local) * 64%),transparent)}
.gx-zoomportal .gxp-pz-nav span::before{position:absolute;top:0;right:0;left:0;height:2px;
  content:"";background:var(--gx-line-strong)}
.gx-zoomportal .gxp-pz-nav span::after{position:absolute;top:0;left:0;width:100%;height:2px;
  content:"";background:var(--c-accent);
  box-shadow:0 0 16px color-mix(in srgb,var(--c-accent) 58%,transparent);
  transform:scaleX(var(--nav-local));transform-origin:left}
.gx-zoomportal .gxp-pz-depth{position:absolute;top:50%;right:26px;z-index:24;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.63rem;font-weight:800;
  letter-spacing:.15em;text-transform:uppercase;writing-mode:vertical-rl;
  transform:translateY(-50%)}
.gx-zoomportal .gxp-pz-depth strong{color:var(--c-ink)}

@media (min-width:901px) and (max-width:1180px){
  .gx-zoomportal .gxp-pz-gate{left:74%;width:min(52vw,560px)}
  .gx-zoomportal .gxp-pz-copy-stack{left:36px;width:34vw}
}
@media (min-width:901px) and (max-height:820px){
  .gx-zoomportal .gxp-pz-copy-stack{height:clamp(210px,28vh,290px);bottom:10vh}
  .gx-zoomportal .gxp-pz-copy-number{font-size:clamp(3.4rem,8vw,8rem)}
  .gx-zoomportal .gxp-pz-copy h3{font-size:clamp(1.9rem,4.2vw,4.4rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the
   flight opens out into a stack of gate + copy reads. ---- */
@media (max-width:900px){
  .gx-zoomportal{min-height:auto;width:100%;margin-left:0}
  .gx-zoomportal .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-zoomportal.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-zoomportal .gxp-pz-halo,.gx-zoomportal .gxp-pz-nav,
  .gx-zoomportal .gxp-pz-depth{display:none}
  .gx-zoomportal .gxp-pz-stage{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --pz-z or --local left by a desktop -> mobile resize must
     never be able to send a gate to infinity or blank its copy. */
  .gx-zoomportal .gxp-pz-step{position:relative;inset:auto;padding:14px 0 6px;
    transform-style:flat}
  .gx-zoomportal .gxp-pz-gate{position:relative;top:auto;left:auto;
    width:min(80vw,320px);margin:0 auto;opacity:1!important;
    transform:none!important;transform-style:flat;will-change:auto}
  .gx-zoomportal .gxp-pz-glyph{font-size:5.4rem;transform:translate(-50%,-50%)!important}
  .gx-zoomportal .gxp-pz-copy-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;gap:0}
  .gx-zoomportal .gxp-pz-copy{position:relative;inset:auto;
    padding:22px 0 28px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-zoomportal .gxp-pz-copy-number{font-size:4.8rem}
  .gx-zoomportal .gxp-pz-copy h3{font-size:clamp(1.9rem,8vw,2.7rem)}
  .gx-zoomportal .gxp-pz-copy p{font-size:.95rem}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width, and the gate's orbit keyframe is switched OFF — an infinite CSS
   animation would otherwise keep running where the frame loop stands down. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-zoomportal{min-height:auto;width:100%;margin-left:0}
  .gx-zoomportal .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-zoomportal.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-zoomportal .gxp-pz-halo,.gx-zoomportal .gxp-pz-nav,
  .gx-zoomportal .gxp-pz-depth{display:none}
  .gx-zoomportal .gxp-pz-gate::before{animation:none}
  .gx-zoomportal .gxp-pz-stage{position:relative;inset:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;
    perspective:none;transform-style:flat}
  .gx-zoomportal .gxp-pz-step{position:relative;inset:auto;padding:10px 0;
    transform-style:flat}
  .gx-zoomportal .gxp-pz-gate{position:relative;top:auto;left:auto;
    width:min(100%,280px);margin:0 auto;opacity:1!important;
    transform:none!important;transform-style:flat;will-change:auto}
  .gx-zoomportal .gxp-pz-glyph{font-size:clamp(3.4rem,8vw,6rem);
    transform:translate(-50%,-50%)!important}
  .gx-zoomportal .gxp-pz-copy-stack{position:relative;bottom:auto;left:auto;
    width:100%;height:auto;display:grid;
    grid-template-columns:repeat(auto-fit,minmax(300px,1fr));gap:18px}
  .gx-zoomportal .gxp-pz-copy{position:relative;inset:auto;
    padding:20px 0 26px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important}
  .gx-zoomportal .gxp-pz-copy-number{font-size:clamp(3.2rem,8vw,5.4rem)}
  .gx-zoomportal .gxp-pz-copy h3{font-size:clamp(1.9rem,3.6vw,2.9rem)}
  .gx-zoomportal .gxp-pz-copy p{font-size:.97rem}
}


/* ---- skin: origami (process-v4 process-step-15 "Origami storyboard") —
   PROCESS family, takeover. The steps are leaves of a folded storyboard hinged
   on their left edge: the leaf you are reading lies flat to the camera while
   the one behind is still folded away and the one you have passed has swung
   open past you. A turned-down paper corner and a single crease line carry the
   paper.

   Port decisions (donor -> engine):
   - Donor `.ps-fold .ps-sticky` painted an opaque `#e9e4da` plate with `#111319`
     ink — the widget shipped a LIGHT theme inside itself. Both are gone: the
     sticky is transparent, ink is `--c-ink`, and the paper survives as
     `.gxp-og-leaf`'s token veil (dominant at 20% falling to nothing) plus the
     donor's crease geometry. The skin now reads on dark AND light packs.
   - Donor `.fold-header` / `.fold-nav` used `mix-blend-mode:difference` to stay
     legible over an unknown ground; over a live GL field that is unpredictable,
     so both are token-coloured here.
   - Donor per-step `--fold-bg` / `--fold-ink` / `--fold-accent` hexes become the
     host token cycle.
   - `.gxp-og-corner` (the turned-down corner) keeps the donor's clip-path
     triangle but loses its opaque fill for a token gradient, so it reads as a
     lifted corner rather than a pasted box.
   - THE FOLD NEEDS NO ANIMATOR GEOMETRY. `--delta` is already written on every
     leaf by the shared step walk, so the hinge angle is
     `clamp(-1, --delta, 1) * -78deg` in pure CSS, and the leaf's forward pop is
     `--local * 60px` of translateZ. That also means the CSS dead-scene floor
     below is not an approximation of the animated state — it IS the animated
     state, evaluated at `--delta = --step-i`.
   - DEPTH, NOT z-index: `.gxp-og-stack` is preserve-3d, so the leaves are
     ordered by that translateZ. The header, nav and readout are SIBLINGS of the
     stack inside a FLAT `.gx-stage`, so z-index does order them above it.
   Count-generality: the hinge angle and the pop are pure functions of the slot
   delta, so 5, 6, 7 and 8 leaves fold identically. ---- */
.gx-origami{min-height:min(335svh,calc(100svh + (var(--gx-n,6) - 1) * 12svh));width:100vw;margin-left:calc(50% - 50vw)}
.gx-origami .gx-sticky{padding:0}
.gx-origami .gx-stage{position:relative;display:block;perspective:none;
  transform-style:flat;min-height:clamp(560px,100svh,1000px)}

.gx-origami .gxp-og-stack{position:absolute;inset:0;
  perspective:1800px;perspective-origin:38% 50%;transform-style:preserve-3d}

/* ---- DEAD-SCENE FLOOR -----------------------------------------------------
   Unusually for this library the floor is EXACT rather than transcribed: the
   fold is written in terms of `--delta`, which `stepWalk` writes every frame,
   so setting `--delta: --step-i` here reproduces the animator's progress-0
   frame identically (at progress 0, pos = 0 and delta_i = i). Leaf 01 lies
   flat and lit; the rest are folded away at -78deg and dark. Without it every
   leaf falls back to `--delta:0` — n leaves all lying flat and superimposed
   with only the last legible, which is what a mis-keyed ANIMATORS entry would
   ship permanently and what no gate in this project catches (MASTER-PLAN §2.1).
   The `:nth-of-type` ladder duplicates the renderer's inline `--step-i` so the
   floor survives a renderer that lost the style attribute. */
.gx-origami .gxp-og-leaf{--step-i:0;
  --delta:var(--step-i);--local:max(0,1 - var(--step-i) * 1.2);
  position:absolute;inset:0;display:grid;
  grid-template-columns:minmax(0,.84fr) minmax(0,1.16fr);
  gap:clamp(24px,5vw,110px);align-items:center;
  padding:clamp(72px,9vh,134px) clamp(24px,6vw,112px) clamp(74px,9vh,104px);
  overflow:hidden;pointer-events:none;backface-visibility:hidden;
  transform-origin:0 50%;
  /* The leaf's own fade is steepened (x1.9 with a -.18 bias) rather than the
     copy's. Measured: the previous version faded the leaf gently and biased the
     COPY down instead, and the two multiplied — mid-slot legibility fell to
     0.092, i.e. the scene went dark between every pair of steps, while a leaf
     0.75 slots away was still at 13.5% and veiled the leaf in front of it
     across 100% of its copy. This curve reaches 0 by |delta| = 0.75 (so a
     distant leaf cannot veil anything) and holds 0.58 at the half-turn, which
     is the highest mid-transition legibility in this batch. */
  opacity:clamp(0,calc(var(--local) * 1.9 - .18),1);
  transform:translateZ(calc(var(--local) * 60px))
    rotateY(calc(clamp(-1,var(--delta),1) * -78deg));
  will-change:transform,opacity}
.gx-origami .gxp-og-leaf:nth-of-type(2){--step-i:1}
.gx-origami .gxp-og-leaf:nth-of-type(3){--step-i:2}
.gx-origami .gxp-og-leaf:nth-of-type(4){--step-i:3}
.gx-origami .gxp-og-leaf:nth-of-type(5){--step-i:4}
.gx-origami .gxp-og-leaf:nth-of-type(6){--step-i:5}
.gx-origami .gxp-og-leaf:nth-of-type(7){--step-i:6}
.gx-origami .gxp-og-leaf:nth-of-type(8){--step-i:7}
.gx-origami .gxp-og-leaf.is-active{pointer-events:auto}

/* the paper — a token veil and the donor's crease geometry, never a plate */
.gx-origami .gxp-og-paper{position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    linear-gradient(118deg,transparent 0 48%,
      color-mix(in srgb,var(--c-ink) 8%,transparent) 48.1% 48.3%,transparent 48.4%),
    linear-gradient(90deg,color-mix(in srgb,var(--c-ink) 4%,transparent) 1px,transparent 1px),
    linear-gradient(255deg,color-mix(in srgb,var(--c-dominant) 20%,transparent),transparent 72%);
  background-size:auto,74px 100%,auto}
.gx-origami .gxp-og-crease{position:absolute;top:-20%;right:19%;width:2px;height:145%;
  z-index:1;pointer-events:none;
  background:linear-gradient(transparent,
    color-mix(in srgb,var(--c-ink) 26%,transparent),transparent);
  transform:rotate(18deg)}
.gx-origami .gxp-og-corner{position:absolute;right:0;bottom:0;z-index:3;
  width:clamp(120px,17vw,290px);aspect-ratio:1;pointer-events:none;
  background:linear-gradient(135deg,
    color-mix(in srgb,var(--c-field) 46%,transparent),
    color-mix(in srgb,var(--c-ink) 12%,transparent));
  clip-path:polygon(100% 0,100% 100%,0 100%);
  filter:drop-shadow(-16px -18px 22px rgba(0,0,0,.22))}

/* the leaf PLATE stays visible through the turn (that is the paper), but its
   COPY must not: two leaves mid-turn would otherwise show two headlines at
   ~54% each — the superimposed-copy defect. Biasing the falloff down takes the
   copy to ~.17 at the half-turn while the plate is still .54. */
/* The donor's horizontal offset on the copy is RESTORED. Dropping it left the
   outgoing and incoming leaves' copy blocks at the same x, and measurement put
   their overlap at 94-96% of the active headline mid-turn — against 57-64% for
   `mountain`, the worst SHIPPED process skin on the same metric. `--delta` is
   signed, so the two neighbours separate in opposite directions. */
.gx-origami .gxp-og-copy{position:relative;z-index:5;max-width:690px;
  transform:translate3d(calc(var(--delta) * -7vw),calc((1 - var(--local)) * 32px),0)}
.gx-origami .gxp-og-copy .gx-kicker{display:inline-flex;align-items:center;gap:12px;
  color:var(--step-accent,var(--c-accent))}
.gx-origami .gxp-og-copy .gx-kicker::before{width:34px;height:2px;content:"";
  background:var(--step-accent,var(--c-accent))}
.gx-origami .gxp-og-copy h3{margin:26px 0 18px;color:var(--c-ink);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.5rem,6.2vw,7rem);letter-spacing:-.076em;line-height:.84;
  text-wrap:balance}
.gx-origami .gxp-og-copy p{max-width:600px;margin:0;color:var(--gx-muted);
  font-size:clamp(1rem,1.38vw,1.18rem);line-height:1.5}
.gx-origami .gxp-og-number{position:relative;z-index:4;justify-self:end;
  color:color-mix(in srgb,var(--step-accent,var(--c-accent)) 78%,transparent);
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(8rem,22vw,26rem);letter-spacing:-.14em;line-height:.62;
  transform:translateX(calc(var(--delta) * 7vw))}

.gx-origami .gxp-og-header{position:absolute;top:clamp(18px,3vh,34px);
  right:clamp(24px,5vw,82px);left:clamp(24px,5vw,82px);z-index:30;
  display:flex;align-items:center;justify-content:space-between;
  color:var(--gx-muted);font-family:var(--f-utility);font-size:.67rem;
  font-weight:800;letter-spacing:.15em;text-transform:uppercase}
.gx-origami .gxp-og-header strong{color:var(--c-ink);font-size:1rem}
.gx-origami .gxp-og-nav{position:absolute;right:clamp(24px,5vw,82px);
  bottom:clamp(18px,2.8vh,32px);left:clamp(24px,5vw,82px);z-index:30;
  display:flex;justify-content:space-between;gap:15px}
.gx-origami .gxp-og-nav span{--nav-local:0;display:flex;align-items:center;gap:9px;
  min-width:0;font-family:var(--f-utility);font-size:.66rem;font-weight:800;
  letter-spacing:.1em;text-transform:uppercase;
  color:color-mix(in srgb,var(--c-ink) calc(34% + var(--nav-local) * 62%),transparent)}
.gx-origami .gxp-og-nav span::before{flex:none;width:34px;height:2px;content:"";
  background:var(--gx-line-strong);
  box-shadow:inset calc(var(--nav-local) * 34px) 0 var(--c-accent)}
.gx-origami .gxp-og-nav b{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

@media (min-width:901px) and (max-width:1180px){
  .gx-origami .gxp-og-leaf{grid-template-columns:1fr 1fr;padding-inline:38px}
  .gx-origami .gxp-og-number{font-size:22vw}
}
@media (min-width:901px) and (max-height:820px){
  .gx-origami .gxp-og-copy h3{font-size:clamp(2.1rem,4.8vw,5rem)}
  .gx-origami .gxp-og-number{font-size:clamp(6rem,17vw,17rem)}
}

/* ---- static collapse A: <=900px. Breakout reverted, scene unpinned; the fold
   opens out flat into a stack of full-width leaves. ---- */
@media (max-width:900px){
  .gx-origami{min-height:auto;width:100%;margin-left:0}
  .gx-origami .gx-sticky{position:relative;min-height:auto;padding:10px 0 24px;overflow:visible}
  .gx-origami.gx-scene .gx-stage{display:block;min-height:auto;padding:0 14px}
  .gx-origami .gxp-og-header,.gx-origami .gxp-og-nav,
  .gx-origami .gxp-og-crease{display:none}
  .gx-origami .gxp-og-stack{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  /* !important on the animated three: the frame loop stands down here, so a
     stale inline --delta or --local left by a desktop -> mobile resize must
     never be able to fold a leaf away or blank it. */
  .gx-origami .gxp-og-leaf{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,1fr);gap:12px;
    padding:30px 0 36px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important;transform-origin:50% 50%;
    will-change:auto}
  .gx-origami .gxp-og-paper{display:none}
  .gx-origami .gxp-og-corner{width:110px;opacity:.5}
  .gx-origami .gxp-og-copy{opacity:1!important;transform:none!important}
  .gx-origami .gxp-og-copy h3{font-size:clamp(1.9rem,8vw,2.7rem);margin:18px 0 14px}
  .gx-origami .gxp-og-copy p{font-size:.95rem}
  .gx-origami .gxp-og-number{order:-1;justify-self:start;font-size:5.6rem;
    opacity:1!important;transform:none!important}
}

/* ---- static collapse B: reduced motion. Same resting composition at every
   width; nothing here reads --scene-progress. ---- */
@media (prefers-reduced-motion:reduce){
  .gx-origami{min-height:auto;width:100%;margin-left:0}
  .gx-origami .gx-sticky{position:relative;min-height:auto;padding:10px 0 28px;overflow:visible}
  .gx-origami.gx-scene .gx-stage{display:block;min-height:auto;padding:0 clamp(14px,3vw,44px)}
  .gx-origami .gxp-og-header,.gx-origami .gxp-og-nav,
  .gx-origami .gxp-og-crease{display:none}
  .gx-origami .gxp-og-stack{position:relative;inset:auto;display:grid;
    perspective:none;transform-style:flat}
  .gx-origami .gxp-og-leaf{position:relative;inset:auto;display:grid;
    grid-template-columns:minmax(0,1fr) minmax(0,.5fr);gap:clamp(18px,4vw,54px);
    align-items:center;padding:32px 0 38px;border-bottom:1px solid var(--gx-line);
    opacity:1!important;transform:none!important;transform-origin:50% 50%;
    will-change:auto}
  .gx-origami .gxp-og-paper{display:none}
  .gx-origami .gxp-og-corner{width:120px;opacity:.5}
  .gx-origami .gxp-og-copy{opacity:1!important;transform:none!important}
  .gx-origami .gxp-og-copy h3{font-size:clamp(1.9rem,3.8vw,3rem);margin:20px 0 16px}
  .gx-origami .gxp-og-copy p{font-size:.97rem}
  .gx-origami .gxp-og-number{justify-self:end;font-size:clamp(4rem,10vw,7.5rem);
    opacity:1!important;transform:none!important}
}

/* ---------- webgl canvas placement ---------- */
.gl{pointer-events:none}
.gl--page{position:fixed;inset:0;z-index:-2}
.gl--hero{position:absolute;inset:0;z-index:0}
.hero{transform-style:flat}
.hero__inner{z-index:2}
.hero__cue{z-index:2}

/* machine track sections stay transparent so the crystal shows through everywhere */
body[data-gl="crystal"] section:not(.scene){background:transparent}
body[data-track="orbit"] .stack--fs .stack__card{
  background:color-mix(in srgb,var(--c-field) 92%,transparent);
  backdrop-filter:blur(10px);
  border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent)}
body[data-track="orbit"] .stack--fs .stack__card:nth-child(even){
  background:color-mix(in srgb,var(--c-dominant) 90%,transparent)}
body[data-track="orbit"] .tst__card,body[data-track="orbit"] .casc__cell{
  background:color-mix(in srgb,var(--c-dominant) 86%,transparent);backdrop-filter:blur(8px)}

/* ---------- testimonials ---------- */
.tst{display:grid;gap:1px;background:color-mix(in srgb,var(--c-ink) 10%,transparent);margin-top:3rem}
.tst__card{background:var(--c-dominant);padding:clamp(2rem,4vw,3.6rem);display:grid;gap:1.4rem;
  position:relative;overflow:hidden}
.tst__mark{position:absolute;top:-1.4rem;right:.6rem;font-family:var(--f-display);
  font-size:9rem;line-height:1;opacity:.07;pointer-events:none}
.tst__q{font-family:var(--f-display);font-weight:var(--fw-display);letter-spacing:var(--tr-display);
  font-size:clamp(1.25rem,2.1vw,1.7rem);line-height:1.32;text-wrap:balance}
.tst__who{display:flex;align-items:center;gap:.9rem;font-family:var(--f-utility);
  font-size:.72rem;letter-spacing:.13em;text-transform:uppercase}
.tst__who b{color:var(--c-accent)}
.tst__stars{color:var(--c-accent);letter-spacing:.2em;
  text-shadow:0 1px 6px color-mix(in srgb,var(--c-dominant) 70%,transparent)}
@media(min-width:901px){.tst{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
  .tst__card:nth-child(2){transform:translateY(2.6rem)}}

/* ---------- structured footer: NAP + hours, the local-business anchor ---------- */
.foot{border-top:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  padding:clamp(3rem,7vh,5.5rem) 0 2.2rem;text-transform:none;opacity:1;font-size:1rem}
/* C6: on `lens`, `div.lens__media` is a FIXED full-viewport video at z-index:-2, so it paints
   over the body's own background and UNDER page content — and the footer declares no ground, so
   footer copy sits directly on footage. Under reduced motion the video holds a paused frame,
   and on a mid-grey frame the FOOTER PHONE NUMBER (accent rust, 30.4px) measured **1.14:1**,
   reproducible 2/2 (out-A1/dentist__lens-cinema, found by the A1 sweep 2026-08-11). Motion-on
   passes at all three viewports, which is why only the reduced-motion pass caught it.
   A phone number is the most important conversion element on a local-service page, so it gets a
   real ground rather than luck: the doctrine is that text never borrows contrast from footage. */
body[data-track="lens"] .foot{position:relative;isolation:isolate}
body[data-track="lens"] .foot::before{position:absolute;z-index:-1;content:"";
  /* bottom was -100vh, which added a full empty viewport of paper BELOW the footer —
     a blank scrollable panel at the end of every lens page. The page background is
     already --c-dominant, so a small symmetric bleed is all this needs. */
  inset:-6vh -50vw -6vh -50vw;
  background:var(--c-dominant)}
.foot__grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:clamp(1.6rem,3vw,3rem)}
.foot__brand{display:grid;gap:1rem;align-content:start}
.foot__logo{height:2.4rem;width:auto}
.foot h4{font-family:var(--f-utility);font-size:.68rem;letter-spacing:.18em;
  text-transform:uppercase;opacity:.65;margin:0 0 1rem}
.foot ul{list-style:none;margin:0;padding:0;display:grid;gap:.55rem;font-size:.92rem}
.foot a:hover{color:var(--c-accent)}
.foot__tel{font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(1.3rem,2.4vw,1.9rem);color:var(--c-accent)}
.foot__legal{display:flex;justify-content:space-between;gap:1rem;margin-top:3rem;
  padding-top:1.4rem;border-top:1px solid color-mix(in srgb,var(--c-ink) 10%,transparent);
  font-family:var(--f-utility);font-size:.68rem;letter-spacing:.12em;text-transform:uppercase;opacity:.62}
@media(max-width:900px){.foot__grid{grid-template-columns:1fr 1fr}
  .tst__card:nth-child(2){transform:none}}


/* ============================================================
   VIDEO MORPH — the video that transforms while you scroll.
   Starts as a rounded card at 38%, expands to full bleed, headline
   lands when it reaches full size. Apple-style scroll metamorphosis.
   ============================================================ */
.vm{height:280vh;position:relative}
.vm__vp{position:sticky;top:0;height:100svh;overflow:hidden;display:grid;place-items:center}
.vm__media{position:absolute;inset:0;will-change:transform,border-radius;overflow:hidden;
  transform:scale(.38);border-radius:26px;box-shadow:0 60px 140px -40px rgba(0,0,0,.8)}
.vm__media video,.vm__media img{width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.18)) contrast(1.05)}
.vm__cap{position:absolute;bottom:12vh;left:50%;transform:translateX(-50%);
  font-family:var(--f-utility);font-size:.72rem;letter-spacing:.22em;text-transform:uppercase;
  opacity:.7;will-change:opacity;white-space:nowrap}
.vm__h{position:relative;z-index:2;text-align:center;opacity:0;transform:translateY(30px) scale(.96);
  will-change:opacity,transform;font-size:clamp(2.6rem,8vw,7.5rem);line-height:.9;
  text-shadow:0 2px 16px rgba(0,0,0,.55),0 10px 60px rgba(0,0,0,.5)}
.vm__scrim{position:absolute;inset:0;opacity:0;will-change:opacity;
  background:linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 70%,transparent),transparent 55%)}
@media(max-width:900px){.vm{height:220vh}.vm__media{transform:scale(.6);border-radius:16px}}
@media(prefers-reduced-motion:reduce){.vm{height:auto}.vm__vp{position:static;height:70svh}
  .vm__media{transform:none;border-radius:0}.vm__h{opacity:1;transform:none}}

/* video_reveal — the "Cortex interface-reveal" second-video scene. Behaviour in engine.js
   (initVideoReveal): clip-path expands the pane to full-bleed, then narrates the headline
   sets. Colour token-derived so it re-hosts on any palette. */
.vreveal{height:360svh;position:relative;background:var(--c-dominant)}
.vreveal__sticky{position:sticky;top:0;height:100svh;overflow:hidden;background:var(--c-dominant)}
.vreveal__media{position:absolute;inset:0;overflow:hidden;clip-path:inset(47% 47% round 22px);
  transform:scale(.93);transform-origin:center;will-change:clip-path,transform}
.vreveal__media video,.vreveal__media img,.vreveal__canvas{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
.vreveal__canvas{z-index:1}
.vreveal__scrim{position:absolute;inset:0;z-index:2;pointer-events:none;
  background:linear-gradient(90deg,color-mix(in srgb,var(--c-dominant) 52%,transparent),color-mix(in srgb,var(--c-dominant) 6%,transparent) 52%,color-mix(in srgb,var(--c-dominant) 36%,transparent)),
    linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 64%,transparent),transparent 48%),
    radial-gradient(circle at 57% 44%,transparent 20%,color-mix(in srgb,var(--c-ink) 20%,transparent) 100%)}
.vreveal__grid{position:absolute;inset:0;z-index:3;opacity:.12;pointer-events:none;
  background-image:linear-gradient(color-mix(in srgb,var(--c-glow2,var(--c-accent)) 55%,transparent) 1px,transparent 1px),
    linear-gradient(90deg,color-mix(in srgb,var(--c-glow2,var(--c-accent)) 55%,transparent) 1px,transparent 1px);
  background-size:72px 72px;-webkit-mask-image:radial-gradient(circle at center,var(--c-ink) 15%,transparent 72%);
  mask-image:radial-gradient(circle at center,var(--c-ink) 15%,transparent 72%)}
.vreveal__set{position:absolute;z-index:7;left:clamp(24px,5vw,74px);right:clamp(24px,5vw,74px);top:50%;
  transform:translateY(-50%);opacity:0;filter:blur(16px);pointer-events:none;will-change:opacity,filter}
.vreveal__set .vreveal__eyebrow{font-family:var(--f-utility);font-weight:500;font-size:10px;
  text-transform:uppercase;letter-spacing:.15em;color:color-mix(in srgb,var(--c-ink) 60%,transparent);margin-bottom:24px}
.vreveal__set h2{font-family:var(--f-display);font-weight:var(--fw-display,400);
  font-size:clamp(64px,10vw,158px);line-height:.76;letter-spacing:-.065em;margin:0;color:var(--c-ink)}
.vreveal__set-bottom{margin-top:6px}
.vreveal__set-bottom h2{-webkit-text-stroke:1px color-mix(in srgb,var(--c-ink) 62%,transparent);color:transparent}
.vreveal__rail{position:absolute;z-index:8;right:clamp(24px,4vw,62px);top:50%;transform:translateY(-50%);
  display:flex;flex-direction:column;align-items:center;gap:12px}
.vreveal__rail-line{width:1px;height:140px;background:color-mix(in srgb,var(--c-ink) 16%,transparent);position:relative;overflow:hidden}
.vreveal__rail-fill{position:absolute;left:0;right:0;top:0;height:0;background:color-mix(in srgb,var(--c-accent) 82%,var(--c-ink))}
.vreveal__rail span{writing-mode:vertical-rl;font-family:var(--f-utility);font-size:8px;letter-spacing:.16em;
  text-transform:uppercase;color:color-mix(in srgb,var(--c-ink) 40%,transparent)}
.vreveal__corner{position:absolute;z-index:8;left:clamp(24px,4vw,62px);bottom:27px;
  font-family:var(--f-utility);font-size:8px;letter-spacing:.14em;text-transform:uppercase;color:color-mix(in srgb,var(--c-ink) 38%,transparent)}
@media(max-width:900px){.vreveal{height:auto}.vreveal__sticky{position:relative;height:88svh}
  .vreveal__media{clip-path:inset(0 round 0);transform:none}
  .vreveal__set{position:absolute}.vreveal__set h2{font-size:clamp(52px,14vw,96px)}
  .vreveal__rail{right:16px}.vreveal__rail-line{height:100px}}
@media(prefers-reduced-motion:reduce){.vreveal{height:auto}.vreveal__sticky{position:static;height:78svh}
  .vreveal__media{clip-path:inset(0 round 0);transform:none}}

/* ============================================================
   KEY BENEFITS — signature benefits grid (donor: guardnet.html #benefits)
   Dark rounded cards; text cards carry a soft glowing accent orb behind
   the copy, one card may be an ambient video. Colour is all --c-* tokens.
   ============================================================ */
.kben{position:relative;background:var(--c-dominant);padding:clamp(72px,11vh,132px) 0}
.kben__h{text-align:center;font-family:var(--f-display,sans-serif);font-weight:300;
  font-size:clamp(30px,4.4vw,54px);line-height:1.02;letter-spacing:-.04em;color:var(--c-ink);margin:0 auto}
.kben__sub{text-align:center;max-width:640px;margin:18px auto 0;font-size:clamp(14px,1.1vw,17px);
  line-height:1.6;color:color-mix(in srgb,var(--c-ink) 68%,transparent)}
.kben__grid{display:grid;grid-template-columns:repeat(3,1fr);gap:clamp(12px,1.4vw,18px);
  margin-top:clamp(44px,7vh,92px)}
.kben__card{position:relative;min-height:clamp(360px,42vh,470px);border-radius:22px;overflow:hidden;
  background:color-mix(in srgb,var(--c-ink) 5%,var(--c-dominant));
  border:1px solid color-mix(in srgb,var(--c-ink) 9%,transparent)}
/* the glow orb — a soft accent radial pushed off an edge behind the copy */
.kben__orb{position:absolute;width:clamp(300px,34vw,460px);height:clamp(300px,34vw,460px);border-radius:50%;
  filter:blur(72px);opacity:.42;pointer-events:none;
  background:radial-gradient(circle at center,var(--c-glow2,var(--c-accent)) 0%,color-mix(in srgb,var(--c-accent) 40%,transparent) 45%,transparent 72%)}
.kben__card--orb-l .kben__orb{left:-46%;top:50%;transform:translateY(-50%)}
.kben__card--orb-r .kben__orb{right:-30%;top:-30%}
.kben__body{position:relative;z-index:2;height:100%;display:flex;flex-direction:column;padding:clamp(24px,2vw,34px)}
.kben__body h3{font-family:var(--f-display,sans-serif);font-weight:300;font-size:clamp(20px,1.7vw,26px);
  line-height:1.14;letter-spacing:-.02em;color:var(--c-ink);margin:0}
.kben__card--text .kben__body p{margin-top:auto;padding-top:clamp(40px,7vh,80px);max-width:300px;
  font-size:clamp(13px,.95vw,14px);line-height:1.6;color:color-mix(in srgb,var(--c-ink) 66%,transparent)}
/* video card — ambient loop on top, title docked beneath */
.kben__card--video{display:flex;flex-direction:column}
.kben__vid{position:relative;width:100%;height:74%;overflow:hidden}
.kben__vid video{width:100%;height:100%;object-fit:cover;display:block}
.kben__vscrim{position:absolute;left:0;right:0;bottom:0;height:44%;pointer-events:none;
  background:linear-gradient(to bottom,transparent,color-mix(in srgb,var(--c-ink) 5%,var(--c-dominant)))}
.kben__vfoot{flex:1;display:flex;align-items:center;padding:0 clamp(24px,2vw,32px)}
.kben__vfoot h3{font-family:var(--f-display,sans-serif);font-weight:300;font-size:clamp(20px,1.7vw,26px);
  line-height:1.14;letter-spacing:-.02em;color:var(--c-ink);margin:0}
@media(max-width:920px){.kben__grid{grid-template-columns:1fr;max-width:520px;margin-inline:auto}
  .kben__card{min-height:340px}}

/* ============================================================
   RITUAL PATH — signature method/process (donor: bloom.html #rituals)
   Sticky left intro + right vertical numbered timeline with a running
   accent line; final node filled in accent. Colour is all --c-* tokens.
   ============================================================ */
.ritual{position:relative;background:var(--c-dominant);padding:clamp(80px,13vh,180px) 0}
.ritual__wrap{display:grid;grid-template-columns:1fr;gap:clamp(48px,7vw,96px);align-items:start}
@media(min-width:1024px){.ritual__wrap{grid-template-columns:1fr 1fr}}
.ritual__intro{align-self:start}
@media(min-width:1024px){.ritual__intro{position:sticky;top:clamp(96px,14vh,140px)}}
.ritual__eyebrow{font-family:var(--f-utility,monospace);font-size:11px;font-weight:500;text-transform:uppercase;
  letter-spacing:.2em;color:var(--c-accent);margin:0 0 18px}
.ritual__h2{font-family:var(--f-display,serif);font-weight:400;font-size:clamp(2.4rem,5.5vw,4.5rem);
  line-height:1.05;letter-spacing:-.02em;color:var(--c-ink);margin:0 0 22px}
.ritual__em{font-style:italic;color:var(--c-accent)}
.ritual__lede{font-family:var(--f-body,sans-serif);font-size:clamp(1rem,1.2vw,1.15rem);line-height:1.6;
  color:color-mix(in srgb,var(--c-ink) 60%,transparent);max-width:34rem;margin:0 0 32px}
.ritual__cta{display:inline-flex;align-items:center;gap:10px;padding:14px 28px;border-radius:10px;
  font-family:var(--f-utility,sans-serif);font-size:13px;font-weight:500;text-decoration:none;color:var(--c-ink);
  border:1px solid color-mix(in srgb,var(--c-ink) 20%,transparent);transition:border-color .3s ease,background .3s ease}
.ritual__cta svg{width:18px;height:18px}
.ritual__cta:hover{border-color:color-mix(in srgb,var(--c-ink) 50%,transparent);background:color-mix(in srgb,var(--c-ink) 7%,transparent)}
/* right timeline */
.ritual__track{position:relative;padding-left:clamp(48px,6vw,72px)}
.ritual__line{position:absolute;left:clamp(19px,2.4vw,23px);top:56px;bottom:40px;width:1px;
  background:linear-gradient(180deg,var(--c-accent),color-mix(in srgb,var(--c-accent) 10%,transparent))}
.ritual__step{position:relative;padding-bottom:clamp(44px,7vh,88px)}
.ritual__step--last{padding-bottom:0}
.ritual__node{position:absolute;left:calc(-1 * clamp(48px,6vw,72px));top:0;width:40px;height:40px;border-radius:50%;
  display:grid;place-items:center;background:var(--c-dominant);border:1px solid color-mix(in srgb,var(--c-ink) 30%,transparent)}
.ritual__node span{font-family:var(--f-utility,monospace);font-size:12px;color:var(--c-ink)}
.ritual__step:first-child .ritual__node{border-color:var(--c-accent)}
.ritual__step:first-child .ritual__node span{color:var(--c-accent)}
.ritual__step--last .ritual__node{background:var(--c-accent);border-color:var(--c-accent)}
.ritual__step--last .ritual__node span{color:var(--c-on-accent,var(--c-dominant))}
.ritual__kicker{font-family:var(--f-utility,monospace);font-size:10px;text-transform:uppercase;letter-spacing:.16em;
  color:color-mix(in srgb,var(--c-ink) 40%,transparent);margin:0 0 8px}
.ritual__h3{font-family:var(--f-display,serif);font-weight:400;font-size:clamp(1.5rem,2.4vw,2rem);line-height:1.1;
  color:var(--c-ink);margin:0 0 12px}
.ritual__copy{font-family:var(--f-body,sans-serif);font-size:clamp(.95rem,1vw,1.05rem);line-height:1.6;
  color:color-mix(in srgb,var(--c-ink) 55%,transparent);margin:0;max-width:32rem}

/* ============================================================
   SCROLL TEXT FILL — words ignite as the reader passes through
   ============================================================ */
[data-fill]{font-family:var(--f-display);font-weight:var(--fw-display);
  letter-spacing:var(--tr-display);font-size:clamp(1.7rem,3.6vw,3.2rem);line-height:1.22;
  text-wrap:balance;max-width:26ch}
[data-fill] .w{color:color-mix(in srgb,var(--c-ink) 14%,transparent);
  transition:color 240ms linear}
[data-fill] .w.on{color:var(--c-ink);
  text-shadow:0 1px 5px color-mix(in srgb,var(--c-dominant) 70%,transparent),
    0 3px 18px color-mix(in srgb,var(--c-dominant) 88%,transparent)}
[data-fill] .w.acc{transition-delay:40ms}
[data-fill] .w.acc.on{color:var(--c-accent)}

/* ============================================================
   TYPOGRAPHY REFINEMENT
   ============================================================ */
.display{line-height:.88}
/* 12vw is the design intent for a SHORT headline and is kept exactly. The middle term is now
   a variable so a long headline can ask for a smaller coefficient: `.hero` is
   min-height:100svh with align-items:end and nothing capped the headline, so a ~36-character
   headline at 172.8px (12vw @1440) grew the hero to 1150px and pushed the CTA and proof strip
   below the fold — measured on the SHIPPED dentist site — and 51 characters grew it to 1454px
   and clipped the headline itself (mesnil-studio). Only `nebula` and `orbit` override this
   rule, so the other eight tracks were all exposed.
   The renderer computes the coefficient from the character count (heroFit() in
   archetypes.js) and emits it inline ONLY above the threshold, so every short-headline hero
   stays byte-identical and the track overrides below still win on cascade order. */
.h-hero{font-size:clamp(3.5rem,var(--s-hero-fit,12vw),12rem)}
[data-char].acc{color:var(--c-accent);font-style:italic}
.eyebrow{display:inline-flex;align-items:center;gap:.7rem}
.eyebrow::before{content:"";width:1.8rem;height:1px;background:var(--c-accent)}
.lede{line-height:1.62}

/* cursor label: "Play" / "View" swelling over media */
.cur{display:grid;place-items:center;font-family:var(--f-utility);font-size:0;
  letter-spacing:.1em;text-transform:uppercase;color:var(--c-dominant)}
.cur.label{width:74px;height:74px;font-size:.62rem;background:var(--c-accent);
  color:var(--c-on-accent,var(--c-dominant));mix-blend-mode:normal}

/* hover: media breathes, captions arm */
.gal__fig img,.sc__med img,.split__med img{transition:transform 900ms var(--ease),filter 600ms var(--ease)}
.gal__fig:hover img,.sc__med:hover img{transform:scale(1.05);filter:saturate(1.35)}
.gal__cap{transition:color 400ms var(--ease)}
.gal__fig:hover .gal__cap{color:var(--c-accent)}
.spec-line{transition:padding-left 450ms var(--ease),border-color 450ms var(--ease)}
.spec-line:hover{padding-left:.9rem;border-bottom-color:var(--c-accent)}
.tst__card{transition:background 500ms var(--ease)}
.tst__card:hover{background:color-mix(in srgb,var(--c-field) 70%,var(--c-dominant))}


/* ============================================================
   TRACK 4 — KINETIC POP: thick type moving on the x-axis + a face
   ============================================================ */
body[data-track="kinetic_pop"]{--tr-display:-0.02em}
body[data-track="kinetic_pop"] .display{font-weight:900;text-transform:uppercase}
body[data-track="kinetic_pop"] .nav__cta{background:var(--c-ink);color:var(--c-dominant);
  border-color:var(--c-ink)}
body[data-track="kinetic_pop"] .tex{display:none}
body[data-track="kinetic_pop"] ::selection{background:var(--c-ink);color:var(--c-accent)}

.xrow{white-space:nowrap;will-change:transform;font-family:var(--f-display);font-weight:900;
  text-transform:uppercase;line-height:.98;font-size:clamp(3.4rem,11vw,10.5rem);
  letter-spacing:-0.02em;padding-block:.4rem}
.xrow--ghost{color:transparent;-webkit-text-stroke:2px var(--c-ink)}
.xrow--accent{color:var(--c-accent);-webkit-text-stroke:0}
.xrow em{font-style:normal;display:inline-block;margin-inline:2.2vw;color:var(--c-accent);
  -webkit-text-stroke:0;transform:translateY(.06em)}
.xsec{overflow:hidden;padding-block:clamp(4rem,9vh,8rem)}

.hero--pop{align-items:center;background:var(--c-dominant)}
.hero--pop .hero__inner{max-width:none;justify-items:start;gap:.4rem}
.hero--pop .pop__line{font-size:clamp(3.6rem,13vw,13rem);line-height:.92;font-weight:900;
  text-transform:uppercase}
.hero--pop .pop__line--ghost{color:transparent;-webkit-text-stroke:2.5px var(--c-ink)}

/* the face: eyes track the cursor, mouth reacts to scroll velocity */
.face{position:absolute;right:clamp(1rem,6vw,7rem);top:50%;translate:0 -50%;
  width:clamp(11rem,26vw,22rem);aspect-ratio:1;z-index:1;will-change:transform}
.face svg{width:100%;height:100%;overflow:visible}
.face__head{fill:var(--c-accent);stroke:var(--c-ink);stroke-width:7}
.face__eye{fill:var(--c-ink)}
.face__pupil{fill:var(--c-dominant)}
.face__mouth{fill:var(--c-ink);transform-origin:100px 132px;
  transition:transform 160ms var(--ease)}
.face__brow{stroke:var(--c-ink);stroke-width:7;stroke-linecap:round;fill:none}
.face--mini{position:fixed;left:clamp(1rem,3vw,2.2rem);bottom:clamp(1rem,3vw,2.2rem);
  width:4.4rem;top:auto;right:auto;translate:0 0;z-index:70}

.pop-card{border:3px solid var(--c-ink);border-radius:22px;background:var(--c-dominant);
  box-shadow:8px 8px 0 var(--c-ink);padding:clamp(1.6rem,3vw,2.8rem);display:grid;gap:1rem;
  transition:transform 350ms var(--ease),box-shadow 350ms var(--ease)}
.pop-card:hover{transform:translate(-4px,-4px);box-shadow:14px 14px 0 var(--c-ink)}
.pop-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));
  gap:clamp(1.2rem,2.6vw,2.2rem);margin-top:2.6rem}
.pop-tag{display:inline-block;padding:.45rem 1rem;border:2.5px solid var(--c-ink);
  border-radius:999px;font-family:var(--f-utility);font-weight:700;font-size:.72rem;
  letter-spacing:.1em;text-transform:uppercase;background:var(--c-accent)}
body[data-track="kinetic_pop"] .opt__btn,body[data-track="kinetic_pop"] .bot__fab{
  border:3px solid var(--c-ink);box-shadow:6px 6px 0 var(--c-ink);border-radius:14px}
body[data-track="kinetic_pop"] .foot,body[data-track="kinetic_pop"] .tst__card{border-color:var(--c-ink)}

@media(max-width:900px){.face{position:relative;right:auto;top:auto;translate:0 0;
  width:9rem;margin-top:1.4rem}.stack--fs .stack__card{min-height:64svh}}
@media(prefers-reduced-motion:reduce){.xrow{transform:none!important}}


/* ---------- ORBIT HERO: object centre stage, text beneath it ---------- */
body[data-track="orbit"] .hero{align-items:end;text-align:center}
body[data-track="orbit"] .hero__inner{max-width:min(56rem,92%);justify-items:center;
  margin-inline:auto;padding-bottom:4vh;gap:1.1rem}
body[data-track="orbit"] .hero .h-hero{font-size:clamp(2.6rem,6.6vw,5.6rem);line-height:1.02}
body[data-track="orbit"] .hero .eyebrow{opacity:.72}
body[data-track="orbit"] .hero .eyebrow::before{display:none}
body[data-track="orbit"] .hero__proof{justify-content:center}
body[data-track="orbit"] .hero__cta{border:1px solid color-mix(in srgb,var(--c-ink) 26%,transparent);
  border-radius:999px;padding:.95rem 1.9rem}
body[data-track="orbit"] .hero__cta::after{display:none}
body[data-track="orbit"] .hero__cta:hover{border-color:var(--c-accent)}

/* ---------- ZIG-ZAG CARDS: half screen, tilted, image + text alternating ---------- */
.stack--zz{display:block;padding-bottom:10vh}
.stack--zz .stack__card{position:sticky;min-height:58svh;overflow:hidden;
  display:grid;grid-template-columns:1.05fr 1fr;gap:0;padding:0;
  border-radius:24px;border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  box-shadow:0 -26px 70px -28px rgba(0,0,0,.7);
  transform-origin:50% 100%;will-change:transform}
.stack--zz .stack__card:nth-child(odd){transform:rotate(-2.6deg)}
.stack--zz .stack__card:nth-child(even){transform:rotate(2.2deg)}
.stack--zz .stack__card:nth-child(1){top:calc(11vh + 0rem)}
.stack--zz .stack__card:nth-child(2){top:calc(11vh + 2.8rem)}
.stack--zz .stack__card:nth-child(3){top:calc(11vh + 5.6rem)}
.stack--zz .stack__card:nth-child(4){top:calc(11vh + 8.4rem)}
.stack--zz .stack__card:nth-child(5){top:calc(11vh + 11.2rem)}
.stack--zz .stack__card:nth-child(6){top:calc(11vh + 14rem)}
.stack--zz .stack__card:nth-child(7){top:calc(11vh + 16.8rem)}
.stack--zz .stack__card:nth-child(8){top:calc(11vh + 19.6rem)}
.stack--zz .stack__card:nth-child(even) .zz__med{order:2}
.stack--zz .zz__med{position:relative;min-height:100%}
.stack--zz .zz__med img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.18))}
.stack--zz .zz__txt{display:grid;align-content:center;gap:1rem;
  padding:clamp(1.8rem,3.6vw,3.4rem)}
.stack--zz .zz__txt .h-2{font-size:clamp(1.7rem,3.4vw,2.9rem)}
/* no wired asset -> single-column text card (empty-pane decree 2026-08-06:
   a blank .zz__med pane reads as a broken build, never a design choice) */
.stack--zz .stack__card--noimg{grid-template-columns:1fr}
body[data-track="orbit"] .stack--zz .stack__card{
  background:color-mix(in srgb,var(--c-field) 90%,transparent);backdrop-filter:blur(12px)}
body[data-track="orbit"] .stack--zz .stack__card:nth-child(even){
  background:color-mix(in srgb,var(--c-dominant) 88%,transparent)}
@media(max-width:900px){
  .stack--zz .stack__card{grid-template-columns:1fr;min-height:0}
  .stack--zz .stack__card:nth-child(odd),.stack--zz .stack__card:nth-child(even){transform:none}
  .stack--zz .zz__med{min-height:38svh;order:0!important}
}
@media(prefers-reduced-motion:reduce){
  .stack--zz .stack__card{transform:none!important;position:static}
}


/* ============================================================
   STYLE 2 — LENS: one video, continuously transforming.
   The video element is fixed; JS drives it between docked states.
   ============================================================ */
.lens__media{position:fixed;inset:0;z-index:-2;overflow:hidden;
  will-change:transform,width,height,top,left,border-radius;
  box-shadow:0 50px 120px -40px rgba(0,0,0,.8);
  transition:opacity 420ms var(--ease)}
.lens__media video,.lens__media img{width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.2)) contrast(1.05)}
.lens__media .lens__scrim{position:absolute;inset:0;
  background:linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 76%,transparent),transparent 46%);
  transition:opacity 500ms var(--ease)}
.lens__media.docked{z-index:5}
.lens__media.docked .lens__scrim{opacity:0}
.lens__tag{position:absolute;left:1.1rem;bottom:.9rem;font-family:var(--f-utility);
  font-size:.62rem;letter-spacing:.2em;text-transform:uppercase;color:#fff;opacity:0;
  transition:opacity 400ms var(--ease)}
.lens__media.docked .lens__tag{opacity:.85}
/* the lens nav floats over raw footage from the first frame — always glassed */
body[data-track="lens"] .nav{background:color-mix(in srgb,var(--c-dominant) 55%,transparent);
  backdrop-filter:blur(12px) saturate(1.15)}
body[data-track="lens"] section[data-lens="dock-r"] .wrap{padding-right:clamp(0px,42vw,46vw)}
body[data-track="lens"] section[data-lens="dock-l"] .wrap{padding-left:clamp(0px,42vw,46vw)}
body[data-track="lens"] .lens-full{min-height:110svh;display:grid;place-items:end center;
  padding-bottom:8vh;text-align:center}
/* lens copy scrolls over a full-bleed fixed video — every glyph carries its own halo */
body[data-track="lens"] main section{
  text-shadow:0 1px 5px color-mix(in srgb,var(--c-dominant) 70%,transparent),
    0 3px 18px color-mix(in srgb,var(--c-dominant) 88%,transparent)}
@media(max-width:900px){
  body[data-track="lens"] section[data-lens] .wrap{padding-left:0!important;padding-right:0!important}
  .lens__media.docked{opacity:.28}
}

/* ============================================================
   STYLE 3 — SPECIMEN: the product rotating in a sticky bay,
   scrubbed by the whole page's scroll, with a degree readout.
   ============================================================ */
.spec-wrap{display:grid;grid-template-columns:1fr 1fr;align-items:start}
.spec-flow{min-width:0}
.spec-flow section{padding-inline:clamp(1.2rem,3vw,3rem)}
.spec-flow .wrap{width:100%;margin:0}
.spec-bay{position:sticky;top:0;height:100svh;overflow:hidden;
  border-left:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);
  display:grid;place-items:center;isolation:isolate}
.spec-bay video,.spec-bay img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.2))}
.spec-bay::after{content:"";position:absolute;inset:0;
  background:radial-gradient(80% 80% at 50% 50%,transparent 55%,color-mix(in srgb,var(--c-dominant) 70%,transparent))}
.spec-deg{position:absolute;bottom:2rem;right:2rem;z-index:2;text-align:right;
  /* the readout floats over arbitrary footage — carry a backing chip so the degree
     number survives a light frame (measured 1.05:1 on a pale placeholder) */
  padding:.7rem 1rem;border-radius:var(--radius);
  background:color-mix(in srgb,var(--c-dominant) 62%,transparent);backdrop-filter:blur(8px)}
.spec-deg b{display:block;font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(2.6rem,5vw,4.6rem);line-height:1;font-variant-numeric:tabular-nums;color:var(--c-accent);
  text-shadow:0 2px 10px color-mix(in srgb,var(--c-dominant) 75%,transparent),
    0 4px 26px color-mix(in srgb,var(--c-dominant) 90%,transparent)}
.spec-deg span{font-family:var(--f-utility);font-size:.64rem;letter-spacing:.22em;
  text-transform:uppercase;opacity:.75;
  text-shadow:0 1px 4px color-mix(in srgb,var(--c-dominant) 70%,transparent)}
.spec-ring{position:absolute;top:2rem;left:2rem;z-index:2;width:64px;height:64px}
.spec-ring circle{fill:none;stroke:color-mix(in srgb,var(--c-ink) 20%,transparent);stroke-width:2}
.spec-ring .on{stroke:var(--c-accent);stroke-dasharray:176;stroke-dashoffset:176;
  transform:rotate(-90deg);transform-origin:center}
@media(max-width:900px){.spec-wrap{grid-template-columns:1fr}
  .spec-bay{position:relative;height:52svh;order:-1;border-left:0;
    border-bottom:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent)}}

/* ============================================================
   STYLE 4 — RIBBON: the site travels sideways like a tracking shot
   ============================================================ */
.rib .hpin__panel{flex:0 0 min(74vw,860px);min-height:74svh;position:relative;
  border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent);border-radius:20px;
  overflow:hidden;padding:clamp(1.8rem,3.4vw,3.2rem);isolation:isolate;
  display:grid;align-content:end;gap:1rem}
.rib .hpin__panel img{position:absolute;inset:0;z-index:-2;width:100%;height:100%;object-fit:cover;
  filter:saturate(var(--media-chroma,1.2))}
.rib .hpin__panel::after{content:"";position:absolute;inset:0;z-index:-1;
  background:linear-gradient(to top,color-mix(in srgb,var(--c-dominant) 84%,transparent),transparent 58%)}
.rib .hpin__panel .rib__n{position:absolute;top:1.4rem;right:1.8rem;
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:clamp(3rem,6vw,5.5rem);line-height:1;color:transparent;
  -webkit-text-stroke:1.5px color-mix(in srgb,var(--c-ink) 45%,transparent)}
.rib__ticker{position:absolute;left:0;right:0;bottom:1.6rem;z-index:3;
  display:flex;align-items:center;gap:1rem;padding-inline:clamp(1.2rem,4vw,4rem);
  font-family:var(--f-utility);font-size:.66rem;letter-spacing:.2em;text-transform:uppercase;opacity:.7}
.rib__bar{flex:1;height:1px;background:color-mix(in srgb,var(--c-ink) 20%,transparent);position:relative}
.rib__bar i{position:absolute;inset:0;transform-origin:left;transform:scaleX(0);background:var(--c-accent)}
@media(max-width:900px){.rib .hpin__panel{flex:1 1 auto;min-height:60svh}.rib__ticker{display:none}}


/* ============================================================
   STYLE — PERSONA (motionsites Jack grammar):
   gradient monument headline, magnetic mascot, scroll marquees,
   a white rounded act, bordered sticky project cards
   ============================================================ */
.grad-text{background:linear-gradient(180deg,var(--c-texture) 0%,var(--c-ink) 100%);
  -webkit-background-clip:text;background-clip:text;-webkit-text-fill-color:transparent;color:transparent}
.btn-nova{display:inline-block;padding:1rem 2.6rem;border-radius:999px;color:var(--c-on-accent,#fff);
  font-family:var(--f-utility);font-size:.78rem;letter-spacing:.18em;text-transform:uppercase;
  background:linear-gradient(123deg,color-mix(in srgb,var(--c-accent) 30%,#000) 7%,var(--c-accent) 37%,var(--c-glow2) 72%,var(--c-glow3) 100%);
  box-shadow:0 4px 4px color-mix(in srgb,var(--c-accent) 25%,transparent),4px 4px 12px color-mix(in srgb,var(--c-glow2) 80%,#000) inset;
  outline:2px solid var(--c-ink);outline-offset:-3px;
  transition:transform 350ms var(--ease),box-shadow 350ms var(--ease)}
.btn-nova:hover{transform:translateY(-2px)}

.hero--persona{align-items:stretch;grid-template-rows:auto 1fr auto;text-align:left;overflow:hidden;
  min-height:100svh}
.persona__shelf{position:relative;display:grid;justify-items:center;padding-bottom:0}
.persona__ground{width:min(46vw,420px);height:22px;border-radius:50%;
  background:radial-gradient(closest-side,color-mix(in srgb,var(--c-ink) 26%,transparent),transparent);
  margin-top:-14px}
.persona__name{font-family:var(--f-display);font-weight:900;text-transform:uppercase;
  letter-spacing:-0.02em;line-height:.86;white-space:nowrap;width:100%;
  font-size:clamp(4rem,16.5vw,17vw);text-align:center}
.persona__foot{display:flex;justify-content:space-between;align-items:flex-end;
  padding-bottom:clamp(1.8rem,5vh,3rem);gap:1rem}
.persona__blurb{max-width:16rem;font-weight:300;text-transform:uppercase;
  letter-spacing:.06em;line-height:1.4;font-size:clamp(.75rem,1.4vw,1.2rem);opacity:.85}
.persona__char{position:relative;width:auto;height:clamp(200px,34svh,360px);
  will-change:transform;filter:drop-shadow(0 24px 44px color-mix(in srgb,var(--c-ink) 35%,transparent))}
.persona__char svg{height:100%;width:auto}
.persona__name{position:relative;z-index:2;padding-top:.6rem;will-change:transform,opacity}
.persona__char svg{width:100%;height:auto;overflow:visible}
.persona__char .float{animation:mascotFloat 5.2s ease-in-out infinite}
@keyframes mascotFloat{50%{transform:translateY(-14px) rotate(1.4deg)}}

/* scroll-driven double marquee */
.smarq{overflow:hidden;display:grid;gap:.8rem;padding-block:clamp(4rem,9vh,8rem) 2rem}
.smarq__row{display:flex;gap:.8rem;will-change:transform;width:max-content}
.smarq__row img{width:min(420px,62vw);aspect-ratio:14/9;object-fit:cover;border-radius:18px;
  filter:saturate(var(--media-chroma,1.2))}

/* about corner decorations parallax on cursor via data-depth */
.pdeco{position:absolute;width:clamp(80px,12vw,190px);opacity:.9;will-change:transform;pointer-events:none}
.pdeco svg{width:100%;height:auto;filter:drop-shadow(0 18px 34px rgba(0,0,0,.5))}

/* light act with rounded shoulders */
.act-light{background:var(--c-ink);color:var(--c-dominant);
  border-radius:44px 44px 0 0;position:relative;z-index:2}
.act-light .eyebrow,.act-light .lede{color:inherit}
.svc{max-width:60rem;margin-inline:auto}
.svc__row{display:grid;grid-template-columns:auto 1fr;gap:clamp(1.4rem,4vw,3.6rem);
  align-items:start;padding-block:clamp(1.8rem,4vh,3rem);
  border-bottom:1px solid color-mix(in srgb,var(--c-dominant) 15%,transparent)}
.svc__n{font-family:var(--f-display);font-weight:900;line-height:.9;
  font-size:clamp(3rem,10vw,8.5rem)}
.svc__row h3{margin:0;font-weight:500;text-transform:uppercase;
  font-size:clamp(1rem,2.2vw,2rem)}
.svc__row p{margin:.6rem 0 0;font-weight:300;line-height:1.6;opacity:.72;max-width:42rem;
  font-size:clamp(.85rem,1.6vw,1.2rem)}

/* dark act pulled over the white one */
.act-dark{background:var(--c-dominant);border-radius:44px 44px 0 0;position:relative;z-index:3;
  margin-top:-2.6rem}

/* bordered sticky project cards */
.proj .stack__card{border:2px solid var(--c-ink);border-radius:44px;background:var(--c-dominant);
  box-shadow:none;grid-template-columns:1fr;align-content:start;gap:1.4rem;min-height:78svh}
.proj__top{display:flex;flex-wrap:wrap;align-items:center;gap:1rem 2rem}
.proj__cat{font-family:var(--f-utility);font-size:.68rem;letter-spacing:.2em;
  text-transform:uppercase;opacity:.72}
.proj__ghostbtn{margin-left:auto;padding:.8rem 2rem;border:2px solid var(--c-ink);
  border-radius:999px;font-family:var(--f-utility);font-size:.72rem;letter-spacing:.18em;
  text-transform:uppercase;transition:background 300ms var(--ease)}
.proj__ghostbtn:hover{background:color-mix(in srgb,var(--c-ink) 10%,transparent)}
.proj__imgs{display:grid;grid-template-columns:2fr 3fr;gap:1rem}
.proj__col{display:grid;gap:1rem}
.proj__imgs img{width:100%;height:100%;object-fit:cover;border-radius:34px;
  filter:saturate(var(--media-chroma,1.2))}
.proj__imgs .tall{min-height:clamp(260px,40vh,460px)}
@media(max-width:900px){
  .persona__foot{flex-direction:column;align-items:flex-start}
  .proj__imgs{grid-template-columns:1fr}
  .svc__row{grid-template-columns:1fr}
}
@media(prefers-reduced-motion:reduce){
  .persona__char .float{animation:none}
  .smarq__row{transform:none!important}
}


/* persona continuous-motion layer */
.svc__row{transition:transform 450ms var(--ease),border-color 450ms var(--ease)}
.svc__row:hover{transform:translateX(.8rem)}
.svc__n{color:transparent;-webkit-text-stroke:2px currentColor;
  transition:color 700ms var(--ease),-webkit-text-stroke-color 700ms var(--ease)}
.svc__row.on .svc__n{color:currentColor;-webkit-text-stroke:2px transparent}
.smarq__row{transition:filter 400ms var(--ease)}
.smarq__row img{transition:transform 700ms var(--ease)}
.smarq__row img:hover{transform:scale(1.05) rotate(-1deg)}
.proj .stack__card{transform-style:preserve-3d}
.pdeco{transition:none}


/* persona light stage: sticker marquee + light-mode polish */
body[data-track="persona"] .smarq__row img{border:3px solid var(--c-ink);border-radius:20px;
  box-shadow:7px 7px 0 color-mix(in srgb,var(--c-ink) 85%,transparent);background:#fff}
body[data-track="persona"] .smarq__row img:hover{transform:scale(1.04) rotate(1deg);
  box-shadow:11px 11px 0 var(--c-accent)}
body[data-track="persona"] .nav__cta{border-color:var(--c-ink)}
body[data-track="persona"] .cur{background:var(--c-ink)}
body[data-track="persona"] .glow{display:none}
body[data-track="persona"] .orb{opacity:.3}
body[data-track="persona"] .hero__cue{background:linear-gradient(var(--c-ink),transparent)}
body[data-track="persona"] .proj .stack__card{box-shadow:10px 10px 0 color-mix(in srgb,var(--c-ink) 12%,transparent)}
body[data-track="persona"] .opt__btn{background:var(--c-ink);color:var(--c-dominant)}
body[data-track="persona"] .bot__fab{background:var(--c-ink);color:var(--c-dominant);
  box-shadow:6px 6px 0 color-mix(in srgb,var(--c-accent) 60%,transparent)}


/* ---------- animated icon library ---------- */
.aicon{display:inline-grid;place-items:center;width:1.15em;height:1.15em;
  vertical-align:-0.18em;color:var(--c-accent)}
.aicon svg{width:100%;height:100%;overflow:visible}
.ai-spin{transform-origin:12px 12px;animation:aiSpin 6s linear infinite}
@keyframes aiSpin{to{transform:rotate(360deg)}}
.ai-nudge{animation:aiNudge 1.6s var(--ease) infinite}
@keyframes aiNudge{0%,100%{transform:translateX(0)}50%{transform:translateX(3px)}}
.ai-twinkle{transform-origin:12px 12px;animation:aiTwinkle 2.4s ease-in-out infinite}
@keyframes aiTwinkle{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(.78);opacity:.55}}
.ai-pulse{animation:aiPulse 2.6s ease-in-out infinite}
@keyframes aiPulse{50%{opacity:.5}}
.ai-draw{stroke-dasharray:16;animation:aiDraw 2.6s var(--ease) infinite}
@keyframes aiDraw{0%{stroke-dashoffset:16}40%,70%{stroke-dashoffset:0}100%{stroke-dashoffset:-16}}
.ai-tick{animation:aiTick 4s steps(8) infinite}
@keyframes aiTick{to{transform:rotate(360deg)}}
.ai-beat{animation:aiBeat 1.4s ease-in-out infinite}
@keyframes aiBeat{0%,100%{transform:scale(1)}12%{transform:scale(1.16)}24%{transform:scale(1)}36%{transform:scale(1.1)}}
@media(prefers-reduced-motion:reduce){.aicon *{animation:none!important}}

/* ---------- persona pure-white pop layer ---------- */
body[data-track="persona"]{--tr-display:-0.03em}
.pblob{position:absolute;z-index:-1;pointer-events:none;border-radius:50%;
  filter:blur(70px);opacity:.5;will-change:transform;
  animation:pblobFloat 14s ease-in-out infinite alternate}
.pblob--a{width:44vw;height:44vw;background:color-mix(in srgb,var(--c-accent) 40%,transparent)}
.pblob--b{width:36vw;height:36vw;background:color-mix(in srgb,var(--c-glow2) 40%,transparent);animation-delay:-5s}
.pblob--c{width:30vw;height:30vw;background:color-mix(in srgb,var(--c-glow3) 36%,transparent);animation-delay:-9s}
@keyframes pblobFloat{to{transform:translate(4vw,-3vh) scale(1.12)}}
body[data-track="persona"] .svc__row h3 .aicon{width:1.35em;height:1.35em}


/* ============================================================
   THE RING — cards on a 3D carousel, spun by scroll
   ============================================================ */
.ringsec{height:300vh;position:relative;padding-block:0!important}
.ringsec__vp{position:sticky;top:0;height:100svh;overflow:hidden;display:grid;
  grid-template-rows:auto 1fr auto;perspective:1400px}
.ring{position:relative;display:grid;place-items:center;transform-style:preserve-3d}
.ring__stage{position:relative;transform-style:preserve-3d;will-change:transform;
  width:min(340px,74vw);height:min(300px,52svh)}
.ring__card{position:absolute;inset:0;margin:0;display:grid;align-content:center;gap:.9rem;
  padding:clamp(1.4rem,2.4vw,2.2rem);border-radius:22px;
  background:var(--c-field);border:2px solid var(--c-ink);
  box-shadow:8px 8px 0 color-mix(in srgb,var(--c-ink) 14%,transparent);
  transform:rotateY(calc(var(--i) * (360deg / var(--n)))) translateZ(var(--ring-r,340px));
  backface-visibility:hidden;transition:opacity 300ms var(--ease)}
.ring__card .tst__who b{color:var(--c-accent)}
.ring__hint{justify-self:center;padding-bottom:2rem;margin:0;
  font-family:var(--f-utility);font-size:.7rem;letter-spacing:.2em;text-transform:uppercase;opacity:.7}
@media(min-width:901px){.ring__stage{--ring-r:400px}}
@media(max-width:900px){.ringsec{height:240vh}.ring__stage{--ring-r:230px}}
@media(prefers-reduced-motion:reduce){
  .ringsec{height:auto}.ringsec__vp{position:static;height:auto}
  .ring__stage{width:auto;height:auto;display:grid;gap:1rem;transform:none!important}
  .ring__card{position:static;transform:none}}
/* the white card must carry its own ink: with a dark persona palette, var(--c-ink) is
   near-white and text vanished at 1.06:1 before --c-on-light existed */
body[data-track="persona"] .ring__card{background:#fff;color:var(--c-on-light,#0d0d10);
  border-color:var(--c-on-light,#0d0d10)}
.ring__card--rec{align-content:start;gap:.8rem;padding:clamp(1rem,1.8vw,1.4rem)}
.ring__card--stat{align-content:center;justify-items:center;text-align:center;gap:.7rem}
.ring__card--svc{align-content:center;gap:.8rem}
.ring__card--svc .svc__n{color:transparent;-webkit-text-stroke:2px currentColor}
.ringsec.act-light{border-radius:44px 44px 0 0}
.ringsec.act-light .ring__card{background:var(--c-ink);color:var(--c-dominant);
  border-color:var(--c-dominant);box-shadow:8px 8px 0 color-mix(in srgb,var(--c-dominant) 18%,transparent)}
body[data-track="persona"] .ringsec.act-light .ring__card{background:#fff;color:var(--c-on-light,#0d0d10);
  border-color:var(--c-on-light,#0d0d10);box-shadow:8px 8px 0 color-mix(in srgb,var(--c-on-light,#0d0d10) 16%,transparent)}
.ring__card--stat .aicon{width:2rem;height:2rem}
.ring__card--stat .cnt__v{font-family:var(--f-display);font-weight:var(--fw-display);
  color:var(--c-accent);font-variant-numeric:tabular-nums}
.ring__img{width:100%;height:44%;object-fit:cover;border-radius:14px;
  border:2px solid var(--c-ink);filter:saturate(var(--media-chroma,1.2))}
.ringsec.act-dark .ring__card{background:var(--c-field);border-color:var(--c-ink)}
.ringsec.act-dark{border-radius:44px 44px 0 0}


/* ---------- ORBIT: soothing gradient stage behind the object ---------- */
body[data-track="orbit"]{
  background:
    radial-gradient(120% 80% at 50% 8%, color-mix(in srgb,var(--c-glow2) 13%,transparent) 0%, transparent 58%),
    radial-gradient(90% 60% at 78% 92%, color-mix(in srgb,var(--c-accent) 9%,transparent) 0%, transparent 62%),
    linear-gradient(178deg, #070b12 0%, #05070c 46%, #04060a 100%);
  background-attachment:fixed}
body[data-track="orbit"] .hero::after{
  content:"";position:absolute;inset:auto 0 0 0;height:34vh;pointer-events:none;z-index:1;
  background:linear-gradient(to top, var(--c-dominant) 4%, color-mix(in srgb,var(--c-dominant) 55%,transparent) 46%, transparent 100%)}
body[data-track="orbit"] .hero__inner{position:relative;z-index:2}
body[data-track="orbit"] .gl{opacity:.92}


/* ---------- PORTRAIT MODE: beautify any transparent cut-out ---------- */
/* fixed stage box: any uploaded cut-out is fitted into it, never stretches the layout */
.persona__char--photo{height:clamp(240px,44svh,460px);width:clamp(190px,26vw,380px)}
.portrait{position:relative;margin:0;height:100%;width:100%;display:block;isolation:isolate}
.portrait .portrait__img{position:absolute;z-index:3;inset:0;height:100%;width:100%;max-width:none;
  object-fit:contain;object-position:bottom center;
  /* sticker cut-line traced from the upload's own alpha, then a hard offset shadow.
     A glow would be invisible on a light stage; a crisp edge is not. */
  filter:
    drop-shadow(3px 0 0 var(--c-ink)) drop-shadow(-3px 0 0 var(--c-ink))
    drop-shadow(0 3px 0 var(--c-ink)) drop-shadow(0 -3px 0 var(--c-ink))
    drop-shadow(2px 2px 0 var(--c-ink)) drop-shadow(-2px 2px 0 var(--c-ink))
    drop-shadow(14px 16px 0 color-mix(in srgb,var(--c-accent) 26%,transparent))
    contrast(1.05) saturate(1.1);
  will-change:transform}
/* colour-graded ghost sits behind the subject and bleeds brand colour into the edges */
.portrait .portrait__ghost{position:absolute;z-index:2;inset:0;height:100%;width:100%;max-width:none;
  object-fit:contain;object-position:bottom center;filter:blur(3px) brightness(0) opacity(.18);
  translate:10px 12px;pointer-events:none}
/* stage glow behind the subject */
.portrait__stage{position:absolute;z-index:1;bottom:6%;left:50%;translate:-50% 0;
  width:108%;aspect-ratio:1;border-radius:50%;pointer-events:none;bottom:14%;
  background:
    radial-gradient(closest-side, color-mix(in srgb,var(--c-glow2) 18%,transparent) 0%, transparent 74%),
    conic-gradient(from 210deg,
      color-mix(in srgb,var(--c-accent) 88%,transparent),
      color-mix(in srgb,var(--c-glow2) 88%,transparent),
      color-mix(in srgb,var(--c-glow3) 82%,transparent),
      color-mix(in srgb,var(--c-accent) 88%,transparent));
  opacity:.9;animation:stageSpin 26s linear infinite}
@keyframes stageSpin{to{transform:translateX(-50%) rotate(360deg)}}
.portrait__stage{transform:translateX(-50%)}

/* hard kicker light from upper left, clipped to the frame */
.portrait__kicker{position:absolute;z-index:1;inset:0;pointer-events:none;
  background:radial-gradient(34% 28% at 27% 13%,
    color-mix(in srgb,#fff 34%,transparent) 0%, transparent 68%);
  filter:blur(16px);opacity:.4;z-index:4}
/* specular sheen sliding across the subject */
.portrait__sheen{position:absolute;z-index:4;inset:0;pointer-events:none;mix-blend-mode:overlay;
  background:linear-gradient(112deg,transparent 38%,
    color-mix(in srgb,#fff 30%,transparent) 49%,transparent 60%);
  background-size:260% 100%;background-position:120% 0;
  animation:sheenSweep 6.5s ease-in-out infinite}
@keyframes sheenSweep{0%,58%{background-position:130% 0}100%{background-position:-40% 0}}
/* contact shadow so the subject sits on the shelf instead of floating */
.portrait__ground{position:absolute;z-index:0;bottom:-2%;left:50%;translate:-50% 0;
  width:74%;height:26px;border-radius:50%;
  background:radial-gradient(closest-side,
    color-mix(in srgb,var(--c-ink) 46%,transparent),transparent 78%);
  filter:blur(7px)}
/* light stage: sticker cut-line, not a glow */
body[data-track="persona"] .portrait .portrait__img{
  filter:
    drop-shadow(3px 0 0 var(--c-ink)) drop-shadow(-3px 0 0 var(--c-ink))
    drop-shadow(0 3px 0 var(--c-ink)) drop-shadow(0 -3px 0 var(--c-ink))
    drop-shadow(2px 2px 0 var(--c-ink)) drop-shadow(-2px 2px 0 var(--c-ink))
    drop-shadow(16px 18px 0 color-mix(in srgb,var(--c-accent) 30%,transparent))
    contrast(1.05) saturate(1.1)}
@media(prefers-reduced-motion:reduce){
  .portrait__stage,.portrait__sheen{animation:none}
}
@media(max-width:900px){.persona__char--photo{height:clamp(200px,34svh,320px)}}


/* ============================================================
   TRACK — APEX: near-black telemetry stage. The page has a
   SPEED STATE driven by scroll velocity: gauges climb, type
   splits into chromatic ghosts, streaks appear. Stop and it cools.
   ============================================================ */
body[data-track="apex"]{--speed:0;--redline:0}
body[data-track="apex"] .tex{opacity:.5}
body[data-track="apex"] section{position:relative}

/* HUD chrome: corner brackets + tick ruler down the left edge */
.apex-hud{position:fixed;inset:14px;pointer-events:none;z-index:60;
  border:1px solid color-mix(in srgb,var(--c-ink) 12%,transparent)}
.apex-hud i{position:absolute;width:16px;height:16px;border:2px solid var(--c-accent);opacity:.85}
.apex-hud i:nth-child(1){top:-1px;left:-1px;border-right:0;border-bottom:0}
.apex-hud i:nth-child(2){top:-1px;right:-1px;border-left:0;border-bottom:0}
.apex-hud i:nth-child(3){bottom:-1px;left:-1px;border-right:0;border-top:0}
.apex-hud i:nth-child(4){bottom:-1px;right:-1px;border-left:0;border-top:0}
.apex-rule{position:fixed;left:20px;top:0;bottom:0;width:26px;z-index:60;pointer-events:none;
  background:repeating-linear-gradient(to bottom,
    color-mix(in srgb,var(--c-ink) 26%,transparent) 0 1px,transparent 1px 22px);
  opacity:.5}
.apex-rule::after{content:"";position:absolute;left:0;width:26px;height:2px;background:var(--c-accent);
  top:calc(var(--scrollpct,0) * 1%);box-shadow:0 0 14px var(--c-accent)}

/* speed gauge, bottom right */
.apex-gauge{position:fixed;right:26px;bottom:26px;z-index:61;width:104px;height:104px;pointer-events:none;
  border-radius:50%;backdrop-filter:blur(6px);
  background:radial-gradient(closest-side,color-mix(in srgb,var(--c-dominant) 72%,transparent),transparent 78%)}
.apex-gauge svg{width:100%;height:100%;overflow:visible}
.apex-gauge .track{fill:none;stroke:color-mix(in srgb,var(--c-ink) 18%,transparent);stroke-width:3}
.apex-gauge .fill{fill:none;stroke:var(--c-accent);stroke-width:3;stroke-linecap:round;
  stroke-dasharray:226;stroke-dashoffset:calc(226 - 226 * var(--speed));
  filter:drop-shadow(0 0 8px var(--c-accent));transition:stroke-dashoffset 120ms linear}
.apex-gauge .rl{fill:none;stroke:var(--c-glow3);stroke-width:3;opacity:var(--redline)}
.apex-gauge text{fill:var(--c-ink);font-family:var(--f-utility);font-size:15px;
  font-variant-numeric:tabular-nums;text-anchor:middle}
.apex-gauge .unit{fill:color-mix(in srgb,var(--c-ink) 45%,transparent);font-size:8px;letter-spacing:.2em}

/* nameplate hero */
.hero--apex{align-content:center;overflow:hidden}
.hero--apex .hero__inner{
  display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(1.5rem,4vw,3.4rem);align-items:center;
  padding-top:clamp(5.5rem,11vh,7.5rem);   /* clears the fixed nav */
  padding-bottom:clamp(2rem,6vh,4rem);max-width:min(96rem,94vw)}
.apex-col{min-width:0;display:grid;align-content:center;gap:.2rem}
.apex-stage{position:relative;min-width:0;display:grid;place-items:center;
  height:clamp(300px,58svh,540px)}
@media(max-width:900px){
  .hero--apex .hero__inner{grid-template-columns:1fr;padding-top:6rem}
  .apex-stage{height:clamp(220px,38svh,340px);order:-1}
}
.apex-plate{display:grid;gap:.4rem}
.apex-loc{font-family:var(--f-utility);font-size:clamp(.66rem,1vw,.8rem);letter-spacing:.4em;
  text-transform:uppercase;color:var(--c-accent)}
.apex-name{font-family:var(--f-display);font-weight:var(--fw-display);text-transform:uppercase;
  line-height:.86;letter-spacing:-0.03em;position:relative;max-width:100%;
  overflow-wrap:anywhere;margin:.2rem 0 .6rem}
.apex-name span{display:block}
.apex-name .thin{-webkit-text-stroke:1.5px color-mix(in srgb,var(--c-ink) 80%,transparent);
  color:transparent}
/* chromatic split grows with scroll speed */
.apex-name{text-shadow:
  calc(var(--speed) * -7px) 0 color-mix(in srgb,var(--c-accent) 85%,transparent),
  calc(var(--speed) * 7px) 0 color-mix(in srgb,var(--c-glow2) 70%,transparent)}
.apex-strip{display:flex;flex-wrap:wrap;gap:1.1rem 2.2rem;margin-top:1.4rem;
  font-family:var(--f-utility);font-size:.7rem;letter-spacing:.18em;text-transform:uppercase}
.apex-strip b{display:block;font-family:var(--f-display);font-size:clamp(1.4rem,2.4vw,2.1rem);
  font-weight:var(--fw-display);letter-spacing:-0.02em;color:var(--c-ink);font-variant-numeric:tabular-nums}
.apex-strip span{color:color-mix(in srgb,var(--c-ink) 70%,transparent)}

/* motion streaks that only appear at speed */
.apex-streaks{position:fixed;inset:0;z-index:2;pointer-events:none;opacity:var(--speed);overflow:hidden;
  background:repeating-linear-gradient(105deg,
    transparent 0 42px,
    color-mix(in srgb,var(--c-accent) 22%,transparent) 42px 43px,
    transparent 43px 96px);
  mask-image:linear-gradient(to right,transparent,#000 30%,#000 70%,transparent)}

/* sector rows: services as timed sectors */
.sector{display:grid;grid-template-columns:auto 1fr auto;gap:clamp(1rem,3vw,2.6rem);
  align-items:center;padding:clamp(1.4rem,3.4vh,2.4rem) 0;
  border-top:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent);
  transition:background 420ms var(--ease),padding-left 420ms var(--ease)}
.sector:hover{background:color-mix(in srgb,var(--c-accent) 7%,transparent);padding-left:1.2rem}
.sector__n{font-family:var(--f-utility);font-size:.7rem;letter-spacing:.2em;color:var(--c-accent)}
.sector__t{font-family:var(--f-display);font-weight:var(--fw-display);text-transform:uppercase;
  font-size:clamp(1.3rem,3vw,2.4rem);letter-spacing:-0.02em;margin:0}
.sector__d{margin:.35rem 0 0;opacity:.72;max-width:46ch;line-height:1.55}
.sector__time{font-family:var(--f-utility);font-size:clamp(.9rem,1.6vw,1.2rem);
  font-variant-numeric:tabular-nums;color:var(--c-accent)}

/* split-flap odometer counters */
.flap{display:inline-flex;gap:3px}
/* qualitative stats ("SQL native", "p99 <50ms") render as words, never digit tiles */
.flap.flap--txt{display:inline}
/* line-height lives on the TILE, never on a container. The tile is 1.18em tall with
   overflow:hidden (the drop animation needs the clip); if the ::before digit inherits
   an ambient line-height from whatever section the flap was dropped into — 1.55 body
   copy on the apex strip, for instance — its line box is taller than the tile and the
   glyph is shaved. Scoping the fix to a container (.cnt__v) missed every flap outside
   that container, so it is stated here once and cannot be missed again. */
.flap i{position:relative;display:grid;place-items:center;width:.72em;height:1.18em;
  line-height:1;overflow:hidden;background:color-mix(in srgb,var(--c-ink) 8%,transparent);
  border-radius:4px;font-style:normal}
.flap i::before{content:attr(data-d);display:block;line-height:1}
/* The drop is class-gated: the engine toggles a roll class per digit change (an inline
   style.animation reset on the tile never reaches its ::before digit, and an innerHTML
   rebuild replays the drop on every cell). Two alternating classes carrying two
   IDENTICAL keyframe sets let a restart be a plain class swap — a different
   animation-name always starts fresh, so the engine never needs a forced
   `void offsetWidth` reflow to replay the drop. */
.flap i.roll::before{animation:flapRoll 620ms var(--ease)}
.flap i.rollb::before{animation:flapRollB 620ms var(--ease)}
@keyframes flapRoll{0%{transform:translateY(-100%);opacity:0}60%{opacity:1}100%{transform:translateY(0)}}
@keyframes flapRollB{0%{transform:translateY(-100%);opacity:0}60%{opacity:1}100%{transform:translateY(0)}}
.flap i::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;
  background:color-mix(in srgb,var(--c-dominant) 70%,transparent)}

@media(max-width:900px){
  .apex-rule,.apex-gauge{display:none}
  .sector{grid-template-columns:1fr;gap:.5rem}
}
@media(prefers-reduced-motion:reduce){
  .apex-streaks{display:none}
  .apex-name{text-shadow:none}
  .flap i::before,.flap i.roll::before,.flap i.rollb::before{animation:none}
}


/* ---------- APEX ROSTER: a switchable subject stage ---------- */
.apex-roster{display:flex;gap:.6rem;align-items:center;margin-bottom:1rem;flex-wrap:wrap}
.apex-chip{width:42px;height:42px;border-radius:50%;display:grid;place-items:center;cursor:pointer;
  border:2px solid color-mix(in srgb,var(--c-ink) 22%,transparent);background:transparent;
  font-family:var(--f-utility);font-size:.66rem;color:color-mix(in srgb,var(--c-ink) 60%,transparent);
  transition:border-color 300ms var(--ease),color 300ms var(--ease),transform 300ms var(--ease)}
.apex-chip:hover{transform:translateY(-3px)}
.apex-chip[aria-selected="true"]{border-color:var(--c-accent);color:var(--c-accent);
  box-shadow:0 0 0 4px color-mix(in srgb,var(--c-accent) 14%,transparent)}
.apex-swap{position:relative}
/* the wipe: the stage passes through black exactly like a cut between cameras */
.apex-swap::after{content:"";position:absolute;inset:-4vw -2vw;pointer-events:none;z-index:5;
  background:var(--c-dominant);opacity:0;transition:opacity 210ms linear}
.apex-swap.swapping::after{opacity:1;transition-duration:130ms}
.apex-subject{position:relative;z-index:2;width:100%;height:100%;margin:0;
  display:grid;place-items:end center;
  transition:opacity 320ms var(--ease),transform 620ms var(--ease)}
.apex-subject img{width:100%;height:100%;object-fit:contain;object-position:bottom center;
  filter:saturate(1.08) contrast(1.06)
    drop-shadow(0 26px 40px rgba(0,0,0,.6))
    drop-shadow(-14px 0 26px color-mix(in srgb,var(--c-accent) 30%,transparent))
    drop-shadow(14px 0 26px color-mix(in srgb,var(--c-glow2) 22%,transparent))}
.apex-swap.swapping .apex-subject{opacity:0;transform:scale(1.04)}
.apex-ghostnum{position:absolute;inset:0;z-index:0;display:grid;place-items:center;
  font-family:var(--f-display);font-weight:var(--fw-display);
  font-size:min(20vw,270px);line-height:.8;color:transparent;
  -webkit-text-stroke:1.6px color-mix(in srgb,var(--c-ink) 13%,transparent);pointer-events:none;
  transition:opacity 400ms var(--ease)}
@media(max-width:900px){ .apex-ghostnum{font-size:38vw} }

.nav a,.nav button,.nav__cta,.nav__logo{pointer-events:auto}



html,body{overflow-x:clip;max-width:100%}


/* ---------- NEBULA: the field is the page; sections float over it ---------- */
body[data-gl="particles"] section:not(.scene){background:transparent}
body[data-track="nebula"]{
  background:
    radial-gradient(120% 78% at 50% 4%, color-mix(in srgb,var(--c-glow2) 15%,transparent) 0%, transparent 60%),
    radial-gradient(80% 60% at 84% 96%, color-mix(in srgb,var(--c-accent) 10%,transparent) 0%, transparent 64%),
    linear-gradient(178deg,#05060c 0%,#04050a 52%,#030408 100%);
  background-attachment:fixed}
body[data-track="nebula"] .hero{align-items:end;text-align:center}
body[data-track="nebula"] .hero__inner{max-width:min(54rem,92%);justify-items:center;
  margin-inline:auto;padding-bottom:6vh;gap:1rem}
body[data-track="nebula"] .hero .h-hero{font-size:clamp(2.5rem,6.2vw,5.2rem);line-height:1.03}
body[data-track="nebula"] .hero__cta{border:1px solid color-mix(in srgb,var(--c-ink) 26%,transparent);
  border-radius:999px;padding:.95rem 1.9rem}
body[data-track="nebula"] .hero__cta::after{display:none}
body[data-track="nebula"] .stack--zz .stack__card,
body[data-track="nebula"] .tst__card,
body[data-track="nebula"] .casc__cell{
  background:color-mix(in srgb,var(--c-field) 58%,transparent);
  backdrop-filter:blur(16px) saturate(1.2);
  border:1px solid color-mix(in srgb,var(--c-ink) 14%,transparent)}
body[data-track="nebula"] .casc__cell:hover{
  border-color:color-mix(in srgb,var(--c-accent) 50%,transparent);
  background:color-mix(in srgb,var(--c-field) 70%,transparent)}
body[data-track="nebula"] .stack--zz .stack__card:nth-child(even){
  background:color-mix(in srgb,var(--c-dominant) 58%,transparent)}
body[data-track="nebula"] .gl{opacity:.95}


/* ---------- LIGHT-THEME HOST DERIVATION ----------
   The engine was written dark-first. Two GL-track surfaces are literal near-black
   (the orbit page stage above, the nebula one below) and the hero copy is allowed
   to sit straight on top of the GL object because on a dark pack the object is the
   luminous thing and the ink is bone. Invert the palette and both assumptions break:
   the page paints #05070c over paper, and near-black ink lands on the object's dark
   mass (measured 1.21:1 on the first whitespace-run render).
   Everything here re-derives those surfaces to the HOST tokens — the same decree
   applied to the imported light-theme sections (faq/pricing/lens). Scope is
   body[data-theme="light"], which assemble.js emits ONLY for a light palette that
   also carries a GL layer, so no dark pack and no GL-less light pack is touched.
   The plate is the imported template's own answer ("opaque plates — content always
   reads, gates fly behind"): copy never borrows contrast from the geometry. ---- */
body[data-theme="light"][data-track="orbit"],
body[data-theme="light"][data-track="nebula"]{
  background:
    radial-gradient(120% 80% at 50% 8%, color-mix(in srgb,var(--c-glow2) 8%,transparent) 0%, transparent 58%),
    radial-gradient(90% 60% at 78% 92%, color-mix(in srgb,var(--c-accent) 6%,transparent) 0%, transparent 62%),
    linear-gradient(178deg, var(--c-field) 0%, var(--c-dominant) 46%,
      color-mix(in srgb,var(--c-dominant) 90%,var(--c-ink) 10%) 100%);
  background-attachment:fixed}
/* the ambient glow canvas is additive-by-intent: over paper it reads as a colour
   wash, not light. Halve it and let the geometry carry the depth. */
body[data-theme="light"] .glow{opacity:.26}
/* hero copy on an opaque plate, the imported template's hero-plate verbatim in tokens */
body[data-theme="light"] .hero__inner{
  padding:clamp(1.6rem,3.4vw,3rem) clamp(1.4rem,3vw,2.8rem) clamp(1.8rem,3.4vw,3rem);
  border-radius:calc(var(--radius) + 2px);
  background:color-mix(in srgb,var(--c-field) 88%,transparent);
  backdrop-filter:blur(14px) saturate(1.06);
  border:1px solid color-mix(in srgb,var(--c-ink) 13%,transparent);
  box-shadow:0 30px 70px -40px color-mix(in srgb,var(--c-ink) 42%,transparent)}
/* the CTA's shadow exists to lift white type off footage; on paper it is soot */
body[data-theme="light"] .hero__cta{text-shadow:none}
@media (max-width:900px){
  body[data-theme="light"] .hero__inner{padding:clamp(1.2rem,5vw,1.8rem) clamp(1rem,4vw,1.4rem)}
}
/* hours "temporal" skin: its band fades accent->TRANSPARENT, which on a dark host
   lands on dark (white label reads) and on paper lands on white (measured 1.54:1).
   On a light host the band stays opaque and fades toward the FIELD instead. */
body[data-theme="light"] .gxh-window{
  background:linear-gradient(180deg,var(--c-accent) 0%,
    color-mix(in srgb,var(--c-accent) 72%,var(--c-field)) 100%)}

/* nova_hero — editorial shell over a full-bleed ambient video: a TOP row (mono service list + intro)
   and a BOTTOM row (left glass badge + big headline; right floating glass contact card with a portrait
   and a white pill). Ported from jeff-templates/novaai-full-template.html (#projects hero). EVERY colour
   is a --c-* token (check-catalog forbids raw hex); dark-first surfaces derive from --c-dominant /
   --c-ink. Only colour + copy vary. */

/* Neutralise the engine base `.hero` (display:grid; align-items:end; padding-block bottom) — stacking
   three min-height:100svh boxes inside its grid overflowed and pushed the bottom headline row below
   the fold. Pin exact heights so the shell is exactly one viewport and owns its own layout. */
.hero--nova { position: relative; display: block; padding: 0; height: 100svh; min-height: 640px; background: var(--c-dominant); }
.nova__wrap { position: relative; height: 100%; width: 100%; overflow: hidden; background: var(--c-dominant); }

.nova__bg { position: absolute; inset: 0; z-index: 0; overflow: hidden; }
.nova__video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity 1s ease; }
.nova__video--on { opacity: 1; }
.nova__video--still { background-size: cover; background-position: center; opacity: 1; }
.nova__scrim { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, color-mix(in srgb, var(--c-dominant) 30%, transparent) 0%, color-mix(in srgb, var(--c-dominant) 4%, transparent) 34%, color-mix(in srgb, var(--c-dominant) 14%, transparent) 66%, color-mix(in srgb, var(--c-dominant) 46%, transparent) 100%); }

.nova__shell { position: absolute; inset: 0; z-index: 10; display: flex; flex-direction: column; justify-content: space-between; gap: 48px;
  padding: 112px clamp(20px, 4vw, 48px) clamp(40px, 6vh, 64px); }

/* top row: service list + intro */
.nova__top { display: flex; flex-direction: column; gap: 24px; }
@media (min-width: 900px) { .nova__top { flex-direction: row; justify-content: space-between; align-items: flex-start; gap: 40px; } }
.nova__services { display: flex; flex-direction: column; gap: 8px; }
.nova__service { margin: 0; font-family: var(--f-utility, sans-serif); font-size: 12px; font-weight: 500; line-height: 1.5;
  text-transform: uppercase; letter-spacing: .15em; color: color-mix(in srgb, var(--c-ink) 90%, transparent); text-shadow: 0 4px 14px color-mix(in srgb, var(--c-dominant) 34%, transparent); }
.nova__intro { margin: 0; max-width: 320px; font-family: var(--f-body, sans-serif); font-size: clamp(15px, 1.3vw, 18px); line-height: 1.6; color: var(--c-ink); text-shadow: 0 4px 14px color-mix(in srgb, var(--c-dominant) 34%, transparent); }

/* bottom row: headline block + contact card */
.nova__bottom { display: flex; flex-direction: column; gap: 32px; align-items: flex-start; }
@media (min-width: 900px) { .nova__bottom { flex-direction: row; align-items: flex-end; justify-content: space-between; gap: 40px; } }
.nova__left { min-width: 0; }
.nova__badge { display: inline-flex; align-items: center; min-height: 31px; margin-bottom: 20px; padding: 6px 12px;
  border-left: 2px solid var(--c-ink); background: color-mix(in srgb, var(--c-ink) 15%, transparent); backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  font-family: var(--f-utility, sans-serif); font-size: 11px; font-weight: 500; letter-spacing: .04em; color: var(--c-ink); }
.nova__headline { margin: 0; font-family: var(--f-display, sans-serif); font-size: clamp(3rem, 7.2vw, 4.5rem); font-weight: 400; line-height: 1.05; letter-spacing: -.04em; color: var(--c-ink);
  text-shadow: 0 7px 24px color-mix(in srgb, var(--c-dominant) 44%, transparent); }

.nova__card { display: flex; align-items: center; gap: 16px; width: min(100%, 347px); border-radius: 12px; padding: 12px;
  background: color-mix(in srgb, var(--c-ink) 15%, transparent); border: 1px solid color-mix(in srgb, var(--c-ink) 15%, transparent);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px); }
@media (min-width: 900px) { .nova__card { width: auto; min-width: 334px; } }
.nova__card-img { width: 80px; height: 96px; flex: 0 0 80px; border-radius: 8px; object-fit: cover; object-position: center; background: color-mix(in srgb, var(--c-ink) 8%, transparent); }
.nova__card-body { display: flex; flex-direction: column; gap: 6px; min-width: 0; padding-right: 8px; }
.nova__card-title { margin: 0; font-family: var(--f-body, sans-serif); font-size: 14px; line-height: 1.35; font-weight: 500; color: var(--c-ink); }
.nova__card-role { margin: 0; font-family: var(--f-utility, sans-serif); font-size: 10px; line-height: 1.45; text-transform: uppercase; letter-spacing: .12em; color: color-mix(in srgb, var(--c-ink) 60%, transparent); }
.nova__pill { display: inline-flex; align-items: center; justify-content: center; gap: 7px; width: fit-content; margin-top: 6px; padding: 8px 16px;
  border-radius: 999px; background: var(--c-ink); color: var(--c-on-accent, var(--c-dominant)); font-family: var(--f-utility, sans-serif); font-size: 12px; font-weight: 600; text-decoration: none;
  transition: background .3s ease, transform .3s ease; }
.nova__pill:hover { transform: translateY(-1px); background: color-mix(in srgb, var(--c-ink) 85%, transparent); }
.nova__chev { width: 14px; height: 14px; flex: 0 0 auto; }

/* staggered reveal (scene.js adds .nova__in) */
.nova__rise { opacity: 0; transform: translateY(24px); transition: opacity .8s var(--ease, ease), transform .8s var(--ease, ease); }
.nova__rise.nova__in { opacity: 1; transform: none; }

@media (max-width: 899px) {
  .nova__shell { padding-top: 96px; }
  .nova__headline { font-size: clamp(2.6rem, 12vw, 3.4rem); }
  .nova__card { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .nova__rise { opacity: 1; transform: none; }
  .nova__video { opacity: 1; }
}
/* nova_hero scene — the ambient hero carries no canvas; this file only isolates the hero stacking
   context. All visible styling lives in hero.css; kept as its own file so the GL-module contract
   (js + css pair) holds. */
.hero--nova { isolation: isolate; }
#s4{transform:rotate(-1.5deg)}@media(max-width:900px){#s4{transform:none}}
:root{--tex-opacity:0.06}
