/*
 * Two pages. The door (index.html) is one form and nothing else; the master page (master.html)
 * has three screens: the library of a player's games, their Configuration, and a game.
 *
 * A game is a column that fills the viewport exactly once, so the NARRATION is the only thing that
 * scrolls, beside a sidebar of what the engine settled about the scene. That is deliberate: a
 * player mid-turn should never have to hunt for the input box, and the HUD and the sidebar are
 * what they check without looking away from what they are reading.
 */

/*
 * THE LOOK IS THE READER'S. Four parts of the page - the page itself, the readout, the story and
 * the controls - each have their own colours, typeface and size, and a person sets them from the
 * Configuration screen. What they chose lands here: appearance.js writes the `--page-*`,
 * `--readout-*`, `--story-*` and `--controls-*` properties on the root (and `data-story` for the
 * story's mode, `data-story-align` for how its prose meets the margin) and NOTHING ELSE. The
 * values below are the standard look (the operator's own, copied 2026-09-20), and what a part left
 * alone follows: the readout and the story's type take the page's; the controls are set in
 * Helvetica. Labels have two colours because they sit on two grounds: `muted` on
 * the part's background, `panel-text` on the boxes standing on it.
 */
:root {
  --page-bg: #14141a;
  --page-panel: #1c1c24;
  --page-text: #ffffff;
  --page-muted: #9a9284;
  --page-panel-text: var(--page-muted);
  --page-accent: #c9a227;
  --page-line: #2e2e3a;
  --page-font: Georgia, 'Iowan Old Style', 'Palatino Linotype', serif;
  /* THE READER'S OWN SIZE, and a step up from it. A browser carries the size its owner chose (the
     default is 16px; a person who reads small or reads far sets it larger), and a page that names
     pixels throws that choice away. So the standard root is a PERCENTAGE of the browser's setting -
     112.5% is 18px on the default - and every size below is in rem, so the whole page scales with
     it. A size the person chose for the page replaces this outright. */
  --page-size: 112.5%;

  --readout-bg: #40404f;
  --readout-panel: var(--page-bg);
  --readout-text: var(--page-text);
  --readout-muted: #8ea0f5;
  --readout-panel-text: var(--readout-muted);
  --readout-accent: var(--page-accent);
  --readout-line: var(--page-line);
  --readout-font: var(--page-font);
  --readout-size: 1rem;

  --controls-bg: #58614c;
  --controls-panel: var(--page-bg);
  --controls-text: var(--page-text);
  --controls-muted: var(--page-muted);
  --controls-panel-text: #d2c8b6;
  --controls-accent: var(--page-accent);
  --controls-line: var(--page-line);
  --controls-font: Helvetica, Arial, 'Liberation Sans', sans-serif;
  --controls-size: 1rem;

  /* The story is read in black on white, or in white on black (`data-story="dark"` below); the
     rest of its palette follows the mode, because markdown prose carries its own headings, quotes
     and tables and a palette drawn for one mode is unreadable in the other. */
  --story-bg: #ffffff;
  --story-panel: #f2f2f2;
  --story-text: #000000;
  --story-muted: #555555;
  --story-accent: #7c6a2a;
  --story-line: #cccccc;
  --story-player: #2b5f8f;
  --story-scheme: light;
  --story-font: var(--page-font);
  --story-size: 1rem;

  /* THE WORKING SET every rule below reads, bound here to the page's colours and bound again at
     each part's root to that part's. A derived colour (the dimmed accent) is re-derived there too,
     because a custom property is resolved where it is declared and inherited resolved. */
  --paper: var(--page-bg);
  --panel: var(--page-panel);
  --ink: var(--page-text);
  --ink-dim: var(--page-muted);
  --ink-panel: var(--page-panel-text);
  --accent: var(--page-accent);
  --panel-edge: var(--page-line);
  --accent-dim: color-mix(in srgb, var(--accent) 65%, var(--paper));
  --player: #8fb8de;
  --alert: #d98b6a;
  /* Go: the one colour that means "this can be sent". */
  --go: #2f9e5f;
  --gutter: 0.75rem;
  --radius: 4px;
  /* Tell the browser the page is dark, so its own furniture - scrollbars, form widgets - is too. */
  color-scheme: dark;
}

:root[data-story="dark"] {
  --story-bg: #000000;
  --story-panel: #141414;
  --story-text: #ffffff;
  --story-muted: #b0b0b0;
  --story-accent: #e2b53a;
  --story-line: #333333;
  --story-player: #9cc4ea;
  --story-scheme: dark;
}

* { box-sizing: border-box; }

html, body {
  height: 100%;
  margin: 0;
}

html { font-size: var(--page-size); }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--page-font);
  font-size: 1rem;
  line-height: 1.6;
}

button, input, select {
  font-family: inherit;
  font-size: 1rem;
  color: inherit;
}

/* A screen that is not the one shown is not on the page. The browser's own rule for `hidden` is
   only a default, and any `display` set below (the game is a flex column) would win over it and
   put a hidden screen's HUD and sidebar under whatever screen IS shown. */
[hidden] { display: none !important; }

/* Every button is legible on the dark page. The browser's default is grey on grey here; a button
   that means something else (a link-like one, a command chip, an option) restyles from this. */
button {
  padding: 0.45rem 0.9rem;
  background: var(--panel-edge);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
}

button:hover:not(:disabled) { border-color: var(--accent-dim); }
button:disabled { opacity: 0.4; cursor: default; }

/* And every box a person types or picks in, wherever it stands: a rung's model and key, the
   profile on a game's row. A labelled field sets its own width below. */
