/* ─────────────────────────────────────────────────────────── SUPERSTES
   Dark theme. Light text on black. Gothic display face for the title,
   period serif for the prose. Reading-heavy: line length and rhythm win
   over decoration.
   ───────────────────────────────────────────────────────────────── */

:root {
  --ink:        #0a0908;   /* the page */
  --ink-deep:   #050504;
  --paper:      #e8e2d6;   /* body text */
  --paper-dim:  #a89f8d;
  --paper-faint:#6f6858;
  --candle:     #c9a55f;   /* candlelight — the only accent */
  --candle-dim: #8a713d;
  --candle-deep:#4e3f21;
  /* The accent again as bare channels, for the glows and washes that need it
     inside rgba(). Without this the 20 hardcoded gold shadows stay gold when a
     chapter overrides --candle, and the accent swap only half happens. Keep in
     step with --candle. */
  --accent-rgb: 201, 165, 95;
  --rule:       rgba(200, 186, 158, 0.2);

  --display: "Grenze Gotisch", "UnifrakturMaguntia", Georgia, serif;
  --head:    "Grenze Gotisch", Georgia, serif;
  --body:    "Grenze", "EB Garamond", Georgia, "Times New Roman", serif;
  --label:   "Grenze", Georgia, serif;   /* small uppercase labels */
  --cal-title: "Mrs Saint Delafield", cursive;   /* the calendar's own hand */
  --cal-mono:  "Special Elite", Georgia, serif;  /* typed weekday/day glyphs */

  --measure: 34rem;        /* ≈ 68–72 characters */
  --fade: 400ms;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--ink);
  color: var(--paper);
  font-family: var(--body);
  font-size: 20px;
  line-height: 1.75;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body { min-height: 100vh; overflow-x: hidden; }

em { font-style: italic; color: #f7f2e6; }

/* ───────────────────────────────────────────────── chapter II's own skin

   Chapter I is a chapel lit by a candle: warm gold accent, blackletter display.
   Chapter II has no fire in it anywhere — it has water, and it happens in cars
   and parking lots. Two tokens carry the whole difference, and `data-chapter`
   is stamped on <body> by activateChapter().

   The accent is taken from the art's own palette rather than invented: #6a80a1
   is the steel-blue anchor unify_colors.py maps every Chapter II image onto, so
   the UI and the pictures are finally the same temperature.

   The display face is deliberately *not* changed. A contemporary serif was
   tried here and read as a different product rather than a second chapter --
   the blackletter is the house face and it carries across. Only the accent
   moves.
   ────────────────────────────────────────────────────────────────────────── */

body[data-chapter="ii"] {
  --candle:      #8fa6c4;
  --candle-dim:  #6a80a1;   /* the art's own highlight anchor */
  --candle-deep: #3a4a61;
  --accent-rgb:  143, 166, 196;
}


/* ───────────────────────────────────────────────────── background stack */

#stage {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--ink-deep);
}

.bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  opacity: 0;
  /* The art is dark-native (cold linework on black). Lift it so the drawing
     actually reads; legibility is handled by the scrim in .vignette, not by
     crushing the image. */
  filter: brightness(2.0) contrast(0.98) saturate(1.05);
  transform: scale(1.03);
}

.bg.visible { opacity: 1; }

@media (prefers-reduced-motion: no-preference) {
  .bg { transition: opacity 900ms ease; }
}

/* ─────────────────────────────────────────────────────────── overlays */

.vignette {
  position: fixed;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background:
    /* A soft column of shade behind the measure, so prose always has ground
       while the art stays legible to either side of it. */
    linear-gradient(to right,
      rgba(5,5,4,0) 0%,
      rgba(5,5,4,0.72) 22%,
      rgba(5,5,4,0.82) 50%,
      rgba(5,5,4,0.72) 78%,
      rgba(5,5,4,0) 100%),
    radial-gradient(ellipse at center, transparent 42%, rgba(0,0,0,0.5) 100%),
    linear-gradient(to bottom, rgba(0,0,0,0.45), rgba(0,0,0,0.1) 30%, rgba(0,0,0,0.6));
}

@media (max-width: 760px) {
  .vignette {
    background:
      linear-gradient(to right, rgba(5,5,4,0.74), rgba(5,5,4,0.8) 50%, rgba(5,5,4,0.74)),
      linear-gradient(to bottom, rgba(0,0,0,0.5), rgba(0,0,0,0.25) 30%, rgba(0,0,0,0.75));
  }
}

.grain {
  position: fixed;
  inset: -50%;
  z-index: 4;
  pointer-events: none;
  opacity: 0.05;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: no-preference) {
  .grain { animation: grain-shift 900ms steps(3) infinite; }
}

@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  33%  { transform: translate(-3%, 2%); }
  66%  { transform: translate(2%, -2%); }
  100% { transform: translate(0, 0); }
}

/* The drip. Deliberately irregular — long, short, short, long.
   CSS only, no JS. */
.drip {
  position: fixed;
  top: 0;
  left: 6vw;
  width: 3px;
  height: 100vh;
  z-index: 2;
  pointer-events: none;
  opacity: 0.5;
}

.drip span,
.drip .drop {
  position: absolute;
  top: -14px;
  left: 0;
  width: 3px;
  height: 14px;
  border-radius: 0 0 50% 50%;
  background: linear-gradient(to bottom, transparent, rgba(var(--accent-rgb), 0.6));
}

@media (prefers-reduced-motion: no-preference) {
  .drip span.ambient { animation: fall 7.3s cubic-bezier(0.45, 0, 0.9, 1) infinite; }
}

/* While a scene is beating out its phrase, the ambient drop stands down so the
   rhythm stays countable. */
.drip.coded span.ambient { display: none; }

.drip .drop {
  animation-name: drop-fall;
  animation-timing-function: cubic-bezier(0.45, 0, 0.9, 1);
  animation-fill-mode: forwards;
}

/* A dash is a longer drop as well as a slower one — length and duration both
   carry the distinction, so it reads even at a glance. */
.drip .drop.long {
  height: 30px;
  background: linear-gradient(to bottom, transparent, rgba(var(--accent-rgb), 0.72));
}

@keyframes drop-fall {
  0%   { transform: translateY(0);     opacity: 0; }
  5%   { opacity: 0.9; }
  92%  { opacity: 0.85; }
  100% { transform: translateY(100vh); opacity: 0; }
}

@keyframes fall {
  0%    { transform: translateY(0);      opacity: 0; }
  4%    { opacity: 0.9; }
  26%   { transform: translateY(100vh);  opacity: 0; }
  27%   { transform: translateY(0);      opacity: 0; }
  30%   { opacity: 0.8; }
  44%   { transform: translateY(100vh);  opacity: 0; }
  45%   { transform: translateY(0);      opacity: 0; }
  48%   { opacity: 0.8; }
  62%   { transform: translateY(100vh);  opacity: 0; }
  63%   { transform: translateY(0);      opacity: 0; }
  67%   { opacity: 0.9; }
  100%  { transform: translateY(100vh);  opacity: 0; }
}

