/* ============================================================
   Infocom player — shared styles
   Themes: light (default), dark, green (CRT), amber (CRT)
   ============================================================ */

:root {
  --font-scale: 1;
  --serif: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, "Times New Roman", serif;
  --mono: "Cascadia Mono", "JetBrains Mono", Consolas, "SF Mono", Menlo, "DejaVu Sans Mono", monospace;
  --ui: -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

/* ---------- theme tokens ---------- */
html[data-theme="light"] {
  --bg: #f6f3ec;
  --bg-raised: #fffdf8;
  --text: #2b2620;
  --text-dim: #7a7266;
  --accent: var(--game-accent, #8b3a2e);
  --input-color: var(--accent);
  --bar-bg: #2b2620;
  --bar-text: #f6f3ec;
  --border: #e0d9cb;
  --shadow: 0 1px 3px rgba(43, 38, 32, .09), 0 8px 24px rgba(43, 38, 32, .06);
  --btn-hover: #ece6da;
  --sel: rgba(139, 58, 46, .18);
  --body-font: var(--serif);
  --body-size: 1.075rem;
  --body-lh: 1.62;
}
html[data-theme="dark"] {
  --bg: #191a1e;
  --bg-raised: #202127;
  --text: #d9d5cc;
  --text-dim: #83807a;
  --accent: var(--game-accent-dark, #d98d6a);
  --input-color: var(--accent);
  --bar-bg: #0e0f11;
  --bar-text: #d9d5cc;
  --border: #2d2e35;
  --shadow: 0 1px 3px rgba(0, 0, 0, .4), 0 8px 24px rgba(0, 0, 0, .3);
  --btn-hover: #2a2b32;
  --sel: rgba(217, 141, 106, .25);
  --body-font: var(--serif);
  --body-size: 1.075rem;
  --body-lh: 1.62;
}
html[data-theme="green"] {
  --bg: #0a0f0a;
  --bg-raised: #0d140d;
  --text: #33ff66;
  --text-dim: #1d8f3d;
  --accent: #baffcf;
  --input-color: #baffcf;
  --bar-bg: #33ff66;
  --bar-text: #0a0f0a;
  --border: #164a26;
  --shadow: none;
  --btn-hover: #10240f;
  --sel: rgba(51, 255, 102, .25);
  --body-font: var(--mono);
  --body-size: 1rem;
  --body-lh: 1.5;
}
html[data-theme="amber"] {
  --bg: #100c04;
  --bg-raised: #171106;
  --text: #ffb000;
  --text-dim: #8f6300;
  --accent: #ffd980;
  --input-color: #ffd980;
  --bar-bg: #ffb000;
  --bar-text: #100c04;
  --border: #4a3a10;
  --shadow: none;
  --btn-hover: #241a08;
  --sel: rgba(255, 176, 0, .25);
  --body-font: var(--mono);
  --body-size: 1rem;
  --body-lh: 1.5;
}

/* CRT effects for green/amber */
html[data-theme="green"] .crt-fx, html[data-theme="amber"] .crt-fx { display: block; }
html[data-theme="green"] #buffer, html[data-theme="amber"] #buffer,
html[data-theme="green"] #statusline, html[data-theme="amber"] #statusline {
  text-shadow: 0 0 6px currentColor;
}

/* ---------- chunky terminal chrome (CRT themes only) ----------
   Heavy phosphor borders and square corners, to feel like a boxed-in
   1980s console rather than a soft modern UI. */
html[data-theme="green"], html[data-theme="amber"] { --crt-frame: var(--text-dim); }

/* framed sections */
html[data-theme="green"] header, html[data-theme="amber"] header { border-bottom: 3px solid var(--crt-frame); }
html[data-theme="green"] #statusline, html[data-theme="amber"] #statusline {
  border-top: 2px solid var(--bar-bg); border-bottom: 3px solid var(--crt-frame);
}
html[data-theme="green"] #assist, html[data-theme="amber"] #assist { border-top: 3px solid var(--crt-frame); }
html[data-theme="green"] #scroll, html[data-theme="amber"] #scroll {
  border-left: 2px solid var(--crt-frame); border-right: 2px solid var(--crt-frame);
}

/* boxy, thick-bordered controls */
html[data-theme="green"] .toolbar button, html[data-theme="amber"] .toolbar button,
html[data-theme="green"] .toolbar select, html[data-theme="amber"] .toolbar select,
html[data-theme="green"] .chip, html[data-theme="amber"] .chip,
html[data-theme="green"] #keyhint .kh-chip, html[data-theme="amber"] #keyhint .kh-chip,
html[data-theme="green"] #keyhint .kh-continue, html[data-theme="amber"] #keyhint .kh-continue,
html[data-theme="green"] .verbmenu button, html[data-theme="amber"] .verbmenu button,
html[data-theme="green"] .modal button.act, html[data-theme="amber"] .modal button.act {
  border-width: 2px; border-style: solid; border-color: var(--crt-frame); border-radius: 2px;
}

/* framed floating panels */
html[data-theme="green"] #keyhint, html[data-theme="amber"] #keyhint,
html[data-theme="green"] .verbmenu, html[data-theme="amber"] .verbmenu,
html[data-theme="green"] .modal, html[data-theme="amber"] .modal {
  border-width: 3px; border-style: solid; border-color: var(--crt-frame); border-radius: 4px;
}

/* selected verb chip / continue button keep the bright phosphor fill */
html[data-theme="green"] .chip.verb.sel, html[data-theme="amber"] .chip.verb.sel,
html[data-theme="green"] #assistbtn.active, html[data-theme="amber"] #assistbtn.active {
  border-color: var(--accent);
}
html[data-theme="green"] .carrygroup, html[data-theme="amber"] .carrygroup {
  border-width: 2px; border-color: var(--crt-frame); border-radius: 2px;
}

* { box-sizing: border-box; }
html, body { height: 100%; margin: 0; }

body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--ui);
  display: flex;
  flex-direction: column;
  transition: background .25s, color .25s;
}

::selection { background: var(--sel); }

.crt-fx {
  display: none;
  pointer-events: none;
  position: fixed; inset: 0; z-index: 50;
}
/* scanlines */
.crt-fx::before {
  content: ""; position: absolute; inset: 0;
  background: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0) 0px, rgba(0, 0, 0, 0) 2px,
    rgba(0, 0, 0, .16) 3px, rgba(0, 0, 0, 0) 4px
  );
  mix-blend-mode: multiply;
}
/* curved-glass tube: light falls off toward the edges and a faint glare sits
   near the top — reads as a bulging CRT without geometrically warping the live
   text. Square corners (no border-radius) so no rounded edge shows on mobile. */
.crt-fx::after {
  content: ""; position: absolute; inset: 0;
  background:
    radial-gradient(125% 125% at 50% 50%,
      rgba(0,0,0,0) 58%, rgba(0,0,0,.22) 84%, rgba(0,0,0,.5) 100%),
    radial-gradient(80% 55% at 50% 6%,
      rgba(255,255,255,.045) 0%, rgba(255,255,255,0) 62%);
  box-shadow:
    inset 0 0 24px 7px rgba(0,0,0,.5),
    inset 0 0 3px 1px rgba(0,0,0,.65);
}

/* ---------- top bar ---------- */
header {
  flex: none;
  display: flex; align-items: center; gap: .6rem;
  padding: .5rem .9rem;
  background: var(--bg-raised);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
  z-index: 20;
}
header .home-link {
  color: var(--text-dim); text-decoration: none;
  font-size: .85rem; padding: .3rem .5rem; border-radius: 6px;
  white-space: nowrap;
}
header .home-link:hover { background: var(--btn-hover); color: var(--text); }
header h1 {
  font-family: var(--serif);
  font-size: 1.02rem; font-weight: 600; letter-spacing: .02em;
  margin: 0; flex: 1 1 auto;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
header h1 .year { color: var(--text-dim); font-weight: 400; font-size: .82rem; margin-left: .5em; }
.toolbar { display: flex; gap: .25rem; flex: none; }
.toolbar button, .toolbar select {
  font-family: var(--ui); font-size: .82rem;
  background: transparent; color: var(--text);
  border: 1px solid transparent; border-radius: 7px;
  padding: .34rem .55rem; cursor: pointer;
}
.toolbar button {
  display: inline-flex; align-items: center; gap: .32rem;
}
.toolbar button svg {
  width: 17px; height: 17px; flex: none;
  fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
@media (max-width: 760px) {
  .toolbar .blbl { display: none; } /* icons + tooltips carry it on phones */
}
.toolbar button:hover, .toolbar select:hover { background: var(--btn-hover); }
.toolbar select { border-color: var(--border); background: var(--bg-raised); }

/* ---------- status line ---------- */
#statusline {
  flex: none;
  font-family: var(--mono);
  font-size: .85rem;
  background: var(--bar-bg);
  color: var(--bar-text);
  padding: .32rem 1.1rem;
  min-height: 1.7em;
  z-index: 10;
}
#statusline .sl-row { display: flex; justify-content: space-between; gap: 2ch; }
#statusline .sl-left { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; }
#statusline .sl-right { white-space: nowrap; color: color-mix(in srgb, var(--bar-text) 82%, transparent); }
#statusline pre { margin: 0; font: inherit; white-space: pre; overflow: hidden; }
/* the game lays a tall upper window out for a fixed column width; center that
   block so it doesn't hug the left of a wide window */
#statusline .gridwrap { width: max-content; max-width: 100%; margin: 0 auto; }
#statusline .gridwrap pre { overflow: visible; }

/* A tall interactive form takes over the main view (rather than sitting in the
   status strip with an empty window below), styled like the screen — dark with
   glowing phosphor text — so it reads as the game, not a status bar. */
body.bigform #statusline {
  flex: 1 1 auto; max-height: none; min-height: 0;
  display: flex; flex-direction: column; justify-content: center; align-items: center;
  background: var(--bg); color: var(--text);
  border: none;
  padding-left: 1.2rem; padding-right: 1.2rem;   /* drop the narrow reading-measure padding so the form uses the width */
}
body.bigform #statusline .gridwrap { margin: auto; font-size: calc(var(--body-size) * var(--font-scale) * 1.32); }
body.bigform > #scroll { display: none; }
html[data-theme="green"] body.bigform #statusline .gridcur,
html[data-theme="amber"] body.bigform #statusline .gridcur { background: var(--text); color: var(--bg); }
#statusline { max-height: 48vh; overflow-y: auto; }
#statusline .gridcur {
  background: var(--bar-text); color: var(--bar-bg);
  animation: curblink 1.1s steps(1) infinite;
}
@keyframes curblink { 50% { opacity: .35; } }

/* ---------- main scroll area ---------- */
#scroll {
  flex: 1 1 auto;
  overflow-y: auto;
  /* column flex + margin-top:auto on #buffer keeps a short exchange pinned
     to the bottom (just above the prompt) instead of floating mid-screen */
  display: flex;
  flex-direction: column;
}
#boxart-wrap { text-align: center; padding: 2rem 1rem 0; }
#boxart-wrap img {
  max-width: min(240px, 60%);
  border-radius: 5px;
  box-shadow: 0 4px 14px rgba(0,0,0,.35), 0 14px 40px rgba(0,0,0,.25);
}
html[data-theme="green"] #boxart-wrap img, html[data-theme="amber"] #boxart-wrap img {
  filter: grayscale(.35) brightness(.85);
  opacity: .9;
}
#buffer {
  max-width: 44em;
  width: 100%;
  margin: auto auto 0;          /* top:auto pushes short content to the bottom */
  padding: 1.6rem 1.4rem 1.6rem;
  font-family: var(--body-font);
  font-size: calc(var(--body-size) * var(--font-scale));
  line-height: var(--body-lh);
}
#buffer .para { margin: 0; min-height: 1em; white-space: pre-wrap; overflow-wrap: break-word; }

/* text styles from the Glk layer */
.s-emphasized { font-style: italic; }
.s-header { font-weight: 700; }
.s-subheader { font-weight: 600; }
.s-alert { font-weight: 600; color: var(--accent); }
.s-note { font-style: italic; color: var(--text-dim); }
.s-preformatted { font-family: var(--mono); font-size: .92em; }
.s-input { color: var(--input-color); font-weight: 600; }
.s-blockquote { color: var(--text-dim); }
.rev { background: var(--text); color: var(--bg); }

/* ---------- inline input ---------- */
#inputwrap { display: inline; }
#cmd {
  font: inherit; color: var(--input-color); font-weight: 600;
  background: transparent; border: none; outline: none;
  padding: 0; margin: 0;
  width: 24ch; max-width: 60vw;
  caret-color: var(--accent);
}
#cmd::placeholder { color: var(--text-dim); opacity: .7; font-weight: 400; }
/* blinking block caret at the prompt when idle, so it's clear the game awaits input */
#inputwrap .caret {
  display: none; width: .55ch; height: 1.05em; margin-right: 1px;
  background: var(--accent); vertical-align: -0.15em; border-radius: 1px;
}
#inputwrap.empty:not(.focused) .caret { display: inline-block; animation: caretblink 1.1s steps(1) infinite; }
@keyframes caretblink { 50% { opacity: 0; } }
body.typing #inputwrap .caret { display: none; }
html[data-theme="green"] #cmd, html[data-theme="amber"] #cmd { text-shadow: 0 0 6px currentColor; }

/* while text is revealing, the prompt and key button stay out of sight.
   opacity (not visibility/display) keeps the input focusable so the
   mobile keyboard intent and focus survive the reveal. */
body.typing #inputwrap { opacity: 0; pointer-events: none; }
body.typing #keyhint { display: none !important; }

