/* =========================================================================
   CASTLESCAPE: project page
   -------------------------------------------------------------------------
   The one page on this site that is dark, and it has to be: the subject is a
   castle with the lights off, and a flame on paper white is just an orange
   smudge. Rather than restate every shared component in a dark skin, the page
   redefines the site's own tokens on :root, so .btn, .section-head,
   .stack-badge, the focus ring, the scrollbar and js/dots-bg.js (which reads
   the same custom properties at runtime) all follow, unedited.

   The accent is the torch: TORCH_LIGHT_COLOR from main.cpp, #ff8029. During a
   hunt it becomes the game's violet, driven by data-phase on <body>.

   Sections:
   01. Tokens
   02. Base & shared overrides
   03. Header
   04. Canvases (shared)
   05. Hero
   06. Overview
   07. The flame
   08. The five stages
   09. The ghosts
   10. The hunt cycle
   11. Under the hood
   12. Controls
   13. Gallery & demo
   14. Footer
   15. Reduced motion & responsive
   ========================================================================= */

/* ====================== 01. TOKENS ====================== */
/* Set on the document element, not on .cs-body: js/palette.js reads these
   from :root, and the dot field behind the page is drawn from them. */
html:has(.cs-body) {
  --paper:     #0b0a09;          /* stone in the dark */
  --paper-rgb: 11, 10, 9;
  --ink:       #e9e3d8;          /* warm off-white, not a clinical grey */
  --ink-rgb:   233, 227, 216;

  /* The accent. Hunt phases rewrite these three, and everything follows. */
  --forest:      #ff8029;        /* TORCH_LIGHT_COLOR, main.cpp */
  --forest-rgb:  255, 128, 41;
  --forest-mid:  #a85a28;        /* dot field at rest */
  --forest-deep: #ffb066;        /* dot field fully displaced: an ember wake */
  --forest-glow: #ffc999;

  --graphite:  #8d8478;          /* muted text */
  --line:      rgba(var(--forest-rgb), 0.22);
  --paper-raise: #15120f;        /* raised surfaces: notes, keycaps, frames */

  /* The readability wash under text becomes a darkening, not a lightening. */
  --veil: rgba(var(--paper-rgb), 0.74);

  /* Page-local */
  --cs-hairline: rgba(var(--ink-rgb), 0.12);
  --cs-dim:      rgba(var(--ink-rgb), 0.62);

  color-scheme: dark;
}

@media (prefers-reduced-motion: reduce) {
  html:has(.cs-body) body[data-phase="warning"],
  html:has(.cs-body) body[data-phase="hunt"],
  html:has(.cs-body) body[data-phase="recover"] {
    --forest:      #8b5cff;
    --forest-rgb:  139, 90, 255;
    --forest-mid:  #4a3580;
    --forest-deep: #b79bff;
    --forest-glow: #cbb8ff;
  }
}

/* During a hunt castlescape-page.js rewrites the accent tokens above every
   frame, from the same hunt clock the flames read: the page turns violet on
   the same ramp and pulses on the same 4 Hz warning beat. These values are
   only the starting point, and what a visitor without JavaScript keeps. */

/* ====================== 02. BASE & SHARED OVERRIDES ====================== */
.cs-body {
  background: var(--paper);
  color: var(--ink);
  overflow-x: clip;
}
html:has(.cs-body) { overflow-x: clip; }

/* The paper grain multiplies, which on near-black does nothing but mud the
   page. Screen it instead, at a fraction of the strength: dust in the air. */
.cs-body::after {
  opacity: 0.035;
  mix-blend-mode: screen;
}

/* A slow warm gradient from the bottom, as if the light were coming from the
   torches further down the page. Behind everything, above the dot field. */
.cs-body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(120% 70% at 50% 108%, rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.16), transparent 62%),
    radial-gradient(80% 50% at 50% -10%, rgba(var(--ink-rgb), 0.05), transparent 70%);
  /* No transition: the colour is already animated frame by frame from the
     hunt clock, and easing on top of that would smear the warning pulse. */
}

.cs-main { display: block; }

.cs-body ::selection { background: var(--forest); color: var(--paper); }

/* Shared components, re-tuned for a dark surface. */
.cs-body .btn { background: rgba(var(--paper-rgb), 0.6); }
/* Text on the accent is pure black: the accent is kept at 4.5:1 or better
   against the stone, which puts black on the accent at 4.8:1 or better. */