/* ───────────────────────────────────────────────────── title screen */

#title-screen {
  position: fixed;
  inset: 0;
  z-index: 20;
  display: grid;
  place-items: center;
  background:
    radial-gradient(ellipse at center, #16120c 0%, #05050400 70%), #050504;
  text-align: center;
  padding: 1.5rem;
  overflow-y: auto;
  transition: opacity 700ms ease;
}

#title-screen.gone { opacity: 0; pointer-events: none; }
#title-screen[hidden] { display: none; }

.title-inner { max-width: 30rem; }

#title-screen h1 {
  font-family: var(--display);
  font-weight: 400;
  /* Small enough on a short viewport that the whole card fits with no
     scroll; still scales up on a tall one via the vw term. */
  font-size: clamp(2.75rem, 10vw, 5rem);
  line-height: 1;
  margin: 0 0 0.6rem;
  color: var(--paper);
  text-shadow: 0 0 42px rgba(var(--accent-rgb), 0.28);
}

/* Centered, like everything else on the card — no flush-left column, no
   jogging left when the passage starts. */
.latin {
  max-width: 24rem;
  margin-left: auto;
  margin-right: auto;
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper-faint);
  margin: 0 0 0.4rem;
}

.latin.small {
  font-size: 0.64rem;
  letter-spacing: 0.12em;
  text-transform: none;
  margin: 0 0 1.4rem;
  color: #5c5548;
}

.latin em { color: var(--candle-dim); font-style: italic; }

/* Capped to roughly the gloss lines' own measure so the passage doesn't
   stretch to the full title card, and centered under it like everything
   else on the card. Each line is its own <p> at a clause break, so every
   line is meant to read as a single held beat rather than a wrapped
   paragraph — center alignment is what keeps that reading intact instead of
   producing a ragged block. */
.blurb {
  max-width: 24rem;
  margin: 0 auto 1.6rem;
  color: var(--paper-dim);
  font-style: italic;
  font-size: 0.92rem;
  text-align: center;
  line-height: 1.4;
}

.blurb-line { margin: 0 0 0.4rem; }
.blurb-line:last-child { margin-bottom: 0; }

#begin {
  font-family: var(--label);
  font-size: 0.76rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--paper);
  background: transparent;
  border: 1px solid var(--candle-dim);
  padding: 0.75rem 1.8rem;
  cursor: pointer;
  transition: background 300ms ease, color 300ms ease, box-shadow 300ms ease;
}

#begin:hover,
#begin:focus-visible {
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--candle);
  box-shadow: 0 0 30px rgba(var(--accent-rgb), 0.15);
  outline: none;
}

.foot {
  font-family: var(--label);
  margin: 1.4rem 0 0;
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: #4a453c;
}

/* The second door into Chapter II.
   Sits under the blurb, above "Enter the fog". Quiet but legible -- an earlier
   pass had it at #332f29 on near-black, which is not subtle, it is invisible.
   Subordinate to the main button by size and weight, not by being hidden.

   One line, in the label face, with the numeral inline. A stacked blackletter
   "II" was tried as a glyph above the line and read as a stray mark rather than
   a chapter number. */
.second-door {
  margin: 2rem 0 2.4rem;
  text-align: center;
}

#skip-to-ii {
  font-family: var(--label);
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  background: none;
  border: 0;
  padding: 0.5rem 1rem;
  cursor: pointer;
  color: var(--paper-dim);
  transition: color 420ms ease, text-shadow 420ms ease;
}

#skip-to-ii .door-numeral {
  color: var(--candle-dim);
  transition: color 420ms ease;
}

#skip-to-ii:hover,
#skip-to-ii:focus-visible {
  color: var(--paper);
  text-shadow: 0 0 24px rgba(var(--accent-rgb), 0.25);
  outline: none;
}

#skip-to-ii:hover .door-numeral,
#skip-to-ii:focus-visible .door-numeral { color: var(--candle); }

/* ────────────────────────────────────────────────────── scene content */

.scene-content {
  position: relative;
  z-index: 5;
  max-width: var(--measure);
  margin: 0 auto;
  padding: 15vh 1.5rem 30vh;
  opacity: 1;
}

@media (prefers-reduced-motion: no-preference) {
  .scene-content { transition: opacity var(--fade) ease; }
}

.scene-content.fade-out { opacity: 0; }

.scene-title {
  font-family: var(--display);
  font-weight: 500;
  font-size: clamp(2.1rem, 5.5vw, 3.1rem);
  line-height: 1.1;
  letter-spacing: 0.02em;
  color: var(--candle);
  margin: 0 0 2.75rem;
  text-align: center;
  text-shadow: 0 0 38px rgba(var(--accent-rgb), 0.3), 0 2px 18px rgba(0,0,0,0.9);
}

.scene-title::after {
  content: "";
  display: block;
  width: 5rem;
  height: 1px;
  margin: 1.4rem auto 0;
  background: linear-gradient(to right, transparent, var(--candle-dim), transparent);
}

/* A chapter subtitle, withheld until its reveal scene and then stated plainly.
   No flash, no glitch, no animation — the script doc is explicit that the words
   simply appear. It gets space around it and nothing else. */
.title-reveal {
  font-family: var(--label);
  font-size: 0.82rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  text-align: center;
  color: var(--candle);
  margin: 3.5rem 0 3rem;
}

.narration p,
.response p,
.tier2-panel p {
  margin: 0 0 1.6em;
  text-shadow: 0 1px 3px rgba(0,0,0,0.95), 0 2px 18px rgba(0,0,0,0.9);
}

.narration p:last-child { margin-bottom: 0; }

/* Blocks that arrive after a choice ease in rather than snapping. */
.enters { opacity: 0; transform: translateY(8px); }
.enters.entered { opacity: 1; transform: none; }

@media (prefers-reduced-motion: no-preference) {
  .enters { transition: opacity 600ms ease, transform 600ms ease; }
}
@media (prefers-reduced-motion: reduce) {
  .enters { opacity: 1; transform: none; }
}

/* An inscription, not a caption. Faint enough to be missed, sharp enough to
   be copied out by anyone who notices it. Never translated. */
.morse {
  /* Reserve the line's height up front so the typing never reflows the page. */
  min-height: 2.1em;
  margin: 2.75rem 0 0;
  text-align: center;
  font-family: var(--label);
  font-size: 1.65rem;
  line-height: 1.4;
  letter-spacing: 0.32em;
  color: var(--candle);
  opacity: 0.72;
  user-select: text;
  text-shadow: 0 0 28px rgba(var(--accent-rgb), 0.26), 0 2px 12px rgba(0,0,0,0.9);
  transition: opacity 600ms ease;
}

.morse:hover { opacity: 1; }