input:not([type="checkbox"]), select {
  padding: 0.45rem 0.6rem;
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
}

input:focus, select:focus { outline: none; border-color: var(--accent-dim); }

/* ---------------------------------------------------------------- the door */

.door {
  min-height: 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--gutter);
}

.door-card {
  width: min(26rem, 100%);
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  padding: 1.75rem;
}

.door-card h1 {
  margin: 0 0 1.25rem;
  font-size: 1.35rem;
  font-weight: normal;
  letter-spacing: 0.02em;
}

.field {
  display: block;
  margin-bottom: 1rem;
}

.field > span {
  display: block;
  margin-bottom: 0.3rem;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-dim);
}

.field input, .field select {
  width: 100%;
  padding: 0.55rem 0.7rem;
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
}

.field-group { border-top: 1px solid var(--panel-edge); padding-top: 1rem; }

.hint { margin: 0 0 0.75rem; color: var(--ink-dim); font-size: 0.9rem; }

.door-note {
  min-height: 1.4em;
  margin: 0 0 1rem;
  font-size: 0.9rem;
  color: var(--alert);
}

.door-note.calm { color: var(--ink-dim); }

#door-submit, #send {
  width: 100%;
  padding: 0.6rem;
  background: var(--accent-dim);
  color: var(--paper);
  border: none;
  border-radius: var(--radius);
  cursor: pointer;
  letter-spacing: 0.05em;
}

/* Send is GREEN while a line can be sent and dim while a turn is running. */
#send { width: auto; padding: 0.55rem 1.4rem; background: var(--go); color: #ffffff; }
#send:disabled { background: var(--panel-edge); color: var(--ink-dim); opacity: 0.6; cursor: default; }

#door-submit:disabled, .commands button:disabled {
  opacity: 0.4;
  cursor: default;
}

/* ---------------------------------------------------------------- the game */

.game {
  height: 100%;
  display: flex;
  flex-direction: column;
}

/* --- HUD: the boxes the world composed, the wide one across the top, the rest down the left --- */

/* THE READOUT is one part: the HUD, the panel of facts down the left, and the scene on the right
   are coloured and set together, in their own typeface and size. Sizes inside are in em, so the
   part scales with its own size and not the page's. */
.hud, .hud-panel, .sidebar {
  --paper: var(--readout-panel);
  --panel: var(--readout-bg);
  --ink: var(--readout-text);
  --ink-dim: var(--readout-muted);
  --ink-panel: var(--readout-panel-text);
  --accent: var(--readout-accent);
  --panel-edge: var(--readout-line);
  --accent-dim: color-mix(in srgb, var(--accent) 65%, var(--paper));
  background: var(--panel);
  color: var(--ink);
  font-family: var(--readout-font);
  font-size: var(--readout-size);
}

.hud {
  flex: none;
  padding: var(--gutter);
  border-bottom: 1px solid var(--panel-edge);
}


/* The one row-of-its-own box: the location, a path with no bound on its length, so it may wrap. */
.hud-banner { margin-top: 0.35rem; }
/* Unlabelled and no taller than its line of text. */
.hud-banner .hud-box { padding: 0.15rem 0.55rem; }
.hud-banner .hud-value { white-space: normal; overflow: visible; }

/* A nested reading - a containment path, outermost first: the enclosing places dim, the WITHIN
   mark between each, and the place the player actually stands in bold. */
.hud-nest { color: var(--ink-dim); }
.hud-nest strong { color: var(--ink); font-weight: 600; }
.hud-nest .hud-in { margin: 0 0.35em; color: var(--accent); }

/* The facts down the left of the story, one box under another, in the order the world reads them. */
.hud-panel {
  flex: 0 0 13rem;
  overflow-y: auto;
  padding: 0.75rem;
  border-right: 1px solid var(--panel-edge);
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}

.hud-box {
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  padding: 0.35rem 0.55rem;
  min-width: 0;
}

/* On its box, so the BOX label colour. */
.hud-label {
  display: block;
  font-size: 0.65em;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-panel);
}