/* The fill is restated here: .cs-body .btn above outranks the shared
   .btn--solid and would otherwise leave black text on the dark stone. */
.cs-body .btn--solid {
  background: var(--forest);
  border-color: var(--forest);
  color: #000;
  font-weight: 600;
}
/* The shared hover paints --forest-mid, which here is the dot field's dark
   ember, dark text on a dark fill. Lighten instead. */
.cs-body .btn--solid:hover {
  background: var(--forest-glow);
  border-color: var(--forest-glow);
  color: #000;
}
.cs-body .btn--ghost { color: var(--forest-glow); border-color: rgba(var(--forest-rgb), 0.7); }
.cs-body .btn--ghost:hover { background: rgba(var(--forest-rgb), 0.14); }

.cs-section-head { border-bottom-color: rgba(var(--forest-rgb), 0.5); }

/* ====================== 03. HEADER ====================== */
.cs-top {
  position: sticky;
  top: 0;
  z-index: 20;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 1rem;
  padding: 1.1rem var(--gutter);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  background: linear-gradient(to bottom, rgba(var(--paper-rgb), 0.94), rgba(var(--paper-rgb), 0));
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  /* The blur must not catch the bar's own bottom edge, or the gradient shows
     a seam where the backdrop stops. */
  -webkit-mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
  mask-image: linear-gradient(to bottom, #000 62%, transparent 100%);
}
.cs-back, .cs-top-github { color: var(--forest); transition: color 0.2s var(--ease-soft); }
.cs-back:hover, .cs-top-github:hover { color: var(--forest-glow); }

/* ------------------------- the pinned hunt switch ------------------------
   Bottom-right, clear of the notch and the home indicator. Every colour in
   it comes from the accent tokens, which the hunt clock rewrites each frame,
   so the switch ramps and pulses with the flames instead of beside them. */
.cs-switch {
  position: fixed;
  right: max(1rem, env(safe-area-inset-right));
  bottom: max(1rem, env(safe-area-inset-bottom));
  z-index: 30;
  display: inline-flex;
  align-items: center;
  gap: 0.85rem;
  padding: 0.55rem 0.6rem 0.55rem 1rem;
  border: 1px solid rgba(var(--forest-rgb), 0.45);
  border-radius: 999px;
  background: rgba(var(--paper-rgb), 0.78);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.45),
              0 0 22px rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.14);
  cursor: pointer;
  transition: border-color 0.2s var(--ease-soft), transform 0.12s var(--ease-soft);
}
.cs-switch:hover { border-color: rgba(var(--forest-rgb), 0.8); }
.cs-switch:active { transform: translateY(1px); }

.cs-switch__text {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  line-height: 1.15;
  font-family: var(--font-mono);
  text-transform: uppercase;
}
.cs-switch__label {
  font-size: 0.68rem;
  letter-spacing: 0.18em;
  color: var(--graphite);
}
.cs-switch__state {
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  color: var(--forest);
  /* Tabular width, so Calm → Warning → Hunt doesn't make the pill breathe. */
  min-width: 7ch;
  text-align: right;
}

.cs-switch__track {
  position: relative;
  width: 2.9rem;
  height: 1.6rem;
  border-radius: 999px;
  background: rgba(var(--ink-rgb), 0.08);
  border: 1px solid var(--cs-hairline);
  transition: background 0.3s var(--ease-soft);
}
.cs-switch__thumb {
  position: absolute;
  top: 50%;
  left: 0.2rem;
  width: 1.15rem;
  height: 1.15rem;
  border-radius: 50%;
  transform: translateY(-50%);
  /* An ember: the thumb is the flame, lit in whatever colour the torches are. */
  background: radial-gradient(circle at 50% 60%, var(--forest-glow), var(--forest) 70%);
  box-shadow: 0 0 10px rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.7);
  transition: left 0.35s var(--ease-out);
}
.cs-switch[aria-checked="true"] .cs-switch__track {
  background: rgba(var(--forest-rgb), 0.22);
  border-color: rgba(var(--forest-rgb), 0.5);
}
.cs-switch[aria-checked="true"] .cs-switch__thumb { left: calc(100% - 1.35rem); }

