/* GENERATED — copied from docs/mocks/fablecraft.css by scripts/sync-design.mjs.
   Edit the mock, then run the script. Editing this file directly will be overwritten. */
/* ═══════════════════════════════════════════════════════════════════
   FABLECRAFT — the Once Upon a World design language, v2 (ground-up)
   ─────────────────────────────────────────────────────────────────────
   Fiction: the entire interface is a LIVING STORYBOOK. The HUD is drawn
   onto the world by a magic quill; menus are book spreads; worlds are
   leather tomes; the parent console is the Publisher's Desk.
   Materials: aged paper · iron-gall ink · gold leaf · brass · wax · enamel.
   Registers: PICTURE-BOOK (day, bright) ↔ GOTHIC TOME (ember, ink) —
   same DNA, so the product scales childish → mature.
   Inspiration bar: first-party Nintendo craft (Odyssey's travel fiction,
   Animal Crossing's material warmth), DQ Builders' chunky diorama light,
   My Time at Portia's pastoral painterliness.
   ═══════════════════════════════════════════════════════════════════ */
@import url('./fonts/fonts.css'); /* self-hosted: the mock book opens with no internet */

:root {
  /* ── ink & paper (deep ink-teal night — never purple) ── */
  --ink-950: #0b111a;          /* deepest night ink            */
  --ink-900: #101a26;          /* night sky base               */
  --ink-800: #182636;          /* panel ink                    */
  --ink-700: #24374c;          /* elevated ink                 */
  --ink-line: #2b2f3e;         /* linework on paper            */
  --paper: #f3e7cf;            /* aged parchment               */
  --paper-hi: #faf3e3;         /* fresh page                   */
  --paper-lo: #e3d0a8;         /* handled edge                 */
  --paper-shade: #cdb587;      /* deep fold                    */

  /* ── gilding & materials ─────────────────────── */
  --gold: #d4a548;             /* gold leaf                    */
  --gold-hi: #f2ce7b;          /* leaf catchlight              */
  --gold-lo: #9a7226;          /* tooled shadow                */
  --brass: #b98d4f;
  --wax: #a3372e;              /* sealing wax                  */
  --wax-hi: #cf5b47;

  /* ── story palette (grounded, painterly) ─────── */
  --meadow: #7ba05b;  --meadow-lo: #55763c;
  --pine: #2e6b5e;    --verdigris: #3e8e7e;
  --sky-day: #8fbfd9; --sky-dusk: #46698a;
  --plum: #6d4a8f;    --plum-lo: #443061;   /* small accents only — never a surface */
  --rose: #c96e8c;    --rose-lo: #9c4a68;
  --ember: #c4573b;   --ember-hi: #e8845f;
  --moon: #f0e7c9;

  /* semantic: gold = touchable · plum = the Director's hand ·
     rose = earned · verdigris = learned/grown · ember = heat/caution */

  --font-display: 'Fraunces', 'Iowan Old Style', Georgia, serif;
  --font-ui: 'Nunito Sans', 'Avenir Next', 'Segoe UI', sans-serif;
  --font-hand: 'Caveat', cursive;
  --font-mono: 'JetBrains Mono', 'SF Mono', Menlo, monospace;
}