.hud-value {
  display: block;
  font-size: 0.95em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A reading with several fields in it: the heading, then a cell per field. The cells wrap, so a
   ship with five readings fills the panel's width down the left of the story and runs across it on
   a narrow screen - never one line that runs off the end. The super box is only the frame: its own
   paper and border would draw a box around a box, so it gives them up to the cells inside. */
.hud-box.hud-super {
  background: none;
  border: none;
  padding: 0;
}

.hud-fields {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  margin-top: 0.2rem;
}

.hud-fields .hud-box {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.2rem 0.45rem;
}

.hud-fields .hud-value { font-size: 0.9em; }

/* --- the narration window: the only thing on the page that scrolls --- */

/* THE STORY is one part, on black or on white; its palette is the mode's (see :root) and its
   typeface and size are its own. Sizes inside are in em for the same reason as the readout's.
   Named twice because the Configuration's preview stands a bare `.narration` on the page with no
   `.story` around it, and that preview IS the page. */
.story, .narration {
  --paper: var(--story-bg);
  --panel: var(--story-panel);
  --ink: var(--story-text);
  --ink-dim: var(--story-muted);
  --accent: var(--story-accent);
  --panel-edge: var(--story-line);
  --player: var(--story-player);
  --accent-dim: color-mix(in srgb, var(--accent) 65%, var(--paper));
  background: var(--paper);
  color: var(--ink);
  color-scheme: var(--story-scheme);
  font-family: var(--story-font);
  font-size: var(--story-size);
}

/* THE SCROLL IS THE WRAPPER'S. The marks beside each beat are laid over the story as an absolutely
   positioned layer, and a layer inside the scrolling box scrolls with its content for nothing - no
   listener, no transform, and the wheel over a mark still turns the story. `position: relative` is
   what the layer is measured against. */
.story {
  position: relative;
  flex: 1 1 auto;
  overflow-y: auto;
  overscroll-behavior: contain;
  /* WHAT THE READING COLUMN GIVES UP on the left for the marks. Both sides, so the column stays
     centred in what is left. Reserved rather than borrowed from the blank band each side of the
     column: that band is `(C - 46rem) / 4` and on a 1280-wide window it is under 2rem, so a mark
     hung in it would sit on the prose.

     The band holds TWO things, one behind the other: the corner the fold over all of them is
     pinned in, and then the marks, hard against the reading column. `--rail-lead` is that corner,
     and every row is padded by it, so a mark long enough to fill its band - a command's name - is
     cut at the corner rather than sliding under the switch when the story scrolls it past. */
  --rail-lead: 2.4rem;
  --rail: calc(3.4rem + var(--rail-lead));
}

.narration { padding: 1.25rem calc(var(--gutter) + var(--rail)); }

/* Focusable so the keyboard can scroll it; the ring shows only for a keyboard, and quietly. */
.story:focus { outline: none; }
.story:focus-visible { outline: 1px solid var(--accent-dim); outline-offset: -1px; }

/* THE READING COLUMN, and the band of paper each side of it. A measure of 46rem is what prose
   wants; the rest of a wide window would stand empty, and the user asked for HALF that band. So
   the cap is half the leftover added back: on a container of C the card is (C + 46rem) / 2 wide,
   leaving (C - 46rem) / 4 each side - exactly half the margin a flat 46rem left. A window narrower
   than the measure is unchanged: the cap is then wider than C and the card fills it. */
.beat {
  max-width: calc(50% + 23rem);
  margin: 0 auto 2rem;
}

.beat-action {
  margin: 0 0 0.9rem;
  color: var(--player);
  font-style: italic;
}

.beat-action::before { content: '> '; color: var(--ink-dim); font-style: normal; }

/* --- the marks beside a beat: on the story to look at, never part of it to select --- */

/* The layer. Inside the scroller, so it scrolls with the story; `pointer-events: none` so the
   blank of it is the story underneath and a drag across it selects prose. `user-select: none` is a
   belt and not the mechanism - the guarantee is that nothing here is a descendant of #narration. */
.beat-rail {
  position: absolute;
  inset: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}

/* One row per card, put at that card's own top and stretched to that card's left edge, so the
   marks sit hard against the reading column however wide the window is. */
.beat-rail-row {
  position: absolute;
  left: 0;
  width: var(--rail-w, 0px);
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: 0.15rem;
  /* The corner at the head of the band belongs to the fold over all of them, whatever row the
     scroll happens to have brought level with it. */
  padding-left: var(--rail-lead);
  padding-right: 0.4rem;
  pointer-events: auto;
}

.beat-mark, .beat-fold {
  padding: 0.05rem 0.3rem;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  color: var(--ink-dim);
  font-family: inherit;
  font-size: 0.72em;
  line-height: 1.4;
  cursor: pointer;
}

.beat-mark:hover, .beat-fold:hover { color: var(--ink); border-color: var(--accent-dim); }
.beat-mark:focus-visible, .beat-fold:focus-visible { outline: 1px solid var(--accent-dim); }

/* A command's mark carries its name, which is longer than a number. The band it stands in is
   whatever the reading column left, so a long name is cut rather than let out over the readout;
   the whole of it is on the mark's title and in the panel behind it. */
.beat-mark {
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.beat-mark[data-command] { font-size: 0.66em; }

/* The fold keys its caret off the one attribute, as the commands panel does. */
.beat-fold { padding-left: 0.15rem; padding-right: 0.15rem; }
.beat-fold::before { content: '\25be'; }
.beat-fold[aria-expanded="false"]::before { content: '\25b8'; }

/* THE FOLD OVER ALL OF THEM. The same caret every beat carries, drawn larger because it governs
   the lot, and held at the head of the column while the story scrolls under it - a beat fifty
   turns down is reached by one click without scrolling back up for it.

   The perch is sticky and the button inside it absolute, so the perch takes no height and the
   prose begins exactly where it did. It is inside the scroller and OUTSIDE #narration, on the
   same ground as the rail: a selection dragged through the story cannot take it, because it is
   not a descendant of what is being selected. */
.beat-fold-all-perch {
  position: sticky;
  top: 0;
  z-index: 2;
  height: 0;
  user-select: none;
  -webkit-user-select: none;
}

.beat-fold-all {
  position: absolute;
  top: 0.55rem;
  left: 0.5rem;
  /* Half again the size of a beat's own caret: the same mark, read as the one above them all. */
  font-size: 1.1em;
  line-height: 1;
  padding: 0.15rem 0.35rem;
}

/* FOLDED. Everything but the line that caused the beat goes, and the stub says what went - a beat
   with no line of its own (an opening beat, a command's answer) would otherwise leave a card with
   nothing in it and the rows above and below would close up over it. */
.beat-stub { display: none; }
.beat[data-folded] > .beat-stub {
  display: block;
  color: var(--ink-dim);
  font-size: 0.85em;
  font-style: italic;
}
.beat[data-folded] > :not(.beat-action):not(.beat-stub) { display: none; }
.beat[data-folded] { margin-bottom: 0.6rem; }

/* WHAT THE UNDO LEFT BEHIND. The beats after the one the reader went back to are still what
   happened, so they are not taken away - they are set aside, and folded, so the story reads on
   from where HEAD now stands. */
.beat[data-superseded] { opacity: 0.45; }
.beat[data-superseded] .beat-action { text-decoration: line-through; }

/* --- what one beat offers --- */

/* A menu, not a modal: opened with showModal() for the Escape key and the light dismiss that come
   with it, but the backdrop is clear so the beat it is about stays lit behind it. */
.beat-menu {
  position: fixed;
  margin: 0;
  max-width: min(24rem, 92vw);
  padding: 0.7rem 0.85rem;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
  font-size: 0.9rem;
}

.beat-menu::backdrop { background: transparent; }
.beat-menu h2 { margin: 0 0 0.4rem; font-size: 1em; color: var(--accent); }
.beat-menu .beat-menu-line { margin: 0 0 0.6rem; color: var(--ink-dim); font-style: italic; overflow-wrap: anywhere; }
.beat-menu .beat-menu-acts { display: flex; flex-wrap: wrap; gap: 0.3rem; align-items: center; }
.beat-menu input[type="text"] { min-width: 7rem; flex: 1 1 7rem; }

/* DRESSED AS THE COMMAND CHIPS. What this panel offers is the same kind of thing the commands
   panel offers - a small thing to press, beside the words it acts on - so Copy, Undo and Save
   are cut to the size of /help and its fellows rather than standing as full-dress buttons. The
   size is in `rem` where the chips' is in `em`, because the panel around these sets a smaller
   text than the story does and a chip read as 0.8 OF THAT comes out smaller than the /help it is
   meant to match. */
.beat-menu .beat-menu-acts button {
  padding: 0.25rem 0.6rem;
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink-panel);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.beat-menu .beat-menu-acts button:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-dim); }
.beat-menu .beat-menu-look { margin-top: 0.6rem; border-top: 1px solid var(--panel-edge); padding-top: 0.5rem; }
.beat-menu .beat-menu-note { margin: 0.4rem 0 0; color: var(--ink-dim); font-size: 0.85em; min-height: 1.2em; }

