/* hero.css — the hero/Mission page composition: everything that styles the
   markup index.html itself carries (stage, spill/scrim/vignette/grain, nav,
   hero copy, CTA, the three world-tracked callouts) plus the ENTRY
   choreography and its fast-forward compression classes. Moved verbatim from
   index.html's inline <style> at the M5 shell step — the journey layer's
   styles live in journey/site.css (linked AFTER this file; keep that order,
   the cascade was authored against it). */
  :root {
    --gold:        #d9a441;
    --gold-bright: #f0c877;
    --parchment:   #f2ebdd;
    --ink:         #f2ebdd;
    --muted:       #c9bfa8;
    --bg:          #141008;
  }
  * { box-sizing: border-box; }
  html, body { margin: 0; height: 100%; }
  body {
    background: var(--bg);
    color: var(--ink);
    font-family: "Inter", -apple-system, BlinkMacSystemFont, "Helvetica Neue", Arial, sans-serif;
    -webkit-font-smoothing: antialiased;
    overflow: hidden;
  }

  /* ============================================================
     ENTRY — one choreography, keyed to the growth timelapse.
     The scene grows over 5.4s in the order a real agaric does
     (see the entry choreography in organism/intro.js):
       0.0-1.7s   ground network converges on the base
       1.6-3.8s   stalk climbs in two SLOW waves: verticals, then lattice
       3.1-4.4s   under-cap double loop: gill sweep, then rim ring
       4.1-4.8s   cap top surface inks in       -> light spill on the page
       3.9-5.4s   the spore plume gathers
       5.55s+     the callouts boot up in numbered order: 01, 02, 03
     Every delay below is tuned against those beats — change one, move all.
     QA: ?introat=0.55 freezes scene AND page at that progress. ?nointro=1
     skips the whole entry.
     ============================================================ */

  /* --- 3D stage: full bleed, behind everything --- */
  #stage {
    position: fixed;
    inset: 0;
    z-index: 0;
    animation: stage-bloom 0.9s ease both;
  }
  @keyframes stage-bloom {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
  #stage canvas { display: block; cursor: grab; }
  #stage canvas:active { cursor: grabbing; }

  /* --- light spill: the mushroom's glow touches the page ---
     one warm bloom spilling from the specimen toward the text column,
     one low ground-glow warming the CTA area from below.
     Arrives as the cap ignites — the page warms because the mushroom lit. */
  .spill {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
      radial-gradient(ellipse 58% 62% at 66% 46%, rgba(255, 190, 95, 0.075), transparent 70%),
      radial-gradient(ellipse 44% 26% at 20% 90%, rgba(255, 175, 80, 0.055), transparent 72%);
    animation: spill-in 1.6s ease 3.8s both;
  }
  @keyframes spill-in {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  /* GPU preparation uses the scene's own empty draw state as its cover. The
     left-hand copy/CTA keep their normal entry; only furniture whose timing
     belongs to the mushroom waits for intro.start(). Pausing (rather than
     hiding a second overlay) makes release the exact next frame of the same
     WebGL scene. */
  body.scene-preparing .spill,
  body.scene-preparing .co,
  body.scene-preparing .co * {
    animation-play-state: paused !important;
  }
  body.scene-preparing .callouts {
    pointer-events: none;
  }

  /* --- feathered scrim: buys text contrast, survives camera motion --- */
  .scrim {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: linear-gradient(100deg,
      rgba(14, 10, 4, 0.55) 0%,
      rgba(14, 10, 4, 0.32) 30%,
      rgba(14, 10, 4, 0) 58%);
  }

  /* --- vignette: corners settle, lighter zone biased toward the mushroom --- */
  .vignette {
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background: radial-gradient(ellipse 120% 105% at 62% 44%,
      transparent 42%,
      rgba(8, 5, 1, 0.28) 76%,
      rgba(6, 4, 1, 0.52) 100%);
  }

  /* --- grain: 2–4% paper texture over everything, kills the flat digital black --- */
  .grain {
    position: fixed;
    inset: 0;
    z-index: 2;
    pointer-events: none;
    opacity: 0.045;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='280' height='280'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='280' height='280' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 280px 280px;
  }

  /* ============================================================
     ACCESSIBILITY (M5 debt burn-down): skip link + :focus-visible for
     every shell-level interactive element (nav, CTA — the journey layer's
     own controls carry the matching treatment in journey/site.css; this
     hero region was its own recorded a11y debt until now). Both use the
     gold family so a focused element reads as clearly as a hovered one,
     never relying on the browser's default focus ring against this dark
     background. ============================================================ */
  .skip-link {
    position: fixed;
    top: -4rem;
    left: 1rem;
    z-index: 100;
    padding: 0.7rem 1.3rem;
    background: var(--bg);
    color: var(--gold-bright);
    border: 1px solid var(--gold);
    border-radius: 4px;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-decoration: none;
    transition: top 0.2s ease;
  }
  /* :focus, not :focus-visible — the whole point of a skip link is that
     the act of tabbing to it (always keyboard-driven) reveals it. */
  .skip-link:focus { top: 1rem; }

  /* Reduced-motion door: a quiet link to the static tier. Hidden for everyone
     whose device does not ask for reduced motion — the reveal lives in the
     @media (prefers-reduced-motion: reduce) block below, so the static tier's
     only door is not buried in the rail menu for the visitors who need it. */
  .static-door { display: none; }

  /* --- UI overlay: clicks fall through to the canvas except on controls --- */
  .ui {
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    display: flex;
    flex-direction: column;
  }
  .ui a, .ui button { pointer-events: auto; }

  /* --- nav --- */
  nav {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 2.1rem 3.4rem;
    animation: fade-on 0.9s ease 0.15s both;
  }
  /* THE WORDMARK IS NOW THE MARK (2026-08-11, Hannah: "use it as the logo
     instead of the BANODOCO text ... you may need to change the colour and
     size to align it nicely with the page").
     THE INK: an isometric wireframe B, supplied as glowing amber line art on
     transparency. It is applied as a MASK and painted with `currentColor`
     rather than dropped in as an <img>, for two reasons that are really one.
     (1) STATE. The control opposite it (.pill-ic) is parchment at rest and
     lifts to --gold-bright on hover and keyboard focus, because GOLD IS THIS
     SITE'S LIT STATE — the note above .pill-ic is the long version. A baked
     amber raster is lit at rest and has nowhere to go when you touch it; the
     row would have one control that answers and one that cannot. Masked, the
     B is the same ink as its neighbour and lifts with it, so the two ends of
     the row are peers in colour and in behaviour, not just in size.
     (2) BYTES. Only the alpha channel is needed, so the shipped rasters are
     grayscale+alpha (2.9-9.7 kB) instead of the 28-155 kB colour cutouts.
     The alpha carries the drawing's own glow falloff, so the hairline keeps
     its soft edge — nothing is re-drawn or traced.
     Masking on ALPHA and not on luminance was measured: the source's RGB
     holds a lighting gradient (bright near faces, dark far ones), and a
     luminance mask drops the far faces almost entirely — the drawing came
     apart into an illustration with holes. On alpha, every line is the same
     hairline the .co leaders and the Discord outline are drawn at, which is
     what makes it read as a MARK and not a picture of one.
     THE BOX HOLDS THE ROW'S EDGE. `height` here is the .pill's computed 33px
     — the same number .pill-ic holds and for the same reason: the nav row's
     bottom is the top of the band Inspire's portrait camera is balanced
     against (the PL-1.4 note below). The mark itself is TALLER than 33px and
     overflows this box symmetrically, which costs the layout nothing —
     padding is whitespace, nav's height is still max(33, 33) — so navBottom
     is untouched at every breakpoint and no camera field goes stale.
     SIZE: see the --mark-h note below. */
  .logo {
    --mark-h: 40px;
    /* GOLD IS THE LOGO'S RESTING COLOUR NOW (2026-08-12, Hannah: "Gold becomes
       its resting colour: a lighter gold at rest, deepening to a heavier,
       richer gold on hover ... Pick the two tones by eye against the actual
       background").
       This OVERRIDES 8b29670's call, recorded in the mask note above — that
       the B should rest in parchment and lift to gold because gold is this
       site's lit state, and spending it at rest is what had made the old
       Discord badge shout. Her decision, for this control; the reasoning still
       holds everywhere else and .pill-ic below is untouched.

       THE TWO TONES, shot in the real row on the real background at 1440,
       1280 and 375, at actual size, against the alternatives (parchment .82,
       #f0c877, #eec27f, #e9c489, #e6bd6e, #d9a441, #cf9a33, #e0a838, #e8b04a):

       REST #e6bd6e. "Lighter gold" has a floor — it must still read as gold.
       In Lab the giveaway is b*: the light candidates run #e9c489 at 34.5 and
       #eec27f at 39.5, and both go creamy against this near-black ground —
       they drift back toward the parchment family and look, exactly as she
       warned, washed out. #e6bd6e holds b* 45.1 at L* 78.7: unmistakably gold,
       and still the lighter of the pair.
       It is deliberately NOT --gold-bright. That token is the site's LIT
       state — it is what every other control, .pill-ic included, reaches only
       on hover — so resting the logo in it would leave the mark permanently
       wearing its neighbour's hover colour, and leave the hover nowhere
       brighter to go. A rest tone of its own keeps the token's meaning intact.

       HOVER #e0a838. Every gold-to-gold step is small next to what this
       control used to do (parchment -> gold measured dE 39.3); the widest
       honest step available here is dE 19.2, and this is it. It is also the
       right KIND of step: a*/b* climb 5.2->10.3 and 45.1->62.4 while L* only
       falls 78.7->72.3. So it deepens by SATURATION rather than by going dark,
       which is what "heavier, richer" describes — #d9a441 (dE 15.0) and
       #cf9a33 (dL -11.7) both spend more of the change on dimming and read
       duller rather than richer.
       The glow below is what keeps a DEEPENING hover from reading as a
       dimming one: the ink concentrates as the light around it comes up. On a
       lattice of hairlines that halo carries most of the perceived lift, which
       is why the hover tone can afford to go down in lightness at all. */
    display: inline-flex;
    align-items: center;
    position: relative;                 /* for the hit pad below */
    height: 33px;
    color: #e6bd6e;
    text-decoration: none;
    transition: color 0.25s, filter 0.25s;
  }
  /* THE HIT PAD, AND WHY IT LEFT THE SHARED PL-1.4 BLOCK (see that note near
     the bottom of this file). PL-1.4 pads `.pill, .cta, .logo` to a 44px
     minimum, but only under coarse-pointer / small-viewport conditions, and
     it sizes on `max(100%, 44px)` — the ELEMENT's box. That was exactly right
     while the logo was a word, whose box IS its ink. It is wrong now: the
     mark deliberately overflows its 33px box (that is what holds navBottom),
     so on a desktop pointer the top and bottom 3.5px of the drawing would
     have been visible but not hoverable or clickable — ink you can see and
     cannot hit. So `.logo` keeps its own pad, unconditionally, sized to the
     INK rather than the box. It resolves to 44x44 at both breakpoints today;
     writing the height as max(var(--mark-h), 44px) means it stays correct on
     its own if the mark is ever taken past 44. Everything PL-1.4 guaranteed
     still holds here — this is a superset of it, not an exception to it. */
  .logo::before {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: max(100%, 44px);
    height: max(var(--mark-h), 44px);
    transform: translate(-50%, -50%);
    background: none;                   /* hit area only: it must never paint */
    pointer-events: auto;
  }
  /* THE MARK'S HEIGHT. The word it replaces was 17px of cap height spread
     over 140px of tracked-out width — a long, thin, low-density thing. A mark
     is compact and dense, so matching the word's 17px would have been both
     unreadable (the interior lattice mushes below ~30px) and, oddly, still
     too heavy. Shot in the real row at 30/33/36/40/44/48: under 36 the
     drawing stops being a drawing, over 44 it starts to own the frame and
     crowd the hero beneath it. 40 is where the B is unmistakably the
     three-dimensional object it is and still reads as quiet furniture — it
     overhangs the 33px band by 3.5px top and bottom, which is what gives the
     row its new breath without moving a single layout edge.
     WIDTH is the source's own aspect, 814/1112 = 0.732, so the ink is never
     distorted; at 40px tall the mark is 29px wide, against the Discord
     outline's 20px. Nearly the same footprint at nearly the same weight, at
     opposite ends of the row. */
  .logo .mark {
    display: block;
    width: calc(var(--mark-h) * 0.732);
    height: var(--mark-h);
    background: currentColor;
    -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat;
    -webkit-mask-position: center;  mask-position: center;
    -webkit-mask-size: contain;     mask-size: contain;
    /* Plain url() FIRST, image-set second, deliberately: if a browser cannot
       parse image-set the whole declaration is dropped and the url() above
       still masks. Without the fallback the element would paint as a solid
       parchment RECTANGLE — the one failure mode worth engineering against.
       Resolutions are picked for the RENDERED size, not the file's: 29px of
       ink needs 29/58/87 device px at 1x/2x/3x, so 48/64/96 are the smallest
       masks that stay oversampled at each. */
    -webkit-mask-image: url("./assets/brand/mark-b-mask-48.png");
    mask-image: url("./assets/brand/mark-b-mask-48.png");
    -webkit-mask-image: -webkit-image-set(
      url("./assets/brand/mark-b-mask-48.png") 1x,
      url("./assets/brand/mark-b-mask-64.png") 2x,
      url("./assets/brand/mark-b-mask-96.png") 3x);
    mask-image: image-set(
      url("./assets/brand/mark-b-mask-48.png") 1x,
      url("./assets/brand/mark-b-mask-64.png") 2x,
      url("./assets/brand/mark-b-mask-96.png") 3x);
  }
  /* (The rest ALPHA this note used to explain is gone with the parchment: the
     0.82 that matched the B's lattice to .pill-ic's closed outline was solving
     a problem — two controls sharing one ink — that the gold rest ends. The
     tone is now opaque and chosen on its own terms; see the block above.)
     THE PAIR IS NO LONGER MATCHED, AND THAT IS THE KNOWN COST. .pill-ic still
     rests in parchment and lifts to --gold-bright, so at rest the row is one
     gold mark and one parchment mark, and on hover the two lift in opposite
     directions — the logo deepens, the Discord mark brightens. Both readings
     are recorded in 25-navigation-redux.md (2026-08-12) for whenever the pair
     is looked at again; nothing here presumes the answer. */
  .logo:is(:hover, :focus-visible) {
    color: #e0a838;
    filter: drop-shadow(0 0 5px rgba(240, 190, 90, 0.55));
  }
  /* THE RING GOES ON THE INK, the same call 5762167 made for .pill-ic and for
     the same reason, arrived at from the opposite direction. There the <a> was
     TALLER than its glyph and the shared `nav a:focus-visible` ring came out a
     portrait rectangle round a wide little face; here the <a> is SHORTER than
     its mark — 33px of box under 40px of drawing — so the shared ring would
     have cut across the B, crossing the artwork it is meant to indicate.
     Neither the box nor the mark itself can carry it: `mask` clips an element's
     own outline away with everything else it paints, so a ring on .mark would
     simply not draw. A pseudo-element sized to the ink is drawn OUTSIDE the
     masked box and hugs the drawing exactly (29x40 at the shared 3px offset).
     Same colour, same width, same offset as every other control in the row. */
  .logo:focus-visible { outline: none; }
  .logo:focus-visible::after {
    content: "";
    position: absolute;
    left: 50%; top: 50%;
    width: calc(var(--mark-h) * 0.732);
    height: var(--mark-h);
    transform: translate(-50%, -50%);
    outline: 2px solid var(--gold-bright);
    outline-offset: 3px;
    border-radius: 2px;
    pointer-events: none;
  }
  .nav-cta { display: flex; gap: 0.7rem; }
  .pill {
    color: var(--ink);
    text-decoration: none;
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.04em;
    padding: 0.5rem 1.35rem;
    border: 1px solid rgba(242, 237, 225, 0.58);
    border-radius: 999px;
    transition: border-color 0.25s, background 0.25s;
  }
  .pill:hover { border-color: var(--ink); background: rgba(242, 237, 225, 0.06); }
  /* THE DISCORD MARK — AN OUTLINE (2026-08-11, Hannah: "make it be an outline
     of the Discord logo ... right now it's excessively dominant, especially on
     mobile. Make it ELEGANT." Supersedes the filled-glyph-in-a-ringed-disc of
     58a63c3.)
     WHAT WAS WRONG: a solid glyph inside a lit circle is TWO enclosed shapes,
     and they were the only opaque things on a page whose entire language is
     fine glowing line — the wireframe specimen, the .co leaders, the .cta's
     hairline. It read as an app badge stuck onto the frame, and on a phone,
     where the row is proportionally larger and the scene behind it thinner,
     it took the top of the composition.
     WHAT IT IS NOW: no disc, no ground, no fill. The clyde is STROKED — the
     real mark's own path, never redrawn, just unfilled — so the control IS
     the mark, at the same 1px the .co leader lines are drawn at
     (vector-effect keeps that 1px true whatever the glyph is scaled to;
     overflow must be visible or the box clips the outer half of the stroke).
     Parchment at rest, a shade under the wordmark it stands beside, because
     GOLD IS THIS SITE'S LIT STATE — spending it at rest is most of what made
     the old badge shout. Hover and keyboard focus lift it to --gold-bright
     with the .co .lit drop-shadow (a filter now, not a box-shadow: there is
     no box left to light), on the nav row's existing 0.25s.
     THE BOX STILL HOLDS THE ROW'S EDGE — height is EXACTLY the text pill's
     computed 33px (28.44px portrait), because the nav row's bottom is the top
     of the band Inspire's portrait camera is balanced against (the PL-1.4
     note below). Only the INK moved. Width is now the ink's own width rather
     than a 33px square, so the mark sits flush to the 3.4rem margin the
     logo starts at — with the circle gone, the ink is the visual edge.
     The 44px touch minimum stays where it always was, on the PL-1.4 pad.

     REBALANCED 20px -> 26px, 2026-08-11, when the wordmark became the mark.
     Hannah, on seeing the B: "maybe the Discord logo needs a ring around it
     in the top right or something." The instinct was right and the remedy
     was not — see the ring note below. What she was reading is that the row
     changed underneath this control: it used to answer a 140x17 word, a long
     thin low-density thing, and it now answers a 29x40 compact drawing. At
     20px the clyde had become the smaller order of object in the row, and
     the eye stopped treating the two ends as a pair. 26px is where they read
     as peers again. Nothing else about the control moves: still the official
     path, still unfilled, still stroked at the 1px the .co leaders use, still
     no disc, still flush to the margin, still a 33px box. This is a size, not
     a return of the badge — 26px of open outline is a long way from the lit
     filled disc of 58a63c3 that she called excessively dominant. */
  .pill-ic {
    display: inline-flex;
    align-items: center;
    justify-content: flex-end;
    width: 26px;
    height: 33px;
    padding: 0;
    /* border: 0, not a transparent border — the base .pill's 1px is still
       LAYOUT under box-sizing: border-box, so a transparent one silently ate
       2px off the ink and pushed the mark a pixel off the margin. */
    border: 0;
    background: none;
    color: rgba(242, 237, 225, 0.74);   /* see the note above the hover rule */
    transition: color 0.25s, filter 0.25s;
  }
  .pill-ic svg {
    width: 26px;
    height: auto;
    display: block;
    overflow: visible;                  /* else the box clips the stroke */
  }
  .pill-ic svg path {
    fill: none;
    stroke: currentColor;
    stroke-width: 1;                    /* .co .lead / .co .lit weight */
    vector-effect: non-scaling-stroke;
    stroke-linejoin: round;
  }
  /* THE RING: TRIED, SHOT, REJECTED (2026-08-11).
     Hannah asked for one — "maybe the Discord logo needs a ring around it in
     the top right or something" — and the case for it had genuinely changed
     since 5762167 took the old one off: both ends of the row are marks now,
     not a mark and a word, so a ring could have read as a matched FRAME
     rather than as a badge. It was worth shooting rather than assuming.
     Shot at 30, 34 and 40px diameter, hairline, unfilled, same currentColor,
     beside the 40px B at 1440 and at 375. All three failed, the same way:
     · A circle is a CLOSED shape. After 5762167 there is not one enclosed
       form left in this header — the B is open wireframe, the clyde is open
       outline, the .co leaders and the .cta are open line. The ring puts the
       only closed contour on the page back in the corner it was removed from,
       and it reads as a button again. That IS the dominance she objected to,
       arriving by a slightly quieter road.
     · It inverts the hierarchy. At 34 and 40 the eye goes to the RING before
       it goes to the logo, because a circle in an otherwise angular field is
       the strongest figure in it. The logo has to lead the row.
     · The geometries fight. The B is an isometric wireframe — all 30/60/90
       edges and flat faces. A true circle shares no vocabulary with it; the
       pair looked assembled from two different kits.
     What she was actually seeing — a right end gone light against the new
     left end — is real, and is fixed by the 20 -> 26px rebalance above, which
     buys the weight without buying an enclosing shape. */

  /* REST ALPHA, measured not guessed: shot at 0.58 / 0.66 / 0.74 / 0.82 in
     the real row at actual size, 1440 and 375@2x. 0.58 is the old .pill
     BORDER's alpha and was the tempting answer, but that pill also held a
     FULL-parchment word — matching only the border undersells the control and
     it goes ghostly. 0.82 started to rival the WORD that used to stand
     opposite it, and 0.74 was where this stroke read at the same density as
     BANODOCO's letter strokes, so the two sat in the row as peers. The word is
     gone (2026-08-11) and 0.74 still holds against what replaced it: the B is
     painted at 0.82 because a lattice of many thin lines reads lighter than
     one closed outline at the same alpha. Both ends measured, in the row, at
     actual size — the point is unchanged, a quiet hairline beside a quiet
     hairline, not a badge beside anything.
     The base .pill's hover paints a border and a wash; there is no box here. */
  .pill-ic:is(:hover, :focus-visible) {
    background: none;
    color: var(--gold-bright);
    filter: drop-shadow(0 0 5px rgba(240, 190, 90, 0.55));
  }
  /* keyboard focus, gold family — matches journey/site.css's treatment for
     every journey-layer control (.j-rail / .j-menu :focus-visible etc).
     Covers .logo and the .pill link (Discord — 2RP left the header
     2026-08-10, by request); the chapter list is no
     longer in this row at all (it is the side navigator, journey/rail.js). */
  nav a:focus-visible {
    outline: 2px solid var(--gold-bright);
    outline-offset: 3px;
    border-radius: 2px;
  }
  /* THE RING GOES ON THE GLYPH, NOT THE BOX. The shared rule above draws on
     the <a>, which is 20x33 — it has to be, the row's bottom edge is camera-
     balanced — while the mark inside it is 20x15. Drawn on the box, the ring
     came out a tall portrait rectangle around a wide little face: it read as
     a button, which is the enclosing-shape problem this whole pass removed,
     handed back on focus. Drawn on the svg it hugs the ink (26x21 with the
     shared 3px offset) and the control still looks like a mark. Same colour,
     same width, same offset — only the element it hangs on changed. */
  .pill-ic:focus-visible { outline: none; }
  .pill-ic:focus-visible svg {
    outline: 2px solid var(--gold-bright);
    outline-offset: 3px;
    border-radius: 2px;
  }

  /* --- hero --- */
  .hero {
    flex: 1;
    display: flex;
    flex-direction: column;
    justify-content: center;
    max-width: 44rem;
    padding: 0 0 12vh 5.2vw;
  }
  h1 {
    margin: 0 0 1.35rem;
    font-family: "Didot", "Playfair Display", Georgia, "Times New Roman", serif;
    font-weight: 400;
    font-size: clamp(2.5rem, 4.4vw, 4.25rem);
    line-height: 1.1;
    letter-spacing: 0.005em;
    color: var(--parchment);
    text-shadow: 0 2px 30px rgba(10, 6, 2, 0.9), 0 0 8px rgba(10, 6, 2, 0.6);
  }
  h1 .hl {
    display: block;
    white-space: nowrap; /* each line is set deliberately — never rewrap */
    /* hero copy arrives with the page itself — the same quiet fade as the
       nav, no choreography; the specimen draws behind it */
    animation: fade-on 0.9s ease 0.15s both;
  }
  h1 em {
    font-style: italic;
    letter-spacing: 0.01em;
  }
  .sub {
    margin: 0 0 2.2rem;
    max-width: 24rem;
    font-size: 1.06rem;
    line-height: 1.6;
    font-weight: 400;
    color: var(--muted);
    text-shadow: 0 1px 18px rgba(10, 6, 2, 0.9), 0 0 6px rgba(10, 6, 2, 0.7);
    animation: fade-on 0.9s ease 0.15s both;
  }
  /* serif body A/B — toggle with the B key or ?body=serif */
  body.body-serif .sub {
    font-family: "EB Garamond", Garamond, "Iowan Old Style", Georgia, serif;
    font-size: 1.13rem;
    line-height: 1.65;
  }
  .cta {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    width: fit-content;
    color: var(--gold-bright);
    text-decoration: none;
    font-size: 0.8rem;
    font-weight: 700;
    letter-spacing: 0.24em;
    padding: 1.02rem 1.9rem;
    border: 1px solid rgba(217, 164, 65, 0.8);
    outline: 1px solid rgba(217, 164, 65, 0.35);
    outline-offset: 3px;
    border-radius: 999px;
    background: rgba(14, 9, 2, 0.4);
    transition: border-color 0.15s, outline-color 0.15s, background 0.15s, box-shadow 0.15s;
    animation: fade-on 0.9s ease 0.15s both;
  }
  .cta .arr {
    display: inline-block;
    transition: transform 0.15s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .cta-short { display: none; }
  .cta:hover {
    border-color: var(--gold-bright);
    outline-color: rgba(240, 200, 119, 0.55);
    background: rgba(28, 18, 4, 0.6);
    box-shadow: 0 0 28px rgba(230, 175, 70, 0.28), inset 0 0 14px rgba(230, 175, 70, 0.08);
  }
  .cta:hover .arr { transform: translateX(3px); }
  /* keyboard focus: .cta already carries a permanent decorative outline
     (above), so focus needs a visibly DIFFERENT ring, not the same one —
     brighter, offset further out, same gold-bright as every other focus
     treatment in this codebase. */
  .cta:focus-visible {
    outline: 2px solid var(--gold-bright);
    outline-offset: 5px;
    border-radius: 999px;
  }

  @keyframes fade-on {
    from { opacity: 0; }
    to   { opacity: 1; }
  }

  /* --- HUD callouts: world-tracked annotations on the specimen ---
     idle: barely-there hairlines aligned to the anatomy.
     hover: the leader lights up node→label, the ring pulses, the
     label catches the light. Minority Report, but candlelit. */
  .callouts {
    position: fixed;
    inset: 0;
    z-index: 1;
    pointer-events: none;
    /* Navigation iteration: the permanently expanded journey rail replaces
       these three specimen labels as the hero's navigation surface. Keep the
       anchor markup for a reversible comparison, but remove it from view. */
    display: none;
  }
  .callout {
    position: absolute;
    top: 0;
    left: 0;
    will-change: transform;
  }
  /* --- callout entry: once the specimen finishes drawing (4.2s), the three
     annotations boot up in NUMBERED order — 01, 02, 03 — each a little
     instrument powering on: node pops, ring pings once, leader draws itself
     node->label, label fades in as its number flickers alive. Every part
     hangs off one per-callout delay (--d), and every keyframe ENDS at the
     resting style, so reduced-motion / ?nointro just shows the final state. */
  #co-inspire { --d: 5.55s; }
  #co-equip   { --d: 6.20s; }
  #co-connect { --d: 6.85s; }
  .co { animation: co-on 0.2s ease both; animation-delay: var(--d); }
  @keyframes co-on { from { opacity: 0; } to { opacity: 1; } }
  .co .core {
    animation: core-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
    animation-delay: var(--d);
  }
  @keyframes core-pop {
    0%   { transform: scale(0); }
    55%  { transform: scale(1.7); box-shadow: 0 0 12px rgba(255, 200, 100, 0.9); }
    100% { transform: scale(1); }
  }
  .co .ring {
    animation: ring-ping 0.7s ease-out both;
    animation-delay: calc(var(--d) + 0.08s);
  }
  @keyframes ring-ping {
    0%   { opacity: 0.9; transform: scale(0.35); }
    100% { opacity: 0;   transform: scale(1.5); }
  }
  .co .lead {
    stroke-dasharray: 100;
    animation: lead-draw 0.4s ease both;
    animation-delay: calc(var(--d) + 0.1s);
  }
  @keyframes lead-draw { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }
  .co .tag {
    animation: tag-in 0.4s ease both;
    animation-delay: calc(var(--d) + 0.2s);
  }
  @keyframes tag-in {
    from { opacity: 0; letter-spacing: 0.4em; }
    to   { opacity: 1; letter-spacing: 0.3em; }
  }
  .co .tag .no {
    animation: no-flicker 0.5s steps(1, end) both;
    animation-delay: calc(var(--d) + 0.2s);
  }
  @keyframes no-flicker {
    0%   { opacity: 0; }
    30%  { opacity: 1; }
    45%  { opacity: 0.35; }
    60%  { opacity: 1; }
    100% { opacity: 1; }
  }

  .co .core {
    position: absolute;
    width: 5px; height: 5px;
    margin: -2.5px 0 0 -2.5px;
    border-radius: 50%;
    background: rgba(240, 200, 119, 0.86);
    box-shadow: 0 0 7px rgba(240, 190, 90, 0.5);
    transition: background 0.3s, box-shadow 0.45s;
  }
  .co .ring {
    position: absolute;
    width: 22px; height: 22px;
    margin: -11px 0 0 -11px;
    border: 1px solid rgba(240, 200, 119, 0.9);
    border-radius: 50%;
    opacity: 0;
    transform: scale(0.4);
    transition: opacity 0.35s, transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .co svg {
    position: absolute;
    top: 0; left: 0;
    width: 1px; height: 1px;
    overflow: visible;
  }
  .co .lead {
    fill: none;
    stroke: rgba(226, 178, 84, 0.6);
    stroke-width: 1;
    filter: drop-shadow(0 0 2px rgba(240, 190, 90, 0.35));
    transition: stroke 0.45s;
  }
  .co .lit {
    fill: none;
    stroke: rgba(255, 216, 140, 0.95);
    stroke-width: 1;
    stroke-dasharray: 100;
    stroke-dashoffset: 100;
    filter: drop-shadow(0 0 4px rgba(240, 190, 90, 0.8));
    transition: stroke-dashoffset 0.45s cubic-bezier(0.22, 1, 0.36, 1);
  }
  /* Tags are ONE typographic system (2026-08-16): right-aligned rows on a
     shared right rail (layoutRail() in main.js pins every tag's right edge
     to the nav content inset). No visible box — a localized dark vignette
     (::before) sits behind each label so it stays legible over particles
     and the bright root network without reading as UI chrome. */
  .co .tag {
    position: absolute;
    display: flex;
    flex-direction: column;
    align-items: flex-end;
    pointer-events: auto;
    padding: 0.5rem 0.7rem;
    text-decoration: none;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.3em;
    white-space: nowrap;
    color: rgba(218, 207, 181, 0.72);
    border-radius: 2px;
    text-shadow: 0 1px 10px rgba(10, 6, 2, 0.9), 0 0 7px rgba(230, 178, 82, 0.16);
    transition: color 0.4s, letter-spacing 0.4s, text-shadow 0.4s;
  }
  .co .tag::before {
    content: '';
    position: absolute;
    inset: -30px -44px;
    z-index: -1;
    pointer-events: none;
    border-radius: 50%;
    background: radial-gradient(closest-side,
      rgba(9, 6, 2, 0.62),
      rgba(9, 6, 2, 0.34) 55%,
      transparent 100%);
    opacity: 0.75;
    transition: opacity 0.4s;
  }
  .co .tag .row {
    display: flex;
    align-items: baseline;
    gap: 0.55em;
    margin-right: -0.3em; /* absorb the trailing letter-space so text centres true */
  }
  .co .tag .no {
    font-size: 0.5rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    color: rgba(226, 176, 78, 0.55);
    transition: color 0.4s;
  }
  /* corner brackets: hidden at rest. On hover they LOCK IN clockwise —
     tl, tr, br, bl — each snapping to its corner with a small overshoot,
     like a reticle acquiring the label. Deactivation snaps back as one. */
  .co .ck {
    position: absolute;
    width: 7px; height: 7px;
    opacity: 0;
    pointer-events: none;
    --ckc: rgba(240, 200, 119, 0.85);
    transition: opacity 0.14s, transform 0.2s cubic-bezier(0.22, 1, 0.36, 1);
  }
  .co .ck.tl { top: 0;    left: 0;  border-top: 1px solid var(--ckc);    border-left: 1px solid var(--ckc);  transform: translate(7px, 7px)   scale(0.4); }
  .co .ck.tr { top: 0;    right: 0; border-top: 1px solid var(--ckc);    border-right: 1px solid var(--ckc); transform: translate(-7px, 7px)  scale(0.4); }
  .co .ck.br { bottom: 0; right: 0; border-bottom: 1px solid var(--ckc); border-right: 1px solid var(--ckc); transform: translate(-7px, -7px) scale(0.4); }
  .co .ck.bl { bottom: 0; left: 0;  border-bottom: 1px solid var(--ckc); border-left: 1px solid var(--ckc);  transform: translate(7px, -7px)  scale(0.4); }
  .co:is(:hover, .force) .core {
    background: rgba(255, 226, 160, 1);
    box-shadow: 0 0 10px rgba(255, 200, 100, 0.9), 0 0 26px rgba(240, 170, 60, 0.5);
  }
  .co:is(:hover, .force) .ring {
    opacity: 1;
    transform: scale(1);
    animation: co-pulse 1.7s cubic-bezier(0.25, 0.6, 0.4, 1) 0.35s infinite;
  }
  @keyframes co-pulse {
    0%   { opacity: 0.85; transform: scale(0.55); }
    70%  { opacity: 0;    transform: scale(1.45); }
    100% { opacity: 0;    transform: scale(1.45); }
  }
  .co:is(:hover, .force) .lit { stroke-dashoffset: 0; }
  .co:is(:hover, .force) .lead { stroke: rgba(240, 190, 90, 0.76); }
  .co:is(:hover, .force) .tag {
    color: var(--gold-bright);
    letter-spacing: 0.34em;
    text-shadow: 0 0 12px rgba(240, 190, 90, 0.55);
  }
  .co:is(:hover, .force) .tag::before { opacity: 1; }
  .co:is(:hover, .force) .tag .no { color: rgba(240, 200, 119, 0.85); }
  .co:is(:hover, .force) .ck {
    opacity: 1;
    transform: translate(0, 0) scale(1);
    transition: opacity 0.12s, transform 0.3s cubic-bezier(0.34, 1.56, 0.64, 1);
  }
  .co:is(:hover, .force) .ck.tr { transition-delay: 0.05s; }
  .co:is(:hover, .force) .ck.br { transition-delay: 0.10s; }
  .co:is(:hover, .force) .ck.bl { transition-delay: 0.15s; }

  .co .tag .soon {
    display: block;
    max-height: 0;
    margin-top: 0;
    overflow: hidden;
    opacity: 0;
    font-family: "Didot", "Playfair Display", Georgia, serif;
    font-style: italic;
    font-weight: 400;
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    color: rgba(228, 182, 102, 0.9);
    transition: max-height 0.4s cubic-bezier(0.22, 1, 0.36, 1),
                margin-top 0.4s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.3s;
  }
  .co:is(:hover, .force) .tag .soon {
    max-height: 1.4em;
    margin-top: 0.28em;
    opacity: 1;
    transition-delay: 0.1s;
  }

  /* per-callout leader geometry RETIRED (2026-08-16): the tag offsets and
     the std/alt/tall leader variants that used to live here (and in the
     mobile / short-height blocks below) were hand-tuned per breakpoint,
     which is exactly why the three labels never shared an edge or a
     rhythm. layoutRail() in main.js now owns ALL of it — it pins every
     tag's right edge to one shared rail (the nav content inset), floats
     each label into the dark band above its node, and routes each leader
     node -> 45° diagonal -> horizontal run into the label. The hero
     camera is bit-exact static (see organism/furniture.js), so this is a
     resize-time computation, not a per-frame one. */

  /* --- touch affordance: no hover on touch devices, so idle state gets a
     visibility boost and taps toggle the lit state via JS (.force class) --- */
  @media (hover: none) {
    .co .tag { color: rgba(218, 207, 181, 0.78); }
    .co .lead { stroke: rgba(230, 182, 88, 0.66); }
    .co .core { background: rgba(248, 211, 132, 0.92); }
  }

  /* ============================================================
     RESPONSIVE — stacked portrait layout (tablet + mobile)
     ============================================================ */
  @media (max-width: 900px) and (orientation: portrait) {
    .hero {
      justify-content: flex-start;
      /* 6vh -> 4vh (2026-08-17, Hannah's label-visibility pass): at 768x1024
         the CTA's right end kissed the cap's left rim. ~20px of lift clears
         it; the phone block below still owns its own 5vh, so only the tablet
         band moves. */
      padding: 4vh 7vw 0;
      max-width: none;
    }
    h1 { font-size: clamp(2.1rem, 6.2vw, 3.4rem); }
    /* The supporting line used to take the full 24rem measure, which left a
       long first line and a short second line on both 430px phones and portrait
       tablets. A 20rem balanced measure makes the two lines read as one quiet
       centred thought without hard-coding a breakpoint-specific <br>. */
    .sub {
      max-width: 20rem;
      font-size: 1rem;
      text-wrap: balance;
    }
    .scrim {
      background: linear-gradient(180deg,
        rgba(14, 10, 4, 0.6) 0%,
        rgba(14, 10, 4, 0.25) 38%,
        transparent 62%);
    }
    .vignette {
      background: radial-gradient(ellipse 130% 100% at 50% 62%,
        transparent 40%,
        rgba(8, 5, 1, 0.3) 78%,
        rgba(6, 4, 1, 0.55) 100%);
    }
    .spill {
      background: radial-gradient(ellipse 90% 50% at 50% 62%, rgba(255, 190, 95, 0.07), transparent 70%);
    }
    nav { padding: 1.6rem 7vw; }
    /* THE CTA LEAVES EVERY PORTRAIT FRAME (2026-08-17, Hannah: "the way on
       mobile we hide the main button and centre align the mushroom with the
       labels — do the same on tablet"). Same reasoning as the phone pass:
       the journey keeps three doors (scroll, the rail's tiles, the callout
       tags' tap-to-travel in main.js), and the freed band lets the
       specimen+labels ensemble centre as one composition (VIEWS.tablet /
       RAIL.tablet in main.js carry the tablet half of that pass). */
    .cta { display: none; }
    .cta-full { display: none; }
    .cta-short { display: inline; }
    /* THE COPY JOINS THE AXIS (2026-08-17, Hannah: "what about centreing the
       text?"): with the specimen+labels ensemble centred and the CTA gone,
       the left-set copy was the last asymmetric element on a symmetric
       frame. Centring it puts headline, sub, cap, stem and root glow on one
       vertical axis — the four-line headline rags symmetrically and the
       italic "thrive." closes the block. Portrait frames only: desktop and
       landscape keep the side-by-side composition, where left-set text IS
       the design. */
    .hero { text-align: center; }
    h1, .sub { margin-left: auto; margin-right: auto; }
  }

  /* ============================================================
     RESPONSIVE — mobile portrait
     ============================================================ */
  @media (max-width: 620px) and (orientation: portrait) {
    nav { padding: 1.3rem 6vw; }
    /* HEIGHT is the .pill's computed 28.44px at this query, the number that
       holds navBottom at 70.03125 — same contract .pill-ic keeps below. The
       MARK steps 40 -> 34 (-15%), close to the step the word it replaced took
       (1.05rem -> 0.92rem, -12%): a phone header is proportionally taller, so
       the mark may take more of the row here than it does at 1440, but not
       much more. Shot at 30/32/34/36 at actual size on a 375 frame — 30 and
       32 go timid and the lattice starts to close up, 36 fills the row edge
       to edge. 34 keeps the drawing legible and the row still airy, which
       matters most HERE: mobile is where Hannah found the old header
       "excessively dominant". */
    .logo { --mark-h: 34px; height: 28.4375px; }
    .pill { padding: 0.42rem 1rem; font-size: 0.72rem; }
    /* HEIGHT is still the text pill's computed 28.44px at this query — the
       row's bottom edge must not move, whatever the ink does. Width follows
       the ink so the mark sits flush to the 6vw margin.
       The ink steps down with the mark beside it — 26 -> 22 here is -15%, the
       same step .logo's 40 -> 34 takes — which keeps the two ends in the same
       proportion to each other at every breakpoint, and keeps this control
       modest on the frame where Hannah felt the old badge worst. A 1px stroke
       on a SMALLER glyph is a BIGGER fraction of it, so the stroke comes down
       with it — 0.9px here keeps the line optically the same hairline it is at
       1440 instead of letting it coarsen on the smallest frame. */
    .pill-ic { width: 22px; height: 28.44px; padding: 0; }
    .pill-ic svg { width: 22px; }
    .pill-ic svg path { stroke-width: 0.9; }
    /* 5vh -> 7vh (2026-08-17, Hannah's vertical rebalance): with the CTA
       gone the copy ended ~100px above the cap while sitting only ~40px
       under the nav. The copy drops ~16px and the camera lifts the specimen
       ~20px (viewFor in main.js), which splits the frame's air roughly
       evenly above and below the text block. */
    .hero { padding: 7vh 7vw 0; }
    h1 { font-size: clamp(1.95rem, 8.3vw, 2.8rem); margin-bottom: 1.1rem; }
    .sub { font-size: 0.98rem; margin-bottom: 1.8rem; }
    /* THE CTA LEAVES THE PHONE FRAME (2026-08-17, Hannah: "just remove the
       explore button on mobile"; later the same day the tablet joined it —
       the hide now lives in the ≤900 portrait block above, which covers
       this one too). */

    .co { transform: scale(0.88); transform-origin: 0 0; }
    .co .tag { font-size: 0.6rem; }
    /* leader/tag geometry: layoutRail() in main.js (rail rises are
       per-mode there; it divides out this 0.88 scale) */

    /* small or short phones: tighten the text stack */
    @media (max-height: 700px) {
      nav { padding: 1rem 6vw; }
      /* 3vh -> 4.5vh: the same rebalance, scaled for the short frame */
      .hero { padding-top: 4.5vh; }
      h1 { font-size: clamp(1.65rem, 7.4vw, 2.2rem); margin-bottom: 0.85rem; }
      .sub { font-size: 0.88rem; margin-bottom: 1.3rem; line-height: 1.5; }
    }
  }

  /* ============================================================
     RESPONSIVE — short-height (phone landscape / short windows):
     keep the desktop side-by-side composition, just compress it
     ============================================================ */
  @media (max-height: 560px) {
    nav { padding: 1.1rem 3.4rem; }
    h1 { font-size: clamp(1.7rem, 3.4vw, 2.6rem); margin-bottom: 0.9rem; }
    .sub { margin-bottom: 1.3rem; font-size: 0.95rem; }
    .cta { padding: 0.8rem 1.4rem; }
    .hero { padding-bottom: 5vh; }
    /* leader/tag geometry: layoutRail() in main.js (compact rises are
       shorter there so EQUIP and CONNECT — whose nodes sit only ~40px
       apart in this squashed frame — still separate vertically) */
  }

  /* ============================================================
     PL-1.4 IN THE HERO ROW (2026-08-07 mobile pass).

     The journey layer has carried 44px minimum touch targets since
     PL-1.4 (journey/site.css), by the same method used here: a
     transparent pseudo-element centred on the control, sized to
     max(its own box, 44px), so the HIT AREA reaches the minimum
     while the artwork renders at exactly the size it was designed
     at. The hero's own row was never brought into that rule and
     was the last thing on any mobile screen still under the line —
     measured at 375x812 and 430x932, `Discord` is 80x28 (and `2RP`,
     still in the row when this was measured, 58x28 — it left the
     header 2026-08-10), i.e. 16px short in height on every single view of the
     site, since the row is fixed and rides the whole journey.
     `.cta` measured 328x43 on a landscape phone, 1px short, and
     the then-wordmark `.logo` — a real link to the site root, live at
     every chapter — measured 122x17.

     Deliberately a pad and not a bigger pill: growing the pills
     would move the nav row's bottom edge, and that edge is the top
     of the band Inspire's portrait camera is balanced against
     (journey/portrait.js, the p 0.260 key). A transparent pad
     changes no furniture, so no camera field goes stale and no
     captured reference can move — .ui is hidden at capture in any
     case, but the point is that nothing MOVED, not that nothing
     was photographed.

     Same media condition as the journey layer's PL-1.4 block, so
     the two halves of one rule can never drift apart.
     ============================================================ */
  @media (pointer: coarse), (max-width: 720px), (max-height: 560px) {
    /* `.logo` LEFT THIS LIST 2026-08-11: its ink now overflows its box, so a
       pad sized on the box is the wrong pad and a pad that only exists on
       coarse pointers is the wrong scope. It carries its own, unconditional,
       sized to the ink — see the note on `.logo::before` above. It is still
       >= 44x44 at every breakpoint, so PL-1.4's guarantee is unbroken; it is
       simply enforced in one place instead of two. */
    .pill, .cta { position: relative; }
    .pill::before, .cta::before {
      content: "";
      position: absolute;
      left: 50%; top: 50%;
      width: max(100%, 44px);
      height: max(100%, 44px);
      transform: translate(-50%, -50%);
      background: none;          /* hit area only: it must never paint */
      pointer-events: auto;
    }
    /* THE CALLOUT TAGS JOIN PL-1.4 (2026-08-17, Hannah's tap-gesture pass):
       the three world-tracked tags were never brought under the rule — a
       ~24px-tall box on the control that now carries tap-to-light and
       tap-again-to-travel (main.js). ::before is taken by the tag's
       legibility vignette, so the pad rides ::after. Same recipe otherwise:
       centred, sized to max(box, 44px), paints nothing, moves no layout
       edge — the leader geometry (layoutRail) reads the tag's own box and
       cannot see this. */
    .co .tag::after {
      content: "";
      position: absolute;
      left: 50%; top: 50%;
      width: max(100%, 44px);
      height: max(100%, 44px);
      transform: translate(-50%, -50%);
      background: none;          /* hit area only: it must never paint */
      pointer-events: auto;
    }
  }

  @media (prefers-reduced-motion: reduce) {
    #stage, .spill, nav, h1 .hl, .sub, .cta, .co, .co *, .co:is(:hover, .force) .ring { animation: none; }
    .co { opacity: 1; }
    .cta .arr { transition: none; }
    .co .lit, .co .ring, .co .tag, .co .ck { transition: none; }
    /* Both marks' warm-up is a transition ENDING at its resting or lit style,
       so switching it off leaves both states intact and instant (colour + the
       lit drop-shadow — there is no box-shadow on either of them now). .logo
       joined this list when it stopped being a word and started carrying the
       same colour+filter lift its neighbour does. */
    .pill-ic, .logo { transition: none; }
  }

  /* The static tier is the reduced-motion escape hatch. Its only other door is
     inside the rail's menu panel, so a reduced-motion visitor needs a visible
     one in the tab order. Fixed to the bottom-left corner — clear of the
     callouts and the CTA at every breakpoint — in the page's own quiet
     parchment furniture (small tracked type, .pill's hairline border). */
  @media (prefers-reduced-motion: reduce) {
    .static-door {
      display: block;
      position: fixed;
      left: 1rem;
      bottom: 1rem;
      z-index: 100;
      padding: 0.5rem 1rem;
      color: var(--muted);
      background: var(--bg);
      border: 1px solid rgba(242, 237, 225, 0.58);
      border-radius: 4px;
      font-size: 0.72rem;
      font-weight: 600;
      letter-spacing: 0.08em;
      text-decoration: none;
    }
    .static-door:is(:hover, :focus-visible) {
      color: var(--gold-bright);
      border-color: var(--gold);
    }
  }

/* --- intro fast-forward (scroll during the entry choreography): the CSS
   half of the intro compresses to match the clock-skewed scene grow-in
   (organism/intro.js accelerate() — main.js adds body.intro-fast when the
   skew engages). The 01/02/03 callouts keep a quick version of their 1-2-3
   sequence. --- */
body.intro-fast .spill { animation-delay: 0.1s !important; animation-duration: 0.35s !important; }
body.intro-fast .co { animation-duration: 0.16s !important; }
body.intro-fast #co-inspire { --d: 0.30s; }
body.intro-fast #co-equip   { --d: 0.44s; }
body.intro-fast #co-connect { --d: 0.58s; }

/* A direct CTA/callout entry can arrive while journey.js is still being
   prepared. This short-lived class acknowledges that navigation immediately;
   early wheel/touch/key gestures deliberately do NOT receive it, because the
   journey's p-driven Mission envelope must fade those with actual travel. */
body.intro-depart .ui .hero,
body.intro-depart .callouts {
  opacity: 0 !important;
  pointer-events: none !important;
  transition: opacity 0.18s ease-out;
}
body.intro-depart .ui .hero {
  transform: translateY(-0.3rem);
  transition: opacity 0.18s ease-out, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1);
}
body.intro-depart.intro-restore .ui .hero,
body.intro-depart.intro-restore .callouts {
  opacity: 1 !important;
}
body.intro-depart.intro-restore .ui .hero {
  transform: translateY(0);
}