/* The same inscription, quoted inline within a tier2 hotspot's own prose
   (the narrator transcribing what they heard) rather than sitting ambiently
   below the scene. Sized down from the ambient version and re-specified
   against `.tier2-panel p` so it isn't quietly overridden by the panel's
   generic paragraph rule. */
.tier2-panel .morse-inline {
  min-height: 1.7em;
  margin: 1.4rem 0 !important;
  font-size: 1.3rem;
  letter-spacing: 0.26em;
  opacity: 0.8;
  text-shadow: 0 0 22px rgba(var(--accent-rgb), 0.22), 0 2px 10px rgba(0,0,0,0.9);
}

.morse-cursor {
  color: var(--candle);
  margin-left: 0.1em;
}

@media (prefers-reduced-motion: no-preference) {
  .morse-cursor { animation: morse-blink 900ms steps(2, end) infinite; }
  /* Lingers after the phrase lands, then bows out. */
  .morse-cursor.done { animation: morse-blink 900ms steps(2, end) 4, morse-retire 700ms ease 3.6s forwards; }
}

@media (prefers-reduced-motion: reduce) {
  .morse-cursor { display: none; }
}

@keyframes morse-blink {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0; }
}

@keyframes morse-retire {
  to { opacity: 0; }
}

@media (max-width: 640px) {
  .morse { font-size: 1.25rem; letter-spacing: 0.22em; }
  .tier2-panel .morse-inline { font-size: 1.05rem; letter-spacing: 0.18em; }
}

/* ───────────────────────────────────────────── tier 2 accordions */

.tier2 {
  margin: 3rem 0 0;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
}

.tier2-heading {
  font-family: var(--label);
  font-size: 0.64rem;
  letter-spacing: 0.32em;
  text-transform: uppercase;
  color: var(--paper-faint);
  margin: 0 0 0.75rem;
}

.tier2-item { margin: 0 0 0.15rem; }

.tier2-label {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  width: 100%;
  text-align: left;
  font-family: var(--body);
  font-size: 1rem;
  font-style: italic;
  color: var(--paper-dim);
  background: transparent;
  border: 0;
  border-left: 1px solid transparent;
  padding: 0.5rem 0 0.5rem 0.9rem;
  cursor: pointer;
  transition: color 250ms ease, border-color 250ms ease;
}

.tier2-label:hover,
.tier2-label:focus-visible {
  color: var(--candle);
  border-left-color: var(--candle-dim);
  outline: none;
}

.tier2-marker {
  font-size: 0.5rem;
  color: var(--candle-dim);
  transform: translateY(-0.2em);
  transition: transform 300ms ease, color 250ms ease;
}

.tier2-item.open .tier2-marker { transform: translateY(-0.2em) rotate(45deg); }
.tier2-label.seen .tier2-text { color: var(--paper-faint); }
.tier2-item.open .tier2-label .tier2-text { color: var(--candle); }

.tier2-panel {
  padding: 0.75rem 0 1.75rem 1.9rem;
  border-left: 2px solid var(--candle-deep);
  margin-left: 0.15rem;
  color: var(--paper-dim);
  font-size: 0.97rem;
}

.tier2-panel p:last-child { margin-bottom: 0; }

/* The examine-image frame: a gilt scrollwork border-image (decorative-frame-
   border.webp, a 1000×1000 gold-on-transparent plate) wraps the photo itself.
   Shipped at 1000px, not the 8000px master: the border draws at ~26px, so
   8000² was ~256MB of decoded RGBA the moment a hotspot opened — enough to
   kill the tab on mobile Safari and Chrome. 1000² decodes to ~4MB and is
   still ~8× the drawn size. It is also the one image that keeps its alpha
   channel; scripts/optimize_images.sh flattens every other asset.
   border-image-slice cuts a fixed 200px corner out of each corner so the
   scrollwork stays crisp at its drawn size; the plain double-rule edges
   between corners stretch to fit whatever size the panel renders at.
   The frame art lives on ::before (its own opacity, dimmed a touch so the
   gold doesn't outshine the photo), boxing the photo in with real padding
   rather than overlapping its edge. */
.tier2-frame {
  display: block;
  position: relative;
  width: min(100%, 20rem);
  margin: 0 0 1.5rem;
  padding: 1.6rem;
}

.tier2-frame::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  border-style: solid;
  border-width: 1.6rem;
  border-image-source: url("../assets/images/decorative-frame-border.webp");
  border-image-slice: 200;
  border-image-width: 1.6rem;
  border-image-repeat: stretch;
  opacity: 0.48;
  filter: saturate(0.6);
  pointer-events: none;
  transition: opacity 400ms ease;
}

.tier2-image {
  display: block;
  position: relative;
  z-index: 1;
  width: 100%;
  /* Heavy enough to frame the plate rather than merely edge it — these sit on
     a dark page against dark art and a hairline disappears. */
  box-shadow: 0 0 0 1px rgba(0,0,0,0.8), 0 8px 30px rgba(0,0,0,0.65);
  filter: brightness(1.35) contrast(1.0) sepia(0.08) saturate(0.95);
  transition: filter 400ms ease;
}

.tier2-frame:hover::before {
  opacity: 0.62;
}
.tier2-frame:hover .tier2-image {
  filter: brightness(1.5) contrast(1.0) sepia(0.05) saturate(1);
}
.tier2-image.is-placeholder { filter: none; opacity: 0.85; }

/* ────────────────────────────────────────────── the May 1543 calendar

   Static on purpose: no navigation, nothing clickable but the hotspot's own
   close. The fixed month is the point. Strikes and the circle are drawn as
   ink paths (see buildMayCalendar/handStrike/handCircle in main.js), not
   CSS line-through or border-radius, so they read as drawn rather than
   styled — the spec is explicit that this matters.
   ─────────────────────────────────────────────────────────────────────── */

.calendar {
  width: min(100%, 22rem);
  margin: 0 0 1.75rem;
  padding: 1.1rem 1rem 1.3rem;
  border: 1px solid rgba(200,186,158,0.28);
  background: rgba(255,255,255,0.02);
}

.calendar-title {
  margin: 0 0 0.9rem;
  text-align: center;
  font-family: var(--cal-title);
  font-size: 1.4rem;
  letter-spacing: 0.04em;
  color: var(--candle-dim);
}

.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}

.cal-head {
  padding: 0.2rem 0 0.4rem;
  text-align: center;
  font-family: var(--cal-mono);
  font-size: 0.68rem;
  letter-spacing: 0.05em;
  color: var(--paper-faint);
}

.cal-cell {
  position: relative;
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
}

.cal-blank { visibility: hidden; }

.cal-day {
  position: relative;
  z-index: 1;
  font-family: var(--cal-mono);
  font-size: 0.92rem;
  color: var(--paper-dim);
}

.cal-mark {
  position: absolute;
  inset: -8%;
  width: 116%;
  height: 116%;
  pointer-events: none;
  overflow: visible;
  opacity: 0;
  transform: scale(0.85);
  transition: opacity 260ms ease, transform 260ms ease;
}