.beat-prose p:first-child { margin-top: 0; }
.beat-prose p { margin: 0 0 1rem; }

/* The prose set to both margins: the standard, so a reader who has chosen nothing reads a story
   with two straight edges. Justified lines need hyphenation to keep the spacing even, and the
   page is English (`lang="en"`), so the browser's own English hyphenation is asked for. A reader
   who asked for ragged right (`align: ragged` on their file) gets lines that end where the words
   do. Only running prose is set this way; a notice, a console block and a table stay as they are. */
.beat-prose p,
.beat-prose li,
.beat-prose blockquote {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}
:root[data-story-align="ragged"] .beat-prose p,
:root[data-story-align="ragged"] .beat-prose li,
:root[data-story-align="ragged"] .beat-prose blockquote {
  text-align: left;
  hyphens: manual;
  -webkit-hyphens: manual;
}
.beat-prose h1, .beat-prose h2, .beat-prose h3,
.beat-prose h4, .beat-prose h5, .beat-prose h6 {
  font-size: 1.05em;
  letter-spacing: 0.04em;
  color: var(--accent);
}
.beat-prose blockquote {
  margin: 0 0 1rem;
  padding-left: 0.9rem;
  border-left: 2px solid var(--panel-edge);
  color: var(--ink-dim);
}
.beat-prose table { border-collapse: collapse; margin-bottom: 1rem; }
.beat-prose th, .beat-prose td {
  border: 1px solid var(--panel-edge);
  padding: 0.3rem 0.6rem;
  text-align: left;
}
.beat-prose code { font-family: ui-monospace, 'DejaVu Sans Mono', monospace; font-size: 0.9em; }

/* Preformatted text: a meta-command's output, or a genre's own stage directions. It is formatted
   for a fixed-width column and must stay in one.

   THE TYPEFACE IS A PROPERTY, and the declarations below stay here. A reader may set a typeface
   for every command's answers and another for one command's - a timetable is read differently
   from a list of branches - and that arrives as a generated rule rebinding these two properties
   for `[data-command="..."]`, never as a declaration of its own. So the stylesheet keeps owning
   what a console block IS, and the choice only says what it is set in. Longhands rather than the
   `font:` shorthand, which cannot carry a property; what the shorthand used to reset is restored
   by hand under it.

   The standard is the `var()` FALLBACK, never a declaration here: the reader's choice for every
   command's answers lands on the root and is inherited, and one declared here would shadow it. A
   rule for ONE command declares the property on the element, which beats the inherited value. */
.beat-console {
  margin: 0 0 1rem;
  padding: 0.7rem 0.9rem;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  font-family: var(--console-font, ui-monospace, 'DejaVu Sans Mono', monospace);
  font-size: var(--console-size, 0.85em);
  font-weight: normal;
  font-style: normal;
  font-variant: normal;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-x: auto;
}

