:root {
  /* ---- THIS GAME'S PALETTE ----------------------------------------------------------------
     The first game was warm brown and gold: lamplight on wood. This country is water, indigo
     and ash, so the page is cold and the light in it is the only warm thing. Every warm value
     here is a LAMP: it appears where a person put it, and nowhere else. The variable names are
     kept (--gold, --blue) so 1,200 existing rules keep working; the values are all new. */
  --bg: #0c1316;              /* the delta at night: black water, not brown wood */
  --bg2: #111a1e;
  --panel: #16222700;         /* set solid just below -- kept here for anything reading it raw */
  --panel: #162227;
  --panel2: #1d2c32;
  --ink: #e9e7dd;             /* cool paper rather than cream */
  --ink-dim: #a7b0ac;
  --ink-faint: #77837f;
  --line: rgba(198, 220, 214, 0.14);
  --gold: #e8b45c;            /* LAMP AMBER: the one warm colour, used like a lit window */
  --gold-dim: #ab8342;
  --green: #6fae86;           /* verdigris, off the wet country */
  --red: #d4785d;             /* kiln-mouth */
  --blue: #6f8fca;            /* the dyers' indigo */
  --indigo: #3d548f;
  --ash: #8b8b83;
  --accent: var(--gold);
  --gw: 1180px;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif; /* @kind font */
  --sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; /* @kind font */
  /* the structural constant: everything in this game is CUT, not rounded (see THE CUT below) */
  --cut: 11px;
  --cut-sm: 7px;
  --cut-tab: 6px;
  /* Spacing. The sheet was written free-hand on a 2px grid long before these existed, so most
     rules still carry their own px - these are not a retrofit. They are here so that new markup,
     and anything generated against the design system, has a correct value to reach for instead
     of guessing or reaching for --gw (which is a page width, not a spacing unit). The eight
     cover 448 of the ~600 spacing declarations in this file. */
  --sp1: 4px;  --sp2: 6px;  --sp3: 8px;  --sp4: 10px;
  --sp5: 12px; --sp6: 14px; --sp7: 16px; --sp8: 18px;
}

* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body {
  /* A single faint khatam star, repeated as a quiet watermark over a warm glow and a soft
     vignette - much simpler than the old interlaced lattice, yet still mosque tilework. */
  background-color: var(--bg);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='120' height='120'%3E%3Cg fill='none' stroke='%23a8ccd8' stroke-opacity='0.045' stroke-width='1'%3E%3Cpath d='M0 60h120M60 0v120'/%3E%3Cpath d='M60 24 96 60 60 96 24 60Z'/%3E%3Cpath d='M0 0 24 60 0 120M120 0 96 60l24 60'/%3E%3Cpath d='M0 0 60 24 120 0M0 120 60 96l60 24'/%3E%3C/g%3E%3Cg fill='none' stroke='%23e8b45c' stroke-opacity='0.05' stroke-width='0.9'%3E%3Crect x='49' y='49' width='22' height='22'/%3E%3Crect x='49' y='49' width='22' height='22' transform='rotate(45 60 60)'/%3E%3C/g%3E%3C/svg%3E"),
    radial-gradient(1300px 850px at 80% -12%, rgba(36,64,74,0.5) 0%, rgba(14,24,28,0) 62%),
    radial-gradient(140% 130% at 50% 40%, rgba(0,0,0,0) 52%, rgba(0,0,0,0.5) 100%),
    linear-gradient(180deg, var(--bg) 0%, var(--bg2) 100%);
  background-size: clamp(92px, 10vmin, 132px), cover, cover, cover;
  background-position: center top, center, center, center;
  background-repeat: repeat, no-repeat, no-repeat, no-repeat;
  background-attachment: fixed;
  color: var(--ink);
  font-family: var(--sans);
  -webkit-font-smoothing: antialiased;
  line-height: 1.55;
  padding-bottom: calc(var(--rail-h, 0px) + 20px); /* always leave room to scroll clear of the fixed rail (it is taller on mobile) */
}
/* ISLAMIC INFLUENCE (the luminous layer): a warm light that strengthens as the heart turns.
   Driven by body[data-iman] (0 in menus; 1-4 in play, from the shahada + faith). Simplified to
   a pure glow - no second lattice - so belief lights the world without adding pattern. */
body::before {
  content: ""; position: fixed; inset: 0; z-index: -1; pointer-events: none;
  background: radial-gradient(58% 50% at 50% 24%, rgba(232,180,92,0.44), rgba(232,180,92,0) 70%);
  background-attachment: fixed;
  opacity: var(--iman-op, 0);
  transition: opacity 1.4s ease;
}
body[data-iman="1"] { --iman-op: 0.05; /* @kind other */ }
body[data-iman="2"] { --iman-op: 0.11; /* @kind other */ }
body[data-iman="3"] { --iman-op: 0.18; /* @kind other */ }
body[data-iman="4"] { --iman-op: 0.26; /* @kind other */ }
body[data-iman="3"] .girih-band, body[data-iman="4"] .girih-band { opacity: 0.92; }

/* ---- top bar ---- */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 16px clamp(16px, 4vw, 40px);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
  backdrop-filter: blur(8px);
  background: rgba(20, 17, 13, 0.7);
}
.brand { display: flex; flex-direction: column; line-height: 1.1; }
.brand-title { font-family: var(--serif); font-size: 20px; letter-spacing: 0.02em; color: var(--ink); }
.brand-sub { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); }
.topnav { display: flex; gap: 6px; align-items: center; }
.linkbtn {
  font-family: var(--sans); font-size: 12px; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--ink-dim); background: none; border: 1px solid transparent; border-radius: 8px;
  padding: 7px 12px; cursor: pointer; text-decoration: none;
}
.linkbtn:hover { color: var(--ink); border-color: var(--line); }

/* ---- layout ---- */
.game { width: 100%; max-width: var(--gw); margin: 0 auto; padding: clamp(20px, 3vw, 44px) clamp(18px, 5vw, 64px) 32px; }
.muted { color: var(--ink-dim); }
.center { text-align: center; }

/* ---- buttons ---- */
.btn {
  font-family: var(--sans); font-size: 15px; color: #1a150e;
  background: linear-gradient(180deg, #e7cd86, var(--gold));
  border: none; border-radius: 11px; padding: 12px 22px; cursor: pointer;
  transition: transform 0.12s cubic-bezier(0.22,1,0.36,1), filter 0.2s ease, box-shadow 0.25s ease; font-weight: 600; letter-spacing: 0.01em;
}
.btn:hover { filter: brightness(1.06); transform: translateY(-1px); box-shadow: 0 5px 18px rgba(217,184,102,0.24); }
.btn:active { transform: translateY(1px); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; filter: none; }
.btn.ghost { background: transparent; color: var(--ink); border: 1px solid var(--line); }
.btn.ghost:hover { background: rgba(233, 220, 196, 0.06); }
.btn.close { display: inline-block; text-decoration: none; margin-top: 18px; }

/* ============ CREATION ============ */
.create-intro { margin-bottom: 26px; }
.create-intro h1 { font-family: var(--serif); font-size: clamp(28px, 6vw, 40px); margin: 0 0 10px; font-weight: 500; }
.create-intro p { color: var(--ink-dim); max-width: 58ch; }

.points {
  position: sticky; top: 74px; z-index: 10;
  display: flex; align-items: baseline; gap: 10px; justify-content: center;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 12px 18px; margin-bottom: 22px;
}
.points-num { font-family: var(--serif); font-size: 30px; color: var(--gold); font-variant-numeric: tabular-nums; }
.points-num.spent { color: var(--red); }
.points-label { font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint); }

.presets { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 24px; justify-content: center; }
.preset {
  font-size: 13px; color: var(--ink-dim); background: var(--panel);
  border: 1px solid var(--line); border-radius: 20px; padding: 7px 14px; cursor: pointer;
}
.preset:hover { color: var(--ink); border-color: var(--gold-dim); }
.preset b { color: var(--ink); font-weight: 600; }

.stat-row { padding: 15px 2px 17px; border-top: 1px solid var(--line); }
.stat-row:last-child { border-bottom: 1px solid var(--line); }
.stat-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.stat-head { display: flex; align-items: center; gap: 9px; }
.stat-emblem { display: inline-flex; }
.stat-emblem svg { width: 19px; height: 19px; display: block; color: var(--sc, var(--gold)); filter: drop-shadow(0 1px 1px rgba(0,0,0,0.4)); }
.stat-name { font-family: var(--serif); font-size: 18px; }
.stat-short { font-size: 11px; letter-spacing: 0.12em; color: var(--ink-faint); }
.stat-desc { font-size: 13px; color: var(--ink-faint); margin: 3px 0 12px; max-width: 60ch; }
.points-num.done { color: var(--green); }

/* the number box — a real editable field */
.stat-num {
  width: 3.4em; text-align: center; font-family: var(--serif); font-size: 22px; font-variant-numeric: tabular-nums;
  color: var(--sc, var(--gold)); background: var(--panel2); border: 1px solid var(--line); border-radius: 9px; padding: 4px 2px;
  -moz-appearance: textfield; transition: border-color 0.15s, box-shadow 0.15s, color 0.15s;
}
.stat-num::-webkit-outer-spin-button, .stat-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.stat-num:focus { outline: none; border-color: var(--gold); box-shadow: 0 0 0 3px rgba(217,184,102,0.15); }
.stat-row.empty .stat-num { color: var(--ink-faint); }
.stat-row.maxed .stat-num { color: var(--green); border-color: rgba(127,176,105,0.5); }

/* the drag bar — a gold-filled track with a jewelled thumb */
.stat-range { -webkit-appearance: none; appearance: none; width: 100%; height: 18px; background: transparent; cursor: pointer; margin: 0; }
.stat-range::-webkit-slider-runnable-track {
  height: 8px; border-radius: 6px; border: 1px solid var(--line);
  background: linear-gradient(90deg, color-mix(in srgb, var(--sc, var(--gold)) 58%, #000), var(--sc, var(--gold))) left / var(--fill, 0%) 100% no-repeat, rgba(233,220,196,0.09);
}
.stat-range::-moz-range-track { height: 8px; border-radius: 6px; background: rgba(233,220,196,0.09); border: 1px solid var(--line); }
.stat-range::-moz-range-progress { height: 8px; border-radius: 6px; background: linear-gradient(90deg, color-mix(in srgb, var(--sc, var(--gold)) 58%, #000), var(--sc, var(--gold))); }
.stat-range::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; width: 18px; height: 18px; margin-top: -5px; border-radius: 50%;
  background: radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--sc, var(--gold)), #fff 48%), var(--sc, var(--gold)) 60%, color-mix(in srgb, var(--sc, var(--gold)), #000 28%));
  border: 1px solid #7a642f; box-shadow: 0 1px 5px rgba(0,0,0,0.5); transition: transform 0.1s ease;
}
.stat-range::-moz-range-thumb {
  width: 17px; height: 17px; border-radius: 50%; border: 1px solid #7a642f;
  background: radial-gradient(circle at 35% 30%, color-mix(in srgb, var(--sc, var(--gold)), #fff 48%), var(--sc, var(--gold)) 60%, color-mix(in srgb, var(--sc, var(--gold)), #000 28%)); box-shadow: 0 1px 5px rgba(0,0,0,0.5);
}
.stat-range:active::-webkit-slider-thumb { transform: scale(1.18); }
.stat-range:focus { outline: none; }
.stat-range:focus::-webkit-slider-thumb { box-shadow: 0 0 0 4px rgba(217,184,102,0.2), 0 1px 5px rgba(0,0,0,0.5); }

.create-actions { margin-top: 28px; display: flex; justify-content: center; }

/* ============ PLAY ============ */
.act-banner { text-align: center; margin-bottom: 8px; }
.act-name { font-size: 12px; letter-spacing: 0.28em; text-transform: uppercase; color: var(--gold-dim); }
.act-sub { font-size: 12px; color: var(--ink-faint); font-style: italic; }

.scene { animation: rise 0.5s cubic-bezier(0.16,1,0.3,1); }
@keyframes rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
.scene-title { font-family: var(--serif); font-size: clamp(24px, 5vw, 32px); font-weight: 500; margin: 4px 0 16px; }
.scene-text { font-family: var(--serif); font-size: 15.5px; line-height: 1.62; color: #d8cdb4; margin-bottom: 18px; } /* prose is supplementary now — the scene leads */

.choices { display: flex; flex-direction: column; gap: 10px; }
.choice {
  position: relative; overflow: hidden; text-align: left; width: 100%;
  font-family: var(--serif); font-size: 16px; color: var(--ink);
  background: linear-gradient(180deg, var(--panel2), var(--panel));
  border: 1px solid var(--line); border-left: 2px solid rgba(217,184,102,0.30); border-radius: 12px;
  padding: 15px 18px; cursor: pointer;
  transition: border-color 0.25s ease, background 0.25s ease, transform 0.2s cubic-bezier(0.22,1,0.36,1), box-shadow 0.25s ease;
  display: flex; align-items: center; gap: 13px;
}
/* a slow light sweeps across on hover */
.choice::after {
  content: ""; position: absolute; inset: 0; z-index: 0; pointer-events: none;
  background: linear-gradient(115deg, transparent 38%, rgba(217,184,102,0.13) 50%, transparent 62%);
  transform: translateX(-130%);
}
.choice > span { position: relative; z-index: 1; }
.choice .choice-label, .choice > span:first-of-type { flex: 1 1 auto; }
.choice:hover:not(.locked) {
  border-color: var(--gold-dim); border-left-color: var(--gold);
  transform: translateY(-1px); box-shadow: 0 6px 20px rgba(0,0,0,0.28), 0 0 0 1px rgba(217,184,102,0.12);
}
.choice:hover:not(.locked)::after { animation: choice-sheen 0.85s ease; }
@keyframes choice-sheen { from { transform: translateX(-130%); } to { transform: translateX(130%); } }
.choice:active:not(.locked) { transform: translateY(0); }
.choice.locked { opacity: 0.5; cursor: not-allowed; }

/* ---- picking a choice is a moment: the chosen option confirms, the rest fall away ---- */
.choices.committing { pointer-events: none; }
/* !important + the extra .committing specificity beat the flow-in entrance animation, which fills both and would otherwise hold opacity:1 */
.choices.committing .choice:not(.chosen) { opacity: 0.28 !important; filter: saturate(0.5); transition: opacity 0.32s ease, filter 0.32s ease; }
.choices.committing .choice.chosen { z-index: 2; border-color: var(--gold) !important; border-left-color: var(--gold) !important; animation: choice-commit 0.44s cubic-bezier(0.22,1,0.36,1) both !important; }
.choices.committing .choice.chosen::after { animation: choice-sheen 0.6s ease 0.04s; }
@keyframes choice-commit {
  0%   { box-shadow: 0 0 0 0 rgba(217,184,102,0.55), 0 6px 20px rgba(0,0,0,0.28); }
  45%  { box-shadow: 0 0 0 5px rgba(217,184,102,0.26), 0 12px 32px rgba(0,0,0,0.42); transform: translateY(-2px) scale(1.012); }
  100% { box-shadow: 0 0 0 1px rgba(217,184,102,0.40), 0 8px 26px rgba(0,0,0,0.34); transform: none; }
}

/* the odds / requirement tag as a small jewelled pill */
.choice-tag {
  font-family: var(--sans); font-size: 11px; letter-spacing: 0.05em; white-space: nowrap; color: var(--ink-dim);
  padding: 3px 10px; border-radius: 20px; border: 1px solid var(--line); background: rgba(0,0,0,0.2);
}
.choice-tag:empty { display: none; }
.choice-tag .odds-favored { color: var(--green); text-shadow: 0 0 8px rgba(127,176,105,0.45); }
.choice-tag .odds-even { color: var(--gold); text-shadow: 0 0 8px rgba(217,184,102,0.45); }
.choice-tag .odds-risky { color: var(--red); text-shadow: 0 0 8px rgba(217,140,115,0.45); }
.choice-tag .locktag { color: var(--red); }

.outcome { animation: rise 0.5s ease; }
.outcome-text { font-family: var(--serif); font-size: 18px; line-height: 1.7; color: #e8ddc7; margin: 6px 0 16px; }
.result-flag { font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; margin-bottom: 10px; }
.result-flag.ok { color: var(--green); }
.result-flag.no { color: var(--red); }
.deltas { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 22px; }
.delta { font-size: 12px; padding: 4px 9px; border-radius: 20px; border: 1px solid var(--line); font-variant-numeric: tabular-nums; }
.delta.up { color: var(--green); border-color: rgba(127,176,105,0.4); }
.delta.down { color: var(--red); border-color: rgba(217,140,115,0.4); }

/* ---- stat rail (during play): a row of unique tokens; every change pulses and floats ---- */
.rail {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 15;
  background: linear-gradient(180deg, rgba(24,20,15,0.84), rgba(19,15,11,0.96));
  border-top: 1px solid rgba(217,184,102,0.22); box-shadow: 0 -9px 28px rgba(0,0,0,0.38);
  backdrop-filter: blur(12px);
  padding: 9px clamp(12px, 4vw, 40px);
}
.rail-inner { max-width: 900px; margin: 0 auto; display: flex; gap: 18px; align-items: center; flex-wrap: wrap; justify-content: center; }
.pip { position: relative; display: flex; flex-direction: column; align-items: center; gap: 3px; min-width: 42px; }
.pip-short { font-size: 10px; letter-spacing: 0.1em; color: var(--ink-faint); }
.pip-val { font-family: var(--serif); font-size: 16px; font-variant-numeric: tabular-nums; transition: color 0.3s ease, text-shadow 0.3s ease; }
.pip-bar { width: 40px; height: 3px; border-radius: 3px; background: rgba(233,220,196,0.1); overflow: hidden; }
.pip-bar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--gold-dim), var(--gold)); width: 0%; transition: width 1.15s cubic-bezier(0.22,1,0.36,1), box-shadow 1.1s ease; }
.rail-sep { width: 1px; align-self: stretch; background: var(--line); margin: 4px 0; }

/* the three life-meters read as their own emblemed gauges */
.meter-pip { min-width: 58px; padding: 3px 9px 5px; border-radius: 10px; background: rgba(233,220,196,0.03); border: 1px solid var(--line); }
.meter-pip .pip-icon { display: block; height: 15px; line-height: 0; margin-bottom: 1px; }
.meter-pip .pip-icon svg { width: 15px; height: 15px; display: block; }
.meter-pip .pip-bar { width: 62px; height: 6px; }
.meter-pip.hitPoints { border-color: rgba(217,140,115,0.32); } .meter-pip.hitPoints .pip-icon { color: var(--red); } .meter-pip.hitPoints .pip-bar > i { background: linear-gradient(90deg, #b8543f, var(--red)); }
.meter-pip.armor { border-color: rgba(199,204,212,0.28); } .meter-pip.armor .pip-icon { color: #c7ccd4; } .meter-pip.armor .pip-bar > i { background: linear-gradient(90deg, #7d828c, #c7ccd4); }
/* A METER THAT MOVES WHILE YOU RUN. The bar's 1.15s ease is right for a value that changes once when
   something happens to you, and hopeless for one draining nine points a second -- it would still be
   sliding toward last second's figure. `.live` is set by paintWind the first time the place spends any,
   so nothing else on the rail is affected. `.spent` warns; `.blown` is you asking to run on empty. */
.meter-pip.stamina.live .pip-bar > i { transition: width 0.16s linear; }
.meter-pip.stamina.spent .pip-val { color: #d8a05e; }
.meter-pip.stamina.spent .pip-bar > i { background: linear-gradient(90deg, #8a5a2a, #d8a05e); }
.meter-pip.stamina.blown { border-color: rgba(217,140,115,0.5); animation: wind-blown 0.7s ease-in-out infinite; }
.meter-pip.stamina.blown .pip-val { color: var(--red); }
@keyframes wind-blown { 0%, 100% { opacity: 1; } 50% { opacity: 0.55; } }
@media (prefers-reduced-motion: reduce) { .meter-pip.stamina.blown { animation: none; } }

.meter-pip.spirit { border-color: rgba(127,168,201,0.32); } .meter-pip.spirit .pip-icon { color: var(--blue); } .meter-pip.spirit .pip-bar > i { background: linear-gradient(90deg, #4f7fa8, var(--blue)); }

/* ---- the seven virtues as illuminated, colour-signed tokens ---- */
.stat-pip { min-width: 46px; }
.stat-pip .pip-top { display: flex; align-items: center; gap: 5px; }
.stat-pip .pip-emblem { display: inline-flex; }
.stat-pip .pip-emblem svg { width: 15px; height: 15px; display: block; color: var(--sc, var(--gold)); filter: drop-shadow(0 1px 1px rgba(0,0,0,0.45)); }
.stat-pip .pip-bar { width: 46px; }
.stat-pip .pip-bar > i { background: var(--sc, var(--gold)); background: linear-gradient(90deg, color-mix(in srgb, var(--sc) 60%, #000), var(--sc)); }
.stat-pip .pip-short { color: var(--ink-faint); letter-spacing: 0.08em; }

/* a change blooms the token slowly, haloes it, and floats a rising +/- that lingers */
.rl-coin { position: relative; }
.pip.flash-up .pip-val, .rl-coin.flash-up { color: var(--green); text-shadow: 0 0 12px rgba(127,176,105,0.6); }
.pip.flash-down .pip-val, .rl-coin.flash-down { color: var(--red); text-shadow: 0 0 12px rgba(217,140,115,0.6); }
.pip.flash-up, .pip.flash-down { animation: pip-bloom 1.3s cubic-bezier(0.22,1,0.36,1); }
@keyframes pip-bloom { 0% { transform: translateY(0) scale(1); } 34% { transform: translateY(-1.5px) scale(1.07); } 100% { transform: none; } }
.pip.flash-up .pip-bar > i { box-shadow: 0 0 12px rgba(127,176,105,0.8); }
.pip.flash-down .pip-bar > i { box-shadow: 0 0 12px rgba(217,140,115,0.8); }
.pip.flash-up::after, .pip.flash-down::after { content: ""; position: absolute; inset: -7px; border-radius: 14px; z-index: -1; pointer-events: none; opacity: 0; animation: pip-halo 1.5s ease-out; }
.pip.flash-up::after { background: radial-gradient(circle, rgba(127,176,105,0.34), transparent 70%); }
.pip.flash-down::after { background: radial-gradient(circle, rgba(217,140,115,0.34), transparent 70%); }
@keyframes pip-halo { 0% { opacity: 0; transform: scale(0.55); } 32% { opacity: 1; } 100% { opacity: 0; transform: scale(1.25); } }
.pip-float {
  position: absolute; left: 50%; top: -8px; font-size: 12.5px; font-weight: 700;
  font-variant-numeric: tabular-nums; pointer-events: none; white-space: nowrap; text-shadow: 0 1px 3px rgba(0,0,0,0.5);
  animation: pip-float 1.9s cubic-bezier(0.16,0.9,0.3,1) forwards;
}
.pip-float.up { color: var(--green); } .pip-float.down { color: var(--red); }
@keyframes pip-float { 0% { opacity: 0; transform: translate(-50%, 6px) scale(0.9); } 18% { opacity: 1; transform: translate(-50%, 0) scale(1); } 70% { opacity: 1; } 100% { opacity: 0; transform: translate(-50%, -26px) scale(1); } }
@media (prefers-reduced-motion: reduce) {
  .pip.flash-up, .pip.flash-down, .pip.flash-up::after, .pip.flash-down::after, .pip-float, .travel-anim .ta-walker, .travel-anim .ta-dune, .combat.clash, .cbar-track.hit, .cbar-track.hurt, .choice::after { animation: none !important; }
}

/* ---- reflection (verse) aside ---- */
.reflection {
  position: fixed; right: 18px; top: 88px; width: min(330px, 40vw); z-index: 12;
  background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 12px; padding: 18px 20px;
  opacity: 0; transform: translateX(16px);
  transition: opacity 1s ease, transform 1s cubic-bezier(0.16,1,0.3,1);
  box-shadow: 0 14px 40px rgba(0,0,0,0.3);
}
.reflection.show { opacity: 1; transform: none; }
.reflection-sign { font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent); margin-bottom: 9px; }
.reflection-text { font-family: var(--serif); font-size: 16px; line-height: 1.55; margin: 0 0 10px; color: #e8ddc7; }
.reflection-ref { font-size: 12px; color: var(--ink-faint); }

/* ============ END ============ */
.ending { text-align: center; animation: rise 0.7s ease; }
.ending-kicker { font-size: 12px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold-dim); }
.ending h1 { font-family: var(--serif); font-size: clamp(30px, 7vw, 46px); font-weight: 500; margin: 8px 0 18px; }
.ending-body { font-family: var(--serif); font-size: 19px; line-height: 1.7; color: #e8ddc7; max-width: 56ch; margin: 0 auto 28px; }
.growth { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 10px; max-width: 560px; margin: 0 auto 28px; text-align: left; }
.growth-row { display: flex; justify-content: space-between; align-items: baseline; padding: 8px 12px; background: var(--panel); border: 1px solid var(--line); border-radius: 9px; }
.growth-name { font-size: 13px; color: var(--ink-dim); }
.growth-change { font-family: var(--serif); font-variant-numeric: tabular-nums; }
.growth-change .g-up { color: var(--green); } .growth-change .g-down { color: var(--red); }

/* ---- modals ---- */
.modal { position: fixed; inset: 0; display: none; place-items: center; padding: 20px; z-index: 40; background: rgba(8,6,4,0.6); backdrop-filter: blur(4px); }
.modal.open { display: grid; }
.modal-card { max-width: 560px; width: 100%; max-height: 82vh; overflow: auto; background: var(--panel); border: 1px solid var(--line); border-radius: 16px; padding: 28px 30px; }
.modal-card h2 { font-family: var(--serif); font-weight: 500; margin: 0 0 8px; }
.modal-card p { color: #ded3bd; }
.modal-card em { font-style: italic; color: var(--ink); }
.modal-card .fineprint { font-size: 12.5px; color: var(--ink-faint); margin-top: 16px; }
.modal-card a { color: var(--gold); }
.journal-list { display: flex; flex-direction: column; gap: 12px; margin: 16px 0; }
.journal-item { border-left: 2px solid var(--gold-dim); padding-left: 14px; }
.journal-item .ji-text { font-family: var(--serif); color: #e8ddc7; }
.journal-item .ji-ref { font-size: 12px; color: var(--ink-faint); margin-top: 3px; }
.journal-empty { color: var(--ink-faint); font-style: italic; }

/* ============ BACKSTORY REVEAL ============ */
.reveal { animation: rise 0.6s ease; max-width: 640px; margin: 10px auto; }
.reveal-kicker { text-align: center; font-size: 12px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 18px; }
.backstory { font-family: var(--serif); font-size: 20px; line-height: 1.72; color: #ece2cd; text-wrap: pretty; }
.backstory.small { font-size: 16px; line-height: 1.6; }
.reveal-trait { margin-top: 22px; padding: 14px 18px; border: 1px solid var(--line); border-left: 3px solid var(--gold); border-radius: 12px; background: var(--panel); font-size: 15px; color: #ded3bd; }
.reveal-trait strong { color: var(--gold); }
.reveal-actions { display: flex; gap: 10px; justify-content: center; margin-top: 26px; }
/* the backstory reveal now sits in an illuminated manuscript frame */
.reveal.illum { padding: 30px clamp(20px, 5vw, 40px); max-width: 680px; }
/* the live "who that makes you" panel on the allocate screen */
.alloc-preview { margin: 18px auto 0; max-width: 520px; border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 12px; background: linear-gradient(180deg, var(--panel2), var(--panel)); padding: 14px 18px; }
.ap-title { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 9px; }
.ap-row { display: flex; justify-content: space-between; gap: 12px; padding: 5px 0; border-bottom: 1px solid rgba(233,220,196,0.05); font-size: 14px; }
.ap-row:last-child { border-bottom: none; }
.ap-k { color: var(--ink-dim); }
.ap-v { color: var(--ink); font-family: var(--serif); text-align: right; }
/* per-trial glyph on the "what you carry" reveal (mind-core for mental, mountains for physical) */
.trial-row { display: flex; align-items: flex-start; gap: 12px; }
.trial-glyph { flex: 0 0 auto; width: 24px; height: 24px; margin-top: 2px; color: var(--gold-dim); }
.trial-glyph svg { width: 100%; height: 100%; display: block; }
.trial-glyph.mental { color: #9db4d6; }
.trial-body { flex: 1 1 auto; min-width: 0; }

/* ============ PATHS (classes) ============ */
.path-preamble { font-size: 14.5px; color: var(--ink-dim); font-style: italic; max-width: 62ch; }
.paths { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 12px; margin-bottom: 8px; }
/* origin vignette cards (the creation "where you come from" step) */
.origins { display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); gap: 13px; margin-bottom: 6px; }
.origin { position: relative; background: linear-gradient(180deg, var(--panel2), var(--panel)); border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 13px; padding: 16px 18px; cursor: pointer; transition: border-color 0.18s ease, transform 0.18s ease, box-shadow 0.18s ease; }
.origin:hover { border-color: var(--gold-dim); border-left-color: var(--gold); transform: translateY(-2px); box-shadow: 0 8px 22px rgba(0,0,0,0.3); }
.origin.picked { border-color: var(--gold); border-left-color: var(--gold); box-shadow: 0 0 0 1px rgba(217,184,102,0.3), 0 8px 22px rgba(0,0,0,0.35); }
.origin-label { font-family: var(--serif); font-size: 18px; color: var(--gold); margin-bottom: 7px; }
.origin-line { font-size: 14px; color: var(--ink-dim); line-height: 1.55; margin: 0 0 11px; }
.origin-seeds { display: flex; flex-wrap: wrap; gap: 6px; }
.origin-seed { font-size: 11px; letter-spacing: 0.03em; padding: 3px 9px; border-radius: 20px; border: 1px solid var(--line); background: rgba(217,184,102,0.08); color: var(--ink-dim); }
.path { border: 1px solid var(--line); border-radius: 14px; padding: 18px 20px; background: var(--panel); cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease; }
.path:hover { border-color: var(--gold-dim); background: var(--panel2); }
.path.picked { border-color: var(--gold); background: var(--panel2); box-shadow: inset 0 0 0 1px var(--gold-dim); }
/* per-path aniconic hero banner, bleeding across the top of the card */
.path.has-art { padding-top: 0; overflow: hidden; }
.path-art { position: relative; margin: 0 -20px 14px; aspect-ratio: 300 / 96; max-height: 116px; overflow: hidden; border-bottom: 1px solid var(--line); }
.path-art svg { width: 100%; height: 100%; display: block; }
.path-art::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 -18px 26px -14px rgba(5,9,12,0.85); pointer-events: none; }
.path.picked .path-art { border-bottom-color: var(--gold-dim); }
.path-name { font-family: var(--serif); font-size: 20px; }
.path-prophet { font-size: 12px; letter-spacing: 0.04em; color: var(--gold-dim); margin: 2px 0 10px; }
.path-exp { font-size: 14px; line-height: 1.55; color: #ded3bd; margin: 0 0 12px; }
.path-verse { display: block; border-left: 2px solid var(--gold-dim); padding: 2px 0 2px 12px; margin: 0 0 12px; min-height: 1em; }
.pv-text { display: block; font-family: var(--serif); font-size: 14px; font-style: italic; color: #e8ddc7; line-height: 1.5; }
.pv-ref { display: block; font-size: 11px; color: var(--ink-faint); margin-top: 4px; letter-spacing: 0.04em; }
.path-abil { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.path-abil li { font-size: 13px; color: var(--ink-dim); line-height: 1.45; }
.path-abil b { color: var(--ink); }
.pv-cite { color: var(--gold-dim); font-size: 11px; }
.ending-path { font-size: 12px; letter-spacing: 0.05em; margin: 14px 0 24px; }

/* ---- the shahada moment ---- */
.shahada { max-width: 600px; margin: 30px auto; text-align: center; animation: rise 0.9s ease; }
.shahada-prompt { font-family: var(--serif); font-size: 19px; line-height: 1.7; color: #e8ddc7; margin: 14px 0 26px; }
.shahada-words { margin: 0 auto 24px; padding: 20px 24px; border: 1px solid var(--line); border-top: 2px solid var(--gold); border-bottom: 2px solid var(--gold); border-radius: 6px; background: rgba(217,184,102,0.05); max-width: 520px; }
.sh-en { display: block; font-family: var(--serif); font-size: 21px; line-height: 1.5; color: var(--ink); }
.sh-tr { display: block; margin-top: 10px; font-size: 14px; letter-spacing: 0.02em; color: var(--gold-dim); font-style: italic; }
.shahada .reveal-actions { margin-top: 26px; }

/* ---- ending verse + closing ---- */
.ending-how { font-size: 14px; font-style: italic; margin: 4px 0 14px; }
.ending-verse { display: block; max-width: 560px; margin: 0 auto 14px; padding: 14px 18px; border-left: 2px solid var(--gold-dim); text-align: left; min-height: 1em; }
.ending .ending-verse { background: var(--panel); border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 10px; }
.ending-closing { font-family: var(--serif); font-style: italic; color: var(--gold-dim); font-size: 15px; margin: 6px 0 4px; }

/* keep the sacred reflection apart from the mechanical run summary */
.ending-reflection { padding-bottom: 8px; }
.ending-divider { border: 0; height: 1px; background: var(--line); max-width: 120px; margin: 40px auto; opacity: 0.7; }
.run-summary { opacity: 0.82; }
.rs-label { font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 14px; }
.run-summary .ending-trait { font-size: 13.5px; }
.run-summary .growth { margin-top: 12px; }

/* the beat of stillness after bearing witness */
.stillness { min-height: 46vh; display: grid; place-items: center; }
.still-mark { width: 5px; height: 5px; border-radius: 50%; background: var(--gold-dim); animation: breathe 2.6s ease-in-out infinite; }
@keyframes breathe { 0%, 100% { opacity: 0.15; transform: scale(1); } 50% { opacity: 0.7; transform: scale(1.5); } }

/* ============ TALENTS ============ */
.talents-screen { animation: rise 0.5s ease; }
.talent-group { margin-bottom: 22px; }
.talent-label { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 10px; display: flex; gap: 10px; align-items: baseline; }
.linkbtn.tiny { padding: 2px 8px; font-size: 10px; }
.talent-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px; }
.talent { border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; background: var(--panel); }
.talent.granted { border-left: 3px solid var(--gold-dim); }
.talent.choose { cursor: pointer; transition: border-color 0.15s ease, background 0.15s ease; }
.talent.choose:hover { border-color: var(--gold-dim); background: var(--panel2); }
.talent.picked { border-color: var(--gold); background: var(--panel2); box-shadow: inset 0 0 0 1px var(--gold-dim); }
.talent-name { font-family: var(--serif); font-size: 17px; margin-bottom: 5px; }
.talent-desc { font-size: 13.5px; color: var(--ink-dim); line-height: 1.5; margin-bottom: 8px; }
.talent-eff { font-size: 12px; color: var(--gold-dim); }

/* ============ TRAIT SHIFT BANNER ============ */
.trait-shift { animation: rise 0.5s ease; border: 1px solid var(--line); border-radius: 14px; padding: 16px 20px; margin-bottom: 22px; background: linear-gradient(180deg, rgba(217,184,102,0.08), rgba(217,184,102,0)); }
.trait-shift.slipped { background: linear-gradient(180deg, rgba(217,140,115,0.08), rgba(217,140,115,0)); }
.ts-kicker { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); }
.trait-shift.slipped .ts-kicker { color: var(--red); }
.ts-name { font-family: var(--serif); font-size: 20px; margin: 6px 0; }
.ts-name strong { color: var(--gold); }
.ts-line { font-size: 14px; color: var(--ink-dim); margin-bottom: 10px; }
.ts-eff { display: flex; flex-wrap: wrap; gap: 8px; }
.ts-buff, .ts-debuff { font-size: 12px; padding: 4px 10px; border-radius: 20px; border: 1px solid var(--line); }
.ts-buff { color: var(--green); border-color: rgba(127,176,105,0.4); }
.ts-debuff { color: var(--red); border-color: rgba(217,140,115,0.4); }

/* ---- pip bonus badge on rail ---- */
.pip-bonus { font-size: 9px; padding: 0 3px; border-radius: 4px; margin-left: 2px; vertical-align: top; }
.pip-bonus.up { color: var(--green); } .pip-bonus.down { color: var(--red); }

/* ---- character modal blocks ---- */
.char-block { margin-bottom: 18px; }
.char-h { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 8px; }
.talent-line { font-size: 14px; margin-bottom: 5px; }

/* ---- ending additions ---- */
.ending-trait { font-size: 15px; color: #ded3bd; max-width: 52ch; margin: 0 auto 22px; }
.ending-trait strong { color: var(--gold); }
.ending-talents { font-size: 12px; letter-spacing: 0.06em; margin: 16px 0 24px; }

/* ============ START — an illuminated frontispiece ============ */
/* corner star ornaments (a manuscript frame) */
.illum-corner.tr { top: 9px; right: 9px; }
.illum-corner.br { bottom: 9px; right: 9px; }

/* the animated dawn hero */
50% { opacity: 1; } }
50% { opacity: 1; transform: scale(1.05); } }
}
50% { opacity: 0.3; } }

.shahada-choice { position: relative; overflow: hidden; display: flex; flex-direction: column; gap: 8px; text-align: center; width: 100%; max-width: 520px; padding: 20px 22px; background: linear-gradient(180deg, rgba(217,184,102,0.16), rgba(217,184,102,0.05)); border: 1px solid var(--gold-dim); color: var(--ink); border-radius: 14px; cursor: pointer; animation: sh-glow 4s ease-in-out infinite; transition: filter 0.2s, border-color 0.2s, transform 0.12s; }
@keyframes sh-glow { 0%, 100% { box-shadow: 0 0 0 rgba(217,184,102,0); } 50% { box-shadow: 0 0 22px rgba(217,184,102,0.22); } }
.shahada-choice::after { content: ""; position: absolute; inset: 0; background: linear-gradient(115deg, transparent 40%, rgba(255,240,200,0.14) 50%, transparent 60%); transform: translateX(-130%); pointer-events: none; }
.shahada-choice:hover { filter: brightness(1.06); border-color: var(--gold); transform: translateY(-1px); }
.shahada-choice:hover::after { animation: choice-sheen 0.9s ease; }
.shahada-choice > * { position: relative; z-index: 1; }
.shahada-choice .sc-en { font-family: var(--serif); font-size: 19px; line-height: 1.45; color: var(--ink); }
.shahada-choice .sc-tr { font-size: 14px; font-style: italic; color: var(--gold-dim); }
.shahada-choice .sc-do { font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold); margin-top: 4px; }
/* staged reveal — the frontispiece assembles piece by piece */
.rv { opacity: 0; animation: start-rise 0.85s cubic-bezier(0.16,1,0.3,1) forwards; }
.rv2 { animation-delay: 0.4s; } .rv3 { animation-delay: 0.66s; } .rv4 { animation-delay: 0.95s; }
@keyframes start-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes start-fade { to { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  .start-hero, .rv { opacity: 1 !important; animation: none !important; }
  .ia-star, .ia-glow, .ia-sun, .ia-rays, .ia-road, .ia-wf-sway, .ia-wf-lamp, .shahada-choice, .shahada-choice::after { animation: none !important; }
}

/* ============ HUB (a place on the map) ============ */
.hub { max-width: 680px; margin: 6px auto; animation: rise 0.4s ease; padding-bottom: 40px; }

/* IMMERSIVE HUB: the location is a full scene you stand IN (the wayfarer in it), actions a compact layer below */
.hub.immersive { max-width: 760px; }
.hub-stage { position: relative; width: 100%; height: clamp(310px, 53vh, 500px); border-radius: 14px; overflow: hidden; border: 1px solid var(--gold-dim); box-shadow: 0 12px 36px rgba(0,0,0,0.5), inset 0 0 0 1px rgba(217,184,102,0.1); margin-bottom: 13px; background: #0d0a06; }
.scene-stage .scene-art { position: absolute; inset: 0; width: 100%; height: 100%; }
/* encounter stage: the scene-first, wayfarer-present narrative screen (shares .scene-stage / .stage-* with the hub) */
.scene-view { max-width: 760px; margin: 6px auto; animation: rise 0.4s ease; padding-bottom: 40px; }
.enc-stage { position: relative; width: 100%; height: clamp(250px, 46vh, 420px); border-radius: 14px; overflow: hidden; border: 1px solid var(--gold-dim); box-shadow: 0 10px 30px rgba(0,0,0,0.45); margin-bottom: 15px; background: #0d0a06; }
.enc-stage .stage-wayfarer { height: 52%; bottom: 3%; }
.stage-cap { display: flex; align-items: center; gap: 8px; font-size: 12.5px; letter-spacing: 0.03em; color: var(--gold); }
.stage-cap .cap-name { color: #f0e2c4; }
.stage-cap svg, .stage-cap .npc-sigil { width: 17px; height: 17px; flex: none; }
.stage-top .scene-title { font-family: var(--serif); font-size: clamp(20px, 4vw, 27px); font-weight: 500; margin: 3px 0 0; color: #f6ecd3; text-shadow: 0 2px 10px rgba(0,0,0,0.72); }
.scene-view .scene { max-width: none; margin: 0; }
.stage-foot { position: absolute; left: 0; right: 0; bottom: 0; height: 60%; background: linear-gradient(to top, rgba(9,6,3,0.95), rgba(9,6,3,0.55) 40%, transparent); pointer-events: none; z-index: 1; }
.stage-wayfarer { position: absolute; left: 50%; bottom: 3.5%; transform: translateX(-50%); height: 47%; width: auto; z-index: 2; filter: drop-shadow(0 4px 7px rgba(0,0,0,0.6)); }
.stage-top { position: absolute; top: 0; left: 0; right: 0; padding: 15px 20px 32px; background: linear-gradient(to bottom, rgba(9,6,3,0.74), transparent); z-index: 2; pointer-events: none; }
.stage-arc { font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--gold); opacity: 0.92; }
.stage-loc { font-family: var(--serif); font-size: clamp(24px, 4.6vw, 34px); font-weight: 500; margin: 3px 0 0; color: #f6ecd3; text-shadow: 0 2px 12px rgba(0,0,0,0.75); }
.stage-wayfarer .wf-body { stroke-width: 0.34; }
.hub-here { display: flex; flex-direction: column; gap: 4px; margin: 0 0 14px; }
.hub-here .loc-state, .hub-here .loc-detail { font-family: var(--serif); font-style: italic; font-size: 14.5px; line-height: 1.5; }
.hub-here .town-view { margin: 0; }
.hub.immersive .hub-acts { grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 8px; }
.hub.immersive .hub-act { padding: 9px 12px; font-size: 13.5px; gap: 9px; }
.hub.immersive .ha-icon svg { width: 17px; height: 17px; }
/* the place's mood tints the stage */
.hub-stage.state-grateful::after, .hub-stage.state-thriving::after, .hub-stage.state-wary::after, .hub-stage.state-grieving::after, .hub-stage.state-troubled::after { content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none; mix-blend-mode: soft-light; }
.hub-stage.state-grateful::after, .hub-stage.state-thriving::after { background: radial-gradient(120% 90% at 50% 20%, rgba(217,184,102,0.28), transparent 70%); }
.hub-stage.state-wary::after, .hub-stage.state-troubled::after { background: radial-gradient(120% 90% at 50% 30%, rgba(190,70,55,0.34), transparent 68%); }
.hub-stage.state-grieving::after { background: linear-gradient(rgba(70,86,120,0.3), rgba(20,24,34,0.4)); mix-blend-mode: multiply; }

.loc-arc { font-size: 11px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--gold-dim); }
.loc-name { font-family: var(--serif); font-size: clamp(24px, 5vw, 32px); font-weight: 500; margin: 4px 0 10px; }
.loc-desc { font-family: var(--serif); font-size: 17px; line-height: 1.65; color: #e8ddc7; margin-bottom: 12px; }
.town-view { font-size: 13.5px; font-style: italic; color: var(--gold-dim); margin: 0 0 18px; }
.ending-rep { font-size: 13px; color: var(--ink-dim); margin: 8px 0; }
.debuff-row { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; }
.debuff-chip { font-size: 12px; color: var(--red); border: 1px solid rgba(217,140,115,0.4); border-radius: 20px; padding: 4px 12px; }
.hub-acts { display: grid; grid-template-columns: repeat(auto-fit, minmax(146px, 1fr)); gap: 8px; }
/* the secondary "Tend to yourself" fold: the inner life + housekeeping, visually lighter so the place's own actions lead */
.hub-tend { margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line); }
.hub-tend-h { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 10px; opacity: 0.85; }
.hub-acts-sec { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 8px; opacity: 0.9; }
.hub-acts-sec .hub-act { font-size: 14.5px; padding: 10px 13px; background: rgba(34, 29, 22, 0.5); }
.hub-acts-sec .hub-act:hover { opacity: 1; }
.hub-act { display: flex; align-items: center; gap: 11px; text-align: left; font-family: var(--serif); font-size: 16px; color: var(--ink); background: var(--panel); border: 1px solid var(--line); border-radius: 11px; padding: 13px 15px; cursor: pointer; transition: border-color 0.15s, background 0.15s, transform 0.12s ease, box-shadow 0.2s ease; }
.hub-act:hover { border-color: var(--gold-dim); background: var(--panel2); }
.hub-act.faith { border-left: 3px solid var(--gold); }
.hub-act.found { border-left: 3px solid var(--green); color: var(--green); }
.hub-act.quiet { font-size: 13.5px; color: var(--ink-dim); font-family: var(--sans); }

/* an icon per action so no two option boxes read the same */
.ha-icon { flex: 0 0 auto; display: inline-flex; color: var(--gold-dim); }
.ha-icon svg { width: 19px; height: 19px; display: block; }
.ha-label { flex: 1 1 auto; min-width: 0; }
/* category icon tints (match each accent) */
.hub-act.faith .ha-icon { color: var(--gold); }
.hub-act.people .ha-icon { color: #d69f7e; }
.hub-act.provision .ha-icon { color: #6bbf59; }
.hub-act.charity .ha-icon { color: #e2879e; }
.hub-act.learn .ha-icon { color: #7fbfa8; }
.hub-act.rest .ha-icon { color: var(--blue); }
.hub-act.found .ha-icon { color: var(--green); }
.hub-act.quiet .ha-icon { color: var(--ink-faint); }
/* the otherwise-plain actions each get their own accent + icon tint, so the whole board reads varied */
.hub-act[data-act="look"] { border-left: 3px solid #b9a06a; } .hub-act[data-act="look"] .ha-icon { color: #cdb679; }
.hub-act[data-act="travel"] { border-left: 3px solid #c2954c; } .hub-act[data-act="travel"] .ha-icon { color: #d7a659; }
.hub-act[data-act="market"] { border-left: 3px solid var(--gold); } .hub-act[data-act="market"] .ha-icon { color: var(--gold); }
.hub-act[data-act="sell"] { border-left: 3px solid var(--gold-dim); } .hub-act[data-act="sell"] .ha-icon { color: var(--gold-dim); }
.hub-act[data-act="ask"] { border-left: 3px solid #7fa8c9; } .hub-act[data-act="ask"] .ha-icon { color: #7fa8c9; }
.hub-act[data-act="work"] { border-left: 3px solid #b08a4e; } .hub-act[data-act="work"] .ha-icon { color: #c79a58; }
.hub-act[data-act="past"] { border-left: 3px solid #9db4d6; } .hub-act[data-act="past"] .ha-icon { color: #9db4d6; }
.hub-act[data-act="future"] { border-left: 3px solid #e0a24f; } .hub-act[data-act="future"] .ha-icon { color: #e0a24f; }
.hub-act[data-act="guide"] { border-left: 3px solid #cbb27a; } .hub-act[data-act="guide"] .ha-icon { color: #dcc389; }
.hub-act[data-act="train"] { border-left: 3px solid #7fbfa8; } .hub-act[data-act="train"] .ha-icon { color: #8fd0b8; }

/* ============ RAIL (now with a life line) ============ */
/* width:100% forces the pip row to the rail's width so flex-wrap actually wraps to fit — without it the
   column parent (align-items:center) sizes the row to its content and it overruns narrow (<=360px) screens */
.rail-inner { flex-direction: column; gap: 7px; width: 100%; }
.rail-life { display: flex; gap: 16px; justify-content: center; align-items: baseline; font-size: 11px; letter-spacing: 0.08em; flex-wrap: wrap; max-width: 100%; }
.rl-arc { color: var(--gold-dim); text-transform: uppercase; letter-spacing: 0.16em; }
.rl-age { color: var(--ink-dim); }
.rl-coin { color: var(--gold); font-variant-numeric: tabular-nums; }
.rail-pips { display: flex; gap: 14px 12px; flex-wrap: wrap; justify-content: center; align-items: center; width: 100%; max-width: 100%; }

/* ============ CHECKPOINT (arc turning + ambition) ============ */
.checkpoint { max-width: 620px; margin: 20px auto; text-align: center; animation: rise 0.7s ease; }
.cp-desc { font-family: var(--serif); font-size: 18px; line-height: 1.7; color: #e8ddc7; margin: 8px 0 16px; }
.cp-shift { font-size: 14px; color: var(--gold-dim); margin-bottom: 22px; }
.cp-shift strong { color: var(--gold); }
.cp-amb-label { font-size: 12px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); margin-bottom: 12px; }
.cp-ambitions { display: flex; flex-direction: column; gap: 10px; }
.cp-amb { text-align: left; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 14px 18px; cursor: pointer; color: var(--ink); }
.cp-amb:hover { border-color: var(--gold-dim); background: var(--panel2); }
.cp-amb b { font-family: var(--serif); font-size: 17px; display: block; }
.cp-amb span { font-size: 13.5px; color: var(--ink-dim); }
.cp-amb.hold { opacity: 0.72; }

/* ---- a trait the road forged (gained at a turning) ---- */
.cp-gain-note { font-size: 13px; margin: 0 0 14px; }
.cp-gain-note strong { color: var(--gold); }
.cp-carry { font-size: 13px; font-style: italic; margin: -6px 0 16px; }
.cp-gains { display: flex; flex-direction: column; gap: 10px; }
.cp-gain {
  text-align: left; background: linear-gradient(180deg, var(--panel2), var(--panel));
  border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 12px;
  padding: 14px 18px; cursor: pointer; color: var(--ink);
  transition: border-color 0.15s, background 0.15s, transform 0.12s ease, box-shadow 0.2s ease;
}
.cp-gain:hover { border-color: var(--gold); border-left-color: var(--gold); transform: translateY(-1px); box-shadow: 0 5px 16px rgba(0,0,0,0.25); }
.cp-gain b { font-family: var(--serif); font-size: 17px; display: block; }
.cp-gain .cpg-desc { display: block; font-size: 13.5px; color: var(--ink-dim); margin: 3px 0 8px; line-height: 1.5; }
.cp-gain .cpg-eff { display: flex; flex-wrap: wrap; gap: 8px; }
.cpg-buff, .cpg-debuff { font-size: 12px; padding: 3px 10px; border-radius: 20px; border: 1px solid var(--line); }
.cpg-buff { color: var(--green); border-color: rgba(127,176,105,0.4); }
.cpg-debuff { color: var(--red); border-color: rgba(217,140,115,0.4); }
.cp-gain.hold { opacity: 0.72; border-left-color: var(--line); }
.cp-gain.hold .cpg-desc { margin-bottom: 0; }

/* ============ MAP ============ */
.map-screen, .panel { max-width: 640px; margin: 8px auto; padding-bottom: 40px; animation: rise 0.4s ease; }
.panel-h { font-family: var(--serif); font-weight: 500; font-size: 24px; margin: 0 0 4px; }
.panel-sub { font-size: 13px; color: var(--ink-dim); margin-bottom: 16px; }
/* ============ THE GILDED CHART — illuminated travel map ============ */
/* ===== THE GILDED CHART — illuminated travel map ===== */
.map{display:block;width:100%;height:auto;aspect-ratio:104/72;overflow:visible;margin-bottom:16px;filter:drop-shadow(0 6px 20px rgba(0,0,0,0.38))}

/* ---- ROUTES: gilt caravan-tracks (danger lives in the texture) ---- */
/* renderer builds each edge as <g class="mapedge [reach|sanct|unknown] [d0-3]">
   with a quadratic <path class="track"> under a <path class="core"> centerline */
.mapedge{fill:none}
.mapedge .track{stroke:var(--gold-dim);stroke-width:1.4;stroke-linecap:round;opacity:.5}
.mapedge .core{stroke:var(--gold);stroke-width:.5;stroke-linecap:round;opacity:.55}
/* danger progressively breaks the centerline into a ragged track */
.mapedge.d1 .core{stroke-dasharray:3 1.4}
.mapedge.d2 .core{stroke-dasharray:2 1.8}
.mapedge.d3 .core{stroke-dasharray:1 2.2}
/* reachable = fully lit gold + a caravan-light running toward the node */
.mapedge.reach .track{opacity:.9}
.mapedge.reach .core{opacity:1;stroke-width:.6;stroke-dasharray:1.4 12;animation:mp-caravan 2.2s linear infinite}
/* the road that leans sanctuary-ward glows a touch brighter */
.mapedge.sanct .core{opacity:1;filter:drop-shadow(0 0 .6px var(--gold))}
/* road to an unknown place fades to a faint dotted trail */
.mapedge.unknown .track{opacity:0}
.mapedge.unknown .core{stroke:var(--line);stroke-width:.4;stroke-dasharray:.6 1.4;opacity:.75;animation:none}

/* ---- NODE ROUNDELS: parent <g class="mapnode STATE dN"> is translate(x,y) ---- */
.mapnode{color:var(--gold-dim);cursor:pointer}
.mapnode .glow{opacity:0;transition:opacity .4s}
.mapnode .ring{transition:stroke .3s}
.mapnode .device{transition:opacity .3s}
.mapnode .halo{opacity:.85}
.mapnode .accent{opacity:0;transition:opacity .3s}
.mapnode .dpips path{opacity:0;transition:opacity .3s}

/* danger pips reveal cumulatively (0-3 red diamonds arced beneath) */
.mapnode.d1 .dp1{opacity:1}
.mapnode.d2 .dp1,.mapnode.d2 .dp2{opacity:1}
.mapnode.d3 .dp1,.mapnode.d3 .dp2,.mapnode.d3 .dp3{opacity:1}

/* CURRENT — fully illuminated, gold ring, breathing halo */
.mapnode.cur{color:var(--gold)}
.mapnode.cur .glow{opacity:1;animation:mp-breathe 3.6s ease-in-out infinite}

/* REACHABLE — green ring + thin gold inner accent, its road lit */
.mapnode.reach{color:var(--green)}
.mapnode.reach .glow{opacity:.35}
.mapnode.reach .accent{opacity:.7}

/* THE WAYFARER — the faceless traveler silhouette you move across the world map */
.wayfarer-fig{pointer-events:none}
.wf-shadow{fill:#000;opacity:.4}
/* WOOL, not a hole. The figure used to be near-black inside a bright outline, which reads as a gap
   in the scene rather than a man in it. These are cloth colours, dark enough to sit on a night
   street and light enough to be a material, with the lit side coming off the same warm key the
   scenes use. The one true void left is inside the hood, and that one is deliberate. */
.wf-body{fill:url(#wfg-cloth);stroke:#5d4529;stroke-width:.07;stroke-opacity:.5;stroke-linejoin:round}
.wf-cloak{fill:#2c2218;stroke:#8d7346;stroke-width:.1;stroke-opacity:.5;stroke-linejoin:round}
.wf-hood-void{fill:#15100e;stroke:none;opacity:.92}
.wf-rim{stroke:#ffe0a4;stroke-width:.1;stroke-opacity:.7;fill:none;stroke-linecap:round}
.wf-brow{stroke:#d9b478;stroke-width:.09;stroke-opacity:.55;fill:none;stroke-linecap:round}
.wf-yoke{fill:url(#wfg-cloth);stroke:#a88752;stroke-width:.07;stroke-opacity:.4;stroke-linejoin:round}
.wf-sash{fill:url(#wfg-sash);stroke:#d69a5c;stroke-width:.06;stroke-opacity:.5}
.wf-sash-tail{fill:#7d4325;stroke:none}
.wf-cuff{fill:#3a2c1f;stroke:#c8a468;stroke-width:.06;stroke-opacity:.5}
/* ---- the road on him: wraps, dust, fray, patches, the layer under the layer ------------------
   It read flat and cartoony because every part was one smooth filled shape. These are the marks a
   road leaves: cloth wound round the shins, a hem gone pale with dust and frayed at the edge, a
   patch sewn over a tear, an under-sleeve at the wrist, and a shadow down the side away from the
   light. Chosen to survive being drawn 31 px tall, so tone and edge rather than fine noise. */
.wf-wrap{fill:url(#wfg-wrap);stroke:#a88752;stroke-width:.06;stroke-opacity:.45;stroke-linejoin:round}
.wf-wrap-line{stroke:#c9a86a;stroke-width:.055;stroke-opacity:.5;fill:none;stroke-linecap:round}
.wf-wrap-tail{fill:#332619;stroke:#a88752;stroke-width:.055;stroke-opacity:.4;stroke-linejoin:round}
.wf-shade{fill:#0f0b07;opacity:.26}
.wf-strap-x{stroke:#6b5233;stroke-width:.11;stroke-opacity:.9;fill:none;stroke-linecap:round}
.wf-pouch-s{fill:#4a3722;stroke:#a88752;stroke-width:.05;stroke-opacity:.5;stroke-linejoin:round}
.wf-puttee{stroke:#c9a86a;stroke-width:.06;stroke-opacity:.45;fill:none;stroke-linecap:round}
.wf-strapf{stroke:#8d7346;stroke-width:.07;stroke-opacity:.8;fill:none;stroke-linecap:round}
.wf-dust{fill:#6a5942;opacity:.3}
.wf-fray{stroke:#8d7346;stroke-width:.05;stroke-opacity:.6;fill:none;stroke-linecap:round}
.wf-patch{fill:#3d2e1e;stroke:#a88752;stroke-width:.045;stroke-opacity:.4;stroke-dasharray:.07 .07}
.wf-sleeve-l{stroke:#d9b478;stroke-width:.055;stroke-opacity:.35;fill:none;stroke-linecap:round}
.wf-underlayer{fill:#7a6242;stroke:none}
/* ---- the lantern, hung ---- */
.wf-lamp-g{transform-box:fill-box;transform-origin:50% 0%}
.wf-lamp-cord{stroke:#8d7346;stroke-width:.05;stroke-opacity:.9;fill:none}
.wf-lamp-cap,.wf-lamp-base{fill:#8a7040;stroke:none}
.wf-lamp-body{fill:#241c12;stroke:#c9a86a;stroke-width:.05;stroke-opacity:.75}
.stage-wayfarer.striding .wf-lamp-g{animation:wf-lampswing .84s ease-in-out infinite}
@keyframes wf-lampswing{0%,100%{transform:rotate(-5deg)}50%{transform:rotate(5deg)}}
@media (prefers-reduced-motion:reduce){.stage-wayfarer.striding .wf-lamp-g{animation:none}}
.wf-fold{stroke:#ffd98f;stroke-width:.09;stroke-opacity:.22;fill:none;stroke-linecap:round}
.wf-staff{stroke:#c9a86a;stroke-width:.2;stroke-linecap:round;stroke-opacity:.85;fill:none}
.wf-staff-grip{stroke:#6d5836;stroke-width:.26;stroke-linecap:round;stroke-opacity:.9}
.wf-lantern{fill:#ffe6ad;animation:wf-lamp 2.6s ease-in-out infinite}
.wf-inner{transform-box:fill-box;transform-origin:50% 100%;animation:wf-sway 3.2s ease-in-out infinite}
.map.walking .wf-inner{animation:fig-stride .46s ease-in-out infinite}
@keyframes wf-sway{0%,100%{transform:rotate(-1.1deg)}50%{transform:rotate(1.1deg)}}
@keyframes wf-walk{0%,100%{transform:translateY(.02px) rotate(-2.4deg)}50%{transform:translateY(-.16px) rotate(2.4deg)}}
@keyframes wf-lamp{0%,100%{opacity:.75}50%{opacity:1}}
@media (prefers-reduced-motion:reduce){.wf-inner,.wf-lantern{animation:none}}
/* combat stage: you vs the foe in a scene, above the tactical bars/moves */
.combat-stage{position:relative;width:100%;height:clamp(168px,27vh,246px);border-radius:14px;overflow:hidden;border:1px solid var(--gold-dim);box-shadow:0 10px 28px rgba(0,0,0,.5);margin-bottom:12px;background:#140b0a}
.combat-stage .scene-art{position:absolute;inset:0;width:100%;height:100%}
/* the arena is the location you were walking, dimmed and vignetted so the fighters read against it */
.combat-scene{position:absolute;inset:0;z-index:0;overflow:hidden}
.combat-dim{position:absolute;inset:0;pointer-events:none;background:linear-gradient(to bottom,rgba(8,5,12,.66),rgba(10,6,10,.42) 55%,rgba(22,9,7,.56))}
.combat-fever{position:absolute;inset:0;pointer-events:none;mix-blend-mode:screen;background:radial-gradient(ellipse at 50% 62%,rgba(126,150,74,.22),rgba(96,120,54,.06) 58%,transparent 78%)}
.combat-ground{position:absolute;left:0;right:0;bottom:0;height:46%;pointer-events:none;background:radial-gradient(ellipse at 50% 112%,rgba(255,180,106,.22),transparent 66%)}
.combat-vig{position:absolute;inset:0;pointer-events:none;box-shadow:inset 0 0 60px 24px rgba(0,0,0,.62)}
/* a heavy blow kicks dust off the fighting ground -- blows land in a place, not on a stage */
.combat-dust{position:absolute;left:22%;right:22%;bottom:0;height:42%;pointer-events:none;opacity:0;transform-origin:50% 100%;background:radial-gradient(ellipse at 50% 100%,rgba(200,160,110,.5),transparent 68%)}
.combat.clash.heavy .combat-dust{animation:dust-kick .55s ease-out}
@keyframes dust-kick{0%{opacity:0;transform:translateY(5px) scale(.55)}28%{opacity:.55}100%{opacity:0;transform:translateY(-10px) scale(1.35)}}
.cs-you{position:absolute;left:11%;bottom:5%;height:64%;width:auto;z-index:2;filter:drop-shadow(0 3px 5px rgba(0,0,0,.6))}
/* THE STRIKE ZONES SHARE THE FOE'S OWN BOX, in one rule, so the two cannot drift apart: same right,
   same bottom, same height. The width comes from the same viewBox ratio -- the svg takes it
   intrinsically, the div is handed it inline -- so a zone laid out in viewBox units converts to a
   percentage of this box EXACTLY. Everything inside is then a percentage and nothing is in px. */
.cs-foe,.combat-stage .strike-zones{position:absolute;right:9%;bottom:5%;height:70%}
.cs-foe{width:auto;z-index:2;transform:scaleX(-1);filter:drop-shadow(0 3px 5px rgba(0,0,0,.6))}
.cs-foe-name{position:absolute;top:11px;right:15px;z-index:3;font-family:var(--serif);font-size:17px;color:#f2c9b6;text-shadow:0 2px 8px rgba(0,0,0,.85)}
/* IMMERSIVE COMBAT: a bigger stage, a fighting-game HUD, hits + reactions play out visually (no narration text) */
.combat-stage.big{height:clamp(230px,40vh,380px)}
.cs-hud{position:absolute;top:13px;z-index:3;width:40%;max-width:240px;display:flex;flex-direction:column;gap:4px}
.cs-hud.you{left:15px;align-items:flex-start}
.cs-hud.foe{right:15px;align-items:flex-end}
.ch-name{font-family:var(--serif);font-size:15.5px;color:#f2e6cc;text-shadow:0 2px 8px rgba(0,0,0,.9)}
.cs-hud.foe .ch-name{color:#f4cbb8}
.ch-arm{font-size:11px;color:var(--gold-dim);letter-spacing:.03em}
.ch-bar{width:100%;height:9px;border-radius:5px;background:rgba(0,0,0,.5);overflow:hidden;border:1px solid rgba(0,0,0,.55);box-shadow:0 1px 3px rgba(0,0,0,.5)}
.ch-bar>i{display:block;height:100%;border-radius:5px;transition:width .45s cubic-bezier(.4,.05,.3,1)}
.ch-bar.you>i{background:linear-gradient(90deg,#6b8f52,#93c46e)}
.ch-bar.foe>i{background:linear-gradient(90deg,#8a3b2f,#dc6450)}
.ch-bar.hit{animation:bar-hit .45s ease}
.ch-bar.hurt{animation:bar-shake .45s ease}
.cs-stam{position:absolute;left:15px;right:15px;bottom:9px;height:4px;border-radius:3px;background:rgba(0,0,0,.45);overflow:hidden;z-index:3}
.cs-stam>i{display:block;height:100%;background:linear-gradient(90deg,#b78a3c,#e0c064);transition:width .3s ease}
.cs-stam.low>i{background:linear-gradient(90deg,#b8603a,#d98c73)}
.cdmg.on-you,.cdmg.on-foe{position:absolute;z-index:4;font-family:var(--serif);font-weight:600;font-size:25px;text-shadow:0 2px 6px rgba(0,0,0,.9);animation:dmg-rise .85s ease forwards;pointer-events:none}
.cdmg.on-you{left:13%;right:auto;bottom:56%;color:#ff9f8a}
.cdmg.on-foe{right:11%;left:auto;bottom:60%;color:#ffcf9f}
@keyframes dmg-rise{0%{opacity:0;transform:translateY(6px) scale(.8)}20%{opacity:1;transform:translateY(0) scale(1.12)}100%{opacity:0;transform:translateY(-24px) scale(1)}}
.combat.clash .cs-you{animation:you-lunge .3s ease}
@keyframes you-lunge{0%,100%{transform:translateX(0)}50%{transform:translateX(7px)}}
.combat.clash.heavy .combat-stage{animation:stage-shake .32s ease}
@keyframes stage-shake{0%,100%{transform:translateX(0)}25%{transform:translateX(-5px)}75%{transform:translateX(5px)}}
.combat.telegraph .cs-foe{animation:foe-rear 1s ease-in-out infinite}
/* the wind-up you REACT to: the foe coils BACK and rises before the heavy blow, so the tell is in the body, not just the cue */
@keyframes foe-rear{0%,100%{transform:scaleX(-1) translateX(0) scale(1) rotate(0)}44%{transform:scaleX(-1) translateX(-4px) scale(1.08) rotate(4deg)}70%{transform:scaleX(-1) translateX(-2px) scale(1.05) rotate(2deg)}}
.combat.telegraph .combat-stage::after{content:"";position:absolute;inset:0;z-index:3;pointer-events:none;box-shadow:inset 0 0 46px 7px rgba(200,50,40,.5);animation:tele-pulse 1s ease-in-out infinite}
@keyframes tele-pulse{0%,100%{opacity:.38}50%{opacity:.9}}
.tele-cue{position:absolute;left:50%;top:42%;transform:translateX(-50%);z-index:4;padding:5px 15px;border-radius:999px;background:rgba(150,30,24,.86);color:#ffdccb;font-size:13px;font-family:var(--serif);letter-spacing:.03em;box-shadow:0 2px 12px rgba(0,0,0,.6);animation:tele-pulse 1s ease-in-out infinite;white-space:nowrap}
.combat.telegraph [data-c=brace],.combat.telegraph [data-c=shield]{border-color:var(--red);animation:react-pulse 1s ease-in-out infinite}
@keyframes react-pulse{0%,100%{box-shadow:0 0 0 2px rgba(217,140,115,.2)}50%{box-shadow:0 0 0 3px rgba(217,140,115,.55)}}
@media (prefers-reduced-motion:reduce){.cdmg.on-you,.cdmg.on-foe,.combat.clash .cs-you,.combat.clash.heavy .combat-stage,.combat.telegraph .cs-foe,.combat.telegraph .combat-stage::after,.tele-cue,.combat.telegraph [data-c=brace],.combat.telegraph [data-c=shield],.combat.clash.heavy .combat-dust{animation:none}}
.foe-shadow{fill:#000;opacity:.42}
.foe-body{fill:#170a08;stroke:#e8836a;stroke-width:.3;stroke-opacity:.92;stroke-linejoin:round}
.foe-weapon{stroke:#e8836a;stroke-width:.34;stroke-linecap:round;stroke-opacity:.85}
.foe-inner{transform-box:fill-box;transform-origin:50% 100%;animation:foe-loom 2.8s ease-in-out infinite}
.combat.clash .cs-foe{animation:foe-lunge .3s ease}
/* struck (and NOT landing a counter this round): a sharp recoil back, so a clean hit reads on the body */
.combat.clash.foe-hit .cs-foe{animation:foe-flinch .3s ease}
/* worn down (<=34% HP): a persistent forward slump, so a failing foe LOOKS like one */
.combat.foe-low .cs-foe .foe-inner{animation:foe-sag 3.2s ease-in-out infinite}
@keyframes foe-loom{0%,100%{transform:rotate(1deg)}50%{transform:rotate(-1.4deg)}}
@keyframes foe-lunge{0%,100%{transform:scaleX(-1) translateX(0)}50%{transform:scaleX(-1) translateX(4px)}}
@keyframes foe-flinch{0%,100%{transform:scaleX(-1) translateX(0) rotate(0)}40%{transform:scaleX(-1) translateX(-6px) rotate(-5deg)}}
@keyframes foe-sag{0%,100%{transform:rotate(6deg) translateY(.3px)}50%{transform:rotate(4deg) translateY(.15px)}}
/* at the brink: down on the knee (yields) or swaying, past sense (delirious) */
.combat.brink .cs-foe .foe-inner{animation:foe-kneel .9s ease forwards}
.combat.brink-delirious .cs-foe .foe-inner{animation:foe-delir 1.5s ease-in-out infinite}
@keyframes foe-kneel{to{transform:rotate(15deg) translateY(1.6px) scale(.9)}}
@keyframes foe-delir{0%,100%{transform:rotate(9deg) translateX(0)}30%{transform:rotate(15deg) translateX(-1.2px)}62%{transform:rotate(5deg) translateX(1.6px)}}
@media (prefers-reduced-motion:reduce){.foe-inner,.combat.foe-low .cs-foe .foe-inner,.combat.clash.foe-hit .cs-foe,.combat.brink .cs-foe .foe-inner,.combat.brink-delirious .cs-foe .foe-inner{animation:none}}
/* the brink choice replaces the control row: finish him, or the disposition-appropriate mercy */
.brink-panel{padding:12px 14px 4px;text-align:center}
.brink-line{font-family:var(--serif);color:#e7cab7;font-size:15px;line-height:1.45;margin:0 auto 10px;max-width:640px}
.brink-info{font-family:var(--serif);color:#cbb98a;font-style:italic;font-size:13.5px;line-height:1.4;margin:-2px auto 12px;max-width:600px}
.brink-choices{display:grid;grid-template-columns:repeat(auto-fit,minmax(158px,1fr));gap:10px;max-width:680px;margin:0 auto}
.brink-choices .btn{min-width:0;display:inline-flex;flex-direction:column;gap:2px;align-items:center;padding:8px 10px;text-align:center}
.brink-choices .mv-meta{font-size:12px;opacity:.7}
.brink-finish{border-color:var(--red);color:#f0c3b6}
.brink-spare{border-color:var(--gold-dim)}

/* MOVEMENT / TRANSITIONS: arriving somewhere is MOVING into it, not a cut */
.scene-stage .scene-art{animation:stage-reveal .8s ease both}
@keyframes stage-reveal{from{opacity:0;transform:scale(1.06)}to{opacity:1;transform:scale(1)}}
.stage-wayfarer,.cs-you,.cs-foe{animation:fig-fade .7s .12s ease both}
@keyframes fig-fade{from{opacity:0}to{opacity:1}}
.cs-foe{animation-delay:.28s}
/* the wayfarer WALKS INTO the scene when you arrive from travel */
.hub-stage.arriving .stage-wayfarer{animation:wf-arrive 1.15s cubic-bezier(.4,.05,.3,1) both}
@keyframes wf-arrive{0%{transform:translateX(-320%);opacity:.15}35%{opacity:1}100%{transform:translateX(-50%);opacity:1}}
/* every screen settles in, so a screen change reads as motion */
.scene-view,.map-screen,.combat{animation:rise .42s ease both}
@media (prefers-reduced-motion:reduce){.scene-stage .scene-art,.stage-wayfarer,.cs-you,.cs-foe,.hub-stage.arriving .stage-wayfarer,.scene-view,.map-screen,.combat{animation:none}}

/* GEAR ON THE WAYFARER — your belongings, worn and slung on the figure (inside .wf-inner, so they sway with you) */
.wf-sack path{fill:#4a3826;stroke:#6e553a;stroke-width:.08}
.wf-tie{stroke:#8a6f4a;stroke-width:.1;fill:none;stroke-linecap:round}
.wf-armor{fill:#4b4335;stroke:#b09a70;stroke-width:.08;opacity:.95}
.wf-mail circle{fill:none;stroke:#aeb6c0;stroke-width:.06;opacity:.85}
.wf-helm{fill:#8b93a0;stroke:#cfd5de;stroke-width:.07}
.wf-helm-n{stroke:#6d7480;stroke-width:.09;stroke-linecap:round}
.wf-shield-o{fill:#4a3a2a;stroke:#caa24e;stroke-width:.09}
.wf-shield-b{fill:#9a7d4e}
.wf-skin{fill:#5a4a30;stroke:#7d6742;stroke-width:.06}
.wf-strap{stroke:#6e553a;stroke-width:.08;fill:none;stroke-linecap:round}
.wf-beads circle{fill:#caa24e}
.wf-mat rect{fill:#6a4a54;stroke:#9f7b88;stroke-width:.06}
.wf-purse{fill:#4a3826;stroke:#7d6742;stroke-width:.06}
.wf-bow{stroke:#7a5a34;stroke-width:.13;fill:none;stroke-linecap:round}
.wf-bow-s{stroke:#caa24e;stroke-width:.05;fill:none;opacity:.55}
.wf-blade{stroke:#c2c8d2;stroke-width:.11;fill:none;stroke-linecap:round}
.wf-hilt{stroke:#7a5a34;stroke-width:.14;fill:none;stroke-linecap:round}

/* THE MADRASA — a real, committed school: enrol, study the staged curriculum, sit the exams, earn the ijāza */
.madrasa-real .term-note{color:var(--gold-dim);font-style:italic;margin-top:-2px}
.madrasa-real .ring-note{color:#9aa88f;font-size:13px;margin-top:-4px;padding-left:11px;border-left:2px solid rgba(154,168,143,0.35)}
.madrasa-curric{display:flex;flex-direction:column;gap:6px;margin:14px 0 18px;padding:13px 15px;border:1px solid var(--gold-dim);border-radius:11px;background:rgba(30,24,16,.5)}
.curric-row{display:flex;align-items:center;gap:12px}
.cr-n{width:23px;height:23px;flex:0 0 auto;display:grid;place-items:center;border-radius:50%;border:1px solid var(--gold-dim);color:var(--gold);font-size:12px;font-variant-numeric:tabular-nums}
.cr-name{font-family:var(--serif);font-size:15px;color:#e9dcc0}
.madrasa-status{margin:8px 0 18px;padding:15px 17px;border:1px solid var(--gold-dim);border-radius:12px;background:radial-gradient(120% 120% at 50% 0,rgba(62,48,26,.55),rgba(20,15,9,.5))}
.ms-stage{font-family:var(--serif);font-size:17px;color:#f2e6c8}
.ms-stage b{color:var(--gold)}
.ms-intro{font-size:13.5px;margin:4px 0 11px;font-style:italic}
.ms-prog{display:flex;align-items:center;gap:10px}
.ms-prog-track{flex:1;height:9px;border-radius:5px;background:rgba(0,0,0,.4);overflow:hidden;border:1px solid var(--gold-dim)}
.ms-prog-track i{display:block;height:100%;background:linear-gradient(90deg,#b98a3e,var(--gold));border-radius:5px;transition:width .5s ease}
.ms-prog-n{font-size:12px;color:var(--gold-dim);white-space:nowrap}
/* THE CROWD — ambient, non-interactive folk set back in the scene, giving a place its life and density */
.crowd-layer{position:absolute;inset:0;z-index:0;pointer-events:none}
.crowd-fig{position:absolute;transform:translateX(-50%);width:auto;pointer-events:none}
.crowd-fig .npc-svg{height:100%}
.crowd-fig.flip .npc-svg{transform:scaleX(-1)}
/* --- a living place: some of the crowd WALK across it, some are at WORK, and animals wander --- */
/* a walker rides a full-width TRACK that TRANSLATES across the stage -- GPU-composited, no layout
   per frame. (It used to animate `left`, which recomputes layout every tick and shakes the scene.)
   The figure sits at the track's left edge; translating the track carries it across. */
.crowd-track,.critter-track{position:absolute;inset:0;pointer-events:none;will-change:transform}
.crowd-track{animation:crowd-cross var(--wd,26s) linear infinite;animation-delay:var(--wo,0s)}
.crowd-track .crowd-fig{left:0}
@keyframes crowd-cross{0%{transform:translateX(-8%)}100%{transform:translateX(108%)}}
/* a robed figure has no legs to show, so a walk is the BODY BOBBING -- rising on each step -- with a
   slight forward lean and a whisper of sway, two bobs to a stride. A metronome rock reads as tipping. */
.crowd-track .nf-inner{animation:fig-stride .46s ease-in-out infinite}
@keyframes fig-stride{
  0%,100%{transform:translateY(.05px) rotate(-.7deg)}
  25%{transform:translateY(-.28px) rotate(.1deg)}
  50%{transform:translateY(.05px) rotate(.7deg)}
  75%{transform:translateY(-.28px) rotate(-.1deg)}
}
/* at work: a repeating stoop-and-rise over the hands, reading as labour rather than standing */
/* at work: a stoop over the hands and a rise, from the waist -- reads as tending something, not tipping */
.crowd-work .nf-inner{animation:crowd-work 2.1s ease-in-out infinite;animation-delay:var(--wo,0s);transform-origin:50% 96%}
@keyframes crowd-work{0%,100%{transform:rotate(0deg) translateY(0)}38%{transform:rotate(7deg) translateY(.12px)}52%{transform:rotate(6deg) translateY(.14px)}72%{transform:rotate(-1.5deg) translateY(-.04px)}}
.critter-fig{position:absolute;transform:translateX(-50%);pointer-events:none;filter:drop-shadow(0 2px 3px rgba(0,0,0,.4))}
.critter{display:block;height:100%;width:auto}
.critter .cr-body,.critter .cr-head{fill:#3a444e}
.critter-fig.flip .critter{transform:scaleX(-1)}
/* a rim of pale light along the top of the silhouette, so it reads as a body and not a flat hole */
.critter .cr-body,.critter .cr-head{stroke:#8798a5;stroke-width:.24;stroke-opacity:.85;stroke-linejoin:round}
.critter{filter:drop-shadow(0 0 1px rgba(0,0,0,.6))}
/* an animal crossing the ground on a translating track, its body bobbing with the stride */
.critter-track{animation:critter-cross var(--wd,40s) linear infinite;animation-delay:var(--wo,0s)}
.critter-track .critter-fig{left:0}
.critter-track .cr-fig{animation:critter-trot .32s ease-in-out infinite;transform-box:fill-box;transform-origin:50% 100%}
@keyframes critter-cross{0%{transform:translateX(-6%)}100%{transform:translateX(106%)}}
/* a trot: the body lifts and pitches nose-down then nose-up, over and over -- the gait of a four-legged thing */
@keyframes critter-trot{0%,100%{transform:translateY(0) rotate(-1.2deg)}50%{transform:translateY(-0.7px) rotate(1.2deg)}}
/* grazing: the head dips to the ground and lifts, over and over */
.critter-graze .cr-head{animation:critter-graze 3.4s ease-in-out infinite;animation-delay:var(--wo,0s)}
@keyframes critter-graze{0%,100%{transform:rotate(0deg)}40%{transform:rotate(34deg) translateY(1px)}70%{transform:rotate(30deg)}}
/* pecking: a quick sharp dip, a pause, again */
.critter-peck .cr-head{animation:critter-peck 2.2s ease-in-out infinite;animation-delay:var(--wo,0s)}
@keyframes critter-peck{0%,55%,100%{transform:rotate(0)}62%{transform:rotate(46deg) translateY(1.4px)}70%{transform:rotate(0)}78%{transform:rotate(42deg) translateY(1.2px)}86%{transform:rotate(0)}}
/* lingering: a resting animal that shifts now and then, and flicks its head */
.critter-linger .cr-head{animation:critter-linger 5s ease-in-out infinite;animation-delay:var(--wo,0s)}
@keyframes critter-linger{0%,60%,100%{transform:rotate(0)}68%{transform:rotate(-12deg)}80%{transform:rotate(6deg)}}
@media (prefers-reduced-motion:reduce){
  .crowd-track,.crowd-track .nf-inner,.crowd-work .nf-inner,.critter-track,.critter-track .cr-fig,
  .critter-graze .cr-head,.critter-peck .cr-head,.critter-linger .cr-head{animation:none}
  .crowd-track,.critter-track{transform:translateX(45%)}
}
/* THE FOLK — shrouded, aniconic figures standing in a location; walk up to one to speak with them */
.npc-svg{width:auto;overflow:visible}
.npc-svg .nf-inner{transform-box:fill-box;transform-origin:50% 100%;animation:wf-sway 3.6s ease-in-out infinite}
.npc-layer{position:absolute;inset:0;z-index:2;pointer-events:none}
.npc-fig{position:absolute;transform:translateX(-50%);width:auto;background:none;border:0;padding:0;margin:0;cursor:pointer;pointer-events:auto;filter:drop-shadow(0 4px 6px rgba(0,0,0,.45)) drop-shadow(0 0 4px rgba(240,214,150,.22));transition:transform .18s ease,filter .18s ease;animation:fig-fade .7s .2s ease both}
.npc-fig .npc-svg{height:100%}
.npc-fig.flip .npc-svg{transform:scaleX(-1)}
.npc-fig:hover,.npc-fig:focus-visible{transform:translateX(-50%) translateY(-3px);filter:drop-shadow(0 7px 10px rgba(0,0,0,.6)) drop-shadow(0 0 5px rgba(217,184,102,.5));outline:none}
.npc-fig:nth-child(2) .nf-inner{animation-duration:4.1s;animation-delay:-1.2s}
.npc-fig:nth-child(3) .nf-inner{animation-duration:3.3s;animation-delay:-.6s}
.npc-name{position:absolute;top:100%;left:50%;transform:translateX(-50%) translateY(3px);white-space:nowrap;font-family:var(--serif);font-size:12.5px;color:#efe0c0;text-shadow:0 1px 4px rgba(0,0,0,.9);background:rgba(12,8,4,.74);padding:1px 8px;border-radius:9px;opacity:0;transition:opacity .18s ease;pointer-events:none}
.npc-fig:hover .npc-name,.npc-fig:focus-visible .npc-name,.npc-fig.npc-seek .npc-name{opacity:1}
.npc-seek::before{content:"";position:absolute;top:-7px;left:50%;transform:translateX(-50%);width:7px;height:7px;border-radius:50%;background:var(--gold);box-shadow:0 0 7px 2px rgba(217,184,102,.75);animation:seek-pulse 1.8s ease-in-out infinite}
@keyframes seek-pulse{0%,100%{opacity:.55;transform:translateX(-50%) scale(.85)}50%{opacity:1;transform:translateX(-50%) scale(1.15)}}
/* walking up to someone: the wayfarer strides across the stage, then the meeting opens */
.stage-wayfarer.face-left{transform:translateX(-50%) scaleX(-1)}
.stage-wayfarer.striding .wf-inner{animation:wf-walk .42s ease-in-out infinite}
/* the person you speak with stands across from you in the encounter scene */
.enc-npc-wrap{position:absolute;right:11%;bottom:3%;height:52%;z-index:2;filter:drop-shadow(0 4px 7px rgba(0,0,0,.55));animation:fig-fade .7s .24s ease both}
.enc-npc-wrap .npc-svg{height:100%;transform:scaleX(-1)}
.enc-stage.has-person .stage-wayfarer{left:32%}
/* the belongings paperdoll — you, wearing everything you carry */
.inv-doll{position:relative;height:210px;margin:4px 0 14px;border-radius:12px;overflow:hidden;background:radial-gradient(120% 100% at 50% 12%,#2a2114,#140d07 76%);border:1px solid var(--gold-dim);box-shadow:inset 0 0 0 1px rgba(217,184,102,.08)}
.inv-doll .stage-wayfarer{height:76%;bottom:9%}
.inv-doll-cap{position:absolute;bottom:8px;left:0;right:0;text-align:center;font-family:var(--serif);font-style:italic;font-size:13px;color:var(--gold-dim);z-index:3}
@media (prefers-reduced-motion:reduce){.npc-svg .nf-inner,.npc-fig,.npc-seek::before,.enc-npc-wrap,.stage-wayfarer.striding .wf-inner{animation:none}}

/* KNOWN — painted but muted, glow off */
.mapnode.known{color:var(--gold-dim)}
.mapnode.known .device{opacity:.72}

/* UNKNOWN — an unfinished patch of the leaf: ruled-but-unpainted, faint */
.mapnode.unknown{color:var(--ink-faint)}
.mapnode.unknown .device{opacity:.4}
.mapnode.unknown .ring{stroke-dasharray:1 1;opacity:.55}
.mapnode.unknown .glow{opacity:0}
.mapnode.unknown .halo{opacity:.3}

/* LABELS — serif on a hair-thin gold rubric underline */
.mapnode text{font-family:var(--serif);font-size:2.4px;fill:var(--ink);text-anchor:middle;letter-spacing:.02px}
.mapnode.unknown text{fill:var(--ink-faint);font-style:italic}
.mapnode .rubric{stroke:var(--gold-dim);stroke-width:.25;opacity:.7}
.mapnode.unknown .rubric{stroke:var(--line)}

/* HOVER echo — links to the matching destination card via shared highlight id */
.mapnode:hover .glow{opacity:.5}
.mapnode:hover .ring{stroke:var(--gold)}
.mapnode:hover .accent{opacity:.85}

/* ---- ORNAMENTAL COMPASS ROSETTE ---- */
.compass{opacity:.55}
.compass .rl{fill:var(--gold);opacity:.85}
.compass .rs{fill:var(--gold-dim);opacity:.7}
.compass .compass-rays{transform-box:fill-box;transform-origin:center;animation:mp-rose 60s linear infinite}

/* ---- SUBTLE, GUARDED LIFE ---- */
@keyframes mp-breathe{0%,100%{opacity:.75}50%{opacity:1}}
@keyframes mp-caravan{to{stroke-dashoffset:-13.4}}
@keyframes mp-rose{to{transform:rotate(360deg)}}
@media (prefers-reduced-motion:reduce){
  .mapnode.cur .glow,.mapedge.reach .core,.compass .compass-rays{animation:none}
  .mapnode.cur .glow{opacity:1}
}

.map-choices { display: flex; flex-direction: column; gap: 12px; margin-bottom: 14px; }
.map-card { display: grid; grid-template-columns: 1fr auto; gap: 7px 10px; }
.map-card .map-go { grid-column: 1 / -1; }
.map-ride {
  grid-column: 2; justify-self: end; display: inline-flex; align-items: baseline; gap: 8px;
  min-width: 112px; padding: 6px 11px; color: var(--gold-dim); background: transparent;
  border: 1px solid color-mix(in srgb, var(--gold-dim) 55%, var(--line)); border-radius: 2px;
  font: 600 11px/1 var(--sans); letter-spacing: .1em; text-transform: uppercase; cursor: pointer;
}
.map-ride small { color: var(--ink-faint); font: 10px/1 var(--sans); letter-spacing: .04em; text-transform: none; }
.map-ride:hover, .map-ride:focus-visible { color: var(--gold); border-color: var(--gold); }
.map-ride:disabled { opacity: .38; cursor: not-allowed; }
.map-road-read {
  margin: 0 0 14px; padding: 12px 14px; color: var(--ink-dim); background: var(--panel);
  border: 1px solid var(--line); font-family: var(--serif); font-style: italic;
}
.map-keys { margin: 0 0 12px; color: var(--ink-faint); font-size: 11px; letter-spacing: .04em; }
.map-keys kbd {
  padding: 1px 5px; color: var(--gold); border: 1px solid color-mix(in srgb, var(--gold) 45%, transparent);
  background: color-mix(in srgb, var(--gold) 10%, transparent); font: inherit;
}

/* ============ THE GILDED CHART — destination card (illuminated catalogue entry) ============ */
.map-go{
  --accent: var(--gold-dim);
  --accent-ground: var(--panel);
  --dg: var(--green);
  position: relative;
  display: flex;
  align-items: center;
  gap: 14px;
  width: 100%;
  margin: 0;
  padding: 14px 16px 14px 22px;
  text-align: left;
  font-family: var(--sans);
  color: var(--ink);
  cursor: pointer;
  border: 1px solid var(--gold-dim);
  border-radius: 3px;
  background:
    radial-gradient(120% 90% at 8% 0%, color-mix(in srgb, var(--gold) 5%, transparent), transparent 55%),
    linear-gradient(180deg, color-mix(in srgb, var(--panel2) 90%, var(--gold) 7%), var(--panel2));
  overflow: visible;
  isolation: isolate;
  transition: transform .18s ease, box-shadow .25s ease, border-color .25s ease;
}
.map-go:focus-visible{ outline: none; }

/* --- LEFT EDGE danger gauge: green(0) -> gold -> red(3), filled by magnitude --- */
.map-go__gauge{
  position: absolute; left: 0; top: 7px; bottom: 7px; width: 4px;
  display: flex; flex-direction: column-reverse; gap: 2px;
  border-radius: 0 2px 2px 0; overflow: hidden;
  box-shadow: inset -1px 0 0 color-mix(in srgb, var(--bg) 70%, transparent);
}
.map-go__gauge i{ flex: 1; background: var(--line); transition: background .25s ease; }
.map-go[data-danger="0"]{ --dg: var(--green); }
.map-go[data-danger="1"]{ --dg: var(--green); }
.map-go[data-danger="2"]{ --dg: var(--gold);  }
.map-go[data-danger="3"]{ --dg: var(--red);   }
.map-go[data-danger="0"] .map-go__gauge i:nth-child(1){ background: color-mix(in srgb, var(--green) 55%, var(--line)); }
.map-go[data-danger="1"] .map-go__gauge i:nth-child(1),
.map-go[data-danger="2"] .map-go__gauge i:nth-child(1),
.map-go[data-danger="2"] .map-go__gauge i:nth-child(2),
.map-go[data-danger="3"] .map-go__gauge i{ background: var(--dg); }
/* a hard road looks dangerous — weight the whole card at max danger */
.map-go[data-danger="3"]{ border-color: color-mix(in srgb, var(--red) 42%, var(--gold-dim)); }
.map-go[data-danger="3"] .map-go__gauge{ box-shadow: inset -1px 0 0 color-mix(in srgb,var(--bg) 70%,transparent), 0 0 7px color-mix(in srgb, var(--red) 55%, transparent); }
.map-go[data-danger="3"]::before{
  content: ""; position: absolute; inset: 0; border-radius: 3px; z-index: 0; pointer-events: none;
  box-shadow: inset 0 0 14px -4px color-mix(in srgb, var(--red) 40%, transparent);
}

/* --- OGEE ARCH CROWN cresting the top rule (mihrab geometry) --- */
.map-go__crown{
  position: absolute; top: 0; left: 26px; width: 48px; height: 16px;
  transform: translateY(-58%); z-index: 4; pointer-events: none;
}
.map-go__crown svg{ display: block; width: 100%; height: 100%; overflow: visible; }
.map-go__crown-arch{ stroke: var(--gold-dim); stroke-width: 1; transition: stroke .25s ease, filter .25s ease; }

/* --- caravan-dot sliding along the top rule on hover --- */
.map-go__caravan{
  position: absolute; top: -1px; left: 8px; width: 5px; height: 5px; z-index: 5;
  border-radius: 50%; background: var(--gold);
  box-shadow: 0 0 5px 1px color-mix(in srgb, var(--gold) 70%, transparent);
  opacity: 0; pointer-events: none;
}

/* --- GILT CARTOUCHE holding the shared type emblem, on a jewel-ink ground --- */
.map-go__cartouche{
  position: relative; flex: 0 0 auto;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 3px;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, var(--gold-dim));
  background:
    radial-gradient(circle at 50% 36%, color-mix(in srgb, var(--accent) 26%, transparent), transparent 70%),
    var(--accent-ground);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.45);
  transition: box-shadow .25s ease, border-color .25s ease;
}
.map-go__halo{
  position: absolute; inset: -7px; z-index: 0; border-radius: 50%;
  background: radial-gradient(circle, color-mix(in srgb, var(--accent) 42%, transparent), transparent 70%);
  opacity: 0; transition: opacity .3s ease; pointer-events: none;
}
.map-go__emblem{ position: relative; z-index: 2; width: 30px; height: 30px; color: var(--accent); }
.map-go__emblem svg{ display: block; width: 100%; height: 100%; }
.map-go__fret{ position: absolute; inset: 0; z-index: 3; color: color-mix(in srgb, var(--accent) 65%, var(--gold)); pointer-events: none; }

/* --- BODY: name on a gold rubric underline, scribe's read-line, marginalia chips --- */
.map-go__body{ display: flex; flex-direction: column; gap: 5px; min-width: 0; flex: 1; position: relative; z-index: 1; }
.map-go__name{
  align-self: flex-start; max-width: 100%;
  font-family: var(--serif); font-size: 18px; line-height: 1.1; color: var(--ink);
  padding-bottom: 3px;
  border-bottom: 1px solid color-mix(in srgb, var(--gold) 42%, transparent);
}
.map-go__read{
  font-family: var(--serif); font-style: italic; font-size: 12.5px; line-height: 1.32;
  color: var(--ink-dim);
}
.map-go__margin{ display: flex; align-items: center; flex-wrap: wrap; gap: 7px; margin-top: 1px; }
.map-go__type{
  font-family: var(--sans); font-size: 10px; font-weight: 600;
  letter-spacing: .14em; text-transform: uppercase; color: var(--accent);
}
.map-go__bullet{ color: var(--gold-dim); flex: 0 0 auto; }
.map-go__bullet rect{ fill: currentColor; }
.map-go__badges{ display: inline-flex; align-items: center; gap: 7px; }
.map-go__badges:empty{ display: none; }
.map-go__margin :where(svg, span, i){ vertical-align: middle; }

/* --- HOVER / FOCUS: lift, gild the rules, bloom the initial --- */
.map-go:hover,
.map-go:focus-visible{
  transform: translateY(-1px);
  border-color: var(--gold);
  box-shadow:
    0 4px 12px -3px color-mix(in srgb, var(--bg) 82%, transparent),
    0 0 0 1px color-mix(in srgb, var(--gold) 28%, transparent);
}
.map-go:hover .map-go__crown-arch,
.map-go:focus-visible .map-go__crown-arch{
  stroke: var(--gold);
  filter: drop-shadow(0 0 2px color-mix(in srgb, var(--gold) 60%, transparent));
}
.map-go:hover .map-go__caravan,
.map-go:focus-visible .map-go__caravan{ opacity: 1; animation: mapGoCaravan 1.5s linear infinite; }
.map-go:hover .map-go__halo,
.map-go:focus-visible .map-go__halo{ opacity: .9; }
.map-go:hover .map-go__cartouche,
.map-go:focus-visible .map-go__cartouche{
  border-color: var(--accent);
  box-shadow: inset 0 1px 3px rgba(0,0,0,.45), 0 0 13px -2px color-mix(in srgb, var(--accent) 60%, transparent);
}
.map-go:hover .map-go__name,
.map-go:focus-visible .map-go__name{ border-bottom-color: var(--gold); }
@keyframes mapGoCaravan{
  from{ left: 8px; }
  to{ left: calc(100% - 10px); }
}

/* ============ PER-TYPE ACCENTS (five distinct hands, shared lexicon with the map) ============ */
.map-go.type-village{ --accent: var(--green);    --accent-ground: color-mix(in srgb, var(--green) 15%, var(--bg2)); }
.map-go.type-town   { --accent: var(--gold-dim); --accent-ground: color-mix(in srgb, var(--gold-dim) 20%, var(--bg2)); }
.map-go.type-city   { --accent: var(--gold);     --accent-ground: color-mix(in srgb, var(--blue) 34%, var(--bg2)); } /* gold-on-lapis */
.map-go.type-wild   { --accent: var(--red);      --accent-ground: color-mix(in srgb, var(--red) 15%, var(--bg2)); }
.map-go.type-sanctuary{ --accent: var(--gold);   --accent-ground: var(--bg2); } /* gold-leaf on night ink */

/* ============ THE SANCTUARY BREAKS THE GRID — the leaf's destination ============ */
.map-go.type-sanctuary{
  padding: 20px 18px 18px 24px;
  border-color: var(--gold);
  background:
    radial-gradient(140% 110% at 50% -10%, color-mix(in srgb, var(--gold) 12%, transparent), transparent 60%),
    linear-gradient(180deg, color-mix(in srgb, var(--bg2) 84%, var(--gold) 6%), var(--bg2));
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--gold) 22%, transparent);
}
.map-go.type-sanctuary .map-go__cartouche{ width: 54px; height: 54px; }
.map-go.type-sanctuary .map-go__emblem{ width: 38px; height: 38px; }
.map-go.type-sanctuary .map-go__halo{ opacity: .5; } /* always radiant */
.map-go.type-sanctuary .map-go__name{ font-size: 20px; }
.map-go.type-sanctuary .map-go__crown{
  width: 62px; height: 22px; left: 24px; transform: translateY(-60%);
}
.map-go.type-sanctuary .map-go__crown-arch{
  stroke: var(--gold); stroke-width: 1.3;
  filter: drop-shadow(0 0 1.5px color-mix(in srgb, var(--gold) 50%, transparent));
}
.map-go.type-sanctuary:hover,
.map-go.type-sanctuary:focus-visible{
  box-shadow:
    0 5px 16px -4px color-mix(in srgb, var(--bg) 80%, transparent),
    0 0 0 1px color-mix(in srgb, var(--gold) 45%, transparent),
    0 0 22px -6px color-mix(in srgb, var(--gold) 45%, transparent);
}

/* ============ reduced motion — dignified stillness ============ */
@media (prefers-reduced-motion: reduce){
  .map-go, .map-go *{ transition: none !important; }
  .map-go:hover .map-go__caravan,
  .map-go:focus-visible .map-go__caravan{ animation: none; opacity: 1; left: 50%; }
}
/* --- marginalia badge glyphs + the unread-place accent --- */
.map-go__badges .mg-badge{display:inline-flex;width:14px;height:14px}
.map-go__badges .mg-badge svg{width:100%;height:100%}
.map-go__badges .mg-badge.market{color:var(--gold)}
.map-go__badges .mg-badge.rest{color:var(--gold-dim)}
.map-go__badges .mg-badge.danger{color:var(--red)}
.map-go.type-unknown{--accent:var(--ink-faint);--accent-ground:var(--panel)}
.map-go.type-unknown .map-go__type{font-style:italic;text-transform:none;letter-spacing:.04em}


/* ============ MARKET / INVENTORY / ASK ============ */
.shop, .inv, .ask, .amb-list { display: flex; flex-direction: column; gap: 10px; margin-bottom: 18px; }
.shop-row, .inv-row { display: flex; justify-content: space-between; align-items: center; gap: 14px; background: var(--panel); border: 1px solid var(--line); border-radius: 11px; padding: 12px 16px; }
.shop-info b { font-family: var(--serif); font-size: 16px; }
.shop-kind { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); margin-left: 6px; }
.shop-info .muted { font-size: 13px; margin-top: 4px; }
.btn.small { font-size: 13px; padding: 8px 14px; white-space: nowrap; }
.worn { font-size: 11px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--gold-dim); }
.ask-row { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 11px 15px; font-size: 14px; }
.ask-row b { font-family: var(--serif); }
.amb-row { background: var(--panel); border: 1px solid var(--line); border-radius: 10px; padding: 12px 15px; }
.amb-row b { font-family: var(--serif); }
.amb-met { color: var(--green); } .amb-unmet { color: var(--ink-faint); }

/* ============ SIMPLE result (pray/reflect/work) ============ */
.simple { max-width: 600px; margin: 30px auto; text-align: center; animation: rise 0.5s ease; }
.hub-act.rest { border-left: 3px solid var(--blue); }

/* ============ COMBAT ============ */
.combat { max-width: 560px; margin: 16px auto; text-align: center; animation: rise 0.4s ease; padding-bottom: 30px; }
/* ── THE FIGHT TAKES THE WINDOW ────────────────────────────────────────────────────────────────
   It was a 560px column: measured 556x360 inside a 1440x900 screen, a postcard of a fight while the
   walkable place next door takes the whole window. You cannot read an angle, or room to move, out of
   a box that size.

   It ASKS THE DOM rather than carrying a flag, for the reason already written above the place's own
   surface rule: a flag set on entry and cleared in resetChrome looked right and was wrong, because
   renderMap and renderCombat repaint the mount without going through it.

   AND IT IS SCOPED TO THE PLAY CHROME, not to `.combat` alone. The Atelier renders a specimen as the
   app's own markup inside an iframe, and the strike-zone specimens are wrapped in a `.combat` -- so a
   bare `body:has(.combat)` matched in the BENCH too and took every specimen full-bleed inside a 590px
   frame, collapsing all four of them into a strip. `[data-view="play"]` is on the body only while a
   life is being played, which a bench never is. Caught by looking at the bench after changing the
   game, which is the second time in two days that the bench has reported a fault the game hid.

   And the deck is a FLEX ROW UNDER the picture rather than an overlay on it, which is where this
   deliberately parts company with the place. The place lays one or two lines of prose over the foot;
   a fight's deck is a log, a Guard, a row of moves and the stances, and the stage's own rails
   (`.cs-stam`, `.charge-meter`, `.charge-hint`) are pinned to the stage's bottom edge. Floating the
   deck over them would need a measured deck height feeding a `calc`, and a wrong measurement there
   puts the controls somewhere the player cannot reach -- the same failure the map already had. A
   flex column cannot do that: the stage takes everything that is left and the deck is always whole. */
body[data-view="play"]:has(.combat) { overflow: hidden; padding-bottom: 0; }
body[data-view="play"]:has(.combat) .game { max-width: none; padding: 0; }
body[data-view="play"]:has(.combat) .combat {
  /* PINNED TO THE WINDOW, not sized as though it started at the top of it. Padding below a 100dvh box
     ADDS to it (the page came out 965 tall in a 900 window), and subtracting only the rail is still
     wrong, because the fight begins 65px down under the fixed topbar and so ran 65px past the rail --
     five controls and two brink choices sat UNDER it, fully on screen and unreachable. Pinning top
     and bottom is the only version with nothing to keep in step. */
  position: fixed; left: 0; right: 0; top: 0; bottom: var(--rail-h, 0px);
  max-width: none; margin: 0; padding: 0; height: auto; z-index: 2;
  display: flex; flex-direction: column;
}
/* and the fight's own HUD drops clear of the topbar cluster, which floats over the top-right corner
   in play chrome and is exactly where the foe's name and health bar want to be */
body[data-view="play"]:has(.combat) .cs-hud { top: 74px; }
/* the picture takes everything the deck does not need */
body[data-view="play"]:has(.combat) .combat-stage.big {
  flex: 1 1 auto; height: auto; min-height: 0;
  margin: 0; border: 0; border-radius: 0; box-shadow: none;
}
body[data-view="play"]:has(.combat) .combat-log { flex: 0 0 auto; margin: 11px auto 9px; }
/* the deck keeps a reading width. Let loose across 1440px the Guard button alone was 870px of button,
   which is not a bigger control, it is the same control stretched out of proportion. */
body[data-view="play"]:has(.combat) .combat-ctrl,
body[data-view="play"]:has(.combat) .brink-panel { flex: 0 0 auto; width: 100%; max-width: 660px; margin: 0 auto; padding: 0 clamp(12px, 3vw, 28px) 12px; }
/* THE PAIR IS A COMPOSITION, NOT TWO THINGS PUSHED TO THE EDGES. `left:11%` / `right:9%` were written
   for a 556px stage, where they put the fighters 400px apart and reading as a pair. The same numbers
   on a full window stand them 1150px apart at opposite ends of an empty picture, which is how making
   the stage bigger managed to make the fight look emptier. They are placed symmetrically about the
   centre now, close enough to read as two men within reach of each other, and the room that opens up
   is at the SIDES -- which is exactly the room increments 2 and 3 need to move around in.
   THE SIZES ARE PROVISIONAL: the camera replaces them with a projected height in increment 1. Held
   near the walkable place's own scale (its wayfarer is 288px at 1440x900) so the two surfaces do not
   disagree about how big a person is on the day they start sharing one projection. */
/* THEY STAND ON THE SCENE'S OWN GROUND LINE (increment 3). The flat 16% was about art y 80 and was
   never read from anything, so in a scene whose ground begins at art y 63 the pair stood a long way
   up its own hillside. `--cb-*-bottom` is written by combatPaint out of the same projection that
   sizes them; the 16% survives as the fallback for a fight with no planes to read a line off, and
   `--cb-foe-rise` with it, because that is the only path where nothing knows where the ground is. */
body[data-view="play"]:has(.combat) .cs-you { left: 31%; right: auto; bottom: var(--cb-you-bottom, 16%);
  height: calc(46% * var(--cb-you-k, 1)); transform: translateX(var(--cb-you-dx, 0px)); }
body[data-view="play"]:has(.combat) .cs-foe,
body[data-view="play"]:has(.combat) .combat-stage .strike-zones { right: 31%; left: auto;
  bottom: var(--cb-foe-bottom, calc(16% + var(--cb-foe-rise, 0%)));
  height: calc(50% * var(--cb-foe-k, 1)); }
/* The mirror is his, and BOTH the shift and the foreshortening are shared: the zones are not
   mirrored and must not be, but they must take the same magnitude of squeeze about the same origin
   or they come off the man they are drawn on the moment you circle him. */
body[data-view="play"]:has(.combat) .cs-foe { transform: translateX(var(--cb-foe-dx, 0px)) scaleX(calc(-1 * var(--cb-foe-sx, 1))); }
body[data-view="play"]:has(.combat) .combat-stage .strike-zones { transform: translateX(var(--cb-foe-dx, 0px)) scaleX(var(--cb-foe-sx, 1)); }

/* ── THE FIGHT'S PLANES ────────────────────────────────────────────────────────────────────────
   Mirrors `.place-plane` rather than reusing it: that rule's width is written per frame by placePaint
   out of the place's own geometry, and the tiled variant slices `--tiles` across it. A fight is one
   frame with no strip, so it takes the same shape and none of the strip machinery.

   THE BLEED IS LOAD-BEARING. A plane that swings has to have somewhere to swing FROM or turning the
   camera opens a bare stripe at the frame's edge, which is the same reason the place carries
   LOOK_BLEED. The far plane travels 0.86 x 66 = 57px at a full look, and 76px of slack each side
   covers it with room to spare. */
.combat-scene .cb-plane { position: absolute; top: 0; bottom: 0; left: -76px; right: -76px; will-change: transform; pointer-events: none; }
.combat-scene .cb-plane .plane-art { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.combat-scene .cb-plane.pl-near .plane-art { filter: brightness(0.88); }   /* as the place dims its near band */
.combat-name { font-family: var(--serif); font-size: 26px; font-weight: 500; margin: 6px 0 4px; }
.combat-desc { font-size: 14.5px; line-height: 1.5; max-width: 46ch; margin: 0 auto 20px; }
.combat-bars { display: flex; flex-direction: column; gap: 12px; margin-bottom: 18px; }
.cbar { display: flex; flex-direction: column; gap: 5px; text-align: left; }
.cbar-label { font-size: 12px; color: var(--ink-dim); letter-spacing: 0.03em; }
.cbar-track { height: 9px; border-radius: 6px; background: rgba(233,220,196,0.1); overflow: hidden; }
.cbar-track > i { display: block; height: 100%; border-radius: 6px; transition: width 0.35s ease; }
.cbar-track.enemy > i { background: linear-gradient(90deg, #8a3b2f, var(--red)); }
.cbar-track.you > i { background: linear-gradient(90deg, #6b8f52, var(--green)); }
.combat-log { font-family: var(--serif); font-style: italic; font-size: 16px; color: #e8ddc7; min-height: 1.4em; margin: 6px 0 18px; }
.combat-acts { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }
/* the stamina/telegraph minigame */
.cbar-track.stamina > i { background: linear-gradient(90deg, #b78a3c, #e0c064); }
.cbar-track.stamina.low > i { background: linear-gradient(90deg, #b8603a, #d98c73); }
.combat-tell { margin: 0 auto 14px; max-width: 46ch; padding: 8px 14px; border: 1px solid var(--red); border-radius: 10px; background: rgba(217,140,115,0.12); color: #e6a48c; font-size: 14px; box-shadow: 0 0 0 3px rgba(217,140,115,0.12); }
.combat-acts .cmove { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; text-align: left; padding: 9px 14px; min-width: 152px; }
.cmove .mv-label { font-size: 15px; }
.cmove .mv-w { font-size: 12px; color: var(--ink-dim); font-style: italic; }
.cmove .mv-meta { font-size: 11px; letter-spacing: 0.02em; color: var(--ink-faint); }
.cmove[disabled] { opacity: 0.45; cursor: not-allowed; }
/* fluid combat classes: the stance toggle (any build), path-tied abilities (class identity), status tags, and Spare (mercy) */
.combat-stances { display: flex; gap: 8px; justify-content: center; flex-wrap: wrap; margin: 2px 0 16px; }
.cstance { font-size: 12.5px; letter-spacing: 0.04em; padding: 6px 15px; border: 1px solid var(--line); border-radius: 999px; background: rgba(233,220,196,0.04); color: var(--ink-dim); cursor: pointer; transition: all .15s ease; }
.cstance:hover { border-color: var(--gold-dim); color: var(--ink); }
.cstance.on { border-color: var(--gold); color: #f3e6c4; background: rgba(217,184,102,0.14); box-shadow: 0 0 8px rgba(217,184,102,0.28); }
.cmove.cability { border-color: var(--gold-dim); background: rgba(217,184,102,0.07); }
.cmove.cability .mv-label { color: #f0dfae; }
.cmove.cability .mv-meta { color: #cbb06e; }
.cmove.csubdue { border-color: #8fbf7a; background: rgba(143,191,122,0.10); }
.cmove.csubdue .mv-label { color: #a9d497; }
.cmove.csubdue .mv-meta { color: #8fbf7a; }
.status-tag { display: inline-block; margin-left: 8px; font-size: 10.5px; letter-spacing: 0.06em; text-transform: uppercase; padding: 1px 7px; border-radius: 999px; vertical-align: middle; }
.status-tag.bleed { color: #e0796b; background: rgba(216,86,75,0.16); border: 1px solid rgba(216,86,75,0.5); }
.combat.heavy .cbar-track.you.hurt > i { background: linear-gradient(90deg, #a83b2f, #e06a5a); }
.inv-eff.wpn-eff { color: #d68a63; }
/* THE BOW — an archer's view, not a timing bar. The SVG carries the shot; this is the frame,
   the draw gauge, and the one piece of feedback that is not in the picture: the shake. */
/* ---- the bow, aimed ON the combat stage (no separate archery screen) ---- */
.combat-stage.aiming { cursor: crosshair; touch-action: none; user-select: none; }
.aim-svg { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 3; pointer-events: none; }
/* the rings sit on the real foe, measured off his element, so they scale with him */
.aim-rings circle { fill: none; }
.aim-rings .ar-outer { stroke: #8fa6b4; stroke-width: 1.1; opacity: 0.5; }
.aim-rings .ar-inner { stroke: #cfe0ea; stroke-width: 1.1; opacity: 0.55; }
.aim-rings .ar-gold  { stroke: #e8b45c; stroke-width: 1.6; opacity: 0.9; }
.aim-rings .ar-pin   { fill: #e8b45c; stroke: none; opacity: 0.9; }
.aim-rig { opacity: 1; transition: opacity 0.2s ease; }
.ab-limb   { fill: none; stroke: #6b5334; stroke-width: 5; stroke-linecap: round; }
.ab-string { fill: none; stroke: #cfc8b4; stroke-width: 1.2; }
.ab-shaft  { stroke: #e6dcc4; stroke-width: 2; }
.ab-head   { fill: #cfd8dc; }
.aim-fly   { stroke: #e6dcc4; stroke-width: 1.6; opacity: 0.85; }
.aim-ret circle, .aim-ret path { fill: none; stroke: #e8b45c; stroke-width: 1.2; }
/* the draw gauge sits with the stamina track it lives beside, not orphaned at the far edge */
.aim-gauge { position: absolute; left: 15px; right: 15px; bottom: 18px; height: 5px; z-index: 4;
  background: rgba(233,231,221,0.12); border: 1px solid var(--line); pointer-events: none; }
.aim-gauge i { display: block; height: 100%; width: 0; background: var(--gold-dim); transition: background 0.15s linear; }
.aim-gauge i.full { background: var(--gold); box-shadow: 0 0 10px rgba(232,180,92,0.7); }
.aim-gauge .ag-full { position: absolute; left: 62%; top: -3px; bottom: -3px; width: 1px; background: rgba(232,180,92,0.55); }
.combat-ctrl.aiming { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.combat-ctrl.aiming .aim-say { font-size: 14px; color: var(--ink-dim); margin: 0; min-height: 20px; text-align: center; }
/* stack this one too: it is a .cmove outside .cbtn-row, so the row's rule does not reach it and the
   label and meta would run together exactly as the subdue button used to */
.combat-ctrl.aiming .cmove:has(.mv-meta) { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; min-width: 152px; }

/* ---- ending: ambition recap ---- */
.amb-recaps { margin: 12px 0; text-align: left; max-width: 520px; margin-left: auto; margin-right: auto; }
.amb-recap { font-size: 13.5px; color: var(--ink-dim); font-style: italic; margin-bottom: 6px; }

/* ============ FEELING (how a choice sits in the soul) ============ */
.feeling { font-family: var(--serif); font-style: italic; font-size: 15.5px; line-height: 1.6; color: var(--gold-dim); margin: -4px 0 16px; opacity: 0.95; }

/* ============ MOSQUE GEOMETRY: tilework band + mihrab arch ============ */
.girih-band { display: block; width: 190px; height: 12px; margin: 16px auto 22px; opacity: 0.72; }
.girih-band rect, .girih-band line { fill: none; stroke: var(--gold-dim); stroke-width: 1; }
.arched {
  position: relative; border: 1px solid var(--gold-dim);
  border-top-left-radius: 46% 88px; border-top-right-radius: 46% 88px;
  border-bottom-left-radius: 14px; border-bottom-right-radius: 14px;
  padding: clamp(40px, 8vw, 60px) clamp(20px, 5vw, 34px) 30px;
  background: radial-gradient(120% 80% at 50% 0%, rgba(217,184,102,0.07), rgba(217,184,102,0) 60%), var(--panel);
  max-width: 620px; margin: 16px auto;
}
.arched::before { /* keystone star at the apex of the arch */
  content: ""; position: absolute; top: -9px; left: 50%; width: 16px; height: 16px; transform: translateX(-50%) rotate(45deg);
  background: var(--bg); border: 1px solid var(--gold-dim);
}

/* ============ SETTING SCENE (hub banner) ============ */
.scene-frame { position: relative; border: 1px solid var(--line); border-radius: 14px; overflow: hidden; margin-bottom: 16px; box-shadow: inset 0 0 30px rgba(0,0,0,0.4); }
/* bolder, more saturated banners + a little more presence */
.scene-art { display: block; width: 100%; height: clamp(82px, 21vw, 112px); filter: saturate(1.22) contrast(1.05); }
.scene-star { fill: none; stroke: #e9d6a8; stroke-width: 1; opacity: 0.4; }
.scene-star.bright { opacity: 0.7; }
/* a slow light passes over every banner, so the scene is never quite still */
.scene-frame::after { content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(105deg, transparent 42%, rgba(255,246,222,0.05) 50%, transparent 58%);
  transform: translateX(-100%); animation: scene-sweep 11s ease-in-out infinite; }
@keyframes scene-sweep { 0%, 12% { transform: translateX(-100%); } 55%, 100% { transform: translateX(100%); } }

/* --- the per-location scenes mark elements for motion; the engine animates these classes ---
   These were timid: a sway of a degree and a half, an opacity blink. Rewritten to actually MOVE --
   a flame that jitters like a flame, water that carries light across itself, a wind that gusts and
   staggers so a field is never in lockstep. Amplitude, phase and duration are per-element vars
   (--wa, --wo, --wd) so a group set from the build reads as many things, not one. */
/* lights twinkle by BRIGHTNESS only. The windows/stars are emitted as groups (a whole city band in
   one <g>), so a group scale pivots on the band centre and slides every light toward the middle --
   that read as "lights moving all over the place". Opacity-only keeps each light where it is. */
.scene-art .scene-twinkle { animation: scene-tw 3.4s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes scene-tw { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
/* AN ANIMATION'S KEYFRAME BEATS A PRESENTATION ATTRIBUTE, and that had been quietly discarding every
   glow opacity the scene builder authored. `opacity="0.12"` on a lamp's ground pool is an SVG
   presentation attribute, which sits at the BOTTOM of the cascade; a running @keyframes sits at the
   top. So every .scene-glow rendered at 0.62-1.0 whatever it asked for, and the lamp pools came out
   about seven times too strong -- the four big pale ellipses floating across the Eel Quays.
   The pulse now MULTIPLIES the authored value instead of replacing it. Elements that carry no --go
   behave exactly as before (0.62 <-> 1). The static `opacity` line is the fallback that matters under
   prefers-reduced-motion, where the animation is switched off and there would otherwise be nothing
   left to hold the value down. */
.scene-art .scene-glow { opacity: var(--go, 1); animation: scene-glow 4.6s ease-in-out infinite; }
@keyframes scene-glow { 0%, 100% { opacity: calc(var(--go, 1) * 0.62); } 50% { opacity: var(--go, 1); } }
.scene-art .scene-shimmer { animation: scene-shimmer 4.2s ease-in-out infinite; }
@keyframes scene-shimmer { 0%, 100% { opacity: 0.45; } 50% { opacity: 0.95; } }
/* a lamp/flame: a slow glow that breathes in place. It used to jitter at 0.42s and LEAN left-right
   (skewX) -- on a bright lamp that swing was the most eye-catching motion on the screen, and read as
   "the lights moving all over the place". No horizontal lean now; just a gentle brightness/height pulse. */
.scene-art .scene-flicker { transform-box: fill-box; transform-origin: 50% 100%; animation: scene-flick 2.4s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes scene-flick {
  0%, 100% { opacity: 0.82; transform: scaleY(1); }
  45% { opacity: 1; transform: scaleY(1.03); }
  72% { opacity: 0.9; transform: scaleY(0.98); }
}
/* the lamp core: a still ball whose SIZE and BRIGHTNESS pulse. It scales about its own centre
   (fill-box + 50% 50%), so the ball does not travel -- it just swells and dims where it sits. */
.scene-art .sc-lampcore { transform-box: fill-box; transform-origin: 50% 50%; animation: sc-lampcore 2.6s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes sc-lampcore {
  0%, 100% { transform: scale(0.9); opacity: 0.7; }
  50% { transform: scale(1.1); opacity: 1; }
}
.scene-art .scene-drift { animation: scene-drift 22s ease-in-out infinite alternate; }
@keyframes scene-drift { 0% { transform: translate3d(-16px, 3px, 0); } 100% { transform: translate3d(16px, -3px, 0); } }
.scene-art .scene-rise { transform-box: fill-box; animation: scene-rise 6.5s ease-out infinite; animation-delay: var(--wo, 0s); }
@keyframes scene-rise { 0% { opacity: 0; transform: translateY(6px) scale(0.8); } 30% { opacity: 0.8; } 100% { opacity: 0; transform: translateY(-14px) scale(1.3); } }

/* the WIND: per-element sway that gusts, so a bed of reeds or a line of cloth moves as many things.
   Each element carries its own amplitude (--wa), phase (--wo) and period (--wd) from the build. */
.scene-art .w-sway { transform-box: fill-box; transform-origin: 50% 100%; animation: w-sway var(--wd, 4.2s) cubic-bezier(0.37,0,0.63,1) infinite; animation-delay: var(--wo, 0s); }
@keyframes w-sway {
  0% { transform: rotate(calc(-1 * var(--wa, 3deg))); }
  38% { transform: rotate(calc(0.55 * var(--wa, 3deg))); }
  50% { transform: rotate(var(--wa, 3deg)); }
  74% { transform: rotate(calc(-0.4 * var(--wa, 3deg))); }
  100% { transform: rotate(calc(-1 * var(--wa, 3deg))); }
}
/* a slow group gust layered on top, so the whole bed leans together now and then */
.scene-art .w-gust { transform-box: fill-box; transform-origin: 50% 100%; animation: w-gust 9s ease-in-out infinite; }
@keyframes w-gust { 0%, 100% { transform: skewX(0deg); } 42% { transform: skewX(-3deg); } 58% { transform: skewX(-3.4deg); } }
/* caustic light on water: it shimmers in place by brightness. It used to translate sideways, which
   in the walkable place (the scene SVG is blown up ~6x there) swept ~450px across the screen. */
.scene-art .w-caustic { animation: w-caustic 7s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes w-caustic { 0%, 100% { opacity: 0.12; } 50% { opacity: 0.6; } }
/* a reflection wavering on the water below its source -- brightness only. The old scaleX pivoted off
   the SVG origin (fill-box isn't honoured on a <g> here), throwing the reflection ~490px sideways. */
.scene-art .w-reflect { animation: w-reflect 3.4s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes w-reflect { 0%, 100% { opacity: 0.6; } 50% { opacity: 0.95; } }
/* a firefly / spark tracing a slow curved drift near a light */
/* fireflies/motes near a lamp: a soft glow that drifts only a hair. The old travel (var(--wa), -10px)
   was magnified in the walkable place to ~150px of flight, which read as lights darting about. */
.scene-art .w-fly { transform-box: fill-box; animation: w-fly var(--wd, 6s) ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes w-fly { 0% { transform: translate(0, 0); opacity: 0.2; } 50% { transform: translate(calc(var(--wa, 8px) * 0.12), -1px); opacity: 0.9; } 100% { transform: translate(0, 0); opacity: 0.2; } }

/* --- weather and motion written for THIS game's country: water, ash, smoke, tide, signal-fire ---
   The first game had six of these. The delta needs rain and ripple, the kilnfields need smoke and
   embers, the cinder country needs falling ash, the salt road needs a tide. Each is cheap (opacity
   or transform only) and every one is switched off under prefers-reduced-motion further down. */
.scene-art .scene-rain { animation: scene-rain 1.15s linear infinite; }
@keyframes scene-rain { from { transform: translate3d(2px, -14px, 0); } to { transform: translate3d(-2px, 14px, 0); } }
.scene-art .scene-fall { animation: scene-fall 9s linear infinite; }
@keyframes scene-fall { from { transform: translate3d(-3px, -16px, 0); opacity: 0; } 12% { opacity: 0.55; } 88% { opacity: 0.55; } to { transform: translate3d(3px, 18px, 0); opacity: 0; } }
.scene-art .scene-smoke { transform-box: fill-box; transform-origin: 50% 100%; animation: scene-smoke 11s ease-out infinite; }
@keyframes scene-smoke { 0% { opacity: 0; transform: translateY(6px) scale(0.7); } 25% { opacity: 0.34; } 100% { opacity: 0; transform: translateY(-16px) scale(1.5); } }
.scene-art .scene-embers { animation: scene-embers 5.5s ease-out infinite; }
@keyframes scene-embers { 0% { opacity: 0; transform: translate3d(0, 6px, 0); } 20% { opacity: 0.9; } 100% { opacity: 0; transform: translate3d(10px, -34px, 0); } }
/* birds far off: a gentle soaring drift, not a streak across the whole sky. Was translateX(-70->430px),
   ~260px on screen, which was the last thing sweeping the frame. Now a small drift that eases back. */
.scene-art .scene-glide { animation: scene-glide 34s ease-in-out infinite alternate; }
@keyframes scene-glide { from { transform: translateX(-18px); } to { transform: translateX(22px); } }
.scene-art .scene-tide { animation: scene-tide 7.5s ease-in-out infinite alternate; }
@keyframes scene-tide { from { transform: translate3d(-18px, 2px, 0); opacity: 0.35; } to { transform: translate3d(18px, -2px, 0); opacity: 0.7; } }
.scene-art .scene-ripple { transform-box: fill-box; animation: scene-ripple 3.6s ease-in-out infinite; }
@keyframes scene-ripple { 0%, 100% { transform: translateY(-0.8px) scaleY(0.97); } 50% { transform: translateY(0.9px) scaleY(1.035); } }
.scene-art .scene-sway { transform-box: fill-box; transform-origin: 50% 100%; animation: scene-sway 5.2s cubic-bezier(0.37,0,0.63,1) infinite; }
/* ...UNLESS THE THING BROUGHT ITS OWN HINGE, and then the box it is measured in has to change with it.
   A cloth on a drying rack is authored with `transform-origin:<x>px <y>px` in the ART'S OWN 400-unit
   space -- the rail it hangs from. The rule above says `fill-box`, so those px were read from the
   CLOTH'S OWN top-left corner instead, and a cloth 4 units wide ended up hinged 84 to 256 units away.
   Over the 6.4deg of the sway that swings its far corner through 10 to 29 units: two and a half to
   seven times its own width, so the sheets flew across the delta instead of stirring on the line.
   Measured at The Reed House, 149 of these across 22 scenes. `view-box` is what an authored art-space
   origin means, and it is what every other inline-origin class here (w-reflect, sc-head, sc-idle,
   sc-work) already gets by default -- this rule is the only one that took it away. The ones with no
   inline hinge keep fill-box and their own 50%/100%. */
.scene-art .scene-sway[style*="transform-origin"] { transform-box: view-box; }
@keyframes scene-sway { 0% { transform: rotate(-3.2deg); } 40% { transform: rotate(1.6deg); } 50% { transform: rotate(3.2deg); } 76% { transform: rotate(-1.2deg); } 100% { transform: rotate(-3.2deg); } }
.scene-art .scene-pall { animation: scene-pall 13s ease-in-out infinite; }
@keyframes scene-pall { 0%, 100% { opacity: 0.16; } 50% { opacity: 0.4; } }
.scene-art .scene-gleam { animation: scene-gleam 3.1s ease-in-out infinite; }
@keyframes scene-gleam { 0%, 100% { opacity: 0.25; } 40% { opacity: 1; } 60% { opacity: 0.5; } }
.scene-art .scene-beat { animation: scene-beat 2.2s ease-in-out infinite; }
@keyframes scene-beat { 0%, 100% { opacity: 0.45; } 8% { opacity: 1; } 20% { opacity: 0.55; } }
.scene-art .scene-blink { animation: scene-blink 4.4s steps(1, end) infinite; }
@keyframes scene-blink { 0%, 44% { opacity: 1; } 45%, 100% { opacity: 0.22; } }
.scene-art .scene-breathe { transform-box: fill-box; transform-origin: 50% 100%; animation: scene-breathe 14s ease-in-out infinite alternate; }
@keyframes scene-breathe { from { transform: scale(0.97); opacity: 0.42; } to { transform: scale(1.05); opacity: 0.72; } }
.scene-art .scene-slide { transform-box: fill-box; animation: scene-slide 8s ease-in infinite; }
@keyframes scene-slide { 0%, 70% { transform: translateY(0); opacity: 0.7; } 85% { transform: translateY(4px); opacity: 0.4; } 100% { transform: translateY(0); opacity: 0.7; } }
@media (prefers-reduced-motion: reduce) {
  .scene-art .scene-rain, .scene-art .scene-fall, .scene-art .scene-smoke, .scene-art .scene-embers,
  .scene-art .scene-glide, .scene-art .scene-tide, .scene-art .scene-ripple, .scene-art .scene-sway,
  .scene-art .scene-pall, .scene-art .scene-gleam, .scene-art .scene-beat, .scene-art .scene-blink,
  .scene-art .scene-breathe, .scene-art .scene-slide, .scene-art .scene-twinkle, .scene-art .scene-glow,
  .scene-art .scene-shimmer, .scene-art .scene-flicker, .scene-art .scene-drift, .scene-art .scene-rise,
  .scene-art .w-sway, .scene-art .w-gust, .scene-art .w-caustic, .scene-art .w-reflect, .scene-art .w-fly,
  .scene-art .sc-lampcore, .scene-frame::after { animation: none; }
}

/* --- a place wears your deeds + the age you live in: the art and frame shift with its state --- */
.scene-frame.state-grateful .scene-art { filter: saturate(1.32) brightness(1.07) contrast(1.05); }
.scene-frame.state-grateful { box-shadow: inset 0 0 0 1px rgba(217,184,102,0.34), inset 0 0 40px rgba(217,184,102,0.12), 0 0 22px rgba(217,184,102,0.12); }
.scene-frame.state-thriving .scene-art { filter: saturate(1.3) brightness(1.05); }
.scene-frame.state-grieving .scene-art { filter: saturate(0.5) brightness(0.82) contrast(1.02); }
.scene-frame.state-grieving { box-shadow: inset 0 0 40px rgba(0,0,0,0.55); }
.scene-frame.state-wary .scene-art { filter: saturate(0.85) brightness(0.9); }
.scene-frame.state-wary { box-shadow: inset 0 0 0 1px rgba(217,140,115,0.28), inset 0 0 34px rgba(0,0,0,0.4); }
.scene-frame.state-troubled .scene-art { filter: saturate(1.06) contrast(1.12) brightness(0.9); }
.scene-frame.state-troubled { box-shadow: inset 0 0 0 1px rgba(180,80,60,0.22), inset 0 0 40px rgba(60,10,10,0.4); }
.loc-state { font-family: var(--serif); font-style: italic; font-size: 14.5px; line-height: 1.55; margin: 0 0 12px; max-width: 66ch; }
.loc-state.grateful { color: var(--gold); } .loc-state.thriving { color: #8fbf7a; }
.loc-state.grieving { color: #9db4d6; } .loc-state.wary { color: var(--red); } .loc-state.troubled { color: #d98c73; }
/* what the journey did to you, shown once on arrival (no more silent meter shifts on travel) */
.arrival-note { font-size: 13px; letter-spacing: .02em; color: var(--ink-dim); background: rgba(233,220,196,0.05); border: 1px solid var(--line); border-radius: 7px; padding: 7px 12px; margin: 0 0 16px; }
/* the story spine: a soft, ignorable nudge that a chapter beat waits in this place */
.thread-nudge { font-family: var(--serif); font-style: italic; font-size: 14px; line-height: 1.55; color: #e6c98b; background: rgba(217,184,102,0.07); border-left: 2px solid var(--gold-dim); border-radius: 3px; padding: 9px 14px; margin: 0 0 16px; max-width: 66ch; }
.journal-item.thread { border-left-color: #e6c98b; }
.journal-item.thread .ji-text { color: #f3e6c4; }
.journal-item.thread .ji-ref { color: #cbb06e; letter-spacing: .04em; }

/* --- an emotional key on toned scenes: a hair of colour on the banner + accent --- */
.scene.tone-tragedy .act-name { color: #9db4d6; } .scene.tone-tragedy .scene-title { color: #d7e0ec; }
.scene.tone-tender .act-name { color: #e2a7b4; }
.scene.tone-comedy .act-name { color: #9bce86; }
.scene.tone-drama .act-name { color: var(--gold); }


/* ---- encounter scene banner: the scenario gets its own illuminated art, keyed to verseTheme + tinted by tone ---- */
.scene-frame.scene-enc { animation: rise 0.45s ease; }
.scene-enc .scene-cap {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3; pointer-events: none;
  padding: 28px 16px 8px; background: linear-gradient(transparent, rgba(9,7,4,0.8));
  font-family: var(--sans); font-size: 11px; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--gold-dim); text-shadow: 0 1px 8px rgba(0,0,0,0.85);
}
/* tone tints the banner art + caption, mirroring the location state-* treatments */
.scene-frame.scene-enc.tone-drama .scene-art { filter: saturate(1.16) contrast(1.16) brightness(1.02); }
.scene-frame.scene-enc.tone-drama { box-shadow: inset 0 0 0 1px rgba(217,184,102,0.22), inset 0 0 34px rgba(0,0,0,0.42); }
.scene-frame.scene-enc.tone-drama .scene-cap { color: var(--gold); }
.scene-frame.scene-enc.tone-tender .scene-art { filter: saturate(1.24) brightness(1.06); }
.scene-frame.scene-enc.tone-tender { box-shadow: inset 0 0 0 1px rgba(226,167,180,0.2), inset 0 0 40px rgba(120,50,70,0.14); }
.scene-frame.scene-enc.tone-tender .scene-cap { color: #e2a7b4; }
.scene-frame.scene-enc.tone-comedy .scene-art { filter: saturate(1.34) brightness(1.1); }
.scene-frame.scene-enc.tone-comedy .scene-cap { color: #9bce86; }
.scene-frame.scene-enc.tone-tragedy .scene-art { filter: saturate(0.5) brightness(0.8) contrast(1.06); }
.scene-frame.scene-enc.tone-tragedy { box-shadow: inset 0 0 0 1px rgba(120,150,190,0.18), inset 0 0 42px rgba(0,0,0,0.55); }
.scene-frame.scene-enc.tone-tragedy .scene-cap { color: #9db4d6; }

/* ---- kinship: per-person scene banner (name + role + a bond chip + an aniconic sigil, never a face) ---- */
.scene-cap .npc-sigil { display: inline-flex; width: 16px; height: 16px; vertical-align: -3px; margin-right: 7px; color: currentColor; opacity: 0.92; }
.scene-cap .npc-sigil svg { width: 100%; height: 100%; }
.bond-chip { display: inline-block; font-size: 9.5px; letter-spacing: 0.08em; padding: 2px 7px; margin-left: 8px; border-radius: 20px; border: 1px solid var(--line); background: rgba(0,0,0,0.28); color: var(--ink-dim); text-shadow: none; }
.bond-chip.bond2 { color: #efd794; border-color: rgba(217,184,102,0.55); background: rgba(217,184,102,0.14); }
.bond-chip.bond1 { color: #9bce86; border-color: rgba(127,176,105,0.45); }
.bond-chip.bond-1 { color: #a9c4de; border-color: rgba(127,168,201,0.45); }
.bond-chip.bond-2 { color: #e6a48c; border-color: rgba(217,140,115,0.5); background: rgba(217,140,115,0.12); }
/* the immediate warmer/colder chip on the outcome screen */
.delta.rel { border: 1px solid var(--line); background: rgba(217,184,102,0.08); }
.delta.rel.up { color: #efd794; }
.delta.rel.down { color: var(--red); }
/* "Those who remember you" on the Character screen */
.rel-row { display: flex; align-items: center; gap: 9px; padding: 7px 2px; border-bottom: 1px solid rgba(233,220,196,0.06); }
.rel-row:last-of-type { border-bottom: none; }
.rel-row .npc-sigil { width: 20px; height: 20px; flex: 0 0 auto; color: var(--gold-dim); opacity: 0.85; }
.rel-row .npc-sigil svg { width: 100%; height: 100%; display: block; }
.rel-name { font-family: var(--serif); font-size: 15px; color: var(--ink); }
.rel-word { font-size: 10.5px; letter-spacing: 0.05em; text-transform: uppercase; color: var(--ink-dim); }
.rel-deed { font-style: italic; font-size: 12.5px; flex: 1 1 auto; min-width: 0; }
.rel-since { font-size: 11px; white-space: nowrap; color: var(--ink-faint); }
.rel-row.bond2 .npc-sigil { color: #efd794; opacity: 1; }
.rel-row.bond2 .rel-word { color: #efd794; }
.rel-row.bond1 .rel-word { color: #9bce86; }
.rel-row.bond-1 .rel-word { color: #a9c4de; }
.rel-row.bond-2 .npc-sigil { color: var(--red); }
.rel-row.bond-2 .rel-word { color: var(--red); }
.rel-renown { margin-top: 9px; font-style: italic; }

/* the madrasa: quiz / examination */
.scene.school .scene-text.small { font-size: 15px; color: var(--ink-dim); margin-bottom: 14px; }
.quiz-q { font-size: 20px; margin-bottom: 6px; }
.choice.quiz-opt.quiz-correct { border-color: var(--green); border-left-color: var(--green); background: rgba(127,176,105,0.14); box-shadow: 0 0 0 1px rgba(127,176,105,0.38); }
.choice.quiz-opt.quiz-correct::after { content: "✓"; position: absolute; right: 16px; color: var(--green); font-size: 18px; z-index: 2; }
.choice.quiz-opt.quiz-wrong { border-color: var(--red); border-left-color: var(--red); background: rgba(217,140,115,0.12); }
.choice.quiz-opt[disabled] { cursor: default; opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .scene-frame.scene-enc { animation: none !important; }
  .scene-frame::after, .scene-art .scene-twinkle, .scene-art .scene-glow, .scene-art .scene-shimmer,
  .scene-art .scene-flicker, .scene-art .sc-lampcore, .scene-art .scene-drift, .scene-art .scene-rise { animation: none !important; }
}

/* ============ ITEM GLYPHS ============ */
.glyph { width: 1.15em; height: 1.15em; vertical-align: -0.2em; margin-right: 7px; color: var(--gold-dim); opacity: 0.92; }
.shop-info b .glyph, .inv-row b .glyph { color: var(--gold); }

/* ============ TRIALS (disabilities) reveal ============ */
.trials { text-align: center; animation: rise 0.7s ease; }
.trials-intro { font-family: var(--serif); font-size: 17px; line-height: 1.7; color: #e8ddc7; max-width: 54ch; margin: 12px auto 22px; text-align: left; }
.trials-none { font-family: var(--serif); font-size: 17px; line-height: 1.7; color: #e8ddc7; max-width: 52ch; margin: 14px auto 22px; }
.trials-list { display: flex; flex-direction: column; gap: 10px; text-align: left; margin-bottom: 18px; }
.trial-row { border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 11px; padding: 12px 16px; background: var(--panel); }
.trial-row.mental { border-left-color: #9db4d6; }
.trial-name { font-family: var(--serif); font-size: 16px; display: flex; align-items: baseline; gap: 8px; }
.trial-kind { font-size: 9px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-faint); border: 1px solid var(--line); border-radius: 10px; padding: 1px 7px; }
.trial-note { font-size: 13.5px; margin: 4px 0 6px; }
.trial-bear { font-family: var(--serif); font-style: italic; font-size: 14px; color: var(--gold-dim); }

/* ============ LESSON (a point of faith) ============ */
.lesson { text-align: center; animation: rise 0.6s ease; }
.lesson-title { font-family: var(--serif); font-size: clamp(22px, 5vw, 28px); font-weight: 500; margin: 6px 0 14px; }
.lesson-body { font-family: var(--serif); font-size: 17px; line-height: 1.72; color: #e8ddc7; text-align: left; max-width: 58ch; margin: 0 auto 18px; text-wrap: pretty; }
.lesson .deltas { justify-content: center; }

/* ============ HUB: learn action + rail bearing ============ */
.hub-act.learn { border-left: 3px solid #7fbfa8; }
.rl-bearing { text-transform: uppercase; letter-spacing: 0.14em; font-size: 10px; }
.rl-bearing.proud { color: var(--red); }
.rl-bearing.humble { color: var(--green); }
.rl-bearing.zulm { color: #d8564b; font-weight: 700; text-shadow: 0 0 8px rgba(216,86,75,0.45); }
.zulm-line b { color: #d8564b; }

/* ============ FOLLOW-UP INFO + INTROSPECTIVE / DOUBT / QUEST SCENES ============ */
.reveal-line { font-family: var(--serif); font-size: 15px; line-height: 1.6; color: var(--ink-dim); border-left: 2px solid var(--line); padding-left: 14px; margin: 0 0 14px; }
.scene.inward .scene-text, .scene.doubt .scene-text { border-left: 2px solid var(--gold-dim); padding-left: 16px; }
.scene.inward .act-name { color: #9db4d6; }   /* reliance-blue — a turn inward */
.scene.doubt .act-name { color: #c7b8e0; }    /* guidance-lavender — a question of faith */
.scene.quest .act-name { color: #7fbfa8; }    /* knowledge-teal — a task asked of you */
.scene.people .act-name { color: #d69f7e; }   /* charity-warm — among the townsfolk */
.hub-act.people { border-left: 3px solid #d69f7e; }
.hub-act.provision { border-left: 3px solid #6bbf59; }
.hub-act.charity { border-left: 3px solid #e2879e; }

/* ============ INVENTORY (reworked: categorized cards) ============ */
.inv-panel .inv-coin { color: var(--gold); margin-bottom: 20px; letter-spacing: 0.06em; }
.inv-panel .ic-num { font-family: var(--serif); font-size: 24px; font-variant-numeric: tabular-nums; }
.inv-section { margin-bottom: 24px; }
.inv-section-h { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 11px; border-bottom: 1px solid var(--line); padding-bottom: 6px; }
.inv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); gap: 10px; }
.inv-card { display: flex; gap: 12px; align-items: flex-start; background: var(--panel); border: 1px solid var(--line); border-radius: 12px; padding: 13px 15px; }
.inv-card.equipment { border-left: 3px solid var(--gold-dim); }
.inv-card.medicine { border-left: 3px solid #e2879e; }
.inv-glyph { flex: 0 0 auto; }
.inv-glyph .glyph { width: 2em; height: 2em; margin: 2px 0 0; color: var(--gold); opacity: 0.95; }
.inv-body { flex: 1 1 auto; min-width: 0; }
.inv-name { font-family: var(--serif); font-size: 16px; }
.inv-count { color: var(--gold-dim); font-size: 13px; }
.inv-eff { font-size: 12.5px; margin-top: 3px; }
.inv-eff.worn-eff { color: var(--gold-dim); }
.inv-eff.use-eff { color: var(--green); }
.inv-desc { font-size: 12.5px; line-height: 1.45; margin-top: 5px; }
.inv-act { flex: 0 0 auto; align-self: center; }

/* ============ WORLD CONDITION (the age you are born into) ============ */
.world-note { border: 1px solid var(--line); border-left: 3px solid var(--gold-dim); border-radius: 12px; padding: 12px 16px; margin: 14px auto 4px; max-width: 56ch; text-align: left; background: rgba(217,184,102,0.05); }
.world-name { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--gold-dim); }
.world-note p { font-family: var(--serif); font-size: 15px; line-height: 1.62; color: #ded3bd; margin: 6px 0 0; }
.loc-detail { font-family: var(--serif); font-style: italic; font-size: 15px; line-height: 1.6; color: var(--ink-faint); margin: 2px 0 10px; max-width: 66ch; }

/* ============ TALENT CATEGORIES ============ */
.talent-cat { margin-bottom: 20px; }
.talent-cat-head { display: flex; align-items: baseline; gap: 10px; margin: 0 0 10px; flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 6px; }
.talent-cat-label { font-family: var(--serif); font-size: 16px; color: var(--gold); letter-spacing: 0.02em; }
.talent-cat-blurb { font-size: 12.5px; color: var(--ink-faint); font-style: italic; }
.talent-cat-tag { font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink-faint); border: 1px solid var(--line); border-radius: 10px; padding: 1px 7px; margin-left: 8px; vertical-align: 2px; }
.talent.choose.cat-body { border-left: 3px solid #d98c73; }
.talent.choose.cat-mind { border-left: 3px solid #7fa8c9; }
.talent.choose.cat-heart { border-left: 3px solid #e2879e; }
.talent.choose.cat-faith { border-left: 3px solid var(--gold); }
.talent.choose.cat-fortune { border-left: 3px solid #7fbfa8; }
.talent.choose.picked { border-left-color: var(--gold); }

/* ============ FULL-BLEED RESPONSIVE LAYOUT ============ */
/* Reading-centric screens stay a measured column; grid/utility screens fan out to fill
   the width; the shell + background go edge to edge and adapt to the viewport. */
.scene, .outcome { max-width: 760px; margin-left: auto; margin-right: auto; }
.choices { max-width: 680px; }
.scene-text, .outcome-text, .loc-desc, .reveal-line, .feeling { max-width: 66ch; }

.hub { max-width: 100%; }
.loc-head { max-width: 860px; }
.hub-acts { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.paths { grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); }
.talent-grid { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
.map-screen, .panel { max-width: 960px; }
.rail-inner { max-width: var(--gw); }
.growth { max-width: 760px; }

/* The peripheral verse ("signs surface at the edge"): a fixed side-lane on wide screens
   (with the content reserving room for it), inline below the content on smaller ones. */
@media (min-width: 1081px) {
  body.has-aside .game { padding-right: 340px; }
  .reflection { right: max(20px, calc((100vw - var(--gw)) / 2 + 14px)); width: clamp(280px, 22vw, 320px); top: 92px; }
}
@media (max-width: 1080px) {
  .reflection { position: static; width: auto; max-width: 680px; margin: 8px auto 22px; transform: none; opacity: 1; transition: none; box-shadow: none; }
  .reflection:not(.show) { display: none; }
}

@media (max-width: 720px) {
  .brand-sub { display: none; }
  .meter-pip .pip-bar { width: 48px; }
  .arched { border-top-left-radius: 40% 54px; border-top-right-radius: 40% 54px; }
}
/* narrow phones: the top bar + nav must never overrun the viewport (which used to stretch the fixed rail
   to match the overflowing document width). flex-wrap guarantees no horizontal overflow; the smaller
   links keep it to one line on most phones. */
@media (max-width: 480px) {
  .topbar { flex-wrap: wrap; row-gap: 2px; padding-top: 12px; padding-bottom: 12px; }
  .topnav { flex-wrap: wrap; justify-content: flex-end; gap: 2px; }
  .linkbtn { padding: 6px 8px; font-size: 10.5px; letter-spacing: 0.03em; }
  .brand-title { font-size: 17px; }
  /* compact the rail so its wrapped tokens don't eat a small phone (nothing is hidden — just tighter) */
  .rail { padding: 7px 10px; }
  .rail-pips { gap: 8px 10px; }
  .stat-pip { min-width: 36px; }
  .stat-pip .pip-emblem svg { width: 13px; height: 13px; }
  .stat-pip .pip-val { font-size: 14px; }
  .stat-pip .pip-bar { width: 34px; }
  .stat-pip .pip-short { font-size: 9px; }
  .meter-pip { min-width: 50px; padding: 2px 7px 4px; }
  .meter-pip .pip-icon svg { width: 13px; height: 13px; }
  .rail-life { gap: 10px; font-size: 10px; }
}

/* ============ GAME-FEEL, ANIMATION & ILLUMINATED TEXT ============ */
/* creation reads more like a game screen */
.points { box-shadow: 0 6px 20px rgba(0,0,0,0.25); }
.preset { transition: color 0.15s, border-color 0.15s, transform 0.08s; }
.preset:active { transform: translateY(1px); }

/* illuminated narrative: a gold drop-cap opens authored prose (verse text is never touched) */
.scene-text::first-letter, .lesson-body::first-letter, .ending-body::first-letter, .reveal.illum .backstory::first-letter {
  font-family: var(--serif); font-size: 3.1em; line-height: 0.82; float: left;
  margin: 0.04em 0.10em 0 0; color: var(--gold); text-shadow: 0 1px 0 rgba(0,0,0,0.45);
}
.scene.inward .scene-text::first-letter { color: #9db4d6; }
.scene.doubt .scene-text::first-letter { color: #c7b8e0; }
/* results read distinct from scenes: a small illuminated rule instead of a drop-cap */
.outcome-text { position: relative; padding-left: 16px; }
.outcome-text::before { content: ""; position: absolute; left: 0; top: 4px; bottom: 4px; width: 2px; border-radius: 2px; background: linear-gradient(180deg, var(--gold), rgba(217,184,102,0)); }

/* delta chips carry a little more presence */
.delta { background: rgba(233,220,196,0.03); }
.delta.up { box-shadow: inset 0 0 0 1px rgba(127,176,105,0.14); }
.delta.down { box-shadow: inset 0 0 0 1px rgba(217,140,115,0.14); }

/* a tilework flourish under the act banner + a small lift on interactive rows */
.act-name::after { content: ""; display: block; width: 30px; height: 1px; margin: 6px auto 0; background: linear-gradient(90deg, transparent, var(--gold-dim), transparent); }
.hub-act:hover { transform: translateY(-1px); box-shadow: 0 4px 14px rgba(0,0,0,0.22); }

/* ---- the "light on your road" gauge (character sheet) ---- */
.iman-gauge { display: flex; gap: 7px; margin: 2px 0 10px; }
.iman-dot { width: 9px; height: 9px; border-radius: 50%; background: rgba(233,220,196,0.12); border: 1px solid var(--line); }
.iman-dot.lit { background: radial-gradient(circle at 35% 30%, #fbeeba, var(--gold)); border-color: var(--gold-dim); box-shadow: 0 0 8px rgba(217,184,102,0.5); }
.iman-note { font-style: italic; font-size: 13.5px; }

/* ---- Where you stand: the three worldly axes (power / wealth / influence) — words + aniconic band pips ---- */
.axis-note { font-size: 13px; margin: 0 0 10px; }
.axis-row { display: grid; grid-template-columns: 74px auto 1fr; align-items: center; gap: 10px; margin-bottom: 7px; }
.axis-name { font-weight: 600; font-size: 14px; color: var(--ink); }
.axis-pips { display: inline-flex; gap: 5px; }
.axis-pip { width: 10px; height: 10px; background: rgba(233,220,196,0.10); border: 1px solid var(--line); transform: rotate(45deg); }
.axis-pip.lit { background: radial-gradient(circle at 35% 30%, #fbeeba, var(--gold)); border-color: var(--gold-dim); box-shadow: 0 0 7px rgba(217,184,102,0.45); }
.axis-word { font-size: 13.5px; font-style: italic; }
@media (max-width: 520px) { .axis-row { grid-template-columns: 66px auto; } .axis-word { grid-column: 1 / -1; margin-left: 0; } }

/* ---- The Gilded Chart — journey interstitial: an atlas leaf illuminates as you set out ---- */
.travel-anim{position:fixed;inset:0;z-index:60;display:grid;place-items:center;cursor:pointer;overflow:hidden;background:radial-gradient(120% 92% at 50% 42%, var(--bg2), var(--bg));opacity:0;transition:opacity .34s ease;}
.travel-anim.in{opacity:1;}
.travel-anim.out{opacity:0;}
.ta-scene{position:absolute;inset:0;width:100%;height:100%;display:block;}

.ta-leaf{transform-box:fill-box;transform-origin:left center;animation:ta-turn 2.24s cubic-bezier(.55,.02,.3,1) both;}

.ta-lattice path{stroke-dashoffset:1;animation:ta-draw 0.99s ease-out both;}
.ta-lattice path:nth-child(1){animation-delay:0.03s;}
.ta-lattice path:nth-child(2){animation-delay:0.14s;}
.ta-lattice path:nth-child(3){animation-delay:0.26s;}
.ta-lattice path:nth-child(4){animation-delay:0.1s;}
.ta-lattice path:nth-child(5){animation-delay:0.21s;}
.ta-lattice path:nth-child(6){animation-delay:0.32s;}
.ta-lstar{opacity:0;animation:ta-fade 0.8s ease-out both;}
.ta-lstar:nth-of-type(1){animation-delay:0.54s;}
.ta-lstar:nth-of-type(2){animation-delay:0.64s;}
.ta-lstar:nth-of-type(3){animation-delay:0.74s;}

.ta-road-line{stroke-dashoffset:1;animation:ta-ink 1.15s cubic-bezier(.4,0,.3,1) both;animation-delay:0.35s;}
.ta-road-base{opacity:0;animation:ta-fade 0.64s ease-out both;animation-delay:0.26s;}

.ta-skyline{opacity:0;animation:ta-fade 0.96s ease-out both;animation-delay:0.45s;}

.ta-cvn{offset-rotate:0deg;offset-anchor:0px 0px;offset-path:path("M18 184 C78 160 118 202 176 178 C236 154 278 180 322 150");animation:ta-march 2.08s cubic-bezier(.35,0,.5,1) both;}
.ta-cvn1{--d0:-4%;--d1:44%;}
.ta-cvn2{--d0:-16%;--d1:32%;animation-delay:0.06s;}
.ta-cvn3{--d0:-28%;--d1:20%;animation-delay:0.13s;}
.ta-cvn4{--d0:-40%;--d1:8%;animation-delay:0.19s;}
.ta-bundle{transform-box:fill-box;transform-origin:center;animation:ta-bob 2.4s ease-in-out infinite;}
.ta-cvn2 .ta-bundle{animation-delay:0.4s;}
.ta-cvn3 .ta-bundle{animation-delay:0.8s;}
.ta-cvn4 .ta-bundle{animation-delay:1.2s;}
.ta-glow{transform-box:fill-box;transform-origin:center;animation:ta-lamp 2.72s ease-in-out infinite;}
.ta-cvn2 .ta-glow{animation-delay:0.64s;}
.ta-cvn3 .ta-glow{animation-delay:1.28s;}
.ta-cvn4 .ta-glow{animation-delay:1.92s;}

.ta-waypoint{offset-rotate:0deg;offset-anchor:0px 0px;offset-path:path("M18 184 C78 160 118 202 176 178 C236 154 278 180 322 150");opacity:0;animation:ta-way 2.24s cubic-bezier(.4,0,.3,1) both;}
.ta-way-star{transform-box:fill-box;transform-origin:center;animation:ta-spin 4.8s linear infinite;}

.ta-mote{transform-box:fill-box;transform-origin:center;animation:ta-drift 6.4s ease-in-out infinite;}
.ta-mote2{animation-duration:8.32s;animation-delay:0.96s;}
.ta-mote3{animation-duration:7.36s;animation-delay:1.76s;}

.ta-frame{opacity:0;animation:ta-fade 0.8s ease-out both;animation-delay:0.16s;}

.ta-iris{pointer-events:none;}
.ta-iris-core{transform-box:fill-box;transform-origin:center;animation:ta-iris 2.24s cubic-bezier(.5,0,.4,1) both;}

.ta-word{position:absolute;left:0;right:0;bottom:14%;display:flex;flex-direction:column;align-items:center;gap:.5em;pointer-events:none;opacity:0;transform:translateY(7px);animation:ta-word 2.24s ease-out both;}
.ta-colophon{width:78px;height:20px;overflow:visible;}
.ta-col-dot{transform-box:fill-box;transform-origin:center;animation:ta-colpulse 3.2s ease-in-out infinite;}
.ta-col-far{animation-delay:0.64s;}
.ta-word-text{font-family:var(--serif);font-size:clamp(15px,2.4vw,21px);color:var(--gold-dim);text-transform:lowercase;font-variant:small-caps;letter-spacing:.3em;text-indent:.3em;text-align:center;text-shadow:0 1px 10px rgba(217,184,102,0.25);}

@keyframes ta-fadein{from{opacity:0;}to{opacity:1;}}
@keyframes ta-fade{from{opacity:0;}to{opacity:1;}}
@keyframes ta-draw{from{stroke-dashoffset:1;}to{stroke-dashoffset:0;}}
@keyframes ta-ink{from{stroke-dashoffset:1;}to{stroke-dashoffset:0;}}
@keyframes ta-march{from{offset-distance:var(--d0);}to{offset-distance:var(--d1);}}
@keyframes ta-bob{0%,100%{transform:translateY(0) scale(.9);}50%{transform:translateY(-2.4px) scale(.9);}}
@keyframes ta-lamp{0%,100%{opacity:.55;transform:scale(.9);}50%{opacity:1;transform:scale(1.18);}}
@keyframes ta-way{0%{offset-distance:32%;opacity:0;}18%{opacity:1;}86%{offset-distance:100%;opacity:1;}100%{offset-distance:100%;opacity:0;}}
@keyframes ta-spin{from{transform:rotate(0);}to{transform:rotate(360deg);}}
@keyframes ta-drift{0%,100%{transform:translate(0,0);opacity:.5;}50%{transform:translate(6px,-4px);opacity:.9;}}
@keyframes ta-iris{0%,66%{transform:scale(0);opacity:0;}74%{opacity:1;}100%{transform:scale(9);opacity:1;}}
@keyframes ta-turn{0%,70%{transform:translateX(0) rotate(0deg);opacity:1;}100%{transform:translateX(48px) rotate(2.4deg);opacity:.12;}}
@keyframes ta-word{0%,36%{opacity:0;transform:translateY(7px);}68%,100%{opacity:1;transform:translateY(0);}}
@keyframes ta-colpulse{0%,100%{opacity:.5;transform:scale(.85);}50%{opacity:1;transform:scale(1.1);}}

@media (prefers-reduced-motion:reduce){
  .ta-leaf,.ta-frame,.ta-skyline,.ta-road-base,.ta-lattice path,.ta-lstar,.ta-bundle,.ta-glow,.ta-mote,.ta-way-star,.ta-col-dot,.ta-word{animation:none !important;}
  .ta-leaf{opacity:1;transform:none;}
  .ta-frame,.ta-skyline,.ta-road-base,.ta-lstar{opacity:1;}
  .ta-lattice path{stroke-dashoffset:0;}
  .ta-road-line{animation:none !important;stroke-dashoffset:0;}
  .ta-cvn{animation:none !important;}
  .ta-cvn1{offset-distance:44%;}.ta-cvn2{offset-distance:34%;}.ta-cvn3{offset-distance:24%;}.ta-cvn4{offset-distance:14%;}
  .ta-waypoint{animation:none !important;offset-distance:100%;opacity:1;}
  .ta-iris-core{animation:none !important;transform:scale(0);opacity:0;}
  .ta-word{opacity:1;transform:none;}
}

/* ---- combat clash: bars flash and shake, damage numbers rise ---- */
.combat.clash .combat-name { animation: clash-title 0.4s ease; }
@keyframes clash-title { 0%, 100% { transform: none; } 30% { transform: translateX(-2px); } 60% { transform: translateX(2px); } }
.cbar { position: relative; }
.cbar-track.hit { animation: bar-hit 0.45s ease; }
.cbar-track.you.hurt { animation: bar-shake 0.45s ease; }
@keyframes bar-hit { 0% { box-shadow: 0 0 0 rgba(255,207,159,0); } 30% { box-shadow: 0 0 16px rgba(255,207,159,0.8); } 100% { box-shadow: none; } }
@keyframes bar-shake { 0%, 100% { transform: translateX(0); } 20% { transform: translateX(-4px); } 40% { transform: translateX(4px); } 60% { transform: translateX(-3px); } 80% { transform: translateX(2px); } }
.cdmg {
  position: absolute; right: 10px; bottom: 2px; font-family: var(--serif); font-weight: 700; font-size: 18px;
  color: var(--red); text-shadow: 0 2px 8px rgba(0,0,0,0.6); pointer-events: none; animation: cdmg-rise 0.9s ease forwards;
}
.cbar:first-of-type .cdmg { color: #ffcf9f; }
@keyframes cdmg-rise { 0% { opacity: 0; transform: translateY(6px) scale(0.8); } 25% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-18px); } }

/* ============ FLOW: small staggered entrances so a screen assembles, not just appears ============ */
@keyframes flow-in { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
/* the play-loop lists cascade in one after another */
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card) {
  animation: flow-in 0.4s cubic-bezier(0.16,1,0.3,1) both;
}
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(2) { animation-delay: 0.045s; }
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(3) { animation-delay: 0.09s; }
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(4) { animation-delay: 0.135s; }
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(5) { animation-delay: 0.18s; }
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(6) { animation-delay: 0.225s; }
:is(.choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row, .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card):nth-child(7) { animation-delay: 0.27s; }
:is(.hub-acts .hub-act, .growth .growth-row):nth-child(8) { animation-delay: 0.315s; }
:is(.hub-acts .hub-act):nth-child(9) { animation-delay: 0.36s; }
:is(.hub-acts .hub-act):nth-child(10) { animation-delay: 0.405s; }
:is(.hub-acts .hub-act):nth-child(11) { animation-delay: 0.45s; }
:is(.hub-acts .hub-act):nth-child(n+12) { animation-delay: 0.49s; }
/* creation lists get a gentle uniform fade (their own screen already rises) */
.paths .path, .talent-grid .talent, .presets .preset { animation: flow-in 0.42s cubic-bezier(0.16,1,0.3,1) both; }
/* the aftermath lines arrive a beat after the outcome text, so a result unfolds */
.feeling, .reveal-line { animation: flow-in 0.55s ease both; animation-delay: 0.16s; }
/* the peripheral verse breathes in (already soft); keep the girih band alive with a faint shimmer */
.girih-band { animation: band-breath 6s ease-in-out infinite; }
@keyframes band-breath { 0%, 100% { opacity: 0.62; } 50% { opacity: 0.82; } }

@media (prefers-reduced-motion: reduce) {
  .choices .choice, .hub-acts .hub-act, .cp-ambitions .cp-amb, .deltas .delta, .trials-list .trial-row, .growth .growth-row,
  .amb-list .amb-row, .shop .shop-row, .ask .ask-row, .inv-grid .inv-card, .paths .path, .talent-grid .talent, .presets .preset,
  .feeling, .reveal-line, .girih-band { animation: none !important; }
  .choice.chosen { animation: none !important; }
  .choices.committing .choice:not(.chosen) { transition: none !important; }
}

/* ---- accounts & saves ---- */
.auth-screen { max-width: 460px; margin: 6vh auto 0; padding: 34px 30px 30px; position: relative; text-align: center; }
.auth-title { font-size: clamp(1.5rem, 4vw, 2rem); color: var(--gold); margin: 0 0 6px; letter-spacing: 0.01em; }
.auth-sub { color: var(--ink-dim, #b7a98a); margin: 0 auto 22px; max-width: 40ch; line-height: 1.5; }
.auth-field { display: block; text-align: left; margin: 0 0 14px; }
.auth-field > span { display: block; font-size: 0.82rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold-dim, #b89b5a); margin: 0 0 5px; }
.auth-input { width: 100%; box-sizing: border-box; padding: 11px 13px; font: inherit; font-size: 1rem; color: var(--ink, #efe4cc);
  background: rgba(20, 15, 8, 0.6); border: 1px solid var(--line, #6a5730); border-radius: 9px; outline: none; transition: border-color 0.2s, box-shadow 0.2s; }
.auth-input:focus { border-color: var(--gold, #d9b866); box-shadow: 0 0 0 3px rgba(217, 184, 102, 0.16); }
.auth-screen .btn { width: 100%; margin-top: 6px; }
.auth-err { color: #e6a07e; background: rgba(120, 40, 30, 0.22); border: 1px solid rgba(200, 90, 60, 0.4); border-radius: 8px; padding: 9px 12px; margin: 0 0 16px; font-size: 0.92rem; }
.auth-alt { margin-top: 18px; font-size: 0.9rem; color: var(--ink-dim, #b7a98a); line-height: 1.7; }
.auth-line { margin-top: 16px; font-size: 0.9rem; color: var(--ink-dim, #b7a98a); }
@media (prefers-reduced-motion: reduce) { .auth-input { transition: none; } }

/* ============================ THE GUIDE (home-village tutorial) & TRAINING ============================ */
/* the guide's topic list — icon + title + a "read" tick that greys once you've heard it */
.guide-topic { align-items: center; }
.guide-topic .ha-icon { flex: 0 0 auto; margin-right: 10px; color: var(--gold-dim); }
.guide-topic .ha-icon svg { width: 18px; height: 18px; display: block; }
.guide-topic.seen { opacity: 0.7; }
.guide-topic.seen .choice-label { color: var(--ink-dim); }
.guide-topic.seen .ha-icon { color: var(--green, #7fb069); }
.guide-topic.seen .choice-tag { color: var(--green, #7fb069); }
.guide-done { margin-top: 4px; border-style: dashed; }
.guide-try { margin: 4px 0 0; padding: 9px 13px; border-left: 2px solid var(--gold-dim); background: rgba(217,184,102,0.07); border-radius: 0 8px 8px 0; color: var(--ink-dim); font-size: 0.92rem; font-style: italic; }
.guide-verse, .mastery .guide-verse { margin: 12px 0 2px; padding: 10px 14px; border-radius: 10px; background: rgba(0,0,0,0.2); border: 1px solid var(--line); min-height: 0; }
.guide-verse:empty { display: none; }
.guide-verse .pv-text { display: block; font-family: var(--serif); font-size: 1.02rem; color: #e9dcbb; line-height: 1.6; }
.guide-verse .pv-ref { display: block; margin-top: 5px; font-size: 0.76rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold-dim); }

/* the master's screen — a stat token, a progress line, and a highlighted "train" option */
.scene-loc-note { margin: 0 0 6px; font-size: 0.86rem; font-style: italic; }
.mentor-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 16px; margin: 6px 0 14px; }
.mentor-stat { display: inline-flex; align-items: center; gap: 8px; font-size: 0.92rem; color: var(--ink); }
.mentor-stat svg { width: 22px; height: 22px; color: var(--sc, var(--gold)); filter: drop-shadow(0 0 6px color-mix(in srgb, var(--sc, var(--gold)) 45%, transparent)); }
.mentor-stat b { color: var(--sc, var(--gold)); font-variant-numeric: tabular-nums; }
.mentor-prog { font-size: 0.8rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-faint); }
.mentor-train { border-left: 3px solid var(--gold); }
.mentor-train .choice-tag { color: #8fd0b8; }

/* mastery capstone — a warm crown on the moment */
.scene-view.mastery .scene-title { color: #efd794; }
.mastery-kicker { font-family: var(--serif); font-size: 1.05rem; color: #efd794; margin: 2px 0 10px; text-shadow: 0 0 16px rgba(217,184,102,0.3); }

/* the "Seek a teacher" list — each master a row with their figure, trade, and your progress */
.trainers-scene .trainer-list { gap: 9px; }
.trainer-row { align-items: center; gap: 12px; text-align: left; }
.tr-fig { flex: 0 0 auto; width: 40px; height: 52px; display: inline-flex; align-items: flex-end; justify-content: center; }
.tr-fig .npc-svg { width: 100%; height: 100%; overflow: visible; }
.tr-body { flex: 1 1 auto; display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tr-sub { font-size: 0.8rem; line-height: 1.4; }
.tr-stat { flex: 0 0 auto; display: inline-flex; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--sc, var(--gold)); }
.tr-stat svg { width: 18px; height: 18px; color: var(--sc, var(--gold)); }
.tr-short { letter-spacing: 0.05em; }
.tr-badge { display: inline-block; margin-left: 4px; font-size: 9.5px; letter-spacing: 0.06em; text-transform: uppercase; padding: 2px 7px; border-radius: 20px; border: 1px solid var(--line); background: rgba(0,0,0,0.28); color: var(--ink-dim); }
.tr-badge.new { color: #8fd0b8; border-color: rgba(127,191,168,0.4); }
.tr-badge.mastered { color: #efd794; border-color: rgba(217,184,102,0.55); background: rgba(217,184,102,0.14); }

/* the stage figures: the guide draws a soft warm attention; a master a quiet teal one */
.npc-fig.npc-guide .npc-svg { filter: drop-shadow(0 0 10px rgba(240,214,150,0.4)); }
.npc-fig.npc-mentor::before { content: ""; position: absolute; top: -7px; left: 50%; transform: translateX(-50%); width: 6px; height: 6px; border-radius: 50%; background: #7fbfa8; box-shadow: 0 0 7px 2px rgba(127,191,168,0.6); }
/* A teacher and the village guide are worth finding across a place, so they keep a head start on the
   ramp -- but they no longer sit permanently at full, which is what put 20 of the 42 always-on names
   on the screen and is what the whole collision problem was made of. */
.place-props .npc-fig.npc-mentor .npc-name,
.place-props .npc-fig.npc-guide .npc-name { opacity: calc(var(--near, 0) * 2.4 - 0.75); }
@media (prefers-reduced-motion: reduce) { .mentor-stat svg { filter: none; } }

/* the continuity line above a re-meeting with a travelling figure — a quiet "we have met before" */
.reunion-note { margin: 0 0 10px; padding: 7px 12px 7px 13px; border-left: 2px solid var(--gold-dim); background: linear-gradient(90deg, rgba(217,184,102,0.09), transparent); border-radius: 0 8px 8px 0; font-size: 0.86rem; font-style: italic; color: #cbb98d; letter-spacing: 0.01em; }

/* ============================ OPENING CINEMATIC (slice 1) ============================ */
/* The start screen plays as a staged sequence: a dawn curtain lifts, the road and sky settle,
   the lone wayfarer walks the road, and the title + testimony illuminate in turn. Skippable;
   plays once per session (return visits show the settled frame). Reduced-motion => settled frame. */
/* taller hero for cinematic presence */

.cine .cine-curtain { animation: cine-curtain 1.5s ease forwards; }
100% { opacity: 0; } }

/* the walking wayfarer on the road */
50% { transform: scale(2.6) rotate(1.4deg); } }
50% { opacity: 1; } }

/* the staged entrance timeline (first play only) */
}
to { opacity: 1; transform: none; } }
60% { opacity: 1; } 100% { opacity: 1; transform: none; } }
100% { opacity: 1; transform: none; } }

.cine .start-hero { opacity: 0; animation: cine-in 1.4s ease 0.2s forwards; }
.cine .ia-stars { opacity: 0; animation: cine-in 1.3s ease 0.4s forwards; }
.cine .ia-glow,
.cine .ia-sun { transform-box: fill-box; transform-origin: center; opacity: 0; animation: cine-dawn 1.5s cubic-bezier(0.16,1,0.3,1) 0.7s forwards, ia-breathe 6s ease-in-out 2.2s infinite; }
.cine .ia-skyline { opacity: 0; animation: cine-in 1.2s ease 1.5s forwards; }
.cine .ia-road { opacity: 0; animation: cine-in 1.4s ease 2.0s forwards, ia-road-shimmer 4.5s ease-in-out 3.4s infinite; }
.cine .ia-wf-in { opacity: 0; animation: cine-walk-in 1.5s cubic-bezier(0.16,1,0.3,1) 2.5s forwards; }

/* the body reveals in a deliberate cadence after the scene settles */
.cine .start-head { opacity: 0; animation: cine-rise 1.0s cubic-bezier(0.16,1,0.3,1) 3.5s forwards; }
.cine .start-intro { opacity: 0; animation: cine-rise 0.9s cubic-bezier(0.16,1,0.3,1) 4.2s forwards; }
.cine .start-body > .girih-band { opacity: 0; animation: cine-in 0.9s ease 4.6s forwards; }
.cine .ending-verse { opacity: 0; animation: cine-rise 0.9s cubic-bezier(0.16,1,0.3,1) 4.9s forwards; }
.cine .start-question { opacity: 0; animation: cine-rise 0.9s cubic-bezier(0.16,1,0.3,1) 5.3s forwards; }
.cine .start-choices { opacity: 0; animation: cine-rise 0.9s cubic-bezier(0.16,1,0.3,1) 5.7s forwards; }
.cine .auth-line { opacity: 0; animation: cine-in 0.9s ease 6.2s forwards; }



/* ============================ STATS REWORK (slice) ============================ */
/* the rail keeps only the body's living meters; STAMINA joins HP/ARM/SPR */
.meter-pip.stamina { border-color: rgba(127,191,168,0.32); }
.meter-pip.stamina .pip-icon { color: #8fd0b8; }
.meter-pip.stamina .pip-bar > i { background: linear-gradient(90deg, #5a9e86, #8fd0b8); }

/* the seven strengths now live in the Character sheet, laid out as a compact grid of virtue tokens */
.char-stats .char-stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(66px, 1fr)); gap: 10px 6px; margin-top: 8px; }
.char-stats .stat-pip { padding: 7px 4px 6px; border-radius: 9px; background: rgba(233,220,196,0.03); border: 1px solid var(--line); }
.char-stats .stat-pip .pip-bar { width: 100%; max-width: 62px; }
.char-stats .stat-pip .pip-val { font-size: 17px; }

/* ============================ LORE BOOKS (reader + library) ============================ */
.reader { max-width: 620px; margin: 0 auto; text-align: center; padding: 8px 4px 24px; }
.reader-found { font-size: 0.82rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 12px; }
.book-art { width: clamp(150px, 42vw, 220px); height: auto; display: block; margin: 0 auto 14px; filter: drop-shadow(0 6px 18px rgba(0,0,0,0.5)); }
.reader-title { font-family: var(--serif); font-size: clamp(1.3rem, 5vw, 1.7rem); color: #efd794; margin: 0 0 14px; }
.reader-body { text-align: left; max-width: 56ch; margin: 0 auto; }
.reader-body p { color: var(--ink); line-height: 1.75; margin: 0 0 14px; }
.reader-note { text-align: left; max-width: 56ch; margin: 6px auto 20px; padding: 9px 14px; border-left: 2px solid var(--gold-dim); background: rgba(217,184,102,0.07); border-radius: 0 8px 8px 0; font-style: italic; color: var(--ink-dim); }
.reader .btn { margin-top: 4px; }

.lib-book { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; width: 100%; text-align: left; padding: 9px 12px; margin-bottom: 7px; border: 1px solid var(--line); border-radius: 9px; background: rgba(233,220,196,0.03); cursor: pointer; transition: border-color 0.2s, background 0.2s; }
.lib-book:hover { border-color: var(--gold-dim); background: rgba(217,184,102,0.06); }
.lib-book b { color: var(--ink); font-weight: 500; }
.lib-book .muted { font-size: 0.82rem; }

/* ============================ XP / PERKS ============================ */
.btn.tiny { font-size: 0.8rem; padding: 5px 12px; }
.levelup-note { margin: 6px 0 0; padding: 9px 14px; border-radius: 10px; background: linear-gradient(90deg, rgba(217,184,102,0.14), rgba(217,184,102,0.03)); border: 1px solid rgba(217,184,102,0.4); color: #efd794; font-size: 0.92rem; }

/* per-stat XP progress under each strength (in the Character sheet) */
.char-stats .pip-xp { width: 100%; max-width: 62px; height: 2px; margin-top: 3px; border-radius: 2px; background: rgba(233,220,196,0.08); overflow: hidden; }
.char-stats .pip-xp > i { display: block; height: 100%; background: color-mix(in srgb, var(--sc, var(--gold)) 55%, transparent); width: 0%; }

/* the Level block */
.char-level .lvl-xpbar { height: 7px; border-radius: 6px; background: rgba(233,220,196,0.08); overflow: hidden; margin: 8px 0 6px; }
.char-level .lvl-xpbar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--gold-dim), var(--gold)); }
.char-level .lvl-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px; }

/* the perks screen */
.perks-screen { max-width: 640px; margin: 0 auto; }
.perks-head { text-align: center; margin-bottom: 18px; }
.perks-level { font-family: var(--serif); font-size: 1.4rem; color: #efd794; }
.perks-xpbar { height: 8px; border-radius: 6px; background: rgba(233,220,196,0.08); overflow: hidden; margin: 8px auto; max-width: 320px; }
.perks-xpbar > i { display: block; height: 100%; background: linear-gradient(90deg, var(--gold-dim), var(--gold)); }
.perks-points { font-size: 0.9rem; color: var(--ink-dim); }
.perks-points.have { color: #efd794; }
.perks-sec { margin-bottom: 18px; }
.perks-sec-h { font-size: 0.8rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 9px; }
.perk-owned { padding: 6px 0; border-bottom: 1px solid var(--line); }
.perk-owned b { color: #efd794; }
.perk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 10px; }
.perk-card { display: flex; flex-direction: column; gap: 5px; text-align: left; padding: 12px 14px; border: 1px solid var(--line); border-radius: 11px; background: rgba(233,220,196,0.03); cursor: pointer; transition: border-color 0.2s, background 0.2s, transform 0.15s; }
.perk-card:hover:not(.locked) { border-color: var(--gold-dim); background: rgba(217,184,102,0.06); transform: translateY(-1px); }
.perk-card.locked { opacity: 0.55; cursor: not-allowed; }
.perk-name { font-family: var(--serif); font-size: 1.05rem; color: var(--ink); }
.perk-desc { font-size: 0.86rem; color: var(--ink-dim); line-height: 1.5; }
.perk-foot { display: flex; flex-wrap: wrap; gap: 6px 10px; margin-top: 2px; font-size: 0.72rem; letter-spacing: 0.04em; }
.perk-cat { text-transform: uppercase; color: var(--gold-dim); }
.perk-scale { color: #8fd0b8; }
.perk-gate { color: var(--ink-faint); margin-left: auto; }

/* ============================ THE APPROACH (walk-up -> action ring -> dialogue) ============================ */
.approach-hint { text-align: center; color: var(--ink-dim); font-style: italic; margin: 2px 0 16px; font-size: 0.92rem; }
.action-ring { display: flex; flex-wrap: wrap; justify-content: center; gap: 10px; }
.ring-act { display: flex; flex-direction: column; align-items: center; gap: 6px; min-width: 84px; padding: 13px 16px; border: 1px solid var(--line); border-radius: 13px; background: rgba(233,220,196,0.03); cursor: pointer; transition: border-color 0.2s, background 0.2s, transform 0.15s; }
.ring-act:hover { border-color: var(--gold-dim); background: rgba(217,184,102,0.06); transform: translateY(-2px); }
.ring-ic { display: inline-flex; color: var(--gold-dim); }
.ring-ic svg { width: 23px; height: 23px; display: block; }
.ring-l { font-family: var(--serif); font-size: 0.95rem; color: var(--ink); letter-spacing: 0.01em; }
.ring-act[data-a="speak"] { border-color: rgba(217,184,102,0.42); background: rgba(217,184,102,0.05); }
.ring-act[data-a="speak"] .ring-ic { color: var(--gold); }
.ring-act[data-a="give"] .ring-ic { color: #e2879e; }
.ring-act[data-a="trade"] .ring-ic { color: var(--gold); }

/* ============================ COMBAT CONTROLS (strike zones + charge + guard) ============================ */
/* three strike zones overlaid on the foe — press-and-hold to charge a heavier blow, release to strike */
/* where you mean to land the blow: three bands over the foe. They must be legible AT REST — a phone has
   no hover, so a player who never mouses over them would otherwise never learn they are there. */
/* the container is only a coordinate space; the boxes inside it take the presses */
.combat-stage .strike-zones { z-index: 4; touch-action: none; pointer-events: none; }
.strike-zone {
  position: absolute; cursor: pointer; overflow: hidden; pointer-events: auto;
  /* an EVEN border now. The heavier left edge was the column's inner edge, the side facing the foe
     across the stage; there is no such side once the box is standing on him. */
  border: 1px solid rgba(217,140,115,0.34); border-radius: 9px;
  /* LIGHTER AT REST THAN IT WAS. It used to be a panel in empty stage beside the foe, where a wash
     cost nothing; it is over the creature now, and a fill heavy enough to be a panel is a fill heavy
     enough to flatten the silhouette you are aiming at. */
  background: linear-gradient(90deg, rgba(217,140,115,0.11), rgba(217,140,115,0.03) 52%, rgba(217,140,115,0.01));
  /* THE LABEL WRAPS RATHER THAN BEING LAID OUT BY A RULE THAT GUESSES THE BOX. These boxes are cut
     to anatomy, so their shapes run from a beast's long leg strip to its narrow head to a man's whole
     torso, and no single choice of row-or-column fits all of them: a row is clipped in the narrow
     ones and a column does not fit the flat ones. `flex-wrap` lets the box answer for itself -- one
     line where there is room, the armour word dropping under the part name where there is not. The
     separator is what stops a row reading as "LEGSBARE", the jam that made this a column originally.
     Caught on the bench: at a smaller stage a wolf's head box is 55px and read "BARE" alone. */
  display: flex; flex-flow: row wrap; align-items: flex-end; align-content: flex-end;
  justify-content: flex-end; gap: 0 5px; padding: 3px 7px;
  transition: background 0.12s, border-color 0.12s, box-shadow 0.12s;
  -webkit-tap-highlight-color: transparent;
}
/* THE AIM MARK IS IN THE MIDDLE OF THE BOX, because the box is now standing on the part of him it
   names: the centre of it is the thing you are aiming at. It used to sit on the column's inner edge
   with a short leader line reaching toward a foe who was somewhere else on the stage -- that line is
   deleted rather than moved, since a box drawn on the animal has nothing left to point at. */
.strike-zone::before {
  content: ""; position: absolute; left: 50%; top: 50%; width: 11px; height: 11px; margin: -5.5px 0 0 -5.5px;
  border: 1px solid rgba(217,140,115,0.75); border-radius: 50%; box-shadow: inset 0 0 0 3px rgba(20,17,13,0.45);
  pointer-events: none; opacity: 0; transition: opacity 0.12s;
}
/* AND IT ONLY SHOWS WHEN YOU ARE ON IT. Three permanent rings on one animal is clutter, and the
   mark existed because the old panel sat in empty stage where nothing said "aim". The box is
   standing on him now, so at rest an outline is enough and the sight appears where the blow will. */
.strike-zone:hover::before, .strike-zone:active::before, .strike-zone.full::before { opacity: 1; }
.strike-zone:hover, .strike-zone:active {
  background: linear-gradient(90deg, rgba(217,140,115,0.34), rgba(217,140,115,0.12) 60%, transparent);
  border-color: rgba(217,140,115,0.85); box-shadow: inset 0 0 18px rgba(217,140,115,0.16);
}
.strike-zone.full { background: rgba(217,140,115,0.42); border-color: var(--red); box-shadow: inset 0 0 24px rgba(217,140,115,0.3), 0 0 14px rgba(217,140,115,0.25); }
/* the others in a band stand back: dimmer and a touch hazed, so the near one still reads as the one
   you are trading blows with rather than the group becoming a wall of identical silhouettes */
.foe-extra { opacity: 0.5; }
.sz-tag { font-size: 9.5px; letter-spacing: 0.14em; text-transform: uppercase; color: #eab9a4; opacity: 0.72; transition: opacity 0.12s; pointer-events: none; text-shadow: 0 1px 3px rgba(0,0,0,0.8); white-space: nowrap; }
.sz-tag::after { content: "·"; margin-left: 5px; opacity: 0.55; }
.strike-zone:hover .sz-tag, .strike-zone:active .sz-tag { opacity: 1; }
/* what is guarding this line. Choosing a zone was a blind guess before: the trade (high hits harder
   but truer is lower, low is safer but softer) and the man's own gear were both invisible. */
.sz-arm { font-size: 8.5px; letter-spacing: 0.1em; text-transform: uppercase; opacity: 0.66;
  color: #cfe0ea; pointer-events: none; text-shadow: 0 1px 3px rgba(0,0,0,0.85); white-space: nowrap; }
.sz-arm.hard { color: #9fb4d0; }
.sz-arm.bare { color: #a9d497; }
.strike-zone:hover .sz-arm, .strike-zone:active .sz-arm { opacity: 1; }
/* drawn, not typed: the shield glyphs are not in the stack's fonts and came out as stray chevrons */
.sz-shield { display: inline-block; width: 7px; height: 9px; margin-left: 5px; vertical-align: -1px;
  background: #8fa6b4; border-radius: 2px 2px 62% 62%; opacity: 0.5; }
.sz-shield.often { background: #cfe0ea; opacity: 0.95; }
/* the charge rail sits visible-but-empty so the hold is discoverable without being told twice */
.combat-stage .charge-meter { position: absolute; left: 6%; right: 6%; bottom: 19px; height: 6px; border-radius: 4px; background: rgba(233,220,196,0.09); box-shadow: inset 0 0 0 1px rgba(217,184,102,0.16); overflow: hidden; z-index: 6; opacity: 0.5; transition: opacity 0.12s, box-shadow 0.12s; }
.combat.charging .charge-meter { opacity: 1; box-shadow: inset 0 0 0 1px rgba(217,140,115,0.45), 0 0 12px rgba(217,140,115,0.2); }
.charge-fill { display: block; height: 100%; width: 0%; background: linear-gradient(90deg, var(--gold), var(--red)); }
/* the bar travels up and back now, so the peak has to be legible at a glance: that instant is the
   whole timing window, and without a tell you are guessing where the top was */
.charge-fill.peak { box-shadow: 0 0 12px 2px rgba(232,180,92,0.85); filter: brightness(1.25); }
/* the label under the rail names the hold once, quietly, and gets out of the way while you are holding */
/* it used to be the last row of the zone column; the zones sit on the foe now, so it stands on its
   own above the charge rail it is talking about */
.charge-hint { position: absolute; right: 6%; bottom: 29px; z-index: 6; font-size: 8.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); pointer-events: none; transition: opacity 0.15s; text-shadow: 0 1px 3px rgba(0,0,0,0.85); }
.combat.charging .charge-hint { opacity: 0; }

/* the control area: a primary Guard, a compact utility row, the stance toggle */
.combat-ctrl { margin-top: 12px; }
.btn.cguard { display: flex; flex-direction: column; align-items: center; gap: 2px; width: 100%; padding: 12px; background: linear-gradient(180deg, rgba(127,168,201,0.15), rgba(127,168,201,0.05)); border-color: rgba(127,168,201,0.42); }
.btn.cguard:hover { border-color: var(--blue); transform: translateY(-1px); }
.cg-l { font-family: var(--serif); font-size: 1.12rem; color: #cfe0f0; }
.cg-m { font-size: 0.78rem; color: var(--ink-dim); letter-spacing: 0.02em; }
.cbtn-row { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.cbtn-row .cmove { flex: 1 1 auto; }
/* A move that carries a meta line must STACK it under the label. The stacking rule was scoped to
   .combat-acts, and this row is not that row, so the two spans ran together inline and read as
   "Spare himlet him live - honor over a kill". Centred, because Guard sits directly above these and
   the stance pills directly below, and both are centred. Buttons with no meta (Break away) keep the
   plain centred label they already had. */
.cbtn-row .cmove:has(.mv-meta) { display: flex; flex-direction: column; align-items: center; gap: 2px; text-align: center; }
.cmove.small { padding: 8px 12px; }

/* ================= cinematic conversation ================= */
/* the opener is no longer a paragraph you read at once: it arrives as beats, speech tied to the person */
/* the dialogue is not loose prose on the page: it has its own place, framed like a page of a manuscript,
   so the space is recognizable as where speech happens even before the first line lands */
.dialogue {
  position: relative; margin: 4px 0 18px; padding: 22px 20px 22px; border-radius: 15px; min-height: 104px;
  background-color: rgba(12,10,8,0.5);
  background-image:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='58' height='58'%3E%3Cg fill='none' stroke='%23e3c583' stroke-opacity='0.055' stroke-width='1'%3E%3Crect x='21' y='21' width='16' height='16'/%3E%3Crect x='21' y='21' width='16' height='16' transform='rotate(45 29 29)'/%3E%3C/g%3E%3C/svg%3E"),
    linear-gradient(180deg, rgba(40,31,20,0.42), rgba(16,13,10,0.36));
  border: 1px solid rgba(217,184,102,0.16);
  box-shadow: inset 0 1px 0 rgba(233,220,196,0.05), inset 0 -26px 44px rgba(0,0,0,0.34), 0 5px 18px rgba(0,0,0,0.26);
}
/* a hairline rule inside the frame, doubled at the head where the medallion sits */
.dialogue::before {
  content: ""; position: absolute; left: 12px; right: 12px; top: 8px; height: 0;
  border-top: 1px solid rgba(217,184,102,0.2); pointer-events: none;
}
.dialogue::after {
  content: ""; position: absolute; left: 12px; right: 12px; bottom: 8px; height: 0;
  border-top: 1px solid rgba(217,184,102,0.11); pointer-events: none;
}
.dlg-chrome { position: absolute; inset: 0; pointer-events: none; }
/* corner brackets — the frame's four marks, drawn short so the box reads as a plate, not a text box */
.dlg-c { position: absolute; width: 15px; height: 15px; border: 0 solid rgba(217,184,102,0.5); }
.dlg-c.tl { top: 5px; left: 5px; border-top-width: 1px; border-left-width: 1px; border-radius: 8px 0 0 0; }
.dlg-c.tr { top: 5px; right: 5px; border-top-width: 1px; border-right-width: 1px; border-radius: 0 8px 0 0; }
.dlg-c.bl { bottom: 5px; left: 5px; border-bottom-width: 1px; border-left-width: 1px; border-radius: 0 0 0 8px; }
.dlg-c.br { bottom: 5px; right: 5px; border-bottom-width: 1px; border-right-width: 1px; border-radius: 0 0 8px 0; }
/* the khatam medallion sits astride the top edge, cutting the rule — the mark of a spoken passage */
.dlg-mark {
  position: absolute; top: -11px; right: 20px; width: 24px; height: 24px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg); border: 1px solid rgba(217,184,102,0.34); color: var(--gold-dim);
  box-shadow: 0 0 12px rgba(217,184,102,0.14);
}
.dlg-mark svg { width: 15px; height: 15px; }
.scene-view .dlg-mark { background: #16120d; }
/* three lamps at the foot: they breathe while there is still more to be said, and go out when it is said */
.dlg-more { position: absolute; left: 18px; bottom: 13px; display: flex; gap: 5px; transition: opacity 0.4s ease; }
.dlg-more i { width: 4px; height: 4px; border-radius: 50%; background: var(--gold-dim); opacity: 0.35; animation: dlg-lamp 1.5s ease-in-out infinite; }
.dlg-more i:nth-child(2) { animation-delay: 0.22s; }
.dlg-more i:nth-child(3) { animation-delay: 0.44s; }
@keyframes dlg-lamp { 0%, 100% { opacity: 0.2; transform: none; } 45% { opacity: 0.95; transform: translateY(-2px); } }
.dialogue.said .dlg-more { opacity: 0; }
.dialogue.said { border-color: rgba(217,184,102,0.22); }
.beat { font-family: var(--serif); font-size: 15.5px; line-height: 1.62; color: #d8cdb4; margin: 0 0 12px; max-width: 66ch;
  visibility: hidden; opacity: 0; }
.beat.shown { visibility: visible; opacity: 1; animation: beat-in 0.5s cubic-bezier(0.16,1,0.3,1) both; }
.beat:last-child { margin-bottom: 0; }
.dialogue .beat { position: relative; z-index: 1; }
@keyframes beat-in { from { opacity: 0; transform: translateY(7px); } to { opacity: 1; transform: none; } }
/* a spoken line belongs to the figure standing across from you: warmer, set in, marked */
.beat-speech {
  position: relative; margin-left: 14px; padding: 9px 14px 9px 15px; border-radius: 3px 11px 11px 3px;
  border-left: 2px solid rgba(217,184,102,0.5); background: linear-gradient(90deg, rgba(217,184,102,0.10), rgba(217,184,102,0.02) 62%, transparent);
  color: #ecdcb6; font-style: italic; letter-spacing: 0.005em;
}
.beat-speech::before { content: ""; position: absolute; left: -1px; top: -9px; width: 0; height: 0;
  border-left: 7px solid rgba(217,184,102,0.5); border-top: 9px solid transparent; }
/* the drop-cap opens the exchange once, on its first narrated beat — not on every line */
.beat-lead::first-letter {
  font-family: var(--serif); font-size: 3.1em; line-height: 0.82; float: left;
  margin: 0.04em 0.10em 0 0; color: var(--gold); text-shadow: 0 1px 0 rgba(0,0,0,0.45);
}
.scene.inward .beat-lead::first-letter { color: #9db4d6; }
.scene.doubt .beat-lead::first-letter { color: #c7b8e0; }
/* the figure stirs while they are speaking */
.enc-npc-wrap.speaking { animation: fig-speak 1.05s ease-in-out; }
@keyframes fig-speak {
  0% { transform: none; filter: drop-shadow(0 4px 7px rgba(0,0,0,0.55)); }
  35% { transform: translateY(-2px) scale(1.014); filter: drop-shadow(0 5px 12px rgba(217,184,102,0.32)); }
  100% { transform: none; filter: drop-shadow(0 4px 7px rgba(0,0,0,0.55)); }
}
/* your options wait while they speak, then open — clicking through them early just skips the exchange */
.choices.pending { opacity: 0.22; pointer-events: none; filter: saturate(0.55); }
.choices { transition: opacity 0.45s ease, filter 0.45s ease; }
/* facing a person, the options are not a menu but the things you might say back */
.choices.as-replies .choice { border-left-width: 2px; border-left-color: rgba(217,184,102,0.42); padding-left: 34px; }
.choices.as-replies .choice::before {
  content: "—"; position: absolute; left: 15px; top: 50%; transform: translateY(-50%); z-index: 1;
  color: var(--gold-dim); font-family: var(--serif); font-size: 15px; opacity: 0.72;
}
.choices.as-replies .choice:hover:not(.locked)::before { color: var(--gold); opacity: 1; }
.choices.as-replies .choice-label { font-style: italic; }
@media (prefers-reduced-motion: reduce) {
  .beat.shown { animation: none; }
  .dlg-more i { animation: none; }
  .enc-npc-wrap.speaking { animation: none; }
}
.beat-attrib { font-style: normal; color: var(--ink-dim); font-size: 0.88em; }
.beat-speech.cont { margin-top: -4px; }
.beat-speech.cont::before { display: none; }

/* ================= the result, played in the scene ================= */
/* an outcome keeps the stage it happened on: the person reacts, what you gained rises off the ground */
.outcome-view .scene.outcome { max-width: none; margin: 0; }
.outcome-stage .result-flag {
  position: absolute; left: 50%; top: 12px; transform: translateX(-50%); z-index: 6;
  padding: 5px 15px; border-radius: 999px; font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase;
  background: rgba(12,10,8,0.72); border: 1px solid rgba(217,184,102,0.34); color: var(--gold);
  animation: flag-drop 0.5s cubic-bezier(0.16,1,0.3,1) both;
}
.outcome-stage .result-flag.ok { border-color: rgba(127,176,105,0.6); color: var(--green); }
.outcome-stage .result-flag.no { border-color: rgba(217,140,115,0.6); color: var(--red); }
@keyframes flag-drop { from { opacity: 0; transform: translate(-50%, -10px); } to { opacity: 1; transform: translate(-50%, 0); } }
/* the gains rise off the ground of the scene rather than listing under a paragraph */
.stage-deltas {
  position: absolute; left: 10px; right: 10px; bottom: 10px; z-index: 6;
  display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; pointer-events: none;
}
.stage-deltas .delta {
  background: rgba(12,10,8,0.74); border: 1px solid rgba(217,184,102,0.22); border-radius: 999px;
  padding: 4px 11px; font-size: 11.5px; letter-spacing: 0.01em; backdrop-filter: blur(2px);
  animation: delta-rise 0.55s cubic-bezier(0.16,1,0.3,1) both;
}
.stage-deltas .delta.up { color: #a8d48d; border-color: rgba(127,176,105,0.38); }
.stage-deltas .delta.down { color: #e8a891; border-color: rgba(217,140,115,0.38); }
@keyframes delta-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
/* the person opposite you answers the result with their whole body */
.outcome-stage.react-ok .enc-npc-wrap { animation: fig-fade 0.7s 0.24s ease both, react-ok 1.1s 0.5s ease both; }
.outcome-stage.react-no .enc-npc-wrap { animation: fig-fade 0.7s 0.24s ease both, react-no 0.9s 0.5s ease both; }
@keyframes react-ok {
  0% { transform: none; } 30% { transform: translateY(-5px) rotate(-1.5deg); }
  60% { transform: translateY(0) rotate(0.8deg); } 100% { transform: none; }
}
@keyframes react-no {
  0% { transform: none; } 22% { transform: translateX(7px) rotate(2.5deg); }
  50% { transform: translateX(-3px) rotate(-1deg); } 100% { transform: none; }
}
.outcome-stage.react-no .scene-art { animation: stage-reveal 0.8s ease both, stage-jolt 0.5s 0.45s ease both; }
@keyframes stage-jolt { 0%, 100% { transform: none; } 30% { transform: translateX(-4px); } 65% { transform: translateX(3px); } }
/* the one way on: a single wide action, not a text link at the end of a page */
.onward { transition: opacity 0.45s ease, filter 0.45s ease; }
.onward.pending { opacity: 0.22; pointer-events: none; filter: saturate(0.55); }
.outcome-view .walk-on { width: 100%; padding: 14px 18px; font-size: 16px; }
.outcome-view .reveal-line, .outcome-view .feeling { margin: 0 0 12px; }
@media (prefers-reduced-motion: reduce) {
  .outcome-stage .result-flag, .stage-deltas .delta,
  .outcome-stage.react-ok .enc-npc-wrap, .outcome-stage.react-no .enc-npc-wrap,
  .outcome-stage.react-no .scene-art { animation: none; }
}

/* ================= quarters and nooks: a town you can walk around in ================= */
/* the corners of this place, as a strip you move along — walking between them costs no day */
.quarter-strip { display: flex; gap: 7px; margin: 10px 0 8px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: none; }
.quarter-strip::-webkit-scrollbar { display: none; }
.quarter {
  flex: 1 1 0; min-width: 74px; display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 8px 6px 7px; border-radius: 11px; cursor: pointer;
  background: linear-gradient(180deg, rgba(34,29,22,0.7), rgba(20,17,13,0.6));
  border: 1px solid var(--line); border-bottom: 2px solid rgba(217,184,102,0.12);
  color: var(--ink-dim); transition: color 0.2s, border-color 0.2s, background 0.2s, transform 0.18s;
}
.quarter:hover { color: var(--ink); border-color: var(--gold-dim); transform: translateY(-1px); }
.quarter.here {
  color: var(--gold); border-color: rgba(217,184,102,0.45); border-bottom-color: var(--gold);
  background: linear-gradient(180deg, rgba(217,184,102,0.13), rgba(20,17,13,0.6));
  box-shadow: 0 0 0 1px rgba(217,184,102,0.1), inset 0 0 18px rgba(217,184,102,0.07);
}
.q-icon { display: block; width: 21px; height: 21px; }
.q-icon svg { width: 100%; height: 100%; }
.q-label { font-size: 10px; letter-spacing: 0.11em; text-transform: uppercase; white-space: nowrap; }
/* which corner you are standing in, said beside the place-name */
.stage-quarter { display: block; font-family: var(--sans); font-size: 11.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--gold-dim); margin-top: 3px; }

/* a nook: something on the scene you could walk straight past, and mostly would */
.nook-layer { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.nook-mark {
  position: absolute; transform: translate(-50%, -50%); pointer-events: auto; cursor: pointer;
  background: none; border: 0; padding: 9px; display: flex; align-items: center; gap: 7px;
  -webkit-tap-highlight-color: transparent;
}
.nook-glint {
  display: block; width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto;
  background: radial-gradient(circle, rgba(255,240,200,0.95) 0%, rgba(217,184,102,0.55) 45%, rgba(217,184,102,0) 72%);
  box-shadow: 0 0 10px rgba(217,184,102,0.55);
  animation: nook-breathe 3.1s ease-in-out infinite;
}
.nook-glint.big { width: 20px; height: 20px; }
/* a faint ring so a mark reads as a place to look, not a stray highlight in the art */
.nook-mark::before {
  content: ""; position: absolute; left: 9px; top: 50%; width: 22px; height: 22px; margin: -11px 0 0 -5.5px;
  border: 1px solid rgba(217,184,102,0.28); border-radius: 50%; pointer-events: none;
  animation: nook-ring 3.1s ease-in-out infinite;
}
@keyframes nook-ring { 0%, 100% { opacity: 0.25; transform: scale(0.82); } 50% { opacity: 0.6; transform: scale(1.06); } }
.nook-mark:hover::before { opacity: 0.85; animation: none; transform: scale(1.1); }
@keyframes nook-breathe { 0%, 100% { opacity: 0.34; transform: scale(0.85); } 50% { opacity: 1; transform: scale(1.15); } }
.nook-mark.noticed .nook-glint { background: radial-gradient(circle, rgba(255,240,200,0.95) 0%, rgba(217,184,102,0.7) 50%, rgba(217,184,102,0) 74%); animation-duration: 2.2s; }
.nook-name {
  font-size: 10.5px; letter-spacing: 0.05em; color: #f0e3c2; white-space: nowrap;
  background: rgba(12,10,8,0.78); border: 1px solid rgba(217,184,102,0.26); border-radius: 999px; padding: 3px 10px;
  opacity: 0; transform: translateX(-4px); transition: opacity 0.18s, transform 0.18s; pointer-events: none;
}
/* A NAME BELONGS TO WHAT YOU ARE NEAR. `.noticed` used to light a nook's name permanently, so every
   corner you had ever glanced at went on shouting across the scene: 4 labels up at once in the busiest
   places, and the crowding is what made them collide at all. A noticed nook keeps its GLINT -- the
   mark that says something is here -- and says its name when you are at it, or when you point at it.
   Measured: this is the difference between up to 4 labels at once and about one. */
.nook-mark:hover .nook-name, .nook-mark:focus-visible .nook-name, .place-prop.in-reach .nook-name { opacity: 1; transform: none; }
.nook-mark:hover .nook-glint { animation: none; opacity: 1; transform: scale(1.3); }
/* standing over the thing itself */
.nook-here { position: absolute; left: 66%; bottom: 30%; transform: translateX(-50%); z-index: 4; } /* off to one side — the thing you are standing over, not a light inside you */
.nook-view .nook-stage { max-height: clamp(210px, 38vh, 340px); }
.nook-verse { margin-top: -4px; }
@media (prefers-reduced-motion: reduce) { .nook-glint, .nook-mark::before { animation: none; opacity: 0.8; } }

/* the illuminated corner mark — still worn by the sign-in screen and the backstory reveal */
.illum-corner { position: absolute; width: 16px; height: 16px; z-index: 3; opacity: 0.5; pointer-events: none;
  background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke='%23d9b866' stroke-width='1.4'%3E%3Crect x='6' y='6' width='12' height='12'/%3E%3Crect x='6' y='6' width='12' height='12' transform='rotate(45 12 12)'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat; }
.illum-corner.tl { top: 9px; left: 9px; }
.illum-corner.bl { bottom: 9px; left: 9px; }

/* =========================================================================================
   THE OPENING — Trials of the Road
   Not a frontispiece. The camera opens pressed against a single lamp in the delta and lets go
   over about five seconds: the sea fills, the roads draw outward in the order the country is
   walked, the three powers light and name themselves, the fever sits dark in the gap, and the
   far lamp in the heights comes up last, under the title. Everything under .tor-cam is real
   map data at its real coordinates, so the opening cannot drift out of step with the world.
   ========================================================================================= */
.tor { position: relative; max-width: 1160px; margin: 0 auto; padding-bottom: 30px; }
/* THE FRAME IS THE MAP'S OWN SHAPE, and before this it was not. The map is a 400x240 viewBox drawn
   `slice`, so a box of any other aspect crops it -- and the box was `82vh`, which is a statement
   about the WINDOW and not about the country. Measured: the Quiet House node, the lamp the whole
   opening builds toward and the last movement is about, sat OUTSIDE the frame at 1280, 1440 and
   1920 wide; its label was cut by 21px at 1440x900 and 131px at 1920x1080, and at 1920 "your house"
   -- the door you start at -- lost 47px off the other end. The opening was cropping both ends of
   its own story. Given the viewBox's aspect the crop cannot happen, whatever the window does.
   A max-height clamp on a short window crops the HEIGHT instead, which trims sky and sea rather
   than marks. */
.tor-scene {
  position: relative; overflow: hidden;
  aspect-ratio: 400 / 240; height: auto; max-height: 84vh; min-height: 360px;
  border-width: 2px;
  border: 1px solid rgba(232,180,92,0.18);
  background: #05090c;
  box-shadow: 0 24px 80px rgba(0,0,0,0.7), inset 0 0 0 1px rgba(232,180,92,0.05);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.tor-map { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
.tor-grain { opacity: 0.055; mix-blend-mode: overlay; pointer-events: none; }

/* --- the camera --- */
.tor-cam { transform-box: view-box; transform-origin: 0 0; }

/* --- the sea, and the channels through the delta --- */
.ts-line { fill: none; stroke: #7fb2c4; stroke-width: 0.7; opacity: 0.34; }
.ts-chan { fill: none; stroke: #8fc6d6; stroke-width: 0.6; opacity: 0.2; }

/* --- the heights, standing behind everything --- */
.th-far { fill: #071219; stroke: #24414c; stroke-width: 1; opacity: 0.95; }
.th-near { fill: #030a0f; stroke: #152c35; stroke-width: 1; opacity: 1; }

/* --- the ground the fever holds --- */
.tor-miasma { opacity: 0; }

/* --- the lamp in the heights --- */
.tor-beam { opacity: 0; }

/* --- the roads --- */
.tor-road {
  fill: none; stroke: #cbb489; stroke-width: 0.85; opacity: 0.42;
  stroke-dasharray: 1; stroke-dashoffset: 0;
}
.tor-road.a0, .tor-road.a1 { stroke: #d9c39a; opacity: 0.5; }
.tor-road.a4, .tor-road.a5 { stroke: #9fb0c6; opacity: 0.34; }

/* --- the places --- */
.tn-halo { fill: #e8b45c; opacity: 0; }
.tor-node .tn-halo { r: 6; }
.tor-node .tn-city rect { fill: none; stroke: #e6d6b0; stroke-width: 1; }
.tor-node .tn-town { fill: #0b1216; stroke: #cdbb94; stroke-width: 1; }
.tor-node .tn-vil { fill: #cdbb94; opacity: 0.9; }
.tor-node .tn-wild line { stroke: #8d9aa4; stroke-width: 0.9; opacity: 0.75; }
.tor-node.f-ledger .tn-city rect, .tor-node.f-ledger .tn-town { stroke: var(--gold); }
.tor-node.f-wardens .tn-city rect, .tor-node.f-wardens .tn-town { stroke: #7f9ed6; }
.tor-node.f-compact .tn-city rect, .tor-node.f-compact .tn-town { stroke: #74b892; }
.tor-node.f-ledger .tn-halo { fill: var(--gold); }
.tor-node.f-wardens .tn-halo { fill: #7f9ed6; }
.tor-node.f-compact .tn-halo { fill: #74b892; }
.tor-node.fevered .tn-city rect, .tor-node.fevered .tn-town, .tor-node.fevered .tn-wild line { stroke: #8b9663; opacity: 0.7; }
.tor-node.fevered .tn-halo { fill: #98a56b; }
.tor-node.gilt .tn-city rect { stroke: #f3d79a; stroke-width: 1.3; }
.tor-node.gilt .tn-halo { fill: #f3d79a; }
.tor-node.origin .tn-vil { fill: #ffe3ae; }
.tor-node.origin .tn-halo { fill: #ffd48a; }
.tn-lamp line { stroke: #ffdca0; stroke-width: 0.8; opacity: 0.55; }
.tn-lamp rect { fill: none; stroke: #ffe6bb; stroke-width: 1.1; }
.tn-wick { fill: #fff3d4; }

/* --- what the country says out loud --- */
.tor-label {
  font-family: var(--serif); font-size: 8.4px; letter-spacing: 0.09em;
  fill: #d9cdb2; opacity: 0; paint-order: stroke; stroke: #05090c; stroke-width: 2.6; stroke-linejoin: round;
}
.tor-label.home { font-style: italic; font-size: 7.4px; fill: #ffdfae; }
.tor-label.led { fill: var(--gold); }
.tor-label.war { fill: #8fabdf; }
.tor-label.com { fill: #86c6a1; }
.tor-label.fev { fill: #a8b477; font-style: italic; }
.tor-label.gilt { fill: #f3d79a; }
.tor-label.beacon { fill: #fff0cf; font-size: 9.2px; letter-spacing: 0.14em; }

/* --- the house of Ghassan: what one man has taken of the country --- */
/* Drawn UNDER the nodes and deliberately not in a road's language. The roads are what the country
   IS; this is a claim laid over it, so it is dashed, warm and faint, and it converges on one seat. */
.th-reach { fill: none; stroke: #d08a5a; stroke-width: 0.55; opacity: 0.32; stroke-dasharray: 2 2.6; }
.th-ring { fill: none; stroke: #d08a5a; stroke-width: 0.75; opacity: 0.46; }
.th-seat rect { fill: #180e09; stroke: #e5a878; stroke-width: 1.1; }
.th-seat rect + rect { fill: #e5a878; stroke: none; }
.tor-label.house { fill: #e5a878; font-size: 8.2px; letter-spacing: 0.11em; opacity: 1; }
.tor.playing .tor-label.house { animation: none; opacity: 1; }

/* --- the world under the map --- */
/* THE PLATE IS THE ART'S OWN SHAPE. A location's banner is drawn 400x96, so the plate is given that
   aspect and the picture is never cropped to fit a frame someone chose. It sits above the narration
   and below the map's labels, which is the only band of the scene box that is reliably empty once
   the camera has pulled back. */
/* THE PLATE IS BOTTOM-RIGHT BECAUSE THE DELTA IS BOTTOM-LEFT, and that was measured rather than
   composed: centred and full width, the plate covered 4 of the 7 rings of the house's district --
   it hid the exact thing its own movement is about. Everything the opening names on the map lives
   left of screen x 580 (the delta and your own door) or high right (the lamp in the heights), so
   this corner is the one band that is empty in every movement. */
.tor-plates { position: absolute; right: 4%; width: 55%; bottom: 14%; z-index: 2; pointer-events: none; }
.tor-plate {
  position: absolute; left: 0; right: 0; bottom: 0; aspect-ratio: 400 / 96; opacity: 0;
  overflow: hidden; background: #05090c;
  border: 1px solid rgba(232,180,92,0.26);
  box-shadow: 0 20px 52px rgba(0,0,0,0.8), 0 0 0 1px rgba(0,0,0,0.5);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.tp-art { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* the cast stands ON the plate, at the same end of it the eye already reads first */
.tp-you { position: absolute; left: 13%; bottom: 7%; height: 47%; aspect-ratio: 7.2 / 8; }
/* the foe keeps the fight's own look, rim light and all -- the whole point is that this IS the
   silhouette you meet. It carries the fight's mirror too, so he faces the way he faces in combat. */
.tp-foe { position: absolute; right: 12%; bottom: 7%; height: 62%; transform: scaleX(-1); }

/* --- the narration over the map --- */
.tor-beats { position: absolute; left: 6%; right: 6%; bottom: 7%; z-index: 3; pointer-events: none; }
.tor-beat {
  position: absolute; left: 0; right: 0; bottom: 0; margin: 0; opacity: 0;
  font-family: var(--serif); font-size: clamp(12.5px, 2.2vw, 15.5px); line-height: 1.6;
  color: #e6dcc4; text-align: center; letter-spacing: 0.015em;
  text-shadow: 0 1px 3px #04080b, 0 2px 16px rgba(0,0,0,0.95), 0 0 34px rgba(0,0,0,0.85);
}

/* --- the name --- */
.tor-name {
  position: absolute; left: 0; right: 0; top: 0; z-index: 4; text-align: center; pointer-events: none;
  padding: clamp(26px, 6vh, 56px) 12px clamp(40px, 10vh, 84px);
  background: linear-gradient(180deg, rgba(5,9,12,0.9) 0%, rgba(5,9,12,0.62) 46%, rgba(5,9,12,0.18) 76%, transparent 100%);
}
.tor-kicker {
  font-size: 10px; letter-spacing: 0.34em; text-transform: uppercase; color: var(--gold-dim);
  margin-bottom: 10px; text-shadow: 0 2px 10px rgba(0,0,0,0.95);
}
.tor-title {
  position: relative; display: inline-block; margin: 0;
  font-family: var(--serif); font-weight: 500;
  font-size: clamp(32px, 8.2vw, 58px); line-height: 1.02; letter-spacing: 0.01em;
}
.tt-ink {
  background: linear-gradient(180deg, #fffaf0 0%, #f4e2bb 46%, #d2a862 100%);
  -webkit-background-clip: text; background-clip: text; color: transparent;
  filter: drop-shadow(0 2px 4px rgba(0,0,0,0.85)) drop-shadow(0 0 40px rgba(255,208,138,0.35));
}
/* A single pass of light across the letters, once, as they settle. THE LIGHT MOVES INSIDE THE BOX,
   the box does not move: translating the element carried its lit band 493px clear of the title, and
   `screen` over a dark map painted that as a free-floating grey slab beside the name. The element is
   the title's own box now and only the gradient travels, so nothing can paint outside the letters. */
.tt-sweep {
  position: absolute; inset: 0; pointer-events: none; opacity: 0;
  background: linear-gradient(105deg, transparent 38%, rgba(255,244,214,0.6) 50%, transparent 62%);
  background-size: 240% 100%; background-repeat: no-repeat;
  mix-blend-mode: screen;
}
.tor-rule { margin: 5px auto 0; width: min(238px, 52%); opacity: 0; }
.tor-rule svg { width: 100%; height: auto; display: block; }
.tr-l { stroke: var(--gold-dim); stroke-width: 1; opacity: 0.6; }
.tr-k { fill: none; stroke: var(--gold); stroke-width: 1.2; }

/* --- below the scene --- */
.tor-below { padding: 30px 6px 0; text-align: center; }
/* THE AYAH — set apart in an ornamented frame, the way a sign is set apart from prose. A khatam
   star above it, cut corners, a hairline rule, gold on ink: the verse is not narration, it is a
   thing you have been shown, and it is framed as one. Unique to this game. */
.tor-passage {
  position: relative; max-width: 40ch; margin: 0 auto 22px; padding: 30px 30px 20px;
  display: flex; flex-direction: column; align-items: center; gap: 12px;
  background: radial-gradient(120% 140% at 50% 0%, rgba(232,180,92,0.07), transparent 62%);
  border: 1px solid rgba(232,180,92,0.28);
  clip-path: polygon(14px 0, calc(100% - 14px) 0, 100% 14px, 100% calc(100% - 14px), calc(100% - 14px) 100%, 14px 100%, 0 calc(100% - 14px), 0 14px);
}
.ayah-star { position: absolute; top: -14px; left: 50%; transform: translateX(-50%); width: 22px; height: 22px;
  fill: #0c1316; stroke: var(--gold); stroke-width: 1.1; filter: drop-shadow(0 0 5px rgba(232,180,92,0.4)); }
.ayah-corner { position: absolute; width: 9px; height: 9px; border: 1px solid var(--gold-dim); opacity: 0.7; }
.ayah-corner.tl { top: 6px; left: 6px; border-right: 0; border-bottom: 0; }
.ayah-corner.tr { top: 6px; right: 6px; border-left: 0; border-bottom: 0; }
.ayah-corner.bl { bottom: 6px; left: 6px; border-right: 0; border-top: 0; }
.ayah-corner.br { bottom: 6px; right: 6px; border-left: 0; border-top: 0; }
.tor-clause {
  font-family: var(--serif); font-style: italic; font-size: clamp(15.5px, 2.5vw, 19px); line-height: 1.6;
  color: #f2e6c8; letter-spacing: 0.01em;
}
.tor-clause + .tor-clause { color: var(--gold); }
.tor-passage .pv-ref { font-size: 10.5px; letter-spacing: 0.24em; text-transform: uppercase; color: var(--gold-dim); margin-top: 2px; }
.tor-ask {
  font-family: var(--serif); font-size: 15.5px; color: var(--ink-dim); letter-spacing: 0.02em; margin-bottom: 16px;
}

/* --- the two ways out --- */
.tor-ways { display: flex; flex-direction: column; gap: 11px; max-width: 560px; margin: 0 auto; }
.tor-way {
  display: flex; align-items: center; gap: 15px; text-align: left; width: 100%; cursor: pointer;
  padding: 15px 18px; font: inherit; color: var(--ink); border-radius: 0;
  background: linear-gradient(180deg, rgba(29,44,50,0.9), rgba(18,28,32,0.86));
  border: 1px solid var(--line); border-bottom: 2px solid rgba(232,180,92,0.18);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
  transition: border-color 0.22s ease, transform 0.2s cubic-bezier(0.22,1,0.36,1), box-shadow 0.22s ease, background 0.22s ease;
}
.tor-way:hover { transform: translateY(-2px); border-color: var(--gold-dim); box-shadow: 0 10px 30px rgba(0,0,0,0.5); }
.tw-arch { flex: 0 0 auto; width: 44px; height: 40px; display: block; }
.tw-arch svg { width: 100%; height: 100%; }
.tw-arch .tw-a, .tw-arch .tw-b { fill: none; stroke: var(--gold-dim); stroke-width: 1.4; opacity: 0.55; }
.tw-arch .tw-k { fill: none; stroke: var(--gold); stroke-width: 1.2; }
.tw-arch.small { width: 30px; height: 28px; }
.tw-body { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.tw-lead { font-family: var(--serif); font-size: 16.5px; color: #f2e6c8; }
.tw-say { font-family: var(--serif); font-style: italic; font-size: 13.5px; color: var(--gold); letter-spacing: 0.01em; }
.tw-en { font-size: 12.5px; color: var(--ink-dim); line-height: 1.5; }
.tw-sub { font-size: 11.5px; color: var(--ink-faint); letter-spacing: 0.02em; }
.tor-way.bearing {
  background: linear-gradient(180deg, rgba(232,180,92,0.14), rgba(18,28,32,0.88));
  border-color: rgba(232,180,92,0.42); border-bottom-color: var(--gold);
  box-shadow: inset 0 0 30px rgba(232,180,92,0.07);
}
.tor-way.bearing .tw-arch .tw-a, .tor-way.bearing .tw-arch .tw-b { stroke: var(--gold); opacity: 0.85; }
.tor-way.bearing:hover { border-color: var(--gold); box-shadow: 0 10px 34px rgba(0,0,0,0.5), 0 0 0 1px rgba(232,180,92,0.2); }
.tor-way.saved { padding: 11px 18px; }
.tor-way.saved .tw-lead { font-size: 14.5px; }
/* the way in sits beside the name, in a lit box, because a visitor should not have to hunt
   the foot of the page to find out their road can be kept */
.auth-chip {
  display: flex; align-items: center; gap: 10px; text-align: left; cursor: pointer; font: inherit;
  padding: 8px 12px; color: var(--ink);
  background: linear-gradient(180deg, rgba(232,180,92,0.2), rgba(232,180,92,0.07));
  border: 1px solid rgba(232,180,92,0.5); border-bottom: 2px solid var(--gold);
  box-shadow: 0 6px 22px rgba(0,0,0,0.5), inset 0 0 22px rgba(232,180,92,0.08);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
  transition: transform 0.2s cubic-bezier(0.22,1,0.36,1), border-color 0.2s ease, box-shadow 0.2s ease;
}
.auth-chip:hover { transform: translateY(-2px); border-color: var(--gold); box-shadow: 0 10px 28px rgba(0,0,0,0.55), inset 0 0 26px rgba(232,180,92,0.14); }
.auth-chip.signed { cursor: default; }
.ac-k { flex: 0 0 auto; width: 19px; height: 19px; }
.ac-k svg { width: 100%; height: 100%; fill: none; stroke: var(--gold); stroke-width: 1.3; }
.ac-t { display: flex; flex-direction: column; line-height: 1.25; white-space: nowrap; }
.ac-t b { font-family: var(--serif); font-weight: 500; font-size: 14px; color: #f6e9cb; }
.ac-t i { font-style: normal; font-size: 10px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gold-dim); }
.auth-chip.signed .linkbtn { margin-left: 4px; font-size: 11px; }

/* --- the skip, while it plays --- */
.tor-skip {
  position: absolute; bottom: 12px; right: 12px; z-index: 6;
  background: rgba(6,10,13,0.72); border: 1px solid rgba(232,180,92,0.26);
  color: var(--ink-dim); font: inherit; font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase;
  padding: 6px 13px; cursor: pointer; transition: color 0.2s, border-color 0.2s;
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
}
.tor-skip:hover { color: var(--ink); border-color: var(--gold-dim); }

/* ================================ at rest ================================ */
/* When the opening is not playing (returning to the title, or reduced motion) the country is
   simply there, fully drawn, with only the slow ambient life running. */
.tor-miasma { opacity: 0.9; animation: tor-fever 9s ease-in-out infinite; }
.tor-beam { opacity: 1; animation: tor-lamp 5.4s ease-in-out infinite; }
.tor-beat { display: none; }
.tor .tor-label { opacity: 1; }
.tor .tn-halo { opacity: 0.05; }
.tor .tor-node.beacon .tn-halo { opacity: 0.18; }
.ts-chan { animation: tor-drift 14s linear infinite; }
.tn-wick { animation: tor-wick 3.6s ease-in-out infinite; }
@keyframes tor-fever { 0%, 100% { opacity: 0.62; transform: scale(0.97); } 50% { opacity: 1; transform: scale(1.05); } }
@keyframes tor-lamp { 0%, 100% { opacity: 0.7; } 50% { opacity: 1; } }
@keyframes tor-wick { 0%, 100% { opacity: 0.6; } 50% { opacity: 1; } }
@keyframes tor-drift { from { stroke-dashoffset: 0; } to { stroke-dashoffset: -60; } }

/* ============================== the opening, playing ============================== */
.tor.playing .tor-cam { animation: tor-pullback 15s cubic-bezier(0.32,0.02,0.18,1) both; }
.tor.playing .tor-map > rect:first-of-type { animation: tor-in 2.1s ease both; }
.tor.playing .ts-body, .tor.playing .ts-line { animation: tor-in 2.7s 1.1s ease both; }
.tor.playing .ts-chan { animation: tor-in 2.4s 1.9s ease both, tor-drift 14s linear 4.4s infinite; }
.tor.playing .th-far { animation: tor-rise 2.9s 5.6s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .th-near { animation: tor-rise 2.9s 6.2s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-road { animation: tor-run 1.7s cubic-bezier(0.4,0,0.2,1) both; }
.tor.playing .tor-node { animation: tor-set 1.35s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-node .tn-halo { animation: tor-strike 3.2s ease both; animation-delay: inherit; }
.tor.playing .tor-label { animation: tor-say 2s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-miasma { animation: tor-creep 3s 9.4s ease both, tor-fever 9s 12.6s ease-in-out infinite; }
.tor.playing .tor-beam { animation: tor-flare 3.2s 13.6s ease both, tor-lamp 5.4s 17s ease-in-out infinite; }
.tor.playing .tor-node.beacon { animation: tor-set 2.5s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-beat { display: block; animation: tor-line 4.2s ease both; }
/* the plate holds for exactly one movement, so each hands over to the next with no gap and no
   overlap. Its duration is TOR_STEP; changing the step in app.js means changing it here too. */
.tor.playing .tor-plate { animation: tor-plate 4.2s ease both; }
.tor.playing .tor-house { animation: tor-claim 2.8s ease both; }
.tor.playing .tor-name { animation: tor-name-in 3.2s 16s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tt-sweep { animation: tor-sweep 2.2s 17.4s ease-out both; }
.tor.playing .tor-rule { animation: tor-rule-in 1.6s 17.2s ease both; }
.tor.playing .tor-passage { animation: tor-up 1.2s 17.8s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-ask { animation: tor-up 1.1s 18.5s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-ways { animation: tor-up 1.1s 18.9s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .diff-pick { animation: tor-up 1.1s 19.2s cubic-bezier(0.16,1,0.3,1) both; }
.tor.playing .tor-skip { animation: tor-up 0.7s 1.6s ease both; }

/* the camera: pressed against the reed house, then released over the whole country */
@keyframes tor-pullback {
  0%   { transform: translate(39.9px, -431.5px) scale(3.4); }
  34%  { transform: translate(-25.5px, -187.5px) scale(2.05); }
  70%  { transform: translate(-31.2px, -70.4px) scale(1.36); }
  100% { transform: translate(0, 0) scale(1); }
}
@keyframes tor-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes tor-rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes tor-run { from { stroke-dashoffset: 1; opacity: 0; } 25% { opacity: 0.55; } to { stroke-dashoffset: 0; } }
@keyframes tor-set { from { opacity: 0; transform: translate(var(--tx, 0), var(--ty, 0)) scale(0.2); } to { opacity: 1; } }
@keyframes tor-strike { 0% { opacity: 0; } 22% { opacity: 0.42; } 100% { opacity: 0.05; } }
@keyframes tor-say { from { opacity: 0; letter-spacing: 0.3em; } to { opacity: 1; letter-spacing: 0.09em; } }
@keyframes tor-creep { from { opacity: 0; } to { opacity: 0.9; } }
@keyframes tor-flare { 0% { opacity: 0; } 55% { opacity: 1; } 100% { opacity: 0.85; } }
@keyframes tor-line { 0% { opacity: 0; transform: translateY(9px); } 14% { opacity: 1; transform: none; } 84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-7px); } }
@keyframes tor-plate { 0% { opacity: 0; transform: translateY(12px) scale(0.985); } 13% { opacity: 1; transform: none; } 84% { opacity: 1; transform: none; } 100% { opacity: 0; transform: translateY(-8px) scale(0.99); } }
@keyframes tor-claim { from { opacity: 0; } to { opacity: 1; } }
@keyframes tor-name-in { 0% { opacity: 0; letter-spacing: 0.42em; filter: blur(14px); transform: scale(1.05); } 100% { opacity: 1; letter-spacing: 0.01em; filter: none; transform: none; } }
@keyframes tor-sweep { 0% { opacity: 0; background-position: -130% 0; } 20% { opacity: 1; } 100% { opacity: 0; background-position: 230% 0; } }
@keyframes tor-rule-in { from { opacity: 0; transform: scaleX(0.4); } to { opacity: 1; transform: none; } }
@keyframes tor-up { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
  .tor-miasma, .tor-beam, .ts-chan, .tn-wick { animation: none; }
  .tor.playing * { animation: none !important; }
}
/* below this the gutters beside a centred title are narrower than the chip, so it goes under the name */

@media (max-width: 560px) {
  /* a narrow frame cannot spare the room a full title band takes, so the band gets out of the map's way */
  .tor-scene { height: 70vh; min-height: 320px; }
  .tor-name { padding: 16px 10px 34px; }
  .tor-kicker { font-size: 9px; letter-spacing: 0.26em; margin-bottom: 6px; }
  .tor-title { font-size: clamp(26px, 7.6vw, 40px); }
  .tor-rule { display: none; }
  .tor-way { padding: 13px 14px; gap: 11px; }
  .tw-arch { width: 34px; height: 31px; }
  .tor-beats { left: 4%; right: 4%; bottom: 5%; }
  .tor-beat { font-size: 12px; line-height: 1.5; }
}

/* the line that greets you when a road brings you back somewhere you have already been */
.return-note {
  margin: 2px 0 8px; padding: 7px 13px; border-left: 2px solid var(--gold-dim); border-radius: 0 8px 8px 0;
  background: linear-gradient(90deg, rgba(217,184,102,0.08), transparent);
  font-size: 0.87rem; font-style: italic; color: #cbb98d; letter-spacing: 0.01em;
}

/* =============================================================================================
   THE CUT — this game's structural language
   =============================================================================================
   The first game was built out of rounded rectangles: every panel, button and choice had the
   same soft 12px corner, and that single decision is most of why the two games look alike.
   Here, nothing is rounded. Everything is CUT — the corner is taken off at 45 degrees, the way
   a tile is cut to sit in a khatam field. It costs one clip-path per surface, it inherits the
   existing markup untouched, and it changes the silhouette of every screen at once.

   Three depths, so the hierarchy still reads:
     .cut-deep  panels, plates, the scene frame     (--cut)
     .cut       choices, buttons, cards             (--cut-sm)
     .cut-tab   chips, tags, pills                  (one corner only, so they read as labels)
   ============================================================================================= */
.panel, .scene-frame, .modal, .sheet, .plate, .dialogue-plate, .card, .mapCard, .map-screen,
.combat, .scene-view, .stage-top, .madrasa-status, .amb-list, .journal, .rail-sheet,
.cut-deep {
  border-radius: 0 !important;
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.choice, .btn, .stat-card, .item-card, .perk-card, .book-card, .figure-card, .quiz-opt, .opt,
.cut {
  border-radius: 0 !important;
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
}
.choice-tag, .chip, .tag, .pill, .locktag, .amb-met, .amb-unmet,
.cut-tab {
  border-radius: 0 !important;
  clip-path: polygon(var(--cut-tab) 0, 100% 0, 100% 100%, 0 100%, 0 var(--cut-tab));
}
/* The three names above were documented for years and never actually existed as classes - the cut
   was only ever reachable by being one of the listed component classes. That left new markup, and
   anything built against the design system, with no way to cut a corner at all. They are real now:
   .cut-deep and .cut are folded into the two selector lists above, .cut-tab into this one. */
/* A cut corner leaves a diagonal with no border on it, so each cut surface gets its own
   corner-mark: a small filled wedge in the notch. It replaces the rounded corner's job of
   telling the eye where the surface ends. */
.panel, .scene-frame, .choice, .btn:not(.ghost), .stat-card, .quiz-opt { position: relative; }
.choice::before, .panel::before, .stat-card::before, .quiz-opt::before {
  content: ""; position: absolute; left: 0; bottom: 0; width: var(--cut-sm); height: var(--cut-sm);
  background: linear-gradient(45deg, var(--gold-dim) 0 50%, transparent 50%); opacity: 0.5;
  transition: opacity 0.25s ease; pointer-events: none; z-index: 2;
}
.choice:hover:not(.locked)::before { opacity: 1; }
.choice.locked::before { opacity: 0.18; }

/* --- structure: a choice is a plate with a lit left edge, not a soft pill ------------------- */
.choice {
  border-left-width: 3px !important;
  border-left-color: rgba(232,180,92,0.34) !important;
  background-image: linear-gradient(90deg, rgba(232,180,92,0.055), rgba(232,180,92,0) 34%);
  transition: border-left-color 0.2s ease, background-color 0.2s ease, transform 0.14s ease, box-shadow 0.2s ease;
}
.choice:hover:not(.locked) {
  border-left-color: var(--gold) !important;
  box-shadow: -6px 0 22px -8px rgba(232,180,92,0.5), inset 0 0 0 1px rgba(232,180,92,0.1);
}

/* =============================================================================================
   TRANSITIONS — arriving, not appearing
   =============================================================================================
   The first game faded things in. Here a screen ARRIVES: it comes up from below with its light
   lagging a beat behind it, and the choices under it unfold one after another rather than all
   at once, so a screen has a reading order the eye can follow.
   ============================================================================================= */
@keyframes tor-arrive { from { opacity: 0; transform: translateY(14px) scale(0.994); filter: brightness(0.72) saturate(0.7); }
  60% { filter: brightness(1.02) saturate(1.04); } to { opacity: 1; transform: none; filter: none; } }
@keyframes tor-unfold { from { opacity: 0; transform: translateX(-10px); clip-path: polygon(var(--cut-sm) 0, 0 0, 0 100%, 0 100%, 0 100%, 0 var(--cut-sm)); }
  to { opacity: 1; transform: none; clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)); } }
@keyframes tor-rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }

.scene-view, .map-screen, .combat, .scene, .school, .market, .creation { animation: tor-arrive 0.46s cubic-bezier(.16,.84,.32,1) both; }
.choices > .choice { animation: tor-unfold 0.34s cubic-bezier(.2,.8,.3,1) both; }
.choices > .choice:nth-child(1) { animation-delay: 0.06s; }
.choices > .choice:nth-child(2) { animation-delay: 0.13s; }
.choices > .choice:nth-child(3) { animation-delay: 0.20s; }
.choices > .choice:nth-child(4) { animation-delay: 0.27s; }
.choices > .choice:nth-child(5) { animation-delay: 0.34s; }
.choices > .choice:nth-child(n+6) { animation-delay: 0.40s; }
/* a hairline rule draws itself under every scene title, left to right, as the screen settles */
.scene-title::after, .stage-top .scene-title::after {
  content: ""; display: block; height: 1px; margin-top: 9px; transform-origin: left center;
  background: linear-gradient(90deg, var(--gold-dim), rgba(232,180,92,0.12) 62%, transparent);
  animation: tor-rule 0.7s 0.14s cubic-bezier(.16,.84,.32,1) both;
}
@media (prefers-reduced-motion: reduce) {
  .scene-view, .map-screen, .combat, .scene, .school, .market, .creation,
  .choices > .choice, .scene-title::after { animation: none; }
}

/* =============================================================================================
   THE KHATIMAH — the ending, told in movements
   ============================================================================================= */
.stillness { min-height: 60vh; display: grid; place-items: center; position: relative; }
.stillness .still-mark { width: 5px; height: 5px; border-radius: 50%; background: var(--gold);
  box-shadow: 0 0 26px 8px rgba(232,180,92,0.45); animation: still-out 2.6s cubic-bezier(.3,0,.2,1) both; }
.stillness .still-ring { position: absolute; width: 8px; height: 8px; border-radius: 50%;
  border: 1px solid rgba(232,180,92,0.6); animation: still-ring 2.6s cubic-bezier(.16,.6,.2,1) both; }
@keyframes still-out { 0% { opacity: 0; transform: scale(2.4); } 22% { opacity: 1; transform: scale(1); }
  70% { opacity: 1; } 100% { opacity: 0; transform: scale(0.2); } }
@keyframes still-ring { 0% { opacity: 0.9; transform: scale(1); } 100% { opacity: 0; transform: scale(26); } }

.ending { max-width: 720px; margin: 0 auto; padding: 26px 4px 60px; cursor: default; }
.ending-head { text-align: center; padding: 22px 0 8px; }
.ending-head h1 { font-family: var(--serif); font-weight: 500; font-size: clamp(28px, 6.6vw, 44px);
  margin: 6px 0 0; letter-spacing: 0.01em; line-height: 1.16; }
.ending .ending-kicker { letter-spacing: 0.2em; }
.end-mv { padding: 30px 0 6px; }
.end-mv .mv-rule { height: 1px; transform-origin: left center;
  background: linear-gradient(90deg, var(--gold-dim), rgba(232,180,92,0.1) 55%, transparent); }
.end-mv .mv-label { font-size: 11.5px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--gold-dim); margin: 12px 0 14px; }
.end-arrival { font-family: var(--serif); font-size: clamp(17px, 3.6vw, 21px); line-height: 1.6;
  color: var(--ink); margin: 0 0 14px; }
.ending-body { font-family: var(--serif); font-size: clamp(16px, 3.3vw, 19px); line-height: 1.68; color: #ddd7c8; }
.end-after { font-family: var(--serif); font-style: italic; color: var(--ink-dim); line-height: 1.62; margin-top: 16px; }
.ending-closing { text-align: center; letter-spacing: 0.16em; text-transform: uppercase;
  font-size: 12px; color: var(--gold-dim); margin-top: 26px; }
.mv-star { display: grid; place-items: center; margin: 4px 0 20px; color: var(--gold-dim); }
.mv-star svg { width: 84px; height: 84px; opacity: 0.75; }
.mv-star svg g { stroke-dasharray: 420; stroke-dashoffset: 420; animation: mv-draw 2.6s ease-out both; }
@keyframes mv-draw { to { stroke-dashoffset: 0; } }

/* the virtues: a bar showing where you started and where you got to, counting up as it draws.
   The first game laid these out as a grid of little cards, which read as a stat block. Here it is
   one row per virtue, in order, so the seven of them read as a list of what a life did to you. */
.ending .growth { display: block; max-width: 100%; margin: 4px 0 22px; }
.ending .growth-row { display: grid; grid-template-columns: 1fr 96px 86px; background: none; border: 0; border-radius: 0; padding: 8px 0; align-items: center; gap: 12px; border-bottom: 1px solid rgba(198,220,214,0.07); }
.ending .growth-name { font-size: 14px; color: var(--ink-dim); }
.ending .growth-bar { position: relative; height: 5px; background: rgba(198,220,214,0.1); }
.ending .growth-bar i { position: absolute; inset: 0 auto 0 0; background: rgba(198,220,214,0.3); }
.ending .growth-bar b { position: absolute; inset: 0 auto 0 0; background: var(--gold); transform-origin: left center;
  animation: grow-in 1.1s 1.4s cubic-bezier(.16,.84,.32,1) both; }
@keyframes grow-in { from { transform: scaleX(0); } to { transform: scaleX(1); } }
.ending .g-num { font-size: 15px; font-variant-numeric: tabular-nums; color: var(--ink); }
.ending .g-up { color: var(--green); } .ending .g-down { color: var(--red); } .ending .g-same { color: var(--ink-faint); }

/* what you meant to do: each ambition its own line, with the mark that says whether you managed it */
.amb-recap { display: grid; grid-template-columns: 14px 1fr; gap: 4px 12px; padding: 12px 0;
  border-bottom: 1px solid rgba(198,220,214,0.07); }
.amb-recap .amb-mark { width: 9px; height: 9px; margin-top: 7px;
  clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%); background: var(--ink-faint); }
.amb-recap.met .amb-mark { background: var(--gold); box-shadow: 0 0 10px rgba(232,180,92,0.6); }
.amb-recap .amb-what { grid-column: 2; font-family: var(--serif); font-size: 16.5px; color: var(--ink); }
.amb-recap .amb-said { grid-column: 2; color: var(--ink-dim); line-height: 1.6; }
.amb-recap.unmet .amb-what { color: var(--ink-dim); }
.ending-rep, .ending-trait { line-height: 1.62; margin: 8px 0; color: #cdc7b8; }
.ending-trait { font-family: var(--serif); font-size: 17px; }
.end-actions { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; margin-top: 40px; }
.end-skip { text-align: center; font-size: 11.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-faint); margin-top: 18px; opacity: 0; animation: fade-in 0.6s 1.2s both; }
@keyframes fade-in { to { opacity: 0.75; } }

/* THE STAGING. Every movement is in the DOM from the first frame; only its arrival is delayed, so
   nothing here can hide content from a reader, a test, or an assistive tree. A click sets .told,
   which drops every delay at once.
   --mv is per-element ordering, not a theme token: app.js writes it inline on each section
   (style="--mv:N") and the delays below multiply by it. Do not promote it to :root — one shared
   value gives every movement the same delay and the whole cascade arrives at once. */
.ending.staged .end-mv, .ending.staged .end-actions { animation: mv-arrive 0.9s calc(var(--mv, 1) * 1.55s + 0.5s) cubic-bezier(.16,.84,.32,1) both; }
.ending.staged .end-actions { --mv: 6; /* @kind other */ }
.ending.staged .end-mv .mv-rule { animation: mv-rule 0.85s calc(var(--mv, 1) * 1.55s + 0.5s) cubic-bezier(.16,.84,.32,1) both; }
.ending.staged .growth-row, .ending.staged .amb-recap { animation: mv-line 0.5s calc(var(--mv, 2) * 1.55s + 0.9s + var(--i, 0) * 0.11s) ease both; }
.ending.staged .amb-recap { animation-delay: calc(3 * 1.55s + 0.9s + var(--i, 0) * 0.22s); }
@keyframes mv-arrive { from { opacity: 0; transform: translateY(20px); } to { opacity: 1; transform: none; } }
@keyframes mv-rule { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes mv-line { from { opacity: 0; transform: translateX(-8px); } to { opacity: 1; transform: none; } }
.ending.told .end-mv, .ending.told .end-actions, .ending.told .mv-rule,
.ending.told .growth-row, .ending.told .amb-recap, .ending.told .growth-bar b, .ending.told .mv-star svg g {
  animation: none !important; opacity: 1 !important; transform: none !important; stroke-dashoffset: 0 !important; }
.ending.told .end-skip { display: none; }
@media (prefers-reduced-motion: reduce) {
  .stillness .still-mark, .stillness .still-ring, .ending * { animation: none !important; }
  .ending .growth-bar b, .ending .mv-star svg g { opacity: 1; stroke-dashoffset: 0; }
  .end-skip { display: none; }
}

.ending .growth-change { text-align: right; font-family: var(--serif); }
.ending .growth-row { border-bottom: 1px solid rgba(198,220,214,0.07) !important; background: none !important; border-left: 0 !important; }

/* =============================================================================================
   THE WHEEL — allocation as a shape, not a form
   ============================================================================================= */
.wheel-wrap { max-width: 400px; margin: 4px auto 10px; padding: 0 18px; text-align: center; }
.alloc-wheel { display: block; width: 100%; height: auto; overflow: visible; touch-action: none; cursor: crosshair; user-select: none; }
.wheel-hint { font-size: 12.5px; color: var(--ink-faint); letter-spacing: 0.06em; margin: -2px 0 4px; }
.wh-field polygon { fill: none; stroke: rgba(198,220,214,0.09); stroke-width: 1; }
.wh-field line { stroke: rgba(198,220,214,0.1); stroke-width: 1; }
.wh-shape { fill: rgba(232,180,92,0.14); stroke: var(--gold); stroke-width: 1.6; stroke-linejoin: round;
  transition: all 0.16s cubic-bezier(.2,.8,.3,1); filter: drop-shadow(0 0 12px rgba(232,180,92,0.22)); }
.wh-spoke .wh-arm { stroke: var(--sc, var(--gold)); stroke-width: 2; opacity: 0.5; transition: all 0.16s ease; }
.wh-spoke .wh-grip { fill: var(--bg2); stroke: var(--sc, var(--gold)); stroke-width: 2; transition: all 0.16s cubic-bezier(.2,.8,.3,1); }
.wh-spoke:hover .wh-grip, .wh-spoke:active .wh-grip { r: 8; filter: drop-shadow(0 0 9px var(--sc, var(--gold))); }
.wh-spoke.maxed .wh-grip { fill: var(--sc, var(--gold)); }
.wh-spoke.empty .wh-grip { stroke: rgba(198,220,214,0.32); }
.wh-tag { fill: var(--ink-dim); font-size: 10.5px; letter-spacing: 0.14em; text-transform: uppercase; }
.wh-val { fill: var(--sc, var(--gold)); font-size: 13px; font-variant-numeric: tabular-nums; font-weight: 600; }
.wh-spoke.empty .wh-val { fill: var(--ink-faint); }
.wh-hub { fill: var(--bg2); stroke: rgba(232,180,92,0.34); stroke-width: 1; }
.wh-left { fill: var(--gold); font-size: 19px; font-variant-numeric: tabular-nums; font-weight: 600; }
.wh-left.done { fill: var(--green); } .wh-left.spent { fill: var(--red); }
.wh-leftlab { fill: var(--ink-faint); font-size: 8.5px; letter-spacing: 0.2em; text-transform: uppercase; }
/* the numbers are still here, folded: exact entry and a keyboard path are real needs */
.alloc-exact { max-width: 620px; margin: 12px auto 6px; border-top: 1px solid var(--line); }
.alloc-exact > summary { cursor: pointer; padding: 11px 2px; font-size: 12px; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); list-style: none; }
.alloc-exact > summary::-webkit-details-marker { display: none; }
.alloc-exact > summary::before { content: "▸ "; color: var(--gold-dim); }
.alloc-exact[open] > summary::before { content: "▾ "; }
.alloc-exact > summary:hover { color: var(--ink-dim); }
@media (prefers-reduced-motion: reduce) { .wh-shape, .wh-spoke .wh-arm, .wh-spoke .wh-grip { transition: none; } }

/* =========================================================================================
   THE WALKABLE PLACE
   The stage stops being a picture with a list under it. `.place-world` is the location's whole
   400x96 banner laid out at full width inside a stage that only shows part of it, so walking
   pans the camera across art that already existed and was previously cropped away. Props are
   positioned in world pixels and only ever moved, never re-rendered. Everything that used to
   be a permanent button lives behind `.place-menu`, which is a deliberate halt.
   ========================================================================================= */
.hub.place { max-width: 900px; margin: 0 auto; }
.place-stage { position: relative; overflow: hidden; cursor: default; }
.place-world { position: absolute; top: 0; left: 0; height: 100%; will-change: transform; }
/* the mid plane is split so the PAINTING can shear/dolly on look while the interaction layer
   (props, you, ground) stays pinned to the camera -- the art/play split. Both fill the plane
   exactly; play sits above art. With the ?depth=1 prototype off, mid-art never transforms. */
.mid-art { position: absolute; inset: 0; z-index: 0; will-change: transform; }
/* the mid painting's depth sub-bands: all share .scene-art framing (absolute, inset:0, full size)
   so they overlay exactly; DOM order (back, main, front) is their stacking. Each is transformed and
   hazed by its own depth in placePaint. */
.mid-band { will-change: transform, filter; }
.mid-play { position: absolute; inset: 0; z-index: 1; }
.place-world .scene-art { position: absolute; inset: 0; width: 100%; height: 100%; z-index: 0; }
.place-world .stage-foot { z-index: 1; }
.place-props { position: absolute; inset: 0; z-index: auto; }
.place-prop { position: absolute; transform: translateX(-50%); pointer-events: auto; cursor: pointer; }
.place-props .nook-mark { top: auto; transform: translateX(-50%); }
/* in the world the figures are smaller than the old fixed composition, which framed three at once */
.place-props .npc-fig { opacity: 0.98; }
/* folk stand ON the ground: a contact shadow pooled at the feet, leaning from this place's own light
   (--shdx / --shsx) and fading with distance (--shop) -- the same reading as the wayfarer's shadow,
   so the townsfolk stop floating. Only figure props get it; glints and way-arrows are not bodies.
   Uses ::after because ::before is already the mentor/seek marker on these figures. */
.place-props .npc-fig.place-prop::after {
  content: ""; position: absolute; z-index: -1; left: 50%; bottom: -2px;
  width: 150%; height: 9px; pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgba(3,6,4,var(--shop, 0.34)), rgba(3,6,4,0) 72%);
  transform: translateX(-50%) translateX(calc(var(--shdx, 0) * 1px)) scaleX(var(--shsx, 1));
  transform-origin: 50% 50%;
}
.place-props .npc-name { opacity: 0.45; }
.place-props .npc-fig { opacity: 0.98; }
.place-you { left: 0; transition: none; }

/* what is within arm's reach lifts and lights, so the world tells you what it will let you do */
.place-prop.in-reach { filter: drop-shadow(0 0 9px rgba(232,180,92,0.75)) drop-shadow(0 5px 8px rgba(0,0,0,0.55)); }
.place-props .npc-fig.in-reach .npc-name { opacity: 1; }

/* the one line of text the place is allowed while you are walking */
.place-prompt {
  position: absolute; left: 0; right: 0; bottom: 46px; z-index: 6; text-align: center;
  pointer-events: none; opacity: 0; transform: translateY(6px);
  transition: opacity 0.18s ease, transform 0.18s cubic-bezier(0.22,1,0.36,1);
}
.place-prompt.on { opacity: 1; transform: none; }
.place-prompt .pp-in {
  display: inline-flex; align-items: baseline; gap: 8px; padding: 7px 15px;
  background: rgba(6,10,13,0.82); border: 1px solid rgba(232,180,92,0.4);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
  font-family: var(--serif); font-size: 15px; color: #f4e8ca;
}
.place-prompt b { color: var(--gold); font-weight: 500; }
.place-prompt .pp-what { font-style: italic; font-size: 13px; color: var(--ink-dim); }
.place-prompt kbd, .place-keys kbd, .place-e kbd {
  font: inherit; font-size: 11px; letter-spacing: 0.04em; padding: 1px 5px; color: var(--gold);
  border: 1px solid rgba(232,180,92,0.45); background: rgba(232,180,92,0.1);
}
.place-keys {
  position: absolute; left: 14px; bottom: 12px; z-index: 6; pointer-events: none;
  font-size: 11px; letter-spacing: 0.04em; color: var(--ink-faint); text-shadow: 0 1px 4px #000;
}
.place-e {
  position: absolute; right: 14px; bottom: 12px; z-index: 7; cursor: pointer; font: inherit; font-size: 12.5px;
  display: flex; align-items: center; gap: 8px; padding: 8px 13px; color: var(--ink);
  background: linear-gradient(180deg, rgba(29,44,50,0.92), rgba(18,28,32,0.9));
  border: 1px solid rgba(232,180,92,0.4); border-bottom: 2px solid var(--gold-dim);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
  transition: transform 0.18s cubic-bezier(0.22,1,0.36,1), border-color 0.18s ease;
}
.place-e:hover { transform: translateY(-2px); border-color: var(--gold); }

/* the halt: the world stops and stands back while you decide */
.place-stage.halted .place-world { filter: saturate(0.55) brightness(0.5) blur(1.4px); transform-origin: 50% 50%; }
.place-stage.halted .place-prompt, .place-stage.halted .place-keys { opacity: 0; }
.place-world { transition: filter 0.28s ease; }

.place-menu {
  position: absolute; inset: 0; z-index: 20; display: none; align-items: center; justify-content: center;
  padding: 18px;
}
.place-menu.open { display: flex; animation: pm-in 0.2s ease both; }
@keyframes pm-in { from { opacity: 0; } to { opacity: 1; } }
.pm-card {
  width: min(600px, 100%); max-height: 90%; overflow: auto; padding: 16px 18px 18px;
  background: linear-gradient(180deg, rgba(16,25,29,0.98), rgba(10,17,20,0.98));
  border: 1px solid rgba(232,180,92,0.34); box-shadow: 0 24px 70px rgba(0,0,0,0.7);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.place-menu.open .pm-card { animation: pm-card-in 0.28s cubic-bezier(0.16,1,0.3,1) both; }
@keyframes pm-card-in { from { transform: translateY(14px); opacity: 0; } to { transform: none; opacity: 1; } }
.pm-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 12px; }
.pm-where { font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold-dim); }
.pm-close { font: inherit; font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; cursor: pointer;
  color: var(--ink-faint); background: none; border: 1px solid var(--line); padding: 3px 9px; }
.pm-close:hover { color: var(--ink); border-color: var(--gold-dim); }
.place-way { background: none; border: 0; padding: 0; }

@media (prefers-reduced-motion: reduce) {
  .place-prompt, .place-world { transition: none; }
  .place-menu.open, .place-menu.open .pm-card { animation: none; }
}
@media (max-width: 560px) {
  .place-keys { display: none; }
  .place-prompt { bottom: 58px; }
}

/* ---- the plate: an encounter over the place you are standing in ----------------------------
   The world does not go away. It halts, stands back, and the exchange rises over the lower part
   of the frame and on down the page if it needs the room. The encounter screens carry their own
   picture of the location, which is redundant when the real one is right there, so inside a
   plate that picture is dropped and the live stage shows through above the text. */
.hub.place { position: relative; }
.place-plate { position: absolute; left: 0; right: 0; top: 38%; z-index: 15; display: none; }
.place-plate.open { display: block; animation: plate-rise 0.36s cubic-bezier(0.16,1,0.3,1) both; }
@keyframes plate-rise { from { transform: translateY(30px); opacity: 0; } to { transform: none; opacity: 1; } }
.place-plate > * { margin: 0; padding: 0; max-width: none; animation: none; }
/* the real place is behind this, so the screen's own copy of it is not needed */
.place-plate .enc-stage, .place-plate .scene-stage, .place-plate .stage-cap, .place-plate .nook-stage { display: none; }
.place-plate .scene, .place-plate .outcome, .place-plate .nook-body, .place-plate .approach, .place-plate .simple {
  background: linear-gradient(180deg, rgba(7,12,15,0) 0%, rgba(7,12,15,0.86) 9%, rgba(9,15,18,0.985) 24%);
  padding: 42px 22px 22px; animation: none;
}
.place-plate .dialogue { min-height: 0; margin: 0 0 14px; padding: 15px 17px; border-radius: 0;
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)); }
.place-plate .scene-text, .place-plate .outcome-text { font-size: 15.5px; line-height: 1.62; margin: 0 0 14px; }
.place-plate .choices { gap: 8px; }
.place-plate .choice { padding: 11px 15px; }
.place-plate .scene-title { font-size: clamp(19px, 3.4vw, 25px); margin: 0 0 10px; }
/* while an exchange is up the walking HUD stands down */
.place-stage.plated .place-prompt, .place-stage.plated .place-keys, .place-stage.plated .place-e { opacity: 0; pointer-events: none; }
.place-stage.plated .place-world { filter: saturate(0.72) brightness(0.66); }
@media (prefers-reduced-motion: reduce) { .place-plate.open { animation: none; } }

/* walking through to the next corner of a place: a step into the dark and out the other side */
.place-stage.crossing .place-world, .place-stage.crossing .place-prompt { animation: cross-out 0.24s ease both; }
.place-stage.crossed .place-world { animation: cross-in 0.42s ease both; }
@keyframes cross-out { to { opacity: 0.06; filter: brightness(0.3); } }
@keyframes cross-in { from { opacity: 0.06; filter: brightness(0.3); } to { opacity: 1; filter: none; } }
/* the corners of a place are a deliberate choice, so the list sits with the others behind E */
.pm-card .quarter-strip { margin: 0 0 12px; }
.pm-card .quarter { font-size: 12.5px; padding: 7px 6px 6px; }
@media (prefers-reduced-motion: reduce) {
  .place-stage.crossing .place-world, .place-stage.crossing .place-prompt, .place-stage.crossed .place-world { animation: none; }
}

/* ---- THE WOODEN ROAD SIGNS -----------------------------------------------------------------------
   A way out and the end of a path are marked by a signpost now, not by a caret and a trapezoid drawn
   at a fixed pixel size. The one thing that has to hold here is that these are OBJECTS: the height
   comes from paintProps in stage % through the play camera, so the sheet must never put a px size on
   the svg again -- `height:100%` and let the viewBox give the width, exactly as `.place-door` does.
   The timber is a warm dark for the same reason the door's frontage is: it stands in front of
   forty-nine different palettes, and being darker than the painting is correct for something nearer.
   What carries it against a dark scene is the lit edge, not a lighter fill. */
.place-way svg, .path-end svg { height: 100%; width: auto; display: block; }
.place-way.to-l { transform: translateX(-50%) scaleX(-1); }
/* AND IT KEEPS POINTING THAT WAY WHEN YOU REACH IT. `.place-prop.in-reach` sets its own transform at
   the SAME specificity as the rule above and later in the sheet, so coming into reach dropped the
   mirror: measured standing on the left-hand way, the matrix went from scaleX(-1) to scaleX(+1) and
   the board's overhang from -23.7px to +23.7px. The atelier's catalogue entry for this part has
   recorded that behaviour all along and it was harmless while the mark was a near-symmetric caret --
   a fingerpost that reverses at the one moment anybody reads it is pointing at the wrong place.
   Specificity, not order, because `.place-prop.in-reach` must keep working for every other prop. */
.place-props .place-way.to-l.in-reach { transform: translateX(-50%) scaleX(-1) translateY(-3px); }
.sg-post { fill: rgba(54,41,27,.95); }
.sg-board { fill: rgba(67,50,32,.96); }
.sg-brace { fill: rgba(45,34,22,.95); }
.sg-lit { fill: rgba(240,212,158,.4); }
.sg-dk { fill: rgba(0,0,0,.5); }
.sg-grain { stroke: rgba(0,0,0,.34); stroke-width: .5; fill: none; }
.sg-damp { fill: rgba(0,0,0,.26); }
/* THE HEAPED EARTH IS LIGHTER THAN THE TIMBER, not darker. Drawn in the post's own shade it was
   invisible against both the post and every night scene, which is a mark that costs bytes and says
   nothing -- the same below-threshold failure the 17-material pass was written to end. */
.sg-earth { fill: rgba(96,76,52,.8); }
.sg-peg { fill: rgba(20,15,10,.85); }
/* an incision is read by its shadow AND the lit lip a chisel leaves below it; one line alone is a
   scratch. Same two-mark reasoning as K.edges on the scene masses. */
.sg-cut { stroke: rgba(0,0,0,.52); stroke-width: 1.1; fill: none; stroke-linecap: round; stroke-linejoin: round; }
.sg-cut-l { stroke: rgba(236,206,150,.2); stroke-width: .8; fill: none; stroke-linecap: round; stroke-linejoin: round; }
/* THE BOARD CATCHES THE LIGHT AS YOU COME UP ON IT, the same way a doorway's lamp does. A sign that
   is equally legible from across the square and at your elbow tells you nothing about where you are;
   this is what replaces the old caret's flat gold stroke without going back to a HUD colour. */
.place-way .sg-lit, .path-end .sg-lit { fill: rgba(240,212,158,calc(0.32 + var(--near, 0) * 0.28)); }
.place-way:hover .sg-cut-l, .place-way:focus-visible .sg-cut-l,
.path-end:hover .sg-cut-l, .path-end:focus-visible .sg-cut-l { stroke: var(--gold); }
/* IT STANDS ON THE GROUND, so it pools a contact shadow leaning from this place's own light, on the
   same --shdx / --shsx / --shop that the townsfolk use and that paintProps now feeds it. Narrower
   than a body's, because a post is a narrow thing and the pool has to look like it belongs to this
   object rather than to something standing behind it. */
.place-props .place-way::after, .place-props .path-end::after {
  content: ""; position: absolute; z-index: -1; left: 50%; bottom: -2px;
  width: 78%; height: 8px; pointer-events: none;
  background: radial-gradient(50% 50% at 50% 50%, rgba(3,6,4,var(--shop, 0.34)), rgba(3,6,4,0) 74%);
  transform: translateX(-50%) translateX(calc(var(--shdx, 0) * 1px)) scaleX(var(--shsx, 1));
  transform-origin: 50% 50%;
}
/* A LEFT-HAND WAY IS THE WHOLE ELEMENT MIRRORED, and the mirror takes the shadow with it: a lean of
   +dx in local space renders as -dx on the glass, which would put every left-hand sign's shadow on
   the side the light is coming FROM. Negated here so the two ways out of a corner lean the same way
   as each other and as the people standing between them. */
.place-props .place-way.to-l::after {
  transform: translateX(-50%) translateX(calc(var(--shdx, 0) * -1px)) scaleX(var(--shsx, 1));
}

/* ---- the last word before the road: the reveal as a decision, not a page --------------------- */
.reveal.confirm { max-width: 720px; }
.confirm-carry { display: flex; flex-direction: column; gap: 9px; margin: 18px 0 4px; padding: 15px 16px;
  background: linear-gradient(180deg, rgba(29,44,50,0.62), rgba(18,28,32,0.5));
  border: 1px solid var(--line);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)); }
.cf-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.cf-lab { flex: 0 0 150px; font-size: 10.5px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-faint); }
.cf-val { display: flex; flex-wrap: wrap; gap: 6px; }
.cf-chip { font-family: var(--serif); font-size: 13.5px; padding: 3px 10px; color: #efe3c6;
  border: 1px solid rgba(232,180,92,0.28); background: rgba(232,180,92,0.07);
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px); }
.cf-chip.path { color: var(--gold); border-color: rgba(232,180,92,0.5); }
.cf-chip.carry { color: #d9a78f; border-color: rgba(212,120,93,0.36); background: rgba(212,120,93,0.08); }
.cf-chip.none { color: var(--ink-faint); font-style: italic; }
.confirm-ask { font-family: var(--serif); font-style: italic; font-size: 14.5px; color: var(--ink-dim); margin: 16px 0 4px; }
.reveal.confirm .reveal-actions { flex-wrap: wrap; gap: 9px; }
.reveal.confirm #walkRoad { font-size: 16px; padding: 12px 24px; }
@media (max-width: 560px) { .cf-lab { flex-basis: 100%; } }

/* =========================================================================================
   CINEMA — one movement at a time, over the world it is talking about
   The shared set-piece player. Art fills the frame, one line sits under it, a rail of pips says
   how far in you are, and the way on is in the DOM from the first frame so nothing is trapped
   behind an animation. Reduced motion collapses it to one frame and every line at once.
   ========================================================================================= */
.cine { max-width: 900px; margin: 0 auto; }
.cine-stage {
  position: relative; overflow: hidden; aspect-ratio: 5 / 3; max-height: 62vh; cursor: pointer;
  background: #05090c; border: 1px solid rgba(232,180,92,0.2);
  box-shadow: 0 22px 70px rgba(0,0,0,0.62);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.cine-art { position: absolute; inset: 0; }
.cine-beat { position: absolute; inset: 0; opacity: 0; transition: opacity 0.9s ease; }
.cine-beat.on { opacity: 1; }
.cine-beat .scene-art {
  position: absolute; left: 0; right: 0; top: 50%; width: 100%; height: auto; aspect-ratio: 400 / 96;
  transform: translateY(-50%) scale(1.02); transition: transform 7s linear;
}
.cine-beat.on .scene-art { transform: translateY(-50%) scale(1.09); }
.cine-band { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, #05090c 0%, #05090c 27%, transparent 29%, transparent 71%, #05090c 73%, #05090c 100%); }
.cine-veil { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(5,9,12,0.72) 0%, rgba(5,9,12,0) 30%, rgba(5,9,12,0) 62%, rgba(5,9,12,0.78) 82%); }
.cine-head { position: absolute; top: 0; left: 0; right: 0; padding: 20px 22px 0; z-index: 3; pointer-events: none; text-align: center; }
.cine-kicker { font-size: 10px; letter-spacing: 0.3em; text-transform: uppercase; color: var(--gold-dim); }
.cine-title { font-family: var(--serif); font-weight: 500; margin: 6px 0 0; color: #f6ecd3;
  font-size: clamp(21px, 4vw, 34px); text-shadow: 0 2px 14px rgba(0,0,0,0.85); }
.cine-say { position: absolute; left: 7%; right: 7%; bottom: 46px; z-index: 3; pointer-events: none; text-align: center; }
.cine-line { margin: 0; font-family: var(--serif); font-size: clamp(14px, 2.3vw, 17px); line-height: 1.65;
  color: #ece1c8; text-shadow: 0 1px 3px #04080b, 0 2px 16px rgba(0,0,0,0.95); }
.cine-line.in { animation: cine-say 0.7s cubic-bezier(0.16,1,0.3,1) both; }
@keyframes cine-say { from { opacity: 0; transform: translateY(9px); } to { opacity: 1; transform: none; } }
.cine-rail { position: absolute; left: 0; right: 0; bottom: 18px; z-index: 3; display: flex; justify-content: center; gap: 7px; pointer-events: none; }
.cine-pip { width: 22px; height: 2px; background: rgba(233,231,221,0.22); transition: background 0.4s ease; }
.cine-pip.on { background: var(--gold); }
.cine-skip { position: absolute; right: 12px; bottom: 12px; z-index: 4; cursor: pointer; font: inherit;
  font-size: 11px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-dim);
  background: rgba(6,10,13,0.7); border: 1px solid rgba(232,180,92,0.26); padding: 5px 11px;
  clip-path: polygon(5px 0, 100% 0, 100% calc(100% - 5px), calc(100% - 5px) 100%, 0 100%, 0 5px); }
.cine-skip:hover { color: var(--ink); border-color: var(--gold-dim); }
.cine-note { max-width: 62ch; margin: 18px auto 0; text-align: center; font-family: var(--serif);
  font-size: clamp(13.5px, 2.2vw, 15.5px); line-height: 1.7; color: var(--ink-dim); }
.cine-foot { display: flex; justify-content: center; flex-wrap: wrap; gap: 10px; margin-top: 18px; opacity: 0; transition: opacity 0.5s ease; }
.cine-foot.ready { opacity: 1; }
.cine.still .cine-stage { cursor: default; }
.cine.still .cine-say {
  position: absolute; left: 6%; right: 6%; bottom: 16px; z-index: 6;
  padding: 14px 18px; background: linear-gradient(180deg, rgba(5,9,12,0) 0%, rgba(5,9,12,0.82) 22%, rgba(5,9,12,0.94) 100%);
}
.cine.still .cine-rest .cine-line { margin-top: 10px; }

/* what a year does to the country it is over */
.w-drought .cine-beat { filter: sepia(0.42) saturate(0.62) brightness(1.05) contrast(1.06); }
.w-plague .cine-beat { filter: saturate(0.4) brightness(0.74) hue-rotate(-14deg); }
.w-cruel-rule .cine-beat { filter: saturate(0.7) brightness(0.68) contrast(1.14); }
.w-lawless .cine-beat { filter: saturate(0.66) brightness(0.72) contrast(1.1); }
.w-plenty .cine-beat { filter: saturate(1.18) brightness(1.1); }
.w-learning .cine-beat { filter: saturate(1.05) brightness(1.06); }
.w-just-rule .cine-beat { filter: saturate(1.06) brightness(1.04); }
.w-pilgrimage .cine-beat { filter: saturate(0.94) brightness(1.02) sepia(0.14); }
.w-drought .cine-veil { background: linear-gradient(180deg, rgba(5,9,12,0.86) 0%, rgba(120,96,52,0.16) 40%, rgba(5,9,12,0.9) 88%); }
.w-plague .cine-veil { background: linear-gradient(180deg, rgba(5,9,12,0.88) 0%, rgba(96,110,70,0.2) 42%, rgba(5,9,12,0.92) 88%); }
.w-cruel-rule .cine-veil { background: linear-gradient(180deg, rgba(5,9,12,0.9) 0%, rgba(60,20,18,0.18) 44%, rgba(5,9,12,0.92) 88%); }

@media (prefers-reduced-motion: reduce) {
  .cine-beat, .cine-line.in, .cine-foot, .cine-beat .scene-art { transition: none; animation: none; }
}
@media (max-width: 560px) {
  .cine-stage { aspect-ratio: 4 / 3; }
  .cine-say { left: 5%; right: 5%; bottom: 40px; }
}

/* where you come from, on the card as well as in the pass */
.origin.has-art { padding-top: 0; overflow: hidden; }
.origin-art { position: relative; margin: 0 -16px 12px; aspect-ratio: 300 / 96; max-height: 104px; overflow: hidden; border-bottom: 1px solid var(--line); }
.origin-art svg { width: 100%; height: 100%; display: block; }
.origin-art::after { content: ""; position: absolute; inset: 0; box-shadow: inset 0 -18px 26px -14px rgba(5,9,12,0.85); pointer-events: none; }
.origin.picked .origin-art { border-bottom-color: var(--gold-dim); }
.cine-origin .cine-beat:first-child .scene-art { aspect-ratio: 300 / 96; }

/* the virtues at work: a caption that names what is being measured, and the small motions */
.cine-cap { margin-top: 9px; font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold); }
.cine.still .cine-cap { display: inline; margin: 0 6px 0 0; }
.sd-swing { transform-origin: 56px 78px; animation: sd-swing 2.2s cubic-bezier(0.4,0,0.3,1) infinite; }
@keyframes sd-swing { 0%, 12% { transform: rotate(-38deg); } 34% { transform: rotate(6deg); } 46%, 100% { transform: rotate(-38deg); } }
.sd-drop { animation: sd-drop 1.7s linear infinite; }
@keyframes sd-drop { 0% { opacity: 0; transform: translateY(-12px); } 15% { opacity: 0.9; } 78% { opacity: 0.9; transform: translateY(46px); } 88%, 100% { opacity: 0; transform: translateY(48px); } }
.sd-flame { transform-origin: 150px 56px; animation: sd-flame 2.4s ease-in-out infinite; }
@keyframes sd-flame { 0%, 100% { transform: rotate(calc(var(--sway) * -1)); } 50% { transform: rotate(var(--sway)); } }
.sd-swing { animation: sd-swing 2.2s cubic-bezier(0.4,0,0.3,1) infinite; }
.sd-chips { animation: sd-chips 2.2s cubic-bezier(0.4,0,0.3,1) infinite; }
@keyframes sd-chips { 0%, 30% { opacity: 0; transform: translateY(0); } 40% { opacity: 0.9; } 70%, 100% { opacity: 0; transform: translate(4px, 14px); } }
.sd-gust path { animation: sd-gust 3s ease-in-out infinite; }
@keyframes sd-gust { 0%, 100% { transform: translateX(0); opacity: 0.5; } 50% { transform: translateX(10px); opacity: 0.9; } }
.sd-drift .sd-mote { animation: sd-mote 6s ease-in-out infinite; }
@keyframes sd-mote { 0%, 100% { transform: translate(0, 0); opacity: 0.28; } 50% { transform: translate(8px, -5px); opacity: 0.5; } }
@media (prefers-reduced-motion: reduce) { .sd-swing, .sd-drop, .sd-flame, .sd-chips, .sd-gust path, .sd-drift .sd-mote { animation: none; } }

/* every gift carries its own mark, coloured by the family it belongs to */
.talent { position: relative; }
.talent-emblem { position: absolute; top: 12px; right: 12px; width: 34px; height: 34px; opacity: 0.85; }
.talent-emblem svg { width: 100%; height: 100%; display: block; }
.talent.picked .talent-emblem { opacity: 1; filter: drop-shadow(0 0 7px rgba(232,180,92,0.45)); }
.talent-name { padding-right: 42px; }

/* what the carrying looks like: the hard half moves, the steady half does not */
.af-steady { opacity: 0.98; }
.af-step { animation: af-step 2.6s cubic-bezier(0.5,0,0.4,1) infinite; }
@keyframes af-step { 0%, 14% { transform: translate(0,0); } 40% { transform: translate(16px,-7px); } 54%, 100% { transform: translate(16px,-7px); } }
.af-dim { animation: af-dim 4.2s ease-in-out infinite; }
@keyframes af-dim { 0%, 100% { opacity: 0.75; } 50% { opacity: 0.22; } }
.af-wave path { animation: af-wave 2.6s ease-in-out infinite; }
@keyframes af-wave { 0%, 100% { opacity: 0.7; } 55% { opacity: 0.12; } }
.af-breathe { animation: af-breathe 3.4s ease-in-out infinite; transform-origin: center; }
@keyframes af-breathe { 0%, 100% { transform: scaleY(0.86); } 50% { transform: scaleY(1.06); } }
.af-mark { animation: af-fade-in 3.2s ease-in-out infinite; }
@keyframes af-fade-in { 0%, 100% { opacity: 0.25; } 50% { opacity: 1; } }
.af-pull { animation: af-pull 3s ease-in-out infinite; }
@keyframes af-pull { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(-7px); } }
.af-shake { animation: af-shake 0.5s steps(2, end) infinite; }
@keyframes af-shake { 0% { transform: translate(0,0); } 50% { transform: translate(1.4px,-1.4px); } 100% { transform: translate(0,0); } }
.af-sag { animation: af-sag 4.6s ease-in-out infinite; }
@keyframes af-sag { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(5px); } }
.af-door { animation: af-door 5s ease-in-out infinite; transform-origin: 150px 56px; }
@keyframes af-door { 0%, 22% { transform: rotateY(0deg) skewX(0deg); } 60%, 100% { transform: skewX(-13deg); } }
.af-beat { animation: af-fade-in 2.4s ease-in-out infinite; }
.af-word { animation: af-word 2.4s ease-out infinite; }
@keyframes af-word { 0%, 62% { opacity: 0; transform: translateX(-8px); } 78%, 100% { opacity: 1; transform: none; } }
.af-stray { animation: af-stray 5.2s ease-in-out infinite; }
@keyframes af-stray { 0%, 100% { opacity: 0.85; } 50% { opacity: 0.3; } }
.af-blink { animation: af-blink 2.8s ease-in-out infinite; }
@keyframes af-blink { 0%, 40%, 100% { opacity: 0.85; } 55%, 80% { opacity: 0.08; } }
.af-fade { animation: af-fadeout 3.6s ease-in-out infinite; }
@keyframes af-fadeout { 0%, 20% { opacity: 0.9; } 70%, 100% { opacity: 0.1; } }
@media (prefers-reduced-motion: reduce) {
  .af-step, .af-dim, .af-wave path, .af-breathe, .af-mark, .af-pull, .af-shake, .af-sag,
  .af-door, .af-beat, .af-word, .af-stray, .af-blink, .af-fade { animation: none; }
}

/* wayfinding for the main questline: quieter than the "a thread pulls at you here" nudge, because
   it is a compass, not a summons — it tells you where the powers sit when none is due right here */
.thread-wayfind {
  font-size: 12.5px; line-height: 1.55; color: #9fb3c8; letter-spacing: 0.01em;
  border-left: 2px solid #3d548f; background: rgba(111,143,202,0.06);
  padding: 8px 13px; margin: 6px 0 10px; border-radius: 0 8px 8px 0;
}
.thread-wayfind::before { content: "↣ "; color: var(--blue); font-weight: 700; }


/* ---- the way in, in the top bar beside the title ---------------------------------------------- */
/* it belongs with the title, not floating in the middle of the bar: the auto margin on the
   right eats the free space the topbar's space-between would otherwise put on its left */
.brand-auth { margin-left: 18px; margin-right: auto; display: flex; align-items: center; }
.ba-in {
  display: inline-flex; align-items: center; gap: 9px; cursor: pointer; font: inherit; text-align: left;
  padding: 5px 12px; color: var(--ink);
  background: linear-gradient(180deg, rgba(232,180,92,0.16), rgba(232,180,92,0.05));
  border: 1px solid rgba(232,180,92,0.42); border-bottom: 2px solid var(--gold-dim);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  transition: transform 0.18s cubic-bezier(0.22,1,0.36,1), border-color 0.18s ease;
}
.ba-in:hover { transform: translateY(-1px); border-color: var(--gold); }
.ba-k { flex: 0 0 auto; width: 16px; height: 16px; }
.ba-k svg { width: 100%; height: 100%; fill: none; stroke: var(--gold); stroke-width: 1.3; }
.ba-t { display: flex; flex-direction: column; line-height: 1.15; }
.ba-t b { font-family: var(--serif); font-weight: 500; font-size: 13px; color: #f6e9cb; }
.ba-t i { font-style: normal; font-size: 9px; letter-spacing: 0.06em; text-transform: uppercase; color: var(--gold-dim); }
.ba-name { font-family: var(--serif); font-size: 13.5px; color: #f0e2c4; }
.ba-btn { font-size: 11px; }
@media (max-width: 560px) {
  .brand-auth { margin-left: 10px; }
  .ba-t i { display: none; }
}


/* ---- difficulty picker on the title screen ---------------------------------------------------- */
.diff-pick { max-width: 560px; margin: 0 auto 16px; text-align: center; }
.diff-lead { font-family: var(--serif); font-size: 13.5px; color: var(--gold-dim); letter-spacing: 0.03em; margin-bottom: 8px; }
.diff-row { display: flex; gap: 7px; justify-content: center; flex-wrap: wrap; }
.diff-opt {
  flex: 1 1 0; min-width: 96px; cursor: pointer; font: inherit; font-size: 12.5px; letter-spacing: 0.02em; color: var(--ink-dim);
  padding: 8px 10px; background: linear-gradient(180deg, rgba(29,44,50,0.7), rgba(18,28,32,0.66));
  border: 1px solid var(--line); border-bottom: 2px solid rgba(232,180,92,0.12);
  clip-path: polygon(6px 0, 100% 0, 100% calc(100% - 6px), calc(100% - 6px) 100%, 0 100%, 0 6px);
  transition: color 0.18s, border-color 0.18s, transform 0.18s cubic-bezier(0.22,1,0.36,1);
}
.diff-opt:hover { color: var(--ink); border-color: var(--gold-dim); transform: translateY(-1px); }
.diff-opt.on {
  color: var(--gold); border-color: rgba(232,180,92,0.5); border-bottom-color: var(--gold);
  background: linear-gradient(180deg, rgba(232,180,92,0.14), rgba(18,28,32,0.66));
  box-shadow: inset 0 0 16px rgba(232,180,92,0.08);
}
.diff-desc { max-width: 52ch; margin: 9px auto 0; font-family: var(--serif); font-style: italic; font-size: 13px; line-height: 1.55; color: var(--ink-dim); min-height: 2.4em; }
@media (max-width: 560px) { .diff-opt { flex-basis: 42%; } .diff-lead { font-size: 12.5px; } }

/* =========================================================================================
   THE DECK — a chooser that is not a menu.
   The paths and the gifts used to be walls of cards, which is what the first game did and what
   every character sheet does. Here one candidate at a time is held in the same lit frame the
   cinema uses: its art behind it, its name large, one line, the verse, and what it hands you.
   Arrows / A-D / the rail walk the line; the button under the frame takes the one you are on.
   Every candidate stays in the DOM behind the shown one, so a chooser is one click deep.
   ========================================================================================= */
.deck { max-width: 980px; margin: 0 auto; }
.deck-head { text-align: center; margin-bottom: 14px; }
.deck-eyebrow { font-family: var(--serif); font-size: clamp(14px, 2.4vw, 17px); color: var(--ink-dim); font-style: italic; }
.deck-tabs { display: flex; justify-content: center; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
.deck-tab {
  font: inherit; font-size: 11.5px; letter-spacing: 0.1em; text-transform: uppercase; cursor: pointer;
  color: var(--ink-faint); background: rgba(10,16,21,0.6); border: 1px solid var(--line); padding: 5px 12px;
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm));
  transition: color 0.18s ease, border-color 0.18s ease, background 0.18s ease;
}
.deck-tab:hover { color: var(--ink); border-color: var(--gold-dim); }
.deck-tab.on { color: #0a0f14; background: var(--gold); border-color: var(--gold); }
.deck-stage {
  position: relative; overflow: hidden; aspect-ratio: 5 / 3; max-height: 64vh;
  background: #05090c; border: 1px solid rgba(232,180,92,0.24);
  box-shadow: 0 22px 70px rgba(0,0,0,0.62);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.deck-cards { position: absolute; inset: 0; }
.deck .deck-cards .deck-card {
  position: absolute; inset: 0; margin: 0; padding: 0; border: 0; border-radius: 0; background: none;
  box-shadow: none; opacity: 0; pointer-events: none; cursor: default; overflow: hidden;
}
.deck .deck-cards .deck-card.on { opacity: 1; pointer-events: auto; z-index: 2; }
.deck .deck-cards .deck-card:hover { background: none; border: 0; box-shadow: none; }
.deck .deck-cards .deck-card.on .deck-art { animation: deck-drift 12s linear both; }
.deck .deck-cards .deck-card.on .deck-face { animation: deck-face-in 0.66s cubic-bezier(0.16,1,0.3,1) both; }
.deck .deck-cards .deck-card.on.from-left .deck-face { animation-name: deck-face-in-l; }
@keyframes deck-drift { from { transform: scale(1.03); } to { transform: scale(1.13); } }
@keyframes deck-face-in { from { opacity: 0; transform: translateX(26px); } to { opacity: 1; transform: none; } }
@keyframes deck-face-in-l { from { opacity: 0; transform: translateX(-26px); } to { opacity: 1; transform: none; } }
.deck-art { position: absolute; inset: 0; }
.deck-art svg { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* a path's country is a wide strip; it fills the top of the frame and is drowned into the dark
   the name sits on, so the art and the words are one picture instead of a banner over a card */
.deck-art .deck-scene { left: 0; right: 0; top: 0; bottom: auto; width: 100%; height: 58%; }
.deck-paths .deck-shade { background: linear-gradient(180deg, rgba(5,9,12,0.55) 0%, rgba(5,9,12,0.06) 16%, rgba(5,9,12,0.42) 40%, rgba(5,9,12,0.96) 56%, #05090c 63%); }
.deck-paths .deck-face { bottom: 36px; }
.deck-shade { position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(180deg, rgba(5,9,12,0.72) 0%, rgba(5,9,12,0.12) 26%, rgba(5,9,12,0.5) 52%, rgba(5,9,12,0.95) 84%); }
.deck-face { position: absolute; left: 7%; right: 7%; bottom: 40px; z-index: 3; text-align: center; pointer-events: none; }
.deck-kicker { font-size: 10px; letter-spacing: 0.3em; text-transform: uppercase; color: var(--gold-dim); margin-bottom: 7px; }
.deck-name { font-family: var(--serif); font-weight: 500; margin: 0; color: #f6ecd3;
  font-size: clamp(22px, 4.2vw, 34px); text-shadow: 0 2px 14px rgba(0,0,0,0.9); }
.deck-sub { font-size: 11.5px; letter-spacing: 0.12em; color: var(--gold-dim); margin-top: 5px; }
.deck-line { margin: 11px auto 0; max-width: 56ch; font-family: var(--serif);
  font-size: clamp(13px, 2.1vw, 15.5px); line-height: 1.6; color: #e6dcc4; text-shadow: 0 1px 3px #04080b, 0 2px 14px rgba(0,0,0,0.95); }
.deck-verse { position: relative; max-width: 52ch; margin: 12px auto 0; padding: 8px 20px; min-height: 1px; }
.deck-verse:empty { display: none; }
.deck-verse::before, .deck-verse::after { content: ""; position: absolute; top: 0; bottom: 0; width: 7px; }
.deck-verse::before { left: 0; border-left: 1px solid var(--gold-dim); border-top: 1px solid var(--gold-dim); border-bottom: 1px solid var(--gold-dim); }
.deck-verse::after { right: 0; border-right: 1px solid var(--gold-dim); border-top: 1px solid var(--gold-dim); border-bottom: 1px solid var(--gold-dim); }
.deck-verse .pv-text { display: block; font-family: var(--serif); font-style: italic; font-size: 13px; line-height: 1.6; color: #dcd0b4; }
.deck-verse .pv-ref { display: block; margin-top: 4px; font-size: 10px; letter-spacing: 0.18em; text-transform: uppercase; color: var(--gold-dim); }
.deck-chips { display: flex; justify-content: center; flex-wrap: wrap; gap: 7px; margin-top: 12px; }
.deck-chip { font-size: 11.5px; line-height: 1.4; padding: 4px 11px; color: #d8cdb5;
  background: rgba(6,10,13,0.72); border: 1px solid rgba(232,180,92,0.3);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)); }
.deck-arm {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 5; cursor: pointer;
  width: 40px; height: 62px; font: inherit; font-size: 26px; line-height: 1; color: var(--ink-dim);
  background: rgba(5,9,12,0.55); border: 1px solid rgba(232,180,92,0.22);
  transition: color 0.18s ease, background 0.18s ease, border-color 0.18s ease;
}
.deck-arm:hover { color: var(--gold); background: rgba(5,9,12,0.85); border-color: var(--gold-dim); }
.deck-arm.prev { left: 0; border-left: 0; }
.deck-arm.next { right: 0; border-right: 0; }
.deck-count { position: absolute; top: 12px; right: 14px; z-index: 5; font-size: 11px; letter-spacing: 0.16em;
  color: var(--ink-faint); text-shadow: 0 1px 6px #000; }
.deck-count .deck-of { opacity: 0.6; }
.deck-rail { position: absolute; left: 0; right: 0; bottom: 14px; z-index: 5; display: flex; justify-content: center; gap: 6px; }
.deck-pip { width: 20px; height: 2px; padding: 0; border: 0; cursor: pointer; background: rgba(233,231,221,0.22); transition: background 0.3s ease; }
.deck-pip:hover { background: rgba(233,231,221,0.5); }
.deck-pip.on { background: var(--gold); }
.deck-aside { margin-top: 16px; }
.deck-held { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 10px; }
.deck-held-label { font-size: 10px; letter-spacing: 0.22em; text-transform: uppercase; color: var(--ink-faint); }
.deck-held-one { display: inline-flex; align-items: center; gap: 9px; padding: 7px 13px 7px 9px;
  border: 1px solid var(--line); border-left: 3px solid var(--cc, var(--gold-dim)); background: rgba(10,16,21,0.6);
  clip-path: polygon(var(--cut-sm) 0, 100% 0, 100% calc(100% - var(--cut-sm)), calc(100% - var(--cut-sm)) 100%, 0 100%, 0 var(--cut-sm)); }
.deck-held-mark { width: 26px; height: 26px; flex: none; opacity: 0.9; }
.deck-held-mark svg { width: 100%; height: 100%; display: block; }
.deck-held-name { font-family: var(--serif); font-size: 14px; color: var(--ink); }
.deck-held-eff { font-size: 11.5px; color: var(--ink-faint); }
.deck-foot { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 10px; margin-top: 18px; }
.deck-take { min-width: 15ch; }
.gf-mark { transform-box: view-box; }
.gf-turn { animation: gf-turn 46s linear infinite; }
@keyframes gf-turn { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .gf-turn { animation: none; }
  .deck .deck-cards .deck-card.on .deck-art,
  .deck .deck-cards .deck-card.on .deck-face { animation: none; }
}
/* On a phone the frame stops being a 5:3 picture and becomes a tall window: the art takes the
   top of it and the words get the room they need underneath, rather than being crushed out of it. */
@media (max-width: 620px) {
  .deck-stage { aspect-ratio: auto; height: min(72vh, 560px); max-height: none; }
  .deck-art .deck-scene { height: 42%; }
  .deck-paths .deck-shade { background: linear-gradient(180deg, rgba(5,9,12,0.55) 0%, rgba(5,9,12,0.06) 12%, rgba(5,9,12,0.4) 28%, rgba(5,9,12,0.96) 41%, #05090c 48%); }
  .deck-face { left: 5%; right: 5%; bottom: 30px; }
  .deck-name { font-size: 23px; }
  .deck-line { font-size: 13px; margin-top: 9px; }
  .deck-verse { margin-top: 10px; padding: 6px 16px; }
  .deck-verse .pv-text { font-size: 12px; line-height: 1.55; }
  .deck-chips { margin-top: 9px; gap: 5px; }
  .deck-chip { font-size: 11px; padding: 3px 9px; }
  .deck-arm { width: 32px; height: 50px; font-size: 22px; top: 24%; } /* over the art, clear of the words */
  .deck-held { gap: 7px; }
  .deck-held-eff { display: none; }
}

/* =========================================================================================
   THE BUTTON — the last thing on screen that still belonged to the first game.
   That was a soft gold pill with dark text: a web button, and it read as one on every screen
   it stood on. This one is a struck plate. It rests dark with a gold hairline and gold serif
   letters, carrying the same cut corner and corner-mark as every other surface here, and it
   FILLS with gold under the hand -- the light arrives when you commit, not before. Ghost stays
   the quieter twin, so the hierarchy that every screen already relies on does not move.
   ========================================================================================= */
.btn {
  font-family: var(--serif); font-size: 15.5px; font-weight: 500; letter-spacing: 0.015em;
  color: #f0dfb4;
  background: linear-gradient(180deg, rgba(232,180,92,0.19), rgba(232,180,92,0.05) 58%, rgba(232,180,92,0.13));
  border: 1px solid var(--gold-dim);
  padding: 11px 24px;
  box-shadow: inset 0 1px 0 rgba(246,236,211,0.14), 0 6px 20px -14px rgba(0,0,0,0.9);
  transition: color 0.2s ease, background 0.24s ease, border-color 0.2s ease,
              box-shadow 0.28s ease, transform 0.12s cubic-bezier(0.22,1,0.36,1);
}
.btn:hover {
  color: #17120a; border-color: var(--gold); filter: none; transform: translateY(-1px);
  background: linear-gradient(180deg, #f0d99a, var(--gold) 72%, #c9a349);
  box-shadow: 0 8px 26px -10px rgba(232,180,92,0.55), inset 0 1px 0 rgba(255,255,255,0.35);
}
.btn:active { transform: translateY(1px); box-shadow: 0 2px 10px -6px rgba(232,180,92,0.5); }
.btn:disabled, .btn:disabled:hover {
  color: var(--ink-faint); background: rgba(233,231,221,0.04); border-color: var(--line);
  box-shadow: none; transform: none; opacity: 0.55;
}
.btn.ghost {
  background: transparent; color: var(--ink-dim); border-color: var(--line); box-shadow: none;
}
.btn.ghost:hover {
  color: var(--ink); background: rgba(233,220,196,0.07); border-color: var(--gold-dim);
  box-shadow: none; transform: translateY(-1px);
}
/* the corner-mark reads against a dark plate, so it lights rather than sits */
.btn:not(.ghost)::before {
  content: ""; position: absolute; left: 0; bottom: 0; width: var(--cut-sm); height: var(--cut-sm);
  background: linear-gradient(45deg, var(--gold-dim) 0 50%, transparent 50%);
  opacity: 0.75; transition: opacity 0.25s ease, background 0.25s ease; pointer-events: none; z-index: 2;
}
.btn:not(.ghost):hover::before { opacity: 1; background: linear-gradient(45deg, #17120a 0 50%, transparent 50%); }
.btn.small { font-size: 13.5px; padding: 8px 15px; }
.btn.tiny { font-size: 12.5px; padding: 5px 12px; letter-spacing: 0.04em; }

/* =========================================================================================
   THE CINEMA FRAME, RESET TO FILL.
   The stage was a wide black box with a thin strip of art floating in the middle of it: two
   thirds of the frame was letterbox, and the art read as a banner rather than a scene. Now the
   art comes down from the top edge and fills the frame, and the dark it dies into at the bottom
   is where the words live. Same behaviour, same skip, same reduced-motion path: only the
   composition moved. The deck is framed the identical way, so a set piece and a chooser sit in
   the same window.
   ========================================================================================= */
.cine { max-width: 980px; }
.cine-stage { max-height: 68vh; }
.cine-beat .scene-art,
.cine-origin .cine-beat:first-child .scene-art {
  position: absolute; left: 0; right: 0; top: 0; bottom: auto;
  width: 100%; height: 68%; aspect-ratio: auto;
  transform: scale(1.02); transform-origin: 50% 38%; transition: transform 8s linear;
}
.cine-beat.on .scene-art { transform: scale(1.11); }
.cine-band {
  background: linear-gradient(180deg,
    rgba(5,9,12,0.86) 0%, rgba(5,9,12,0.2) 13%, rgba(5,9,12,0) 28%,
    rgba(5,9,12,0.3) 50%, rgba(5,9,12,0.95) 66%, #05090c 74%);
}
.cine-veil { background: radial-gradient(122% 92% at 50% 38%, rgba(5,9,12,0) 44%, rgba(5,9,12,0.6) 100%); }
.cine-say { bottom: 52px; }
@media (prefers-reduced-motion: reduce) {
  .cine-beat .scene-art, .cine-beat.on .scene-art { transform: scale(1.04); transition: none; }
}

/* The wheel IS the screen now that the paragraph and the tabulated reveal are gone, so it is given
   the room they were taking: a lit field, framed like every other stage in the game. */
.wheel-wrap {
  max-width: 560px; margin: 6px auto 16px; padding: 22px 18px 14px;
  background: radial-gradient(78% 68% at 50% 44%, rgba(232,180,92,0.07), rgba(5,9,12,0) 70%), rgba(8,13,17,0.5);
  border: 1px solid rgba(232,180,92,0.18);
  clip-path: polygon(var(--cut) 0, 100% 0, 100% calc(100% - var(--cut)), calc(100% - var(--cut)) 100%, 0 100%, 0 var(--cut));
}
.wheel-hint { margin-top: 12px; font-family: var(--serif); font-style: italic; font-size: 13.5px; color: var(--ink-faint); }
.create-intro.tight { margin-bottom: 14px; text-align: center; }
.create-intro.tight h1 { margin: 0; }
@media (max-width: 620px) { .wheel-wrap { max-width: 100%; padding: 16px 12px 10px; } }

/* =========================================================================================
   THE PLACE, IN PLANES — the walkable world overhaul.
   The location was one flat banner with a figure sliding across it. It is four planes now: the
   sky, a far band, the ground you walk on, and a near band that passes in FRONT of you. Each
   travels against the camera at its own rate, which is the whole of the illusion -- every asset
   is still the same 2D vector art the banner is cut from. The same rates carry the LOOK: press
   and drag and the planes shear past each other by their distance from you, which reads as a
   head turning. The ground plane never shears, because you stand on it.
   ========================================================================================= */
.place-plane { position: absolute; top: 0; left: 0; height: 100%; will-change: transform; pointer-events: none; }
.place-plane .plane-art { position: absolute; inset: 0; width: 100%; height: 100%; display: block; }
/* A TILED STRIP (roads only). Each tile is the art's own 400:96 at this view height, laid end to end,
   so a long road is framed exactly as well as a short one. Odd tiles are mirrored inside their own
   svg coordinate space (see roadFlip in app.js), NOT here: a CSS flip would be overwritten by
   placePaint on the mid bands and would reverse the look-shear on alternate tiles. */
.place-plane.tiled .plane-art { inset: 0 auto 0 0; width: calc(100% / var(--tiles, 1)); left: calc(var(--i, 0) * 100% / var(--tiles, 1)); }
/* The sun is drawn ONCE across the whole strip, never per tile, or a long road rises under six suns.
   It also carries `plane-art`, so this must stay AFTER the rule above to win on source order. */
.place-plane.tiled .plane-light { inset: 0; width: 100%; left: 0; }
/* The foreground, now things standing in the play space rather than a band painted over it. Each is
   placed every frame by placePaint through the same projection the props use. */
.place-fore-obs { position: absolute; inset: 0; pointer-events: none; }
/* The far end of a path: the way narrowing away from you with light in it. Sits deep in the scene, so
   it is small until you walk at it and the play camera brings it up. */
.path-end { background: none; border: 0; padding: 0; cursor: pointer; }

/* A DOOR IN THE FRONTAGE. Sized in stage % by the play camera exactly as a figure is (placePaint),
   because the reading that matters is against the people: an opening a person could walk through.
   It is drawn as a hole rather than as an object -- the dark IS the door, and the step is what stops
   it floating.
   The lamp inside is the one thing that says somebody is home, and it comes up with `--near` rather
   than being on from across the square: a light in a doorway should be something you see as you come
   up on it. Same idiom as a nook's glint, and the same reason. */
.place-door { background: none; border: 0; padding: 0; cursor: pointer; }
.place-door svg { height: 100%; width: auto; display: block; filter: drop-shadow(0 3px 7px rgba(0,0,0,.55)); }
/* The stroke is transparent at rest and only coloured on hover/focus, rather than being added there:
   a stroke straddles its path, so declaring the width only in the hover rule would grow the opening
   by half a unit the moment the pointer touched it. */
/* THE FRONTAGE THE DOOR IS CUT INTO. It travels with the door because it has to: a painted building
   cannot hold one (audit-door-align measures 0.338px of drift per pixel of pan, against a k=1
   control that reads 0). Semi-opaque warm dark rather than an authored palette, because this stands
   in front of forty-nine different scenes and a fixed colour would be wrong in most of them; being
   DARKER than the painting is correct anyway, since it is nearer than everything behind it. */
.dr-front { pointer-events: none; }
/* THE FRONT IS MADE OF WHAT ITS BUILDING IS MADE OF. `--dr-c` is the painted plot's own wall colour,
   set per prop from the scene's building register; the fallback is the fixed dark this front carried
   while it was the only building in the picture. Everything else on the face is derived from that one
   colour rather than stated again, so a front cannot come apart into a wall of one material with a
   plinth and an eave of another -- and the derivations keep the rule that a thing NEARER the eye is
   darker than the painting behind it. */
.place-door, .place-shut { --dr-c: #221c16; }
.dr-wall { fill: color-mix(in srgb, var(--dr-c) 78%, #0b0806); fill-opacity: .93; }
/* THE MATERIAL, over the wall and under the modelling. The fill is set per element to the scene's own
   `#tx-<material>` pattern -- the very one the painted building is filled with -- so this rule only
   has to say how hard the grain bites. Grain belongs to the surface; light belongs to the room. */
.dr-tex { opacity: .55; }
/* the face of the reveal: the wall seen edge-on through its own thickness, so it is the wall's colour
   carried a little further into the dark than the face of it is */
.dr-rev { fill: color-mix(in srgb, var(--dr-c) 58%, #070505); fill-opacity: .95; }
.dr-course { stroke: rgba(0,0,0,.34); stroke-width: .5; fill: none; }
.dr-lit { fill: rgba(233,214,168,.17); }
.dr-dk { fill: rgba(0,0,0,.44); }
.dr-plinth { fill: color-mix(in srgb, var(--dr-c) 46%, #060403); fill-opacity: .92; }
.dr-eave { fill: color-mix(in srgb, var(--dr-c) 62%, #080605); fill-opacity: .96; }
.dr-eave-l { fill: rgba(233,214,168,.3); }
.dr-corbel { fill: color-mix(in srgb, var(--dr-c) 52%, #070504); fill-opacity: .92; }
.dr-lintel { fill: color-mix(in srgb, var(--dr-c) 92%, #17120d); fill-opacity: .95; }
.dr-win { fill: #0a0807; }
/* THE DOORWAY TAKES THE CLICK AND THE WALL NEVER DOES. A button is hit anywhere in its box, and
   this box is now 2.7x wider than the opening in it, so without this a press on the wall beside a
   door -- or on the ground the wall is standing on -- would be answered with "Go in" rather than
   with walking you there. :hover still resolves, because hovering a child matches the ancestor. */
.place-door { pointer-events: none; }
.place-door .dr-way { pointer-events: auto; }
.dr-dark { fill: #07060a; stroke: transparent; stroke-width: 1.1; }
.dr-step { fill: rgba(206,188,152,.42); }
.dr-lamp { fill: rgba(255,201,116,0); transition: fill .25s ease; }
.place-door.lit .dr-lamp { fill: rgba(255,201,116,calc(0.03 + var(--near, 0) * 0.2)); }
.place-door.lit .dr-floor { fill: rgba(255,214,146,calc(0.05 + var(--near, 0) * 0.42)); }
.place-door.lit .dr-step { fill: rgba(233,196,140,calc(0.42 + var(--near, 0) * 0.34)); }
.place-door:hover .dr-dark, .place-door:focus-visible .dr-dark { stroke: var(--gold); outline: none; }
/* THE LIGHT ON THE CLOTH. A lamp behind a hanging does not light the room, it lights the SHEET --
   so the warm goes on the curtain's own outline rather than through it, and comes up with `--near`
   exactly as the lamp in an open doorway does. The cloth's colours are authored per instance as
   presentation attributes (drCurtain), so nothing here may animate `fill` on the falls themselves:
   a running animation beats an authored attribute and every curtain in the game would go one
   colour. This rule only ever touches the overlay, which has no authored fill to lose. */
.dr-rod { fill: #2b2117; }
.dr-cloth-warm { fill: rgba(255,201,116,0); transition: fill .25s ease; }
.place-door.lit .dr-cloth-warm { fill: rgba(255,201,116,calc(0.02 + var(--near, 0) * 0.17)); }
/* A SHUT DOOR IS SCENERY AND MUST NEVER TAKE A CLICK. Same covenant as every painted layer in a
   place: it is in the frontage to say the street is built up, not to promise a way in. */
.place-shut { pointer-events: none; }
.place-shut svg { height: 100%; width: auto; display: block; filter: drop-shadow(0 3px 7px rgba(0,0,0,.55)); }
.place-door .nook-name { position: absolute; left: 50%; bottom: -6px; transform: translateX(-50%) translateY(100%); }
.place-prop.in-reach .place-door .nook-name, .place-door.in-reach .nook-name { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .dr-lamp, .dr-cloth-warm { transition: none; } }

/* The way into the scene, lit while you are on it or walking to it. The road is already drawn into
   the ground; this only says WHICH one is yours, because a step into the depth works on about a
   quarter of a place's width and nothing used to tell you which quarter. */
.pg-road { transition: opacity .35s ease, filter .35s ease; }
.pg-road.lit { filter: brightness(1.5) saturate(1.15) drop-shadow(0 0 7px rgba(233,214,168,.5)); }


/* ===== THE RAIL, INCORPORATED INTO THE FULL SCREEN =====================================
   Everywhere else the rail is a band along the bottom and that is right: those surfaces are columns
   of content and the rail closes them off. On the walkable place it was an opaque 124px strip eating
   the bottom 14% of the picture, with a blur and a shadow, and it repeated the arc and the age that
   .stage-arc prints two inches above it. Here it is laid OVER the world instead: no band, no border,
   no backdrop, no reserved space (--rail-h goes to 0), sat under the title block where a HUD belongs.
   The coin moved up into the arc line, so what is left is the four meters and nothing repeated. */
body:has(.hub.place) .rail {
  position: absolute; left: clamp(14px, 2vw, 26px); right: auto; bottom: auto;
  top: calc(var(--stage-top-h, 118px));
  width: max-content; padding: 0; z-index: 16;
  background: none; border-top: 0; box-shadow: none; backdrop-filter: none;
}
body:has(.hub.place) .rail-inner { max-width: none; margin: 0; width: auto; align-items: flex-start;
  flex-direction: column; gap: 6px; justify-content: flex-start; }
/* the arc, the age and the coin are all said by the title block now */
body:has(.hub.place) .rail-life { display: none; }
/* ...except a bearing, which the title block does NOT say and which matters when it appears */
body:has(.hub.place) .rail-life:has(.rl-bearing) { display: flex; justify-content: flex-start; width: auto; }
body:has(.hub.place) .rail-life:has(.rl-bearing) > :not(.rl-bearing) { display: none; }
body:has(.hub.place) .rail-pips { justify-content: flex-start; width: auto; gap: 10px; }
/* a meter over a painting has to carry its own legibility: the row reads as one quiet plate rather
   than four labels fighting the scene behind them */
body:has(.hub.place) .rail-pips { background: rgba(14,11,8,0.42); border: 1px solid rgba(217,184,102,0.14);
  border-radius: 10px; padding: 7px 11px; backdrop-filter: blur(5px); }
body:has(.hub.place) .pip { min-width: 38px; gap: 2px; }
body:has(.hub.place) .pip-val { font-size: 14px; }
body:has(.hub.place) .pip-bar { width: 34px; }
body:has(.hub.place) .pip-icon { opacity: 0.8; }
/* the coin, now on the arc line where the age already was */
.stage-coin { margin-left: 14px; color: var(--gold); font-variant-numeric: tabular-nums; }

.fore-ob { position: absolute; width: auto; aspect-ratio: 1 / 1; transform: translateX(-50%);
  filter: drop-shadow(0 2px 6px rgba(0,0,0,.45)); will-change: left, bottom, height; }

/* Prop name tags sit on one of three lines, set per prop in paintProps. Two tags now collide only if
   they share a row AND overlap in x, which two things standing side by side no longer can. */
.place-props .npc-name { transform: translateX(-50%) translateY(calc(3px + var(--lbl-dy, 0px))); }
.place-props .nook-name { margin-top: var(--lbl-dy, 0px); }

/* A DEN off the side of a road: a mouth in the ground, not a glint you pick up. Sized a touch larger
   than a nook mark because what is in it is larger than what is in a nook. */
.den-mark { background: none; border: 0; padding: 0; cursor: pointer; }
.den-mark svg { width: 46px; height: 34px; display: block; filter: drop-shadow(0 2px 3px rgba(0,0,0,.55)); }
.den-mouth { fill: #241d15; stroke: #6b5a3e; stroke-width: 1.1; }
.den-dark { fill: #0b0907; }
.den-mark:hover .den-mouth, .den-mark:focus-visible .den-mouth { stroke: var(--gold); }
.den-mark .nook-name { display: block; }

.pl-sky { z-index: 0; }
.pl-far { z-index: 1; }
.pl-mid { z-index: 2; pointer-events: auto; }
.pl-near { z-index: 40; }
/* The near band is already drawn almost black in the scene's own light, and it now carries a lit rim
   on the side the light is on -- which is the whole thing that makes it read as an object standing in
   front of you rather than a hole. Multiplying all of it by 0.72 was throwing that rim away. */
.place-plane.pl-near .plane-art { filter: brightness(0.88); }
/* the air of the place: it closes in at the edges, so the middle distance reads as distance */
/* the lane: the strip of ground you can actually stand on, so W and S have somewhere visible to go */
.place-lane {
  position: absolute; left: 0; right: 0; bottom: 0; height: 30%; z-index: 1; pointer-events: none;
  background:
    linear-gradient(to top, rgba(232,180,92,0.07), rgba(232,180,92,0.018) 46%, rgba(232,180,92,0) 82%),
    linear-gradient(to top, rgba(4,8,11,0.5), rgba(4,8,11,0) 60%);
  -webkit-mask-image: radial-gradient(130% 100% at 50% 100%, #000 42%, transparent 100%);
  mask-image: radial-gradient(130% 100% at 50% 100%, #000 42%, transparent 100%);
}
.place-stage { cursor: grab; touch-action: pan-y; }
.place-stage.looking { cursor: grabbing; }
.place-stage.looking .place-prompt, .place-stage.looking .place-keys { opacity: 0.25; }
.place-prop, .place-stage button, .place-stage a { cursor: pointer; }
/* the halt (E, or an encounter on the plate) dims the whole world, not just the ground */
.place-stage.halted .place-plane { filter: saturate(0.55) brightness(0.5) blur(1.4px); }
.place-stage.plated .place-plane { filter: saturate(0.72) brightness(0.66); }
.place-plane { transition: filter 0.28s ease; }
@media (prefers-reduced-motion: reduce) { .place-plane { transition: none; } }

/* =========================================================================================
   THE WALK. Two legs under a shortened hem, an arm that swings against them, a skirt that lags
   the stride, a body that rises on each step and a shadow that tightens under it. The rotations
   are about real joints (the hip, the shoulder, the waist), which is why it reads as walking
   rather than as a shape being rocked.
   ========================================================================================= */
.wf-leg { transform-box: fill-box; transform-origin: 50% 0%; }   /* the hip */
.wf-leg path { fill: url(#wfg-robe); stroke: #5d4529; stroke-width: 0.07; stroke-opacity: 0.5; stroke-linejoin: round; }
.wf-leg-b path { fill: #191309; stroke-opacity: 0.28; }
.wf-foot { stroke-width: 0.08 !important; }
.wf-arm-g { transform-box: fill-box; transform-origin: 50% 0%; } /* the shoulder */
.wf-arm { fill: url(#wfg-cloth); stroke: #c8a468; stroke-width: 0.07; stroke-opacity: 0.5; stroke-linejoin: round; }
/* the FAR arm: the same sleeve, turned away from the light. Drawn identically it reads as a second
   near arm stuck on the wrong side, which is worse than the one arm it replaced. But it hangs
   against the CLOAK, which is #2c2218 -- taken all the way down to shadow it disappeared into the
   cloak entirely and the figure was one-armed again. It has to be darker than the near arm and
   lighter than the mantle behind it, which is a narrow band and the whole reason this rule exists. */
.wf-arm-b .wf-arm { fill: #574328; stroke: #9a7f4e; stroke-opacity: 0.45; }
.wf-arm-b .wf-cuff { fill: #3a2c1f; stroke-opacity: 0.4; }
.wf-arm-b .wf-underlayer { fill: #5e4a2f; }
.wf-arm-b .wf-sleeve-l { stroke-opacity: 0.2; }
.wf-skirt { fill: url(#wfg-robe); stroke: #5d4529; stroke-width: 0.08; stroke-opacity: 0.55; stroke-linejoin: round;
  transform-box: fill-box; transform-origin: 50% 0%; }           /* the waist */
.wf-lamp-hook { stroke: #e7c583; stroke-width: 0.14; stroke-linecap: round; opacity: 0.8; fill: none; }
.wf-staff-g { transform-box: fill-box; transform-origin: 50% 38%; } /* the hand on the staff */
/* the outline is a hairline at this size: any heavier and the figure reads as a cut-out sticker */
.stage-wayfarer .wf-body, .stage-wayfarer .wf-skirt { stroke-width: 0.13; }
.stage-wayfarer .wf-leg path { stroke-width: 0.09; }
.wf-shadow { transform-box: fill-box; transform-origin: 50% 50%; }

/* THE CADENCE IS ONE NUMBER, so a run can change it in one place. Every duration here used to be
   written out as 0.54s or 0.27s in seven rules, which is fine until the figure has a second gait. */
.stage-wayfarer.striding { --stride: 0.54s; --bob: 0.27s; }
/* HOLD SHIFT AND HE RUNS. Faster cadence, a longer lean into it, and the skirt lagging further behind
   -- and the feet come down oftener (placeStep shortens STEP_MS to match), because a run whose ground
   marks arrive at walking pace is a glide with a fast bob on top of it.
   SLOWED 2026-07-30 with the speed it belongs to: 0.33s was six leg cycles a second, which is roughly
   twice a real runner's cadence and read as scurrying. The three numbers here are not free -- the legs
   swing through a FIXED arc, so what the eye judges as stride length is speed divided by cadence, and
   the walk's 340px per 0.54s is the ratio a step has to keep. 527px at 0.42s holds it (1.19x the walk's
   stride, which is what makes it a run and not a fast walk), and STEP_MS * 0.78 puts the ground marks
   under the feet that made them. Change one of the three and change all three. */
.stage-wayfarer.striding.sprinting { --stride: 0.42s; --bob: 0.21s; }
.stage-wayfarer.striding .wf-inner { animation: wf-bob var(--bob) ease-in-out infinite; rotate: 1.6deg; }
/* and the lean keeps its share of the same slowdown: it is how hard he is pushing, not a second dial */
.stage-wayfarer.striding.sprinting .wf-inner { rotate: 3.1deg; }
.stage-wayfarer.striding .wf-leg-f { animation: wf-step-f var(--stride) ease-in-out infinite; }
.stage-wayfarer.striding .wf-leg-b { animation: wf-step-b var(--stride) ease-in-out infinite; }
.stage-wayfarer.striding .wf-arm-g { animation: wf-swing var(--stride) ease-in-out infinite; }
/* opposite phase, the same way the two legs are: arms that swing together is a march, not a walk */
.stage-wayfarer.striding .wf-arm-b { animation-name: wf-swing-b; }
.stage-wayfarer.striding .wf-skirt { animation: wf-lag var(--stride) ease-in-out infinite; }
.stage-wayfarer.striding .wf-shadow { animation: wf-tread var(--bob) ease-in-out infinite; }
.stage-wayfarer.striding .wf-staff-g { animation: wf-plant var(--stride) ease-in-out infinite; }
/* standing still is not standing dead: the weight settles from one foot to the other */
.stage-wayfarer .wf-leg-f { animation: wf-rest-f 5.2s ease-in-out infinite; }
.stage-wayfarer .wf-leg-b { animation: wf-rest-b 5.2s ease-in-out infinite; }
/* the swing angles are smaller than they were because the LEG is longer: a leg is half the figure
   now instead of 0.38 of it, and the same 15deg carried the foot a third again as far, which reads
   as a march rather than a walk. Arc length is what the eye judges, not the angle. */
@keyframes wf-step-f { 0%, 100% { transform: rotate(11deg); } 50% { transform: rotate(-11deg); } }
@keyframes wf-step-b { 0%, 100% { transform: rotate(-11deg); } 50% { transform: rotate(11deg); } }
@keyframes wf-swing { 0%, 100% { transform: rotate(-13deg); } 50% { transform: rotate(13deg); } }
@keyframes wf-swing-b { 0%, 100% { transform: rotate(13deg); } 50% { transform: rotate(-13deg); } }
@keyframes wf-lag { 0%, 100% { transform: rotate(2.8deg); } 50% { transform: rotate(-2.8deg); } }
@keyframes wf-plant { 0%, 100% { transform: rotate(-3.5deg); } 50% { transform: rotate(3.5deg); } }
@keyframes wf-bob { 0%, 100% { translate: 0 0.01px; } 50% { translate: 0 -0.11px; } }
@keyframes wf-tread { 0%, 100% { transform: scaleX(1) scaleY(1); opacity: 0.4; } 50% { transform: scaleX(0.82) scaleY(0.9); opacity: 0.3; } }
@keyframes wf-rest-f { 0%, 100% { transform: rotate(2deg); } 50% { transform: rotate(0.6deg); } }
@keyframes wf-rest-b { 0%, 100% { transform: rotate(-2deg); } 50% { transform: rotate(-0.6deg); } }
@media (prefers-reduced-motion: reduce) {
  .stage-wayfarer .wf-leg-f, .stage-wayfarer .wf-leg-b, .stage-wayfarer .wf-arm-g,
  .stage-wayfarer .wf-skirt, .stage-wayfarer .wf-shadow, .stage-wayfarer .wf-staff-g,
  .stage-wayfarer.striding .wf-inner { animation: none; }
}
/* weather is painted for a 400x96 banner; standing inside the place it is seven times the size,
   so a cloud that read as a cloud becomes a smear. On the far plane it is thinned and softened. */
.pl-far .place-weather { opacity: 0.5; filter: blur(2.5px); }
/* the bleed a sheared plane turns into: the ground is left alone, since the props are pinned to it */
.pl-sky, .pl-far, .pl-near { left: -76px; top: -26px; height: calc(100% + 52px); }
/* reduced motion drops every animation, which used to take the opening's narration with it: the
   lines are display:block but their whole appearance lived in the keyframes. Here they simply
   stack, reading upward from where the last one would have sat. */
@media (prefers-reduced-motion: reduce) {
  .tor.playing .tor-beat { position: relative; opacity: 1; margin-top: 7px; }
}

/* The narration sat straight on the map, and the map has its own names written across it at the
   same height -- so "The Compact" and "the Gilded Water" ran through the middle of the sentence
   you were trying to read. Each line brings its own lower third with it: a band that darkens
   what is under the words and lifts again as the line clears, so the map is never permanently
   dimmed and the two never collide. It fades with the line because it lives on the line. */
.tor-beat::before {
  content: ""; position: absolute; left: -7%; right: -7%; top: -34px; bottom: -90px; z-index: -1;
  pointer-events: none;
  background: linear-gradient(180deg,
    rgba(5,9,12,0) 0%, rgba(5,9,12,0.5) 13%, rgba(5,9,12,0.88) 22%,
    rgba(5,9,12,0.95) 42%, rgba(5,9,12,0.95) 76%, rgba(5,9,12,0.86) 100%);
}
/* and the map's own names step back for as long as there are words to read, so the sentence is
   the brightest thing in the frame rather than one of two things competing at the same height.
   They come back up the moment the narration is over, which is when the map is the point again. */
.tor.playing .tor-labels { animation: tor-labels-back 22.4s linear both; }
@keyframes tor-labels-back {
  0%, 4.5% { opacity: 1; }
  9%, 93.8% { opacity: 0.34; }
  100% { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) { .tor.playing .tor-labels { animation: none; } }

/* =========================================================================================
   THE WORLD ANSWERS YOU — the second half of the walkable overhaul.
   Planes gave the place depth; this gives it a response. Walking leaves marks on the ground it
   is walked on, the shadow knows where the location's light stands, and a thing worth reaching
   comes up as you come up on it rather than switching on at the edge of a radius.
   ========================================================================================= */
.place-fx { position: absolute; inset: 0; z-index: 3; pointer-events: none; }
.fx-step {
  position: absolute; transform: translateX(-50%) scale(var(--sc, 1)); transform-origin: 50% 100%;
  pointer-events: none; will-change: opacity, transform;
}
/* dry ground: a puff that lifts and thins */
.fx-dust {
  width: 22px; height: 9px; margin-bottom: -3px;
  background: radial-gradient(ellipse at 50% 100%, rgba(226,214,186,0.5), rgba(226,214,186,0) 72%);
  animation: fx-dust 1s cubic-bezier(0.2,0.7,0.4,1) forwards;
}
@keyframes fx-dust {
  0% { opacity: 0; transform: translateX(-50%) scale(calc(var(--sc,1) * 0.4)); }
  22% { opacity: 0.85; }
  100% { opacity: 0; transform: translateX(-50%) translateY(-9px) scale(calc(var(--sc,1) * 1.7)); }
}
/* stone and street: grit that scuffs and settles, no lift */
.fx-grit {
  width: 15px; height: 5px; margin-bottom: -1px;
  background: radial-gradient(ellipse at 50% 100%, rgba(212,200,176,0.4), rgba(212,200,176,0) 70%);
  animation: fx-grit 0.85s ease-out forwards;
}
@keyframes fx-grit {
  0% { opacity: 0; transform: translateX(-50%) scale(calc(var(--sc,1) * 0.5)); }
  18% { opacity: 0.7; }
  100% { opacity: 0; transform: translateX(-50%) scale(calc(var(--sc,1) * 1.25)); }
}
/* water: a ring that opens and dies, which is what a foot in the shallows actually does */
.fx-ring {
  width: 26px; height: 9px; margin-bottom: -4px;
  border: 1px solid rgba(190,224,232,0.65); border-radius: 50%;
  animation: fx-ring 1.5s cubic-bezier(0.15,0.75,0.35,1) forwards;
}
@keyframes fx-ring {
  0% { opacity: 0; transform: translateX(-50%) scale(calc(var(--sc,1) * 0.18)); }
  16% { opacity: 0.8; }
  100% { opacity: 0; transform: translateX(-50%) scale(calc(var(--sc,1) * 2.2)); }
}
/* the shadow leans away from the location's own light and lengthens as you walk out from under it */
/* the lean rides on `translate`/`scale`, not `transform`, so it COMPOSES with the tread animation
   rather than being overwritten by it every time a foot lands */
.place-you .wf-shadow {
  translate: calc(var(--shdx, 0) * 1px) 0;
  scale: var(--shsx, 1) 1;
  opacity: var(--shop, 0.4);
}
/* THE CADENCE, not 0.27s written out again. This rule exists to restate the tread on top of the lean
   above, and it has the same specificity as the one that carries `var(--bob)` while sitting later in
   the sheet -- so the hardcoded number silently won, and the player's own shadow went on beating at a
   walk's pace through every sprint while his body bobbed at the run's. It is the one figure in the
   game whose shadow you watch for a minute at a time. */
.place-you.striding .wf-shadow { animation: wf-tread var(--bob) ease-in-out infinite; }
/* coming up on a thing: it lifts and lights by degrees, and says its name before you are on it */
.place-prop { transition: transform 0.2s ease; }
/* --k is the play camera's scale for this prop: 1 standing beside you, smaller up the road, larger
   once you have come past it. A mark on the ground has no height to grow with, so it grows here.
   It defaults to 1, which is exactly the old sheet, so nothing changes without the depth camera. */
.place-props .nook-glint { transform: scale(calc((0.75 + var(--near, 0) * 0.5) * var(--k, 1))); opacity: calc(0.35 + var(--near, 0) * 0.65); }
/* A NAME IS FOR WHAT YOU ARE COMING UP ON. The ramp used to start the moment a prop registered at all
   (opacity = near * 1.15), so half a dozen labels sat part-visible across the scene at once and it was
   that crowd, not the geometry, that made them collide. It now stays dark until you are most of the
   way to a thing and then comes up quickly: measured, that is the difference between up to 4 readable
   names at once and one. The glint still marks every one of them from across the place. */
.place-props .nook-name, .place-props .npc-name { opacity: calc(var(--near, 0) * 2.4 - 1.1); }
/* ...and whatever the ramp says, the thing you are actually AT names itself. The ramp is for reading
   ahead; this is the affordance, and it must never be dimmer than legible. */
.place-props .place-prop.in-reach .nook-name,
.place-props .place-prop.in-reach .npc-name,
.place-props .npc-fig.npc-mentor.in-reach .npc-name,
.place-props .npc-fig.npc-guide.in-reach .npc-name { opacity: 1; }
.place-props .npc-fig { transform: translateX(-50%) translateY(calc(var(--near, 0) * -2px)); }
.place-prop.in-reach { transform: translateX(-50%) translateY(-3px); }
.place-props .npc-fig.in-reach { transform: translateX(-50%) translateY(-3px); }
@media (prefers-reduced-motion: reduce) {
  .fx-step { display: none; }
  .place-prop, .place-props .npc-fig { transition: none; transform: translateX(-50%); }
}
/* the mid-ground occluders: on the ground plane, sorted by their depth like everything else on it */

/* =========================================================================================
   GRAIN. The materials carry their own texture in the art; this is the layer over all of it --
   a fine, fixed-size tooth that does NOT scale with the world, so a place read close and a place
   read far share the same surface. It is one rasterised noise tile, not a live filter, so it
   costs a paint and nothing per frame.
   ========================================================================================= */
.place-stage::after, .cine-stage::after, .deck-stage::after {
  content: ""; position: absolute; inset: 0; z-index: 45; pointer-events: none;
  opacity: 0.07; mix-blend-mode: overlay;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='150' height='150'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='150' height='150' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 150px 150px;
}
.deck-stage::after { z-index: 6; }
@media (prefers-reduced-motion: reduce) { .place-stage::after, .cine-stage::after, .deck-stage::after { opacity: 0.05; } }

/* the armoury pieces carry their own art, so where a stroked glyph stood in for a sword, the sword
   stands there instead. Sized to the line it sits on; the inventory card gets the larger read. */
.item-art { height: calc(2.1em * var(--isc, 1)); width: auto; max-width: calc(2.6em * var(--isc, 1)); object-fit: contain; vertical-align: -0.78em;
  margin-right: 8px; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.7)); }
/* max-width has to clear the widest piece or that piece silently renders short: every sprite is
   340px tall, so the clamp divided by 88 is the widest aspect that still fills the height. At 74px
   that ceiling was 0.84 and the shield (separ, 339x340) came out a sixth smaller than its
   neighbours. 92px clears a square piece with room to spare. */
.item-art.big { height: calc(88px * var(--isc, 1)); max-width: calc(92px * var(--isc, 1)); width: auto; margin: 0; vertical-align: middle;
  filter: drop-shadow(0 2px 7px rgba(0,0,0,0.75)); }
.inv-glyph:has(.item-art.big) { display: flex; align-items: center; justify-content: center; min-height: 92px; }
/* the armoury, worn: each piece read by silhouette, because at this size that is all there is */
.wf-aventail{fill:#5a6068;stroke:#8f98a4;stroke-width:.06;opacity:.9}
.wf-plume{stroke:#c9b27a;stroke-width:.1;fill:none;stroke-linecap:round;opacity:.85}
.wf-vambrace{fill:#6a6f78;stroke:#c9a961;stroke-width:.06}
.wf-mirror rect{fill:#8d949e;stroke:#d8dde4;stroke-width:.05;opacity:.95}

/* origin banners animate through their own light and reeds, reusing the scene/affliction motion */
.scene-art .oa-flicker, .deck-scene .oa-flicker { animation: scene-flicker 2.8s ease-in-out infinite; }
.scene-art .oa-sway, .deck-scene .oa-sway { transform-box: fill-box; transform-origin: 50% 100%; animation: scene-sway 7.2s ease-in-out infinite alternate; }
.scene-art .oa-ripple, .deck-scene .oa-ripple { animation: scene-ripple 5s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .oa-flicker, .oa-sway, .oa-ripple { animation: none !important; } }

/* =========================================================================================
   THE LIVING BANNER — the crowd baked into the flat scene art (cinema, encounters, map).
   The walkable place has its own interactive crowd; this is the same life for every screen that
   shows a place as a picture, so a location is never an empty stage.
   ========================================================================================= */
/* NB: these figures/heads carry their own transform-origin IN SCENE COORDINATES (a figure's feet,
   an animal's neck) from the build. Do NOT set transform-box:fill-box here -- that reinterprets those
   coordinates against each tiny element's own box, flinging the pivot off-element so the head/figure
   swings hundreds of px across the frame. Scene-space (the SVG default) is what these origins want. */
.scene-art .sc-idle { animation: sc-idle 5.4s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes sc-idle { 0%, 100% { transform: rotate(-1.4deg); } 50% { transform: rotate(1.4deg); } }
.scene-art .sc-work { animation: sc-work 1.9s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes sc-work { 0%, 100% { transform: rotate(0) translateY(0); } 45% { transform: rotate(5deg) translateY(0.4px); } 70% { transform: rotate(-2deg); } }
.scene-art .sc-graze .sc-head { animation: sc-graze 3.6s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes sc-graze { 0%, 100% { transform: rotate(0); } 42% { transform: rotate(32deg) translateY(1px); } 70% { transform: rotate(28deg); } }
.scene-art .sc-peck .sc-head { animation: sc-peck 2.3s ease-in-out infinite; animation-delay: var(--wo, 0s); }
@keyframes sc-peck { 0%, 55%, 100% { transform: rotate(0); } 62% { transform: rotate(44deg) translateY(1.2px); } 72% { transform: rotate(0); } 80% { transform: rotate(40deg) translateY(1px); } 88% { transform: rotate(0); } }
@media (prefers-reduced-motion: reduce) {
  .scene-art .sc-idle, .scene-art .sc-work, .scene-art .sc-walk,
  .scene-art .sc-graze .sc-head, .scene-art .sc-peck .sc-head { animation: none; }
}

/* ---- what turns shapes into cloth: where layers meet, and where the light does not reach -------
   Every mass now has a lit side and a shadow side (the gradients), and these are the contact
   shadows between them: under the shoulder cape, under the sash, and a lip inside the hood so the
   opening has a depth rather than being a dark decal pasted on the front. */
.wf-occl{fill:#0b0805;opacity:.32}
.wf-void-lip{stroke:#6a563a;stroke-width:.055;stroke-opacity:.5;fill:none;stroke-linecap:round}
.wf-hemline{stroke:#d9b478;stroke-width:.05;stroke-opacity:.3;fill:none;stroke-linecap:round}
.wf-lamp-glow{fill:url(#wfg-lamp);pointer-events:none}
.wf-lamp-spec{stroke:#fff6dd;stroke-width:.045;stroke-opacity:.55;fill:none;stroke-linecap:round}

/* ---- DRAPE: the cloth stops being a filled shape ---------------------------------------------
   The masses were still large empty fills. A woven ground over each, then folds drawn as PAIRS:
   a valley that goes dark and a ridge just off it that catches the light, so the eye reads a
   rounded surface rather than a line scratched on a flat one. The folds gather at the sash and
   spread as they fall, which is what makes cloth look hung rather than painted on. */
.wf-weave { fill: url(#wfg-weave); opacity: .5; pointer-events: none; }
.wf-valley { stroke: #0c0805; stroke-width: .055; stroke-opacity: .5; fill: none; stroke-linecap: round; }
.wf-ridge  { stroke: #e8c894; stroke-width: .045; stroke-opacity: .22; fill: none; stroke-linecap: round; }
.wf-gather { stroke: #0c0805; stroke-width: .045; stroke-opacity: .38; fill: none; stroke-linecap: round; }
.wf-wrap-edge { stroke: #d9b478; stroke-width: .05; stroke-opacity: .4; fill: none; stroke-linecap: round; }
/* at the map-token size the fine work is sub-pixel and only muddies the silhouette, so it goes */
.map .wf-weave, .map .wf-ridge, .map .wf-gather { display: none; }

/* THE GROUND IN PERSPECTIVE — sized to the VIEW, not to the world.
   A vanishing point belongs to the camera. This used to be laid out across the whole world, which
   stretched its 400-unit viewBox over 3644px inside a 776px view: every converging line flattened
   4.7x and the surface read as a brown wash. `left` and `width` are now set per frame by
   paintGround (left = P.cam) so it parks exactly over the view while still sitting in the pannning
   interaction layer, which is the only place with the right stacking — above the painted bands,
   below the crowd and the props and you, so people stand ON the ground rather than behind it.
   top:42% is VP_Y, the same vanishing height the planes dolly about, so the drawn perspective and
   the moving one agree; if they disagreed, stepping forward would slide the world against its own lines. */
/* The walkable ground begins at the PAINTING'S horizon (VP_Y), not above it, and it fades in over its
   first sixteenth rather than butting the art with a hard horizontal seam. Before this the scene and
   the ground were two stacked images with a visible join; now they are one place, and the quays, the
   domes and the reeds are no longer cut off at the line. Kept slightly translucent so the art reads
   THROUGH the near ground instead of being covered by it. */
/* THERE ARE TWO GROUNDS AND ONLY ONE OF THEM IS THE SUBJECT. The motif paints its own ground (or its
   water, or its quay) across the bottom third of the art; this walkable surface is laid over the
   bottom 42% of the stage on top of it. At opacity .78 behind a 15% fade the second one WON, and what
   it won with was a wash: everything the scenes had in their lower half -- the water, the reeds, the
   moored boats, every piece of near dressing -- came through a film. Measured on the reed house, the
   art from art-y 54 down (42% of the frame) was under it.
   So the ramp is long now instead of short. The walkable surface is barely there at the horizon, where
   the PAINTING is the authority on what the distance looks like, and full at your feet, where it is the
   authority on what you are standing on. The material, the lane and the roads all live in the half
   that stayed strong; only the film over the painting is gone. */
/* A LANDING IS NOT A GLAZE, and it is not atmosphere either. At .8 the painted water went on rippling
   through the boards you are standing on, which is the thing the deck exists to stop; under the mask
   ramp below, its far half rendered at about half opacity as well, so the timber read as a stain on
   the delta rather than as something built. Raising the WHOLE layer to .96 fixed the boards and broke
   the water either side of them -- it hid the painting's own reeds, lilies and moored boats under the
   ground's tiles, in exactly the strip the landing exists to be seen against.
   So the landing is its own layer: no ramp, no fade, opaque, sitting above the ground and below
   everything that stands on it. The earth keeps .8 everywhere, including at the water places. */
/* Written `.place-deck` alone this LOST to `.place-ground` below it -- same specificity, later rule
   wins -- so the landing quietly kept the .8 and the ramp it is here to escape, and the layer split
   did nothing at all. Caught by reading the computed style off the live element rather than trusting
   that a rule I had written was a rule that applied. Doubled up to outrank it wherever it sits. */
.place-ground.place-deck { opacity: 1; -webkit-mask-image: none; mask-image: none; z-index: 0; }
/* THE GROUND'S y=130 IS YOUR FEET, AND IT HAS TO LAND ON THE FRAME BOTTOM. The play camera puts a
   thing of ground weight s at stage fraction VP_Y + s(1-VP_Y), and the ground plane's own law is
   s = y/130 -- so ground y 130 IS the bottom of the frame, by the projection both of them share.
   `bottom: 0` did not achieve that: an <svg> is a REPLACED element with an intrinsic ratio (400:130
   here), so with height:auto the used height comes from the WIDTH it is given and `bottom` is simply
   dropped as over-constrained. The ground therefore ran past the frame bottom by 90px at 1440x900 and
   170px at 1920x1080, which put every ground feature -- the lane, the depth ways, the landings -- a
   quarter of the way further down its own depth axis than the people standing on them. Measured: your
   feet sat at ground y 63.0 / 56.7 / 75.6 at three window sizes instead of the 78.0 the camera says
   you have. It fits only near 4:3.1; it was correct when VP_Y was 0.42 and broke when VP_Y became
   0.58 on 2026-07-29, silently, because nothing compares the two projections.
   An explicit height is what makes the two agree, and it must stay 100% - VP_Y. */
.place-ground { position: absolute; top: 58%; height: 42%; left: 0; width: 100%; z-index: 0; pointer-events: none; opacity: .8;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, rgba(0,0,0,.18) 16%, rgba(0,0,0,.55) 38%, #000 66%, #000 100%);
  mask-image: linear-gradient(to bottom, transparent 0, rgba(0,0,0,.18) 16%, rgba(0,0,0,.55) 38%, #000 66%, #000 100%); }
/* the ambient crowd is ATMOSPHERE, not cast: it sits back into the scene rather than over it. At .58
   it was a ghost -- and once the figures gained garments, texture and a contact shadow, 42% of all of
   that was being thrown away. The play camera and depthHaze already put air between you and them by
   DEPTH, which is the honest way to do it; a flat opacity did it to the near ones as well. */
.crowd-fig { opacity: .88; }
/* the tiles are shared defs living in a zero-size host; it must never take part in layout */
#groundDefs { position: absolute; width: 0; height: 0; overflow: hidden; pointer-events: none; }

/* =========================================================================================
   THE FULL SCREEN — the landing page is a page, everything after it is the game
   Two independent halves, and keeping them independent is the whole design:
     * body[data-view="play"]  the CHROME. The topbar stops being a bar; it becomes a dim
                               cluster in the top-right corner that lights on approach. It is a
                               VIEW flag because it is true of every screen after the title.
     * body:has(.hub.place)    the SURFACE. The walkable place drops its column, its frame and its
                               rounded corners and takes the window edge to edge; everything that
                               used to sit UNDER the picture is laid OVER the foot of it.
   The surface half asks the DOM instead of carrying a flag, and that is deliberate. A flag set in
   renderPlace and cleared in resetChrome LOOKED right and was wrong: renderMap and renderCombat
   repaint the mount without going through resetChrome, so the map kept the flag, kept `overflow:
   hidden` with it, and put 118px of its own content somewhere the player could not scroll to.
   `.hub.place` is painted by exactly one function and vanishes the moment anything repaints over
   it, so the question "is the walkable place the surface right now" can only ever be answered
   correctly. Rules that live INSIDE the place are scoped on `.hub.place` directly; only the two
   that must reach its ancestors need :has().
   The browser Fullscreen API is a third, separate thing (app.js). It can be refused, and Esc drops
   it without asking, so nothing below is conditioned on it: the game is fullscreen inside whatever
   window it has been given. The landing page matches no selector here and is untouched.
   ========================================================================================= */

/* ---- the chrome stands down ---- */
body[data-view="play"] .topbar {
  position: fixed; top: 0; left: 0; right: 0; z-index: 24;
  justify-content: flex-end; gap: 8px;
  padding: 10px clamp(12px, 3vw, 26px);
  background: none; border-bottom: 0; backdrop-filter: none;
  pointer-events: none; /* the bar is no longer a surface; only the controls on it are */
}
body[data-view="play"] .brand { display: none; } /* the title belongs to the landing page */
/* the sign-in chip held the left of the bar open with margin-right:auto; in the corner it joins
   the others, or it lands on top of the place's own name */
body[data-view="play"] .brand-auth { margin: 0; }
/* dim until reached for, so the corner is not competing with the place's own name */
body[data-view="play"] .topbar > * { pointer-events: auto; opacity: 0.3; transition: opacity 0.22s ease; }
body[data-view="play"] .topbar > *:hover, body[data-view="play"] .topbar > *:focus-within { opacity: 1; }
body[data-view="play"] .linkbtn { background: rgba(9,14,17,0.55); backdrop-filter: blur(6px); }
/* the reflection hung off the topbar's height; with the bar gone it rises with it */
body[data-view="play"] .reflection { top: 60px; }
/* the bar left the flow, so the screens that are still PAGES (creation, the map, the market, an
   ending) get back the room it used to take, and keep clear of the controls now floating over it */
body[data-view="play"] .game { padding-top: clamp(44px, 4.5vw, 66px); }
body[data-view="play"] .points { top: 14px; }

/* ---- the place takes the window ---- */
/* the only two rules that must reach outside the place, so the only two that need :has() */
body:has(.hub.place) { overflow: hidden; padding-bottom: 0; } /* no page under the game to scroll */
body:has(.hub.place) .game { max-width: none; padding: 0; }

.hub.place { max-width: none; margin: 0; padding: 0; height: 100vh; height: 100dvh; }
.hub.place .hub-stage.place-stage {
  position: absolute; inset: 0; width: auto; height: auto;
  margin: 0; border: 0; border-radius: 0; box-shadow: none;
}
/* the rail is fixed to the foot of the window, so the walking HUD is lifted clear of it rather
   than being drawn underneath it. --rail-h is measured by renderRail, the same number the page
   used to reserve as a gap. */
.hub.place .place-keys { bottom: calc(var(--rail-h, 0px) + 12px); left: clamp(14px, 2vw, 26px); }
.hub.place .place-e { bottom: calc(var(--rail-h, 0px) + 12px); right: clamp(14px, 2vw, 26px); }
.hub.place .place-prompt { bottom: calc(var(--rail-h, 0px) + 56px); }
/* the title stops hugging the top-left corner of a small frame and takes the room a window has */
.hub.place .stage-top { padding: 14px clamp(18px, 3vw, 30px) 44px; padding-right: 240px; }

/* what the place SAYS is no longer under the picture; it is laid over the foot of it, left of the
   prompt, in a column narrow enough to stay out of the way of what is happening in the middle. */
.hub.place .place-say {
  position: absolute; left: 0; right: auto; z-index: 8;
  bottom: calc(var(--rail-h, 0px) + 34px);
  width: min(58ch, 41vw);
  padding: 58px clamp(18px, 3vw, 30px) 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  /* ONE radial pooled in the corner the words sit in, because it fades in every direction at once.
     A pair of linear gradients cannot: whichever one runs vertically still has full strength at the
     element's side, and that edge shows as a rectangle drawn on the picture. */
  background: radial-gradient(155% 132% at 0% 100%,
    rgba(6,10,13,0.94) 0%, rgba(6,10,13,0.78) 30%, rgba(6,10,13,0.34) 58%, rgba(6,10,13,0) 80%);
}
.hub.place .place-say > * { max-width: 44ch; margin-bottom: 0; }
.hub.place .place-say .hub-here { margin: 0; gap: 3px; }
/* the words are on the picture now, not under it: they carry their own darkness with them */
.hub.place .place-say .loc-state,
.hub.place .place-say .loc-detail,
.hub.place .place-say .town-view { text-shadow: 0 1px 3px #000, 0 0 14px rgba(0,0,0,0.85); }
.hub.place .place-say .town-view { color: #c9bda6; }
.hub.place .place-say .arrival-note,
.hub.place .place-say .return-note,
.hub.place .place-say .levelup-note,
.hub.place .place-say .thread-nudge,
.hub.place .place-say .thread-wayfind { margin: 6px 0 0; }
.hub.place .place-say .debuff-row { margin-top: 6px; }
/* the world has halted for a menu or an exchange: its own commentary gets out of the way */
.hub.place .place-stage.halted ~ .place-say { opacity: 0; }

/* the plate rises off the rail instead of running down a page that no longer scrolls; if an
   exchange is longer than the room above it, the plate scrolls rather than the window. */
.hub.place .place-plate {
  top: auto; bottom: calc(var(--rail-h, 0px));
  max-height: calc(100vh - var(--rail-h, 0px) - 96px);
  overflow-y: auto; overscroll-behavior: contain;
}

/* ── RANGE: what the distance looks like ──────────────────────────────────────────────────────────
   The move row sits above the utility row and reads as one line: step in, where you are, back off.
   It is a ROW of its own rather than two more buttons in the pile, because the distance is a
   different kind of decision from what to hit him with and should not be hunted for among them. */
.range-row { align-items: center; justify-content: center; gap: 12px; }
.range-row .crange { flex: 0 0 auto; min-width: 132px; }
.range-say { font-family: var(--serif); font-size: 13.5px; letter-spacing: 0.04em; color: var(--ink-dim);
  min-width: 11ch; text-align: center; }
/* out of reach there is nothing to aim at, so the boxes are gone and the stage says why */
.range-gap { position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%); z-index: 4;
  padding: 7px 16px; border-radius: 999px; background: rgba(20,15,11,0.72);
  border: 1px solid rgba(217,184,102,0.22); backdrop-filter: blur(4px);
  font-family: var(--serif); font-size: 14px; color: #e7d8bd; letter-spacing: 0.02em;
  text-shadow: 0 2px 8px rgba(0,0,0,0.8); pointer-events: none; white-space: nowrap; }
/* against a shooter the gap is HIS ground, not yours, and the plate says so in his colour */
.range-gap.shot { border-color: rgba(217,140,115,0.45); color: #f0c6b4;
  box-shadow: 0 0 0 3px rgba(217,140,115,0.1); }
/* WHICH SIDE OF HIM YOU ARE ON, said on the same line that says how far away he is, because it is
   the same sentence: where you are standing. */
.arc-say { display: block; font-size: 12.5px; opacity: 0.8; }
/* AND THE CONTROL FOR IT IS ON THE STAGE, IN THE ROOM THE PAIR LEAVES AT THE SIDES. As a deck row it
   was measured taking 116px out of a 547px picture -- the stage is `flex: 1 1 auto`, so every row
   added below it comes straight off the painting, and that is a fifth of what going full-bleed was
   for. Here it costs the picture nothing and reads as what it is: press the side you want to go
   round to. Held clear of the pair (they stand 31% in from each edge) and clear of the charge meter
   and the log, so no press can be ambiguous between circling and winding up.
   A CONTROL THE PLAYER CANNOT SEE IS A MECHANIC THAT DOES NOT EXIST. The first cut sat at the very
   frame edge at 0.42 opacity over a dark scene and was, on looking at it, invisible -- so it is
   pulled inboard to just outside each fighter and carries its own weight at rest. */
.cb-circle { position: absolute; top: 50%; transform: translateY(-50%); z-index: 5;
  width: 58px; height: 148px; padding: 0; border-radius: 14px; cursor: pointer;
  background: rgba(18,13,9,0.58); border: 1px solid rgba(217,184,102,0.34);
  box-shadow: 0 4px 16px rgba(0,0,0,0.45); backdrop-filter: blur(3px);
  opacity: 0.78; transition: opacity 0.16s, background 0.16s, border-color 0.16s; }
.cb-circle.to-l { left: 6%; }
.cb-circle.to-r { right: 6%; }
.cb-circle:hover, .cb-circle:focus-visible { opacity: 1; background: rgba(30,22,14,0.78); border-color: rgba(217,184,102,0.62); }
.cb-circle[disabled] { opacity: 0.2; cursor: default; box-shadow: none; }
/* the mark is an arc with a head, not a straight arrow: you are going ROUND him, not away from him,
   and the shape has to state which of those it does. */
.cb-circle .cbc-mark { display: block; width: 30px; height: 30px; margin: 0 auto; position: relative;
  border: 2.5px solid #e8d5a8; border-radius: 50%;
  border-bottom-color: transparent; border-left-color: transparent; transform: rotate(-40deg); }
.cb-circle .cbc-mark::after { content: ""; position: absolute; top: -3px; right: 2px;
  border: 5px solid transparent; border-bottom-color: #e8d5a8; transform: rotate(105deg); }
.cb-circle.to-l .cbc-mark { transform: rotate(-40deg) scaleX(-1); }
@media (prefers-reduced-motion: reduce) { .cb-circle { transition: none; } }