.cal-mark.revealed {
  opacity: 1;
  transform: scale(1);
}

/* the circled day never quite settles — a slow, steady pulse, the animation's
   one point that doesn't resolve. Opacity-only: animating transform or the
   drop-shadow itself here is what caused the flicker, since a running
   animation and the .cal-mark transition above were fighting over the same
   properties. */
.cal-circle.revealed {
  animation: cal-circle-blink 2400ms ease-in-out infinite;
  animation-delay: 260ms;
}

@keyframes cal-circle-blink {
  0%, 100% { opacity: 0.5; }
  50%      { opacity: 1; }
}

.cal-strike path {
  fill: none;
  stroke: rgba(160,148,128,0.68);
  stroke-width: 2.4;
  stroke-linecap: round;
}

.cal-circle path {
  fill: none;
  stroke: var(--candle);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
  opacity: 0.85;
  filter: drop-shadow(0 0 6px rgba(var(--accent-rgb), 0.35));
}

/* mid-shuffle, the digits read as unsettled — dimmer, no drawn marks yet */
.cal-day.is-shuffling {
  color: var(--paper-faint);
}

@media (prefers-reduced-motion: reduce) {
  .cal-mark, .cal-circle.revealed { transition: none; animation: none; }
}

@media (max-width: 640px) {
  .cal-day { font-size: 0.8rem; }
  .calendar-title { font-size: 1.2rem; }
  .calendar { padding: 0.9rem 0.7rem 1.1rem; }
}

/* ─────────────────────────────────────────────────── the name interaction */

.interaction { margin-top: 1.5rem; }

.interaction-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}

#carve-input {
  flex: 1 1 12rem;
  font-family: var(--body);
  font-size: 1rem;
  color: var(--paper);
  background: rgba(255,255,255,0.03);
  border: 1px solid var(--rule);
  border-radius: 0;
  padding: 0.7rem 0.85rem;
  letter-spacing: 0.08em;
}

#carve-input::placeholder { color: #575044; font-style: italic; }
#carve-input:focus {
  outline: none;
  border-color: var(--candle-dim);
  background: rgba(var(--accent-rgb), 0.08);
}

.interaction-btn { margin: 0; flex: 0 0 auto; }

.carved-name {
  font-family: var(--display);
  font-size: 2rem;
  line-height: 1.2;
  color: var(--candle);
  margin: 0 0 1rem !important;
  word-break: break-word;
  text-shadow: 0 0 26px rgba(var(--accent-rgb), 0.25);
}

/* ───────────────────────────────────────────────────────── choices */

.choices {
  margin: 2.75rem 0 0;
  padding-top: 1.75rem;
  border-top: 1px solid var(--rule);
}

/* The question stays with the options. On a short viewport — and on the final
   branch, whose options are long — the prompt would otherwise scroll away and
   leave the player choosing between three sentences with nothing to answer.
   No ground behind it: a filled bar reads as a UI panel dropped onto the art,
   which is not what this page is. It carries its own weight in the shadow. */
.prompt {
  position: sticky;
  top: 0;
  z-index: 1;
  font-family: var(--head);
  font-weight: 500;
  font-size: 1.5rem;
  line-height: 1.35;
  letter-spacing: 0.01em;
  color: var(--candle);
  margin: 0 0 0.9rem;
  padding: 0.85rem 3.2rem 0.7rem 0;
  text-shadow:
    0 0 26px rgba(var(--accent-rgb), 0.28),
    0 2px 12px rgba(0,0,0,0.95),
    0 0 22px rgba(5,5,4,0.95),
    0 0 10px rgba(5,5,4,0.9);
}

/* An answered block is history; only the open question pins itself. */
.choices.resolved .prompt {
  position: static;
  padding-right: 0;
}

.choice {
  display: flex;
  align-items: baseline;
  gap: 0.85rem;
  width: 100%;
  text-align: left;
  font-family: var(--body);
  font-size: 1.06rem;
  line-height: 1.55;
  color: var(--paper);
  background: rgba(var(--accent-rgb), 0.05);
  /* Border is always present so hover never shifts the layout. */
  border: 1px solid rgba(200,186,158,0.34);
  border-left: 3px solid var(--candle-deep);
  padding: 0.9rem 1.15rem;
  margin: 0 0 0.7rem;
  cursor: pointer;
  transition: background 220ms ease, border-color 220ms ease, color 220ms ease,
              box-shadow 220ms ease;
}

.choice-mark {
  flex: 0 0 auto;
  font-size: 0.62rem;
  color: var(--candle-dim);
  transform: translateY(-0.15em);
  transition: color 220ms ease;
}

.choice-body { flex: 1 1 auto; }

.choice:hover:not(:disabled),
.choice:focus-visible:not(:disabled) {
  background: rgba(var(--accent-rgb), 0.14);
  border-color: var(--candle-dim);
  border-left-color: var(--candle);
  color: #fff8e9;
  box-shadow: 0 0 26px rgba(var(--accent-rgb), 0.12) inset;
  outline: none;
}

.choice:hover:not(:disabled) .choice-mark,
.choice:focus-visible:not(:disabled) .choice-mark { color: var(--candle); }

/* The two real branch points carry more weight than a reactive beat. */
.choices.branch { border-top-color: var(--candle-deep); }
.choices.final .choice { font-size: 1.12rem; padding: 1.05rem 1.2rem; }
.choices.final .prompt { font-size: 1.75rem; }

.choices.resolved .choice {
  cursor: default;
  opacity: 0.26;
  background: transparent;
  border-color: transparent;
  box-shadow: none;
  padding-left: 0;
}

.choices.resolved .choice.picked {
  opacity: 1;
  color: var(--candle);
  border-left: 3px solid var(--candle-dim);
  padding-left: 1.15rem;
}

.choices.resolved .choice-mark { opacity: 0; }
.choices.resolved .choice.picked .choice-mark { opacity: 1; }

.choice:disabled { color: var(--paper-dim); }
.choices.resolved .choice.picked:disabled { color: var(--candle); }

/* ─────────────────────────────────── the held beat before a choice

   A choice block is painted with its scene and holds its full height from the
   start, so nothing below it ever shifts — but it is veiled until the player
   has actually scrolled to it. Then the bed goes out, a beat passes, and the
   prompt and options uncover one at a time. See the comment above
   `renderChoices` in js/main.js for the timings; REVEAL_FADE_MS there is this
   transition and the two must stay equal. */

.choices.veiled { border-top-color: transparent; }

.veiled-item {
  opacity: 0;
  transform: translateY(6px);
}

/* Inert while it is still uncovering. Kept focusable (aria-disabled, not
   `disabled`) so a keyboard player can tab into the block and trigger it. */
.choice[aria-disabled="true"] {
  pointer-events: none;
  cursor: default;
}