.beat-notice, .beat-system {
  margin: 0 0 0.5rem;
  padding: 0.4rem 0.7rem;
  border-left: 2px solid var(--accent-dim);
  font-size: 0.9em;
  color: var(--ink-dim);
  /* An engine statement keeps its line breaks, and a rule of eighty equals signs - one unbreakable
     word - wraps rather than scrolling the whole story sideways. */
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* Not fiction: a rollback, an unreachable upstream, a death notice. */
.beat-system { border-left-color: var(--alert); color: var(--alert); }

/* The forty seconds between the prose being final and the HUD settling. */
.beat.settling .beat-prose::after {
  content: ' …settling';
  color: var(--ink-dim);
  font-style: italic;
  font-size: 0.85em;
}

.beat-error {
  color: var(--alert);
  font-size: 0.9em;
}

/* THE HUD IN THE FLOW: the rows the console prints at the END of a turn - the HUD's, the status
   line, the scene digest - bold, one under another with no gap, as the terminal sets them. On the
   console they close every turn, so the reader sees where the world stood when the beat ended and
   where one turn stops and the next starts. Every beat carries them; the Show in Flow switch (the
   story's `readout` choice on the reader's file) shows them under every beat, past and future, or
   takes them all out again. The boxes and the scene panels around the story are untouched either
   way: the switch is about the story window only. */
.beat-readout {
  display: none;
  margin: 1rem 0 0;
  font-weight: 600;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
:root[data-story-readout="flow"] .beat-readout { display: block; }

/* --- the controls --- */

/* THE CONTROLS are one part: the command chips, the world's question and the line being typed,
   in their own colours, typeface and size; sizes inside are in em. */
.controls {
  --paper: var(--controls-panel);
  --panel: var(--controls-bg);
  --ink: var(--controls-text);
  --ink-dim: var(--controls-muted);
  --ink-panel: var(--controls-panel-text);
  --accent: var(--controls-accent);
  --panel-edge: var(--controls-line);
  --accent-dim: color-mix(in srgb, var(--accent) 65%, var(--paper));
  flex: none;
  padding: var(--gutter);
  border-top: 1px solid var(--panel-edge);
  background: var(--panel);
  color: var(--ink);
  font-family: var(--controls-font);
  font-size: var(--controls-size);
}

.controls button, .controls input { font-size: 1em; }
.controls .linkish { font-size: 0.8em; }

/* The commands FOLD. Open is where a game is entered; the switch above them takes them away and
   brings them back, and nothing of that is kept - it is not a look the reader has chosen, it is
   what they are doing while this page is up. */
.command-panel { margin-bottom: 0.6rem; }

/* A fold, not a link: the caret says what it does, so it is not underlined. Named with `.linkish`
   too, because that rule stands further down the sheet and would otherwise win. */
.command-toggle.linkish {
  display: block;
  margin-bottom: 0.4rem;
  color: var(--ink-panel);
  font-family: inherit;
  letter-spacing: 0.04em;
  text-decoration: none;
}

.command-toggle::before { content: '\25be\00a0'; color: var(--accent-dim); }
.command-toggle[aria-expanded="false"]::before { content: '\25b8\00a0'; }
.command-toggle.linkish:hover { color: var(--ink); }

.commands {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

/* `hidden` says nothing to a flex box unless the stylesheet says it does. */
.commands[hidden] { display: none; }

.commands button {
  padding: 0.25rem 0.6rem;
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink-panel);
  font-size: 0.8em;
  letter-spacing: 0.04em;
  cursor: pointer;
}

.commands button:hover:not(:disabled) { color: var(--ink); border-color: var(--accent-dim); }

/* A command that cannot be sent by a click: it fills the box and waits for an argument. */
.commands button.needs-argument::after { content: '…'; color: var(--accent-dim); }

/* The question a world is asking of its own, and the options that answer it. Capped and scrolled
   because a menu can run to a dozen builds and must not push the input box off the screen. */
.question {
  margin-bottom: 0.6rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--accent-dim);
  border-radius: var(--radius);
  color: var(--accent);
  font-size: 0.95em;
  max-height: 45vh;
  overflow-y: auto;
}

.question-preamble { color: var(--ink); margin-bottom: 0.2rem; }
.question-ask { font-weight: 600; }

.question-options {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.6rem;
}

/* One option. Wide enough to read its own numbers, narrow enough that several sit side by side. */
.question-options .choice {
  flex: 1 1 14rem;
  max-width: 100%;
  text-align: left;
  padding: 0.5rem 0.65rem;
  background: var(--panel);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}

.question-options .choice:hover:not(:disabled) { border-color: var(--accent); }
.question-options .choice:disabled { opacity: 0.5; cursor: default; }

/* What an empty answer would have taken. */
.question-options .choice.preferred { border-color: var(--accent-dim); }

.choice-name { font-weight: 600; color: var(--accent); }

.choice-detail {
  color: var(--ink-panel);
  font-size: 0.82em;
  line-height: 1.45;
  margin-top: 0.15rem;
}

.controls .question-back { margin-top: 0.6rem; font-size: 0.85em; }

.action {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.prompt { color: var(--ink-dim); }

/* The line is typed in the STORY's colours: it is the next line of the story, and a box that is
   dark under a white page of prose (or the reverse) reads as belonging to something else. */
.action input {
  flex: 1 1 auto;
  min-width: 0;
  padding: 0.55rem 0.7rem;
  background: var(--story-bg);
  color: var(--story-text);
  border: 1px solid var(--story-line);
  border-radius: var(--radius);
  color-scheme: var(--story-scheme);
}

.action input::placeholder { color: var(--story-muted); opacity: 1; }


/* A turn is minutes long and its phase text can sit unchanged for a while, so the line carries
   something that is visibly still moving. Marked by the page whenever a turn is running. */
.progress.working::before {
  content: '';
  display: inline-block;
  width: 0.7em;
  height: 0.7em;
  margin-right: 0.5em;
  vertical-align: -0.05em;
  border: 2px solid var(--panel-edge);
  border-top-color: var(--ink);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

/* Honour a reader who has asked for less movement: the text still says what is happening. */
@media (prefers-reduced-motion: reduce) {
  .progress.working::before { animation-duration: 3s; }
}

/* The engine a game runs on: a fact the server decides at each turn, shown beside the title. */
.engine-chip {
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink-dim);
  font-size: 0.7em;
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
}

/* The player's line, already on screen while the engine is still opening the world. */
.beat.pending .beat-action { opacity: 0.65; }

@media (max-width: 32rem) {
  .beat { margin-bottom: 1.5rem; }
}


/* ============================================================ the library */

.library {
  max-width: 46rem;
  margin: 0 auto;
  padding: 2rem 1rem;
}

.library-head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  border-bottom: 1px solid var(--panel-edge);
  padding-bottom: 0.5rem;
}

.library-head h1 { flex: 1 1 auto; margin: 0; font-size: 1.2rem; }

.slots { color: var(--ink-dim); font-size: 0.8rem; }

.linkish {
  padding: 0;
  background: none;
  border: none;
  color: var(--ink-dim);
  font: inherit;
  font-size: 0.8rem;
  cursor: pointer;
  text-decoration: underline;
}

.session-list, .realm-list { list-style: none; margin: 1rem 0; padding: 0; }

/* A row in a list of things the person holds: the name, the facts about it under the name, and the
   buttons at the end. A game and a world of their own are the same row with one fewer button. */
.session, .realm {
  display: grid;
  gap: 0.15rem 0.5rem;
  align-items: center;
  padding: 0.6rem 0.7rem;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  margin-bottom: 0.5rem;
}
.session { grid-template-columns: 1fr auto auto; grid-template-areas: "name open delete" "meta open delete"; }
.realm { grid-template-columns: 1fr auto; grid-template-areas: "name delete" "meta delete"; }

.session.live { border-color: var(--accent); }
.session-name, .realm-name { grid-area: name; font-weight: 600; }
.session-meta, .realm-meta { grid-area: meta; color: var(--ink-panel); font-size: 0.75rem; }
.session button { grid-area: open; }
.session button.danger, .realm button.danger { grid-area: delete; color: var(--alert); }

.new-game { border-top: 1px solid var(--panel-edge); padding-top: 0.75rem; }
.new-game summary { cursor: pointer; color: var(--ink-dim); }
.new-game form { margin-top: 0.75rem; }
.field-row { display: flex; gap: 0.75rem; }
.field-row .field { flex: 1 1 0; }

/* ------------------------------------------------------ the person's own worlds */

.steps { margin: 0 0 0.75rem 1.1rem; padding: 0; color: var(--ink-dim); font-size: 0.9rem; }
.steps li { margin-bottom: 0.3rem; }
.steps b { color: var(--ink); font-weight: 600; }

.realm-dialogue { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; margin: 0 0 0.5rem; }

/* The dialogue is a FILE, so what fetches it is a link and the browser saves it with no script at
   all. Dressed as the button beside it, because that is what it does. */
.realm-dialogue a {
  padding: 0.45rem 0.9rem;
  background: var(--panel-edge);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
  text-decoration: none;
}
.realm-dialogue a:hover { border-color: var(--accent-dim); }

/* THE FILE BOX IS THE BROWSER'S, dressed to match the controls around it. In a rule of its OWN and
   never added to the button rule's selector list: a browser that does not know
   ::file-selector-button throws away the WHOLE list it appears in, which would leave every button
   on the page unstyled. The box itself already takes the page's own input rule. */
input[type="file"] { padding: 0.35rem 0.5rem; }
input[type="file"]::file-selector-button {
  margin-right: 0.6rem;
  padding: 0.35rem 0.8rem;
  font: inherit;
  background: var(--panel-edge);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  color: var(--ink);
  cursor: pointer;
}
input[type="file"]:hover::file-selector-button { border-color: var(--accent-dim); }

.realm-head { margin: 1rem 0 0.4rem; font-size: 1rem; font-weight: 600; }
.realm-head .slots { margin-left: 0.5rem; }
.realm-empty { color: var(--ink-dim); font-size: 0.9rem; }

/* A reader's refusal can be several faults at once, and a list run together into one sentence is
   not a reason anybody can act on. */
.door-note.detail { white-space: pre-line; }

/* ============================================== the scene beside the story */

.stage { display: flex; flex: 1 1 auto; min-height: 0; }
.stage .story { flex: 1 1 auto; min-width: 0; }

/* The switch at the head of the column keeps its place while the panels under it scroll. */
.sidebar {
  flex: 0 0 14rem;
  min-height: 0;
  padding: 0.75rem;
  border-left: 1px solid var(--panel-edge);
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}

.scene-panels { flex: 1 1 auto; min-height: 0; overflow-y: auto; }

/* The Show in Flow switch, at the top of the scene. Sized in em so it scales with the readout. */
#flow { flex: none; padding: 0.3em 0.8em; font-size: 0.8em; }
/* On. */
#flow[aria-pressed="true"] { background: var(--accent-dim); border-color: var(--accent); color: var(--paper); }

.panel { margin-bottom: 0.9rem; }
.panel h2 {
  margin: 0 0 0.3rem;
  font-size: 0.7em;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-dim);
}