/* The footer's last line must stay readable with the pill parked over it. */
.cs-footer { padding-bottom: 5.5rem; }

/* ====================== 04. CANVASES (SHARED) ====================== */
/* Every live subject sits in a bare canvas over the page: no frame, no bed,
   no background. The renderer composites with an alpha that carries the
   bloom halo, so the glow spills onto the stone instead of stopping at a box. */
.cs-canvas {
  display: block;
  width: 100%;
  /* A canvas is a replaced element: left alone it keeps its 300x150 intrinsic
     size, so every subject states its own ratio and lets the height follow.
     Where a max-height clamps that ratio away, nothing breaks: the renderer
     reads the drawing buffer's real aspect each frame and reframes to it. */
  height: auto;
  min-height: 0;
}
/* The hero is the exception: its stage owns the box, the canvas fills it. */
.cs-canvas--hero { height: 100%; }

.cs-canvas__fallback {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  line-height: 1.6;
  color: var(--cs-dim);
  border: 1px dashed var(--cs-hairline);
  border-radius: 3px;
  padding: 1.4rem;
  text-align: center;
  align-self: center;
}

/* ====================== 05. HERO ====================== */
.cs-hero {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4rem);
  padding: clamp(2rem, 7vh, 5rem) var(--gutter) clamp(3rem, 9vh, 6rem);
  min-height: min(88vh, 900px);
}

.cs-hero__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.18em;
  color: var(--forest);
  margin-bottom: 1.4rem;
}

.cs-hero__title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 125;
  font-weight: 800;
  text-transform: uppercase;
  font-size: clamp(3rem, 11vw, 7.5rem);
  line-height: 0.86;
  letter-spacing: -0.02em;
  margin-bottom: 1.6rem;
  /* Lit from below, the way the torches light the page. */
  background: linear-gradient(to top, var(--forest-glow) 0%, var(--ink) 58%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.cs-hero__title-break { display: block; }
.cs-hero__title-sub {
  /* The second half sits back: it is the same word, further into the dark. */
  color: transparent;
  -webkit-text-stroke: 1px rgba(var(--ink-rgb), 0.5);
  background: none;
}

.cs-hero__tagline {
  font-size: clamp(1rem, 1.6vw, 1.18rem);
  color: var(--cs-dim);
  max-width: 44ch;
  margin-bottom: 2.2rem;
}

.cs-hero__spec {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1rem 2rem;
  max-width: 34rem;
  margin-bottom: 2.4rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--cs-hairline);
}
.cs-hero__spec dt {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--forest);
  margin-bottom: 0.25rem;
}
.cs-hero__spec dd { font-size: 0.95rem; color: var(--ink); }

.cs-hero__cta { display: flex; flex-wrap: wrap; gap: 0.8rem; }

.cs-hero__stage {
  display: flex;
  aspect-ratio: 4 / 5;
  max-height: min(78vh, 760px);
  /* The torches are lit from within; a glow behind them would flatten the
     silhouette, so the stage paints nothing at all. */
}

/* ====================== 06. OVERVIEW ====================== */
.cs-overview {
  max-width: 62rem;
  margin: 0 auto;
  padding: 0 var(--gutter) var(--section-gap);
  text-align: center;
}
.cs-overview__label {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--forest);
  margin-bottom: 1.6rem;
}
.cs-overview__lead {
  font-size: clamp(1.25rem, 2.6vw, 1.8rem);
  line-height: 1.45;
  margin-bottom: 1.4rem;
}
.cs-overview__lead strong { color: var(--forest-glow); font-weight: 700; }
.cs-overview__text { color: var(--cs-dim); max-width: 52ch; margin: 0 auto; }

/* ====================== 07. THE FLAME ====================== */
.cs-fire__intro {
  max-width: 58ch;
  margin: 0 auto clamp(2.5rem, 6vh, 4rem);
  padding: 0 var(--gutter);
  color: var(--cs-dim);
  font-size: 1.05rem;
  text-align: center;
}
.cs-fire__intro strong { color: var(--ink); font-weight: 600; }

.cs-fire__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4.5rem);
  padding: 0 var(--gutter);
}

