/* ==========================================================================
   YONDA v3 — cohesion pass. White primary theme; four acts share styling:
     ACT I  white editorial (ink type, rounded media panels)
     ACT II white + lavender mesh gradient (violet = Yasi thread)
     ACT III white product register (cards/circles, violet hinge accents)
     ACT IV dark finale sheet (iyo register, violet glow)
   One type system: INTER site-wide (user directive 2026-07-26) — the four
   role tokens all resolve to it. Layout/interaction patterns are all
   sourced from the inspo pool — see index.html per-section notes.
   ========================================================================== */

:root{
  --white:#fff; --ink:#111013; --ink-soft:#5b5860;
  --line:rgba(17,16,19,.12);
  --vio-1:#cebdf8; --vio-2:#e2dbf8; --vio-3:#c4c5f1;
  --vio-deep:#241d3d; --violet:#7a63d6;
  --dark:#0c0b0e; --dark-2:#141218;
  /* ONE TYPEFACE SITE-WIDE — Inter (user directive 2026-07-26). The four
     role tokens are kept so every call site stays untouched and the type
     system can be re-differentiated later by editing only these lines;
     right now all four deliberately resolve to Inter. The former roles were
     Hanken Grotesk / Instrument Serif / IBM Plex Mono / Archivo. */
  --sans:"Inter",system-ui,sans-serif;
  --serif:"Inter",system-ui,sans-serif;      /* italic accents still work */
  --monof:"Inter",ui-monospace,monospace;    /* label role: letterspaced   */
  --slam:"Inter",system-ui,sans-serif;
  --margin:clamp(20px,3vw,48px);
  /* ── TYPE SCALE (2026-07-26) ────────────────────────────────────────────
     Every step below is seeded with the EXACT value that rule already used, so
     introducing the layer changed nothing visually (verified by comparing
     computed font-size before/after). design-system.html edits these. */
  --t-display:clamp(50px,7.05vw,106px);  /* hero H1 (user: +a bit)   */
  --t-h1:clamp(38px,4.4vw,66px);         /* identity H2              */
  --t-h2:clamp(34px,3.9vw,60px);         /* manifesto statement      */
  --t-h3:clamp(30px,3.6vw,54px);         /* pillars / eco H2         */
  --t-beat:clamp(28px,3vw,56px);         /* the two Yasi beats       */
  --t-slam:clamp(34px,5.6vw,86px);       /* invitation              */
  --t-step:clamp(26px,3.1vw,58px);       /* Yasi step title          */
  --t-list:clamp(24px,2.36vw,35px);      /* Experience OS list (-)   */
  --t-row:clamp(24px,2.8vw,40px);        /* ecosystem row title      */
  --t-tail:clamp(24px,2.9vw,44px);       /* manifesto tail           */
  --t-lede:clamp(15px,1.4vw,18px);       /* section lede             */
  --t-body:15px;                          /* body copy                */
  --t-small:13.5px;                       /* captions                 */
  --t-mono:12px;                          /* mono chips/labels        */
  --beat:var(--t-beat);                   /* back-compat alias        */
  /* MEASURED (floema computed styles, 2026-07-24): their signature ease is
     0.7s cubic-bezier(0.19, 1, 0.22, 1) — adopted as the global curve. */
  --ease:cubic-bezier(.19,1,.22,1);
  --dur:.7s;
}

*{margin:0;padding:0;box-sizing:border-box}
body{-webkit-font-smoothing:antialiased;overflow-x:hidden;background:var(--white);
  color:var(--ink);font-family:var(--sans)}
img{display:block;max-width:100%}
a{color:inherit;text-decoration:none;cursor:pointer}
/* HIGHLIGHTS IN HEADINGS ARE PURPLE, NEVER ITALIC (user 2026-07-27).
   - font-style:normal kills the italic idiom site-wide (these four <em>s were
     the only visible italic text on the page).
   - no font-weight here, so a highlight inherits its heading's 500 instead of
     dropping to 400 mid-sentence.
   - white-space:nowrap is the GUARANTEE the user asked for: a highlight can
     never be split across two lines, whatever the viewport. The heading widths
     are tuned (below) so the natural wrap falls either side of it. */
em{font-style:normal;color:var(--violet);white-space:nowrap}
/* ...and the blocks that hold a highlight balance their own line lengths, so the
   wrap lands EITHER SIDE of the highlight instead of stranding a word after it.
   Measured at 420/500/600/700/889/1280: this is what turns
   "The foundations of a smarter way to / travel." into
   "The foundations of a / smarter way to travel." with no breakpoint hack —
   the browser re-solves it per width. Unsupported browsers fall back to normal
   wrapping, which is the behaviour that shipped before. */
.mani__tail,.idn__h,.pil__intro h2,.eco__head h2{text-wrap:balance}
.mono{font-family:var(--monof);font-size:var(--t-mono);letter-spacing:.14em}
.pin{position:relative}
.pin .stage{position:sticky;top:0;height:100vh;overflow:hidden}
.glmesh{position:absolute;inset:0;width:100%;height:100%;display:block}

/* entrance reveals (all inspo sites use soft rise-ins) */
[data-rev]{opacity:0;transform:translateY(26px);
  transition:opacity .9s var(--ease),transform .9s var(--ease)}
[data-rev].in{opacity:1;transform:none}

/* shared pill button (genie/nuraform capsule language) */
.pill{display:inline-flex;align-items:center;height:52px;padding:0 30px;
  border-radius:100px;font-size:14px;font-weight:500;letter-spacing:.04em;
  text-transform:uppercase;
  transition:transform .35s var(--ease),background .35s var(--ease)}