.chips { list-style: none; margin: 0; padding: 0; }
.chips li {
  display: inline-block;
  margin: 0 0.25rem 0.25rem 0;
  padding: 0.1rem 0.4rem;
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  font-size: 0.75em;
}

/* THE TOP ROW IS THREE PLACES, and the middle one is the page's centre however wide the two ends
   grow: the button at the left, what the engine is doing in the centre, the game's name and its
   engine at the right. A flex row would centre the progress between its neighbours instead - which
   is not the centre of the page - and would shift it every time the phase text changed length, so
   the row is a grid whose outer columns take equal room. */
.hud-top {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: baseline;
  gap: 0.75rem;
}

.hud-top > #back { justify-self: start; }
.hud-top-end { justify-self: end; display: flex; align-items: baseline; gap: 0.75rem; }
.game-title { color: var(--ink-dim); font-size: 0.8em; }

/* What the turn is doing, and after it what the page has to say - the roll the turn stopped to
   settle, or a refusal - centred at the top of the page. THE PAGE SAYS NOTHING AT ITS FOOT: a turn
   runs for minutes with the reader's eye on this row, and a line under the typed one was read late
   or not at all. They stand on the readout's ground and are set in its type, as the rest of this
   row is; both are empty between beats, and the row they ride is there either way, so nothing
   moves when they fill. The note keeps the progress's line while there is room for it and takes
   the next when there is not, rather than pushing the ends of the row off the page. */