.cs-fire__stage { display: flex; flex-direction: column; gap: 1rem; }
.cs-canvas--torch { aspect-ratio: 3 / 4; }
/* Side by side, a full-width 3/4 torch stood nearly twice as tall as the notes
   next to it, leaving them floating in empty space. Narrower, it keeps its
   ratio and roughly matches their height. */
@media (min-width: 1025px) {
  .cs-fire__grid { grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.1fr); }
  .cs-fire__stage { width: 100%; max-width: 28rem; justify-self: center; }
}

.cs-fire__hint {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.08em;
  color: var(--graphite);
  text-align: center;
}

/* A definition list used as a spec sheet: term in the accent, rule between
   rows, same language as the drafting-sheet lines elsewhere on the site. */
.cs-notes { display: grid; gap: 0; }
.cs-notes__row {
  display: grid;
  grid-template-columns: minmax(7.5rem, 0.32fr) minmax(0, 1fr);
  gap: 1.2rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--cs-hairline);
}
.cs-notes__row:last-child { border-bottom: 1px solid var(--cs-hairline); }
.cs-notes dt {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest);
  padding-top: 0.15em;
}
.cs-notes dd { color: var(--cs-dim); font-size: 0.95rem; }

/* ====================== 08. THE FIVE STAGES ====================== */
.cs-stages {
  margin-top: clamp(3.5rem, 9vh, 6rem);
  padding: 0 var(--gutter);
}
.cs-stages__title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 110;
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(1.1rem, 2.4vw, 1.5rem);
  letter-spacing: 0.02em;
  margin-bottom: 0.6rem;
}
.cs-stages__lead {
  color: var(--cs-dim);
  max-width: 58ch;
  margin-bottom: 1.8rem;
  font-size: 0.98rem;
}

.cs-canvas--stages {
  aspect-ratio: 5 / 2;
  max-height: 46vh;
}

.cs-stages__legend {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1rem;
  margin-top: 0.75rem;
  padding-top: 1rem;
  border-top: 1px solid var(--cs-hairline);
}
.cs-stages__legend li { display: flex; flex-direction: column; gap: 0.3rem; }
.cs-stages__no {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  color: var(--forest);
}
.cs-stages__name {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink);
}
.cs-stages__desc { font-size: 0.85rem; line-height: 1.5; color: var(--cs-dim); }

/* ====================== 09. THE GHOSTS ====================== */
.cs-ghosts__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: center;
  gap: clamp(2rem, 5vw, 4.5rem);
  padding: 0 var(--gutter);
}
.cs-canvas--ghost { aspect-ratio: 1 / 1; }

.cs-ghosts__lead {
  font-size: clamp(1.05rem, 2vw, 1.28rem);
  line-height: 1.5;
  margin-bottom: 1.6rem;
}

/* ====================== 10. THE HUNT CYCLE ====================== */
.cs-hunt {
  margin: clamp(3.5rem, 9vh, 6rem) var(--gutter) 0;
  padding: clamp(1.6rem, 4vw, 2.6rem);
  border: 1px solid rgba(var(--forest-rgb), 0.28);
  border-radius: 3px;
  background: var(--paper-raise);
  transition: border-color 0.6s var(--ease-soft), background 0.6s var(--ease-soft);
}
body[data-phase="warning"] .cs-hunt,
body[data-phase="hunt"] .cs-hunt,
body[data-phase="recover"] .cs-hunt {
  border-color: rgba(var(--forest-rgb), 0.55);
  background: #120e1c;
}
/* The panel is where the pulse is most legible: let the ember glow breathe
   through its border while the telegraph runs. */
body[data-phase="warning"] .cs-hunt {
  box-shadow: 0 0 40px rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.18);
}

.cs-hunt__title {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 110;
  font-weight: 700;
  text-transform: uppercase;
  font-size: clamp(1.1rem, 2.4vw, 1.5rem);
  margin-bottom: 0.5rem;
}
.cs-hunt__lead { color: var(--cs-dim); max-width: 56ch; margin-bottom: 1.8rem; }

