/* One Night in the Storybook Sky: tokens, fonts, layout. Spec: docs/creative-direction.md
   The page NEVER scrolls. The stage is fixed to the dynamic viewport (100dvh) and every scene is sized
   from it, so each scene fits one screen at every viewport. Navigation is scene-to-scene (js/scenes.js). */

@font-face { font-family: "Pinyon Script"; src: url("../fonts/pinyon-script-latin-400-normal.woff2?v=3e1f69a2") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Cinzel"; src: url("../fonts/cinzel-latin-400-normal.woff2?v=b873cdd9") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Cinzel"; src: url("../fonts/cinzel-latin-600-normal.woff2?v=61efc27c") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Cormorant Garamond"; src: url("../fonts/cormorant-garamond-latin-400-normal.woff2?v=8048ac20") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Cormorant Garamond"; src: url("../fonts/cormorant-garamond-latin-400-italic.woff2?v=e48e5802") format("woff2"); font-weight: 400; font-style: italic; font-display: swap; }
@font-face { font-family: "Cormorant Garamond"; src: url("../fonts/cormorant-garamond-latin-500-italic.woff2?v=4fb55a7a") format("woff2"); font-weight: 500; font-style: italic; font-display: swap; }
@font-face { font-family: "Cormorant Garamond"; src: url("../fonts/cormorant-garamond-latin-600-normal.woff2?v=ae062b6d") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }
@font-face { font-family: "Josefin Sans"; src: url("../fonts/josefin-sans-latin-300-normal.woff2?v=c42b4374") format("woff2"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Josefin Sans"; src: url("../fonts/josefin-sans-latin-400-normal.woff2?v=242e9878") format("woff2"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Josefin Sans"; src: url("../fonts/josefin-sans-latin-600-normal.woff2?v=56c4c278") format("woff2"); font-weight: 600; font-style: normal; font-display: swap; }

:root {
  --night-900: #0B1030;
  --night-700: #1A2257;
  --periwinkle: #6E7FD0;
  --lavender: #B8B2EA;
  --blush: #F4D6E4;
  --cream: #FFF5E4;
  --powder: #CFE1F6;
  --ink: #1D2C5E;
  --gold-500: #D4A24C;
  --gold-300: #F3D28C;
  --crystal: #C9D4FF;
  --gold-deep: #A8741F; /* gold foil on cream paper: darker so it stays legible */

  /* legibility tokens (hero copy over the sky): measured in runs/task-005, see CODING.md */
  --hero-label: #F3F0FF;
  --hero-title: #FFF5E4;
  --hero-line: #FFF0F6;
  --halo: 11, 16, 48;

  --font-script: "Pinyon Script", "Snell Roundhand", cursive;
  --font-title: "Cinzel", "Trajan Pro", Georgia, serif;
  --font-body: "Cormorant Garamond", Georgia, serif;
  --font-label: "Josefin Sans", "Avenir Next", system-ui, sans-serif;

  --vh: 100vh; /* dynamic viewport height where supported */
}
@supports (height: 100dvh) { :root { --vh: 100dvh; } }

*, *::before, *::after { box-sizing: border-box; }
/* No document scroll, no scrollbars, no pull-to-refresh or rubber-banding, at any time (also during the loader). */
html { height: 100%; background: var(--night-900); color: var(--cream); -webkit-text-size-adjust: 100%; overflow: hidden; overscroll-behavior: none; touch-action: none; }
body { margin: 0; height: 100%; overflow: hidden; overscroll-behavior: none; touch-action: none; background: var(--night-900); font-family: var(--font-body); font-size: clamp(1.125rem, 1.05rem + 0.3vw, 1.3rem); line-height: 1.55; -webkit-user-select: none; user-select: none; }
img, picture, canvas, svg { display: block; }
img { max-width: none; }
p, h1, h2 { margin: 0; }
button { font: inherit; color: inherit; }

/* ---------- loader ---------- */
#loader { position: fixed; inset: 0; z-index: 100; display: none; flex-direction: column; align-items: center; justify-content: center; gap: 1.6rem; background: var(--night-900); }
.js #loader { display: flex; }
.loader-ring { position: relative; width: 74px; height: 74px; }
.loader-ring svg { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.loader-arc { fill: none; stroke: var(--gold-500); stroke-width: 1.2; stroke-linecap: round; stroke-dasharray: 0 100; opacity: .75; }
.loader-orbit { position: absolute; inset: 0; }
.loader-star { position: absolute; left: 50%; top: -5px; width: 12px; height: 12px; margin-left: -6px; background: var(--gold-300); clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%); filter: drop-shadow(0 0 6px var(--gold-300)); }
.loader-label { font-family: var(--font-label); font-weight: 300; font-size: .78rem; letter-spacing: .34em; text-transform: uppercase; color: var(--lavender); padding-left: .34em; }

/* ---------- fixed sky ---------- */
#sky { position: fixed; left: 0; top: 0; width: 100%; height: 100vh; height: var(--vh); z-index: 0; overflow: hidden; background: var(--night-900); }
.sky-img { position: absolute; inset: -24px; }
.sky-img img { width: 100%; height: 100%; object-fit: cover; object-position: 50% 70%; }
/* 6% bleed above and below: the Ascent/Descent drift the canvases vertically (5%) without ever showing an edge */
#sky-gl, #sky-stars { position: absolute; left: 0; top: -6%; width: 100%; height: 112%; mix-blend-mode: screen; opacity: 0; }
#sky-tint { position: absolute; inset: 0; opacity: 0; background: linear-gradient(to bottom, var(--night-900) 0%, rgba(26, 34, 87, .72) 55%, rgba(110, 127, 208, .25) 100%); }
.twinkle-fallback { position: absolute; inset: 0; display: none; opacity: .7;
  background-image: radial-gradient(1.5px 1.5px at 12% 18%, var(--gold-300), transparent), radial-gradient(1.5px 1.5px at 31% 9%, var(--gold-300), transparent), radial-gradient(2px 2px at 54% 22%, var(--gold-300), transparent), radial-gradient(1.5px 1.5px at 73% 12%, var(--gold-300), transparent), radial-gradient(2px 2px at 88% 30%, var(--gold-300), transparent), radial-gradient(1.5px 1.5px at 21% 38%, var(--gold-300), transparent), radial-gradient(1.5px 1.5px at 64% 41%, var(--gold-300), transparent);
  animation: twinkle 5s ease-in-out infinite alternate; }
.no-gl .twinkle-fallback { display: block; }
@keyframes twinkle { from { opacity: .25; } to { opacity: .9; } }

#fx { position: fixed; inset: 0; z-index: 60; width: 100%; height: 100%; pointer-events: none; }

/* ---------- scene stack ---------- */
#scenes { position: fixed; left: 0; top: 0; width: 100%; height: 100vh; height: var(--vh); z-index: 1; overflow: hidden; }
.scene { position: absolute; inset: 0; overflow: hidden; }
/* task 063 IDLE RULE: a scene's css loops run only while it is LIVE (.sc-live: the current scene and both scenes of a transition, js/scenes.js;
   scroll mode: the active sections, js/scrollmode.js). Paused, not removed: each one goes on from where it was. Covers card-sheen (invitation),
   bk-breathe (books), fd-twinkle (fund .fd-sp, registry .rg-sp), rs-rise and rs-tw (rsvp), cl-spk and rs-tw (closing), glint (any scene). */
html.js .scene:not(.sc-live), html.js .scene:not(.sc-live) * { animation-play-state: paused !important; }
.js .scene { visibility: hidden; } /* the engine reveals the current scene with autoAlpha */
#opening { z-index: 2; } /* the opening's clouds close over the invitation, which waits underneath */
#invitation { z-index: 1; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* scene dots (task 058): a column of small gold four-point stars on a slim, soft night pill, so they read on the bright clouds as on
   the night sky; the current one glows. Once the welcome intro ends they light up top to bottom, one pass (js/lib/dothello.js, the
   .hello class; DOT_HELLO_MS = the animation's length); desktop: a hover / focus label with the section's name (data-label). Nothing
   about the dots animates at rest (IDLE RULE). The pill is .8 opaque (idle star >= 3:1 on the brightest cloud) and has NO backdrop
   blur: a backdrop-filter re-blurs whenever the sky under it repaints (the desktop "&" glint idle went 51 -> 73). Under PHONE NAV
   (task 060, below) the stars are hidden and a menu button takes their place; the task 059 phone outlines are gone. */
#dots { position: fixed; right: calc(2px + env(safe-area-inset-right, 0px)); top: 50%; transform: translateY(-50%); z-index: 65; display: none; flex-direction: column; --dots-plate: .8; }
.js #dots { display: flex; opacity: 0; pointer-events: none; transition: opacity .8s ease; }
.js.ready #dots { opacity: 1; pointer-events: auto; }
#dots::before { content: ""; position: absolute; inset: 4px 9px; box-sizing: border-box; border: 1px solid rgba(243, 210, 140, .25); border-radius: 99px; background: rgba(11, 16, 48, var(--dots-plate)); pointer-events: none; }
#dots button { width: 40px; height: 44px; position: relative; display: grid; place-items: center; padding: 0; border: 0; background: none; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: filter .5s ease; }
#dots i { display: block; width: 12px; height: 12px; background: var(--gold-300); opacity: .62; clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%); transition: opacity .5s ease, transform .5s ease; }
#dots button:hover i { opacity: .9; }
#dots button.on { filter: drop-shadow(0 0 5px rgba(243, 210, 140, .95)); }
#dots button.on i { opacity: 1; transform: scale(1.45); }
#dots button:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -4px; border-radius: 8px; }
#dots.hello i { animation: dots-hello .6s ease-in-out; }
@keyframes dots-hello { 45% { opacity: 1; } }
@media (hover: hover) and (pointer: fine) and (min-width: 700px) {
  #dots button::after { content: attr(data-label); position: absolute; right: calc(100% - 2px); top: 50%; transform: translateY(-50%); padding: 6px 12px 5px calc(12px + .14em); border: 1px solid rgba(243, 210, 140, .25); border-radius: 99px; background: rgba(11, 16, 48, .78);
    color: var(--cream); font-family: var(--font-label); font-size: .7rem; line-height: 1; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .2s ease, visibility 0s linear .2s; }
  #dots button:hover::after, #dots button:focus-visible::after { opacity: 1; visibility: visible; transition: opacity .2s ease; }
}

/* sound toggle (task 026): top left, a 44x44 hit area around a 30 px gold music note; above the scenes (and the dots), below any modal */
#sound { position: fixed; left: calc(env(safe-area-inset-left, 0px) + 4px); top: calc(env(safe-area-inset-top, 0px) + 4px); width: 44px; height: 44px; z-index: 66; display: none; padding: 0; border: 0; background: none; color: var(--gold-300); cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.js #sound { display: block; opacity: 0; pointer-events: none; transition: opacity .8s ease; }
.js.ready #sound { opacity: 1; pointer-events: auto; }
#sound .snd-disc { position: absolute; left: 7px; top: 7px; width: 30px; height: 30px; border-radius: 50%; background: rgba(14, 18, 48, .42); box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .28); }
#sound .snd-icon { position: absolute; left: 7px; top: 7px; width: 30px; height: 30px; display: block; opacity: .45; transition: opacity .4s ease; }
#sound .snd-icon svg { position: absolute; left: 0; top: 0; display: block; }
#sound[aria-pressed="true"] .snd-icon { opacity: 1; }
/* the glow is static per layer (rastered once); only the arcs' svg opacity pulses, on the compositor (task 043) */
#sound[aria-pressed="true"] .snd-icon svg { filter: drop-shadow(0 0 4px rgba(243, 210, 140, .6)); }
#sound .snd-slash { opacity: 1; transition: opacity .3s ease; }
#sound[aria-pressed="true"] .snd-slash { opacity: 0; }
#sound .snd-arc { opacity: 0; }
#sound[aria-pressed="true"] .snd-arc { animation: snd-arc 1.8s ease-in-out infinite; will-change: opacity; }
#sound[aria-pressed="true"] .snd-arc.a2 { animation-delay: .3s; }
#sound[aria-pressed="true"] .snd-arc.a3 { animation-delay: .6s; }
@keyframes snd-arc { 0%, 100% { opacity: .15; } 40% { opacity: 1; } }
#sound .snd-label { position: absolute; left: 40px; top: 50%; margin-top: -9px; padding-left: 4px; font: 600 11px/18px var(--font-label), sans-serif; letter-spacing: .18em; text-transform: uppercase; color: var(--gold-300); opacity: 0; pointer-events: none; transition: opacity .8s ease; white-space: nowrap; }
#sound.welcome .snd-label { opacity: .85; }
#sound.welcome .snd-disc { animation: snd-glow 2.2s ease-in-out 1; } /* one pulse (task 028) */
@keyframes snd-glow { 0%, 100% { box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .28), 0 0 0 rgba(243, 210, 140, 0); } 50% { box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .8), 0 0 14px 3px rgba(243, 210, 140, .55); } }
#sound:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -3px; border-radius: 50%; }
#sound.quick .snd-label { transition-duration: .2s; }
/* shuffle button (task 031, closer in task 032): the 44 px hit boxes overlap by 3 px (visible disc gap 11 px, was 22); clip-path trims the shuffle box's inner 1.5 px so a tap at or left of the midpoint between the discs (x 46.5) goes to the toggle; hidden (not focusable) unless the playlist music plays */
#shuffle { position: fixed; left: calc(env(safe-area-inset-left, 0px) + 45px); clip-path: inset(0 0 0 1.5px); top: calc(env(safe-area-inset-top, 0px) + 4px); width: 44px; height: 44px; z-index: 66; display: none; padding: 0; border: 0; background: none; color: var(--gold-300); cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.js #shuffle { display: block; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .3s ease, visibility 0s linear .3s; }
.js #shuffle.show { opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .4s ease, visibility 0s; }
#shuffle .shf-disc { position: absolute; left: 7px; top: 7px; width: 30px; height: 30px; border-radius: 50%; background: rgba(14, 18, 48, .42); box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .28); }
#shuffle .shf-icon { position: absolute; left: 7px; top: 7px; width: 30px; height: 30px; display: block; opacity: .7; }
#shuffle .shf-icon svg { position: absolute; left: 0; top: 0; display: block; overflow: visible; } /* halos spill past the box, like the old CSS filter */
#shuffle .shf-glow.g4 { opacity: 0; }
/* task 043: the breathing is opacity only; the glow grows by cross-fading the pre-blurred 2 px and 4 px halos (no filter animation) */
#shuffle.show .shf-icon { animation: shf-breathe 3.2s ease-in-out infinite; will-change: opacity; }
#shuffle.show .shf-glow.g2 { animation: shf-halo-out 3.2s ease-in-out infinite; will-change: opacity; }
#shuffle.show .shf-glow.g4 { animation: shf-halo-in 3.2s ease-in-out infinite; will-change: opacity; }
@keyframes shf-breathe { 0%, 100% { opacity: .7; } 50% { opacity: 1; } }
@keyframes shf-halo-out { 0%, 100% { opacity: 1; } 50% { opacity: 0; } }
@keyframes shf-halo-in { 0%, 100% { opacity: 0; } 50% { opacity: 1; } }
#shuffle .shf-burst { position: absolute; left: 20px; top: 20px; width: 4px; height: 4px; border-radius: 50%; background: #fff3c4; box-shadow: 0 0 5px 1px rgba(243, 210, 140, .8); pointer-events: none; }
#shuffle:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -3px; border-radius: 50%; }