.hud-top-middle {
  justify-self: center;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem 0.75rem;
  min-width: 0;
}

/* An empty line is not a line: a gap held open beside it would push the other off centre. */
.hud-top-middle > :empty { display: none; }

.progress, .notes {
  min-width: 0;
  text-align: center;
  font-size: 0.8em;
  color: var(--ink-dim);
}

/* ================================================================ the guide */

/* The engine's own /help, as it printed it: preformatted, in a dialog rather than in the story. */
.guide {
  max-width: min(60rem, 92vw);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  background: var(--paper);
  color: var(--ink);
}

/* THE HELP TABLE IS A COMMAND'S ANSWER, shown in a dialog rather than in the story because it is
   not a beat - so it is set in what the reader chose for a command's answer, like every other one.
   It takes no typeface of its OWN, for the same reason: the list of commands offers no `/help`
   row, because nothing it could set is in the story to be set. */
.guide pre {
  max-height: 65vh;
  overflow: auto;
  margin: 0.75rem 0;
  font-family: var(--console-font, ui-monospace, 'DejaVu Sans Mono', monospace);
  font-size: var(--console-size, 0.85rem);
  line-height: 1.5;
  white-space: pre-wrap;
}

@media (max-width: 52rem) {
  .stage { flex-direction: column; }
  /* Less room to give: the marks get smaller rather than the reading column getting narrower. The
     band is not 2.4rem, as it was when the marks were all that stood in it: the fold over all of
     them is pinned to the left of the same band, and a band that only fitted the marks would have
     the two sitting on each other at the top of the story. What it costs the reading column at
     this width is a character. */
  .story { --rail-lead: 2.15rem; --rail: calc(2.4rem + var(--rail-lead)); }
  .beat-mark, .beat-fold { font-size: 0.62em; padding: 0.05rem 0.2rem; }
  /* Still the larger of the two, on a band that has less to give. */
  .beat-fold-all { font-size: 0.95em; padding: 0.1rem 0.25rem; }
  .sidebar { flex: 0 0 auto; border-left: none; border-top: 1px solid var(--panel-edge); }
  /* Nothing left to scroll against on a narrow screen: the column runs down the page. */
  .scene-panels { overflow-y: visible; }
  /* On a narrow screen the readout runs across the top as a wrapping row of boxes. */
  .hud-panel { flex: 0 0 auto; flex-direction: row; flex-wrap: wrap; border-right: none;
               border-bottom: 1px solid var(--panel-edge); }
  .hud-panel .hud-box { flex: 1 1 8rem; }
  /* A reading made of fields is several boxes, so it takes the whole row rather than being squeezed
     into one box's width beside them. */
  .hud-panel > .hud-super { flex: 1 1 100%; }
  /* No room for three places across a narrow screen: the ends keep the top row and the progress
     takes a row of its own under them, still centred on the page. That row keeps its line's height
     whether or not there is anything in it - an empty one that closed up would drop the whole page
     by a line every time a turn ended. */
  .hud-top { grid-template-columns: auto 1fr; }
  .hud-top > #back { grid-column: 1; grid-row: 1; }
  .hud-top-end { grid-column: 2; grid-row: 1; }
  .hud-top-middle { grid-column: 1 / -1; grid-row: 2; min-height: 1.5em; }
}

/* Options a typed answer refers to: shown for reference, never clickable. */
.question-options .choice.reference { cursor: default; background: transparent; }

/* --------------------------------------------------------------- configuration */
/* The parts of a person's file: the password, the model profiles, then the look of the page. */
.config-part { margin-top: 1.25rem; padding-top: 1rem; border-top: 1px solid var(--panel-edge); }
.config-part > h2 { margin: 0 0 0.6rem; font-size: 1rem; letter-spacing: 0.06em; }
/* The small buttons in a header: to the Configuration, and back to the games from it or from a game. */
#config-open, #config-back, #back { padding: 0.3rem 0.8rem; font-size: 0.85rem; }

/* ------------------------------------------------------------- model profiles */
/* A field with its buttons on the same row: the profile picker, and the file box that takes a world. */
.profile-bar, .realm-upload {
  display: flex; flex-wrap: wrap; gap: 0.75rem; align-items: flex-end; margin-bottom: 0.75rem;
}
.profile-bar .field { flex: 1 1 14rem; margin: 0; }
.realm-upload .field { flex: 1 1 16rem; margin: 0; }
.levels { display: grid; gap: 1rem; margin: 1rem 0; }
.level { border: 1px solid var(--panel-edge); border-radius: 0.5rem; padding: 0.75rem 1rem; }
.level h2 { margin: 0 0 0.25rem; font-size: 1rem; letter-spacing: 0.08em; }
.rungs { list-style: none; margin: 0.5rem 0; padding: 0; display: grid; gap: 0.5rem; }
.rung { border-left: 3px solid var(--accent); padding-left: 0.75rem; display: grid; gap: 0.35rem; }
.rung-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.rung-row select { flex: 1 1 12rem; }
.rung-row input { flex: 2 1 14rem; }
.rung-row .tag { font-size: 0.8rem; color: var(--ink-panel); min-width: 4.5rem; }
.rung-row button { padding: 0.2rem 0.55rem; font-size: 0.85rem; }
.rung-row .linkish { padding: 0; }
.session-profile { font-size: 0.85rem; }