/* The cycle to scale, as a strip, with the clock's position running along it. */
.cs-hunt__timeline {
  position: relative;
  display: flex;
  gap: 3px;
  height: 1.5rem;
  margin-bottom: 1rem;
}
.cs-hunt__seg {
  display: flex;
  align-items: center;
  padding: 0 0.6rem;
  border-radius: 2px;
  background: rgba(var(--ink-rgb), 0.07);
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cs-dim);
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
}
.cs-hunt__seg--warning { background: rgba(var(--forest-rgb), 0.45); padding: 0; min-width: 6px; }
.cs-hunt__seg--hunt { background: rgba(var(--forest-rgb), 0.22); color: var(--ink); }
.cs-hunt__playhead {
  position: absolute;
  top: -4px;
  bottom: -4px;
  left: 0;
  width: 2px;
  border-radius: 1px;
  background: var(--forest-glow);
  box-shadow: 0 0 10px rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.8);
  /* Moved by transform from the clock, every frame: no transition to lag it. */
  will-change: transform;
}

.cs-hunt__phases {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.6rem;
  margin-bottom: 1.6rem;
}
.cs-hunt__phase {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: 1rem 1.1rem;
  border-radius: 2px;
  border: 1px solid var(--cs-hairline);
  background: rgba(var(--ink-rgb), 0.02);
  transition: border-color 0.4s var(--ease-soft), background 0.4s var(--ease-soft);
}
.cs-hunt__phase.is-active {
  border-color: rgba(var(--forest-rgb), 0.7);
  background: rgba(var(--forest-rgb), 0.10);
}
.cs-hunt__phase-name {
  font-family: var(--font-mono);
  font-size: 0.88rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest);
}
.cs-hunt__phase-time { font-family: var(--font-mono); font-size: 0.78rem; color: var(--graphite); }
.cs-hunt__phase-desc { font-size: 0.97rem; line-height: 1.5; color: var(--ink); }

.cs-hunt__readout {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--cs-hairline);
}
.cs-hunt__status {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  font-family: var(--font-mono);
}
.cs-hunt__status-label {
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--graphite);
}
.cs-hunt__status-value {
  font-size: 1.15rem;
  letter-spacing: 0.04em;
  color: var(--forest);
}
.cs-hunt__status-timer {
  /* Tabular figures, or a counting timer jitters the label beside it. */
  font-variant-numeric: tabular-nums;
  font-size: 0.95rem;
  color: var(--cs-dim);
}
.cs-hunt__note {
  margin-top: 1.2rem;
  font-size: 0.85rem;
  color: var(--graphite);
}

/* ====================== 10b. THE HUB ====================== */
.cs-room__intro {
  max-width: 60ch;
  margin: 0 auto clamp(2rem, 5vh, 3rem);
  padding: 0 var(--gutter);
  color: var(--cs-dim);
  font-size: 1.05rem;
  text-align: center;
}

.cs-room__stage {
  position: relative;
  padding: 0 var(--gutter);
}
.cs-canvas--room { aspect-ratio: 16 / 10; }

.cs-room__status {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cs-dim);
  pointer-events: none;
  transition: opacity 0.6s var(--ease-soft);
}
.cs-room__status.is-done { opacity: 0; }

.cs-room__notes {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(1rem, 2.5vw, 2rem);
  margin-top: clamp(1.5rem, 4vh, 2.5rem);
  padding: 0 var(--gutter);
}
.cs-room__notes > div {
  padding-top: 1rem;
  border-top: 1px solid var(--cs-hairline);
}
.cs-room__notes dt {
  font-family: var(--font-mono);
  font-size: 0.74rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--forest);
  margin-bottom: 0.5rem;
}
.cs-room__notes dd { color: var(--cs-dim); font-size: 0.95rem; line-height: 1.55; }
.cs-room__notes code {
  font-family: var(--font-mono);
  font-size: 0.85em;
  color: var(--ink);
}

/* ====================== 11. UNDER THE HOOD ====================== */
.cs-build__intro {
  max-width: 58ch;
  margin: 0 auto clamp(2rem, 5vh, 3rem);
  padding: 0 var(--gutter);
  color: var(--cs-dim);
  text-align: center;
}