/* PHONE NAV (task 060; js/navmenu.js): on phones and landscape phones the scene dots give way to a gold three-line button in the sound
   toggle's old place; the sound moves one place right (x 45) and the shuffle one more (x 86), 41 px steps, and the right-hand box of
   each pair loses its inner 1.5 px (the sound's clip reaches far out on the other sides, for its label and its welcome glow). The menu
   card lists the nine sections (rows >= 44 px; two columns on short landscape screens, so it never scrolls). Desktop: stars unchanged.
   The media query below MUST equal js/lib/phonenav.js PHONE_NAV (static test). Nothing here animates at rest (IDLE RULE). */
#menu, #nav, #nav-dim { display: none; }
@media (max-width: 699px), (pointer: coarse) and (max-height: 520px) {
  #dots, .js #dots { display: none; }
  #menu { position: fixed; left: calc(env(safe-area-inset-left, 0px) + 4px); top: calc(env(safe-area-inset-top, 0px) + 4px); width: 44px; height: 44px; z-index: 66; padding: 0; border: 0; background: none; color: var(--gold-300); cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
  .js #menu { display: block; opacity: 0; pointer-events: none; transition: opacity .8s ease; }
  .js.ready #menu { opacity: 1; pointer-events: auto; }
  #menu[aria-expanded="true"] { z-index: 69; }
  #menu .mn-disc { position: absolute; left: 7px; top: 7px; width: 30px; height: 30px; border-radius: 50%; background: rgba(14, 18, 48, .42); box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .28); }
  #menu .mn-icon { position: absolute; left: 15px; top: 15px; width: 14px; height: 14px; opacity: .9; }
  #menu .mn-icon i { position: absolute; left: 0; top: 6.2px; width: 14px; height: 1.6px; border-radius: 1px; background: currentColor; transition: transform .25s ease, opacity .25s ease; }
  #menu .mn-icon i:nth-child(1) { transform: translateY(-4.5px); }
  #menu .mn-icon i:nth-child(3) { transform: translateY(4.5px); }
  #menu[aria-expanded="true"] .mn-icon i:nth-child(1) { transform: rotate(45deg); }
  #menu[aria-expanded="true"] .mn-icon i:nth-child(2) { opacity: 0; transform: scaleX(.2); }
  #menu[aria-expanded="true"] .mn-icon i:nth-child(3) { transform: rotate(-45deg); }
  #menu.hello .mn-icon { animation: menu-hello .9s ease-in-out 1; }
  #menu:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -3px; border-radius: 50%; }
  #sound { left: calc(env(safe-area-inset-left, 0px) + 45px); clip-path: inset(-40px -400px -40px 1.5px); }
  #shuffle { left: calc(env(safe-area-inset-left, 0px) + 86px); }
  .js #nav-dim:not([hidden]) { display: block; position: fixed; inset: 0; z-index: 67; background: transparent; touch-action: none; }
  .js #nav:not([hidden]) { display: block; position: fixed; z-index: 68; left: calc(env(safe-area-inset-left, 0px) + 8px); top: calc(env(safe-area-inset-top, 0px) + 50px); box-sizing: border-box; width: min(78vw, 300px); max-height: calc(100% - 58px - env(safe-area-inset-top, 0px)); overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y;
    padding: 6px; border: 1px solid rgba(243, 210, 140, .3); border-radius: 14px; background: rgba(11, 16, 48, .92); box-shadow: 0 14px 40px rgba(0, 0, 0, .45); }
  #nav .nav-list { display: grid; margin: 0; padding: 0; list-style: none; }
  #nav .nav-row { display: flex; align-items: center; gap: 12px; box-sizing: border-box; width: 100%; min-height: 44px; padding: 0 12px; border: 0; border-radius: 10px; background: none; color: var(--cream); font-family: var(--font-label); font-size: .78rem; font-weight: 400; line-height: 1.2; letter-spacing: .16em; text-transform: uppercase; text-align: left; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
  #nav .nav-row i { flex: none; width: 10px; height: 10px; background: var(--gold-300); clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%); opacity: 0; }
  #nav .nav-row.on { color: var(--gold-300); }
  #nav .nav-row.on i { opacity: 1; }
  #nav .nav-row:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -2px; }
}
@media (pointer: coarse) and (max-height: 520px) {
  #nav .nav-list { grid-template-rows: repeat(5, auto); grid-auto-flow: column; column-gap: 2px; }
  #nav .nav-row { gap: 9px; padding: 0 9px; letter-spacing: .12em; }
}
@keyframes menu-hello { 0%, 100% { opacity: .9; transform: scale(1); } 45% { opacity: 1; transform: scale(1.18); } }

/* ---------- shared type ---------- */
.label { font-family: var(--font-label); font-weight: 300; text-transform: uppercase; letter-spacing: .3em; font-size: clamp(.72rem, .66rem + .3vw, .9rem); color: var(--lavender); }

/* ---------- 1 · opening ---------- */
.stage { position: absolute; inset: 0; overflow: hidden; }
.px { position: absolute; inset: 0; pointer-events: none; }

/* Cloud banks are full-bleed: wider than the viewport, pushed past both side edges, and their bottoms sit
   BELOW the viewport (the image ends off screen). The mask also fades the outer sides and the bottom, so no
   cloud layer ever ends in a visible hard line. */
.bank-wrap { position: absolute; bottom: -7vh; bottom: calc(var(--vh) * -0.07); width: max(62vw, calc(var(--vh) * 0.8)); will-change: transform; }
.bank-wrap.l { left: -9vw; transform-origin: 0 100%; }
.bank-wrap.r { right: -9vw; transform-origin: 100% 100%; }
.bank-wrap img { width: 100%; height: auto; }
/* F4 (task 036): every cloud layer feathers to alpha 0 on ALL four sides (12% each), so the image frame never shows as a cut line.
   The feather is BAKED into the art (tools/imagegen/feather.js); a CSS mask on a large moving or scaled layer is repainted every frame.
   will-change: the idle drift (idle.js, x) moves this element on the compositor, its pixels are never repainted. */
.bank { display: block; will-change: transform; }
.bank-back { width: max(54vw, calc(var(--vh) * 0.7)); bottom: calc(var(--vh) * -0.05); opacity: .8; }
.bank-front { z-index: 2; }

/* moon-cats sits above the cloud tops; during 1 to 2 it sinks BEHIND the closing front layer (z-order), fully opaque */
.moon-exit { position: absolute; right: 2vw; bottom: calc(var(--vh) * 0.22); width: min(62vw, calc(var(--vh) * 1.08)); }
.moon-enter { position: relative; }
.moon-enter img { width: 100%; height: auto; transform-origin: 50% 100%; }
/* task 010: the moon, its cats and the blurred glow live on stable compositor layers for the whole life of the scene. The
   ambient tweens at the intro's end (scaleY on the img, glow scale) used to promote new layers and re-rasterise the
   blur(18px) glow (measured: glow luminance -3% at t=6.5 s, when the glow's transform went from 2D to translate3d). */
.moon-exit, .moon-idle, .moon-enter, .moon-enter img, .moon-glow { will-change: transform; }
.moon-glow { will-change: transform, opacity; }
.moon-glow { position: absolute; inset: 6% 6% 10% 6%; border-radius: 50%; background: radial-gradient(closest-side, rgba(243, 210, 140, .5), rgba(212, 162, 76, .16) 55%, transparent 75%); filter: blur(18px); opacity: .6; }
/* task 046 P4: the moon's flare during the celestial shower; its own element after .moon-glow (made on the first shower), only its
   opacity tweens, so the idle drift on the wrappers runs on untouched. No filter: the gradient is soft already. */
.moon-flare { position: absolute; inset: -4% -4% 2% -4%; border-radius: 50%; background: radial-gradient(closest-side, rgba(255, 240, 200, .7), rgba(243, 210, 140, .32) 50%, rgba(243, 210, 140, 0)); opacity: 0; visibility: hidden; pointer-events: none; will-change: opacity; }
/* task 046 P5: the glint hints (js/glints.js): one tiny gold site-star per egg surface, idle and invisible; .on plays one 0.8 s twinkle
   (compositor only: opacity and transform), added by a gsap timer and removed on animationend */
.glint { position: absolute; z-index: 3; width: 12px; height: 12px; margin: -6px 0 0 -6px; opacity: 0; pointer-events: none; }
.glint svg { display: block; width: 100%; height: 100%; overflow: visible; fill: #F3D28C; }
.glint.on { animation: glint .8s ease-in-out 1 both; }
@keyframes glint { 0%, 100% { opacity: 0; transform: scale(.6); } 50% { opacity: .9; transform: scale(1); } }
.cl-glints { position: absolute; inset: 0; pointer-events: none; }

.px-copy { z-index: 5; will-change: transform; backface-visibility: hidden; }
/* Hero copy: a soft dark scrim behind it plus a halo on every line. Landscape: radial scrim at the text block. */
/* F3 lockup: one centred axis. Desktop axis = --lock-axis (27-30% of the viewport width, clear of the moon);
   phone axis = the centre. The vertical rhythm is tokens: label->title, title->subtitle, subtitle->tagline. */
:root { --lock-axis: 28vw; --lock-gap-label: 1.1rem; --lock-gap-sub: .5rem; --lock-gap-line: 1.4rem; }
.scrim { position: absolute; left: var(--lock-axis); top: 33%; width: 52vw; height: 64%; transform: translate(-50%, -50%); pointer-events: none;
  background: radial-gradient(closest-side, rgba(var(--halo), .6), rgba(var(--halo), .32) 55%, transparent 100%); }
.copy { position: absolute; left: var(--lock-axis); top: 33%; width: min(33vw, calc(var(--vh) * 0.62)); min-width: 280px; transform: translate(-50%, -50%); text-align: center; pointer-events: auto; }
.copy .label { margin-bottom: var(--lock-gap-label); padding-left: .3em; font-weight: 400; font-size: clamp(.84rem, .7rem + .4vw, 1.08rem); color: var(--hero-label); text-shadow: 0 0 5px rgba(var(--halo), 1), 0 0 14px rgba(var(--halo), 1), 0 0 30px rgba(var(--halo), .9); }
.title { font-weight: 400; display: flex; flex-direction: column; align-items: center; gap: var(--lock-gap-sub); }
.title-script { display: block; width: 100%; filter: drop-shadow(0 2px 14px rgba(var(--halo), .8)); }
.title-script svg { width: 100%; height: auto; overflow: visible; }
.title-script .glyph { fill: url(#foil); stroke: none; }
.title-fallback { display: none; font-family: var(--font-script); font-size: clamp(4rem, 12vw, 8rem); color: var(--gold-300); line-height: 1.1; }
html:not(.js) .title-fallback { display: block; }
.title-sub { font-family: var(--font-title); font-weight: 600; text-transform: uppercase; letter-spacing: .12em; white-space: nowrap; font-size: min(clamp(1.15rem, 2.5vw, 3.6rem), calc(var(--vh) * 0.06)); color: var(--hero-title); padding-left: .12em; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), 1), 0 0 38px rgba(var(--halo), .85); }
.open-line { margin-top: var(--lock-gap-line); font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.35rem, 1rem + 1vw, 2.5rem), calc(var(--vh) * 0.06)); color: var(--hero-line); text-shadow: 0 0 5px rgba(var(--halo), 1), 0 0 16px rgba(var(--halo), 1), 0 0 34px rgba(var(--halo), .9); }

#overlay { position: fixed; left: 0; top: 0; width: 100%; height: 100vh; height: var(--vh); z-index: 40; overflow: hidden; pointer-events: none; }
/* task 018: shooting stars (js/meteors.js) replace the astronaut. Each .mt is a tail whose bright head sits at the origin end:
   moving right the head is the right end (tail fades to the left), moving left (.rev) the left end. Transforms and opacity only. */