#keyhint {
  display: none;
  position: fixed; left: 50%; bottom: 1.4rem; transform: translateX(-50%);
  max-width: min(92vw, 40rem);
  background: var(--bg-raised);
  border: 1px solid var(--border); border-radius: 999px;
  box-shadow: var(--shadow);
  padding: .4rem .55rem;
  z-index: 30;
  align-items: center; gap: .4rem; flex-wrap: wrap; justify-content: center;
}
#keyhint .kh-lbl { color: var(--text-dim); font-size: .8rem; padding-left: .3rem; }
#keyhint .kh-continue {
  font-family: var(--ui); font-size: .85rem; font-weight: 600;
  color: var(--bg-raised); background: var(--accent);
  border: none; border-radius: 999px; cursor: pointer;
  padding: .5rem 1.1rem;
  animation: pulse 1.8s ease-in-out infinite;
}
#keyhint .kh-chip {
  font-family: var(--ui); font-size: .9rem; font-weight: 600;
  color: var(--text); background: var(--bg);
  border: 1px solid var(--accent); border-radius: 999px; cursor: pointer;
  min-width: 2.4rem; padding: .5rem .8rem;
}
#keyhint .kh-chip:hover { background: var(--accent); color: var(--bg-raised); }
#keyhint .kh-chip.kh-spec { min-width: 2rem; padding: .5rem .55rem; }
#keyhint .kh-type {
  font-size: 1rem; line-height: 1;
  color: var(--text-dim); background: transparent;
  border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
  padding: .35rem .5rem;
}
@media (pointer: coarse) {
  #keyhint .kh-continue, #keyhint .kh-chip { min-height: 44px; }
}
@keyframes pulse { 50% { opacity: .6; } }

/* ---------- assist mode (verb → target) ---------- */
#assist {
  display: none;
  flex: none;
  background: var(--bg-raised);
  border-top: 1px solid var(--border);
  padding: .15rem 0 .3rem;
  z-index: 25;
}
/* The compass sits to one side of the verb/noun rows (rather than below them)
   so the whole panel stays short. Grid: verbs+nouns stack in the left column,
   the compass block spans both on the right. */
body.assist-on #assist {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "hint hint" "verbs dirs" "nouns dirs";
  align-items: center;
  column-gap: .5rem;
  max-width: 900px; margin: 0 auto; padding-left: .3rem; padding-right: .3rem;
  position: relative; /* anchor for the pop-up compass on touch */
}
#as-hint { grid-area: hint; align-self: start; }
#as-verbs { grid-area: verbs; align-self: center; }
#as-nouns { grid-area: nouns; align-self: center; }
#as-dirs  { grid-area: dirs; justify-self: end; }
#as-hint {
  display: none;
  font-family: var(--ui); font-size: .75rem;
  color: var(--text-dim);
  padding: .3rem .75rem 0;
}
.chiprow {
  display: flex; gap: .35rem;
  overflow-x: auto; scrollbar-width: none;
  padding: .28rem .6rem 0;
  /* edge fade signals there's more to scroll */
  -webkit-mask-image: linear-gradient(to right, black calc(100% - 28px), transparent);
  mask-image: linear-gradient(to right, black calc(100% - 28px), transparent);
}
/* verbs: two compact rows that scroll sideways together — every verb
   reachable, panel height fixed */
#as-verbs {
  display: grid;
  grid-auto-flow: column;
  grid-template-rows: repeat(2, auto);
  justify-content: start;
}
.chiprow::-webkit-scrollbar { display: none; }
.chip {
  flex: none;
  font-family: var(--ui); font-size: .8rem;
  padding: .34rem .7rem;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg); color: var(--text);
  cursor: pointer;
  white-space: nowrap;
}
.chip:disabled { opacity: .4; cursor: default; }
.chip.verb.sel {
  background: var(--accent); border-color: var(--accent);
  color: var(--bg-raised); font-weight: 600;
}
.chip.noun { border-style: dashed; }
.chip.verb.toggle { background: transparent; color: var(--text-dim); border-style: dashed; font-style: italic; }
.chip.verb.toggle:hover { color: var(--text); border-color: var(--text-dim); }
.chip { display: inline-flex; align-items: center; }

/* carried items: one dotted group, one icon, then the items */
.carrygroup {
  flex: none;
  display: inline-flex; align-items: center; gap: .35rem;
  padding: .22rem .45rem;
  border: 1.5px dotted var(--text-dim); border-radius: 9px;
}
.carrygroup .cg-icon {
  width: 17px; height: 17px; flex: none;
  fill: none; stroke: currentColor; color: var(--text-dim);
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}
.carrygroup .chip.in-bag { border-style: solid; } /* the group's dotted edge says "carried" */
@media (pointer: coarse) { .carrygroup { gap: .45rem; padding: .3rem .5rem; } }
.chip.dir { font-family: var(--mono); font-size: .74rem; padding: .3rem .6rem; }
/* the little pointers on ►IN◄ and ◄OUT► — quieter than the letters */
.chip.dir .da { font-style: normal; font-size: .72em; opacity: .7; padding: 0 1px; vertical-align: 6%; }
/* every direction is fully tappable; a confirmed exit (travelled or named in
   the room text) just gets an accent tint so the eye can find it */
.chip.dir.lit { border-color: var(--accent); color: var(--accent); }
.chip-placeholder {
  font-family: var(--ui); font-size: .75rem; font-style: italic;
  color: var(--text-dim); padding: .4rem .2rem; white-space: nowrap;
}
#assistbtn.active, #assistbtn.active:hover {
  background: var(--accent); color: var(--bg-raised); border-radius: 7px;
}

/* comfortable thumbs: platform-minimum touch targets on coarse pointers */
@media (pointer: coarse) {
  .chip { min-height: 44px; padding: .55rem .95rem; font-size: .92rem; }
  .chip.dir { min-width: 44px; font-size: .85rem; }
  .chiprow { gap: .45rem; padding-top: .4rem; }
  .chip-placeholder { min-height: 44px; display: inline-flex; align-items: center; }
}

/* ---------- compass rose beside the commands ----------
   One 4x3 grid: the 3x3 rose (IN at its centre) plus an outer up/out/down
   column that lines up with the rose's three rows. Sits at the panel edge so
   it adds width, not height. */
#as-dirs {
  display: flex; align-items: center; justify-content: center; overflow: visible;
  -webkit-mask-image: none; mask-image: none;   /* not a scroll row */
  padding: .2rem 0;
}
.compass-grid { display: grid; grid-template-columns: repeat(4, auto); gap: 4px; }
.compass-grid .chip.dir {
  width: 42px; height: 38px; min-width: 0; min-height: 0; margin: 0; padding: 0;
  font-size: .72rem;
  display: flex; align-items: center; justify-content: center;
}
.compass-grid .chip.dir.ext { margin-left: 9px; } /* set the outer column apart from the rose */

/* compass toggle button — hidden on desktop (compass is inline there); on
   touch it floats over the top-left of the text (positioned by JS) */
#dirtoggle {
  display: none;
  align-items: center; justify-content: center; gap: 5px;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  padding: .4rem .7rem; cursor: pointer;
  font-family: var(--ui); font-size: .68rem; font-weight: 600; letter-spacing: .02em;
}
#dirtoggle svg { width: 24px; height: 24px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
#dirtoggle.active { background: var(--accent); color: var(--bg-raised); border-color: var(--accent); }

@media (pointer: coarse) {
  .compass-grid .chip.dir { width: 44px; height: 40px; }
  /* reserve a slim band under the status line for the floating Move + menu
     pills so they never sit on top of the transcript (positioned there by JS) */
  #scroll { margin-top: 52px; }
  /* On phones both action rows now run the full width — the Move button no
     longer sits in the panel; it floats over the top-left of the text. */
  body.assist-on #assist { grid-template-areas: "hint hint" "verbs verbs" "nouns nouns"; }
  #as-verbs, #as-nouns { justify-self: stretch; }
  /* the floating Move button — a compact, translucent pill over the text,
     positioned top-left by JS; tapping it pops the rose up just beneath it */
  #dirtoggle {
    display: inline-flex;
    position: fixed; z-index: 30; top: 0; left: 0;
    min-width: 0; padding: .34rem .55rem; gap: 5px;
    background: color-mix(in srgb, var(--bg-raised) 82%, transparent);
    -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
    box-shadow: var(--shadow); border-radius: 11px; font-size: .64rem;
  }
  #dirtoggle svg { width: 20px; height: 20px; }
  /* the compass pops up from the button (JS sets top/left), not the panel */
  #as-dirs {
    position: fixed; top: 0; left: 0;
    display: none;
    background: var(--bg-raised); border: 1px solid var(--border);
    border-radius: 14px; padding: .55rem; box-shadow: var(--shadow); z-index: 40;
  }
  body.dirs-open #as-dirs { display: flex; }
  html[data-theme="green"] #as-dirs, html[data-theme="amber"] #as-dirs { border: 2px solid var(--crt-frame); border-radius: 4px; }
}
/* desktop has room: the command panel spans the whole screen width, with
   bigger buttons and a bigger rose */
@media (pointer: fine) and (min-width: 760px) {
  body.assist-on #assist { max-width: none; margin: 0; padding-left: 1.5rem; padding-right: 1.5rem; column-gap: 1.5rem; }
  .chip { font-size: .92rem; padding: .5rem 1rem; }
  .compass-grid { gap: 6px; }
  .compass-grid .chip.dir { width: 56px; height: 46px; font-size: .85rem; }
  .compass-grid .chip.dir.ext { margin-left: 12px; }
}
body.targeting #buffer { cursor: crosshair; }
body.targeting #buffer .para:hover { background: color-mix(in srgb, var(--accent) 6%, transparent); }

/* interactable object words — plain text unless assist is on */
#buffer b.obj { font-weight: inherit; }
body.assist-on #buffer b.obj {
  font-weight: 700;
  cursor: pointer;
  text-decoration: underline dotted color-mix(in srgb, var(--accent) 55%, transparent);
  text-underline-offset: 3px;
}
body.assist-on #buffer b.obj:hover { color: var(--accent); }
html[data-theme="green"] .chip, html[data-theme="amber"] .chip { text-shadow: 0 0 5px currentColor; }

/* ---------- verb menu (object action popover) ---------- */
.vm-back { position: fixed; inset: 0; z-index: 119; background: rgba(0, 0, 0, .32); }
.verbmenu {
  position: fixed; z-index: 120;
  background: var(--bg-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow);
  padding: .55rem;
  max-width: min(90vw, 21rem);
}
.verbmenu .vm-title {
  font-family: var(--serif); font-weight: 700; font-size: .92rem;
  padding: .1rem .35rem .45rem; color: var(--text);
  border-bottom: 1px solid var(--border); margin-bottom: .45rem;
  text-transform: capitalize;
}
.verbmenu .vm-grid { display: flex; flex-wrap: wrap; gap: .35rem; }
.verbmenu button {
  font-family: var(--ui); font-size: .85rem;
  padding: .4rem .75rem; border-radius: 999px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
  cursor: pointer; white-space: nowrap;
}
.verbmenu button:hover { background: var(--accent); border-color: var(--accent); color: var(--bg-raised); }
@media (pointer: coarse) { .verbmenu button { min-height: 42px; padding: .5rem .9rem; } }
html[data-theme="green"] .verbmenu button, html[data-theme="amber"] .verbmenu button { text-shadow: 0 0 5px currentColor; }
/* Cabinet mode: the verb menu should feel drawn on the CRT itself, not float
   over a dimmed page. No backdrop darkening; phosphor font, colour and glow to
   match the on-screen text. */
body.cabinet .vm-back { background: transparent; }
body.cabinet .verbmenu {
  font-family: var(--mono);
  font-size: calc(var(--body-size) * var(--font-scale) * 1.32);   /* same size as the CRT text */
  letter-spacing: .02em;
  max-width: min(92vw, 32rem);
  background: color-mix(in srgb, var(--text) 6%, rgba(6,7,5,.94));
  border-color: color-mix(in srgb, var(--text) 55%, transparent);
  box-shadow: 0 0 20px color-mix(in srgb, var(--text) 22%, transparent), inset 0 0 22px rgba(0,0,0,.75);
  color: var(--text);
}
body.cabinet .verbmenu .vm-title {
  font-family: var(--mono); text-transform: none; font-size: 1em; line-height: 1.4;
  border-bottom-color: color-mix(in srgb, var(--text) 28%, transparent);
  text-shadow: 0 0 1px currentColor, 0 0 7px color-mix(in srgb, currentColor 60%, transparent), 0 0 15px color-mix(in srgb, currentColor 38%, transparent);
}
body.cabinet .verbmenu button {
  font-family: var(--mono); font-size: 1em; letter-spacing: .02em;
  background: rgba(0,0,0,.32);
  border-color: color-mix(in srgb, var(--text) 42%, transparent);
  text-shadow: 0 0 1px currentColor, 0 0 7px color-mix(in srgb, currentColor 60%, transparent), 0 0 15px color-mix(in srgb, currentColor 38%, transparent);
}
body.cabinet .verbmenu button:hover { background: var(--accent); color: var(--bg); border-color: var(--accent); text-shadow: none; }
/* rendered inside #cab-slot: absolute so the tube's glass overlays, scanlines
   and brightness fall over it and it is clipped to the screen */
body.cabinet .verbmenu.vm-incab { position: absolute; }
/* above the in-screen status line (z 10) so it catches outside clicks, below
   the menu itself */
body.cabinet .vm-back.vm-back-incab { position: absolute; z-index: 11; }
body.cabinet .verbmenu.vm-incab { z-index: 12; }