.cs-bom { padding: 0 var(--gutter); }
.cs-bom__row {
  display: grid;
  grid-template-columns: 3.5rem minmax(0, 0.9fr) minmax(0, 1.6fr);
  align-items: start;
  gap: clamp(1rem, 3vw, 2.5rem);
  padding: clamp(1.4rem, 3vh, 2rem) 0;
  border-top: 1px solid var(--cs-hairline);
}
.cs-bom__row:last-child { border-bottom: 1px solid var(--cs-hairline); }
.cs-bom__no {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--forest);
  padding-top: 0.35em;
}
.cs-bom__head { display: flex; flex-direction: column; gap: 0.3rem; }
.cs-bom__name {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 110;
  font-weight: 700;
  font-size: clamp(1.1rem, 2.2vw, 1.4rem);
  line-height: 1.15;
}
.cs-bom__role {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--graphite);
}
.cs-bom__desc { color: var(--cs-dim); font-size: 0.95rem; }

/* ====================== 12. CONTROLS ====================== */
.cs-controls__intro,
.cs-controls__note {
  max-width: 58ch;
  padding: 0 var(--gutter);
  color: var(--cs-dim);
}
.cs-controls__intro { margin-bottom: clamp(2rem, 5vh, 3rem); }
.cs-controls__note { margin-top: 2rem; font-size: 0.88rem; color: var(--graphite); }

.cs-keys {
  display: grid;
  grid-template-columns: auto minmax(0, 1.5fr) minmax(0, 1fr);
  align-items: start;
  gap: clamp(2rem, 5vw, 4rem);
  padding: 0 var(--gutter);
}

.cs-keys__cluster { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.cs-keys__row { display: flex; gap: 0.4rem; }
.cs-keys__caption {
  margin-top: 0.5rem;
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--forest);
}

.cs-key {
  display: grid;
  place-items: center;
  min-width: 2.9rem;
  height: 2.9rem;
  padding: 0 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.8rem;
  color: var(--ink);
  background: var(--paper-raise);
  border: 1px solid var(--cs-hairline);
  border-bottom-width: 3px;
  border-radius: 4px;
  transition: transform 0.06s linear, background 0.12s var(--ease-soft),
              border-color 0.12s var(--ease-soft), color 0.12s var(--ease-soft),
              box-shadow 0.12s var(--ease-soft);
}
.cs-key--wide { min-width: 4.6rem; }
/* Pressed: the cap travels the 2px its taller bottom border was standing on,
   so the key looks depressed rather than merely recoloured. */
.cs-key.is-down {
  transform: translateY(2px);
  border-bottom-width: 1px;
  margin-bottom: 2px;
  color: var(--forest-glow);
  border-color: rgba(var(--forest-rgb), 0.7);
  background: rgba(var(--forest-rgb), 0.12);
  box-shadow: 0 0 18px rgba(var(--forest-rgb), 0.25);
}

.cs-keys__list { display: grid; gap: 0.7rem; align-content: start; }
.cs-keys__list li {
  display: grid;
  grid-template-columns: 4.8rem minmax(0, 1fr);
  align-items: center;
  gap: 1rem;
}
.cs-keys__list span { color: var(--cs-dim); font-size: 0.93rem; }

/* The readout: a panel in the same raised-stone language as the hunt panel,
   so the section reads as one instrument rather than a list with a gap. */
.cs-readout {
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.4rem;
  min-height: 12rem;
  padding: 1.6rem 1.8rem;
  border: 1px solid rgba(var(--forest-rgb), 0.28);
  border-radius: 3px;
  background:
    radial-gradient(90% 80% at 50% 120%, rgba(var(--cs-ember-rgb, var(--forest-rgb)), 0.10), transparent 70%),
    var(--paper-raise);
}
.cs-readout__label {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--graphite);
}
.cs-readout__key {
  font-family: var(--font-display);
  font-variation-settings: "wdth" 125;
  font-weight: 800;
  font-size: clamp(2.2rem, 4vw, 3.2rem);
  line-height: 1;
  color: var(--forest);
}
.cs-readout__action { color: var(--ink); font-size: 1.02rem; }
/* A short flash on each new press, so repeating the same key still reads. */
.cs-readout.is-fresh .cs-readout__key { animation: cs-readout-flash 0.35s var(--ease-out); }
@keyframes cs-readout-flash {
  from { color: var(--forest-glow); text-shadow: 0 0 22px rgba(var(--forest-rgb), 0.6); }
  to   { text-shadow: 0 0 0 transparent; }
}