.mt { position: absolute; left: 0; top: 0; height: 3px; border-radius: 3px; opacity: 0; visibility: hidden; pointer-events: none; will-change: transform, opacity; transform-origin: 100% 50%; background: linear-gradient(to right, rgba(243, 214, 150, 0), rgba(247, 226, 170, .55) 62%, rgba(255, 252, 240, .95)); }
.mt::after { content: ""; position: absolute; right: -3px; top: 50%; width: 7px; height: 7px; margin-top: -3.5px; border-radius: 50%; background: #fffdf4; box-shadow: 0 0 6px 2px rgba(255, 244, 205, .95), 0 0 16px 5px rgba(243, 214, 150, .6); }
.mt.rev { transform-origin: 0 50%; background: linear-gradient(to left, rgba(243, 214, 150, 0), rgba(247, 226, 170, .55) 62%, rgba(255, 252, 240, .95)); }
.mt.rev::after { right: auto; left: -3px; }

/* W4 scroll cue (task 012: text only). A bold navy label, bottom centre, floating 4px down and up, with a soft cream glow that
   breathes around it (an aria-hidden duplicate .cue-glow carries only the text-shadow; its opacity pulses, the text never
   changes). js/cue.js adds .on after the idle delay (as the intro completes on the welcome scene, 2 s elsewhere) and removes
   it within 150 ms of any gesture. The label is SWIPE UP on touch (pointer: coarse) and SCROLL otherwise. */
.cue { position: fixed; left: 50%; bottom: calc(env(safe-area-inset-bottom, 0px) + max(var(--vh) * 0.025, 12px)); z-index: 30; width: 160px; margin-left: -80px; display: flex; flex-direction: column; align-items: center; opacity: 0; transition: opacity .12s linear; pointer-events: none; }
.cue.on { opacity: 1; transition: opacity .5s ease; }
/* task 011: no scrim. Ink label and gold line with a hairline navy outline sit directly on the bright cloud banks.
   Per-scene overrides: a scene with a dark bottom sets .cue.cue-dark (cream ink, gold-300). */
.cue { --cue-ink: #1D2C5E; --cue-g1: rgba(255, 252, 244, 1); --cue-g2: rgba(255, 246, 228, 1); --cue-g3: rgba(243, 214, 150, .8); }
.cue.cue-solid { --cue-ink: #1D2C5E; } /* Section 3: its phone backdrop is lighter, the label goes fully opaque */
.cue.cue-dark { --cue-ink: rgba(255, 245, 228, .9); --cue-g1: rgba(11, 16, 48, .4); --cue-g2: rgba(11, 16, 48, .4); --cue-g3: rgba(11, 16, 48, .25); }
.cue-label { position: relative; font-family: var(--font-label); font-weight: 600; font-size: 13px; line-height: 1; letter-spacing: .3em; padding-left: .3em; text-transform: uppercase; white-space: nowrap; }
.cue-text { position: relative; color: var(--cue-ink); }
/* task 018: the glow is EXACTLY the text's box (inset 0, inherited font, letter-spacing and padding-left): no hand offset, so it
   is centred for any label length (the task 016 `left: .18em` only fitted one) */
.cue-glow { position: absolute; inset: 0; padding-left: inherit; color: transparent; -webkit-text-stroke: 26px var(--cue-g1); filter: blur(7px); text-shadow: 0 0 10px var(--cue-g1), 0 0 16px var(--cue-g1), 0 0 24px var(--cue-g1), 0 0 34px var(--cue-g2), 0 0 48px var(--cue-g2), 0 0 66px var(--cue-g3); opacity: .6; will-change: opacity; }
.cue.on .cue-label { animation: cue-float 2.4s ease-in-out infinite; }
.cue.on .cue-glow { animation: cue-breathe 2.4s ease-in-out infinite; }
@keyframes cue-float { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(4px); } }
@keyframes cue-breathe { 0%, 100% { opacity: .6; } 50% { opacity: 1; } }
/* W3 idle: the moon wrapper floats, sparks twinkle around it, a shooting star streaks across */
.moon-idle { position: relative; }
/* task 045 E2: the moon picture's own tap surface (three taps: a meteor shower); a plain div role=button, no visible hint.
   It takes the pointer only while the welcome is the scene: the pair timeline leaves .moon-exit with an inline visibility: visible,
   so inside the hidden welcome it would otherwise catch taps meant for a later scene (the cats, after a dot jump from the welcome) */
.moon-tap { pointer-events: none; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; outline: none; }
html[data-scene="opening"] .moon-tap, html.mode-scroll .moon-tap { pointer-events: auto; }
.moon-tap:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -8%; border-radius: 40%; }
.spark { position: absolute; width: 14px; height: 14px; margin: -7px 0 0 -7px; background: #FFE9AA; clip-path: polygon(50% 0, 62% 38%, 100% 50%, 62% 62%, 50% 100%, 38% 62%, 0 50%, 38% 38%); opacity: 0; pointer-events: none; filter: drop-shadow(0 0 4px rgba(255, 233, 170, .9)); }
.shoot { position: fixed; left: 0; top: 0; z-index: 3; width: 130px; height: 2px; transform-origin: 0 50%; background: linear-gradient(to right, transparent, rgba(255, 233, 170, .95)); opacity: 0; pointer-events: none; }

/* ---------- 2 · invitation ---------- */
.inv-stage { --ew: min(124vw, calc(var(--vh) * 0.872 * 4 / 3)); position: absolute; inset: 0; display: grid; place-items: center; overflow: hidden; }
/* envelope: the art canvas is 4:3 (2048x1536) but the painted body fills only its middle 68% (x 16%..84%, y 35.9%..95.4%; the
   raised flap fills the canvas above). --ew is therefore ~1.41x the old box so the body keeps its old size (84vw on a phone), and
   .env is placed so the BODY (not the canvas) is centred in the stage. .env makes NO stacking context (no transform, no z-index),
   so its back (.env-float, z 4), the letter (z 5) and the pocket front (.env-front, z 6) layer against each other in the stage. */
.env { position: absolute; left: calc(50% - var(--ew) / 2); top: calc(50% - var(--ew) * .493); width: var(--ew); height: calc(var(--ew) * .75); pointer-events: none; }
/* F6: .env-float IS the image-aspect box (4:3 = the PNG's 2048x1536, no letterboxing). Seal, glow, ring and hit area are placed
   by percentage inside it and the whole box rotates together. */
.env-float, .env-front { position: absolute; left: 0; top: 0; width: 100%; height: 100%; transform-origin: 50% 50%; }
.env-float { z-index: 4; will-change: transform, opacity; filter: drop-shadow(0 18px 28px rgba(5, 8, 28, .55)) blur(0px); }
.env-img { position: absolute; inset: 0; }
.env-img img { display: block; width: 100%; height: 100%; object-fit: fill; }
/* task 015 layers (all in the 2048x1536 canvas, hinge = the body's top edge, y 552 = 35.94%) */
.env-base { clip-path: inset(35.94% 0 0 0); }
.env-flap, .env-flap-open { position: absolute; inset: 0; transform-origin: 50% 35.94%; will-change: transform, filter; }
/* closed art -> open-art canvas: the matched pair differ by this affine (x 1.0058 / -5.9px, y 0.9394 / +66.3px), measured in
   runs/task-015/results.md; the clip is the flap triangle in the closed art's own coordinates (apex hidden under the seal) */
.env-flap-art { transform-origin: 0 0; transform: translate(-0.288%, 4.32%) scale(1.0058, 0.9394);
  clip-path: polygon(16.16% 33.66%, 83.84% 33.66%, 83.84% 42.54%, 49.93% 75.4%, 16.16% 42.18%); }
/* task 016: the wax seal is its own layer (a second copy of the closed art, same affine) clipped to a disc that encloses the whole
   wavy rim: centre (1022.5, 1112.5) of the 2048x1536 closed art, rim radius 124.5 px + 3% margin = 128 px = 7.07% of the circle
   reference (sqrt((w^2+h^2)/2) = 1810.2 px). Measured on envelope-pad-01.png (runs/task-016/scratch/seal-edge.mjs, seal-overlay.mjs). */
.env-flap-art.env-flap-seal { clip-path: circle(7.07% at 49.93% 72.43%); }
.env-flap-open { visibility: hidden; opacity: 0; }
.env-flap-open .env-img { clip-path: inset(0 0 64.06% 0); }
/* F7 pocket front: envelope-open-03 clipped to the pocket. Percentages of the image, measured on the 2048x1536 PNG
   (runs/task-007/scratch/pocket-grid.png): the two side diagonals meet the flap apex (50, 59) with its ~3.6% rounded
   shoulders at (45.4, 61.1) and (54.6, 61.1); sides run out along the paper edges to the bottom edge at y 96. */
/* task 016: the polygon was re-checked on the CURRENT open art (private/imagegen/out/envelope-open-matched-01.png, overlay drawn by
   runs/task-016/scratch/pocket-overlay.mjs): it follows the inner flap diagonals and the body edges. The task-015 drift came from the
   box, not the polygon: .env-front used .env's own top offset INSIDE .env, ~0.118 ew too high, and a different transform origin.
   It now shares ONE rule with .env-float (box + origin, above) so the two can never drift. */
.env-front { z-index: 6; opacity: 0; pointer-events: none; will-change: transform;
  clip-path: polygon(15.97% 35.94%, 17.98% 35.94%, 45.46% 65.23%, 47.71% 64.13%, 49.95% 64.13%, 52.20% 64.13%, 54.44% 64.77%, 81.95% 35.94%, 84.03% 35.94%, 84.03% 95.44%, 15.97% 95.44%); }
/* wax-seal centre: (1022.5, 1111.3) of the 2048x1536 canvas after the closed->open affine. Task 016 re-measured the painted seal on
   envelope-pad-01.png (centre (1022.5, 1112.5) in the closed art; the task-015 value (1000, 1112) was 22 px too far left, so the
   glow and hit area sat about 11 px (desktop) left of the wax; runs/task-016/scratch/seal-check.mjs) */
.env { --seal-x: 49.93%; --seal-y: 72.35%; }
.seal-glow { position: absolute; left: var(--seal-x); top: var(--seal-y); width: 13.5%; aspect-ratio: 1; transform: translate(-50%, -50%); border-radius: 50%; background: radial-gradient(closest-side, rgba(255, 233, 170, .95), rgba(243, 210, 140, .45) 45%, transparent 72%); opacity: 0; pointer-events: none; mix-blend-mode: screen; will-change: opacity, transform; }
/* tap target: at least 72x72 CSS px (the seal itself is about 15% of the envelope width) */
.seal-btn { position: absolute; left: var(--seal-x); top: var(--seal-y); width: max(13.5%, 72px); aspect-ratio: 1; transform: translate(-50%, -50%); padding: 0; border: 0; border-radius: 50%; background: transparent; cursor: pointer; pointer-events: auto; touch-action: manipulation; -webkit-tap-highlight-color: transparent; }
.seal-btn:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; }
.env.opened .seal-btn { pointer-events: none; }

/* The letter is the owner's real 5x7 card (task 047), ONE flat card. It ignores pointer events until it is open, so it can never
   swallow a tap meant for the seal. Phone size is unchanged (92vw); desktop grows to 80dvh tall. */
/* task 009: the final height is min(80dvh, viewport - 2 x 4dvh) on desktop (--vh is the dynamic viewport height) */
.letter { --lh: min(calc(var(--vh) * 0.8), calc(var(--vh) - var(--vh) * 0.08)); --lw: min(92vw, calc(100vw - 72px), calc(var(--lh) * 5 / 7)); --u: calc(var(--lw) / 100); position: relative; z-index: 5; width: var(--lw); aspect-ratio: 5 / 7; perspective: 1600px; color: var(--ink); pointer-events: none; }
.letter.live { pointer-events: auto; }
.letter-shadow { position: absolute; inset: 1.5%; border-radius: 4px; box-shadow: 0 26px 50px rgba(5, 8, 28, .55), 0 4px 14px rgba(5, 8, 28, .35); pointer-events: none; }
/* ONE flat card, two faces (the back turned 180deg, hidden by backface-visibility; task 048 spins the card) */
.card { position: absolute; inset: 0; transform-style: preserve-3d; }
.card-face { position: absolute; inset: 0; border-radius: 3px; overflow: hidden; background: var(--cream); -webkit-backface-visibility: hidden; backface-visibility: hidden; }
.card-back { transform: rotateY(180deg); }
.card-art img { display: block; width: 100%; height: 100%; object-fit: cover; }
/* the live gold on "BABY SHOWER" and "Alexis & Eric": the owner's text mask (alpha = lettering) over the .names gold stops; the
   highlight band moves by TRANSFORM only (2.8 s sweep, 2 s pause). It runs only while the invitation is active (JS toggles .on).
   Task 051: each block is its own element, placed at its cropped mask's rect (JS sets --x/--y/--w/--h, fractions of the card, from
   lib/cardgeom.js CARD_MASKS) with that mask at 100% 100%: no clip-path. The gold and the band stay card-sized (sized and offset by
   the same fractions), so the colours and the one sweep across both blocks are unchanged. The names egg scales .names-pop, the names
   block's own wrapper, about its centre: no copy. */
.card-sheen { position: absolute; inset: 0; z-index: 1; overflow: hidden; pointer-events: none; }
.sheen-title, .names-pop { position: absolute; left: calc(var(--x, 0) * 100%); top: calc(var(--y, 0) * 100%); width: calc(var(--w, 1) * 100%); height: calc(var(--h, 1) * 100%); }
.names-pop { transform-origin: 50% 50%; }
.sheen-text { overflow: hidden;
  background: linear-gradient(100deg, #9A6A1C 0%, #C08A2E 38%, #E6BE6A 50%, #C08A2E 62%, #9A6A1C 100%);
  background-size: calc(100% / var(--w, 1)) calc(100% / var(--h, 1)); background-repeat: no-repeat;
  background-position: calc(var(--x, 0) / (1 - var(--w, 0.5)) * 100%) calc(var(--y, 0) / (1 - var(--h, 0.5)) * 100%);
  -webkit-mask-size: 100% 100%; mask-size: 100% 100%; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; }
.sheen-title { -webkit-mask-image: url(../assets/img/card-text-mask-title-1536.png?v=f9b955fd); mask-image: url(../assets/img/card-text-mask-title-1536.png?v=f9b955fd); }
.sheen-names { position: absolute; inset: 0; -webkit-mask-image: url(../assets/img/card-text-mask-names-1536.png?v=17cc1f26); mask-image: url(../assets/img/card-text-mask-names-1536.png?v=17cc1f26); }
.sheen-band { position: absolute; left: calc(var(--x, 0) / var(--w, 1) * -100%); top: calc(var(--y, 0) / var(--h, 1) * -100%); width: calc(100% / var(--w, 1)); height: calc(100% / var(--h, 1)); background: linear-gradient(100deg, transparent 42%, rgba(255, 240, 200, .85) 50%, transparent 58%); transform: translateX(-60%); will-change: transform; animation: card-sheen 4.8s ease-in-out infinite paused; }
.card-sheen.on .sheen-band { animation-play-state: running; }
@keyframes card-sheen { 0% { transform: translateX(-60%); } 58.33% { transform: translateX(60%); } 100% { transform: translateX(60%); } }
/* task 049: the egg's tap surface is the whole "Alexis & Eric" line: the measured names box grown by 6% a side (JS sets
   --ax/--ay/--aw/--ah from lib/cardtaps.js NAMES_BOX), never shorter than 44 CSS px (grown about its centre). While the card is
   flipped it takes no taps (the card's own tap turns it back). A tap anywhere else on the live card turns it over. */
.amp { position: absolute; left: var(--ax); top: calc(var(--ay) - max(0px, (44px - var(--ah)) / 2)); width: var(--aw); height: max(var(--ah), 44px); z-index: 4; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; outline: none; }
.amp:focus-visible { outline: 1px solid var(--gold-deep); outline-offset: 2px; border-radius: 6px; }
.letter.flipped .amp { pointer-events: none; }
.letter.live .card { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.amp-bloom { position: absolute; inset: 0; z-index: 7; width: 100%; height: 100%; pointer-events: none; }

/* ---------- portrait phones: hero text in the top band, moon-cats below it, never overlapping ---------- */
@media (orientation: portrait) {
  .scrim { left: 0; top: 0; width: 100%; height: 46%; transform: none; background: linear-gradient(to bottom, rgba(var(--halo), .72) 0%, rgba(var(--halo), .62) 60%, transparent 100%); }
  :root { --lock-axis: 50vw; --lock-gap-label: .9rem; --lock-gap-line: 1.1rem; }
  .copy { left: 0; right: 0; width: auto; min-width: 0; max-width: none; top: 8%; top: calc(var(--vh) * 0.08); padding: 0 6vw; transform: none; text-align: center; }
  .title-script { width: min(84vw, calc(var(--vh) * 0.52)); }
  .title-sub { font-size: min(clamp(1.2rem, 5.6vw, 2rem), calc(var(--vh) * 0.034)); }
  .open-line { font-size: min(clamp(1.35rem, 5.8vw, 2.1rem), calc(var(--vh) * 0.036)); }
  /* moon: sits just above the front cloud tops (bank height is 59vw, its bottom is 6dvh below the screen) */
  .moon-exit { --mw: min(104vw, calc(var(--vh) * 0.62)); right: auto; left: calc((100% - var(--mw)) / 2); width: var(--mw); bottom: max(calc(59vw - var(--vh) * 0.12), calc(var(--vh) * 0.12)); }
  .bank-wrap { width: 118vw; bottom: calc(var(--vh) * -0.06); }
  .bank-wrap.l { left: -14vw; }
  .bank-wrap.r { right: -14vw; }
  .bank-back { width: 90vw; bottom: calc(var(--vh) * -0.04); }
}

/* ---------- no JavaScript: a plain, readable, scrollable page ---------- */
html:not(.js), html:not(.js) body { height: auto; overflow: auto; touch-action: auto; -webkit-user-select: text; user-select: text; }
html:not(.js) #scenes { position: static; height: auto; overflow: visible; }
html:not(.js) .scene { position: relative; height: 100vh; height: var(--vh); min-height: 560px; }
/* ---------- Scene 3 · Countdowns (orrery + His Moon) ---------- */
/* F9. Desktop: two columns, the orrery on the left (diameter ~56dvh), title / counters / His Moon on the right.
   Phone (portrait override below): one column, title -> orrery -> counters -> His Moon, all on one screen. */
.cd-stage { position: absolute; inset: 0; overflow: hidden; display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr auto auto auto 1fr; column-gap: 2vw; padding: 0 4vw; }
.orrery { grid-column: 1; grid-row: 1 / -1; align-self: center; justify-self: center; width: min(calc(var(--vh) * 0.6), 44vw); aspect-ratio: 1; overflow: visible; }
/* C3: the orbit rings read clearly against the night sky: gold-300 at 90%, a thin line, plus a soft glow */
.orb-rings { filter: drop-shadow(0 0 6px rgba(243, 210, 140, .35)); }
.orb-rings circle { fill: none; stroke: var(--gold-300); stroke-opacity: .9; stroke-width: 1.5; vector-effect: non-scaling-stroke; }
.planet { fill: #FFE9AA; filter: drop-shadow(0 0 4px rgba(255, 233, 170, 1)) drop-shadow(0 0 10px rgba(243, 210, 140, .6)); }
.planet-moon { fill: var(--cream); }
.planet-star { fill: #FFE9AA; filter: drop-shadow(0 0 4px rgba(255, 233, 170, 1)); }
/* task 060: each body's fading trail (an arc of its ring) and the alignment beam, cream on a soft gold glow (no filter: it redraws) */
.orb-trail { fill: none; stroke-width: 7; stroke-linecap: round; }
.orb-beam path { fill: none; stroke-linecap: round; }
.orb-beam-glow { stroke: #F3D28C; stroke-opacity: .28; stroke-width: 12; }
.orb-beam-core { stroke: #FFF1CC; stroke-opacity: .85; stroke-width: 2.5; }
/* C2: the vertical rhythm of the heading block is three tokens (label -> title, title -> counters, counters -> His Moon).
   The phone sets the same three, scaled. */
.cd-stage { --sp-lt: clamp(.5rem, calc(var(--vh) * .016), 1.1rem); --sp-tc: clamp(1rem, calc(var(--vh) * .032), 2.2rem); --sp-cm: clamp(1rem, calc(var(--vh) * .034), 2.2rem); --hm-pad: 2rem; }
.cd-head, .cd-count, .his-moon { grid-column: 2; justify-self: center; position: relative; z-index: 2; }
.cd-head { grid-row: 2; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 1.6rem 1rem 0; }
/* STANDING RULE (task 014): a backdrop behind text is a real element that fades with that text, never a pseudo-element of a container that stays visible */
.cd-scrim { position: absolute; z-index: -1; inset: -4% -8% -14%; background: radial-gradient(closest-side, rgba(var(--halo), .7), rgba(var(--halo), .35) 65%, transparent); pointer-events: none; opacity: 0; }
.cd-kicker { margin: 0; padding-left: .3em; font-weight: 400; font-size: clamp(.84rem, .7rem + .4vw, 1.08rem); color: var(--hero-label); }
.cd-kicker { text-shadow: 0 0 5px rgba(var(--halo), 1), 0 0 14px rgba(var(--halo), 1), 0 0 30px rgba(var(--halo), .9); }
.cd-title { margin: var(--sp-lt) 0 0; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3rem, 5.2vw, 6.5rem), calc(var(--vh) * 0.13)); line-height: 1.1; color: var(--gold-300); text-shadow: 0 0 10px rgba(var(--halo), 1), 0 0 30px rgba(var(--halo), .9); }
.cd-count { grid-row: 3; display: flex; flex-direction: column; align-items: center; padding: var(--sp-tc) 0 0; }
.cd-digits { display: flex; gap: clamp(.6rem, 1.2vw, 1.6rem); justify-content: center; }
.cd-cell { display: flex; flex-direction: column; align-items: center; min-width: clamp(64px, 7.4vw, 140px); padding: .5em .4em .6em; border-radius: 14px; background: rgba(var(--halo), .6); border: 1px solid rgba(243, 210, 140, .4); }
.cd-num { font-family: var(--font-title); font-weight: 600; font-variant-numeric: tabular-nums; font-size: min(clamp(1.8rem, 3.8vw, 4.4rem), calc(var(--vh) * 0.09)); line-height: 1.1; color: var(--gold-300); }
.cd-cell .label { margin-top: .2rem; padding-left: .3em; font-size: clamp(.6rem, .55rem + .2vw, .78rem); color: var(--cream); }
/* C2: equal padding left and right (>= 1.2em of the largest text), the card hugs its content */
.his-moon { /* task 018: NO will-change. A promoted layer is rastered at a fractional offset (this card sits at x 778.36 / y 524.28) that differs per page load, so its 1px edge shimmered; unpromoted, the card rasters with the page and is pixel-identical every load */ grid-row: 4; margin-top: var(--sp-cm); display: flex; align-items: center; gap: clamp(.9rem, 2vw, 1.8rem); padding: .9rem var(--hm-pad); width: fit-content; border-radius: 99px; background: rgba(var(--halo), .72); border: 1px solid rgba(243, 210, 140, .45); max-width: 100%; }
.asc { position: absolute; left: 0; width: max(62vw, calc(var(--vh) * 0.8)); top: 0; opacity: 0; visibility: hidden; will-change: transform; }
.asc .bank { width: 100%; height: auto; }
.asc img { display: block; width: 100%; height: auto; }
.asc-a { left: -9vw; }
.asc-b { left: auto; right: -9vw; }
.asc-star { position: absolute; left: 50%; top: 50%; width: 46px; height: 46px; margin: -23px 0 0 -23px; opacity: 0; visibility: hidden; border-radius: 50%; background: radial-gradient(closest-side, #FFF5E4 0, rgba(255, 233, 170, .95) 22%, rgba(243, 210, 140, .45) 55%, transparent 100%); pointer-events: none; will-change: transform; }
.hm-disc { position: relative; flex: none; width: clamp(64px, min(11vw, 15vh), 150px); aspect-ratio: 1; }
.hm-disc picture, .hm-disc img { position: absolute; inset: 0; width: 100%; height: 100%; }
.hm-dim img { opacity: .2; filter: brightness(.6) saturate(.7); }
.hm-lit { -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-size: 100% 100%; mask-size: 100% 100%; filter: drop-shadow(0 0 10px rgba(243, 210, 140, .55)); }
.hm-text { text-align: left; }
.hm-label { margin: 0 0 .2rem; color: var(--gold-300); }
.hm-title { margin: 0; font-family: var(--font-title); font-weight: 600; font-size: clamp(1rem, .8rem + .6vw, 1.5rem); color: var(--cream); line-height: 1.2; }
.hm-line { margin: .15rem 0 0; font-style: italic; font-size: clamp(1rem, .9rem + .35vw, 1.3rem); color: var(--lavender); }
@media (orientation: portrait) {
  .cd-stage { --sp-lt: calc(var(--vh) * .007); --sp-tc: calc(var(--vh) * .014); --sp-cm: calc(var(--vh) * .016); --hm-pad: 1.4rem; grid-template-columns: 1fr; grid-template-rows: 1fr auto auto auto auto 1fr; row-gap: 0; padding: 0 4vw; }
  .orb-rings circle { stroke-width: 1.25; }
  .cd-head, .cd-count, .his-moon { grid-column: 1; }
  .cd-head { grid-row: 2; padding: .2rem .5rem 0; }
  .cd-title { font-size: min(10.6vw, calc(var(--vh) * 0.068)); white-space: nowrap; }
  .orrery { grid-column: 1; grid-row: 3; width: min(calc(var(--vh) * 0.35), 78vw); margin-top: var(--sp-tc); }
  .cd-count { grid-row: 4; padding: var(--sp-tc) 0 0; justify-self: stretch; }
  .cd-digits { gap: .35rem; width: 100%; }
  .cd-cell { min-width: 0; flex: 1; padding: .4em .1em .5em; }
  .cd-num { font-size: min(2.1rem, calc(var(--vh) * 0.05)); }
  .cd-cell .label { letter-spacing: .02em; padding-left: .02em; font-size: min(.54rem, calc(var(--vh) * 0.0145), 2.85vw); }
  .his-moon { grid-row: 5; padding: .55rem var(--hm-pad); border-radius: 28px; max-width: 100%; gap: .8rem; }
  .hm-title { font-size: clamp(.9rem, 4vw, 1.1rem); }
  .hm-disc { width: min(calc(var(--vh) * 0.09), 22vw); }
  .asc { width: 118vw; }
  .asc-a { left: -14vw; }
  .asc-b { right: -14vw; }
}
/* ---------- Scene 4 · Where (task 011) ---------- */
/* Desktop: the star chart on the left (~55%), kicker / title / venue / buttons on the right.
   Phone: kicker + title, the chart (36 dvh), then venue, address, date and buttons, all on one screen. */
.wh-stage { position: absolute; inset: 0; overflow: hidden; display: grid; grid-template-columns: 1.22fr 1fr; grid-template-rows: 1fr auto auto 1fr; column-gap: 2vw; padding: 0 4vw; --wh-gap: clamp(.8rem, calc(var(--vh) * .03), 2rem); }
.chart { grid-column: 1; grid-row: 1 / -1; align-self: center; justify-self: center; width: min(54vw, calc(var(--vh) * 1.1)); aspect-ratio: 600 / 440; overflow: visible; }
.wh-head, .wh-info { grid-column: 2; justify-self: center; position: relative; z-index: 2; text-align: center; }
.wh-head { grid-row: 2; display: flex; flex-direction: column; align-items: center; padding: 0 1rem; }
.wh-kicker { margin: 0; padding-left: .3em; font-weight: 400; font-size: clamp(.84rem, .7rem + .4vw, 1.08rem); color: var(--hero-label); }
.wh-title { margin: var(--wh-gap) 0 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3.2rem, 6vw, 7rem), calc(var(--vh) * 0.15)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.wh-info { grid-row: 3; display: flex; flex-direction: column; align-items: center; padding-top: var(--wh-gap); max-width: 100%; }
.wh-panel { box-sizing: border-box; display: flex; flex-direction: column; align-items: center; width: fit-content; max-width: 100%; padding: 1.15rem 1.8rem .85rem; border-radius: 22px; background: rgba(var(--halo), .72); border: 1px solid rgba(243, 210, 140, .45); }
.wh-venue { margin: 0; font-family: var(--font-title); font-weight: 600; text-transform: uppercase; letter-spacing: .1em; padding-left: .1em; font-size: min(clamp(1.3rem, 2.2vw, 2.4rem), calc(var(--vh) * 0.05)); line-height: 1.2; color: var(--cream); text-shadow: 0 0 8px rgba(var(--halo), 1), 0 0 22px rgba(var(--halo), .9); }
.wh-line { margin: .3rem 0 0; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.1rem, 1.6vw, 1.7rem), calc(var(--vh) * 0.04)); line-height: 1.3; color: var(--hero-line); text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); }
.wh-line .sep { color: var(--gold-300); padding: 0 .2em; }
.wh-actions { position: relative; display: flex; flex-wrap: wrap; justify-content: center; gap: .8rem; margin-top: var(--wh-gap); }
.wh-btn { display: inline-flex; align-items: center; justify-content: center; box-sizing: border-box; min-height: 46px; padding: 0 1.5rem; border-radius: 99px; border: 1px solid var(--gold-300); background: rgba(var(--halo), .55); color: var(--cream); font-family: var(--font-label); font-weight: 400; font-size: .8rem; letter-spacing: .18em; padding-left: calc(1.5rem + .18em); text-transform: uppercase; text-decoration: none; white-space: nowrap; cursor: pointer; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: box-shadow .3s ease, background .3s ease; }
.wh-btn:hover, .wh-btn:focus-visible { box-shadow: 0 0 14px 1px rgba(243, 210, 140, .55), inset 0 0 10px rgba(243, 210, 140, .18); background: rgba(var(--halo), .75); }
.wh-btn:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 3px; }
.save-menu { position: absolute; left: 50%; bottom: calc(100% + .6rem); transform: translateX(-50%); z-index: 5; display: flex; flex-direction: column; gap: .5rem; padding: .6rem; border-radius: 26px; border: 1px solid rgba(243, 210, 140, .55); background: rgba(var(--halo), .94); box-shadow: 0 0 24px rgba(var(--halo), .9); }
.save-menu[hidden] { display: none; }
.save-menu .sm-item { background: transparent; }
.ch-deco .compass { opacity: .35; fill: none; stroke: var(--gold-300); stroke-width: 1; }
.ch-deco .compass text { fill: var(--gold-300); stroke: none; font: 400 12px var(--font-label); letter-spacing: .1em; }
.ch-deco .arc { fill: none; stroke: var(--gold-300); stroke-opacity: .28; stroke-width: 1.2; stroke-dasharray: 1 7; stroke-linecap: round; }
.cl, .cl-dot { fill: none; stroke: var(--gold-300); stroke-opacity: .95; stroke-width: 2.2; stroke-linecap: round; vector-effect: non-scaling-stroke; filter: drop-shadow(0 0 4px rgba(243, 210, 140, .45)); }
.cl-dot { stroke-dasharray: 1 7; } /* same width as the solid segments (task 012: owner found the lines hard to see) */
.cs { fill: var(--gold-300); filter: drop-shadow(0 0 4px rgba(255, 233, 170, .95)) drop-shadow(0 0 9px rgba(243, 210, 140, .5)); }
.venue-star { fill: #FFF0C8; }
.v-label { fill: var(--cream); font: 400 14px var(--font-label); letter-spacing: .16em; }
.wh-shoot { fill: none; stroke: #FFF0C8; stroke-width: 2; stroke-linecap: round; opacity: 0; }
/* task 060: the guiding light (a cream core in a soft gold glow, gradients only) and the line's brief glow behind it */
.wl-core { fill: #FFF8E6; }
.cl-hi { fill: none; stroke: #FFF1CC; stroke-width: 4; stroke-opacity: .55; stroke-linecap: round; stroke-linejoin: round; vector-effect: non-scaling-stroke; }
/* the map porthole */
.porthole { position: fixed; inset: 0; z-index: 70; display: grid; place-items: center; }
.porthole[hidden] { display: none; }
.ph-back { position: absolute; inset: 0; background: rgba(var(--halo), .72); }
.ph-win { position: relative; display: flex; flex-direction: column; align-items: center; gap: 1rem; pointer-events: none; }
.ph-rim, .ph-bar > * { pointer-events: auto; }
.ph-rim { --d: min(80vw, calc(var(--vh) * .7)); width: var(--d); height: var(--d); border-radius: 50%; padding: 7px; box-sizing: border-box; background: linear-gradient(135deg, #FFF0C8, #D4A24C 45%, #8A5C14 75%, #F3D28C); box-shadow: 0 0 0 3px rgba(var(--halo), 1), 0 0 0 5px var(--gold-500), 0 0 36px rgba(243, 210, 140, .45); }
.ph-map { width: 100%; height: 100%; border-radius: 50%; overflow: hidden; background: #0B1030; box-shadow: inset 0 0 0 2px rgba(var(--halo), 1); }
.ph-map iframe { width: 100%; height: 100%; border: 0; display: block; }
.ph-bar { display: flex; align-items: center; gap: .8rem; }
.ph-x { width: 46px; height: 46px; border-radius: 50%; border: 1px solid var(--gold-300); background: rgba(var(--halo), .75); cursor: pointer; display: grid; place-items: center; padding: 0; touch-action: manipulation; transition: box-shadow .3s ease; }
.ph-x svg { width: 18px; height: 18px; fill: none; stroke: var(--gold-300); stroke-width: 2; stroke-linecap: round; }
.ph-x:hover, .ph-x:focus-visible { box-shadow: 0 0 14px 1px rgba(243, 210, 140, .55); }
.ph-x:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 3px; }
@media (orientation: portrait) {
  .wh-stage { grid-template-columns: 1fr; grid-template-rows: 1fr auto auto auto 1fr; padding: 0 4vw; --wh-gap: calc(var(--vh) * .016); }
  .wh-head, .wh-info { grid-column: 1; }
  .wh-head { grid-row: 2; padding: 0 .5rem; }
  .wh-title { font-size: min(14vw, calc(var(--vh) * 0.085)); }
  .chart { grid-column: 1; grid-row: 3; width: 100%; height: calc(var(--vh) * .36); aspect-ratio: auto; margin-top: var(--wh-gap); }
  .wh-info { grid-row: 4; justify-self: stretch; padding-top: 0; }
  .cl, .cl-dot { stroke-width: 1.6; }
  .wh-panel { padding: .8rem 1.2rem; }
  .wh-venue { font-size: min(1.15rem, calc(var(--vh) * 0.03), 5.6vw); }
  .wh-line { font-size: min(1.1rem, calc(var(--vh) * 0.026)); margin-top: .2rem; }
  .wh-actions { width: 100%; flex-wrap: nowrap; gap: .5rem; }
  .wh-actions .wh-btn:not(.sm-item) { flex: 1; min-width: 0; padding: 0 .5rem 0 calc(.5rem + .12em); font-size: min(.72rem, 3.1vw); letter-spacing: .12em; }
  .save-menu { width: max-content; max-width: 92vw; }
}
/* ---------- Scene 5 · Books for Baby (task 013) ---------- */
/* A code-built 3D hardcover. Desktop: an open two-page spread (~62% of the width); phone: the right page only, spine at the
   left edge. Leaves rotate about the spine (transform-origin: left); each leaf's front is the right page, its back the next left page. */
#books { --bh: min(calc(var(--vh) * .66), 41vw); --pw: calc(var(--bh) * .75); --lw: 11vw; }
.bk-lanterns, .bk-stars { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
.bk-stars { z-index: 1; }
.bk-stage { position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: calc(var(--vh) * .022); pointer-events: none; }
.bk-head { display: flex; flex-direction: column; align-items: center; text-align: center; gap: calc(var(--vh) * .012); }
.bk-kicker { margin: 0; padding-left: .3em; font-weight: 400; font-size: clamp(.84rem, .7rem + .4vw, 1.08rem); color: var(--hero-label); }
.bk-title { margin: 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3rem, 5.4vw, 6.4rem), calc(var(--vh) * .12)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.bk-fig { position: relative; width: calc(var(--pw) * 2); height: var(--bh); perspective: calc(var(--pw) * 4.5); perspective-origin: 50% 40%; }
.bk-slide { position: absolute; inset: 0; transform-style: preserve-3d; }
.bk-book { position: absolute; left: 50%; top: 0; width: var(--pw); height: 100%; transform-style: preserve-3d; }
.bk-pgs { position: absolute; inset: 0; transform-style: preserve-3d; }
.bk-base { position: absolute; left: -100%; top: 1.5%; width: 200%; height: 97%; border-radius: 4px; box-shadow: 0 calc(var(--pw) * .05) calc(var(--pw) * .14) rgba(3, 5, 20, .7), 0 0 calc(var(--pw) * .1) rgba(243, 210, 140, .08); opacity: 0; }
/* task 018 shadow rule: a shadow always matches the shape it belongs to. .bk-csh is the CLOSED book's shadow (the cover's box, an
   empty box so only the outer shadow paints); .bk-base is the open spread's. They hand over only while the book is at rest. */
.bk-csh { position: absolute; left: 0; top: -1.6%; width: 101.8%; height: 103.2%; border-radius: 3px 9px 9px 3px; box-shadow: 0 calc(var(--pw) * .05) calc(var(--pw) * .14) rgba(3, 5, 20, .7), 0 0 calc(var(--pw) * .1) rgba(243, 210, 140, .08); pointer-events: none; }
.bk-leaf { position: absolute; inset: 0; transform-origin: 0 50%; transform-style: preserve-3d; }
.bk-cover { inset: -1.6% -1.8% -1.6% 0; }
.bk-face { position: absolute; inset: 0; overflow: hidden; backface-visibility: hidden; -webkit-backface-visibility: hidden; }
.bk-back { transform: rotateY(180deg); }
.bk-front { border-radius: 0 3px 3px 0; }
.bk-back { border-radius: 3px 0 0 3px; }
.bk-cloth { border-radius: 3px 9px 9px 3px; background: radial-gradient(120% 90% at 30% 18%, #22347a 0%, #14204f 52%, #0a1033 100%), #101a46; box-shadow: inset 0 0 0 1px rgba(243, 210, 140, .12), inset 0 0 40px rgba(3, 5, 20, .55); }
.bk-cloth::before { content: ""; position: absolute; inset: 0; background: repeating-linear-gradient(45deg, rgba(255, 255, 255, .028) 0 1px, transparent 1px 3px), repeating-linear-gradient(-45deg, rgba(255, 255, 255, .02) 0 1px, transparent 1px 3px); }
.bk-board { background: #E9D9B8; border-radius: 9px 3px 3px 9px; }
.bk-cart, .bk-cart img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.bk-cart img { object-fit: fill; }
/* the live gold-foil title sits in the clear upper third of the painted cover */
.bk-ctitle { position: absolute; left: 0; right: 0; top: 6%; margin: 0; display: flex; flex-direction: column; align-items: center; font-family: var(--font-script); font-weight: 400; font-size: calc(var(--pw) * .15); line-height: 1.12; text-align: center; white-space: nowrap; }
.bk-ctitle span { padding: 0 .1em .08em; background: linear-gradient(135deg, #F3D28C 0%, #FFF0C8 35%, #D4A24C 62%, #F3D28C 100%); -webkit-background-clip: text; background-clip: text; color: transparent; filter: drop-shadow(0 1px 2px rgba(3, 5, 20, .6)); }
.bk-hinge { position: absolute; left: 5%; top: 0; bottom: 0; width: 2.4%; background: linear-gradient(90deg, rgba(0, 0, 0, .5), rgba(255, 255, 255, .06) 50%, rgba(0, 0, 0, .4)); }
.bk-paper, .bk-paper img { position: absolute; inset: 0; display: block; width: 100%; height: 100%; }
.bk-paper img { object-fit: cover; }
.bk-endpaper { position: absolute; inset: 1.6% 1.8% 1.6% 0; overflow: hidden; background: #F4E6C8; }
.bk-pg { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: calc(var(--pw) * .08); padding: 0 13%; text-align: center; }
.bk-pass { margin: 0; font-family: var(--font-body); font-weight: 500; font-size: calc(var(--pw) * .072); line-height: 1.38; color: var(--ink); text-wrap: balance; }
.bk-orn { width: calc(var(--pw) * .26); height: auto; overflow: visible; }
.bk-pg .gl { fill: none; stroke: #B98A34; stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
.bk-pg .gl.thin { stroke-width: .9; }
.bk-pg .gf { fill: #C49632; }
/* task 015 page-ornament rule: a page carries only its passage and the centred divider (.bk-orn); no art on any leaf face */
.bk-gut { position: absolute; top: 0; bottom: 0; left: 0; width: 15%; background: linear-gradient(90deg, rgba(70, 46, 12, .3), rgba(70, 46, 12, .1) 45%, transparent); pointer-events: none; }
.bk-back .bk-gut { left: auto; right: 0; background: linear-gradient(270deg, rgba(70, 46, 12, .3), rgba(70, 46, 12, .1) 45%, transparent); }
.bk-shade { position: absolute; inset: 0; opacity: 0; pointer-events: none; background: linear-gradient(90deg, rgba(14, 18, 52, 0) 0%, rgba(14, 18, 52, .62) 48%, rgba(14, 18, 52, 0) 100%); background-size: 260% 100%; background-position: 100% 0; }
.bk-hits { position: absolute; inset: 0; pointer-events: none; }
/* task 016: the page-turn controls are glowing solid triangles at the bottom corners of the pages (right: the right page's bottom-right;
   left: the visible left page's bottom-left on desktop, the page's bottom-left on a phone). 48px hit area, 14px triangle. Which one
   shows follows the spread (1 = next only, 2 = both, 3 = prev only) and both fade out while a page turns (js/books.js paintArrows). */
.bk-hit { position: absolute; bottom: calc(var(--pw) * .02); width: 48px; height: 48px; display: grid; place-items: center; padding: 0; border: 0; background: none; cursor: pointer; pointer-events: auto; touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: opacity .35s ease, visibility 0s; }
.bk-next { right: calc(var(--pw) * .02); }
.bk-prev { left: calc(var(--pw) * .02); }
.bk-hit svg { width: 14px; height: 16px; overflow: visible; fill: var(--gold-300); stroke: var(--gold-500); stroke-width: .8; stroke-linejoin: round; filter: drop-shadow(0 0 7px rgba(243, 210, 140, .95)) drop-shadow(0 0 2px rgba(212, 162, 76, .9)); animation: bk-breathe 2.2s ease-in-out infinite; }
@keyframes bk-breathe { 0%, 100% { opacity: .55; } 50% { opacity: 1; } }
@media (hover: hover) { .bk-hit:hover svg { opacity: 1; animation: none; } }
.bk-hit:focus-visible svg { opacity: 1; animation: none; }
.bk-hit:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -6px; border-radius: 50%; }
.bk-hit.off { opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .3s ease, visibility 0s .3s; }
.bk-pager { display: flex; gap: .2rem; }
.bk-pager b { width: 26px; height: 26px; display: grid; place-items: center; }
.bk-pager i { width: 14px; height: 14px; background: var(--gold-300); opacity: .3; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); transition: opacity .4s ease, transform .4s ease; }
.bk-pager b.on i { opacity: 1; transform: scale(1.25); }
.bk-pager b.on { filter: drop-shadow(0 0 5px rgba(255, 233, 170, .9)); }
.bk-lan { position: absolute; left: var(--lx); top: 100%; width: calc(var(--s) * var(--lw)); margin-left: calc(var(--s) * var(--lw) / -2); }
.bk-rise, .bk-sway { position: relative; }
.bk-lan picture { position: relative; display: block; transform: rotate(var(--r)); }
.bk-lan img { display: block; width: 100%; height: auto; }
.bk-glow { position: absolute; inset: -22%; border-radius: 50%; background: radial-gradient(closest-side, rgba(255, 205, 120, .4), rgba(255, 190, 100, .13) 58%, transparent 78%); }
.bk-st { position: absolute; left: 0; top: 0; width: 34px; height: 34px; margin: -17px 0 0 -17px; opacity: 0; filter: drop-shadow(0 0 5px rgba(255, 233, 170, .95)) drop-shadow(0 0 11px rgba(243, 210, 140, .5)); }
.bk-st i { display: block; width: 100%; height: 100%; background: #F3D28C; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); }
@media (orientation: portrait) {
  #books { --bh: min(calc(var(--vh) * .62), 114.6vw); --lw: 17vw; }
  .bk-fig { width: var(--pw); }
  .bk-book { left: 0; }
  .bk-base { left: 0; width: 100%; }
  .bk-title { font-size: min(14vw, calc(var(--vh) * .085)); }
  .bk-lan { left: var(--lxp); } /* kept in the margins beside the page text, whatever the sway */
  .bk-lan:nth-child(1) { left: 4%; }
  .bk-lan:nth-child(2) { left: 96%; }
  .bk-lan:nth-child(3) { left: 3%; }
  .bk-lan:nth-child(4) { left: 97%; }
  .bk-lan:nth-child(n+5):not(.bk-ex), .bk-ex:nth-child(n+12) { display: none; } /* phone pool: 4 transition lanterns + 4 clones */
  .bk-pass { font-size: calc(var(--pw) * .078); }
}
/* ---------- Scene 6 · The Diaper Fund (task 017) ---------- */
/* Desktop: the jar on the left (about 70 dvh tall), kicker / title / panel / button on the right (the Section 4 rhythm).
   Phone: kicker + title, the jar, then the panel and button, all on one screen. The stardust canvas sits between the jar's back
   glow and the jar art and is clipped to the measured inner outline of the glass (js/fund.js). */
.fd-stage { position: absolute; inset: 0; overflow: hidden; display: grid; grid-template-columns: 1.22fr 1fr; grid-template-rows: 1fr auto auto 1fr; column-gap: 2vw; padding: 0 4vw; --wh-gap: clamp(.8rem, calc(var(--vh) * .03), 2rem); }
.fd-jar { position: relative; grid-column: 1; grid-row: 1 / -1; align-self: center; justify-self: center; height: calc(var(--vh) * .7); aspect-ratio: 3 / 4; }
.fd-glow { position: absolute; inset: 4% -14% 0; border-radius: 50%; background: radial-gradient(closest-side, rgba(243, 210, 140, .34), rgba(184, 178, 234, .14) 58%, transparent 80%); pointer-events: none; }
.fd-dust { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* task 018 large-tap-surface rule: a tap surface bigger than ~15% of the screen is never a <button> (the swipe Observer ignores
   buttons, so a swipe starting on it would die). The jar is a div role=button; js/fund.js detects the tap (< 10 px, < 500 ms). */
.fd-art { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; transform-origin: 50% 55%; }
.fd-art picture, .fd-art img { display: block; width: 100%; height: 100%; }
.fd-art:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; border-radius: 24px; }
.fd-sp { position: absolute; left: var(--x); top: var(--y); width: 15px; height: 15px; margin: -7px 0 0 -7px; background: #FFF0C8; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); opacity: 0; pointer-events: none; animation: fd-twinkle 6.4s ease-in-out var(--d) infinite; }
@keyframes fd-twinkle { 0%, 70%, 100% { opacity: 0; transform: scale(.4); } 80% { opacity: .95; transform: scale(1); } 90% { opacity: 0; transform: scale(.5); } }
.fd-head, .fd-info { grid-column: 2; justify-self: center; position: relative; z-index: 2; text-align: center; }
.fd-head { grid-row: 2; display: flex; flex-direction: column; align-items: center; padding: 0 1rem; }
.fd-kicker { margin: 0; padding-left: .3em; font-weight: 400; color: var(--hero-label); }
.fd-title { margin: var(--wh-gap) 0 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3.2rem, 6vw, 7rem), calc(var(--vh) * 0.15)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.fd-info { grid-row: 3; display: flex; flex-direction: column; align-items: center; padding-top: var(--wh-gap); max-width: 100%; }
.fd-panel { max-width: min(100%, 31rem); }
.fd-copy { margin: 0; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.05rem, 1.45vw, 1.55rem), calc(var(--vh) * .036)); line-height: 1.38; color: var(--hero-line); text-wrap: balance; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); }
.fd-actions { display: flex; justify-content: center; margin-top: var(--wh-gap); }
.fd-btn.is-off { opacity: .55; border-color: rgba(243, 210, 140, .4); cursor: default; }
.fd-btn.is-off:hover { box-shadow: none; background: rgba(var(--halo), .55); }
.fd-ribbon { position: absolute; inset: 0; z-index: 1; /* above the sky and the jar, below the book (.bk-stage is z-index 2) */ pointer-events: none; overflow: hidden; }
.fd-rb { position: absolute; left: 0; top: 0; width: 9px; height: 9px; margin: -4.5px 0 0 -4.5px; border-radius: 50%; opacity: 0; background: radial-gradient(closest-side, #FFF6D8, #F3D28C 55%, rgba(243, 210, 140, 0)); box-shadow: 0 0 8px 2px rgba(243, 210, 140, .55); }
.fd-rb.rg-rb { box-shadow: 0 0 4px 1px rgba(243, 210, 140, .5); }
@media (orientation: portrait) {
  .fd-stage { grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto auto auto 1fr; padding: 0 4vw; --wh-gap: calc(var(--vh) * .016); }
  .fd-head, .fd-info { grid-column: 1; }
  .fd-head { grid-row: 2; padding: 0 .5rem; }
  .fd-title { font-size: min(13vw, calc(var(--vh) * 0.085)); }
  .fd-jar { grid-column: 1; grid-row: 3; height: calc(var(--vh) * .42); margin-top: var(--wh-gap); }
  .fd-info { grid-row: 4; justify-self: stretch; padding-top: 0; }
  .fd-panel { padding: .8rem 1.2rem; max-width: 100%; }
  .fd-copy { font-size: min(1.12rem, calc(var(--vh) * .027), 5.2vw); line-height: 1.32; }
}
button.wh-btn { -webkit-appearance: none; appearance: none; margin: 0; line-height: inherit; text-align: center; } /* task 057: #fd-btn is a <button>, same look as the <a> buttons */
.fd-pay-ns { display: flex; flex-wrap: wrap; justify-content: center; gap: .4rem 1.4rem; margin: var(--wh-gap) 0 0; }
.fd-pay-ns-note { flex-basis: 100%; font-family: var(--font-body); font-style: italic; font-size: 1rem; line-height: 1.32; color: var(--hero-line); text-align: center; } /* task 066: Amazon's note, on its own line */
.fd-pay-link { display: inline-flex; align-items: center; min-height: 44px; font-family: var(--font-label); font-size: .8rem; letter-spacing: .16em; text-transform: uppercase; color: var(--gold-300); text-decoration: underline; text-underline-offset: .3em; }
/* The Diaper Fund popup (task 057, js/fundpop.js): a native modal <dialog> over the whole viewport. A plain dim behind; the card is
   night-900 at .96 and NO backdrop blur (task 065: a backdrop-filter re-blurs whenever anything under it repaints; .94 + blur(10px)
   before, the same look; the scene behind holds while it is open, js/lib/modalhold.js), a gold hairline frame with the site's
   star at its four corners. The logos are the services' own marks on white tiles: never recoloured, cropped or altered. Only transform
   and opacity animate (the GSAP open / close); nothing runs while it is closed. */
.fp { position: fixed; inset: 0; z-index: 90; width: 100%; height: 100%; max-width: none; max-height: none; margin: 0; padding: 12px; box-sizing: border-box; border: 0; background: transparent; color: var(--cream); overflow: hidden; }
.fp[open] { display: grid; place-items: center; }
.fp::backdrop { background: transparent; }
.fp-dim { position: absolute; inset: 0; background: rgba(5, 8, 26, .62); opacity: 0; }
.fp-sparks { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; z-index: 2; }
.fp-card { position: relative; z-index: 1; box-sizing: border-box; width: min(92vw, 420px); max-height: calc(100vh - 24px); max-height: calc(100dvh - 24px); overflow-y: auto; overscroll-behavior: contain; touch-action: pan-y;
  --fp-text: 1.12rem; /* task 058: the intro and the three notes share one size and line-height */
  padding: 26px 22px 22px; border-radius: 18px; background: rgba(11, 16, 48, .96); box-shadow: 0 18px 60px rgba(0, 0, 0, .5), 0 0 30px rgba(243, 210, 140, .12); text-align: center; opacity: 0; transform: translateY(12px); }
.fp-card::before { content: ""; position: absolute; inset: 7px; border: 1px solid rgba(212, 162, 76, .6); border-radius: 12px; pointer-events: none; }
.fp-corner { position: absolute; width: 11px; height: 11px; background: var(--gold-300); clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); pointer-events: none; }
.fp-corner.c1 { left: 2px; top: 2px; } .fp-corner.c2 { right: 2px; top: 2px; } .fp-corner.c3 { left: 2px; bottom: 2px; } .fp-corner.c4 { right: 2px; bottom: 2px; }
.fp-star { display: block; width: 18px; height: 18px; margin: 0 auto 4px; background: var(--gold-300); clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); }
.fp-title { margin: 0; padding: 0 .2em .05em; font-family: var(--font-script); font-weight: 400; font-size: 2.6rem; line-height: 1.15; color: var(--gold-300); }
.fp-intro { margin: .35rem auto 0; max-width: 22rem; font-family: var(--font-body); font-weight: 500; font-size: var(--fp-text); line-height: 1.32; color: var(--cream); text-wrap: balance; }
.fp-rows { list-style: none; margin: 1rem 0 0; padding: 0; display: flex; flex-direction: column; gap: .55rem; text-align: left; }
.fp-item[hidden], .fp-note[hidden] { display: none; }
/* task 066: the bordered box is the whole item: the link row on top, its note under a gold hairline INSIDE the same border. The note is
   not part of the link (tapping it opens nothing); the hover / focus highlight stays on the link part. */
.fp-item { border-radius: 14px; border: 1px solid rgba(243, 210, 140, .22); background: rgba(255, 245, 228, .04); }
.fp-row { display: flex; align-items: center; gap: 12px; min-height: 60px; padding: 8px 8px 8px 10px; box-sizing: border-box; border-radius: 13px; color: var(--cream); text-decoration: none; -webkit-tap-highlight-color: transparent; touch-action: manipulation; transition: background .3s ease; }
.fp-item:has(> .fp-note:not([hidden])) .fp-row { border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.fp-row:hover, .fp-row:focus-visible { background: rgba(243, 210, 140, .09); }
.fp-row:focus-visible { outline: 2px solid var(--gold-300); outline-offset: -2px; }
.fp-tile { flex: none; display: grid; place-items: center; width: 44px; height: 44px; border-radius: 11px; background: #fff; }
.fp-tile img { display: block; width: 30px; height: 30px; object-fit: contain; }
.fp-text { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }
.fp-name { font-family: var(--font-body); font-weight: 600; font-size: 1.15rem; line-height: 1.15; color: var(--cream); }
.fp-handle { font-family: var(--font-body); font-weight: 500; font-size: calc(1.15rem * .92); line-height: 1.2; color: rgba(246, 236, 214, .9); overflow-wrap: anywhere; } /* task 058: 0.92x the name, warm cream, no small caps */
.fp-pill { flex: none; display: inline-flex; align-items: center; justify-content: center; min-height: 44px; padding: 0 .9rem 0 calc(.9rem + .14em); box-sizing: border-box; border-radius: 99px; border: 1px solid var(--gold-300); color: var(--gold-300); font-family: var(--font-label); font-size: .7rem; letter-spacing: .14em; text-transform: uppercase; white-space: nowrap; }
.fp-row:hover .fp-pill, .fp-row:focus-visible .fp-pill { box-shadow: 0 0 12px rgba(243, 210, 140, .45); }
/* task 058: the notes are the intro's size and line-height, italic to set them apart, each centred under its row (066: inside its box) */
.fp-note { margin: 0; padding: .4rem .75rem .5rem; border-top: 1px solid rgba(243, 210, 140, .18); font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: var(--fp-text); line-height: 1.32; color: var(--hero-line); text-align: center; text-wrap: balance; }
.fp-foot { margin: .9rem auto 0; max-width: 22rem; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: var(--fp-text); line-height: 1.32; color: var(--hero-line); text-align: center; text-wrap: balance; }
.fp-x { position: absolute; top: 9px; right: 9px; width: 44px; height: 44px; display: grid; place-items: center; margin: 0; padding: 0; border: 0; border-radius: 50%; background: transparent; color: var(--gold-300); cursor: pointer; -webkit-tap-highlight-color: transparent; touch-action: manipulation; }
.fp-x svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; }
.fp-x:hover, .fp-x:focus-visible { background: rgba(243, 210, 140, .1); }
.fp-x:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 0; }
@media (max-width: 480px) {
  .fp-card { padding: 18px 16px 16px; --fp-text: 1.04rem; } /* task 066: four boxes fit 412x780; the gaps and padding shrink, never the text */
  .fp-title { font-size: 2.3rem; }
  .fp-rows { margin-top: .75rem; gap: .4rem; }
  .fp-row { gap: 10px; padding: 7px 7px 7px 8px; }
  .fp-note { padding: .3rem .7rem .4rem; }
  .fp-foot { margin-top: .7rem; }
  .fp-pill { width: 76px; padding: 0 .4rem 0 calc(.4rem + .1em); font-size: .64rem; letter-spacing: .1em; line-height: 1.2; white-space: normal; text-align: center; }
}
/* PHONE POPUP (task 067, js/fundpop.js PHONE_POP; replaces the 059 title anchor): the free area runs from 8 px under the top button
   row (the safe area + 4 + 44 px) down to 16 px above the viewport's bottom (and its safe area). The card is centred in it, at least
   90% of its height and never more: the gaps grow by 20% (never the text) and the auto margins share what is left; taller content
   fills the free area and scrolls inside (the auto margins collapse to 0, so nothing is hidden). The kicker and the title fade out
   behind it (fundpop.js behind). */
@media (orientation: portrait) and (max-width: 699px) {
  .fp[open] { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: calc(env(safe-area-inset-top, 0px) + 56px) 12px calc(16px + env(safe-area-inset-bottom, 0px)); }
  .fp-card { flex: none; display: flex; flex-direction: column; margin: 0; min-height: 90%; max-height: 100%; }
  .fp-card > * { flex-shrink: 0; }
  .fp-star { margin-top: auto; margin-bottom: 5px; }
  .fp-intro { margin-top: .42rem; }
  .fp-rows { margin-top: auto; padding-top: 1.2rem; gap: .66rem; }
  .fp-foot { margin-top: auto; margin-bottom: auto; padding-top: 1.08rem; }
}
@media (orientation: portrait) and (max-width: 480px) {
  .fp-card { padding: 22px 16px 19px; } /* 18 / 16 px x 1.2 */
  .fp-rows { padding-top: .9rem; gap: .48rem; }
  .fp-foot { padding-top: .84rem; }
}
/* Scene 7, The Registry (task 019). Desktop: the crystal ball on the left (68 dvh tall), kicker / title / panel / button on the right.
   Phone: kicker + title, the ball (about 40 dvh), then the panel and button, all on one screen. The nebula canvas sits between the
   back glow and the ball art and is clipped to the measured sphere circle (js/registry.js); the gift boxes fly above the art. */
.rg-stage { position: absolute; inset: 0; overflow: hidden; display: grid; grid-template-columns: 1.22fr 1fr; grid-template-rows: 1fr auto auto 1fr; column-gap: 2vw; padding: 0 4vw; --wh-gap: clamp(.8rem, calc(var(--vh) * .03), 2rem); }
.rg-ball { position: relative; z-index: 3; grid-column: 1; grid-row: 1 / -1; align-self: center; justify-self: center; height: calc(var(--vh) * .68); aspect-ratio: 1; }
.rg-glow { position: absolute; inset: 0 -10% 8%; border-radius: 50%; background: radial-gradient(closest-side, rgba(184, 178, 234, .3), rgba(243, 210, 140, .12) 58%, transparent 80%); pointer-events: none; }
.rg-neb { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
/* large-tap-surface rule: the ball is a div role=button (a swipe starting on it still reaches the scene engine); js/registry.js detects the tap */
.rg-art { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; }
.rg-art picture, .rg-art img { display: block; width: 100%; height: 100%; }
.rg-art:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; border-radius: 24px; }
.rg-sp { position: absolute; left: var(--x); top: var(--y); width: 15px; height: 15px; margin: -7px 0 0 -7px; background: #FFF0C8; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); opacity: 0; pointer-events: none; animation: fd-twinkle 6.4s ease-in-out var(--d) infinite; }
.rg-gifts { position: absolute; inset: 0; pointer-events: none; }
.rg-gift { display: none; }
.rg-gift.is-live { display: block; position: absolute; left: 50%; top: 38.16%; width: 20%; aspect-ratio: 1; margin: -10% 0 0 -10%; opacity: 0; will-change: transform; }
.rg-gift img { display: block; width: 100%; height: 100%; }
.rg-tr { position: absolute; left: 50%; top: 38.16%; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; border-radius: 50%; opacity: 0; background: radial-gradient(closest-side, #FFF6D8, #F3D28C 55%, rgba(243, 210, 140, 0)); box-shadow: 0 0 8px 2px rgba(243, 210, 140, .5); }
.rg-head, .rg-info { grid-column: 2; justify-self: center; position: relative; z-index: 2; text-align: center; }
.rg-head { grid-row: 2; display: flex; flex-direction: column; align-items: center; padding: 0 1rem; }
.rg-kicker { margin: 0; padding-left: .3em; font-weight: 400; color: var(--hero-label); }
.rg-title { margin: var(--wh-gap) 0 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3.2rem, 6vw, 7rem), calc(var(--vh) * 0.15)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.rg-info { grid-row: 3; display: flex; flex-direction: column; align-items: center; padding-top: var(--wh-gap); max-width: 100%; }
.rg-panel { max-width: min(100%, 31rem); padding: 1rem 1.8rem; /* symmetric: the shared panel pads 1.15rem above and .85rem below */ }
.rg-copy { margin: 0; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.05rem, 1.45vw, 1.55rem), calc(var(--vh) * .036)); line-height: 1.38; color: var(--hero-line); text-wrap: balance; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); }
.rg-actions { display: flex; justify-content: center; margin-top: var(--wh-gap); }
.rg-btn.is-off { opacity: .55; border-color: rgba(243, 210, 140, .4); cursor: default; }
.rg-btn.is-off:hover { box-shadow: none; background: rgba(var(--halo), .55); }
.rg-ribbon { position: absolute; inset: 0; z-index: 4; /* a sibling of the scenes, above the jar and the ball: the dust crosses both; display is set by registry.dust() */ display: none; pointer-events: none; overflow: hidden; }
@media (orientation: portrait) {
  .rg-stage { grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto auto auto 1fr; padding: 0 4vw; --wh-gap: calc(var(--vh) * .016); }
  .rg-head, .rg-info { grid-column: 1; }
  .rg-head { grid-row: 2; padding: 0 .5rem; }
  .rg-title { font-size: min(13vw, calc(var(--vh) * 0.085)); }
  .rg-ball { grid-column: 1; grid-row: 3; height: calc(var(--vh) * .4); margin-top: var(--wh-gap); }
  .rg-info { grid-row: 4; justify-self: stretch; padding-top: 0; }
  .rg-panel { padding: .8rem 1.2rem; max-width: 100%; }
  .rg-copy { font-size: min(1.12rem, calc(var(--vh) * .027), 5.2vw); line-height: 1.32; }
}
/* Scene 8, Send Your Star (task 022). Desktop: the lantern on the left (62 dvh tall) hangs from the top edge of its column on a thin gold
   chain; kicker / title / panel / button on the right. Phone: kicker + title, the lantern (36 dvh) with a short chain stub, then the panel
   and button. The star canvas sits behind the lantern art, over exactly the measured glass chamber (js/rsvp.js). */
.rs-stage { position: absolute; inset: 0; overflow: hidden; display: grid; grid-template-columns: 1.22fr 1fr; grid-template-rows: 1fr auto auto 1fr; column-gap: 2vw; padding: 0 4vw; --wh-gap: clamp(.8rem, calc(var(--vh) * .03), 2rem); }
.rs-lan { position: relative; z-index: 3; grid-column: 1; grid-row: 1 / -1; align-self: start; justify-self: center; margin-top: calc(var(--vh) * .17); height: calc(var(--vh) * .62); aspect-ratio: 2 / 3; }
.rs-chain { position: absolute; left: 49.85%; bottom: 92.6%; width: 6px; margin-left: -3px; height: 120dvh; pointer-events: none;
  background: radial-gradient(ellipse 2.2px 4.2px at 50% 50%, transparent 55%, #E9C46F 62%, #B98A33 100%) 0 0 / 6px 9px repeat-y; filter: drop-shadow(0 0 3px rgba(243, 210, 140, .55)); }
.rs-swing, .rs-sway { position: absolute; inset: 0; transform-origin: 49.85% 6.8%; }
.rs-halo { position: absolute; left: -22%; right: -22%; top: 34%; bottom: 4%; border-radius: 50%; background: radial-gradient(closest-side, rgba(255, 232, 160, .34), rgba(243, 210, 140, .12) 58%, transparent 80%); pointer-events: none; }
/* task 023: the light-up. The glow is a cream-gold radial 2.2x the lantern's width centred on the chamber (opacity set by js/rsvp.js: 0.45 at
   its steady peak); the nightlight ring canvas and the tap burst's shooting stars sit behind the lantern (z 3) and the copy (z 2). */
.rs-glow { will-change: opacity; position: absolute; left: -60%; top: 62.11%; width: 220%; aspect-ratio: 1; transform: translateY(-50%); border-radius: 50%; opacity: 0; pointer-events: none; background: radial-gradient(closest-side, rgba(255, 226, 150, .585), rgba(255, 214, 130, .26) 42%, rgba(255, 205, 120, .08) 72%, rgba(255, 205, 120, 0)); }
.rs-ring, .rs-sky { position: absolute; left: 0; top: 0; width: 100%; height: 100%; pointer-events: none; }
.rs-sky { z-index: 0; }
.rs-ring { z-index: 1; }
.rs-star { position: absolute; left: 27.93%; top: 46.68%; width: 44.62%; height: 30.86%; pointer-events: none; }
/* large-tap-surface rule: the lantern is a div role=button (a swipe starting on it still reaches the scene engine); js/rsvp.js detects the tap */
.rs-art { position: absolute; inset: 0; margin: 0; padding: 0; border: 0; background: none; cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; }
.rs-art picture, .rs-art img { display: block; width: 100%; height: 100%; }
.rs-art:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; border-radius: 24px; }
.rs-spk { position: absolute; left: var(--x); top: 6%; width: 11px; height: 11px; margin: -5px 0 0 -5px; background: #FFF0C8; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); opacity: 0; pointer-events: none; animation: rs-rise 6.4s ease-in-out var(--d) infinite; }
@keyframes rs-rise { 0%, 55%, 100% { opacity: 0; transform: translateY(0) scale(.4); } 62% { opacity: .95; transform: translateY(-2dvh) scale(1); } 100% { opacity: 0; transform: translateY(-9dvh) scale(.5); } }
html:not([data-scene="rsvp"]) .rs-spk, html:not([data-scene="rsvp"]) .rs-mine-star svg { animation-play-state: paused; }
.rs-frozen .rs-spk { animation: none; opacity: 0; }
.rs-frozen .rs-mine-star svg { animation: none; }
.rs-head, .rs-info { grid-column: 2; justify-self: center; position: relative; z-index: 2; text-align: center; }
.rs-head { grid-row: 2; display: flex; flex-direction: column; align-items: center; padding: 0 1rem; }
.rs-kicker { margin: 0; padding-left: .3em; font-weight: 400; color: var(--hero-label); }
.rs-title { margin: var(--wh-gap) 0 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3.2rem, 6vw, 7rem), calc(var(--vh) * 0.15)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.rs-info { grid-row: 3; display: flex; flex-direction: column; align-items: center; padding-top: var(--wh-gap); max-width: 100%; }
.rs-panel { max-width: min(100%, 31rem); padding: 1rem 1.8rem; /* symmetric: the shared panel pads 1.15rem above and .85rem below */ }
.rs-copy, .rs-by { margin: 0; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.05rem, 1.45vw, 1.55rem), calc(var(--vh) * .036)); line-height: 1.38; color: var(--hero-line); text-wrap: balance; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); }
.rs-by { margin-top: .45rem; }
.rs-actions { display: flex; flex-direction: column; align-items: center; margin-top: var(--wh-gap); }
.rs-btn.is-off { opacity: .55; border-color: rgba(243, 210, 140, .4); cursor: default; }
.rs-btn.is-off:hover { box-shadow: none; background: rgba(var(--halo), .55); }
/* the quiet line takes its space from the start (no layout jump when it appears); task 064 R2: the night halo alone measured 1.8:1
   (phone) / 3:1 (desktop) against the lantern-lit sky, so it sits on a soft night pill (the label-plate style, no backdrop blur) */
.rs-note { box-sizing: border-box; width: fit-content; margin: .55rem auto 0; padding: .3em 1em; max-width: min(100%, 24rem); min-height: 2.7em; border: 1px solid rgba(243, 210, 140, .25); border-radius: 1.4em; background: rgba(11, 16, 48, .78); font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(1.05rem, calc(var(--vh) * .027)); line-height: 1.3; color: var(--hero-line); text-wrap: balance; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); opacity: 0; transition: opacity .9s ease; }
.rs-note.is-sent { opacity: 1; }
/* "your star": a fixed spot clear of the copy, upper right (task 064 R1: on a phone too, clear of the menu / sound buttons at the
   top left and of the scroll-mode overlay scrollbar at the right edge) */
.rs-mine { position: absolute; z-index: 2; left: 90%; top: 13%; width: 0; height: 0; opacity: 0; pointer-events: none; }
.rs-mine-star { position: absolute; left: -50px; top: -50px; width: 100px; height: 100px; transform: scale(.3); }
.rs-mine-star svg { display: block; width: 100%; height: 100%; animation: rs-tw 3.2s ease-in-out infinite; }
@keyframes rs-tw { 0%, 100% { opacity: .8; transform: scale(.9); } 50% { opacity: 1; transform: scale(1.08); } }
.rs-mine-label { position: absolute; left: 0; top: 20px; transform: translateX(-50%); white-space: nowrap; font-family: var(--font-label); font-weight: 300; font-size: 10.5px; line-height: 1; letter-spacing: .22em; padding-left: .22em; text-transform: lowercase; color: #FFF0C8; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 12px rgba(var(--halo), .9); }
.rs-mine-star svg, .rs-hd svg { fill: #FFF3D0; overflow: visible; filter: drop-shadow(0 0 5px rgba(255, 236, 170, .95)) drop-shadow(0 0 14px rgba(243, 210, 140, .7)); }
/* the travelling star: a sibling of the scenes, drawn above them, shown only while it travels */
.rs-fly { position: absolute; inset: 0; z-index: 5; display: none; pointer-events: none; overflow: hidden; }
.rs-hd { position: absolute; left: 0; top: 0; width: 100px; height: 100px; margin: -50px 0 0 -50px; opacity: 0; will-change: transform; }
.rs-hd svg { display: block; width: 100%; height: 100%; }
.rs-tr { position: absolute; left: 0; top: 0; width: 14px; height: 14px; margin: -7px 0 0 -7px; border-radius: 50%; opacity: 0; background: radial-gradient(closest-side, #FFF6D8, #F3D28C 55%, rgba(243, 210, 140, 0)); }
@media (orientation: portrait) {
  .rs-stage { grid-template-columns: minmax(0, 1fr); grid-template-rows: 1fr auto auto auto 1fr; padding: 0 4vw; --wh-gap: calc(var(--vh) * .016); }
  .rs-head, .rs-info { grid-column: 1; }
  .rs-head { grid-row: 2; padding: 0 .5rem; }
  .rs-title { font-size: min(13vw, calc(var(--vh) * 0.085)); }
  .rs-lan { grid-column: 1; grid-row: 3; align-self: center; margin-top: var(--wh-gap); height: calc(var(--vh) * .36); }
  .rs-chain { height: 3dvh; -webkit-mask-image: linear-gradient(to top, #000, transparent); mask-image: linear-gradient(to top, #000, transparent); }
  .rs-info { grid-row: 4; justify-self: stretch; padding-top: 0; }
  .rs-panel { padding: .8rem 1.2rem; max-width: 100%; }
  .rs-copy, .rs-by { font-size: min(1.12rem, calc(var(--vh) * .027), 5.2vw); line-height: 1.32; }
  .rs-note { font-size: min(.98rem, calc(var(--vh) * .022), 4.4vw); min-height: 2.6em; }
  .rs-mine { left: 84%; top: 5%; }
  .rs-mine-star { transform: scale(.26); }
}

/* ---- Scene 9 · Goodnight, Little Star (task 024) ---- */
/* the dawn: drawn over the painted sky and under the star canvases, so the stars stay on top; its opacity follows the sky progress (main.js) */
#sky-dawn { position: absolute; inset: 0; opacity: 0; pointer-events: none; background: linear-gradient(to bottom, rgb(132, 148, 226) 0%, rgb(160, 156, 222) 22%, rgb(238, 176, 196) 55%, rgb(255, 206, 128) 100%); }
.cl-stage { position: absolute; inset: 0; overflow: hidden; --cl-gap: clamp(.7rem, calc(var(--vh) * .022), 1.6rem); }
.cl-head { position: absolute; left: 0; right: 0; top: calc(var(--vh) * .075); z-index: 2; display: flex; flex-direction: column; align-items: center; text-align: center; padding: 0 1.2rem; }
/* the scrim: a soft dark halo behind the words, so they read over the bright dawn. Task 042 C2: its top never reaches above the section
   (max() with the head's own offset), so the section edge never cuts the ellipse into a line */
.cl-head::before { content: ''; position: absolute; z-index: -1; inset: -14% -4% -18%; top: max(-14%, calc(var(--vh) * -.075)); background: radial-gradient(closest-side, rgba(11, 16, 48, .8), rgba(11, 16, 48, .46) 60%, rgba(11, 16, 48, 0)); pointer-events: none; }
.cl-kicker { margin: 0; padding-left: .3em; font-weight: 400; color: var(--hero-label); }
.cl-title { margin: var(--cl-gap) 0 0; padding: 0 .12em .1em; font-family: var(--font-script); font-weight: 400; font-size: min(clamp(3.2rem, 6vw, 7rem), calc(var(--vh) * 0.15)); line-height: 1.15; white-space: nowrap;
  background: linear-gradient(100deg, #D4A24C 0%, #F3D28C 30%, #FFF0C8 50%, #F3D28C 70%, #D4A24C 100%); -webkit-background-clip: text; background-clip: text; -webkit-text-fill-color: transparent; color: var(--gold-300); filter: drop-shadow(0 0 10px rgba(var(--halo), .9)); }
.cl-line { margin: var(--cl-gap) 0 0; max-width: 34rem; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: min(clamp(1.1rem, 1.55vw, 1.7rem), calc(var(--vh) * .038)); line-height: 1.36; color: var(--hero-line); text-wrap: balance; text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 18px rgba(var(--halo), .9); }
.cl-where { margin: var(--cl-gap) 0 0; padding-left: .26em; font-weight: 400; font-size: clamp(.6rem, .56rem + .18vw, .74rem); letter-spacing: .26em; color: var(--hero-line); text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 14px rgba(var(--halo), .9); }
/* the cats: three nested wrappers (outer: the transitions; drift and breathe: the idle), so no tween ever fights another */
.cl-cats { position: absolute; left: 0; right: 0; margin: 0 auto; bottom: calc(var(--vh) * .115); width: calc(var(--vh) * .66); aspect-ratio: 3 / 2; z-index: 1; }
.cl-snug, .cl-drift, .cl-breathe { position: absolute; inset: 0; }
.cl-snug { transform-origin: 50% 100%; }
.cl-bloom { position: absolute; left: 0; top: 0; width: 100%; height: 100%; z-index: 1; pointer-events: none; }
.cl-breathe { transform-origin: 50% 100%; }
.cl-tap { position: absolute; inset: 0; cursor: pointer; touch-action: manipulation; user-select: none; -webkit-user-select: none; -webkit-tap-highlight-color: transparent; }
.cl-tap picture, .cl-tap img { display: block; width: 100%; height: 100%; pointer-events: none; }
.cl-tap:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 4px; border-radius: 24px; }
.cl-spk { position: absolute; left: var(--x); top: var(--y); width: 12px; height: 12px; margin: -6px 0 0 -6px; background: #FFF0C8; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); opacity: 0; pointer-events: none; animation: cl-spk 7.2s ease-in-out var(--d) infinite; animation-play-state: paused; }
#closing.cl-on .cl-spk { animation-play-state: running; }
@keyframes cl-spk { 0%, 100% { opacity: 0; transform: scale(.4); } 45%, 55% { opacity: .95; transform: scale(1); } }
/* the sleepy z's float up from above the big black cat (positioned over its head; gsap drives them while the scene is active) */
.cl-z { position: absolute; left: 38%; top: 22%; margin: -.6em 0 0 -.3em; font-family: var(--font-body); font-style: italic; font-weight: 500; font-size: 13px; line-height: 1; color: var(--cream); opacity: 0; pointer-events: none; text-shadow: 0 0 6px rgba(var(--halo), .9); }
/* task 045 E1: the cat reactions' layer (js/catreactions.js; empty, and so invisible, except during a reaction) */
.cl-egg { position: absolute; inset: 0; pointer-events: none; }
.cl-egg-spk { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px; background: #FFF0C8; clip-path: polygon(50% 0, 60% 40%, 100% 50%, 60% 60%, 50% 100%, 40% 60%, 0 50%, 40% 40%); opacity: 0; }
.cl-egg-spk.gold { background: #F3D28C; }
/* task 046: cat1's warm glow pulse under the sparks; cat2's paw (task 056: small four-point stars, each over a star-shaped glow, no filter); cat3's dream
   (its own wrapper: three cream bubbles and a thought cloud with a gold hairline and a gold star) */
.cl-egg-glow { position: absolute; border-radius: 50%; background: radial-gradient(closest-side, rgba(255, 226, 160, .75), rgba(243, 210, 140, .3) 55%, rgba(243, 210, 140, 0)); opacity: 0; }
.cl-egg-paw { position: absolute; aspect-ratio: 1; }
.cl-egg-paw svg { display: block; width: 100%; height: 100%; overflow: visible; }
.cl-egg-paw .paw-line { fill: none; stroke: #F3D28C; stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; opacity: .85; }
.cl-egg-paw .paw-glow { fill: url(#cl-paw-glow); }
.cl-egg-paw .paw-star { fill: url(#cl-paw-star); }
.cl-egg-dream { position: absolute; inset: 0; }
.cl-egg-bub { position: absolute; aspect-ratio: 1; border-radius: 50%; background: radial-gradient(circle at 35% 35%, #FFFCF2, #FFF0C8 60%, #F6E2B0); box-shadow: 0 0 0 .5px rgba(212, 162, 76, .7); opacity: 0; }
.cl-egg-cloud { position: absolute; aspect-ratio: 140 / 100; opacity: 0; }
.cl-egg-cloud svg { display: block; width: 100%; height: 100%; overflow: visible; }
.cl-egg-cloud .dream-cloud { fill: #FFF6DE; stroke: #D4A24C; stroke-width: 1; vector-effect: non-scaling-stroke; }
.cl-egg-cloud .dream-star { fill: #E9BE68; }
/* task 056 E3: cat4's peeking eyes: an SVG almond (yellow-green iris, thin dark rim, slit pupil, catch-light), no glow */
.cl-egg-eye { position: absolute; aspect-ratio: 3 / 1; opacity: 0; }
.cl-egg-eye svg { display: block; width: 100%; height: 100%; overflow: visible; }
.cl-egg-eye .eye-pupil { fill: #1B1A22; }
.cl-egg-eye .eye-light { fill: #FFF5E4; }
.cl-egg-eye .eye-rim { fill: none; stroke: #1B1A22; stroke-width: .8; stroke-linejoin: round; }
.cl-egg-box { position: absolute; outline: 2px solid #f0f; outline-offset: -1px; }
.cl-egg-box::after { content: attr(data-cat); position: absolute; left: 3px; top: 1px; font: 700 12px/1 sans-serif; color: #f0f; }
/* the morning star the travelling star settles into (a sibling of the words; the outer is the transition's, the inner twinkles) */
.cl-star { position: absolute; z-index: 1; left: 82%; top: 22%; width: 0; height: 0; opacity: 0; pointer-events: none; }
.cl-star-in { position: absolute; left: -50px; top: -50px; width: 100px; height: 100px; transform: scale(.34); }
.cl-star-in svg { display: block; width: 100%; height: 100%; fill: #FFF3D0; overflow: visible; filter: drop-shadow(0 0 5px rgba(255, 236, 170, .95)) drop-shadow(0 0 14px rgba(243, 210, 140, .7)); animation: rs-tw 3.2s ease-in-out infinite; animation-play-state: paused; }
#closing.cl-on .cl-star-in svg { animation-play-state: running; }
.cl-actions { position: absolute; left: 0; right: 0; bottom: calc(var(--vh) * .03 + env(safe-area-inset-bottom, 0px)); z-index: 3; display: flex; justify-content: center; }
.cl-back { display: inline-flex; align-items: center; gap: .6rem; min-height: 46px; min-width: 46px; padding: 0 1.1rem; margin: 0; border: 0; border-radius: 23px; background: rgba(40, 28, 78, .62); cursor: pointer; font-family: var(--font-label); font-weight: 400; font-size: .74rem; letter-spacing: .22em; text-transform: uppercase; color: var(--hero-line); text-shadow: 0 0 6px rgba(var(--halo), 1), 0 0 14px rgba(var(--halo), .9); touch-action: manipulation; -webkit-tap-highlight-color: transparent; transition: color .3s ease, text-shadow .3s ease; }
.cl-back svg { width: 18px; height: 18px; flex: none; overflow: visible; }
.cl-arc { fill: none; stroke: var(--gold-300); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; }
.cl-glyph { fill: var(--gold-300); }
.cl-back:hover, .cl-back:focus-visible { color: #fff; text-shadow: 0 0 8px rgba(var(--halo), 1), 0 0 16px rgba(243, 210, 140, .7); }
.cl-back:focus-visible { outline: 2px solid var(--gold-300); outline-offset: 2px; }
@media (orientation: portrait) {
  .cl-head { top: max(calc(var(--vh) * .06), 52px); } /* task 053 L3a: the kicker clears the sound / shuffle tap areas (4..48 px) on short phones */
  .cl-head::before { inset: -24% -14% -26%; top: max(-24%, calc(var(--vh) * -.06)); background: radial-gradient(closest-side, rgba(11, 16, 48, .9), rgba(11, 16, 48, .62) 62%, rgba(11, 16, 48, 0)); }
  .cl-title { font-size: min(12.5vw, calc(var(--vh) * 0.072)); white-space: normal; text-wrap: balance; line-height: 1.1; }
  .cl-line { max-width: 90vw; font-size: min(1.12rem, calc(var(--vh) * .027), 5.2vw); line-height: 1.32; }
  .cl-cats { bottom: calc(var(--vh) * .125); width: min(96vw, calc(var(--vh) * .51)); }
  .cl-star { left: 90%; top: 3.6%; }
  .cl-star-in { transform: scale(.26); }
}
html:not(.js) #dots, html:not(.js) .env { display: none; }
html:not(.js) .letter { pointer-events: auto; }

/* task 040 · SCROLL MODE (html.mode-scroll, set by js/main.js for ?scroll or SITE_MODE = "scroll"): one plain long scroll, the nine sections stacked.
   task 041: the DOCUMENT never scrolls (as in scenes mode), so the phone address bar never hides or shows; #scenes is a fixed full-screen box and
   only it scrolls. It is 1 px shorter than the viewport so Chrome cannot promote it to the implicit root scroller; ?scroller=full
   (html.scroller-full, js/abparams.js parseScroller) makes it fill the viewport exactly. --vh stays the dynamic height, as in scenes mode. */
html.mode-scroll, html.mode-scroll body { height: 100%; overflow: hidden; overscroll-behavior: none; touch-action: auto; }
html.mode-scroll #scenes { position: fixed; left: 0; top: 0; width: 100%; height: calc(100vh - 1px); height: calc(var(--vh) - 1px); overflow-x: hidden; overflow-y: auto; overscroll-behavior: contain; -webkit-overflow-scrolling: touch; overflow-anchor: none; outline: none; }
html.mode-scroll.scroller-full #scenes { height: 100vh; height: var(--vh); }
/* task 051 S1 (replaces the task 048 R4 / 050 F2 native-bar styling): the native bar is hidden, so every section is exactly one viewport
   wide, and js/scrollbar.js floats our own over the sections: #sbar, a transparent fixed column at the right edge (above the sections,
   under #sound / the shuffle button / the perf chip; the scene dots are hidden in scroll mode), 10 px on a mouse/trackpad desktop and
   6 px on touch, holding a rounded gold thumb moved by transform only */
html.mode-scroll #scenes { scrollbar-width: none; }
html.mode-scroll #scenes::-webkit-scrollbar { display: none; }
#sbar { position: fixed; z-index: 62; right: 0; top: 0; width: 6px; height: 100%; background: transparent; touch-action: none; }
#sbar .sbar-thumb { position: absolute; left: 1px; right: 1px; top: 0; border-radius: 3px; background: rgb(243, 210, 140); opacity: .55; transition: opacity .25s ease-out; will-change: transform; }
#sbar.live .sbar-thumb { opacity: .85; }
@media (hover: hover) and (pointer: fine) and (min-width: 701px) {
  #sbar { width: 10px; }
  #sbar .sbar-thumb { left: 2px; right: 2px; border-radius: 4px; }
  #sbar .sbar-thumb:hover, #sbar.drag .sbar-thumb { background: var(--gold-500); }
}
@media (hover: none), (pointer: coarse) { #sbar, #sbar .sbar-thumb { pointer-events: none; } } /* touch: a pure indicator */
html.mode-scroll .scene { position: relative; inset: auto; width: 100%; height: 100%; overflow: hidden; visibility: visible !important; opacity: 1 !important; }
html.mode-scroll.cv .scene:not(#opening) { content-visibility: auto; contain-intrinsic-size: auto 100dvh; }
html.mode-scroll #dots, html.mode-scroll #cue { display: none !important; }
.scene.sp-held, .scene.sp-held * { animation-play-state: paused !important; } /* task 044 SCROLL PAUSE: a section's css idle waits while the scroller is busy; task 065: both modes, also under a modal (MODAL HOLD) */
.scene.sp-skip { content-visibility: hidden; } /* task 065 MODAL SKIP: a scene the open popup hides wholly skips the inert page's restyle */
html.mode-scroll #books, html.mode-scroll #books .bk-fig { touch-action: pan-y; } /* the book's swipe area: vertical drags scroll the page, sideways ones are ours */
/* task 050 F4 (replaces the task 042 C1 raise): the welcome keeps its scenes-mode layout (moon-cats under the lockup) and, in scroll mode
   only, lets its banks hang past its bottom edge (sideways it still clips), so they spill on their baked feather over the top of the
   invitation section, which paints no background of its own */
html.mode-scroll #opening, html.mode-scroll #opening > .stage { overflow-x: clip; overflow-y: visible; }
/* the banks' baked feather ends in a flat line ~7vh under the section (off-screen in scenes mode); a long static fade over the stage
   (opaque to 12vh above the edge, gone 7vh under it, past every bank) dissolves the spill instead. A mask paints only inside the
   border box, so a transparent bottom border stretches that box 8vh under the section while the padding box (the children's
   containing block) stays exactly the section: nothing moves */
html.mode-scroll #opening > .stage {
  bottom: calc(var(--vh) * -0.08); border-bottom: calc(var(--vh) * 0.08) solid transparent;
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - var(--vh) * 0.20), transparent calc(100% - var(--vh) * 0.01));
  mask-image: linear-gradient(to bottom, #000 calc(100% - var(--vh) * 0.20), transparent calc(100% - var(--vh) * 0.01));
}
/* that border hit-tests; the invitation (no background of its own, so the spill still shows through it) stacks above the welcome
   here, so every tap under the welcome's edge reaches the invitation as before */
html.mode-scroll #invitation { z-index: 3; }

#debug { position: fixed; z-index: 90; left: 8px; top: 8px; padding: 4px 8px; border-radius: 6px; background: rgba(0, 0, 0, .6); color: #9ff; font: 11px/1.4 ui-monospace, Consolas, monospace; pointer-events: none; }