/* ---------- boot splash ---------- */
#bootsplash {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  background: var(--bg);
  transition: opacity .45s ease;
}
#bootsplash.gone { opacity: 0; pointer-events: none; }
#bootsplash .bs-inner { display: flex; flex-direction: column; align-items: center; gap: 1.15rem; padding: 1rem; }
#bootsplash .bs-spinner {
  width: 46px; height: 46px; border-radius: 50%;
  border: 3px solid color-mix(in srgb, var(--game-accent, var(--accent)) 22%, transparent);
  border-top-color: var(--game-accent, var(--accent));
  animation: bs-spin .8s linear infinite;
}
#bootsplash .bs-title {
  font-family: var(--serif); font-size: 1rem; color: var(--text);
  opacity: .82; letter-spacing: .01em; text-align: center;
}
@keyframes bs-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { #bootsplash .bs-spinner { animation-duration: 1.8s; } }
html[data-theme="green"] #bootsplash .bs-title,
html[data-theme="amber"] #bootsplash .bs-title { text-shadow: 0 0 6px currentColor; }

/* ---------- modals ---------- */
.modal-back {
  position: fixed; inset: 0; z-index: 130;
  background: rgba(0, 0, 0, .45);
  display: none; align-items: center; justify-content: center;
  padding: 1rem;
}
.modal-back.open { display: flex; }
.modal {
  background: var(--bg-raised); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  box-shadow: var(--shadow);
  width: 100%; max-width: 30rem;
  max-height: 80vh; overflow: auto;
  padding: 1.1rem 1.2rem;
  font-size: .92rem;
}
.modal h2 { font-family: var(--serif); font-size: 1.05rem; margin: 0 0 .8rem; }
.modal .slot {
  display: flex; align-items: center; gap: .6rem;
  padding: .5rem .6rem; border: 1px solid var(--border); border-radius: 8px;
  margin-bottom: .45rem; cursor: pointer;
}
.modal .slot:hover { background: var(--btn-hover); }
.modal .slot .nm { flex: 1 1 auto; font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.modal .slot .dt { color: var(--text-dim); font-size: .8rem; white-space: nowrap; }
.modal .slot .del {
  background: none; border: none; color: var(--text-dim);
  cursor: pointer; font-size: .95rem; padding: .15rem .3rem; border-radius: 5px;
}
.modal .slot .del:hover { color: var(--accent); background: var(--btn-hover); }
.modal .newrow { display: flex; gap: .5rem; margin-top: .75rem; }
.modal .newrow input {
  flex: 1; font: inherit; padding: .45rem .6rem;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px; outline: none;
}
.modal .newrow input:focus { border-color: var(--accent); }
.modal .btnrow { display: flex; justify-content: flex-end; gap: .5rem; margin-top: .9rem; }
.modal button.act {
  font: inherit; padding: .45rem .9rem; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border); background: var(--bg); color: var(--text);
}
.modal button.act.primary { background: var(--accent); border-color: var(--accent); color: var(--bg-raised); font-weight: 600; }
.modal button.act:hover { filter: brightness(1.08); }
.modal .empty { color: var(--text-dim); font-style: italic; padding: .4rem 0 .2rem; }
.modal .help-cols { columns: 2; gap: 1.5rem; font-size: .88rem; line-height: 1.75; }
.modal .help-cols b { font-family: var(--mono); font-weight: 600; color: var(--accent); }
.modal .optrow { display: flex; align-items: flex-start; gap: .5rem; margin-top: .9rem; font-size: .85rem; color: var(--text); cursor: pointer; }
.modal .optrow input { margin-top: .2rem; flex: none; }
.modal p.note { color: var(--text-dim); font-size: .82rem; }

/* ---------- auto-map ---------- */
/* Fill most of the screen and scroll in ONE place (the map pane), so the
   dialog itself never grows its own scrollbar alongside the map's. */
.modal.mapmodal {
  max-width: min(68rem, 94vw);
  width: 94vw;
  height: 88vh;
  max-height: 88vh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}
.modal.mapmodal h2,
.modal.mapmodal .empty,
.modal.mapmodal .note,
.modal.mapmodal .btnrow { flex: none; }
#mapwrap {
  overflow: auto;
  flex: 1 1 auto;
  min-height: 0;            /* allow the pane to shrink inside the flex column */
  margin-bottom: .3rem;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--bg);
  cursor: grab;            /* drag to pan — no scrollbar */
  touch-action: none;      /* finger drag pans instead of native scroll */
  scrollbar-width: none;   /* Firefox: hide the bar */
}
#mapwrap.grabbing { cursor: grabbing; }
#mapwrap::-webkit-scrollbar { width: 0; height: 0; display: none; }
/* plain block, natural size — a small map is centred with JS margins in
   renderMap(); a large one overflows and is dragged. (Flex would shrink it.) */
.mapsvg { display: block; }
/* ---------- scrollbars ----------
   Every pane that scrolls wears the current palette, so Paper never shows a
   grey OS bar over cream and the CRT themes never show a bright modern one
   over black. Browsers take two routes here and we serve both: Chrome and
   Firefox honour the standard scrollbar-color/width (and ignore the -webkit-
   pseudo-elements once those are set), while Safari only understands the
   pseudo-elements — where we can also round the thumb off. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--text-dim) transparent;             /* plain fallback */
  /* halfway between the hairline border and the dim text: visible enough to
     aim at on cream, still quiet on the phosphor themes */
  scrollbar-color: color-mix(in srgb, var(--text-dim) 55%, var(--border)) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: color-mix(in srgb, var(--text-dim) 55%, var(--border));
  border-radius: 8px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--text-dim); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }
/* the terminal themes are square-cornered everywhere else, so are their bars */
html[data-theme="green"] ::-webkit-scrollbar-thumb,
html[data-theme="amber"] ::-webkit-scrollbar-thumb { border-radius: 0; }
/* the feelie table is its own dark room whatever the page theme is */
#feeliemodal, #feeliemodal * { scrollbar-color: #3a352b transparent; }
#feeliemodal ::-webkit-scrollbar-thumb { background: #3a352b; }

/* ---------- feelies viewer ---------- */
#feeliemodal { padding: 0; background: #0c0b09; }
/* Feelies take over the whole viewport for an immersive, on-the-table feel:
   the stage runs edge to edge, a floating close button sits top-right, and the
   caption floats along the bottom. */
.feelie-shell {
  position: relative;
  background: #0c0b09; color: var(--text);
  width: 100vw; height: 100vh; height: 100dvh;
  overflow: hidden;
}
.feelie-close {
  position: absolute; top: max(14px, env(safe-area-inset-top, 0px)); right: 16px; z-index: 3;
  width: 46px; height: 46px; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: 1px solid rgba(255,255,255,.18);
  background: rgba(20,18,14,.55); color: #f3efe6;
  -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
  box-shadow: 0 2px 12px rgba(0,0,0,.5);
}
.feelie-close svg { width: 22px; height: 22px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; }
.feelie-close:hover { background: rgba(40,36,28,.7); border-color: rgba(255,255,255,.32); }
#feeliestage {
  position: absolute; inset: 0;
  overflow: hidden;
  background: radial-gradient(130% 130% at 50% 38%, #150f08 0%, #070605 100%);
  cursor: grab; touch-action: none;
}
/* the desk: a huge walnut slab lying on the table layer, so it pans and zooms
   with the feelies — grain, and board seams running WITH the grain */
.fe-desk {
  position: absolute; left: -12000px; top: -12000px; width: 24000px; height: 24000px;
  pointer-events: none;
  /* the photographed desk sits on the table layer (pans and zooms with the
     objects), its edges feathered into the darker surrounding wood; the props
     band lives just above where the feelies spawn */
  background:
    radial-gradient(ellipse at 50% 50%, rgba(43,31,19,0) 55%, rgba(43,31,19,.96) 96%) 11500px 11250px / 3200px 2240px no-repeat,
    url("desk.webp") 11500px 11250px / 3200px 2240px no-repeat,
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='300' height='120'><filter id='g'><feTurbulence type='fractalNoise' baseFrequency='0.006 0.11' numOctaves='3' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='linear' slope='0.05'/></feComponentTransfer></filter><rect width='300' height='120' filter='url(%23g)'/></svg>") repeat,
    repeating-linear-gradient(180deg, rgba(0,0,0,.2) 0 1px, rgba(255,235,200,.03) 1px 2px, rgba(0,0,0,0) 2px 170px),
    linear-gradient(168deg, #33261a 0%, #2b1f13 48%, #221812 100%);
}
/* the lamplight pool and edge vignette are LIGHT, not furniture: they stay
   screen-fixed above the table, gently shading whatever slides beneath them */
#feeliestage::after {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 60;
  background:
    radial-gradient(120% 85% at 50% 28%, rgba(255,209,143,.12) 0%, rgba(255,190,120,.04) 34%, rgba(0,0,0,0) 58%),
    radial-gradient(145% 120% at 50% 45%, rgba(0,0,0,0) 45%, rgba(0,0,0,.58) 100%);
}
#feeliestage.grabbing { cursor: grabbing; }
#feelietable { position: absolute; top: 0; left: 0; transform-origin: 0 0; will-change: transform; }
/* Make every gesture inside the stage go to our JS / the booklet, never the
   browser: no native scroll/zoom hijack on touch, no image-drag or text
   selection on desktop (these are what made dragging hit-and-miss). */
#feeliestage, #feeliestage * { touch-action: none; -webkit-user-select: none; user-select: none; }
#feeliestage img { -webkit-user-drag: none; -webkit-touch-callout: none; }
.feelie {
  position: absolute; cursor: grab;
  padding: 8px; border-radius: 3px;
  transform: rotate(var(--rot, 0deg));
  perspective: 1600px;
}
.feelie.grabbing { cursor: grabbing; }
.feelie-inner {
  position: relative; width: 100%;
  transform-style: preserve-3d;
  transition: transform .6s cubic-bezier(.2, .7, .2, 1);
  border-radius: 3px;
}
.feelie.flipped .feelie-inner { transform: rotateY(180deg); }
.feelie-face {
  background: #fbf7ee; border-radius: 3px;
  box-shadow: 0 2px 6px rgba(0,0,0,.35), 0 14px 34px rgba(0,0,0,.32);
  backface-visibility: hidden; -webkit-backface-visibility: hidden;
}
.feelie.grabbing .feelie-face { box-shadow: 0 6px 16px rgba(0,0,0,.4), 0 26px 60px rgba(0,0,0,.4); }
.feelie-face.front { position: relative; display: block; }
.feelie-face.back { position: absolute; inset: 0; transform: rotateY(180deg); display: flex; }
.feelie-face img { display: block; width: 100%; height: auto; border-radius: 1px; user-select: none; -webkit-user-drag: none; }
.feelie-face.back img { height: 100%; object-fit: contain; }
/* a transparent PNG (a cut-out object) shows with no paper card behind it —
   just the image and a shadow that follows its shape */
.feelie.fe-cutout { padding: 0; }
.feelie.fe-cutout .feelie-face { background: transparent; box-shadow: none; border-radius: 0; }
.feelie.fe-cutout .feelie-face img { border-radius: 0; filter: drop-shadow(0 3px 5px rgba(0,0,0,.45)); }
.feelie.fe-cutout.grabbing .feelie-face { box-shadow: none; }
.feelie.fe-cutout.grabbing .feelie-face img { filter: drop-shadow(0 8px 14px rgba(0,0,0,.5)); }
.feelie .fe-flip {
  position: absolute; right: 2px; bottom: 2px; z-index: 2;
  width: 30px; height: 30px; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; border: none;
  background: rgba(20, 16, 10, .62); color: #f4ecda;
  opacity: 0; transition: opacity .15s ease;
}
.feelie.has-back:hover .fe-flip, .feelie.flipped .fe-flip { opacity: 1; }
.feelie .fe-flip svg { width: 17px; height: 17px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
@media (pointer: coarse) { .feelie .fe-flip { opacity: 1; width: 34px; height: 34px; } }

/* corner rotate grips (single feelies): grab a corner to spin the feelie */
.feelie:not(.book) .fe-rot {
  position: absolute; z-index: 4; width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  border-radius: 50%; background: rgba(20, 16, 10, .6); color: #f4ecda;
  cursor: grab; opacity: 0; transition: opacity .15s ease;
}
.feelie:not(.book):hover .fe-rot { opacity: 1; }
.feelie.grabbing .fe-rot { cursor: grabbing; }
.feelie .fe-rot svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.feelie .fe-rot.tl { top: -10px; left: -10px; }
.feelie .fe-rot.tr { top: -10px; right: -10px; }
.feelie .fe-rot.bl { bottom: -10px; left: -10px; }
.feelie .fe-rot.br { bottom: -10px; right: -10px; }
@media (pointer: coarse) { .feelie:not(.book) .fe-rot { opacity: .9; width: 27px; height: 27px; } }

/* an open double-page booklet on the table — StPageFlip renders the spread and
   the drag-a-corner curl; the table's zoom scales it, the player zooms to read */
.feelie.book { padding: 0; background: none; box-shadow: none; perspective: none; transform: rotate(0deg); cursor: default; }
/* on the pages: a page-flip cursor (turn); down the spine: a move cursor (slide) */
.feelie.book.fe-turn { cursor: url("data:image/svg+xml,%3Csvg%20xmlns%3D%27http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%27%20width%3D%2728%27%20height%3D%2728%27%20viewBox%3D%270%200%2028%2028%27%3E%3Cg%20fill%3D%27none%27%20stroke-linecap%3D%27round%27%20stroke-linejoin%3D%27round%27%3E%3Cpath%20d%3D%27M21%209a9%209%200%201%200%201.6%206%27%20stroke%3D%27%23fff%27%20stroke-width%3D%275%27%2F%3E%3Cpath%20d%3D%27M22%203v6h-6%27%20stroke%3D%27%23fff%27%20stroke-width%3D%275%27%2F%3E%3Cpath%20d%3D%27M21%209a9%209%200%201%200%201.6%206%27%20stroke%3D%27%23111%27%20stroke-width%3D%272.4%27%2F%3E%3Cpath%20d%3D%27M22%203v6h-6%27%20stroke%3D%27%23111%27%20stroke-width%3D%272.4%27%2F%3E%3C%2Fg%3E%3C%2Fsvg%3E") 14 14, pointer; }
.feelie.book.fe-move { cursor: grab; }
.feelie.book.grabbing { cursor: grabbing; }
.feelie.book .book-flip { background: transparent; }
.feelie.book .book-flip .page { background: #fbf7ee; overflow: hidden; }
.feelie.book .book-flip .page img { width: 100%; height: 100%; display: block; user-select: none; -webkit-user-drag: none; }
.feelie.book .book-flip .stf__item { box-shadow: 0 3px 10px rgba(0,0,0,.4), 0 14px 34px rgba(0,0,0,.34); }

.feelie-note {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; margin: 0;
  padding: 2.2rem 1.2rem calc(1rem + env(safe-area-inset-bottom, 0px));
  color: rgba(240,236,226,.82); font-size: .82rem; text-align: center; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.55) 60%, rgba(0,0,0,.7) 100%);
}
@media (max-width: 640px) { .feelie-note { font-size: .76rem; padding-top: 1.6rem; } }