.pill:hover{transform:translateY(-2px)}
.pill--ink{background:var(--ink);color:#fff}
.pill--ink:hover{background:#26242b}
.pill--vio{background:var(--violet);color:#fff}
.pill--vio:hover{background:#8b76e0}

/* section labels (elva/iyo mono chip language) */
.slabel{position:absolute;left:var(--margin);top:4.5vh;z-index:8;
  color:var(--ink-soft);border:1px solid var(--line);border-radius:100px;
  padding:9px 16px;background:rgba(255,255,255,.7);backdrop-filter:blur(6px)}
.slabel--vio{color:var(--vio-deep);border-color:rgba(36,29,61,.25);
  background:rgba(255,255,255,.4)}
.slabel--dark{color:rgba(255,255,255,.7);border-color:rgba(255,255,255,.2);
  background:rgba(12,11,14,.4)}
.slabel--flow{position:static;display:inline-block;margin-bottom:26px}

/* fixed chrome (difference blend + iyo mono) */
.chrome{position:fixed;inset:0 0 auto 0;z-index:100;display:flex;
  align-items:center;justify-content:space-between;
  padding:24px var(--margin);mix-blend-mode:difference;color:#fff;
  pointer-events:none}
.chrome__logo{font-weight:600;font-size:17px;letter-spacing:.32em;text-indent:.32em}
.chrome__right{display:flex;flex-direction:column;align-items:flex-end;gap:7px}
.chrome__act{font-family:var(--monof);font-size:11px;letter-spacing:.16em}
.chrome__bar{width:120px;height:1px;background:rgba(255,255,255,.25);display:block}
.chrome__bar i{display:block;height:100%;width:0;background:#fff}

/* ═══════════ ACT I · 01 HERO — restored v2 QB clone ═══════════
   MEASURED (qb): bg #201914 · H1 120px / lh .85 / ls -.025em / cream
   #ffebc4 / top-left · cream nav · bottom hairline grid · blurred
   full-bleed backdrop that focuses. Neue Haas → Inter (ledger). */
/* REVISED 2026-07-26 (user): black type on the bright pool photograph, H1
   pulled down to sit directly on the full-width rule, and the introduction
   grid reduced from ~44% to ~1/4 of the screen. */
.qb-hero{background:#f2f1ee;font-family:var(--sans)}
.qb-hero__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  filter:blur(22px) saturate(1.05);transform:scale(1.12);opacity:.85;
  will-change:filter,transform}
.qb-hero__nav{position:absolute;inset:0 0 auto 0;display:flex;align-items:center;
  padding:24px 20px;color:var(--ink);font-size:15px;z-index:5}
.qb-hero__logo{font-weight:500;letter-spacing:.01em;margin-right:auto}
.qb-hero__logo sup{font-size:8px}
.qb-hero__nav nav{display:flex;gap:26px;margin-right:auto;letter-spacing:.02em}
.qb-hero__nav nav a,.qb-hero__contact{text-transform:uppercase;font-size:14px}
/* bottom-anchored to the rule: the grid starts at 75%, so the H1's baseline
   block ends exactly there */
.qb-hero__h1{position:absolute;left:20px;bottom:25%;z-index:4;color:var(--ink);
  font-weight:400;font-size:var(--t-display);line-height:.85;
  letter-spacing:-.09em;margin-bottom:14px}
.qb-hero__grid{position:absolute;left:0;right:0;bottom:0;top:75%;z-index:4;
  display:grid;grid-template-columns:1fr 1fr;color:var(--ink)}
.qb-hero__cell{border-top:1px solid rgba(17,16,19,.22);padding:14px 20px;
  position:relative}
.qb-hero__cell + .qb-hero__cell{border-left:1px solid rgba(17,16,19,.22)}
.qb-hero__label{font-size:15px;opacity:.9}
.qb-hero__intro{margin-top:26px;font-size:19px;line-height:1.25}
.qb-hero__down{position:absolute;right:20px;top:14px;font-size:18px;
  display:flex;align-items:center;gap:8px}
.qb-hero__down i{font-style:normal;font-size:10px;letter-spacing:.28em;opacity:.7}

/* ═══════════ ACT I · 02 MANIFESTO — floema image-over-text ═══════════ */
.mani{background:var(--white)}
/* the lede is this section's INTRODUCTION and stays for as long as the statement
   (user 2026-07-26): 24px, two lines, above it — not a fading beat */
.mani__lede{position:absolute;left:50%;top:15vh;transform:translate(-50%,0);
  width:22ch;text-align:center;color:var(--ink);
  font-size:21.5px;letter-spacing:-.022em;line-height:1.35;z-index:4;opacity:0}
/* chip rides directly above the lede/statement block, not in the corner */
.mani .slabel{position:absolute;left:50%;top:8.5vh;transform:translateX(-50%)}
/* measured floema statement: ~3.9vw / lh 1.05 / ls -.04em / w400 / centered */
.mani__head{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(920px,92vw);text-align:center;z-index:2;
  font-weight:500;font-size:var(--t-h2);line-height:1.05;
  letter-spacing:-.04em}
.mani__w{position:relative;display:inline-block}
.mani__w::after{content:"";position:absolute;left:-3%;top:54%;height:.045em;
  width:calc(var(--sw,0)*106%);background:var(--ink);transform:rotate(-1.2deg);
  transform-origin:left center}
.mani__track{position:absolute;inset:0;z-index:3;pointer-events:none;overflow:hidden}
.mani__card{position:absolute;overflow:hidden;border-radius:14px;
  box-shadow:0 20px 60px rgba(17,16,19,.16);will-change:transform}
.mani__card img{width:100%;height:100%;object-fit:cover;display:block}
.mani__tail{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(820px,90vw);text-align:center;color:var(--ink);z-index:5;
  font-weight:400;font-size:var(--t-tail);line-height:1.25;
  letter-spacing:-.03em;opacity:0}
/* was ink-soft (a de-emphasis) — the em idiom is now the purple highlight */

/* ═══════════ ACT II · 03 EXPERIENCE OS — dark futuristic (iyo register,
   floema layout): glowing violet materials, HUD ring, mono details ═══ */
.os{background:var(--dark)}
/* All three §3 blocks share one horizontal centre line (user 2026-07-26): the
   list, the orbit and the rail are each vertically centred on 50%. */
.os__list{position:absolute;left:var(--margin);top:57%;
  transform:translateY(-50%);list-style:none;z-index:4}
/* MEASURED floema mechanics: inactive 30% / active 100%; active slides
   translateX(40px); everything on 0.7s cubic-bezier(.19,1,.22,1); the
   arrow is a real inline element fading in (not a pseudo). */
.os__list li{position:relative;font-size:var(--t-list);
  line-height:1.35;letter-spacing:-.04em;font-weight:400;
  color:rgba(255,255,255,.3);white-space:nowrap;transform:translateX(0);
  transition:color var(--dur) var(--ease),transform var(--dur) var(--ease),
    text-shadow var(--dur) var(--ease)}
.os__list li.on{color:#fff;transform:translateX(40px);
  text-shadow:0 0 24px rgba(122,99,214,.45)}
.os__arr{position:absolute;left:-38px;top:0;color:var(--vio-1);opacity:0;
  transform:translateX(-12px);
  transition:opacity var(--dur) var(--ease),transform var(--dur) var(--ease)}
.os__list li.on .os__arr{opacity:1;transform:none}
/* centred header (user storyboard): violet kicker + big white title */
.os__head{position:absolute;left:50%;top:7vh;transform:translateX(-50%);
  text-align:center;z-index:4}
.os__kicker{color:#9d8bea;font-size:clamp(18px,1.75vw,25px);margin-bottom:10px}
.os__head h2{color:#fff;font-weight:500;font-size:clamp(38px,4.05vw,64px);
  letter-spacing:-.03em}
/* HUD orbit: glowing ring; the five OS layers sit ON the ring as nodes —
   the ACTIVE node lights violet, grows, and shows its label (storyboard:
   the highlight travels around the ring as the list advances) */
.os__orbit{position:absolute;left:50%;top:57%;transform:translate(-50%,-50%);
  width:min(35vw,490px);height:min(35vw,490px);
  border:1px solid rgba(158,139,234,.4);border-radius:50%;z-index:3;
  box-shadow:0 0 60px rgba(122,99,214,.14),inset 0 0 60px rgba(122,99,214,.08)}
.os__node{position:absolute;width:0;height:0;z-index:4}
.os__node i{position:absolute;left:0;top:0;transform:translate(-50%,-50%);
  width:28px;height:28px;border-radius:8px;background:#3c3b42;
  border:1px solid rgba(255,255,255,.16);
  transition:background var(--dur) var(--ease),width var(--dur) var(--ease),
    height var(--dur) var(--ease),box-shadow var(--dur) var(--ease),
    border-radius var(--dur) var(--ease)}
.os__node b{position:absolute;left:0;top:26px;transform:translateX(-50%);
  font-family:var(--monof);font-weight:400;font-size:11px;letter-spacing:.1em;
  color:#fff;white-space:nowrap;opacity:0;
  transition:opacity var(--dur) var(--ease)}
.os__node.on i{width:44px;height:44px;border-radius:12px;
  background:var(--violet);border-color:rgba(255,255,255,.7);
  box-shadow:0 0 30px rgba(122,99,214,.6)}
.os__node.on b{opacity:1;top:32px}
/* inner shape: a lit SPHERE (user storyboard update) — shaded ball with
   drifting sheen; material recolours per active layer */
/* inner shape: the SAME WebGL lit sphere as the Yasi voice orb (user
   2026-07-26). No background/box-shadow here — the shader paints the whole
   orb, and NOTHING may clip it: the canvas is deliberately ~1.9x the orb so
   the bloom reaches zero before its own edge, and every wrapper stays
   overflow:visible so the glow is never cut. */
.os__sphere{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:150px;height:150px;overflow:visible}   /* 65% of 230px (user) */
.os__sphere canvas{position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%);width:190%;height:190%;display:block}
/* Nothing around either orb clips its bloom. NOTE the pinned .stage keeps its
   own overflow:hidden — that clip is what stops pinned sections bleeding into
   each other, and it does NOT cut either orb: both canvases are sized well
   inside the viewport and force alpha to 0 before their own edge. */
.os__orbit,.os__world,.os__sphere,.ya__blob{overflow:visible}

/* ═══ seam transition — VERIFIED on amaterasu.ai (2026-07-24, cload-lite):
   their transition is rendered INSIDE a worker-driven WebGL scene
   (OffscreenCanvas; the DOM splash circles sit at opacity 0). Recreated
   the same way — a transition SHADER: veil ramp + central light flare +
   expanding ripple rings, scroll-driven. ═══ */
/* the whole dark world in one layer so the portal mask consumes bg AND
   content together; veil (glow) stays outside it, unmasked */
.os__world{position:absolute;inset:0;z-index:1;background:var(--dark)}
.os__veil{position:absolute;inset:0;z-index:6;pointer-events:none}
.os__trcv{position:absolute;inset:0;width:100%;height:100%;display:block}
.os__rail{position:absolute;right:var(--margin);top:57%;transform:translateY(-50%);
  width:250px;z-index:4;
  border-left:1px solid rgba(255,255,255,.16);padding-left:26px}
.os__rail p{font-size:14px;line-height:1.5;margin-bottom:18px;
  color:rgba(255,255,255,.55)}
.os__rail p:first-child{color:#fff;font-weight:500}
.os__railimg{border-radius:10px;overflow:hidden;
  border:1px solid rgba(255,255,255,.14)}
.os__railimg img{width:100%;height:170px;object-fit:cover;opacity:.85}
.os__char{position:absolute;left:var(--margin);bottom:5vh;z-index:4;
  color:rgba(255,255,255,.5)}

/* ═══════════ ACT II bridge · QUESTIONS — dark aurora ═══════════ */
.qs{background:var(--dark)}
.qs .stage{display:flex;align-items:center;justify-content:center}
/* dark seams blend the aurora canvas into the act's dark field */
.qs .stage::before,.qs .stage::after{content:"";position:absolute;left:0;right:0;
  height:16vh;z-index:2;pointer-events:none}
.qs .stage::before{top:0;background:linear-gradient(var(--dark),transparent)}
.qs .stage::after{bottom:0;background:linear-gradient(transparent,var(--dark))}
.qs__q{position:absolute;width:min(820px,88vw);text-align:center;z-index:3;
  color:#efeaff;font-family:var(--serif);font-style:italic;
  font-size:clamp(28px,3.6vw,54px);line-height:1.25;opacity:0;
  text-shadow:0 0 34px rgba(122,99,214,.5)}

/* ═══════════ ACT II · 04 MEET YASI™ — the merged 8-shot experience ═══════
   One pinned stage carries the whole chapter so elements survive across the
   former section seams (the input pill BECOMES the photograph; the
   photograph carries into the steps chapter). Geometry below is read off
   the user's storyboard renders (2000×1254) and expressed in vw/vh. ═══ */
.ya{background:var(--white)}
.ya .stage{background:var(--white)}     /* white base (user 2026-07-26) */
/* true portal handoff — UNCHANGED: yasi is fully pinned behind the OS stage
   for the whole aperture window, so the iris (a transparent hole in the veil
   shader) opens directly onto the live section, never onto a blank fill.
   overlap = stage exit (100vh) + portal window (20% of the 380vh OS scrub
   = 76vh) = 176vh. data-pin carries the +76 compensation. */
/* overlap = stage exit (100vh) + portal window. OS pin 640vh => scrub 540vh
   => 20% window = 108vh => 208vh. #yasi data-pin carries the same +108.
   These THREE numbers are coupled — change one, change all three. */
#yasi{margin-top:-208vh}
.os .stage{z-index:5}                    /* keeps the portal above yasi */

/* ---- shots 6–8 world: photograph + WebGL colour grade ---- */
/* the CLIP lives here, not on the img: photo, colour grade and scrim then
   always share one rect — incl. the 20px full-bleed margin (user 2026-07-26) */
.ya__world{position:absolute;inset:0;z-index:1;isolation:isolate;
  will-change:clip-path}
.ya__bg{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;will-change:filter,transform}
.ya__fx{position:absolute;inset:0;width:100%;height:100%;display:block;
  opacity:0;mix-blend-mode:color;pointer-events:none}
/* comes in WITH the full-bleed transition so the phone's glass content reads
   against bright water, then deepens into the shot-8 grade */
/* NOTE the centre is only slightly lighter than the edges: the phone sits in
   the middle, so a strongly centre-light gradient (the first attempt) left the
   phone's glass unreadable over bright water. */
.ya__scrim{position:absolute;inset:0;z-index:2;opacity:0;pointer-events:none;
  background:linear-gradient(100deg,rgba(9,7,18,.88),rgba(9,7,18,.76) 50%,
    rgba(9,7,18,.84))}

/* ---- shots 1–5 copy ---- */
/* base transforms exist in CSS as well as JS: main.js COMPOSES onto them
   (`translate(-50%,-50%) translateY(…)`), so the copy is centred correctly
   on the very first paint, before the first rAF tick runs */
.ya__b1{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  z-index:4;width:min(860px,90vw);text-align:center;color:#302a45;
  font-size:var(--beat);line-height:1.26;font-weight:500;
  letter-spacing:-.06em}
.ya__ln{display:block;opacity:0;will-change:transform,opacity}
.ya__b2{position:absolute;left:50%;top:50%;
  transform:translate(-50%,-50%) translateY(-3vh);z-index:4;
  display:flex;align-items:baseline;justify-content:center;gap:.24em;
  white-space:nowrap;font-size:var(--beat);font-weight:500;
  letter-spacing:-.06em;line-height:1.1}
.ya__ours{color:#1b1630;opacity:0;will-change:transform,opacity}
/* the WebGL wordmark: the span reserves the glyph box (so "Ours is" never
   shifts when Yasi arrives), the canvas paints into it with bleed for glow */
.ya__yasi{position:relative;display:inline-block;overflow:visible}
.ya__yasi canvas{position:absolute;display:block;opacity:0}

/* ---- the voice blob ---- */
.ya__blob{position:absolute;left:50%;top:73%;z-index:5;
  /* 2x the visible orb: the bloom must fade to 0 before the canvas edge,
     otherwise it is clipped square (the halo the user spotted) */
  width:clamp(96px,8vw,150px);height:clamp(96px,8vw,150px);
  transform:translate(-50%,-50%);opacity:0;display:block;pointer-events:none}

/* ---- the mock input that becomes the world ---- */
.ya__pill{position:absolute;left:50%;bottom:10vh;transform:translateX(-50%);
  width:min(66vw,1180px);z-index:9;display:flex;align-items:center;
  gap:clamp(14px,1.5vw,26px);border-radius:60px;
  padding:clamp(15px,1.35vw,25px) clamp(18px,1.6vw,30px);
  background:rgba(255,255,255,1);opacity:0;transform-origin:50% 50%;
  box-shadow:0 18px 50px rgba(58,44,104,.14);will-change:transform,opacity}
/* the pill's children read their size from custom properties so main.js can
   LERP them during the morph into the phone bar (a plain transform:scale would
   shrink the glyphs and the radius too, which reads as a miniature) */
.ya__pill{--p-ast:clamp(15px,1.35vw,26px);--p-fs:clamp(13px,1.3vw,25px);
  --p-btn:clamp(34px,2.65vw,54px);--p-bfs:clamp(14px,1.15vw,21px)}
.ya__ast{flex:none;color:#17131f;font-size:var(--p-ast);line-height:1}
.ya__typed{flex:1;min-width:0;color:#17131f;line-height:1.3;letter-spacing:-.022em;
  font-size:var(--p-fs);white-space:nowrap;overflow:hidden}
.ya__send{flex:none;width:var(--p-btn);height:var(--p-btn);
  border-radius:50%;background:#100d16;color:#fff;display:flex;
  align-items:center;justify-content:center;
  font-size:var(--p-bfs);line-height:1}

/* ---- the glass phone (shots 7–8) ---- */
.ya__phone{position:absolute;left:50%;top:9vh;transform:translateX(-50%);
  width:min(23vw,430px);height:82vh;z-index:7;opacity:0;
  border:1.5px solid rgba(255,255,255,.5);
  border-radius:clamp(28px,2.9vw,52px);
  display:flex;flex-direction:column;overflow:hidden;
  will-change:transform,opacity}
.ya__notch{flex:none;width:28%;height:clamp(20px,1.9vw,34px);margin:14px auto 0;
  border:1.5px solid rgba(255,255,255,.5);border-radius:100px}
.ya__thread{flex:1;min-height:0;overflow:hidden;
  padding:clamp(14px,1.5vw,26px) clamp(11px,1.15vw,20px) 0;
  display:flex;flex-direction:column;gap:clamp(10px,1.1vw,19px)}
.ya__bub{opacity:0;border-radius:clamp(12px,1.15vw,20px);
  padding:clamp(9px,.85vw,15px) clamp(11px,1.05vw,18px);
  font-size:clamp(9px,.83vw,15px);line-height:1.42;color:#17131f;
  will-change:transform,opacity}
.ya__bub--me{align-self:flex-end;max-width:88%;background:#fff;
  box-shadow:0 6px 18px rgba(20,14,40,.16)}
.ya__bub--ya{align-self:flex-start;max-width:94%;
  background:linear-gradient(126deg,#e7dafc,#f6d6ec 62%,#fcdbe6);
  box-shadow:0 8px 22px rgba(20,14,40,.18)}
.ya__ylabel{display:flex;align-items:center;gap:5px;margin-bottom:6px;
  color:#7a63d6;font-family:var(--monof);letter-spacing:.13em;
  font-size:clamp(7px,.6vw,11px)}
.ya__ylabel svg{width:1.15em;height:1.15em;flex:none;fill:none;
  stroke:currentColor;stroke-width:1.5}
.ya__cards{display:flex;gap:clamp(4px,.42vw,8px);opacity:0;
  will-change:transform,opacity}
.ya__cards span{flex:1;min-width:0;aspect-ratio:3/4;overflow:hidden;
  border-radius:clamp(5px,.5vw,9px);background:rgba(255,255,255,.15)}
.ya__cards img{width:100%;height:100%;object-fit:cover}
/* invisible: reserves the bar space in the phone's flex column and gives JS a
   target rect. The REAL .ya__pill flies into it and becomes the phone input. */
/* The user asked for the phone's input ~16px higher. It is NOT nudged here:
   the pill was landing 18px below this slot because the flight measured its
   drop against the pill's natural half-height (fixed in main.js), so the exact
   landing delivers the lift. Padding stays the phone's natural rhythm. */
.ya__pbar{flex:none;margin:0 clamp(11px,1.15vw,20px) clamp(13px,1.35vw,24px);
  height:clamp(30px,2.6vw,50px);border-radius:100px;opacity:0;
  pointer-events:none}

/* ---- shot 8: step-chapter chrome ---- */
.ya__left{position:absolute;left:7.5vw;top:50%;transform:translateY(-50%);
  width:min(25vw,390px);text-align:center;z-index:8;opacity:0}
.ya__leftin{will-change:transform,opacity}
.ya__title{font-weight:500;font-size:var(--t-step);line-height:1.04;
  letter-spacing:-.03em;color:#fff;text-shadow:0 2px 30px rgba(12,8,26,.45)}
.ya__sub{margin-top:clamp(14px,1.5vw,26px);font-size:clamp(12px,1.02vw,17px);
  line-height:1.56;color:rgba(255,255,255,.88);
  text-shadow:0 1px 16px rgba(10,6,24,.6)}
/* Vertically centred on the page, horizontally centred in the gap to the RIGHT
   of the phone (user 2026-07-26). left/top are set from JS off the phone's LIVE
   rect — a hardcoded calc() was wrong the moment the phone's width changed at a
   breakpoint (it is 64vw under 900px, not 23vw). */
.ya__count{position:absolute;transform:translate(-50%,-50%);
  width:clamp(52px,4vw,76px);height:clamp(52px,4vw,76px);
  z-index:8;color:#fff;opacity:0}
.ya__count svg{position:absolute;inset:0;width:100%;height:100%;
  transform:rotate(-90deg)}
.ya__count .bgc{fill:none;stroke:rgba(255,255,255,.38);stroke-width:2}
.ya__count .fgc{fill:none;stroke:#fff;stroke-width:2;stroke-dasharray:126;
  stroke-dashoffset:94;transition:stroke-dashoffset .6s var(--ease);
  filter:drop-shadow(0 0 5px rgba(255,255,255,.55))}
.ya__count span{position:absolute;inset:0;display:flex;align-items:center;
  justify-content:center;font-size:clamp(9px,.78vw,13px)}

/* ═══════════ ACT III · 05 IDENTITY — floema flyby on white ═══════════ */
.idn{background:var(--white)}
.idn__eyebrow{position:absolute;left:50%;top:17vh;transform:translateX(-50%);
  color:var(--ink-soft);z-index:4;opacity:0}
.idn__h{position:absolute;left:50%;top:50%;transform:translate(-50%,-58%);
  text-align:center;font-weight:500;font-size:var(--t-h1);
  /* MEASURED floema h1: 4.945vw / lh 1.05 / ls -.04em / w400 / centred */
  line-height:1.05;letter-spacing:-.04em;z-index:4;width:min(900px,92vw);
  opacity:0}
.idn__sub{position:absolute;left:50%;bottom:11vh;transform:translateX(-50%);
  width:min(560px,86vw);text-align:center;font-size:15px;line-height:1.55;
  color:var(--ink-soft);z-index:4;opacity:0}
/* TUNNEL — MEASURED off floema.com's hero (2026-07-26): their fly-through is a
   Three.js scene (`.floating-images > .three-container`, z-index:-1, so it sits
   BEHIND the copy). Recreated here in CSS 3D, which is the same projection maths
   a perspective camera does, without adding a WebGL dependency to this build:
   tiles ride translateZ from far to near, so perspective makes them grow AND
   spread radially out of a centre vanishing point — exactly the reference.
   Measured there: tiles ~15px at the far plane → ~115-135px as they exit,
   ~22-26 visible at once, mixed portrait/square/landscape crops, opacity
   fading in out of the vanishing point. */
.idn__field{position:absolute;inset:0;z-index:1;pointer-events:none;
  perspective:1000px;transform-style:preserve-3d}
.idn__ph{position:absolute;left:50%;top:50%;overflow:hidden;border-radius:10px;
  will-change:transform,opacity;box-shadow:0 14px 40px rgba(17,16,19,.13)}
.idn__ph img{width:100%;height:100%;object-fit:cover}
.idn__chip{position:absolute;left:50%;top:63%;transform:translate(-50%,-50%);
  display:grid;grid-template-columns:40px auto;grid-template-rows:auto auto;
  column-gap:12px;align-items:center;background:#fff;border-radius:16px;
  border:1px solid var(--line);padding:14px 20px;
  box-shadow:0 18px 50px rgba(17,16,19,.18);opacity:0;z-index:5}
.idn__chip .a{grid-row:1/3;width:40px;height:40px;border-radius:50%;
  background:linear-gradient(135deg,var(--violet),var(--vio-deep));color:#fff;
  display:flex;align-items:center;justify-content:center;font-weight:600}
.idn__chip b{font-weight:600;font-size:15px}
.idn__chip i{font-style:normal;font-size:12px;color:var(--ink-soft)}

/* ═══════════ ACT III · 06 PILLARS — lightship stack on white ═══════════ */
.pil{background:var(--white);padding-bottom:14vh}
/* centred header block (user 2026-07-26): text AND position, pill above */
.pil__intro{padding:20vh var(--margin) 9vh;max-width:900px;margin:0 auto;
  text-align:center;display:flex;flex-direction:column;align-items:center}
.pil__intro h2{font-weight:500;font-size:var(--t-h3);line-height:1.05;
  letter-spacing:-.03em}
.pil__cards{padding:0 var(--margin)}
.ls-card{position:sticky;height:72vh;border-radius:24px;background:#fff;
  border:1px solid var(--line);margin-bottom:4vh;display:grid;
  grid-template-columns:1.05fr 1fr;overflow:hidden;
  box-shadow:0 18px 60px rgba(17,16,19,.08);will-change:transform,filter}
.ls-card:nth-child(1){top:11vh}.ls-card:nth-child(2){top:14vh}
.ls-card:nth-child(3){top:17vh}.ls-card:nth-child(4){top:20vh}
.ls-card__txt{padding:clamp(26px,3.6vw,54px);display:flex;
  flex-direction:column;justify-content:space-between}
.ls-card__num{font-family:var(--monof);font-size:12px;letter-spacing:.14em;
  color:var(--violet)}
.ls-card__txt h3{font-weight:500;font-size:clamp(28px,3vw,46px);line-height:1.05;
  letter-spacing:-.02em;margin:14px 0 18px}
.ls-card__txt p{font-size:clamp(14px,1.25vw,17px);line-height:1.55;
  color:var(--ink-soft);max-width:420px}
.ls-card__img{overflow:hidden}
.ls-card__img img{width:100%;height:100%;object-fit:cover}
/* featured card — the Yasi violet thread */
/* WHITE like the others (user 2026-07-26); differentiated only by a soft violet
   gradient that fades out INTO the card instead of filling it */
/* NOTE: must NOT set `position` here — .ls-card is position:sticky and that is
   what makes the cards stack. Overriding it to relative broke the whole
   interaction (regression caught 2026-07-26). The ::before overlay works fine
   inside the sticky box. */
.ls-card--feat{background:#fff}
.ls-card--feat::before{content:"";position:absolute;inset:0;pointer-events:none;
  border-radius:inherit;z-index:0;
  background:radial-gradient(125% 95% at 5% 0%,rgba(122,99,214,.30),
    rgba(206,189,248,.15) 44%,rgba(255,255,255,0) 74%)}
.ls-card--feat > *{position:relative;z-index:1}
.ls-card--feat--unused{
  border-color:transparent;color:#fff}
.ls-card--feat h3{color:var(--ink)}
.ls-card--feat p{color:var(--ink-soft)}
.ls-card--feat .ls-card__num{color:var(--violet)}

/* ═══════════ ACT III · 07 ECOSYSTEM — nuraform trail on white ═══════════ */
/* ═══════════ ACT III · 07 ECOSYSTEM — nuraform rotating wheel ═══════════
   MEASURED off nuraform.com's Educators/Consultants/Creators/Founders/
   Freelancers section (2026-07-26). It is NOT a hand-placed zigzag (which is
   what the first build guessed) — it is a WHEEL, pinned with GSAP
   ScrollTrigger (`pin-spacer` in their DOM) and rotated by the scrub:
     · circle images 173px, border-radius 50%          @1440 viewport
     · titles h4, 40px, weight 300, black
     · items sit on a radius ~575px arc, spaced EXACTLY 20 deg apart
       (fitted: circles at 90.9/110.8/130.7/150.5/170.1/189.9/209.5/229.3,
        titles at -90/-70/-50/-30/-10 -> 19.9 deg spacing in both)
     · the two columns share centre-y (~2457) with their centres offset
       ~1288px in x, which is what makes the circle column and the title
       column bulge in OPPOSITE directions as items descend — the
       "serpentine" read in the reference. */
.eco{background:var(--white)}
.eco .stage{background:var(--white)}
.eco__head{position:absolute;left:50%;top:8vh;transform:translateX(-50%);
  text-align:center;padding:0 var(--margin);z-index:6;width:min(760px,92vw)}
.eco__head h2{font-weight:500;font-size:var(--t-h3);
  letter-spacing:-.03em;line-height:1.06}
.eco__head p{margin:14px auto 0;max-width:460px;color:var(--ink-soft);
  font-size:14.5px;line-height:1.55}
.eco__head .slabel--flow{margin-bottom:16px}
.eco__wheel{position:absolute;inset:0;z-index:2;pointer-events:none}
.nura__row{position:absolute;left:0;top:0;width:100%;height:0}
.nura__circle{position:absolute;border-radius:50%;overflow:hidden;
  width:clamp(96px,12vw,200px);height:clamp(96px,12vw,200px);
  will-change:transform;box-shadow:0 22px 60px rgba(17,16,19,.13)}
.nura__circle img{width:100%;height:100%;object-fit:cover;display:block}
/* the text block and the circle column are the SAME width — that symmetry is
   what makes the composition centre structurally (user 2026-07-26) */
.nura__txt{position:absolute;text-align:left}   /* width set from JS */
/* MEASURED: h4 40px / weight 300 / black */
.nura__txt h4{font-weight:500;font-size:var(--t-row);color:var(--ink);
  letter-spacing:-.02em;line-height:1.05}
.nura__txt p{margin-top:12px;font-size:14px;line-height:1.5;
  color:var(--ink-soft);max-width:22em}
.nura__txt .n{display:block;font-family:var(--monof);font-size:11px;
  letter-spacing:.14em;color:var(--violet);margin-bottom:10px}

/* ═══════════ act sheets — rounded panels sliding over the previous act
   (lightship/iyo card language); dark for II & IV, light for III.
   NOTE: overflow:hidden on the SECTION kills position:sticky (v3 review
   bug) — so the radius+bg live on the pinned STAGE instead. ═══════════ */
/* opaque, not transparent: a .pin section is taller than its sticky stage, so
   the uncovered slice would reveal the fixed footer behind it */
section.dark-sheet{margin-top:-32px;background:var(--dark)}
/* section sheets are square (user 2026-07-26); only the full-bleed photo
   inside the Yasi chapter keeps rounded corners */
.dark-sheet .stage{background:var(--dark)}
/* os: the dark lives on .os__world (the maskable layer), not the stage —
   the stage must be see-through where the portal punches the world */
.os .stage{background:transparent}
section.light-sheet{margin-top:-32px;background:var(--white)}
.light-sheet .stage{background:var(--white)}

/* 08 ROAD — floema pin-nav on dark */
.road{background:var(--dark);color:#fff}
.road__list{position:absolute;left:var(--margin);top:36vh;list-style:none;z-index:3}
/* The active phase used to be marked with `li.on::before{content:"→ "}`, which
   INSERTS a glyph into the line box — every activation reflowed the label
   sideways in a single frame, which is the "jump" the user reported. The arrow
   is now an absolutely-positioned element (out of flow, so it can never reflow
   anything) and the label slides clear of it with a transform. Both are
   composited properties, so the handover is smooth at any scroll speed. */
.road__list li{position:relative;font-size:clamp(34px,3.6vw,54px);line-height:1.28;
  letter-spacing:-.04em;font-weight:400;color:rgba(255,255,255,.28);
  transition:color .5s var(--ease);white-space:nowrap}
.road__list li.on{color:#fff}
.road__arr{position:absolute;left:0;top:0;color:var(--violet);opacity:0;
  transform:translateX(-14px);
  transition:opacity .5s var(--ease),transform .5s var(--ease)}
.road__lbl{display:inline-block;
  transition:transform .5s var(--ease)}
.road__list li.on .road__arr{opacity:1;transform:none}
.road__list li.on .road__lbl{transform:translateX(1.25em)}
.road__divider{position:absolute;left:36%;top:30vh;bottom:10vh;width:1px;
  background:rgba(255,255,255,.14)}
.road__right{position:absolute;left:calc(36% + 4vw);right:var(--margin);top:30vh}
/* no CSS transition on these three: main.js writes their opacity/transform on
   EVERY frame of the swap, and a CSS transition on the same property fights the
   per-frame value (it re-targets each frame and never lands). The easing lives
   in the JS curve instead. */
.road__right h3{font-weight:500;font-size:clamp(26px,2.6vw,40px);
  letter-spacing:-.03em;line-height:1.1;max-width:560px}
.road__right p{margin-top:16px;font-size:15.5px;line-height:1.6;
  color:rgba(255,255,255,.6);max-width:440px}
.road__right{will-change:opacity,transform}
.road__img{margin-top:4vh;width:min(40vw,640px);height:34vh;overflow:hidden;
  border-radius:16px}
.road__img img{width:100%;height:100%;object-fit:cover}
/* .road__label ("THE ROAD AHEAD ↓" chip) removed — user 2026-07-27 */

/* 09 INVITATION — tinywins slam on dark, violet echo */
.tw{background:var(--dark);color:#fff;font-family:var(--slam);
  text-align:center;padding:20vh 4vw 16vh}
.tw__kicker{font-family:var(--sans);font-size:clamp(14px,1.3vw,17px);
  color:rgba(255,255,255,.55);max-width:520px;margin:0 auto 7vh;line-height:1.55}
/* REVISED 2026-07-26 (user): no hard line breaks — one string wrapping inside
   a fixed measure, materially lighter and smaller than the old 900/150px slam */
/* headings are 500 site-wide; only the hero H1 is 400 (user 2026-07-26) */
.tw__stack{max-width:min(15ch,88vw);margin:0 auto;font-weight:500;
  font-size:var(--t-slam);line-height:1.0;letter-spacing:-.028em;
  opacity:0;transform:translateY(34px)}
.tw.in .tw__stack{opacity:1;transform:none;
  transition:opacity .8s var(--ease),transform .8s var(--ease)}
.tw .pill{margin-top:8vh}

/* FOOTER — iyo register */
/* LIGHT theme (user 2026-07-26) */
/* FIXED reveal (user 2026-07-26): the footer is pinned to the bottom of the
   viewport BEHIND the page, and the last section slides up over it to uncover
   it. `#cta` gets an opaque background and a higher z-index so it occludes the
   footer until it has scrolled past, and `.iyo-spacer` supplies the scroll
   distance that performs the reveal. */
/* z-index MUST be negative: the footer is last in the DOM, so at z-index 0 it
   would paint ABOVE every preceding section and cover the page. At -1 it sits
   above the canvas background but behind all in-flow content. */
/* HALF-HEIGHT (user 2026-07-27). The spacer must match the footer exactly: it
   is the scroll distance that performs the reveal, so if the two disagree the
   footer is either clipped at the end of the page or leaves dead scroll. Every
   internal offset below was halved with it — they are vh-based, i.e. relative to
   the VIEWPORT, not to the footer, so at 50vh they would otherwise consume the
   whole box. */
.iyo{position:fixed;left:0;right:0;bottom:0;height:50vh;z-index:-1;
  background:var(--white);color:var(--ink-soft);overflow:hidden;
  font-family:var(--sans);padding:5.5vh 60px 5vh}
.iyo-spacer{height:50vh;position:relative;z-index:0;pointer-events:none}
.tw{position:relative;z-index:2}
/* decoration must never eat a click: the glow's blurred ::after spreads 60px
   over the top of the footer (measured 2026-07-27) */
.iyo__glow{pointer-events:none;position:absolute;inset:0 0 auto 0;height:1px;
  background:linear-gradient(90deg,transparent,var(--vio-1),var(--violet),transparent)}
.iyo__glow::after{content:"";position:absolute;inset:-60px 10%;filter:blur(60px);
  background:linear-gradient(90deg,transparent,rgba(122,99,214,.25),transparent)}
.iyo__mandala{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:min(60vw,860px);opacity:.85;pointer-events:none}
.iyo__mark{color:var(--ink);font-size:clamp(40px,4.6vw,68px);font-weight:500;
  letter-spacing:.02em}
.iyo__addr{margin-top:3.4vh;font-size:14px;color:var(--ink-soft)}
.iyo__social{margin-top:2.2vh;display:flex;gap:18px;font-size:17px;color:var(--ink-soft)}
.iyo__cols{position:absolute;right:60px;top:5.5vh;display:flex;gap:7vw}
.iyo__cols h5{font-weight:400;font-size:11px;letter-spacing:.12em;
  color:#5c5c64;margin-bottom:18px}
.iyo__cols a{display:block;font-size:15px;color:var(--ink-soft);margin-bottom:11px}
.iyo__cols a:hover{color:var(--violet)}
/* .iyo__ghost ("The invitation" watermark) removed — user 2026-07-27. It was
   also the last italic type on the site. */

/* ═══════════ responsive (draft level) ═══════════ */
@media (max-width:900px){
  .hero__media{width:92vw;height:36vh}
  .hero__orb{display:none}
  .os__rail{display:none}
  /* NOTE: no `top` override for .os__list here — it must stay on the shared
     50% centre line with the orbit (user 2026-07-26). An override at this
     breakpoint silently broke that alignment, and <=900px is the width the
     user actually views at. */
  /* merged yasi chapter: the phone IS the chapter, so it stays — the layout
     stacks instead (copy below, counter to a corner) rather than hiding it */
  .ya__phone{width:64vw;height:60vh;top:15vh}
  .ya__left{left:6vw;right:6vw;width:auto;top:auto;bottom:3.5vh;
    transform:none;text-align:center}
  .ya__title{font-size:clamp(24px,6.4vw,40px)}
  .ya__count{width:46px;height:46px}   /* position stays JS-driven */
  .ya__pill{width:88vw}
  .ya__b1,.ya__b2{width:90vw}
  .ls-card{grid-template-columns:1fr}
  .ls-card__img{display:none}
  .road__divider,.road__img{display:none}
  .road__right{left:var(--margin);right:var(--margin);top:62vh}
  .iyo__cols{position:static;margin-top:5vh;flex-wrap:wrap;gap:40px}
  /* the tunnel needs its full population to read as depth — the old flyby
     design hid all but 4 tiles here, which made the rebuilt tunnel look empty
     at <=900px (caught in verification 2026-07-26) */
  .idn__field{perspective:700px}
  .chrome__right{display:none}
}
@media (prefers-reduced-motion:reduce){
  *{transition-duration:.01ms !important;animation-duration:.01ms !important}
}