/* ── paper grain & vignette (material, not decoration) ─────────── */
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
body {
  font-family: var(--font-ui); font-size: 16.5px; line-height: 1.55;
  color: var(--paper-hi); background: var(--ink-900); min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
/* night register: a painted teal dusk with a warm ember horizon */
body.night {
  background:
    radial-gradient(1100px 500px at 78% -6%, rgba(62,142,126,.30), transparent 60%),
    radial-gradient(900px 420px at 12% 4%, rgba(38,84,104,.35), transparent 55%),
    radial-gradient(1600px 900px at 50% 118%, rgba(196,120,58,.28) 0%, transparent 55%),
    linear-gradient(180deg, #0c1420 0%, #101a26 38%, #16283a 78%, #1d3344 100%);
}
body.night::before { /* starfield, two depths */
  content:''; position: fixed; inset: 0; pointer-events: none; z-index: 0;
  background-image:
    radial-gradient(1.6px 1.6px at 11% 12%, #fff9, transparent),
    radial-gradient(1.2px 1.2px at 27% 6%, #f2ce7b99, transparent),
    radial-gradient(2px 2px at 41% 16%, #fffb, transparent),
    radial-gradient(1.2px 1.2px at 58% 9%, #fff7, transparent),
    radial-gradient(1.7px 1.7px at 73% 14%, #f2ce7b88, transparent),
    radial-gradient(1.3px 1.3px at 88% 7%, #fff8, transparent),
    radial-gradient(1.1px 1.1px at 66% 24%, #fff5, transparent),
    radial-gradient(1.4px 1.4px at 19% 28%, #fff6, transparent);
}
body.night::after { /* gentle vignette pulls the eye center */
  content:''; position: fixed; inset: 0; pointer-events:none; z-index:0;
  box-shadow: inset 0 0 220px 40px rgba(10, 8, 22, .55);
}
/* paper register (Publisher's Desk, specimen pages) */
body.paper {
  color: var(--ink-line);
  background:
    radial-gradient(1200px 700px at 50% -10%, var(--paper-hi), transparent 70%),
    linear-gradient(180deg, var(--paper) 0%, #ecdcbc 100%);
}
body.paper::before { /* fiber grain */
  content:''; position: fixed; inset:0; pointer-events:none; opacity:.5; z-index:0;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 .45 0 0 0 0 .38 0 0 0 0 .26 0 0 0 .05 0'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23g)'/%3E%3C/svg%3E");
}
main, header, footer, nav { position: relative; z-index: 1; }

/* ── typography: storybook voice ────────────────────────────────── */
h1, h2, h3, .display { font-family: var(--font-display); letter-spacing:.2px; }
h1 { font-size: 46px; font-weight: 800; line-height: 1.08; margin: 0 0 10px;
     font-variation-settings: 'opsz' 90; }
h2 { font-size: 27px; font-weight: 700; margin: 44px 0 14px; font-variation-settings:'opsz' 40; }
h3 { font-size: 19px; font-weight: 700; margin: 14px 0 6px; }
p { margin: 6px 0; }
.lead { font-size: 18.5px; max-width: 74ch; opacity:.95; }
.muted { opacity:.66; } .paper .muted { color:#6d5c3f; opacity:1; }
.hand { font-family: var(--font-hand); font-size: 1.25em; }
.smallcaps { font-variant: small-caps; letter-spacing: 2.5px; font-weight: 700; }
h1 .gild, h2 .gild {
  background: linear-gradient(175deg, var(--gold-hi) 12%, var(--gold) 45%, var(--gold-lo) 88%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
}
.chapter { /* ── ❦ chapter rule ── */
  display:flex; align-items:center; gap:14px; margin: 46px 0 16px;
}
.chapter::before, .chapter::after {
  content:''; height:2px; flex:1;
  background: linear-gradient(90deg, transparent, var(--gold) 30%, var(--gold) 70%, transparent);
  opacity:.55;
}
.chapter h2 { margin: 0; white-space: nowrap; }
.chapter .fleuron { color: var(--gold); font-size: 18px; }

/* ── layout ─────────────────────────────────────────────────────── */
.wrap  { max-width: 1220px; margin: 0 auto; padding: 40px 28px 110px; }
.grid  { display: grid; gap: 20px; }
.grid.c2 { grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); }
.grid.c3 { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.grid.c4 { grid-template-columns: repeat(auto-fit, minmax(215px, 1fr)); }
.row   { display:flex; gap:12px; align-items:center; flex-wrap:wrap; }

/* ── top bar: a book's headband ─────────────────────────────────── */
.topbar {
  display:flex; align-items:center; gap:16px; padding: 13px 28px;
  background: linear-gradient(180deg, rgba(18,16,31,.92), rgba(18,16,31,.78));
  border-bottom: 1px solid rgba(212,165,72,.5);
  box-shadow: 0 1px 0 rgba(242,206,123,.18) inset, 0 8px 24px rgba(0,0,0,.35);
  position: sticky; top:0; z-index:60; backdrop-filter: blur(8px);
}
.paper .topbar {
  background: linear-gradient(180deg, #efe2c6, #e6d4ae);
  border-bottom: 1px solid #c9ae7c; box-shadow: 0 1px 0 #fff8 inset, 0 6px 18px rgba(80,60,20,.18);
  color: var(--ink-line);
}
.topbar a { color: inherit; text-decoration: none; }
.topbar .logo { font-family: var(--font-display); font-weight: 800; font-size: 21px; }
.topbar .logo .gild { font-style: italic; }
.topbar .crumb { font-size: 13.5px; opacity:.7; font-variant: small-caps; letter-spacing:1.5px; }
.mocktag {
  margin-left:auto; font-size: 11px; font-weight: 900; letter-spacing: 2px;
  color: var(--ink-950); background: linear-gradient(170deg, var(--gold-hi), var(--gold));
  border: 1px solid var(--gold-lo); border-radius: 3px; padding: 4px 12px;
  box-shadow: 0 1px 0 #fff6 inset, 0 2px 6px rgba(0,0,0,.4);
}

/* ── PLATE: the layered-paper panel (core surface) ──────────────── */
.plate {
  position: relative; border-radius: 10px; padding: 22px 24px;
  background:
    linear-gradient(180deg, rgba(250,243,227,.06), rgba(250,243,227,.02)),
    linear-gradient(180deg, var(--ink-800), var(--ink-900));
  border: 1px solid rgba(212,165,72,.35);
  box-shadow:
    0 0 0 1px rgba(18,16,31,.9),
    0 1px 0 rgba(242,206,123,.16) inset,
    0 -14px 34px rgba(0,0,0,.22) inset,
    0 16px 34px rgba(0,0,0,.42);
}
.plate.gilded { border-color: var(--gold); box-shadow:
    0 0 0 1px rgba(18,16,31,.9), 0 1px 0 rgba(242,206,123,.35) inset,
    0 -14px 34px rgba(0,0,0,.22) inset, 0 16px 40px rgba(0,0,0,.5), 0 0 40px rgba(212,165,72,.12); }
.plate > h3:first-child { margin-top: 2px; }
.plate .corner { /* gold corner tooling */
  position:absolute; width:26px; height:26px; pointer-events:none; opacity:.9;
  border: 2px solid var(--gold); border-radius: 2px;
}
.plate .corner.tl { top:6px; left:6px; border-right:none; border-bottom:none; border-top-left-radius:8px; }
.plate .corner.tr { top:6px; right:6px; border-left:none; border-bottom:none; border-top-right-radius:8px; }
.plate .corner.bl { bottom:6px; left:6px; border-right:none; border-top:none; border-bottom-left-radius:8px; }
.plate .corner.br { bottom:6px; right:6px; border-left:none; border-top:none; border-bottom-right-radius:8px; }

/* paper-register plate: an actual manuscript page */
.paper .plate, .plate.page {
  color: var(--ink-line);
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='g'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='2'/%3E%3CfeColorMatrix values='0 0 0 0 .5 0 0 0 0 .42 0 0 0 0 .28 0 0 0 .06 0'/%3E%3C/filter%3E%3Crect width='200' height='200' filter='url(%23g)'/%3E%3C/svg%3E"),
    linear-gradient(180deg, var(--paper-hi), var(--paper) 70%, #eddebd 100%);
  border: 1px solid #b9995f;
  box-shadow: 0 1px 0 #fffa inset, 0 -12px 26px rgba(160,120,50,.14) inset,
              0 3px 0 -1px #d9c69b, 0 4px 0 -1px #b9995f, 0 18px 30px rgba(60,42,10,.35);
}

/* ── TOME: leather book cover (worlds, rulesets) ────────────────── */
.tome {
  position: relative; border-radius: 6px 14px 14px 6px; padding: 22px 22px 18px 30px;
  color: var(--paper-hi);
  background:
    radial-gradient(120% 90% at 20% 0%, rgba(255,255,255,.10), transparent 40%),
    linear-gradient(100deg, #3a2b23 0%, #55392b 30%, #4a3226 70%, #331f18 100%);
  border: 1px solid #201510;
  box-shadow: 0 1px 0 rgba(255,220,160,.14) inset, 0 -16px 30px rgba(0,0,0,.4) inset, 0 18px 36px rgba(0,0,0,.5);
}
.tome::before { /* spine + raised bands */
  content:''; position:absolute; left:10px; top:8px; bottom:8px; width:3px;
  background: linear-gradient(180deg, var(--gold-lo), var(--gold), var(--gold-lo));
  opacity:.8; border-radius:2px;
  box-shadow: 0 -26px 0 -1px var(--gold-lo), 0 26px 0 -1px var(--gold-lo);
}
.tome.plum  { background: radial-gradient(120% 90% at 20% 0%, rgba(255,255,255,.10), transparent 40%),
              linear-gradient(100deg,#1e2a40,#2f4260 30%,#293a54 70%,#18222f 100%); }
.tome.pine  { background: radial-gradient(120% 90% at 20% 0%, rgba(255,255,255,.10), transparent 40%),
              linear-gradient(100deg,#1c3d36,#2e6b5e 30%,#265a4e 70%,#16302a 100%); }
.tome h3 { font-size: 22px; margin: 4px 0 8px; }
.tome .tooling { color: var(--gold-hi); font-variant: small-caps; letter-spacing: 3px; font-size: 12px; opacity:.95; }

/* ── controls: seals, ribbons, keys ─────────────────────────────── */
.btn {
  display:inline-flex; align-items:center; gap:9px;
  font-family: var(--font-display); font-weight: 700; font-size: 16.5px;
  padding: 11px 24px 12px; border-radius: 10px; cursor: pointer; text-decoration:none;
  color: var(--ink-950);
  background: linear-gradient(178deg, var(--gold-hi) 0%, var(--gold) 55%, #c1912f 100%);
  border: 1px solid var(--gold-lo);
  box-shadow: 0 1px 0 #fff8 inset, 0 -3px 0 rgba(122,84,20,.45) inset, 0 5px 14px rgba(0,0,0,.45);
  text-shadow: 0 1px 0 #ffffff55;
}
.btn.quiet {
  color: var(--paper-hi);
  background: linear-gradient(178deg, var(--ink-700), var(--ink-800));
  border-color: rgba(212,165,72,.4);
  box-shadow: 0 1px 0 rgba(255,255,255,.12) inset, 0 -3px 0 rgba(0,0,0,.4) inset, 0 5px 14px rgba(0,0,0,.4);
  text-shadow:none;
}
.btn.wax {
  color: #ffe9dd; border-radius: 999px; border-color:#6d1f16;
  background: radial-gradient(120% 140% at 30% 20%, var(--wax-hi), var(--wax) 60%, #7c261e 100%);
  box-shadow: 0 2px 0 #ffffff2e inset, 0 -4px 8px #5a170f inset, 0 6px 16px rgba(0,0,0,.5);
  text-shadow: 0 1px 2px #5a170f;
}
.paper .btn.quiet { color: var(--ink-line);
  background: linear-gradient(178deg, #efe2c4, #dfcba0); border-color:#b9995f;
  box-shadow: 0 1px 0 #fff9 inset, 0 -3px 0 #b9995f66 inset, 0 4px 10px rgba(90,64,20,.3); }

.pill {
  display:inline-flex; align-items:center; gap:7px; font-size: 12.5px; font-weight: 800;
  letter-spacing:.4px; border-radius: 999px; padding: 4px 13px;
  border:1px solid rgba(212,165,72,.4); background: rgba(212,165,72,.12); color: var(--gold-hi);
}
.pill.plum { border-color:#8f6fd066; background:#6d4a8f33; color:#cbb3ef; }
.pill.rose { border-color:#c96e8c66; background:#c96e8c26; color:#eeb3c8; }
.pill.pine { border-color:#3e8e7e66; background:#3e8e7e26; color:#9fd6c9; }
.pill.ember{ border-color:#c4573b66; background:#c4573b26; color:#f0a98f; }
.paper .pill { background:#efe2c4; color:#7c5f22; border-color:#c9ae7c; }
.paper .pill.plum { color:#5c3f80; } .paper .pill.pine { color:#2e6b5e; }
.paper .pill.rose { color:#9c4a68; } .paper .pill.ember{ color:#a34526; }

.keycap {
  font-family: var(--font-ui); font-size: 12px; font-weight: 800; letter-spacing:.5px;
  color: #3a2c10; padding: 2.5px 9px 3.5px; border-radius: 6px;
  background: linear-gradient(180deg, #e8cf96, #c9a95e);
  border: 1px solid #9a7c3a;
  box-shadow: 0 1px 0 #fff8 inset, 0 -2px 0 #9a7c3a99 inset, 0 2px 3px rgba(0,0,0,.4);
  display:inline-block; line-height:1.35;
}

/* ── speech: storybook captions with illuminated initials ───────── */
.speech {
  position: relative; display:flex; gap: 13px; align-items:flex-start;
  background: linear-gradient(180deg, rgba(250,243,227,.97), rgba(236,222,189,.97));
  color: #2b2340; border-radius: 8px; padding: 11px 16px 11px 12px;
  border: 1px solid #b9995f;
  box-shadow: 0 1px 0 #fff9 inset, 0 6px 16px rgba(0,0,0,.45);
  font-size: 15.5px; max-width: 62ch;
}
.speech .initial {
  flex: 0 0 auto; width: 40px; height: 40px; border-radius: 7px;
  display:flex; align-items:center; justify-content:center;
  font-family: var(--font-display); font-weight: 900; font-size: 24px; color: var(--paper-hi);
  background: linear-gradient(160deg, var(--plum), var(--plum-lo));
  border: 1.5px solid var(--gold); box-shadow: 0 2px 6px rgba(0,0,0,.35);
}
.speech.player .initial { background: linear-gradient(160deg, var(--ember-hi), var(--ember)); }
.speech.beast  .initial { background: linear-gradient(160deg, var(--rose), var(--rose-lo)); }
.speech.narrator { font-style: italic; }
.speech.narrator .initial { background: linear-gradient(160deg, #4a4463, #2b2740); }
.speech b.name { font-family: var(--font-display); }

/* ── HUD atoms (quill-drawn onto the world) ─────────────────────── */
.medallion { /* hotbar slot = brass charm medallion */
  width: 58px; height: 58px; border-radius: 14px; position: relative;
  display:flex; align-items:center; justify-content:center;
  background:
    radial-gradient(80% 60% at 50% 18%, rgba(255,255,255,.22), transparent 55%),
    linear-gradient(180deg, #3d4a5e, #202b3a 60%, #16202e 100%);
  border: 2px solid #7a6438;
  box-shadow: 0 1px 0 rgba(242,206,123,.35) inset, 0 -6px 10px rgba(0,0,0,.5) inset, 0 6px 12px rgba(0,0,0,.5);
}
.medallion .num {
  position:absolute; bottom:-7px; left:50%; transform:translateX(-50%);
  font-size:10px; font-weight:900; color:#3a2c10; padding: 0 7px 1px; border-radius: 5px;
  background: linear-gradient(180deg,#e8cf96,#c9a95e); border:1px solid #9a7c3a;
}
.medallion.sel {
  border-color: var(--gold-hi); transform: translateY(-7px);
  box-shadow: 0 1px 0 rgba(242,206,123,.5) inset, 0 -6px 10px rgba(0,0,0,.5) inset,
              0 10px 18px rgba(0,0,0,.55), 0 0 26px rgba(242,206,123,.4);
}
.medallion svg { filter: drop-shadow(0 2px 2px rgba(0,0,0,.5)); }

.inkgauge { /* meters = glass inkwell tubes */
  height: 16px; border-radius: 999px; position:relative; overflow:hidden;
  background: linear-gradient(180deg, #0e141d, #182432);
  border: 1.5px solid #7a6438;
  box-shadow: 0 2px 4px rgba(0,0,0,.6) inset, 0 1px 0 rgba(242,206,123,.25);
}
.inkgauge > i { display:block; height:100%; border-radius: 999px; position:relative; }
.inkgauge > i::after { /* meniscus highlight */
  content:''; position:absolute; left:6%; right:8%; top:2px; height:4px; border-radius:999px;
  background: rgba(255,255,255,.4); filter: blur(.5px);
}
.inkgauge.magic > i { background: linear-gradient(90deg, #8f6fd0, #b79cff 60%, #cdb9ff); }
.inkgauge.wings > i { background: linear-gradient(90deg, #3e8e7e, #6fc4b0 60%, #a8e6d8); }

/* ── ribbon banner (section headers on game screens) ────────────── */
.ribbon {
  position: relative; display:inline-block; padding: 7px 26px 8px;
  font-family: var(--font-display); font-weight: 800; font-size: 15px; letter-spacing:.6px;
  color: var(--paper-hi);
  background: linear-gradient(180deg, #35707e, #234a56);
  border-top: 1px solid #6fc4b0; border-bottom: 2px solid #14282e;
  box-shadow: 0 6px 14px rgba(0,0,0,.45);
}
.ribbon::before, .ribbon::after {
  content:''; position:absolute; top:0; bottom:0; width:14px;
  background: inherit; border-bottom: 2px solid #14282e;
}
.ribbon::before { left:-13px; clip-path: polygon(100% 0, 0 50%, 100% 100%); }
.ribbon::after  { right:-13px; clip-path: polygon(0 0, 100% 50%, 0 100%); }
.ribbon.gold { background: linear-gradient(180deg, var(--gold), #b0872e); color:#2b2010;
  border-top-color: var(--gold-hi); border-bottom-color:#6d5316; }

/* ── portrait frame: gilt oval miniature ────────────────────────── */
.frame-oval {
  width: 100%; aspect-ratio: 4/4.6; border-radius: 50% / 46%;
  padding: 8px; position: relative;
  background: conic-gradient(from 210deg, var(--gold-lo), var(--gold-hi) 25%, var(--gold) 40%, var(--gold-lo) 60%, var(--gold-hi) 80%, var(--gold-lo));
  box-shadow: 0 3px 0 rgba(0,0,0,.3), 0 14px 28px rgba(0,0,0,.5), 0 1px 0 #fff5 inset;
}
.frame-oval .canvas {
  width:100%; height:100%; border-radius: 50% / 46%; overflow:hidden; position:relative;
  border: 2px solid #6d5316;
  box-shadow: 0 -14px 24px rgba(0,0,0,.35) inset;
}
.frame-oval .canvas svg { display:block; width:100%; height:100%; }

/* ── storyboard beats ───────────────────────────────────────────── */
.beat { display:grid; grid-template-columns: 52px 1fr; gap: 15px; margin: 15px 0; align-items:start; }
.beat .no {
  width: 43px; height: 43px; border-radius: 50%;
  display:flex; align-items:center; justify-content:center;
  font-family: var(--font-display); font-weight: 900; font-size: 19px; color: #2b2010;
  background: radial-gradient(120% 130% at 32% 22%, var(--gold-hi), var(--gold) 55%, var(--gold-lo));
  border: 1.5px solid var(--gold-lo);
  box-shadow: 0 1px 0 #fff7 inset, 0 4px 10px rgba(0,0,0,.45);
}
.beat.ai  .no { color:#f0e8ff; background: radial-gradient(120% 130% at 32% 22%, #a687d8, var(--plum) 55%, var(--plum-lo)); border-color: var(--plum-lo); }
.beat.kid .no { color:#fff0e8; background: radial-gradient(120% 130% at 32% 22%, var(--ember-hi), var(--ember) 55%, #8f3a24); border-color:#8f3a24; }
.beat .what b { font-family: var(--font-display); }

/* ── code / under-the-hood: the quill's shorthand ───────────────── */
.under {
  font-family: var(--font-mono); font-size: 12px; line-height: 1.7;
  color: #a8cfe0; background: linear-gradient(180deg, #0e1620, #13202c);
  border: 1px solid #2e4a5c; border-left: 3px solid var(--verdigris);
  border-radius: 8px; padding: 11px 14px; overflow-x:auto; white-space: pre; margin-top: 8px;
  box-shadow: 0 4px 10px rgba(0,0,0,.35) inset;
}

/* ── tables: ledger style ───────────────────────────────────────── */
table.ledger { width:100%; border-collapse: collapse; font-size: 15px; }
table.ledger th { font-family: var(--font-display); text-align:left; padding: 9px 11px;
  border-bottom: 2px solid rgba(212,165,72,.5); font-size: 14.5px; letter-spacing:.3px; }
table.ledger td { padding: 9px 11px; border-bottom: 1px solid rgba(212,165,72,.16); vertical-align: top; }
.paper table.ledger th { border-bottom-color:#b9995f; }
.paper table.ledger td { border-bottom-color:#dcc79c; }

/* ── swatch plates ──────────────────────────────────────────────── */
.swatch {
  border-radius: 9px; padding: 15px 15px 12px; min-height: 96px; position:relative;
  display:flex; flex-direction:column; justify-content:space-between;
  border: 1px solid rgba(0,0,0,.35); box-shadow: 0 1px 0 rgba(255,255,255,.25) inset, 0 8px 18px rgba(0,0,0,.35);
  font-weight: 800; font-size: 14px;
}
.swatch code { font-family: var(--font-mono); font-size: 11.5px; font-weight: 600; opacity:.85; }

/* ── icons ──────────────────────────────────────────────────────── */
.ic { width: 26px; height: 26px; display:inline-block; vertical-align: -6px; }
.ic.lg { width: 34px; height: 34px; }
.ic.xl { width: 46px; height: 46px; }

footer.colophon {
  margin-top: 70px; padding: 20px 26px; text-align:center;
  font-size: 13px; opacity:.62; font-style: italic; font-family: var(--font-display);
}

/* ── generated art plates (gpt-image-2 pipeline) ────────────────── */
.artframe { border-radius: 12px; overflow: hidden; border: 1px solid rgba(212,165,72,.55);
  box-shadow: 0 0 0 1px #0b111a, 0 24px 55px rgba(0,0,0,.55); position: relative; }
.artframe img { display: block; width: 100%; height: auto; }
.artframe .plaquette { position: absolute; left: 14px; bottom: 12px; font-size: 12px; font-weight: 800;
  letter-spacing: .4px; background: rgba(11,17,26,.72); border: 1px solid rgba(212,165,72,.5);
  border-radius: 999px; padding: 4px 13px; backdrop-filter: blur(4px); }
.frame-oval .canvas img { width: 100%; height: 100%; object-fit: cover; display: block; }
.worldart img { width: 100%; height: 100%; object-fit: cover; display: block; }
.atlas img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }

/* ══════════════════════════════════════════════════════════════════════════
   IN-GAME HUD — the layer the client actually renders
   ══════════════════════════════════════════════════════════════════════════
   Everything above this line dresses the mock book's own pages. Everything
   below is a component the GAME uses, which is why it lives here rather than
   in a page's <style> block: `scripts/sync-design.mjs` copies THIS FILE into
   the client, and nothing else. A HUD component defined in a mock page is a
   drawing; defined here, it is the game's.

   Vista 12 designs the five mode slots and vista 01 the persistent HUD around
   them. (Debt, deliberately not fixed in the same pass: vista 01 still carries
   its own copies of .plaque/.hearts/.bracelet in its page style block, which
   win by cascade and keep that approved page rendering exactly as reviewed.
   They should be deleted once someone re-checks that page's render.)          */

/* the overlay itself. The scrim is one gradient under the bottom band instead
   of a text-shadow argument per label — the same styling has to stay legible
   over a noon meadow and a midnight sky, and a shadow only wins one of those. */
.hud { position: absolute; inset: 0; pointer-events: none; font-size: 14px; }
.hud > * { position: absolute; z-index: 1; }
.hud::after { content:''; position:absolute; left:0; right:0; bottom:0; height:30%; z-index:0;
  background: linear-gradient(180deg, transparent, rgba(11,17,26,.5)); }

/* ── persistent: who she is. Never leaves, in any mode. ─────────────── */
.plaque { display:flex; align-items:center; gap:12px; padding: 8px 18px 9px 10px; border-radius: 10px;
  background: linear-gradient(180deg, rgba(24,38,54,.9), rgba(11,17,26,.9));
  border: 1px solid rgba(212,165,72,.6);
  box-shadow: 0 1px 0 rgba(242,206,123,.3) inset, 0 8px 18px rgba(0,0,0,.5); }
.plaque .cameo { width: 40px; height: 40px; border-radius: 50%;
  background: radial-gradient(120% 120% at 35% 25%, #f0c9a0, #b97a4e);
  border: 2px solid var(--gold); box-shadow: 0 3px 8px rgba(0,0,0,.5);
  display:flex; align-items:center; justify-content:center;
  font-family: var(--font-display); font-weight:900; font-size:19px; color:#4a2c12; }
.plaque .name { font-family: var(--font-display); font-weight: 800; font-size: 16px; }
.hearts { display:flex; gap:3px; margin-top: 2px; }
.hearts .ic { width: 17px; height: 17px; }
.hearts .dim { opacity:.28; filter: grayscale(.7); }

/* ── persistent: the bracelet, and what "resting" looks like ────────────
   Resting fades the TOOLS, not the plaque. Fading the whole thing made it
   vanish over a bright meadow, and the honest reading is that the bracelet is
   still there holding things that are asleep — she can see it waiting.       */
.bracelet { display:flex; gap: 9px; align-items:flex-end; padding: 11px 16px 13px; border-radius: 18px;
  background: linear-gradient(180deg, rgba(24,38,54,.7), rgba(11,17,26,.82));
  border: 1px solid rgba(212,165,72,.5);
  box-shadow: 0 1px 0 rgba(242,206,123,.25) inset, 0 14px 30px rgba(0,0,0,.55); }
.bracelet::before { content:''; position:absolute; left:24px; right:24px; top:8px; height:2px;
  background: repeating-linear-gradient(90deg, var(--gold) 0 7px, transparent 7px 13px);
  opacity:.5; border-radius:2px; }
.bracelet.resting .medallion { opacity:.38; filter: grayscale(.75); }
/* the bracelet and its note travel together so neither can drift onto the
   other as the viewport's aspect changes; the note hangs off the right edge
   rather than sitting inside, where its width pushed the bracelet off centre */
.braceletdock { bottom: 24px; left:50%; transform:translateX(-50%); }
.braceletdock .bracelet { position:static; }
.restnote { font-size:11.5px; color:#e3d0a8; max-width:124px; line-height:1.35;
  padding: 5px 9px; border-radius: 8px; background: rgba(11,17,26,.62);
  box-shadow: inset 0 0 0 1px rgba(212,165,72,.24); }
.braceletdock .restnote { position:absolute; left:100%; margin-left:11px; bottom:8px; width:124px; }

/* ── SLOT 1 · the mode ribbon ───────────────────────────────────────────
   top centre: which game this is and how far through. The only thing on
   screen that names the mode. Dresses the book's .ribbon.                    */
.hud-ribbon { top: 18px; left:50%; transform:translateX(-50%); }

/* ── SLOT 2 · the card ──────────────────────────────────────────────────
   the quest parchment's own place. While a mode runs the mode's structured
   state stands here; the quest returns the moment she steps off.             */
.hud-card { top:16px; right:16px; width: 262px; padding: 13px 15px 12px; border-radius: 8px;
  color:#2b2340;
  background: linear-gradient(180deg, var(--paper-hi), var(--paper) 75%, #eaddbd 100%);
  border: 1px solid #b9995f;
  box-shadow: 0 1px 0 #fff9 inset, 0 3px 0 -1px #d9c69b, 0 4px 0 -1px #b9995f, 0 14px 26px rgba(0,0,0,.5); }
.hud-card::before { content:''; position:absolute; top:-6px; right:20px; width: 15px; height: 42px;
  background: linear-gradient(180deg, var(--wax-hi), var(--wax));
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% 84%, 0 100%);
  box-shadow: 0 2px 5px rgba(0,0,0,.4); }
.hud-card h4 { margin:0 0 8px; font-family: var(--font-display); font-size: 16px;
  display:flex; gap:7px; align-items:center; }
.hud-card h4 .ic { width:17px; height:17px; color:#7a5a2a; }

/* a race is about who is ahead, so the standings ARE the card */
.standing { display:flex; align-items:center; gap:8px; padding:4px 0; font-size:13.5px; }
.standing + .standing { border-top: 1px dashed rgba(122,90,42,.28); }
.standing .pos { flex:none; width:21px; height:21px; border-radius:50%;
  display:flex; align-items:center; justify-content:center;
  font-family: var(--font-display); font-weight:900; font-size:12px;
  background: linear-gradient(180deg,#e3d0a8,#cdb587); color:#4a3a20;
  box-shadow: inset 0 0 0 1px rgba(122,90,42,.4); }
.standing.lead .pos { background: linear-gradient(180deg, var(--gold-hi), var(--gold)); color:#4a2c12;
  box-shadow: inset 0 0 0 1px var(--gold-lo), 0 0 9px rgba(212,165,72,.55); }
.standing .who { flex:1; font-weight:700; color:#2b2340; }
.standing .gap { font-family: var(--font-mono); font-size:12px; color:#6b5836; }
.standing.me { background: rgba(207,91,71,.13); margin: 0 -7px; padding-left:7px; padding-right:7px;
  border-radius:5px; }
.standing.me .who::after { content:' — you'; font-weight:400; opacity:.62; font-size:12px; }
/* somebody who left the race: still on the card, plainly not in it any more.
   The same .5 the train's called stops use — this game already has one way of
   saying "past", and a second one would be a second visual language. */
.standing.out { opacity:.48; }
.standing.out .pos { background: linear-gradient(180deg,#d8d2c6,#bdb6a8); color:#5a5346; box-shadow: inset 0 0 0 1px rgba(90,83,70,.35); }
.standing.out .gap { font-family: var(--font-body); font-style: italic; }

/* a train is about where it stops next, so the card is a line */
.stop { font-size: 13px; padding: 4px 0 4px 21px; position:relative; color:#4a3a20; }
.stop::before { content:''; position:absolute; left:3px; top:9px; width:8px; height:8px;
  border-radius:50%; border: 2px solid #b9995f; background: var(--paper-hi); }
.stop::after { content:''; position:absolute; left:6.5px; top:17px; bottom:-4px; width:2px;
  background: rgba(122,90,42,.35); }
.stop:last-child::after { display:none; }
.stop.done { opacity:.5; }
.stop.done::before { background:#7ba05b; border-color:#55763c; }
.stop.now { font-weight:800; color:#2b2340; }
.stop.now::before { background: var(--gold); border-color: var(--gold-lo); box-shadow: 0 0 8px var(--gold); }
.stop .t { float:right; font-family: var(--font-mono); font-size:11.5px; color:#6b5836; font-weight:400; }

/* a goal, when the mode is a challenge */
.goalbar { height: 11px; border-radius: 999px; margin-top: 7px;
  background: #d9c69b; box-shadow: inset 0 1px 3px rgba(74,58,32,.4); overflow:hidden; }
.goalbar i { display:block; height:100%; border-radius:999px;
  background: linear-gradient(180deg, var(--meadow), var(--meadow-lo)); }
.goalnums { display:flex; justify-content:space-between; font-size:12px; color:#6b5836; margin-top:5px; }

/* ── SLOT 3 · the gauge ─────────────────────────────────────────────────
   the one thing changing continuously. Notches with a WORD, because a bar can
   be read at a glance by someone still learning to read numbers — and because
   the same component serving a kart's speed and a train's throttle is what
   makes this a system rather than two HUDs.                                  */
.hud-gauge { bottom: 108px; left:50%; transform:translateX(-50%); width: 330px;
  display:flex; flex-direction:column; gap:7px; align-items:center; }
.hud-gauge .gaugerow { display:grid; grid-template-columns: 24px 1fr auto; gap:9px; align-items:center; width:100%; }
.hud-gauge .gaugerow .ic { width:22px; height:22px; color:#f0e7c9; filter: drop-shadow(0 1px 2px #000); }
.hud-gauge .val { font-family: var(--font-display); font-weight:800; font-size:14px;
  color:#f0e7c9; text-shadow: 0 1px 3px #000; min-width: 74px; text-align:right; }
.notches { display:flex; gap:4px; width:100%; }
.notches i { flex:1; height:13px; border-radius:3px; background: rgba(240,231,201,.16);
  box-shadow: inset 0 0 0 1px rgba(240,231,201,.28); }
.notches i.on { background: linear-gradient(180deg, var(--gold-hi), var(--gold));
  box-shadow: inset 0 0 0 1px var(--gold-lo), 0 0 8px rgba(212,165,72,.5); }
/* the one exception to "no number she cannot read": a lap time is the point
   of a lap, so it gets to be big */
.bigtime { font-family: var(--font-display); font-weight:900; font-size: 26px; color:#f0e7c9;
  text-shadow: 0 2px 6px #000; letter-spacing:.5px; }
.bigtime small { font-size: 13px; font-weight:700; opacity:.72; margin-left:5px; }

/* ── SLOT 4 · the call ──────────────────────────────────────────────────
   dead centre, and only for a moment: the countdown, the lap turning over,
   the result. Nothing else may ever sit in the middle of her view.           */
.hud-call { left:50%; top:38%; transform:translate(-50%,-50%); text-align:center; }
.hud-call .word { font-family: var(--font-display); font-weight:900; font-size: 52px;
  color: var(--gold-hi); text-shadow: 0 3px 0 var(--gold-lo), 0 10px 26px rgba(0,0,0,.75);
  letter-spacing: 2px; }
.hud-call .under { font-size: 14px; color:#f0e7c9; text-shadow:0 1px 4px #000; margin-top:2px; }
.hud-call .count { font-size: 96px; line-height:1; }

/* ── SLOT 5 · the prompt ────────────────────────────────────────────────
   what the keys do right now — and ALWAYS the way out. A child must never be
   trapped inside a game she cannot leave.                                    */
.hud-prompt { bottom: 28px; right: 20px; display:flex; flex-direction:column; align-items:flex-end; gap:7px; }
.hud-prompt .line { font-size: 12.5px; color:#f0e7c9; text-shadow: 0 1px 3px #000; }
.hud-prompt .exit { color:#ffd9cf; }

/* ══════════════════════════════════════════════════════════════════════════
   THE MAP — the full spread, and the pins that name what is on it
   ══════════════════════════════════════════════════════════════════════════
   Doc 31 Cluster B. The minimap disc has always been a picture of the ground;
   what it never had was the NAMES of things — the track, the halts, the
   cottages, the places the story is pointing at. And there was no full map at
   all: `M` grew the disc to 520px and zoomed out, which is a bigger disc.

   The spread is a MODE (doc 26 §0a): it covers the middle of the screen, so it
   claims the middle of the screen. Two halves — the SHEET (the drawn ground,
   with pins on it) and the INDEX (what is on this map, in a list) — because a
   pin at the edge of a 480-block railway is four pixels wide, and a child
   looking for "the cottage" should be able to read its name rather than hunt
   for a dot.

   Materials are the book's: parchment, iron-gall ink, gold leaf for what she
   can touch, and a handled edge. Nothing new is invented here.              */
/* `position: absolute` said HERE, not inherited.
   Found by MEASURING (`__ouaw.mode.collisions()`): the mode HUD's slots are
   children of a `.hud` element and pick up `position: absolute` from
   `.hud > *`, but the spread is mounted on the game's `#hud` — which has no
   such class — so `left`/`top` were ignored, the transform ran from the static
   position, and the sheet's own rectangle came back at x = −411. It sat across
   the plaque and off the left of the window, and every other check passed. */
.mapspread { position:absolute;
  left:calc(50% + var(--center-shift, 0px)); transform:translateX(-50%);
  /* ── positioned by its EDGES, not by its centre ──
     A centred box of a fixed height lands wherever the window puts it, and
     measuring found what that costs: at 1440x900 the spread's bottom edge was
     at y=705 and the build-progress card lives at `bottom: 240px` (y~620), so a
     card saying "still laying the line" sat across the strip that says
     "Enter go there".
     The bottom of this screen is where the WORLD's own notices live — the build
     card, the story gauges, the caption band, the block strip — and a map may
     not swallow any of them. Nor the TOP, where the toast stack says "the
     ground moved — I got you out". So the spread is pinned between the two and
     takes whatever height is left, which is correct at every window height
     rather than at the one it was measured on.

     Both numbers are MEASURED, and each is stated so the next reader can check
     it rather than trust it:
       top 140px    — `#toasts` is `top: 12px` and grows DOWNWARD; three of them
                      measured 108px tall, so the stack can reach y=120.
       bottom 300px — `.builtcard` is `bottom: 240px` and ~52px tall, so its top
                      edge is 292px up from the bottom.
     A measured constant is what doc 28 settled on for `RACE_RAIL_PUSH_PX` and
     `RIBBON_TOAST_PUSH_PX`, for the same reason: the arithmetic cannot live at
     enter time, and `__ouaw.mode.collisions()` is what catches it drifting.
     Do not "tidy" these to round numbers without re-running that check. */
  top:140px; bottom:300px; min-height:260px;
  display:grid; grid-template-columns: minmax(0,1fr) 232px;
  grid-template-rows: minmax(0,1fr) auto auto; gap:0;
  /* ── the width is the SUBTITLES formula, not a guess ──
     This game centres things in the space left between the chat rail and the
     voice column, and the book centres on the viewport: the 67px between those
     two is the whole reason the first mode HUD's gauge sat under Bloom's chat
     (doc 28 §8 finding 4). `#subtitles` already solved exactly this, so the
     spread borrows its arithmetic rather than inventing a second answer —
     which also means it shrinks correctly on a narrow window instead of
     running under the columns. */
  width:min(860px, calc(var(--app-w, 100vw) - var(--rail, 0px) - var(--dock, 0px) - 40px));
  pointer-events:auto;
  border-radius:12px; overflow:hidden;
  background: linear-gradient(180deg, var(--paper) 0%, #ecdcbc 100%);
  box-shadow: 0 22px 60px rgba(0,0,0,.62), inset 0 0 0 1px rgba(122,90,42,.55),
    0 0 0 3px var(--brass), 0 0 0 5px rgba(11,17,26,.72); }
/* the drawn ground. `image-rendering: pixelated` on purpose: the memory canvas
   is ONE PIXEL PER BLOCK, and smoothing a hand-inked map into a blur is the
   fastest way to make a drawn thing look like a screenshot of nothing. */
/* No `aspect-ratio`: the sheet takes the room the spread has, and the DRAWING
   is fitted to whatever that turns out to be (`atlas-map.ts` sizes its canvas
   from the rendered box). An aspect declared here and a view computed from two
   constants over there is one fact in two places, and the world would come out
   stretched the first time they disagreed. */
.mapsheet { position:relative; min-height:0; overflow:hidden;
  background: var(--paper); }
.mapsheet > canvas, .mapsheet > .sheetart { position:absolute; inset:0; width:100%; height:100%;
  image-rendering: pixelated; }
/* the fold, so a spread reads as a page and not a viewport */
.mapsheet::after { content:''; position:absolute; inset:0; pointer-events:none;
  box-shadow: inset 0 0 34px rgba(122,90,42,.34), inset 0 0 0 1px rgba(122,90,42,.4); }
.mapspread .mapindex { background: linear-gradient(180deg, #f6ecd2, #e3d0a8);
  box-shadow: inset 1px 0 0 rgba(122,90,42,.42); padding:11px 12px 12px;
  display:flex; flex-direction:column; gap:2px; overflow-y:auto; }
.mapindex h4 { margin:0 0 6px; font-family:var(--font-display); font-size:15px; color:#2b2340;
  display:flex; align-items:center; gap:7px; }
.mapindex h4 .ic { width:16px; height:16px; color:#7a5a2a; }
/* one row per named thing. A row is TOUCHABLE when she can go there, and the
   whole row is the target — a child aiming at a 9px pin with a mouse she is
   also using to steer is a target she will miss. */
.mapindex .place { display:grid; grid-template-columns:18px 1fr auto; gap:8px; align-items:center;
  padding:5px 6px; border-radius:6px; font-size:13px; color:#4a3a20; cursor:pointer;
  border:0; background:none; text-align:left; width:100%; font-family:inherit; }
.mapindex .place + .place { border-top:1px dashed rgba(122,90,42,.26); border-radius:0; }
.mapindex .place .ic { width:15px; height:15px; color:#7a5a2a; }
.mapindex .place .nm { font-weight:700; color:#2b2340; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.mapindex .place .far { font-family:var(--font-mono); font-size:11px; color:#6b5836; }
.mapindex .place.sel { background:rgba(212,165,72,.28); box-shadow:inset 0 0 0 1px var(--gold-lo); }
/* NOT REACHABLE YET, and it says so rather than vanishing. A place she cannot
   travel to is still a place, and hiding it would hide the world. */
.mapindex .place.locked { opacity:.52; }
.mapindex .place.locked .nm { font-weight:400; font-style:italic; }

/* a pin: what it is, and what it is called, on the sheet itself */
.mappin { position:absolute; transform:translate(-50%,-100%);
  display:flex; flex-direction:column; align-items:center; gap:1px; pointer-events:auto;
  border:0; background:none; padding:0; cursor:pointer; }
.mappin .head { width:19px; height:19px; border-radius:50% 50% 50% 4px; transform:rotate(-45deg);
  background: linear-gradient(180deg, var(--gold-hi), var(--gold));
  box-shadow: 0 2px 5px rgba(30,22,12,.6), inset 0 0 0 1.5px var(--gold-lo);
  display:flex; align-items:center; justify-content:center; }
.mappin .head .ic { width:11px; height:11px; color:#3a2408; transform:rotate(45deg); }
.mappin .lbl { font-family:var(--font-ui); font-size:10.5px; font-weight:800; color:#2b2340;
  background:rgba(250,243,227,.86); border-radius:4px; padding:0 4px; white-space:nowrap;
  box-shadow:0 1px 2px rgba(30,22,12,.35); }
.mappin.locked .head { background:linear-gradient(180deg,#d8c9ab,#b9a884); box-shadow:0 1px 3px rgba(30,22,12,.4); }
/* PAST THE EDGE — the pin is a direction, not a position.
   A place beyond the sheet is clamped to its border rather than dropped: a map
   that silently omits a place is worse than no map, because she believes she has
   looked. The dashed ring is what says "over there, further than this page". */
.mappin.off .head { border-radius:50%; transform:none; outline:2px dashed rgba(122,90,42,.7); outline-offset:2px; }
.mappin.off .head .ic { transform:none; }
.mappin.off .lbl { opacity:.85; font-style:italic; }
.mappin.locked .lbl { color:#6b5836; font-weight:600; background:rgba(250,243,227,.6); }
.mappin.sel .head { box-shadow:0 0 0 3px rgba(212,165,72,.55), 0 2px 6px rgba(30,22,12,.6); }
/* HER, on the sheet — the same arrow the disc draws, so two views of one map
   never disagree about which way she is facing. */
.mapme { position:absolute; transform:translate(-50%,-50%); width:0; height:0;
  border-left:6px solid transparent; border-right:6px solid transparent; border-bottom:13px solid #1e1a12;
  filter: drop-shadow(0 0 2px var(--paper-hi)); }

/* the callout: the one place selected, and what she can do about it */
.mapcallout { grid-column:1 / -1; display:flex; align-items:center; gap:11px;
  padding:9px 13px; background:linear-gradient(180deg,#2b2340,#1b1730);
  box-shadow: inset 0 1px 0 rgba(212,165,72,.4); color:var(--paper-hi); }
.mapcallout .ic { width:20px; height:20px; color:var(--gold-hi); flex:none; }
.mapcallout .what { flex:1; min-width:0; }
.mapcallout .what b { font-family:var(--font-display); font-size:15px; }
.mapcallout .what .sub { font-size:12px; opacity:.78; }
/* the REASON, when there is one. Never a disabled button with no explanation:
   "you have not been here yet" is a thing she can act on, and a grey button is
   not. */
.mapcallout .why { font-size:12px; color:#ffd9cf; max-width:340px; text-align:right; }
.mapcallout .go { flex:none; }

/* the key. Four kinds and no fifth — a village is laid AT a halt, so drawing
   both would put two pins on one place. */
.maplegend { display:flex; gap:12px; flex-wrap:wrap; font-size:11px; color:#6b5836;
  padding:7px 10px 8px; background:rgba(227,208,168,.75);
  box-shadow: inset 0 1px 0 rgba(122,90,42,.3); grid-column:1 / -1; }
.maplegend span { display:flex; align-items:center; gap:4px; }
.maplegend .ic { width:12px; height:12px; color:#7a5a2a; }
.maplegend .rail { width:16px; height:0; border-top:2.5px dashed #7a5a2a; }

/* ══════════════════════════════════════════════════════════════════════════
   THE BUILDING CARD — is it working, and is it done? (vista 14)
   ══════════════════════════════════════════════════════════════════════════
   The card that tells her what the world built. It lived in the client's own
   <style> block until 2026-08-22 and moved here for a reason worth keeping:
   designing its RISING state meant drawing it over a real frame, and a
   component declared in the client is a component no mock can contain. A
   surface nobody can draw is a surface nobody designs.

   What it lacked was any notion of TIME. It fired at the START of a reveal,
   announced a block count, offered to put the thing back — and faded, while two
   and a half more minutes of blocks were still landing. So it has two states
   now: `rising` while the world is still arriving, and `done` when it has.

   The colour language of the bar is borrowed whole from `.notches` above —
   faint parchment track, gold fill — so a child who has read the speed gauge
   in a race is reading the same instrument here. No new colours, no new font.

   ── where it stands, and why not where it used to ──
   It sat at `bottom: 196px; left: 50%`. Both halves were wrong and neither had
   ever been measured: `#ribbons` (the story's own ability ribbons) is pinned to
   `bottom: 196px` too — the same line — and every other centred element in this
   game uses `calc(50% + var(--center-shift))`, the 67px that accounts for the
   chat rail and the voice column (doc 28 §8 finding 4). 240px clears the whole
   bottom-centre stack (hotbar 14 · subtitles 76 · gauges 152 · orders/ask 168 ·
   ribbons 196) with room to spare, and the shift puts it over the middle of the
   space the game actually centres on. Both defects were invisible because
   nothing measured this card: it was in neither list of
   `__ouaw.mode.collisions()`. It is in both now.

   The fallbacks in the `var()` calls are for the mock book, which has neither
   custom property; the client defines both.                                    */
.builtcard { position: absolute; left: calc(50% + var(--center-shift, 0px));
  transform: translateX(-50%) translateY(10px);
  bottom: calc(240px + var(--safe-b, 0px)); z-index: 44; display: flex; align-items: center; gap: 12px;
  padding: 9px 10px 9px 16px; border-radius: 999px; opacity: 0; pointer-events: none;
  background: rgba(12,8,18,.86); box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(212,165,72,.35);
  transition: opacity .35s ease, transform .35s ease; }
.builtcard.in { opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
.builtcard .bc-what { font-family: var(--font-display); font-weight: 700; font-size: 14px; color: #F3E7CF; }
.builtcard .bc-undo { padding: 7px 15px; border-radius: 999px; cursor: pointer; border: 0;
  font-family: var(--font-display); font-weight: 700; font-size: 13px; color: #2a1c07;
  background: linear-gradient(180deg, #F2CE7B, #D4A548); box-shadow: 0 3px 0 #8a6a2b; }
.builtcard .bc-undo:active { transform: translateY(2px); box-shadow: 0 1px 0 #8a6a2b; }

/* the bar. `width` is set by the client from `done / total`, so the ONLY thing
   this owns is how it looks; the transition is what makes an update read as
   filling rather than as a jump between two pictures. */
.bc-bar { display:block; width:132px; height:7px; border-radius:999px; flex:none;
  background: rgba(240,231,201,.16); box-shadow: inset 0 0 0 1px rgba(240,231,201,.28); overflow:hidden; }
.bc-bar > i { display:block; height:100%; width:0; border-radius:999px;
  background: linear-gradient(180deg, var(--gold-hi), var(--gold));
  box-shadow: inset 0 0 0 1px var(--gold-lo), 0 0 8px rgba(212,165,72,.5);
  transition: width .28s linear; }

/* ── the pulse, which is the part that actually answers "is it working" ──
   A bar between two updates is a STILL bar, and a still bar is exactly the
   screen he could not read. The dot never stops while the card is rising, so
   "something is happening" survives a slow chunk, a stalled socket and a
   glance away. It is on the CARD, not rebuilt per update — an element recreated
   on every tick restarts its animation at frame one and is never seen (doc 28
   §8 finding 3, which cost a mode ribbon its entire visibility).             */
.bc-dot { width:8px; height:8px; border-radius:50%; flex:none;
  background: radial-gradient(120% 120% at 35% 30%, var(--gold-hi), var(--gold) 60%, var(--gold-lo));
  animation: bcPulse 1.35s ease-in-out infinite; }
@keyframes bcPulse {
  0%, 100% { opacity:.35; transform:scale(.8); box-shadow:0 0 0 0 rgba(212,165,72,0); }
  50%      { opacity:1;   transform:scale(1);  box-shadow:0 0 9px 2px rgba(212,165,72,.45); }
}

/* ── WHICH STAGE, as a COLOUR on the bar that already exists (doc 31 §B3a) ──
   The operator asked for the area we allocate to be visible: marked out, then
   shimmering, then cleared, then built, then the ground put back. Five words
   and one bar can carry that — a sixth panel cannot be justified on a band that
   already had a collision in it (see the plate at the foot of vista 14).

   So the stage is an ATTRIBUTE on the card and the only thing it changes is the
   fill's colour, which is a thing a child reads without reading: slate while
   the ground is being cut into, gold while the building rises, an earthy green
   while the earth comes back. The WORD beside it is the real message and it
   comes from `stageLine` in shared, so the server, the card and the tests
   cannot each hold a different opinion about what is happening.

   Deliberately NOT a set of pips or a stepper: six dots is a diagram of a
   process, and the child does not have a process, she has a chapel going up. */
.builtcard[data-stage="reserved"] .bc-bar > i,
.builtcard[data-stage="imagining"] .bc-bar > i {
  background: linear-gradient(180deg, var(--gold-hi), var(--gold)); opacity:.55; }
.builtcard[data-stage="clearing"] .bc-bar > i {
  background: linear-gradient(180deg, #cdd8e4, #8fa3b8);
  box-shadow: inset 0 0 0 1px #6b7f95, 0 0 8px rgba(160,180,205,.45); }
.builtcard[data-stage="settling"] .bc-bar > i {
  background: linear-gradient(180deg, #b9d79a, #7ba05b);
  box-shadow: inset 0 0 0 1px #55763c, 0 0 8px rgba(123,160,91,.45); }

/* While the ground is only reserved there is nothing to fill, so the bar would
   be an empty track saying nothing. The pulse carries it on its own — which is
   what the pulse was put there for. */
.builtcard[data-stage="reserved"] .bc-bar,
.builtcard[data-stage="imagining"] .bc-bar { opacity:.4; }

/* the word she reads instead of a percentage. "still rising", never "58%". */
.bc-state { font-family: var(--font-ui); font-size:11.5px; font-weight:800; letter-spacing:.03em;
  color: var(--paper-lo); flex:none; }

/* ── the finish, as an EVENT ──
   A bar reaching its end still looks like a bar, so the switch to `done` is
   given something to see: the gold ring closes round the whole card for a beat.
   This is the moment the operator could not find, so it is the one moment on
   this card that is allowed to be emphatic.                                   */
.builtcard.done { animation: bcLanded .9s ease-out both; }
@keyframes bcLanded {
  0%   { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(212,165,72,.35), 0 0 0 0 rgba(212,165,72,.55); }
  35%  { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px var(--gold-hi), 0 0 0 5px rgba(212,165,72,.3); }
  100% { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(212,165,72,.35), 0 0 0 0 rgba(212,165,72,0); }
}

/* ── THE WORLD ARRIVING — the other wait (vista 14, doc 82 §G1) ─────────────
   The card above paces a REVEAL, which `REVEAL_MAX_MS` bounds to 18 seconds.
   This one covers the wait nobody had ever shown a child: the client turning
   voxels it already holds into meshes she can see — 91 s for an ordinary new
   world at the shipped `[10,4]`, and 348 s for Ironhold (2,926 chunks at
   118.9 ms). For all of that the screen said nothing.

   IT IS THE BUILD CARD'S OWN SLOT, and that is the whole design decision.
   Same `bottom: 240px`, same `--center-shift`, same pill, same bar, same
   pulse — so vista 41's ceiling is untouched: no new anchor, no new box, and
   `.mapspread`'s measured `bottom: 300px` still clears it, because there is
   nothing new below that line to clear.

   The two are MUTUALLY EXCLUSIVE by construction: `Ui` owns both, and a build
   card always wins because the build is what she asked for. So they cannot
   collide — which is a stronger guarantee than measuring that they happen not
   to, and it is the reason this is a sibling rule rather than a second band.

   Everything visual is inherited or shared: `.bc-dot`, `.bc-bar`, `.bc-what`
   and `.bc-state` are declared for both selectors above. What is different is
   the one thing that IS different — the world is not being BUILT, it is coming
   into view — so the pill is a shade cooler and its bar is moonlight rather
   than gold leaf. A child reads that without reading it: gold is the world
   being made, moon is the world being shown.                                */
.arrivecard { position: absolute; left: calc(50% + var(--center-shift, 0px));
  transform: translateX(-50%) translateY(10px);
  bottom: calc(240px + var(--safe-b, 0px)); z-index: 44; display: flex; align-items: center; gap: 12px;
  padding: 9px 16px; border-radius: 999px; opacity: 0; pointer-events: none;
  background: rgba(12,8,18,.86); box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(240,231,201,.3);
  transition: opacity .35s ease, transform .35s ease; }
.arrivecard.in { opacity: 1; transform: translateX(-50%) translateY(0); }
.arrivecard .bc-what { font-family: var(--font-display); font-weight: 700; font-size: 14px; color: #F3E7CF; }
/* moonlight, not gold leaf — see the plate above */
.arrivecard .bc-bar > i {
  background: linear-gradient(180deg, #fbf6e4, var(--moon));
  box-shadow: inset 0 0 0 1px rgba(122,120,100,.5), 0 0 8px rgba(240,231,201,.45); }
.arrivecard .bc-dot {
  background: radial-gradient(120% 120% at 35% 30%, #fffdf4, var(--moon) 60%, #c9c0a0);
  animation: acPulse 1.35s ease-in-out infinite; }
@keyframes acPulse {
  0%, 100% { opacity:.35; transform:scale(.8); box-shadow:0 0 0 0 rgba(240,231,201,0); }
  50%      { opacity:1;   transform:scale(1);  box-shadow:0 0 9px 2px rgba(240,231,201,.4); }
}
/* ARRIVED — the moment the world is whole, and the ONLY thing allowed to say
   so. Not "the bar reached its end": the bar CANNOT reach its end, capped at
   `ARRIVE_MAX_FRACTION` (0.96) precisely so that completion is an event rather
   than an extrapolation. Deliberately quieter than `bcLanded`: a world
   finishing loading is a relief, not an achievement, and the emphatic ring is
   reserved for a thing she asked for and got.                               */
.arrivecard.here { animation: acHere 1s ease-out both; }
@keyframes acHere {
  0%   { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(240,231,201,.3), 0 0 0 0 rgba(240,231,201,.4); }
  40%  { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(240,231,201,.85), 0 0 0 4px rgba(240,231,201,.2); }
  100% { box-shadow: 0 8px 24px rgba(0,0,0,.5), inset 0 0 0 1px rgba(240,231,201,.3), 0 0 0 0 rgba(240,231,201,0); }
}
@media (prefers-reduced-motion: reduce) {
  .arrivecard .bc-dot { animation: none; opacity: 1; }
  .arrivecard.here { animation: none; }
}