/* ---------- INFOCARD DECODER (hidden-clue infocards) ---------- */
/* A draggable blue decoder card (title, slot, "Insert Infocard Here") with a
   red acetate window. The blue body is opaque and hides the infocard around it;
   only the red window decodes. The window (.decoder-window) is a SEPARATE table
   sibling with mix-blend-mode:multiply sitting just beneath the blue card — a
   child of the blue card would be trapped by the card's own stacking context
   and never blend with the infocard underneath. Geometry mirrors the JS
   constants DEC_* exactly (360x650, 150 head, 30 sides, 40 foot). */
.feelie.decoder {
  padding: 0; background: none; box-shadow: none; cursor: grab;
  width: 520px; height: 650px;
  filter: drop-shadow(0 14px 30px rgba(0,0,0,.45));
}
.feelie.decoder.grabbing { cursor: grabbing; }
.feelie.decoder .dec-head,
.feelie.decoder .dec-side,
.feelie.decoder .dec-foot { position: absolute; background: linear-gradient(180deg, #1a68d6, #145bc4); }
.feelie.decoder .dec-head { left: 0; top: 0; width: 100%; height: 235px; border-radius: 14px 14px 0 0; }
.feelie.decoder .dec-side { top: 235px; height: 187px; width: 127px; background: #155ec8; }
.feelie.decoder .dec-left { left: 0; } .feelie.decoder .dec-right { right: 0; }
.feelie.decoder .dec-foot { left: 0; bottom: 0; width: 100%; height: 228px; border-radius: 0 0 14px 14px; background: linear-gradient(180deg, #145bc4, #114fad); }
.feelie.decoder .dec-ttl {
  text-align: center; color: #fff; padding-top: 40px;
  font: 800 1.9rem/1 var(--ui); letter-spacing: 2px;
  text-shadow: 0 1px 2px rgba(0,0,0,.35);
}
.feelie.decoder .dec-slot {
  position: absolute; left: 20%; top: 118px; width: 60%; height: 20px;
  background: #fff; border-radius: 10px; box-shadow: inset 0 1px 2px rgba(0,0,0,.2);
}
.feelie.decoder .dec-notch {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 30px; height: 30px; background: #fff; border-radius: 50%;
}
.feelie.decoder .dec-insert {
  position: absolute; left: 0; top: 190px; width: 100%; text-align: center;
  color: #eaf2ff; font: italic 600 1rem var(--ui);
}
/* the red acetate window: a normal table sibling, multiplied over the infocard */
.decoder-window {
  position: absolute; background: #ff0000; mix-blend-mode: multiply;
  border-radius: 2px;
}
.mapsvg .mapedge { stroke: var(--text-dim); stroke-width: 1.5; opacity: .6; }
/* long links arc past intermediate rooms: quieter, clearly not adjacency */
.mapsvg .mapedge.far { fill: none; opacity: .38; stroke-dasharray: 5 5; }
/* a way out the story has mentioned but you haven't taken: a stub pointing
   off the edge of the room and stopping in mid-air */
.mapsvg .mapstub { stroke: var(--text-dim); stroke-width: 1.5; opacity: .34; stroke-linecap: round; stroke-dasharray: 3 3; }
.mapsvg .mapnode.maze rect { stroke-dasharray: 4 3; }

/* ---------- map zoom controls ---------- */
.mapzoom { display: inline-flex; gap: 4px; margin-right: auto; }
.mapzoom .zbtn { min-width: 2.1rem; padding: .35rem .5rem; font-variant-numeric: tabular-nums; }
.mapzoom.rail { gap: 2px; }
.mapzoom.rail .zbtn {
  min-width: 1.5rem; padding: 1px 5px 2px; line-height: 1.4;
  font: 600 .68rem var(--ui); color: var(--text-dim);
  background: none; border: 1px solid var(--border); border-radius: 5px; cursor: pointer;
}
.mapzoom.rail .zbtn:hover { color: var(--text); border-color: var(--text-dim); }
.rl-maprow { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 6px; }
/* the pane is a zoom surface, so the browser must not claim the gesture */
#mapwrap, .rl-map { touch-action: none; overscroll-behavior: contain; }
.mapsvg .maplbl { fill: var(--text-dim); font: 9px var(--ui); text-anchor: middle; }
.mapsvg .mapnode rect { fill: var(--bg-raised); stroke: var(--border); stroke-width: 1.2; }
.mapsvg .mapnode text { fill: var(--text); font: 11px var(--ui); text-anchor: middle; }
.mapsvg .mapnode.cur rect { stroke: var(--accent); stroke-width: 2.5; }
.mapsvg .mapnode.cur text { fill: var(--accent); font-weight: 700; }

/* ---------- first-open box intro ---------- */
#feboxintro {
  position: absolute; inset: 0; z-index: 5;
  display: flex; align-items: center; justify-content: center;
  background: rgba(7,6,5,.9); cursor: pointer;
  transition: opacity .5s ease .35s;
}
#feboxintro.opening { opacity: 0; }
#feboxintro .fb-box { position: relative; width: min(380px, 74vw); aspect-ratio: 5/6; }
#feboxintro .fb-base {
  position: absolute; inset: 6% 4% 0; border-radius: 6px;
  background: linear-gradient(165deg, #8a8580 0%, #6e6a66 100%);
  box-shadow: 0 24px 60px rgba(0,0,0,.6);
}
#feboxintro .fb-lid {
  position: absolute; inset: 0 0 4%; border-radius: 7px; overflow: hidden;
  background: linear-gradient(165deg, #9a958f 0%, #7a7671 60%, #6b6763 100%);
  box-shadow: 0 10px 26px rgba(0,0,0,.5), inset 0 2px 0 rgba(255,255,255,.22);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .6rem; padding: 1.4rem; text-align: center;
  transition: transform .85s cubic-bezier(.35,.05,.3,1), opacity .85s ease;
}
#feboxintro.opening .fb-lid { transform: translate(34%, -66%) rotate(11deg); opacity: 0; }
#feboxintro .fb-name { font-family: var(--serif, Georgia); font-weight: 700; font-size: 1.25rem; color: #f2ede4; text-shadow: 0 1px 2px rgba(0,0,0,.4); }
#feboxintro .fb-infocom { font-family: var(--mono); letter-spacing: .5em; font-size: .8rem; color: #dcd6cb; }
#feboxintro .fb-year { font-family: var(--mono); font-size: .72rem; color: #b9b3a8; }
#feboxintro .fb-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: center top; }
#feboxintro .fb-hint {
  position: absolute; left: 50%; bottom: -3.2rem; transform: translateX(-50%);
  margin: 0; color: rgba(240,236,226,.85); font: 600 .85rem var(--ui); letter-spacing: .4px;
  animation: fb-pulse 1.8s ease-in-out infinite;
}
@keyframes fb-pulse { 0%,100% { opacity: .55; } 50% { opacity: 1; } }

/* ---------- InvisiClues viewer ---------- */
#cluesview {
  position: fixed; inset: 0; z-index: 150; display: none;
  background: rgba(6,5,4,.88); -webkit-backdrop-filter: blur(3px); backdrop-filter: blur(3px);
}
#cluesview.open { display: block; }
#cluesview .ic-shell { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding: 3.5rem 1rem 1.5rem; }
#cluesview .ic-book {
  width: min(680px, 94vw); max-height: 100%; overflow-y: auto;
  background: #f7f2e6; color: #241f18; border-radius: 10px;
  padding: 1.8rem 2.2rem 2.2rem;
  box-shadow: 0 20px 60px rgba(0,0,0,.6);
}
#cluesview h2 { margin: 0 0 .3rem; font-family: var(--serif, Georgia); font-size: 1.6rem; }
#cluesview .ic-note { margin: 0 0 1.4rem; color: #6e6353; font-size: .85rem; line-height: 1.5; }
#cluesview h3 {
  margin: 1.6rem 0 .5rem; font-family: var(--mono); font-size: .78rem;
  letter-spacing: .22em; text-transform: uppercase; color: #8a7c64;
  border-bottom: 1px solid #e0d6c2; padding-bottom: .35rem;
}
#cluesview .ic-qbtn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: none; border: none; padding: .55rem .2rem;
  font: 600 .95rem/1.4 var(--ui); color: #241f18;
}
#cluesview .ic-qbtn::before { content: "▸ "; color: #a08c66; }
#cluesview .ic-q.openq .ic-qbtn::before { content: "▾ "; }
#cluesview .ic-a { display: none; padding: .2rem 0 .8rem 1.2rem; }
#cluesview .ic-q.openq .ic-a { display: block; }
/* a blank strip of developer-pen ink: rub across it to bring the answer up */
#cluesview .ic-hint {
  position: relative; margin: .4rem 0; padding: .5rem .8rem;
  border-radius: 4px; cursor: crosshair; touch-action: none;
  background: repeating-linear-gradient(0deg, #efe7d5 0 2px, #e9dfc9 2px 4px);
  box-shadow: inset 0 0 0 1px #e0d4bb;
  min-height: 1.4em;
  font: .92rem/1.5 var(--ui); color: transparent;
}
#cluesview .ic-hint .ic-ink {
  color: #241f18;
  clip-path: inset(0 calc((1 - var(--dev, 0)) * 100%) 0 0);
}
#cluesview .ic-hint.dev { cursor: default; background: #f3ecdc; }
#cluesview .ic-hint.dev .ic-ink { clip-path: none; }
#cluesview .ic-hint:not(.dev):hover { box-shadow: inset 0 0 0 1px #c9b489; }