@media (prefers-reduced-motion: no-preference) {
  .prompt, .choice {
    transition: opacity 460ms ease, transform 460ms ease,
                background 220ms ease, border-color 220ms ease,
                color 220ms ease, box-shadow 220ms ease;
  }
}

/* Still veiled, still held — it appears rather than fades. The pause is the
   point; the movement is not. */
@media (prefers-reduced-motion: reduce) {
  .veiled-item { transform: none; transition: none; }
}

/* While the player is down at a choice, everything already read falls back to
   leave it alone on the page. Not a mode the page puts them in: `choosing` is
   driven by scroll position and comes off the moment they scroll back up to
   re-read something — text you are going back for has to be readable — and off
   for good once the choice is answered. Held light enough to be legible either
   way; the point is to stop competing, not to make the prose work to read. */
.scene-content.choosing .scene-title,
.scene-content.choosing .narration,
.scene-content.choosing .morse,
.scene-content.choosing .tier2,
.scene-content.choosing .response,
.scene-content.choosing .choices.resolved { opacity: 0.55; }

/* Quick enough to track a scroll rather than lag behind it. */
@media (prefers-reduced-motion: no-preference) {
  .scene-content .scene-title,
  .scene-content .narration,
  .scene-content .morse,
  .scene-content .tier2,
  .scene-content .response,
  .scene-content .choices.resolved { transition: opacity 420ms ease; }
}

/* ───────────────────────────────────────────────────────── response */

.response {
  margin: 1.75rem 0 0;
  color: var(--paper);
}

.response.closing {
  margin-top: 2.5rem;
  padding-top: 2rem;
  border-top: 1px solid var(--rule);
  font-style: italic;
  color: #f5efe2;
}

/* ───────────────────────────────────────────────────────── continue */

.continue-wrap {
  margin: 3.5rem 0 0;
  text-align: center;
}

.continue {
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--paper-dim);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule);
  padding: 0.75rem 1.5rem 0.6rem;
  cursor: pointer;
  transition: color 300ms ease, border-color 300ms ease, letter-spacing 400ms ease;
}

.continue:hover:not(:disabled),
.continue:focus-visible:not(:disabled) {
  color: var(--candle);
  border-color: var(--candle-dim);
  letter-spacing: 0.36em;
  outline: none;
}

.continue:disabled { opacity: 0.3; cursor: default; }

/* ─────────────────────────────────────────────────────────── the plate

   The one place art is shown undimmed. A 4:5 portrait can't fill a 16:9
   viewport without cropping the face, so the figure is contained and a
   blurred copy of itself fills the room behind it.
   ────────────────────────────────────────────────────────────────────── */

/* position: fixed (not just overflow: hidden) so a touch-scroll gesture on
   iOS Safari can't rubber-band the page under the plate; js/main.js pins the
   exact scroll offset here and restores it when the plate closes. */
body.plate-open { position: fixed; width: 100%; overflow: hidden; }

.plate {
  position: fixed;
  inset: 0;
  z-index: 30;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 2rem;
  padding: 5vh 1.5rem 4vh;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: #030303;
  cursor: pointer;
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .plate { transition: opacity 900ms ease; }
}

.plate.visible { opacity: 1; }
.plate:focus { outline: none; }

.plate-backdrop {
  position: absolute;
  inset: -8%;
  background-size: cover;
  background-position: center;
  filter: blur(46px) brightness(0.34) saturate(0.8);
  z-index: 0;
}

.plate-backdrop::after {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(ellipse at center, transparent 25%, rgba(0,0,0,0.85) 100%);
}

.plate-figure {
  position: relative;
  z-index: 1;
  flex: 0 1 auto;
  min-height: 0;
  display: flex;
}

.plate-figure img {
  max-height: 62vh;
  max-width: min(100%, 42rem);
  object-fit: contain;
  /* Undimmed on purpose — this is the only time the art plays at full strength.
     The hairline reads the crop as a deliberate plate rather than a seam. */
  border: 1px solid rgba(var(--accent-rgb), 0.16);
  box-shadow: 0 0 90px rgba(0,0,0,0.9), 0 0 200px rgba(var(--accent-rgb), 0.07);
}

.plate-text {
  position: relative;
  z-index: 1;
  max-width: 36rem;
  text-align: center;
  font-size: 1.08rem;
  line-height: 1.8;
  font-style: italic;
  color: #f5efe2;
}

.plate-text p { margin: 0; text-shadow: 0 2px 20px rgba(0,0,0,0.95); }

/* A captionless plate gives the image the whole frame. */
.plate.silent .plate-figure img { max-height: 74vh; }
.plate.silent .plate-text { display: none; }

.plate-hint {
  position: relative;
  z-index: 1;
  margin: 0;
  font-family: var(--label);
  font-size: 0.66rem;
  letter-spacing: 0.34em;
  text-transform: uppercase;
  color: var(--candle-dim);
  opacity: 0;
}

@media (prefers-reduced-motion: no-preference) {
  .plate.visible .plate-figure img { animation: plate-breathe 24s ease-out forwards; }
  .plate.visible .plate-hint { animation: plate-hint 1.6s ease 3.2s forwards; }
}

@media (prefers-reduced-motion: reduce) {
  .plate-hint { opacity: 0.8; }
}

@keyframes plate-breathe {
  from { transform: scale(1); }
  to   { transform: scale(1.055); }
}

@keyframes plate-hint {
  from { opacity: 0; }
  to   { opacity: 0.75; }
}

@media (max-width: 640px) {
  .plate { gap: 1.5rem; padding: 4vh 1.25rem 3vh; }
  .plate-figure img { max-height: 50vh; }
  .plate-text { font-size: 1rem; }
}

/* Wide-and-shorter-than-tall reads as "a computer", not just "a big phone" —
   min-aspect-ratio keeps a portrait tablet on the stacked mobile layout above
   even though it's wider than 640px. Side by side: art left, text right. */
@media (min-width: 641px) and (min-aspect-ratio: 1/1) {
  .plate {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    gap: 3rem;
    padding: 4vh 5vw;
  }

  .plate-figure { order: 1; }
  .plate-figure img { max-height: 72vh; max-width: 40vw; }

  .plate-text {
    order: 2;
    max-width: 26rem;
    text-align: left;
  }

  /* The hint stays a single line under both columns rather than squeezed
     beside them — flex-basis: 100% forces it onto its own wrapped row. */
  .plate-hint { order: 3; flex-basis: 100%; text-align: center; }

  .plate.silent .plate-figure img { max-width: min(70vw, 44rem); }
}

/* ───────────────────────────────────────────────────────── endings */

body.is-ending .scene-content { padding-top: 20vh; }

.ending-text {
  font-size: 1.05rem;
  line-height: 1.85;
}

/* Not the blackletter here: a single blackletter capital is hard to read out
   of context, and this one is the first word of the ending. */