/* ====================== 13. GALLERY & DEMO ====================== */
.cs-gallery__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: clamp(0.8rem, 2vw, 1.4rem);
  padding: 0 var(--gutter);
}
.cs-shot { margin: 0; }
.cs-shot__frame {
  aspect-ratio: 4 / 3;
  border-radius: 3px;
  overflow: hidden;
  background: var(--paper-raise);
  display: grid;
  place-items: center;
}
.cs-shot__frame img { width: 100%; height: 100%; object-fit: cover; }

.cs-demo__frame {
  /* Capped so a 4:3 clip at full page width doesn't fill the whole screen. */
  width: calc(100% - 2 * var(--gutter));
  max-width: 60rem;
  margin: 0 auto;
  border-radius: 3px;
  overflow: hidden;
  background: var(--paper-raise);
  display: grid;
  place-items: center;
}
/* The clip is 4:3, so the frame takes the video's own shape instead of
   forcing 16:9 and cropping the top and bottom off. */
.cs-demo__frame video { display: block; width: 100%; height: auto; }
.cs-demo__frame.is-pending { aspect-ratio: 16 / 9; }
.cs-demo__caption {
  width: calc(100% - 2 * var(--gutter));
  max-width: 60rem;
  margin: 1rem auto 0;
  font-size: 0.85rem;
  color: var(--graphite);
}

/* Placeholders. Dashed, labelled and obviously unfinished, a smooth empty
   box would just look like a broken image. */
.cs-shot.is-pending .cs-shot__frame,
.cs-demo__frame.is-pending {
  border: 1px dashed var(--cs-hairline);
  background: rgba(var(--ink-rgb), 0.02);
}
.cs-shot__pending {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--graphite);
}

/* ====================== 14. FOOTER ====================== */
.cs-footer {
  margin-top: var(--section-gap);
  padding: 2.5rem var(--gutter) 3rem;
  border-top: 1px solid var(--cs-hairline);
  font-size: 0.88rem;
  color: var(--cs-dim);
}
.cs-footer a { color: var(--forest); }
.cs-footer a:hover { color: var(--forest-glow); text-decoration: underline; }
.cs-footer__credit { margin-top: 0.6rem; font-size: 0.78rem; color: var(--graphite); }

/* ====================== 15. REDUCED MOTION & RESPONSIVE ====================== */
@media (prefers-reduced-motion: reduce) {
  .cs-hunt,
  .cs-hunt__phase,
  .cs-switch,
  .cs-switch__track,
  .cs-switch__thumb,
  .cs-key { transition: none; }
  .cs-readout.is-fresh .cs-readout__key { animation: none; }
}

@media (max-width: 1024px) {
  .cs-room__notes { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .cs-keys { grid-template-columns: auto minmax(0, 1fr); }
  .cs-readout { grid-column: 1 / -1; min-height: 0; }
  .cs-fire__grid,
  .cs-ghosts__grid { grid-template-columns: minmax(0, 1fr); }
  .cs-canvas--torch { aspect-ratio: 16 / 11; }
  .cs-canvas--ghost { aspect-ratio: 4 / 3; max-height: 60vh; }
  .cs-bom__row { grid-template-columns: 2.5rem minmax(0, 1fr); }
  .cs-bom__desc { grid-column: 2; }
}

@media (max-width: 768px) {
  .cs-hero {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
    padding-top: 1rem;
  }
  /* The torches lead on a phone: a wall of type above the fold is the one
     thing a page about a dark castle should not open with. */
  .cs-hero__stage { order: -1; aspect-ratio: 5 / 4; max-height: 44vh; }
  .cs-hero__spec { grid-template-columns: minmax(0, 1fr); }

  /* Five panels across a phone is five slivers. Stack into a grid the legend
     still lines up with. */
  .cs-canvas--stages { aspect-ratio: 5 / 3; }
  .cs-stages__legend { grid-template-columns: repeat(2, minmax(0, 1fr)); }

  .cs-hunt__phases { grid-template-columns: minmax(0, 1fr); }
  .cs-canvas--room { aspect-ratio: 4 / 3; }
  .cs-room__notes { grid-template-columns: minmax(0, 1fr); }
  .cs-gallery__grid { grid-template-columns: minmax(0, 1fr); }

  .cs-keys { grid-template-columns: minmax(0, 1fr); justify-items: start; }
  .cs-keys__list li { grid-template-columns: 4.4rem minmax(0, 1fr); }
}