/* InvisiClues pages inside the page-flip booklet */
.feelie.book .book-flip .page.ic-page {
  padding: 26px 30px; overflow: hidden;
  font: 13px/1.45 var(--ui); color: #2c2417;
  /* forty-year-old paper: browned edges, a wash of grain, warm uneven base */
  background:
    radial-gradient(130% 105% at 50% 48%, rgba(0,0,0,0) 58%, rgba(118,88,48,.18) 100%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='linear' slope='0.05'/></feComponentTransfer></filter><rect width='160' height='160' filter='url(%23n)'/></svg>"),
    linear-gradient(163deg, #f5eeda 0%, #ecdfc2 55%, #e6d6b4 100%);
}
/* foxing: soft age stains, drifting position from page to page so no two
   spreads look photocopied (cover pages keep their own artwork clean) */
.feelie.book .book-flip .page.ic-page:not(.ic-cover-pg)::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background:
    radial-gradient(24% 16% at 81% 11%, rgba(151,111,59,.13), transparent 72%),
    radial-gradient(15% 11% at 10% 76%, rgba(140,100,52,.11), transparent 70%),
    radial-gradient(10% 8% at 64% 90%, rgba(122,86,44,.12), transparent 70%);
}
.feelie.book .book-flip .page.ic-page:not(.ic-cover-pg):nth-of-type(3n)::after {
  background:
    radial-gradient(20% 14% at 16% 14%, rgba(151,111,59,.12), transparent 72%),
    radial-gradient(13% 10% at 87% 62%, rgba(140,100,52,.12), transparent 70%),
    radial-gradient(24% 15% at 42% 95%, rgba(122,86,44,.10), transparent 72%);
}
.feelie.book .book-flip .page.ic-page:not(.ic-cover-pg):nth-of-type(3n+1)::after {
  background:
    radial-gradient(17% 12% at 70% 20%, rgba(146,106,56,.10), transparent 70%),
    radial-gradient(26% 17% at 6% 40%, rgba(151,111,59,.12), transparent 74%),
    radial-gradient(11% 8% at 88% 88%, rgba(122,86,44,.13), transparent 68%);
}
.ic-page h3 { font: 700 10px var(--mono); letter-spacing: .18em; text-transform: uppercase; color: #85765b; border-bottom: 1px solid #d3c5a3; margin: 12px 0 6px; padding-bottom: 3px; }
.ic-page .q { font-weight: 700; margin: 9px 0 4px; }
.ic-page .ic-hint {
  position: relative; margin: 3px 0; padding: 4px 9px; border-radius: 3px;
  background: repeating-linear-gradient(0deg, #ece2cc 0 2px, #e5dac0 2px 4px);
  box-shadow: inset 0 0 0 1px #dccfb0; cursor: pointer; color: transparent;
}
.ic-page .ic-hint .ic-ink { visibility: hidden; }
.ic-page .ic-hint.dev { background: #f4eddd; color: inherit; cursor: default; box-shadow: inset 0 0 0 1px #e7dcc2; }
.ic-page .ic-hint.dev .ic-ink { visibility: visible; }
.ic-cover-pg { position: relative; display: flex; flex-direction: column; align-items: center; justify-content: center; text-align: center; gap: 14px; }
.ic-cover-pg .ic-cover-art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ic-cover-pg .ic-brand { font-family: var(--serif, Georgia); font-weight: 700; font-size: 2rem; }
.ic-cover-pg .ic-brand span { font-size: .55em; vertical-align: super; }
.ic-cover-pg .ic-sub { font-family: var(--mono); font-size: .68rem; letter-spacing: .34em; color: #8a7c64; }
.ic-cover-pg .ic-game { font: 600 1rem/1.4 var(--ui); color: #4a4030; max-width: 80%; }
.ic-cover-pg .ic-sub2 { font: italic 500 .74rem var(--ui); color: #a08c66; margin-top: 1.2rem; }

/* the booklet on the feelie table */
.feelie.fe-clues { cursor: pointer; }
.feelie.fe-clues .ic-cover {
  aspect-ratio: 5/7; border-radius: 6px; overflow: hidden;
  background: linear-gradient(160deg, #f4eede 0%, #e7ddc4 100%);
  border: 1px solid #d5c8a9;
  box-shadow: 0 2px 6px rgba(0,0,0,.35), 0 14px 34px rgba(0,0,0,.32);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: .5rem; text-align: center; padding: 1rem; color: #2c2417;
}
.feelie.fe-clues .ic-brand { font-family: var(--serif, Georgia); font-weight: 700; font-size: 1.35rem; }
.feelie.fe-clues .ic-brand span { font-size: .6em; vertical-align: super; }
.feelie.fe-clues .ic-sub { font-family: var(--mono); font-size: .6rem; letter-spacing: .3em; color: #8a7c64; }
.feelie.fe-clues .ic-game { font: 600 .8rem/1.3 var(--ui); color: #4a4030; margin-top: .6rem; }
.feelie.fe-clues .ic-tap { font: 600 .62rem var(--ui); letter-spacing: .16em; text-transform: uppercase; color: #a08c66; margin-top: 1rem; }

/* ---------- cabinet surround: period hardware ---------- */
/* moulded plastic: fine noise over the cream case */
#cabinet .cab-grain::after {
  content: ""; position: absolute; inset: 0; opacity: .3; mix-blend-mode: multiply;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='120' height='120'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='linear' slope='0.14'/></feComponentTransfer></filter><rect width='120' height='120' filter='url(%23n)'/></svg>");
}
/* cooling vents along the top edge */
#cabinet .cab-vents {
  position: absolute; top: 12px; left: 50%; transform: translateX(-50%);
  width: 240px; height: 11px; border-radius: 6px;
  background: repeating-linear-gradient(90deg, rgba(30,26,18,.5) 0 4px, rgba(0,0,0,0) 4px 12px);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.4), 0 1px 0 rgba(255,252,235,.4);
}
/* recessed cross-head screws in the corners, each seated at its own angle.
   They really unscrew: click one and it backs out, drops off the case and
   leaves the empty threaded hole behind; click the hole to thread it back
   in. All four out and the case sits loose on its chassis. */
#cabinet .cab-screw {
  position: absolute; width: 13px; height: 13px; border-radius: 50%;
  cursor: pointer;
  background:
    radial-gradient(circle at 50% 42%, rgba(0,0,0,.65) 0 22%, transparent 60%),
    repeating-radial-gradient(circle at 50% 50%, rgba(0,0,0,.55) 0 1px, rgba(96,88,66,.5) 1px 2.6px);
  box-shadow: inset 0 2px 3px rgba(0,0,0,.8), inset 0 0 0 1px rgba(0,0,0,.4), 0 1px 0 rgba(255,252,235,.4);
}
#cabinet .cab-screw .head {
  position: absolute; inset: 0; border-radius: 50%;
  background:
    linear-gradient(0deg, transparent 44%, #55503f 44% 56%, transparent 56%),
    linear-gradient(90deg, transparent 44%, #55503f 44% 56%, transparent 56%),
    radial-gradient(circle at 36% 30%, #d3c9ad, #857b64);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.55);
}
#cabinet .cab-screw.out .head { display: none; }
#cabinet .cab-screw.turning-out .head { animation: cabScrewOut 1.05s cubic-bezier(.55,.06,.68,.19) forwards; }
#cabinet .cab-screw.turning-in .head { animation: cabScrewIn .9s cubic-bezier(.25,.46,.45,.94) forwards; }
@keyframes cabScrewOut {
  0%   { transform: rotate(0deg) scale(1); filter: drop-shadow(0 0 0 rgba(0,0,0,0)); }
  70%  { transform: rotate(-900deg) scale(1.1); filter: drop-shadow(0 2px 2px rgba(0,0,0,.35)); }
  100% { transform: rotate(-1170deg) translateY(-2px) scale(1.28); filter: drop-shadow(0 5px 4px rgba(0,0,0,.5)); }
}
@keyframes cabScrewIn {
  0%   { transform: rotate(-1170deg) translateY(-2px) scale(1.28); filter: drop-shadow(0 5px 4px rgba(0,0,0,.5)); }
  30%  { transform: rotate(-900deg) scale(1.1); filter: drop-shadow(0 2px 2px rgba(0,0,0,.35)); }
  100% { transform: rotate(0deg) scale(1); filter: drop-shadow(0 0 0 rgba(0,0,0,0)); }
}
/* a freed screw head tumbling down the front of the case */
.cab-screw-fall {
  position: fixed; width: 13px; height: 13px; border-radius: 50%; z-index: 260;
  pointer-events: none; will-change: transform, opacity;
  background:
    linear-gradient(0deg, transparent 44%, #55503f 44% 56%, transparent 56%),
    linear-gradient(90deg, transparent 44%, #55503f 44% 56%, transparent 56%),
    radial-gradient(circle at 36% 30%, #d3c9ad, #857b64);
  filter: drop-shadow(0 3px 3px rgba(0,0,0,.45));
}
/* with every screw gone the terminal shifts on its chassis */
@media (prefers-reduced-motion: no-preference) {
  .cab-shell { transition: transform .25s cubic-bezier(.34,1.56,.64,1); }
}
#cabinet.cab-loose .cab-shell { transform: rotate(.45deg) translateY(6px); }

/* ---------- percussive maintenance ----------
   A smack on the case jolts the raster (the scanlines and glass stay put,
   only the picture jumps). If the vertical hold has drifted — a hum bar
   slowly rolling up the tube — the smack is what settles it. */
@media (prefers-reduced-motion: no-preference) {
  /* must outrank the standing cab-jitter drift on body.cabinet #cab-slot */
  body.cabinet #cab-slot.slapped { animation: cabSlap .5s cubic-bezier(.2,.7,.3,1); }
}
@keyframes cabSlap {
  0%   { transform: translateY(0); }
  12%  { transform: translateY(-9px) skewX(1.1deg); }
  28%  { transform: translateY(5px) skewX(-.6deg); }
  46%  { transform: translateY(-3px); }
  66%  { transform: translateY(1.5px); }
  100% { transform: translateY(0); }
}
.cab-fx.humbar { display: none; }
.cab-tube.rolling .cab-fx.humbar {
  display: block; inset: auto 0 -36% 0; height: 36%;
  background: linear-gradient(180deg, transparent 0%, rgba(0,0,0,.20) 36%, rgba(255,255,255,.05) 50%, rgba(0,0,0,.20) 64%, transparent 100%);
  animation: humRoll 3.8s linear infinite;
}
@keyframes humRoll { from { transform: translateY(0); } to { transform: translateY(-380%); } }
body.cabinet .cab-tube.rolling #cab-slot { animation: vJitter .14s steps(2) infinite; }
@keyframes vJitter { 0% { transform: translateY(0); } 50% { transform: translateY(1.6px); } 100% { transform: translateY(0); } }

/* ---------- fingerprints on the glass ---------- */
.cab-fx.smudges { mix-blend-mode: screen; }

/* ---------- the tamper sticker over the bottom-left screw ---------- */
#cabinet .cab-warranty {
  position: absolute; left: 2px; bottom: 4px; width: 46px; padding: 2px 0 3px;
  transform: rotate(-7deg); pointer-events: none; text-align: center; z-index: 2;
  background: linear-gradient(165deg, #f0f0f1 0%, #d3d4d8 100%);
  border-radius: 2px;
  box-shadow: 0 1px 2px rgba(0,0,0,.35), inset 0 0 0 1px rgba(0,0,0,.08);
}
#cabinet .cab-warranty span { display: block; font: 800 4.5px/1.6 var(--ui); letter-spacing: .08em; color: #b3261e; }
#cabinet .cab-warranty.torn { clip-path: polygon(0 0, 62% 0, 55% 18%, 64% 38%, 52% 55%, 63% 75%, 54% 100%, 0 100%); }
#cabinet .cab-warranty-half { clip-path: polygon(62% 0, 100% 0, 100% 100%, 54% 100%, 63% 75%, 52% 55%, 64% 38%, 55% 18%); z-index: 4; }
#cabinet .cab-residue.res-void { font: 800 4px/1.7 var(--ui); letter-spacing: .1em; color: rgba(110,104,88,.55); overflow: hidden; text-align: right; }

/* ---------- peelable labels ---------- */
#cabinet .peelable { --fold: 0px; cursor: pointer; }
#cabinet .peelable[data-peel="1"] { --fold: 9px; }
#cabinet .peelable[data-peel="2"] { --fold: 17px; box-shadow: 0 2px 5px rgba(0,0,0,.4); }
#cabinet .peelable[data-peel] {
  clip-path: polygon(0 0, 100% 0, 100% calc(100% - var(--fold)), calc(100% - var(--fold)) 100%, 0 100%);
}
/* the folded-over corner, showing the sticker's adhesive back */
#cabinet .peelable[data-peel]::after {
  content: ""; position: absolute;
  right: calc(var(--fold) * -0.08); bottom: calc(var(--fold) * -0.08);
  width: calc(var(--fold) * 1.5); height: calc(var(--fold) * 1.5);
  background: linear-gradient(315deg, #ece7d6 12%, #cbc5b2 48%, #a9a28c 100%);
  clip-path: polygon(0 0, 100% 0, 0 100%);
  filter: drop-shadow(-1px -1px 1px rgba(0,0,0,.2));
}
/* a peeled sticker flutters down the case */
#cabinet .st-fall { animation: stickerFall 1.25s ease-in forwards; z-index: 5; pointer-events: none; }
@keyframes stickerFall {
  0%   { opacity: 1; }
  25%  { transform: translate(7px, 24px) rotate(9deg); opacity: 1; }
  55%  { transform: translate(-6px, 56px) rotate(-8deg); opacity: 1; }
  80%  { transform: translate(5px, 92px) rotate(6deg); opacity: .9; }
  100% { transform: translate(2px, 126px) rotate(11deg); opacity: 0; }
}
/* what the labels were hiding */
#cabinet .cab-residue { position: absolute; pointer-events: none; border-radius: 3px; }
#cabinet .cab-residue.res-caution { background: rgba(255,252,235,.30); box-shadow: inset 0 0 4px rgba(0,0,0,.06); }
#cabinet .cab-residue.res-asset { background: repeating-linear-gradient(115deg, rgba(70,58,34,.15) 0 6px, rgba(70,58,34,.07) 6px 11px); }
#cabinet .cab-residue.res-badge { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; background: rgba(255,252,235,.14); }
#cabinet .cab-residue.res-badge b { font: 800 8px/1 var(--ui); letter-spacing: .2em; color: rgba(46,52,94,.44); transform: rotate(-1.2deg); text-shadow: 0 0 1px rgba(46,52,94,.2); }
#cabinet .cab-screw.s-tl { top: 10px; left: 12px; transform: rotate(14deg); }
#cabinet .cab-screw.s-tr { top: 10px; right: 12px; transform: rotate(-32deg); }
#cabinet .cab-screw.s-bl { bottom: 9px; left: 12px; transform: rotate(51deg); }
#cabinet .cab-screw.s-br { bottom: 9px; right: 12px; transform: rotate(-8deg); }
/* the INFOCOM badge sticker on the chin, slightly askew like a hand-applied label */
#cabinet .cab-sticker {
  position: absolute; right: 345px; bottom: 13px; transform: rotate(-2.4deg);
  padding: 5px 11px 6px; border-radius: 3px; text-align: center;
  background: linear-gradient(168deg, #f4eeda 0%, #e9e1c6 100%);
  box-shadow: 0 1px 3px rgba(0,0,0,.4), inset 0 0 0 1px rgba(0,0,0,.07);
}
#cabinet .cab-sticker b { display: block; font: 800 11px/1 var(--ui); letter-spacing: .26em; color: #21327a; }
#cabinet .cab-sticker span { display: block; margin-top: 3px; font: 700 5px/1 var(--ui); letter-spacing: .3em; color: #a03a30; }
/* a narrow shell has no room between the brand and the controls: no sticker
   is better than a sticker over a knob */
@media (max-width: 1360px) { #cabinet .cab-sticker { display: none; } }

/* controls: machined knob, paddle switches, power rocker with jewel lamp */
#cabinet .cab-knob .dial {
  width: 32px; height: 32px;
  background:
    radial-gradient(circle at 42% 36%, rgba(255,252,235,.6) 0 7%, transparent 24%),
    radial-gradient(circle at 50% 50%, #d8cfb4 0 34%, transparent 35%),
    repeating-conic-gradient(#8a8069 0deg 9deg, #b9ae91 9deg 18deg);
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.25), inset 0 2px 3px rgba(255,252,235,.35), 0 3px 5px rgba(0,0,0,.5);
}
#cabinet .cab-knob .dial::after { top: 2px; height: 12px; background: #2e2920; }
#cabinet .cab-slider .track {
  width: 52px; height: 18px; border-radius: 3px;
  background: linear-gradient(180deg, #35302a, #4a4438);
  box-shadow: inset 0 2px 4px rgba(0,0,0,.65), 0 1px 0 rgba(255,252,235,.35);
}
#cabinet .cab-slider .thumb {
  top: 2px; left: 2px; width: 23px; height: 14px; border-radius: 2px;
  background:
    repeating-linear-gradient(90deg, rgba(0,0,0,.16) 0 2px, rgba(0,0,0,0) 2px 5px),
    linear-gradient(180deg, #efe8d3, #c2b799);
  box-shadow: 0 1px 2px rgba(0,0,0,.55), inset 0 1px 0 rgba(255,255,255,.55);
}
#cabinet .cab-slider.pos-amber .thumb { left: 27px; }
#cabinet #cab-sound.on .thumb { left: 27px; background: repeating-linear-gradient(90deg, rgba(0,0,0,.16) 0 2px, rgba(0,0,0,0) 2px 5px), linear-gradient(180deg, var(--phos, #ffb000), #c98d00); }
#cabinet .cab-power .io {
  width: 34px; height: 22px; border-radius: 3px;
  background: linear-gradient(90deg, #d9cfb6 0 50%, #b6ab8f 50%);
  box-shadow: inset 0 0 0 2px rgba(0,0,0,.22), inset 2px 2px 3px rgba(255,252,235,.4), 0 2px 4px rgba(0,0,0,.5);
}
#cabinet .cab-power .io::before,
#cabinet .cab-power .io::after {
  position: absolute; top: 0; height: 100%; width: 50%; margin: 0;
  display: flex; align-items: center; justify-content: center;
  font: 700 9px var(--mono); letter-spacing: 0; text-indent: 0;
  color: #4b4436; background: none; border-radius: 0;
}
#cabinet .cab-power .io::before { content: "I"; left: 0; }
#cabinet .cab-power .io::after  { content: "O"; left: 50%; }
#cabinet .cab-power .led {
  width: 9px; height: 9px;
  background: radial-gradient(circle at 38% 30%, color-mix(in srgb, var(--phos, #ffb000) 60%, #fff), var(--phos, #ffb000) 65%);
  box-shadow: 0 0 10px var(--phos, #ffb000), inset 0 -1px 2px rgba(0,0,0,.45);
}
/* engraved labels */
#cabinet .cab-knob .l, #cabinet .cab-slider .l, #cabinet .cab-power .l {
  font-family: var(--mono); font-size: 7.5px; letter-spacing: .24em; color: #5f5745;
  text-shadow: 0 1px 0 rgba(255,252,235,.5);
}

/* ---------- CRT physics: jitter, degauss, collapse, grain ---------- */
/* the raster never sits perfectly still: sub-pixel drift on a slow loop */
body.cabinet #cab-slot { animation: cab-jitter 9s linear infinite; }
@keyframes cab-jitter {
  0%, 100% { transform: translateX(0); }
  7%  { transform: translateX(.3px); }
  13% { transform: translateX(-.2px); }
  31% { transform: translate(.2px, .2px); }
  47% { transform: translateX(-.35px); }
  62% { transform: translateX(.15px); }
  81% { transform: translate(-.25px, -.2px); }
}
/* switch-on: the field settles with a degauss wobble and a flash of bloom */
body.cabinet #cab-slot.cab-degauss { animation: cab-degauss .95s cubic-bezier(.2,.8,.3,1); }
@keyframes cab-degauss {
  0%   { transform: scale(1.014, 1.035) skewX(.5deg);  filter: brightness(1.7) blur(.6px); }
  30%  { transform: scale(.995, .988) skewX(-.3deg);   filter: brightness(1.18); }
  60%  { transform: scale(1.005, 1.01);                filter: brightness(1.06); }
  100% { transform: none; filter: none; }
}
/* switch-off: collapse to a bright line, then a dot */
#cab-off { position: absolute; inset: 0; z-index: 40; overflow: hidden; background: #000; display: flex; align-items: center; }
#cab-off .cab-off-line {
  width: 100%; height: 100%;
  background: var(--phos, #ffb000);
  box-shadow: 0 0 34px var(--phos, #ffb000), 0 0 90px var(--phos, #ffb000);
  animation: cab-collapse .24s cubic-bezier(.7,0,.9,1) forwards;
}
@keyframes cab-collapse {
  0%   { transform: scaleY(1); opacity: .85; }
  55%  { transform: scaleY(.006); opacity: 1; }
  80%  { transform: scaleY(.004) scaleX(.4); opacity: 1; }
  100% { transform: scaleY(.003) scaleX(.02); opacity: 0; }
}
/* fine vertical phosphor grain under the scanlines */
body.cabinet .cab-tube::after {
  background:
    repeating-linear-gradient(180deg, rgba(0,0,0,.42) 0, rgba(0,0,0,.42) 1px, rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px),
    repeating-linear-gradient(90deg, rgba(0,0,0,.08) 0 1px, rgba(0,0,0,0) 1px 3px);
}
@media (prefers-reduced-motion: reduce) {
  body.cabinet #cab-slot, body.cabinet #cab-slot.cab-degauss { animation: none; }
}

/* ---------- phosphor persistence + corner defocus ---------- */
/* the lagging clone that smears when the page rolls (filled by paintReflect,
   driven by ghostTick); screen-blended so it only ever adds glow */
#cab-ghost {
  position: absolute; overflow: hidden; pointer-events: none;
  z-index: 4; opacity: 0; mix-blend-mode: screen;
  filter: blur(1.4px);
}
#cab-ghost .ghostbuf { will-change: transform; }
#cab-ghost .ghostbuf .para { margin: 0; min-height: 1em; white-space: pre-wrap; overflow-wrap: break-word; }
#cab-ghost .ghostbuf, #cab-ghost .ghostbuf * {
  color: color-mix(in srgb, var(--phos, #ffb000) 58%, transparent) !important;
  text-shadow: none !important; background: none !important;
}
/* the beam loses focus toward the glass edges — kept BELOW the scanline
   overlay (z3) so the raster stays crisp, and gentle enough to read through */
body.cabinet .cab-fx.defocus {
  z-index: 2;
  -webkit-backdrop-filter: blur(.55px); backdrop-filter: blur(.55px);
  -webkit-mask: radial-gradient(ellipse 76% 72% at 50% 48%, transparent 72%, #000 98%);
  mask: radial-gradient(ellipse 76% 72% at 50% 48%, transparent 72%, #000 98%);
}

/* ---------- cabinet sound switch ----------
   Movement comes from the slider's own `left` rule — an earlier
   translateX(100%) here stacked on top of it and shoved the thumb
   out of the track. Only the lit glow lives here now. */
#cab-sound.on .thumb { box-shadow: 0 0 8px var(--phos, #ffb000), 0 1px 2px rgba(0,0,0,.55); }

/* assist off = no tap-to-play, so no Move pill either */
body:not(.assist-on) #dirtoggle { display: none; }

/* ---------- death overlay ---------- */
#deathveil {
  position: fixed; inset: 0; z-index: 150; display: none;
  align-items: center; justify-content: center;
  background: radial-gradient(ellipse at 50% 45%, rgba(60,0,0,.18) 0%, rgba(10,0,0,.55) 100%);
}
#deathveil.show { display: flex; animation: dv-in .4s ease; }
@keyframes dv-in { from { opacity: 0; } to { opacity: 1; } }
#deathveil .dv-card {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 12px;
  padding: 1.6rem 2.2rem 1.2rem; text-align: center;
  box-shadow: 0 18px 50px rgba(0,0,0,.5);
}
#deathveil .dv-title { font-family: var(--mono); font-size: 1.05rem; letter-spacing: .12em; margin-bottom: 1.1rem; }
#deathveil .dv-btns { display: flex; gap: .7rem; justify-content: center; }
#deathveil .dv-btns button {
  font-family: var(--mono); font-size: .82rem; letter-spacing: .14em; cursor: pointer;
  padding: .55rem 1.1rem; border-radius: 8px;
  border: 1px solid var(--border); background: var(--bg-raised, var(--bg)); color: var(--text);
}
#deathveil .dv-btns button:hover { border-color: var(--text); }
#deathveil .dv-note { margin: 1rem 0 0; font-size: .74rem; color: var(--text-dim); }

/* ---------- grue dark: pitch-black rooms close in at the edges ---------- */
#darkveil {
  position: fixed; inset: 0; z-index: 90; pointer-events: none;
  opacity: 0; transition: opacity 1.6s ease;
  background: radial-gradient(ellipse at 50% 42%, transparent 34%, rgba(0,0,0,.88) 96%);
}
body.darkroom #darkveil { opacity: 1; }
body.darkroom.cabinet #darkveil { display: none; }  /* the tube is already its own darkness */

/* ---------- cabinet power-on self test ---------- */
#cab-boot {
  position: absolute; inset: 0; z-index: 30;
  background: #050403;
  font-family: var(--mono); font-size: 1rem; line-height: 1.7;
  color: var(--phos, #ffb000); text-shadow: 0 0 6px currentColor;
  padding: 2.2rem 2.6rem;
}

/* ---------- per-game accent in the player chrome ---------- */
/* each game signs the play view: its accent underlines the status bar */
html[data-theme="light"] body:not(.cabinet) #statusline { border-bottom: 2px solid var(--game-accent); }
html[data-theme="dark"] body:not(.cabinet) #statusline { border-bottom: 2px solid var(--game-accent-dark); }
/* the empty map is an invitation, not a void */
.rl-map .empty { text-align: center; padding: 2.6rem 1rem; color: var(--text-dim); font-size: .78rem; line-height: 1.7; }
.rl-map .empty::before {
  content: ""; display: block; width: 34px; height: 34px; margin: 0 auto .7rem; opacity: .45;
  background: no-repeat center / contain url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23888' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M16 8l-2.5 5.5L8 16l2.5-5.5z'/%3E%3C/svg%3E");
}

html[data-theme="light"] header .toolbar button:hover,
html[data-theme="dark"] header .toolbar button:hover {
  border-color: var(--game-accent); color: var(--game-accent);
}
html[data-theme="dark"] header .toolbar button:hover { border-color: var(--game-accent-dark); color: var(--game-accent-dark); }
#bootsplash .bs-spinner { border-top-color: var(--game-accent); }

/* ---------- paper grain on the light theme ---------- */
html[data-theme="light"] body {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='140' height='140'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/><feColorMatrix type='saturate' values='0'/><feComponentTransfer><feFuncA type='linear' slope='0.045'/></feComponentTransfer></filter><rect width='140' height='140' filter='url(%23n)'/></svg>");
}

/* ---------- toast ---------- */
#toast {
  pointer-events: none; /* informational only — must never block a tap */
  position: fixed; left: 50%; bottom: 1.4rem; transform: translateX(-50%) translateY(20px);
  background: var(--bar-bg); color: var(--bar-text);
  border-radius: 8px; padding: .55rem 1rem; font-size: .85rem;
  opacity: 0; pointer-events: none; transition: all .25s; z-index: 200;
}
#toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

/* command-in-progress label that rides the cursor in Assist mode:
   no border — the text sits on a soft darkened glow so it stays legible
   over the story without a boxed edge */
#cursorcmd {
  position: fixed; z-index: 210; pointer-events: none; display: none;
  background: rgba(12, 12, 12, .72);
  color: var(--text);
  font-family: var(--mono); font-size: .82rem; font-weight: 600;
  padding: .16rem .55rem; border-radius: 9px; white-space: nowrap;
  box-shadow: 0 0 14px 5px rgba(0, 0, 0, .55);
  text-shadow: 0 0 7px currentColor;
}
#cursorcmd.waiting { opacity: .8; }
/* Paper theme: body text is dark, so use a light face on the darkened glow */
html[data-theme="light"] #cursorcmd { color: #f4ede2; text-shadow: none; }

@media (max-width: 640px) {
  header { padding: .4rem .5rem; flex-wrap: wrap; }
  #buffer { padding: 1.1rem .9rem 1.2rem; }
  .toolbar button.optional { display: none; }
  /* Give the toolbar the full row and let it wrap, so no button (e.g. Feelies
     or Help) gets clipped off the edge. Without width:100% the flex:none
     toolbar sizes to its one-row content and overflows instead of wrapping. */
  .toolbar { flex-wrap: wrap; justify-content: flex-start; row-gap: .3rem; width: 100%; }
}

@media (pointer: coarse) {
  /* iOS zooms into inputs under 16px; keep them at or above it */
  #cmd, .modal .newrow input { font-size: max(16px, 1em); }
  .toolbar select, .toolbar button { padding: .5rem .65rem; }

  /* The toolbar collapses on touch; a floating toggle summons it. The toggle
     mirrors the Move button — a compact translucent pill floated just beneath
     the status bar (top set by JS), on the right instead of the left. */
  header { overflow: hidden; max-height: 40vh;
    transition: max-height .26s ease, padding .22s ease, opacity .16s ease; }
  body.chrome-hidden header {
    max-height: 0; padding-top: 0; padding-bottom: 0;
    opacity: 0; border-bottom-width: 0; box-shadow: none;
  }
  #chrometoggle {
    position: fixed; top: 0; right: 8px; z-index: 30;
    display: flex; align-items: center; justify-content: center;
    padding: .34rem .5rem; cursor: pointer;
    background: color-mix(in srgb, var(--bg-raised) 82%, transparent);
    -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px);
    color: var(--text); border: 1px solid var(--border);
    border-radius: 11px; box-shadow: var(--shadow);
  }
  #chrometoggle svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
  #chrometoggle.open { background: var(--accent); color: var(--bg-raised); border-color: var(--accent); }
}

/* ---------- themed on-screen keyboard (touch) ----------
   A tactile vintage keyboard: sculpted, beveled keycaps on a moulded deck.
   Every colour is derived from the active theme, so the whole thing recolours
   itself for Paper / Night / CRT. Suggestions come from the game's own parser
   dictionary, so they only ever offer words the game understands. */
#kbd {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 120;
  display: none;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--bar-bg) 84%, #fff) 0%,
    var(--bar-bg) 34%,
    color-mix(in srgb, var(--bar-bg) 84%, #000) 100%);
  border-top: 1px solid color-mix(in srgb, var(--bar-bg) 55%, #000);
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--bar-bg) 65%, #fff), 0 -8px 20px rgba(0, 0, 0, .3);
  padding: 8px 6px calc(10px + env(safe-area-inset-bottom, 0px));
  user-select: none; -webkit-user-select: none; touch-action: manipulation;
}
body.kbd-open #kbd { display: block; }
body.kbd-open #assist { display: none; }        /* one input method at a time */
body.kbd-open #buffer { padding-bottom: 340px; } /* keep the prompt above the keys */
#kbd-suggest {
  display: flex; gap: 7px; padding: 1px 2px 8px; min-height: 34px;
  overflow-x: auto; scrollbar-width: none;
}
#kbd-suggest::-webkit-scrollbar { display: none; }
#kbd-suggest .sug {
  flex: 0 0 auto; padding: 6px 13px; border-radius: 7px;
  --keycap: color-mix(in srgb, var(--bg-raised) 92%, var(--accent));
  color: var(--text); font-family: var(--mono); font-size: .92rem; font-weight: 700;
  background: linear-gradient(180deg, color-mix(in srgb, var(--keycap) 74%, #fff), var(--keycap) 55%, color-mix(in srgb, var(--keycap) 86%, #000));
  box-shadow: inset 0 1px 0 color-mix(in srgb, var(--keycap) 55%, #fff), 0 2px 0 color-mix(in srgb, var(--keycap) 55%, #000), 0 3px 4px rgba(0, 0, 0, .28);
}
#kbd-suggest .sug:active { transform: translateY(2px); box-shadow: inset 0 2px 3px rgba(0, 0, 0, .3); }
.kbd-row { display: flex; gap: 6px; justify-content: center; margin-bottom: 6px; }
.kbd-row:last-child { margin-bottom: 0; }
.kbd-key {
  --keycap: var(--bg-raised);
  flex: 1 1 0; min-width: 0; height: 46px;
  display: flex; align-items: center; justify-content: center;
  color: var(--text); font-family: var(--mono); font-size: 1.06rem; font-weight: 700;
  border: none; border-radius: 6px 6px 8px 8px;
  background: linear-gradient(180deg,
    color-mix(in srgb, var(--keycap) 76%, #fff) 0%,
    var(--keycap) 46%,
    color-mix(in srgb, var(--keycap) 88%, #000) 100%);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--keycap) 52%, #fff),   /* top gloss */
    inset 0 -3px 5px color-mix(in srgb, var(--keycap) 84%, #000), /* dished face */
    0 2px 0 color-mix(in srgb, var(--keycap) 55%, #000),          /* keycap skirt */
    0 4px 5px rgba(0, 0, 0, .32);                                 /* cast shadow */
  text-shadow: 0 1px 0 color-mix(in srgb, var(--keycap) 62%, #fff);
  cursor: pointer;
  transition: transform .04s ease, box-shadow .04s ease;
}
.kbd-key:active {
  transform: translateY(3px);
  box-shadow: inset 0 2px 5px color-mix(in srgb, var(--keycap) 66%, #000), 0 1px 2px rgba(0, 0, 0, .3);
}
.kbd-key.spacer { flex: .5 1 0; background: none; box-shadow: none; pointer-events: none; }
.kbd-key.wide { flex: 1.55 1 0; font-size: .82rem; }
.kbd-key.space { flex: 5 1 0; }
.kbd-key.go { --keycap: var(--accent); color: var(--bg-raised); text-shadow: none; }
.kbd-key svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
/* CRT themes: phosphor lettering on the caps */
html[data-theme="green"] .kbd-key, html[data-theme="amber"] .kbd-key,
html[data-theme="green"] #kbd-suggest .sug, html[data-theme="amber"] #kbd-suggest .sug { text-shadow: 0 0 6px currentColor; }
html[data-theme="green"] .kbd-key.go, html[data-theme="amber"] .kbd-key.go { text-shadow: none; }

/* floating button to summon the on-screen keyboard again after switching to
   the device keyboard */
#kbdswap {
  position: fixed; right: 12px; bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 115; display: none; width: 54px; height: 42px; border: none;
  border-radius: 11px; cursor: pointer;
  align-items: center; justify-content: center;
  --keycap: var(--accent); color: var(--bg-raised);
  background: linear-gradient(180deg, color-mix(in srgb, var(--keycap) 76%, #fff), var(--keycap) 50%, color-mix(in srgb, var(--keycap) 86%, #000));
  box-shadow: 0 2px 0 color-mix(in srgb, var(--keycap) 55%, #000), 0 5px 12px rgba(0, 0, 0, .4);
}
#kbdswap:active { transform: translateY(2px); }
body.native-kbd #kbdswap { display: flex; }
#kbdswap svg { width: 26px; height: 26px; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ============================================================
   Wide-desktop side-rail layout (>=1080px, fine pointer only).
   Mobile and narrower desktop are untouched — rails stay hidden
   and the classic bottom assist panel / map modal remain.
   ============================================================ */
.siderail { display: none; }

@media (pointer: fine) and (min-width: 1080px) {
  body.desk {
    display: grid;
    grid-template-columns: var(--rail-l, 266px) minmax(0, 1fr) var(--rail-r, 316px);
    grid-template-rows: auto auto minmax(0, 1fr);
    grid-template-areas:
      "hdr   hdr    hdr"
      "left  status right"
      "left  scroll right";
    height: 100vh;
    /* the cabinet's loose-case sag and falling stickers/screws briefly poke
       past 100vh; without this the page grows a scrollbar for a few frames
       and the whole grid jolts sideways */
    overflow: hidden;
    overflow: clip;
  }
  body.desk > header      { grid-area: hdr; }
  body.desk > #statusline { grid-area: status; }
  body.desk > #scroll     { grid-area: scroll; min-height: 0; }
  body.desk > #assist     { display: none; }          /* its controls moved into the rails */
  body.desk #assistbtn,
  body.desk #mapbtn       { display: none; }            /* always-on in the rails now */

  /* reading column kept to a comfortable measure, centred */
  body.desk #buffer {
    max-width: 64ch; margin-left: auto; margin-right: auto;
  }
  body.desk:not(.bigform) #statusline {
    padding-left:  max(.9rem, calc((100% - 64ch) / 2));
    padding-right: max(.9rem, calc((100% - 64ch) / 2));
  }

  /* rails: a scrolling body plus an absolute drag grip on the inner edge */
  body.desk .siderail {
    position: relative; display: flex; flex-direction: column;
    overflow: hidden; background: var(--bg-raised);
  }
  body.desk #railLeft  { grid-area: left;  border-right: 1px solid var(--border); }
  body.desk #railRight { grid-area: right; border-left:  1px solid var(--border); }
  body.desk .rail-body {
    flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; gap: 1.15rem;
    overflow-y: auto; overflow-x: hidden; padding: 1rem .9rem 1.4rem;
  }
  body.desk #railLeft .rail-body { overflow: hidden; }   /* the map scrolls internally */

  /* drag-to-resize grip on the inner edge of each rail */
  body.desk .rail-grip { position: absolute; top: 0; bottom: 0; width: 10px; z-index: 6; cursor: col-resize; }
  body.desk #railLeft  .rail-grip { right: 0; }
  body.desk #railRight .rail-grip { left: 0; }
  body.desk .rail-grip::before {
    content: ""; position: absolute; top: 0; bottom: 0; left: 50%; width: 2px;
    transform: translateX(-50%); background: transparent; transition: background .12s;
  }
  body.desk .rail-grip:hover::before, body.desk .rail-grip.drag::before { background: var(--accent); }

  .rl-sect h2 {
    margin: 0 0 .55rem; font-family: var(--ui); font-size: .66rem; font-weight: 700;
    letter-spacing: .17em; text-transform: uppercase; color: var(--text-dim);
  }

  /* LEFT rail = map only, filling the panel */
  .rl-mapsect { flex: 0 1 auto; display: flex; flex-direction: column; min-height: 0; }
  /* With a pre-built map the minimap sizes itself to the rail width, so the
     box hugs it rather than leaving a tall empty well underneath. */
  body.mapdef .rl-mapsect { flex: 0 0 auto; }
  body.mapdef .rl-map { flex: 0 0 auto; max-height: 46vh; }
  .rl-map {
    flex: 1 1 auto; min-height: 0; overflow: auto; position: relative;
    border: 1px solid var(--border); border-radius: 10px; background: var(--bg);
    cursor: grab;
  }
  .rl-map.grabbing { cursor: grabbing; }
  .rl-map svg { display: block; }
  .rl-map .empty { color: var(--text-dim); font-style: italic; font-size: .8rem; padding: .8rem; }
  .rl-mini {
    margin-top: .5rem; align-self: flex-start; background: none; border: none;
    color: var(--text-dim); cursor: pointer; padding: .1rem 0;
    font-family: var(--ui); font-size: .72rem; text-decoration: underline;
  }

  /* RIGHT rail: verbs/nouns/feelies scroll; the compass is a fixed footer */
  body.desk #railRight .rl-movesect {
    flex: none; padding: .85rem .9rem 1rem; border-top: 1px solid var(--border);
    background: var(--bg-raised);
  }
  body.desk #as-verbs, body.desk #as-nouns {
    display: flex; flex-wrap: wrap; gap: .38rem;
    grid-auto-flow: unset; grid-template-rows: unset;
    overflow: visible; padding: 0; justify-content: flex-start;
    -webkit-mask-image: none; mask-image: none;
  }
  /* the carried-items group takes its own full-width row and wraps inside it,
     instead of running off the side of the rail */
  body.desk .carrygroup { flex: 1 1 100%; flex-wrap: wrap; }
  /* compass inline, filling the rail width in four even columns */
  body.desk #rl-move-slot { width: 100%; }
  body.desk #as-dirs {
    position: static; display: block; width: 100%; justify-self: stretch;
    background: none; border: none; box-shadow: none; padding: 0; inset: auto;
  }
  body.desk #as-dirs .compass-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 5px; width: 100%; }
  body.desk #as-dirs .compass-grid .chip.dir { width: auto; min-width: 0; height: 40px; font-size: .74rem; }
  body.desk #as-dirs .compass-grid .chip.dir.ext { margin-left: 8px; }

  /* feelies teaser in the right rail */
  .rl-feelies {
    display: flex; align-items: center; gap: .6rem; width: 100%; text-align: left;
    background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
    padding: .6rem; cursor: pointer; color: var(--text);
  }
  .rl-fe-stack { position: relative; width: 40px; height: 50px; flex: none; }
  .rl-fe-stack i { position: absolute; inset: 0; border: 1px solid var(--border); border-radius: 3px; background: #efe6d0; }
  .rl-fe-stack i:nth-child(1){ transform: rotate(-8deg) translate(-3px, 2px); }
  .rl-fe-stack i:nth-child(2){ transform: rotate(5deg) translate(3px, -1px); background: #e7dcc2; }
  .rl-fe-lbl b { display: block; font-size: .9rem; }
  .rl-fe-lbl span { color: var(--text-dim); font-size: .74rem; }
}

/* ---- mouse hover feedback across every clickable control (mouse only) ---- */
@media (hover: hover) and (pointer: fine) {
  .chip { transition: border-color .12s, background .12s, color .12s; }
  .chip:hover { border-color: var(--accent); background: var(--btn-hover); color: var(--text); }
  .chip.dir:hover { border-color: var(--accent); color: var(--accent); background: var(--btn-hover); }
  .rl-feelies { transition: border-color .12s, background .12s; }
  .rl-feelies:hover { border-color: var(--accent); background: var(--btn-hover); }
  .rl-mini:hover { color: var(--text); }
  .rl-map:hover { border-color: var(--text-dim); }
}

/* ============================================================
   Cabinet mode — the DT-8600 terminal (desktop only). When
   body.desk.cabinet is set, the status line + transcript are
   moved into #cab-slot so the game plays inside the screen.
   ============================================================ */
#cabinet { display: none; }
#cabinetbtn { display: none; }

@media (pointer: fine) and (min-width: 1080px) {
  body.desk #cabinetbtn { display: inline-flex; }
  #cabinetbtn.active, #cabinetbtn.active:hover { background: var(--accent); color: var(--bg-raised); border-radius: 7px; }
  body.desk.cabinet #themesel { display: none; }   /* the COLOUR knob is the phosphor control in here */

  /* the terminal fills the whole centre (status + scroll rows) */
  body.desk.cabinet {
    grid-template-areas:
      "hdr  hdr  hdr"
      "left cab  right"
      "left cab  right";
  }
  body.desk.cabinet > #statusline, body.desk.cabinet > #scroll { display: none; }  /* relocated into the screen */
  body.desk.cabinet .crt-fx { display: none; }  /* the monitor supplies its own glass */

  /* --- Cabinet mode: make the side rails belong to the same machine ---------
     Dark, recessed instrument panels flanking the CRT, with phosphor-coloured
     labels and terminal-style chips, so the monitor stays the hero. --text is
     the phosphor colour in the amber/green themes cabinet mode runs in. */
  body.desk.cabinet .siderail {
    background:
      radial-gradient(120% 80% at 50% 0%, rgba(255,255,255,.02), rgba(0,0,0,0) 60%),
      linear-gradient(180deg, #100d09 0%, #0a0806 55%, #060504 100%);
    box-shadow: inset 0 0 0 1px rgba(0,0,0,.7), inset 0 2px 14px rgba(0,0,0,.75);
  }
  body.desk.cabinet #railLeft  { border-right: 1px solid color-mix(in srgb, var(--text) 20%, transparent); }
  body.desk.cabinet #railRight { border-left:  1px solid color-mix(in srgb, var(--text) 20%, transparent); }
  body.desk.cabinet .rl-sect h2 {
    color: color-mix(in srgb, var(--text) 82%, transparent);
    text-shadow: 0 0 7px color-mix(in srgb, var(--text) 45%, transparent);
  }
  body.desk.cabinet .rl-map {
    background: #060806;
    border-color: color-mix(in srgb, var(--text) 22%, transparent);
    box-shadow: inset 0 0 26px rgba(0,0,0,.85);
  }
  body.desk.cabinet #railRight .rl-movesect {
    background: transparent;
    border-top: 1px solid color-mix(in srgb, var(--text) 18%, transparent);
  }
  body.desk.cabinet .chip {
    background: rgba(0,0,0,.28);
    border-color: color-mix(in srgb, var(--text) 34%, transparent);
    box-shadow: inset 0 0 8px rgba(0,0,0,.5);
  }
  body.desk.cabinet .chip:hover { border-color: color-mix(in srgb, var(--text) 60%, transparent); }
  body.desk.cabinet .rl-feelies {
    background: rgba(0,0,0,.28);
    border-color: color-mix(in srgb, var(--text) 26%, transparent);
  }
  body.desk.cabinet .rl-mini,
  body.desk.cabinet .rl-map .empty { color: color-mix(in srgb, var(--text) 55%, transparent); }

  #cabinet { --phos: #ffb000; }
  html[data-theme="green"] #cabinet { --phos: #56ffa6; }
  html[data-theme="amber"] #cabinet { --phos: #ffb000; }

  body.desk.cabinet > #cabinet {
    grid-area: cab; display: flex; align-items: stretch; justify-content: center;
    min-width: 0; min-height: 0; padding: 12px 16px 14px;
    background: radial-gradient(ellipse 120% 90% at 50% 30%, #23241f 0%, #0e0f0d 55%, #050605 100%);
  }

  .cab-shell {
    position: relative; flex: 1 1 auto; min-width: 0; min-height: 0;
    display: flex; flex-direction: column;
    border-radius: 20px 20px 16px 16px; padding: 32px 38px 14px;   /* boxy cream case, wider chin below */
    background: linear-gradient(172deg, #dbd0a6 0%, #c9bc8b 46%, #b0a06f 100%);
    box-shadow: inset 0 3px 0 rgba(255,252,235,.46), inset 0 -4px 10px rgba(0,0,0,.26), 0 34px 80px rgba(0,0,0,.62);
  }
  .cab-grain { position: absolute; inset: 0; pointer-events: none; border-radius: inherit; overflow: hidden; }
  .cab-grain .ga { position: absolute; inset: 0; opacity: .5; background: repeating-linear-gradient(112deg, rgba(0,0,0,.05) 0, rgba(0,0,0,.05) 1px, rgba(255,255,255,.05) 2px, rgba(255,255,255,0) 3px); }
  .cab-grain .gb { position: absolute; inset: 0; background: radial-gradient(ellipse 60% 50% at 88% 92%, rgba(120,92,40,.30), rgba(0,0,0,0) 70%), radial-gradient(ellipse 45% 40% at 6% 8%, rgba(140,110,55,.22), rgba(0,0,0,0) 72%); }
  .cab-asset { position: absolute; top: 10px; right: 40px; transform: rotate(-.8deg); display: flex; flex-direction: column; gap: 3px; padding: 6px 12px 7px; width: 184px; background: linear-gradient(180deg, #f2eee2, #e3ddcb); border-radius: 3px; box-shadow: 0 1px 3px rgba(0,0,0,.35), inset 0 0 0 1px rgba(0,0,0,.08); }
  .cab-asset .bar { height: 14px; opacity: .78; background: repeating-linear-gradient(90deg, #232019 0, #232019 1px, transparent 1px, transparent 3px, #232019 3px, #232019 5px, transparent 5px, transparent 6px, #232019 6px, #232019 8px, transparent 8px, transparent 10px); }
  .cab-asset .txt { font-family: var(--ui); font-size: 9px; letter-spacing: .14em; color: #3d382c; }
  .cab-caution { position: absolute; top: 12px; left: 34px; transform: rotate(1.4deg); padding: 5px 10px 6px; background: linear-gradient(180deg, #e8c23a, #d4a91f); border-radius: 2px; box-shadow: 0 1px 3px rgba(0,0,0,.3); font-family: var(--ui); font-weight: 600; font-size: 9px; letter-spacing: .12em; color: #3a2c07; }

  /* a thick, glossy near-black bezel around the tube, then the deeply-recessed
     glass. The bezel is molded plastic, so it catches a soft sheen along its top
     and upper-left, with a bright lip at the very top edge and a dark base. */
  .cab-well { position: relative; flex: 1 1 auto; min-height: 0; border-radius: 20px; padding: 38px; display: flex;
    background:
      radial-gradient(20% 68% at -2% 44%, color-mix(in srgb, var(--phos) 42%, transparent), rgba(0,0,0,0) 64%),
      radial-gradient(58% 78% at 4% 108%, rgba(230,228,218,.15), rgba(255,255,255,0) 54%),
      radial-gradient(58% 78% at 96% 108%, rgba(230,228,218,.15), rgba(255,255,255,0) 54%),
      linear-gradient(180deg, #080705 0%, #131009 56%, #211e17 100%);
    box-shadow:
      inset 0 6px 20px rgba(0,0,0,.85),
      inset 0 0 0 1px rgba(0,0,0,.85),
      inset 0 -2px 1px rgba(255,255,255,.07); }
  /* a subtle diagonal glint the glossy bezel catches at each bottom corner: a
     thin light band at ~45deg, kept small and right in the visible corner (the
     tube covers the centre, so it must sit in the corner itself to show) */
  .cab-well::before, .cab-well::after { content: ""; position: absolute; bottom: 0; width: 165px; height: 165px;
    pointer-events: none; }
  .cab-well::before { left: 0;
    background: linear-gradient(135deg, rgba(244,242,232,0) 43.25%, rgba(244,242,232,.3) 50%, rgba(244,242,232,0) 56.75%);
    -webkit-mask: linear-gradient(45deg, transparent 5%, #000 42%, #000 100%);
    mask: linear-gradient(45deg, transparent 5%, #000 42%, #000 100%); }
  .cab-well::after { right: 0;
    background: linear-gradient(45deg, rgba(244,242,232,0) 43.25%, rgba(244,242,232,.3) 50%, rgba(244,242,232,0) 56.75%);
    -webkit-mask: linear-gradient(315deg, transparent 5%, #000 42%, #000 100%);
    mask: linear-gradient(315deg, transparent 5%, #000 42%, #000 100%); }
  .cab-tube { position: relative; z-index: 1; flex: 1 1 auto; min-height: 0; min-width: 0; overflow: hidden; border-radius: 54px / 42px; clip-path: url(#crt-curve); background: #060806; box-shadow: inset 0 0 100px rgba(0,0,0,.95); }
  /* a live, blurred reflection of the transcript spilling onto the left bezel;
     JS keeps its content + scroll position synced to the real screen, so the
     phosphor glow bands track the actual lines of text and move as you scroll */
  .cab-reflect { position: absolute; left: 0; width: 42px; z-index: 2; overflow: hidden; pointer-events: none;
    opacity: .28; mix-blend-mode: screen; filter: blur(7px) saturate(1.1);
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 48%), linear-gradient(180deg, transparent 0, #000 12%, #000 88%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image: linear-gradient(90deg, transparent 0, #000 48%), linear-gradient(180deg, transparent 0, #000 12%, #000 88%, transparent 100%);
    mask-composite: intersect; }
  .cab-reflect-inner { position: absolute; left: 0; top: 0; color: var(--phos, var(--text)); font-family: var(--mono);
    font-size: calc(var(--body-size) * var(--font-scale) * 1.32); line-height: 1.5; letter-spacing: .02em;
    padding: 22px 30px; will-change: transform; }
  .cab-reflect-inner .para { margin: 0; min-height: 1em; white-space: pre-wrap; overflow-wrap: break-word; }
  /* a much fainter mirror of the same reflection on the right bezel */
  .cab-reflect.right { left: auto; right: 0; opacity: .17; transform: scaleX(-1); }
  /* keep the text clear of the bulging corners */
  body.cabinet #cab-slot { padding: 4.2% 5.6%; }
  .cab-tube::after { content: ""; position: absolute; inset: 0; pointer-events: none; opacity: .5; z-index: 3; background: repeating-linear-gradient(180deg, rgba(0,0,0,.42) 0, rgba(0,0,0,.42) 1px, rgba(0,0,0,0) 2px, rgba(0,0,0,0) 4px); }

  .cab-slot { position: absolute; inset: 0; display: flex; flex-direction: column; filter: brightness(var(--cab-bright, 1)); }
  .cab-fx { position: absolute; inset: 0; pointer-events: none; }
  .cab-fx.aberr { background: repeating-linear-gradient(90deg, rgba(255,0,80,.05) 0, rgba(0,255,120,.04) 1px, rgba(60,120,255,.05) 2px, rgba(0,0,0,0) 3px); }
  .cab-fx.bloom { background: radial-gradient(ellipse 48% 44% at 50% 46%, color-mix(in srgb, var(--phos) 20%, transparent), rgba(0,0,0,0) 60%); mix-blend-mode: screen; }
  /* broad ambient sheen raking across the top of the convex glass */
  .cab-fx.glare { mix-blend-mode: screen; background:
    radial-gradient(ellipse 90% 60% at 32% 2%, rgba(255,255,255,.08), rgba(255,255,255,0) 62%),
    radial-gradient(ellipse 60% 46% at 80% 98%, rgba(255,255,255,.03), rgba(255,255,255,0) 62%); }
  /* the specular reflection: a soft reflected light-panel top-left, a bright
     gleam curving along the top rim (the clip-path bends it to the glass), and a
     faint gleam down the left edge — this is what reads as real glass */
  .cab-fx.gloss { mix-blend-mode: screen; background:
    radial-gradient(ellipse 52% 32% at 34% 12%, rgba(255,255,255,.06), rgba(255,255,255,0) 72%),
    radial-gradient(ellipse 130% 13% at 44% -4%, rgba(255,255,255,.09), rgba(255,255,255,0) 78%); }
  .cab-fx.vig { inset: -2px; border-radius: 54px / 42px; box-shadow: inset 0 0 130px rgba(0,0,0,.95), inset 0 0 44px rgba(0,0,0,.85); }
  .cab-fx.sweep { left: 0; right: 0; top: 0; height: 22%; background: linear-gradient(180deg, rgba(255,255,255,0), rgba(255,255,255,.05) 50%, rgba(255,255,255,0)); animation: cab-scan 7.5s linear infinite; }
  .cab-fx.flick { animation: cab-flicker 5s steps(1,end) infinite; background: rgba(0,0,0,.02); }
  @keyframes cab-scan { 0% { transform: translateY(-100%);} 100% { transform: translateY(460%);} }
  @keyframes cab-flicker { 0%,100%{opacity:.94} 8%{opacity:.88} 12%{opacity:.97} 43%{opacity:.9} 70%{opacity:.95} }

  /* the moved-in status line + transcript become the display */
  body.cabinet #cab-slot #statusline { flex: none; background: transparent; border: none; border-bottom: 1px solid rgba(255,255,255,.08); color: var(--text); text-shadow: 0 0 6px currentColor; }
  body.cabinet #cab-slot #statusline .sl-left { color: var(--text); }
  body.cabinet #cab-slot #statusline .sl-right { color: color-mix(in srgb, var(--text) 72%, transparent); }
  body.cabinet #cab-slot #scroll { flex: 1 1 auto; min-height: 0; background: transparent; border: none; padding: 22px 30px; }
  /* bigger, glowier phosphor text — reads like an 80-column DOS screen */
  body.cabinet #cab-slot #buffer {
    max-width: none; margin: 0;
    font-size: calc(var(--body-size) * var(--font-scale) * 1.32);
    line-height: 1.5; letter-spacing: .02em;
    text-shadow: 0 0 1px currentColor, 0 0 5px color-mix(in srgb, currentColor 80%, transparent), 0 0 14px color-mix(in srgb, currentColor 55%, transparent), 0 0 24px color-mix(in srgb, currentColor 35%, transparent);
  }
  body.cabinet #cab-slot #statusline { font-size: calc(var(--body-size) * var(--font-scale) * 1.15); padding-top: .55rem; padding-bottom: .55rem; }
  /* a full interactive form fills the whole CRT screen in cabinet mode too */
  body.bigform.desk.cabinet #cab-slot #statusline {
    flex: 1 1 auto; max-height: none; min-height: 0;
    display: flex; flex-direction: column; justify-content: center; align-items: center;
    font-size: calc(var(--body-size) * var(--font-scale) * 1.32);
  }
  body.bigform.desk.cabinet #cab-slot #scroll { display: none; }
  /* fat, glowing block cursor at the prompt — pure DOS. Unlike the default (a
     block only when unfocused), in the cabinet it blinks whenever the prompt is
     idle even though the field is focused, and the thin native caret is hidden. */
  body.cabinet #inputwrap .caret { width: .62ch; height: 1.15em; border-radius: 0; box-shadow: 0 0 9px var(--accent); vertical-align: -0.18em; }
  body.cabinet #inputwrap.empty .caret { display: inline-block; animation: caretblink 1.1s steps(1) infinite; }
  body.cabinet:not(.typing) #inputwrap.empty #cmd { caret-color: transparent; }
  body.cabinet #inputwrap.empty #cmd::placeholder { color: transparent; }

  /* controls along the bottom chin: BRIGHT knob, COLOUR slide-switch, ON/OFF + lamp */
  .cab-chin { flex: none; display: flex; align-items: flex-start; justify-content: flex-end; gap: 30px; margin-top: 12px; padding: 4px 12px 0; }
  .cab-brand { margin-right: auto; align-self: center; }
  .cab-brand b { font-family: var(--ui); font-weight: 600; font-size: 12px; letter-spacing: .3em; color: #4b4436; display: block; }
  .cab-brand span { font-family: var(--ui); font-size: 9px; letter-spacing: .18em; color: #6b6250; }
  .cab-knob, .cab-slider { background: none; border: none; cursor: pointer; display: flex; flex-direction: column; align-items: center; gap: 6px; padding: 0; }
  #cab-bright { cursor: grab; }
  #cab-bright:active { cursor: grabbing; }
  .cab-knob .dial { width: 30px; height: 30px; border-radius: 50%; background: conic-gradient(from 210deg, #7d7461, #cdc2a6 30%, #6f6754 70%, #b4a98e); box-shadow: inset 0 0 0 3px rgba(0,0,0,.18), 0 2px 4px rgba(0,0,0,.45); position: relative; }
  .cab-knob .dial::after { content: ""; position: absolute; left: 50%; top: 3px; width: 2px; height: 10px; margin-left: -1px; background: #3a3428; border-radius: 1px; }
  .cab-knob .l, .cab-slider .l, .cab-power .l { font-family: var(--ui); font-size: 8px; letter-spacing: .2em; color: #6b6250; }
  /* colour: a two-position slide switch (left = green, right = amber) */
  .cab-slider .track { position: relative; width: 54px; height: 16px; border-radius: 8px; background: linear-gradient(180deg, #3f3a30, #5a5346); box-shadow: inset 0 1px 3px rgba(0,0,0,.6); }
  .cab-slider .thumb { position: absolute; top: 1px; left: 1px; width: 24px; height: 14px; border-radius: 6px; background: linear-gradient(180deg, #ece6d4, #c6bca1); box-shadow: 0 1px 2px rgba(0,0,0,.5), inset 0 1px 0 rgba(255,255,255,.5); transition: left .18s ease; }
  .cab-slider.pos-amber .thumb { left: 29px; }
  .cab-power { display: flex; flex-direction: column; align-items: center; gap: 6px; }
  .cab-power .prow { display: flex; align-items: center; gap: 8px; }
  .cab-power .led { width: 10px; height: 10px; border-radius: 50%; background: var(--phos); box-shadow: 0 0 9px var(--phos), inset 0 -1px 2px rgba(0,0,0,.5); }
  .cab-power .io { width: 27px; height: 27px; border-radius: 50%; background: radial-gradient(circle at 40% 34%, #ddd3bc, #b1a78d); box-shadow: inset 0 0 0 2px rgba(0,0,0,.2), 0 2px 4px rgba(0,0,0,.45); border: none; cursor: pointer; position: relative; }
  .cab-power .io::after { content: ""; position: absolute; left: 50%; top: 5px; width: 2px; height: 9px; margin-left: -1px; background: #4b4436; border-radius: 1px; }
  .cab-power .io:hover { filter: brightness(1.06); }
}