.ending-text p:first-child::first-letter {
  font-family: var(--label);
  font-weight: 600;
  font-size: 3.1rem;
  line-height: 0.95;
  float: left;
  padding: 0.06em 0.12em 0 0;
  color: var(--candle);
}

.player-name {
  font-family: var(--display);
  font-size: 1.35em;
  color: var(--candle);
  padding: 0 0.06em;
  text-shadow: 0 0 22px rgba(var(--accent-rgb), 0.3);
  word-break: break-word;
}

.ending-label {
  margin: 4rem 0 0;
  font-family: var(--label);
  font-size: 0.7rem;
  letter-spacing: 0.36em;
  text-transform: uppercase;
  text-align: center;
  color: var(--candle-dim);
}

.ending-label::before {
  content: "";
  display: block;
  width: 3.5rem;
  height: 1px;
  margin: 0 auto 1.5rem;
  background: var(--rule);
}

/* ───────────────────────────────────────────────────────── small screens */

@media (max-width: 640px) {
  html, body { font-size: 17px; }
  .scene-content { padding: 9vh 1.25rem 26vh; }
  .drip { left: 3vw; }
  #title-screen h1 { text-shadow: none; }
}

/* ────────────────────────────────────────────────────────── sound toggle
   Fixed top right, above everything including plates and endings, and
   reachable at every moment of the game. Off by default — the press that
   turns it on is also the gesture browsers require before audio may start.
   ────────────────────────────────────────────────────────────────────── */

/* The standing controls: sound, and language beneath it. One rule for both, so
   a change to the well, the ring or the lit state cannot land on one and miss
   the other. Positions are set per button below; everything else is shared.

   Both sit above the tally wall (80) as well as plates and endings — sound doc
   §2 says always visible, and the wall is a page the player can sit on while
   the ending's bed is still sounding. Below the wall's own lightbox (90),
   which any click dismisses anyway. */
#sound-toggle,
#lang-toggle {
  position: fixed;
  right: 1.4rem;
  z-index: 85;
  width: 2.6rem;
  height: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: rgba(6, 6, 5, 0.55);
  border: 1px solid var(--rule);
  border-radius: 50%;
  cursor: pointer;
  /* The lit resting state. The sound button spends the whole game here (it is
     on by default), so this is what "a standing control" looks like in this
     corner, and the language button matches it rather than sitting a stop
     dimmer and reading as disabled. */
  color: var(--candle-dim);
  opacity: 0.8;
  transition: opacity 300ms ease, color 300ms ease, border-color 300ms ease;
}

#sound-toggle:hover,
#sound-toggle:focus-visible,
#lang-toggle:hover,
#lang-toggle:focus-visible {
  opacity: 1;
  color: var(--candle);
  border-color: var(--candle-dim);
  outline: none;
}

#sound-toggle { top: 1.4rem; }

/* Muted only when sound is actually off — the one control here with an
   off state to show. */
#sound-toggle:not(.is-on) { color: var(--paper-faint); opacity: 0.55; }

#sound-toggle svg {
  width: 1.2rem;
  height: 1.2rem;
  fill: currentColor;
}

#sound-toggle svg .stroke {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.6;
  stroke-linecap: round;
}

@media (max-width: 640px) {
  #sound-toggle,
  #lang-toggle { right: 0.9rem; width: 2.3rem; height: 2.3rem; }
  #sound-toggle { top: 0.9rem; }
}

/* ──────────────────────────────────────────────────────── the tally wall */
/* A record of every plate the game can show. Inked cells are the art, shown
   plain — it is already black-and-white line work, so no filter is applied to
   it anywhere here. Un-inked cells draw marks and request no image at all;
   see js/gallery.js for why that matters on a phone. */

/* Root font-size is 20px, so this is ~1000px, not the ~990 a 16px root would
   give. Wider than this and the evidence grid stops reading as a wall. */
.wall {
  max-width: 50rem;
  margin: 0 auto;
  padding: 0 0 1rem;
}

.wall-head { text-align: center; margin-bottom: 3.4rem; }

.wall-head h2 {
  font-family: var(--label);
  font-weight: 400;
  font-size: 0.78rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--paper-faint);
  margin: 0 0 1.4rem;
}

/* Roman numerals in the gothic display face are unreadable — XVII comes out a
   smear. The typewriter face is also the right voice for a count. */
.wall-count {
  font-family: var(--cal-mono);
  font-size: clamp(1.8rem, 5vw, 2.7rem);
  letter-spacing: 0.12em;
  line-height: 1;
  color: var(--candle);
  margin: 0;
}

.wall-count .of { color: var(--candle-deep); }

.wall-rule {
  height: 1px;
  width: 12rem;
  margin: 1.6rem auto 0;
  background: linear-gradient(to right, transparent, var(--candle-dim), transparent);
}

.wall-register { margin-bottom: 3.2rem; }

.wall-register > h3 {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  font-family: var(--label);
  font-weight: 400;
  font-size: 0.7rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  color: var(--candle-dim);
  margin: 0 0 1.3rem;
}

.wall-register > h3::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--rule);
}

.wall-of {
  font-family: var(--cal-mono);
  font-size: 0.62rem;
  letter-spacing: 0.1em;
  color: var(--paper-faint);
}

/* Keyed off each register's `layout` role (wall.js), never its display name:
   Chapter II renames every register, and keying this to the label silently
   dropped the columns and the aspect-ratio, collapsing the grid to one giant
   cell per row. */
.wall-grid { display: grid; gap: 0.7rem; }
.wall-grid.wide     { grid-template-columns: repeat(2, 1fr); }
.wall-grid.square   { grid-template-columns: repeat(5, 1fr); }
.wall-grid.portrait { grid-template-columns: repeat(4, 1fr); gap: 1rem; }

.wall-cell {
  position: relative;
  display: block;
  width: 100%;
  padding: 0;
  overflow: hidden;
  font: inherit;
  color: inherit;
  background: var(--ink-deep);
  /* Louder than --rule: the plates are dark and edge-to-edge, and a 0.2-alpha
     hairline lets them bleed into each other instead of reading as a wall of
     framed things. */
  border: 1px solid var(--candle-deep);
  transition: border-color 300ms ease;
}

.wall-cell.seen:hover,
.wall-cell.seen:focus-visible { border-color: var(--candle-dim); }

.wide     .wall-cell { aspect-ratio: 16 / 9; }
.square   .wall-cell { aspect-ratio: 1 / 1; }
.portrait .wall-cell { aspect-ratio: 4 / 5; }

.wall-cell.seen { cursor: pointer; }

.wall-cell.seen img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 400ms ease;
}

.wall-cell.seen:hover img,
.wall-cell.seen:focus-visible img { transform: scale(1.015); }

/* Candlelight over the plate, so it sits in the room's light rather than on
   top of it. This is a wash only — the art itself is never filtered, it is
   already black and white. Lifts on hover so the plate comes up to meet you. */