/* ---------------------------------------------------------------- appearance */
/* One fieldset per part of the page: swatches in a row, then typeface and size. A swatch is the
   browser's own colour picker - the person sees a colour, never a code. */
.look { margin: 0 0 1rem; padding: 0.75rem 1rem; border: 1px solid var(--panel-edge); border-radius: 0.5rem; }
.look legend { padding: 0 0.4rem; font-size: 0.9rem; letter-spacing: 0.06em; }
/* Inside a bordered box, so the BOX label colour, as are the hints in a profile's level boxes. */
.look .hint, .level .hint { color: var(--ink-panel); }
.look .hint { margin-bottom: 0.6rem; }
.look-row { display: flex; flex-wrap: wrap; gap: 0.75rem 1rem; align-items: flex-end; margin-bottom: 0.6rem; }
.look-row .field { flex: 1 1 11rem; margin: 0; }
.look-follow { flex: 0 0 auto; margin-bottom: 0.6rem; }

/* ONE COMMAND AT A TIME. Twenty-odd rows, so the list scrolls inside its own box rather than
   pushing the rest of the Configuration off the screen. The same row is built into the panel on a
   beat, where it stands alone and needs none of this. */
.command-looks { max-height: 22rem; overflow-y: auto; padding-right: 0.4rem; }
.command-look { align-items: flex-end; gap: 0.5rem 0.75rem; margin-bottom: 0.5rem; }
.command-look-name {
  flex: 0 0 7rem;
  padding-bottom: 0.4rem;
  color: var(--ink-panel);
  font-family: ui-monospace, 'DejaVu Sans Mono', monospace;
  font-size: 0.85rem;
}
.command-look .field { flex: 1 1 9rem; }

/* The two words are said ONCE, at the head of the list. Each row's own captions are taken out of
   sight and put on the select as its name, so a row still says what it is to a reader who cannot
   see the column it is under. The row standing alone in the panel on a beat keeps them. */
.command-looks .command-look .field > span { display: none; }
.command-look-head { gap: 0.5rem 0.75rem; margin-bottom: 0.2rem; }
.command-look-head > span { color: var(--ink-panel); font-size: 0.7rem;
                            letter-spacing: 0.06em; text-transform: uppercase; }
.command-look-head > span:first-child { flex: 0 0 7rem; }
.command-look-head > span:not(:first-child) { flex: 1 1 9rem; }

.swatch { display: flex; flex-direction: column; align-items: center; gap: 0.25rem; }
.swatch span { font-size: 0.7rem; letter-spacing: 0.06em; text-transform: uppercase; color: var(--ink-panel); }
.swatch input[type="color"] {
  width: 3rem;
  height: 2rem;
  padding: 0.15rem;
  background: var(--paper);
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  cursor: pointer;
}

.mode { display: flex; gap: 0.5rem; align-items: baseline; cursor: pointer; }
.mode span { display: flex; flex-direction: column; }
.mode small { color: var(--ink-panel); font-size: 0.75rem; }

/* The game's parts, as they would look, so a change is seen here without opening a game. The
   parts keep their own markup and rules; only their place on this screen is set, since here they
   neither fill a column nor scroll. */
.look-preview {
  display: grid;
  grid-template-columns: 13rem 1fr;
  grid-template-areas: "readout story" "controls controls";
  border: 1px solid var(--panel-edge);
  border-radius: var(--radius);
  overflow: hidden;
  margin: 0 0 1rem;
}
.look-preview .hud-panel { grid-area: readout; flex: none; overflow: visible; }
.look-preview .story { grid-area: story; flex: none; overflow: visible; position: relative; }
.look-preview .narration { flex: none; overflow: visible; }
.look-preview .narration .beat:last-child { margin-bottom: 0; }
.look-preview .controls { grid-area: controls; }
.look-preview .commands button, .look-preview .command-toggle,
.look-preview .beat-mark, .look-preview .beat-fold { cursor: default; }
/* The preview is not scrolled and its one card starts at the top, so the mark is placed here
   rather than measured. */
.look-preview .beat-rail { position: absolute; inset: 1.25rem auto 0 0; }

#appearance-reset { margin-left: 1rem; }

@media (max-width: 40rem) {
  .look-preview { grid-template-columns: 1fr; grid-template-areas: "readout" "story" "controls"; }
}

/* ---- Manage My Advertisements: the tick-list of worlds and the rows' usage line. ---- */
.tick-list { border: 1px solid var(--panel-edge); border-radius: 0.4rem; padding: 0.5rem 0.75rem; margin: 0 0 0.75rem; }
.tick-list legend { color: var(--ink-dim); font-size: 0.9rem; padding: 0 0.3rem; }
.tick-list .tick-group { color: var(--ink-dim); font-size: 0.8rem; margin: 0.4rem 0 0.2rem; }
.tick-list label { display: inline-flex; align-items: center; gap: 0.35rem; margin: 0.15rem 1rem 0.15rem 0; cursor: pointer; }
.tick-list input { width: auto; margin: 0; }
.realm .ad-actions { grid-area: delete; display: flex; gap: 0.4rem; align-items: start; }
.realm .ad-actions button.danger { color: var(--alert); }
.realm-meta .ad-usage { font-weight: 600; color: var(--ink); }
.field textarea { width: 100%; box-sizing: border-box; font: inherit; }