.wall-cell.seen::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: radial-gradient(ellipse at 50% 45%,
    rgba(var(--accent-rgb), 0.13), rgba(10, 9, 8, 0.42) 82%);
  transition: opacity 300ms ease;
}

.wall-cell.seen:hover::after,
.wall-cell.seen:focus-visible::after { opacity: 0.45; }

.wall-cell.seen:focus-visible { outline: 1px solid var(--candle); outline-offset: 2px; }

.wall-plate {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 2;
  padding: 1.5rem 0.55rem 0.45rem;
  background: linear-gradient(to top, rgba(5, 5, 4, 0.94), transparent);
  opacity: 0;
  transition: opacity 260ms ease;
  text-align: left;
  pointer-events: none;
}

.wall-cell.seen:hover .wall-plate,
.wall-cell.seen:focus-visible .wall-plate { opacity: 1; }

.wall-plate .num {
  display: block;
  font-family: var(--cal-mono);
  font-size: 0.55rem;
  letter-spacing: 0.14em;
  color: var(--candle-dim);
}

.wall-plate .name {
  font-family: var(--label);
  font-size: 0.72rem;
  line-height: 1.35;
  letter-spacing: 0.04em;
  color: var(--paper);
}

/* Marks scratched into the wall, in place of a plate not reached. */
.wall-cell.unseen svg { display: block; width: 100%; height: 100%; }

.wall-cell.unseen svg line {
  stroke: var(--candle-deep);
  stroke-width: 3.4;
  stroke-linecap: round;
}

.wall-veil {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--cal-mono);
  font-size: 0.5rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: transparent;
  transition: color 220ms ease;
}

.wall-cell.unseen:hover .wall-veil { color: var(--paper-faint); }

.wall-foot {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid var(--rule);
  font-family: var(--label);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  line-height: 1.9;
  color: var(--paper-faint);
  text-align: center;
}

.wall-secret {
  max-width: 24rem;
  margin: 2rem auto 0;
  text-align: center;
}

/* Inert until armSecret's observer uncovers it — mirrors .choice[aria-disabled],
   but the whole plaque at once: there is only ever the one question here. */
.wall-secret.veiled { pointer-events: none; }

.wall-secret-prompt {
  font-family: var(--label);
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  color: var(--paper-dim);
  margin: 0 0 1rem;
}

.wall-secret-form { display: flex; justify-content: center; }

.wall-secret-input {
  width: 100%;
  max-width: 18rem;
  padding: 0.5rem 0;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--rule);
  font-family: var(--label);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-align: center;
  text-transform: uppercase;
  color: var(--paper-dim);
}

.wall-secret-input::placeholder { color: var(--paper-faint); }

.wall-secret-input:focus {
  outline: none;
  border-color: var(--candle-dim);
}

.wall-secret-ask {
  display: block;
  margin: 0.9rem auto 0;
  padding: 0;
  background: none;
  border: none;
  font-family: var(--label);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--paper-faint);
  cursor: pointer;
  opacity: 0.65;
  transition: opacity 300ms ease, color 300ms ease;
}

.wall-secret-ask:hover,
.wall-secret-ask:focus-visible { opacity: 1; color: var(--candle-dim); }

/* Same specificity as .wall-secret-ask's own opacity, so source order alone
   would let this lose to it while still veiled. The extra class wins instead. */
.wall-secret-ask.veiled-item { opacity: 0; }

.wall-secret.is-revealed { animation: wall-secret-in 500ms ease; }

@media (prefers-reduced-motion: no-preference) {
  .wall-secret-prompt, .wall-secret-form, .wall-secret-ask {
    transition: opacity 460ms ease, transform 460ms ease;
  }
}

.wall-secret-phrase {
  font-family: var(--label);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--paper-dim);
}

.wall-secret-translation {
  margin-top: 0.4rem;
  font-family: var(--body);
  font-size: 0.78rem;
  font-style: italic;
  color: var(--paper-faint);
}

@keyframes wall-secret-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* ── one plate at full size ── */

.wall-box {
  position: fixed;
  inset: 0;
  z-index: 90;
  display: grid;
  place-items: center;
  padding: 2rem;
  background: rgba(5, 5, 4, 0.97);
  opacity: 0;
  transition: opacity 300ms ease;
  cursor: zoom-out;
}

.wall-box.visible { opacity: 1; }

.wall-box img {
  display: block;
  max-width: min(94vw, 1400px);
  max-height: 82vh;
  border: 1px solid var(--candle-deep);
}

.wall-box-caption {
  margin: 1.1rem 0 0;
  text-align: center;
  font-family: var(--label);
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  color: var(--paper-dim);
}

.wall-box-caption .num {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--cal-mono);
  font-size: 0.6rem;
  letter-spacing: 0.16em;
  color: var(--candle-dim);
}

/* ── the wall as an overlay, from the title screen ── */

.wall-overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 4.5rem 1.5rem 5rem;
  background: var(--ink);
  opacity: 0;
  transition: opacity 400ms ease;
}

.wall-overlay.visible { opacity: 1; }

body.wall-open { overflow: hidden; }

/* The wall has no close icon in any of its forms. Its only exit is the button
   at its foot — "Begin again" on the wall an ending arrives at, and the same
   button dropping `?all` on the wall opened straight from the title screen.
   The top corner belongs to the two standing controls (sound, language) and a
   third icon of a different kind crowded them. Escape still dismisses a
   non-final wall. */

/* ── the wall as the page an ending arrives at ── */

/* Opaque, and above everything the ending left on screen: this is its own
   black page, not a panel over the ending. */
.wall-overlay.is-final { background: var(--ink-deep); }

.wall-again {
  max-width: 50rem;
  margin: 0 auto;
  padding: 2.5rem 0 1rem;
  text-align: center;
}

/* Held back until the secret plaque above it has been answered or asked
   for (js/gallery.js, revealBeginAgain) — the wall's one earned arrival. */
.wall-again.wall-again-pending { display: none; }
.wall-again.wall-again-in { animation: wall-secret-in 500ms ease; }

/* Two exits at the foot of the wall: stack them rather than sitting them side
   by side, so neither reads as the other's alternative. */
.wall-again .continue + .continue { margin-top: 1.1rem; }

/* The door on to Chapter II, kept quieter than "Begin again" — the same
   treatment as the title screen's second door, since it is the same door.
   No border, dimmer than the text around it, and it only comes up to legible
   on hover. */
.wall-next-chapter {
  display: block;
  margin-left: auto;
  margin-right: auto;
  border-color: transparent;
  color: #4a453c;
  letter-spacing: 0.14em;
  font-size: 0.68rem;
  padding: 0.6rem 1rem;
  transition: color 400ms ease, border-color 400ms ease;
}

.wall-next-chapter:hover,
.wall-next-chapter:focus-visible {
  color: var(--candle-dim);
  border-color: transparent;
  background: transparent;
  box-shadow: none;
  outline: none;
}

@media (max-width: 760px) {
  .wall-grid.wide     { grid-template-columns: 1fr; }
  .wall-grid.square   { grid-template-columns: repeat(2, 1fr); }
  .wall-grid.portrait { grid-template-columns: repeat(2, 1fr); }
  .wall-overlay { padding: 4rem 1rem 3.5rem; }
}

/* ═════════════════════════════════════════════════ language ══════════ */

/* Shape, well, ring and lit state come from the shared standing-control rule
   up with #sound-toggle. What is left here is position and lettering.

   The face is the language it offers, not the one being read: 文 while you are
   in English, EN while you are in Chinese. A single 文 is legible to the
   reader who wants it; "EN" is the more familiar mark going the other way than
   a bare A, and two letters still fit the circle. The accessible name carries
   the full word, in the offered language, with a matching `lang` (i18n.js). */
#lang-toggle {
  top: 4.5rem;               /* 1.4rem + the sound button's 2.6rem + 0.5rem */
  font-family: var(--label);
  font-size: 1rem;
  line-height: 1;
  letter-spacing: 0;
}

/* Offering EN: two Latin letters, set in the Latin label face rather than the
   kai body face and tightened to sit inside the same circle as one CJK glyph. */
#lang-toggle[data-lang="zh"] {
  font-family: "Grenze", Georgia, serif;
  font-size: 0.92rem;
  letter-spacing: 0.02em;
}

@media (max-width: 640px) {
  #lang-toggle { top: 3.6rem; font-size: 0.9rem; }   /* 0.9 + 2.3 + 0.4 */
  #lang-toggle[data-lang="zh"] { font-size: 0.82rem; }
}

/* ─────────────────────────────────────────────── the Chinese palette

   Every face in this stylesheet is reached through the six variables in
   :root, so a translation swaps the whole typographic register in one block
   rather than at ~40 call sites. None of the Latin faces carry CJK glyphs at
   all — without this the Chinese pack renders in the system UI font.

   The body face is LXGW WenKai (霞鹜文楷), an open 楷体 — brush-derived regular
   script, with the stroke entries and exits of a written hand still in it.
   That is the right register for this game and a Song/Ming face is not: a
   Song is the Chinese newspaper letter, upright and mechanical, and it made
   the narration read like a report. The whole story is one person remembering
   out loud. Kai is the closest Chinese equivalent to the hand the English is
   set in.

   Google Fonts carries no kai, so it comes from jsDelivr; the system kai
   faces sit behind it for anyone offline, and Noto Serif SC behind those as
   the last resort before generic serif. Only one of them is ever downloaded.

   Ma Shan Zheng stays in the display slot the blackletter held. It is much
   more cursive than the body kai, which keeps the hierarchy the blackletter
   gave the English, and it carries the calendar's hand as well.

   The metric adjustments below are not cosmetic. CJK glyphs are full-width and
   have no ascender/descender air, so Latin line-heights read cramped and Latin
   letter-spacing — set on nearly every .label rule for the small-caps look —
   reads as broken text rather than as tracking. */
body[data-lang="zh"] {
  --kai: "LXGW WenKai Screen", "Kaiti SC", "STKaiti", "TW-Kai", KaiTi, BiauKai,
         "Noto Serif SC", Georgia, serif;

  --display: "Ma Shan Zheng", var(--kai);
  --head:    var(--kai);
  --body:    var(--kai);
  --label:   var(--kai);
  --cal-title: "Ma Shan Zheng", var(--kai);
  --cal-mono:  var(--kai);

  /* A CJK line needs fewer characters to fill the same measure, and more air
     between lines to stay readable at this size. */
  --measure: 30rem;

  /* WenKai ships a single weight and is already light on a dark ground; asking
     for 300 here only invited a synthesised thin from the fallbacks. */
  line-height: 2;
}

body[data-lang="zh"] .narration p,
body[data-lang="zh"] .plate-text p,
body[data-lang="zh"] .response p,
body[data-lang="zh"] .tier2-panel p {
  line-height: 2;
}

/* Tracking that flatters small-caps Latin pulls CJK apart. Kept on the
   numerals and register headings, which are still Roman. */
body[data-lang="zh"] .tier2-heading,
body[data-lang="zh"] .prompt,
body[data-lang="zh"] .choice-body,
body[data-lang="zh"] .ending-label,
body[data-lang="zh"] .foot,
body[data-lang="zh"] .latin,
body[data-lang="zh"] #begin,
body[data-lang="zh"] #skip-to-ii,
body[data-lang="zh"] .continue,
body[data-lang="zh"] .wall-veil,
body[data-lang="zh"] .calendar-title {
  letter-spacing: 0.02em;
}

/* The blackletter title kept a heavy display size the brush hand does not
   need; Ma Shan Zheng at the same size overruns the measure on a phone. */
body[data-lang="zh"] #title-screen h1 {
  letter-spacing: 0.08em;
}

/* The calendar's weekday row is a single character per column in Chinese
   rather than one Latin initial, so the typed-glyph face's wide tracking is
   what makes it wrap. */
body[data-lang="zh"] .cal-head {
  letter-spacing: 0;
}

/* Noto Serif SC ships no italic, so every `font-style: italic` in this
   stylesheet becomes a synthesised oblique — a slanted CJK line is not an
   emphasis convention anywhere, it just looks like a rendering fault. The
   whole-block italics (the blurb, plates, the closing response, the wall's
   translation) go upright; they are already set apart by size, colour and
   measure. Inline <em>, which carries actual stress in the prose, takes the
   Chinese convention instead: 着重号, a mark beside each character. */
body[data-lang="zh"] .blurb,
body[data-lang="zh"] .latin em,
body[data-lang="zh"] .tier2-label,
body[data-lang="zh"] .response.closing,
body[data-lang="zh"] .plate-text,
body[data-lang="zh"] .wall-secret-translation,
body[data-lang="zh"] #carve-input::placeholder {
  font-style: normal;
}

body[data-lang="zh"] em {
  font-style: normal;
  text-emphasis: sesame;
  -webkit-text-emphasis: sesame;
  text-emphasis-color: var(--candle-dim);
  -webkit-text-emphasis-color: var(--candle-dim);
  /* 着重号 sit under the line in Chinese, not over it — over is the Japanese
     convention and reads here as a stray row of marks. */
  text-emphasis-position: under right;
  -webkit-text-emphasis-position: under;
}

/* …except where <em> wraps a whole quoted line rather than a word or two.
   Sesame marks under a four-line remembered quotation read as damage; those
   passages are set off by colour alone. */
body[data-lang="zh"] .plate-text em,
body[data-lang="zh"] .latin em {
  text-emphasis: none;
  -webkit-text-emphasis: none;
}

/* The wordmark is Latin and is not translated: it keeps the blackletter it has
   in every language. Only the copy around it changes face. */
body[data-lang="zh"] #title-screen h1 {
  font-family: "Grenze Gotisch", "UnifrakturMaguntia", Georgia, serif;
}
