/*
 * Dark by default, and sized so the board stays the largest thing on screen
 * at any window size. Two people reading the same position over a phone call
 * need the pieces big and the chrome quiet.
 */

:root {
  --bg: #16150f;
  --surface: #211f18;
  --surface-raised: #2b2820;
  --border: #3a362b;
  --text: #ece6d8;
  --text-dim: #a49c88;
  --accent: #b58863;
  --accent-text: #1a1811;
  --good: #7fb069;
  --bad: #d16a5a;
  --radius: 8px;
  --gap: 0.75rem;

  /* The panel grows with the window instead of sitting at one width: on a
     large monitor there is room to spare beside a board that is bounded by
     height, and a 20rem column next to it wastes all of it. Floored so it
     never squeezes the move list, capped so it never becomes the main event.
     The board's own width subtracts this, so the two cannot disagree. */
  --panel-width: clamp(20rem, 24vw, 32rem);

  /* The terminal takes a column of its own when it is open. `--terminal-space`
     is what the board subtracts, and stays at zero while it is shut. */
  --terminal-width: clamp(20rem, 24vw, 34rem);
  --terminal-space: 0px;
}

body[data-terminal="open"] { --terminal-space: var(--terminal-width); }

* { box-sizing: border-box; }

/*
 * The board and its panels are an instrument, not a document.
 *
 * Dragging anywhere — reaching for a piece, aiming at a move in the list —
 * used to paint headings, buttons, coordinates and move numbers blue, none
 * of which is text anybody wants on their clipboard. What you type stays
 * selectable, because that is the text that is really yours.
 */
body {
  -webkit-user-select: none;
  user-select: none;
}

input,
textarea {
  -webkit-user-select: text;
  user-select: text;
}

/* `hidden` must win over the display rules below, or a hidden row still shows. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.5 system-ui, -apple-system, "Segoe UI", sans-serif;
  min-height: 100dvh;
}

/* --- top bar --- */

.bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--gap);
  flex-wrap: wrap;
  padding: 0.6rem 1rem;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.bar__title { margin: 0; font-size: 1rem; font-weight: 600; letter-spacing: 0.02em; }
/* Sits with the title rather than floating in the middle of the bar: the
   auto margin eats the space `space-between` would otherwise open up here. */
.bar__version {
  margin-right: auto;
  font-size: 0.7rem;
  color: var(--text-dim);
  opacity: 0.75;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.bar__meta { display: flex; align-items: center; gap: 1rem; font-size: 0.85rem; }

.status { color: var(--text-dim); }
.status::before {
  content: "●";
  margin-right: 0.4em;
  color: var(--text-dim);
}
.status[data-status="online"]::before { color: var(--good); }
.status[data-status="offline"]::before { color: var(--bad); }
.status[data-status="connecting"]::before { color: var(--accent); }

/* --- layout --- */

/* The board column takes whatever the board needs and the pair is centred,
   rather than the board being squeezed into a fixed-width column. */
.layout {
  display: grid;
  grid-template-columns: auto var(--panel-width);
  gap: 1.25rem;
  align-items: start;
  justify-content: center;
  padding: 1.25rem;
}

/* The board slides over to make room rather than being covered: a position
   you are reading should never have a window dropped on top of it. */
body[data-terminal="open"] .layout {
  grid-template-columns: var(--terminal-width) auto var(--panel-width);
}

.board-area { display: grid; gap: 0.75rem; justify-items: center; min-width: 0; }

/* Wraps the board so the grip can sit on its corner. Sized to the board, so
   the grip lands on the board's edge and not the column's. */
.board-frame { position: relative; width: fit-content; max-width: 100%; }

/* The corner grip, in the spirit of the one on chess sites. */
.resize {
  position: absolute;
  right: -0.35rem;
  bottom: -0.35rem;
  width: 1.15rem;
  height: 1.15rem;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0 0 var(--radius) 0;
  cursor: nwse-resize;
  /* Claim the gesture so a drag on a phone resizes instead of scrolling. */
  touch-action: none;
  opacity: 0.45;
  transition: opacity 120ms ease;
}

/* Two diagonal strokes, the conventional "drag me" corner. */
.resize::before {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(135deg, transparent 42%, var(--text-dim) 42%, var(--text-dim) 54%, transparent 54%),
    linear-gradient(135deg, transparent 68%, var(--text-dim) 68%, var(--text-dim) 80%, transparent 80%);
}

.resize:hover,
.resize:focus-visible,
.resize[data-dragging] { opacity: 1; }
.resize[data-dragging]::before { background-color: transparent; }

/* Square, and bounded by the window rather than by its column: as tall as the
   viewport allows, as wide as the space left beside the panel, and no larger
   than the size asked for. With no `--board-size` set the first term drops out
   and the board simply fills the window. */
.board {
  width: min(
    var(--board-size, 100vmax),
    calc(100dvh - 11rem),
    calc(100vw - var(--panel-width) - var(--terminal-space) - 4rem)
  );
  aspect-ratio: 1;
  border-radius: var(--radius);
  overflow: hidden;
  box-shadow: 0 8px 28px rgb(0 0 0 / 0.45);
}

/* Kept to one line: if this wrapped it would change the height of everything
   below the board, and the board sizes itself around exactly that. */
.position {
  margin: 0;
  color: var(--text-dim);
  font-size: 0.9rem;
  text-align: center;
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.position[data-over="true"] { color: var(--accent); font-weight: 600; }

/* --- controls --- */

.controls { display: flex; gap: 0.4rem; }

button, select, input, textarea {
  font: inherit;
  color: var(--text);
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.45rem 0.7rem;
  max-width: 100%;
}

/* Fields size to the panel, not to their longest option. */
select, input, textarea { width: 100%; min-width: 0; }


button { cursor: pointer; }
button:hover { background: #38342a; }
button:active { transform: translateY(1px); }
button:focus-visible, select:focus-visible, input:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.controls button { min-width: 3rem; font-size: 1rem; }
.button--warn { color: var(--bad); }

.buttons { display: flex; flex-wrap: wrap; gap: 0.45rem; }
.buttons button { flex: 1 1 8rem; }

/* --- side panel --- */

.panel {
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1.1rem;
  max-height: calc(100dvh - 6rem);
  position: sticky;
  top: 1.25rem;
  overflow-y: auto;
  scrollbar-gutter: stable;
  overflow-x: hidden;
}

.panel__block { display: grid; gap: 0.6rem; }
.panel__block--grow { min-height: 8rem; }

/* A hairline between sections: enough to group things without drawing a box
   around every one of them. */
.panel__block + .panel__block {
  border-top: 1px solid var(--border);
  padding-top: 1.1rem;
}

.panel__heading {
  margin: 0;
  font-size: 0.72rem;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-dim);
  cursor: default;
}

summary.panel__heading { cursor: pointer; padding: 0.15rem 0; border-radius: 4px; }
summary.panel__heading:hover { color: var(--text); }

/* Browsers wrap a <details> element's content in an anonymous box, so its
   children are not grid items and `gap` never reaches them. Lay the content
   out in a wrapper of our own instead of relying on the details itself. */
details.panel__block { display: block; }
.panel__body { display: grid; gap: 0.75rem; margin-top: 0.75rem; }
.panel__body--loaders { gap: 1.4rem; }

.field { display: grid; gap: 0.35rem; }

/* An input and the button that acts on it: grouped, but not crowded. */
.loader { display: grid; gap: 0.85rem; }

/* Anywhere else a button follows a field directly, give it the same air. */
.field + button { margin-top: 0.25rem; }
.field__label { font-size: 0.72rem; text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-dim); }
textarea { resize: vertical; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.85rem; }


/* --- who's here --- */

.who { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.2rem; }

.who__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0.45rem;
  border-radius: 6px;
  background: rgb(255 255 255 / 0.03);
  font-size: 0.88rem;
}

.who__row--empty { color: var(--text-dim); background: none; font-size: 0.82rem; }

.who__name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A generated name is a placeholder, not a claim about who someone is. */
.who__name--anon { font-style: italic; color: var(--text-dim); }
.who__you { color: var(--text-dim); font-style: normal; }

/* The reading: a number, then bars, tallest last. */
.signal { display: inline-flex; align-items: center; gap: 0.4rem; flex: none; }

.signal__ms {
  /* Tabular figures so the number does not jiggle as it updates. */
  font-variant-numeric: tabular-nums;
  font-size: 0.78rem;
  color: var(--text-dim);
  min-width: 3.6em;
  text-align: right;
}
.signal__unit { opacity: 0.6; }
.signal__ms--pending { opacity: 0.5; }

.signal__bars { display: inline-flex; align-items: flex-end; gap: 2px; height: 0.85rem; }

.signal__bar {
  width: 3px;
  border-radius: 1px;
  background: var(--border);
}
.signal__bar:nth-child(1) { height: 25%; }
.signal__bar:nth-child(2) { height: 50%; }
.signal__bar:nth-child(3) { height: 75%; }
.signal__bar:nth-child(4) { height: 100%; }

.signal__bar--on { background: var(--good); }
.signal[data-signal="weak"] .signal__bar--on { background: #d6a44c; }
.signal[data-signal="poor"] .signal__bar--on { background: var(--bad); }

/* Still measuring: show the shape of the thing without claiming a reading. */
.signal[data-signal="unknown"] .signal__bars { opacity: 0.45; }

.signal[data-signal="weak"] .signal__ms { color: #d6a44c; }
.signal[data-signal="poor"] .signal__ms { color: var(--bad); }

/* --- where the other person's pointer is --- */

/* Over the board, and deaf to the pointer: this must never be the thing a
   click lands on. */
.pointers {
  position: absolute;
  inset: 0;
  pointer-events: none;
  z-index: 6;
}

/* The tip of the arrow sits on the point, the way a real cursor does — hence
   no centring transform. The transition covers the gap between updates so it
   glides rather than stepping. */
.pointer {
  position: absolute;
  width: 0;
  height: 0;
  transition: left 90ms linear, top 90ms linear;
}

.pointer__arrow {
  position: absolute;
  width: 1.15rem;
  height: auto;
  overflow: visible;
  fill: var(--good);
  stroke: rgb(0 0 0 / 0.55);
  stroke-width: 1;
  /* The glow, and enough of a shadow underneath to keep it off the board. */
  filter: drop-shadow(0 0 5px rgb(94 240 138 / 0.9)) drop-shadow(0 1px 2px rgb(0 0 0 / 0.5));
}

.pointer__who {
  position: absolute;
  left: 0.9rem;
  top: 1rem;
  padding: 0.05rem 0.3rem;
  border-radius: 3px;
  background: var(--good);
  color: #0c1a10;
  font-size: 0.62rem;
  font-weight: 600;
  white-space: nowrap;
  box-shadow: 0 0 6px rgb(94 240 138 / 0.55);
}

/* --- pieces taken --- */

/* A thin row above and below the board, as a chess site puts them. Kept the
   same height whether or not anything has been taken, so nothing shifts the
   board up and down as the game goes on. */
.taken {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  min-height: 1.35rem;
  width: 100%;
  padding: 0 0.15rem;
}

/* Pieces of a kind, overlapping the way a handful of them would. */
.taken__group { display: flex; }
.taken__group piece {
  display: block;
  width: 1.15rem;
  height: 1.15rem;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.taken__group piece + piece { margin-left: -0.55rem; }

.taken__lead {
  font-size: 0.8rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}

/* --- move list --- */

.movelist {
  overflow-y: auto;
  scrollbar-gutter: stable;
  max-height: 16rem;
  overscroll-behavior: contain;
}
.movelist__empty { margin: 0; color: var(--text-dim); font-size: 0.85rem; }
.movelist__list { margin: 0; padding: 0; list-style: none; }

.movelist__row {
  display: grid;
  grid-template-columns: 2.2rem 1fr 1fr;
  align-items: center;
  gap: 0.25rem;
}
.movelist__row:nth-child(odd) { background: rgb(255 255 255 / 0.025); }

.movelist__number { color: var(--text-dim); font-size: 0.8rem; text-align: right; padding-right: 0.35rem; }

.movelist__move {
  background: none;
  border: none;
  border-radius: 4px;
  padding: 0.15rem 0.35rem;
  text-align: left;
  font-variant-numeric: tabular-nums;
}
.movelist__move:hover { background: var(--surface-raised); }

/* The moving piece, drawn rather than spelled. Chessground sizes pieces only
   inside `.cg-wrap`, so out here it is ours to set: a square box the height of
   the line, dropped below the baseline so the piece sits on it rather than
   floating above it. `vertical-align` is the knob if a set sits high or low —
   the sets do not agree about how much padding to leave inside their viewBox. */
.movelist__move piece {
  display: inline-block;
  width: 1.15em;
  height: 1.15em;
  vertical-align: -0.24em;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}
.movelist__move[aria-current="true"] {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

/* --- the chooser --- */

/* A native <select> opens a menu the operating system draws — white, in the
   system's fonts, immune to styling. This is the same control in the board's
   colours. It expands inline rather than floating: the panel scrolls, and a
   floating list would be clipped at its edge. */
.select { position: relative; display: grid; gap: 2px; }

.select__value {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  width: 100%;
  text-align: left;
}
.select__chosen { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.select__arrow { flex: none; color: var(--text-dim); font-size: 0.8rem; }
.select[data-open] .select__arrow { transform: rotate(180deg); }

.select__list {
  list-style: none;
  margin: 0;
  padding: 0.2rem;
  display: grid;
  gap: 1px;
  background: var(--surface-raised);
  border: 1px solid var(--border);
  border-radius: 6px;
  max-height: 14rem;
  overflow-y: auto;
  scrollbar-gutter: stable;
}

.select__option {
  display: grid;
  grid-template-columns: 1.1rem 1fr;
  align-items: center;
  gap: 0.35rem;
  padding: 0.35rem 0.4rem;
  border-radius: 4px;
  cursor: pointer;
  font-size: 0.9rem;
}
.select__option:hover { background: rgb(255 255 255 / 0.06); }

/* Where the keyboard is, which is not yet what is chosen. */
.select__option.is-cursor { background: rgb(255 255 255 / 0.1); }

.select__option.is-chosen { color: var(--accent); font-weight: 600; }
.select__tick { color: var(--accent); font-size: 0.8rem; text-align: center; }

/* --- board coordinates --- */

/*
 * Chessground's own coordinates are 9px and positioned with fixed pixel
 * offsets — `top: -20px` against a strip whose rows are percentages — so
 * they drift out of line with the ranks as the board changes size, which is
 * exactly what a resizable board does. Replaced here with the arrangement
 * chess.com uses: big, bold, tucked into the corner of the square they
 * label, and sized as a fraction of the board so they hold their place and
 * their weight at any size.
 */
.board { container-type: inline-size; }

.cg-wrap coords {
  font-family: inherit;
  font-size: 2.6cqw;
  font-weight: 700;
  opacity: 1;
  /* The board themes are photographs and textures; a whisper of shadow keeps
     the letters legible over wood grain without making them look embossed. */
  text-shadow: 0 1px 1px rgb(0 0 0 / 0.25);
}

/* Ranks: down the left, at the top of each square, like a book's margin. */
.cg-wrap coords.ranks {
  top: 0;
  left: 0;
  width: 12.5%;
  height: 100%;
}

.cg-wrap coords.ranks coord {
  display: flex;
  align-items: flex-start;
  /* Chessground nudges each label by 39% of its own height to fake centring
     against the -20px offset above. Both go together or neither works. */
  transform: none;
  padding: 0.4cqw 0 0 0.6cqw;
}

/* Files: along the bottom, at the trailing corner of each square. */
.cg-wrap coords.files {
  bottom: 0;
  left: 0;
  width: 100%;
  height: 12.5%;
}

.cg-wrap coords.files coord {
  display: flex;
  align-items: flex-end;
  justify-content: flex-end;
  padding: 0 0.6cqw 0.3cqw 0;
}

/* Chessground's alternating colours assume 0.8 alpha against a square; at
   this size that reads as grey rather than as a label. */
.orientation-white .ranks :nth-child(odd),
.orientation-white .files :nth-child(even),
.orientation-black .ranks :nth-child(even),
.orientation-black .files :nth-child(odd) {
  color: rgb(60 60 60 / 0.9);
}

.orientation-white .ranks :nth-child(even),
.orientation-white .files :nth-child(odd),
.orientation-black .ranks :nth-child(odd),
.orientation-black .files :nth-child(even) {
  color: rgb(255 255 255 / 0.9);
}

/* --- games --- */

.games {
  list-style: none;
  margin: 0 0 0.6rem;
  padding: 0;
  max-height: 12rem;
  overflow-y: auto;
  scrollbar-gutter: stable;
  /* A long name is cut short, never scrolled sideways: a horizontal bar under
     the list looks broken, and the move count belongs on screen. */
  overflow-x: hidden;
  overscroll-behavior: contain;
  display: grid;
  gap: 2px;
}

/* The row has to be allowed to shrink as well, or its own min-content width
   holds the list open no matter what the button inside it does. */
.games__row { min-width: 0; display: flex; align-items: stretch; gap: 2px; }

/* Catching a game up with chess.com: quiet until wanted, and never wider than
   the name it sits beside. */
.games__sync {
  flex: none;
  width: 2.4rem;
  padding: 0;
  display: grid;
  place-items: center;
  font-size: 1.35rem;
  line-height: 1;
  color: var(--text-dim);
  background: none;
  border: none;
  border-radius: 4px;
}
.games__sync:hover { color: var(--text); background: var(--surface-raised); }

.games__game {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  background: none;
  border: none;
  border-radius: 4px;
  padding: 0.3rem 0.45rem;
  text-align: left;
}
.games__game:hover { background: var(--surface-raised); }

.games__game.is-open {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

/* Long names lose their tail rather than pushing the count off the row.
   `min-width: 0` is what actually does it: without it a flex item refuses to
   shrink below its text, and the row grows instead of the name ellipsing. */
.games__name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.games__moves {
  flex: none;
  font-size: 0.75rem;
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.games__game.is-open .games__moves { color: var(--accent-text); opacity: 0.7; }

/* A pair of buttons rather than a dropdown: two choices, both worth seeing. */
.sides { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }

.sides__pick {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.35rem;
  padding: 0.35rem 0.5rem;
  font-size: 0.85rem;
  color: var(--text-dim);
  background: var(--surface-raised);
  border: 1px solid var(--border);
}
.sides__pick:first-child { border-radius: 4px 0 0 4px; }
.sides__pick:last-child { border-radius: 0 4px 4px 0; }
.sides__pick:hover { color: var(--text); }

.sides__pick.is-chosen {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

/* --- branches --- */

/* Flowing notation rather than a grid: variations nest, so there are no
   columns to line up, and the text needs to read like a book's. */
/* Reserve the scrollbar's width whether or not it is showing.
   Without this the bar appears when you reach for a move, takes width from
   the content, and the text reflows — so the move you were aiming at slides
   out from under the pointer between pressing and releasing. The click then
   lands on the panel rather than the move, and nothing happens. */
.branches {
  overflow-y: auto;
  scrollbar-gutter: stable;
  max-height: 14rem;
  overscroll-behavior: contain;
  margin-bottom: 0.6rem;
}

.branches__empty { margin: 0; color: var(--text-dim); font-size: 0.85rem; line-height: 1.5; }

.branches__text {
  margin: 0;
  font-size: 0.85rem;
  line-height: 1.9;
  word-spacing: 0.1em;
}

.branches__number { color: var(--text-dim); font-size: 0.78rem; margin-right: 0.1em; }

.branches__move {
  background: none;
  border: none;
  border-radius: 4px;
  padding: 0.1rem 0.25rem;
  font-size: 0.85rem;
  /* Sidelines are the quieter reading; the line on the board is the loud one. */
  color: var(--text-dim);
}
.branches__move.is-line { color: var(--text); }
.branches__move:hover { background: var(--surface-raised); color: var(--text); }

.branches__move.is-current {
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
}

/* A note about a move, printed where a book prints it. Quieter than the
   moves, so the line still reads as a line of chess with prose in it rather
   than prose with chess in it. */
.branches__note {
  background: none;
  border: none;
  border-radius: 4px;
  padding: 0.1rem 0.2rem;
  font-size: 0.8rem;
  font-style: italic;
  color: var(--good);
  opacity: 0.85;
  text-align: left;
}
.branches__note:hover { background: var(--surface-raised); opacity: 1; }

/* Bracketed and indented in colour, so the eye can find where a sideline
   ends without counting parentheses. */
.branches__variation {
  color: var(--text-dim);
  opacity: 0.85;
}

/* --- promotion dialog --- */

/* Sits over the board itself, now that the board has its own frame. */
.promotion {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  background: rgb(0 0 0 / 0.6);
  border-radius: var(--radius);
  z-index: 10;
}

.promotion__panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 1rem;
  display: grid;
  gap: 0.6rem;
  justify-items: center;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.5);
}
.promotion__title { margin: 0; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-dim); }
.promotion__choices { display: flex; gap: 0.5rem; }

/* Big enough to hit without aiming — this interrupts a drag, so it wants to
   be answered at a glance rather than squinted at. */
.promotion__choice {
  display: grid;
  place-items: center;
  width: 4.5rem;
  height: 4.5rem;
  padding: 0.3rem;
}

/* Chessground sizes and positions pieces only inside `.cg-wrap`, so out here
   the size is ours to set. `contain`, not `cover`: a piece cropped to fill a
   square is a piece with its head cut off. */
.promotion__choice piece {
  display: block;
  width: 100%;
  height: 100%;
  background-size: contain;
  background-repeat: no-repeat;
  background-position: center;
}

.promotion__cancel { font-size: 0.8rem; padding: 0.3rem 0.9rem; }

/* --- asking "are you sure?" --- */

/* Covers the page rather than the board: these questions come from the panel,
   and the board behind should dim with everything else. */
.dialog {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgb(0 0 0 / 0.6);
  z-index: 20;
}

.dialog__panel {
  width: min(26rem, 100%);
  display: grid;
  gap: 0.5rem;
  padding: 1.15rem;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.5);
}

.dialog__question { margin: 0; font-size: 1rem; font-weight: 600; }
.dialog__detail { margin: 0; font-size: 0.85rem; line-height: 1.5; color: var(--text-dim); }

/* The way out on the left, the thing you asked for on the right. */
.dialog__answers {
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin-top: 0.4rem;
}
.dialog__answers button { padding: 0.4rem 0.9rem; }

/* --- the terminal --- */

/*
 * Green phosphor, not a desktop window. The palette is its own — the rest of
 * the board is warm and tan, and this deliberately is not: it should read as
 * a different kind of thing sitting next to the game.
 */
.terminal {
  --term-bg: #040705;
  --term-text: #5ef08a;
  --term-bright: #c2ffd8;
  --term-dim: #2f7048;
  --term-edge: #14361f;

  display: flex;
  flex-direction: column;
  min-width: 0;
  position: sticky;
  top: 1.25rem;
  max-height: calc(100dvh - 6rem);
  overflow: hidden;
  background: var(--term-bg);
  border: 1px solid var(--term-edge);
  border-radius: var(--radius);
  color: var(--term-text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.82rem;
  line-height: 1.55;
  box-shadow: 0 10px 30px rgb(0 0 0 / 0.5), 0 0 0 1px rgb(94 240 138 / 0.06),
              inset 0 0 80px rgb(94 240 138 / 0.04);
}

/* Scanlines, faint enough to be felt rather than seen. */
.terminal::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, rgb(0 0 0 / 0.28) 0 1px, transparent 1px 3px);
  opacity: 0.5;
}

.terminal__bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: none;
  padding: 0.4rem 0.6rem;
  background: rgb(94 240 138 / 0.05);
  border-bottom: 1px solid var(--term-edge);
}

/* Alive, and quietly saying so. */
.terminal__pulse {
  width: 0.45rem;
  height: 0.45rem;
  flex: none;
  border-radius: 50%;
  background: var(--term-text);
  box-shadow: 0 0 6px var(--term-text);
  animation: terminal-pulse 2.4s ease-in-out infinite;
}

@keyframes terminal-pulse { 50% { opacity: 0.25; } }

.terminal__title {
  flex: 1;
  color: var(--term-dim);
  font-size: 0.72rem;
  letter-spacing: 0.05em;
}

.terminal__close {
  flex: none;
  padding: 0 0.35rem;
  background: none;
  border: none;
  color: var(--term-dim);
  font-size: 0.8rem;
  line-height: 1;
}
.terminal__close:hover { color: var(--term-bright); background: none; }

.terminal__output {
  flex: 1;
  min-height: 6rem;
  overflow-y: auto;
  scrollbar-gutter: stable;
  overscroll-behavior: contain;
  padding: 0.7rem;
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
  /* The bloom a phosphor screen has, at a strength that stays readable. */
  text-shadow: 0 0 8px rgb(94 240 138 / 0.35);
}

.terminal__line {
  margin: 0;
  display: flex;
  gap: 0.45rem;
  white-space: pre-wrap;
  word-break: break-word;
}

.terminal__line--out { color: var(--term-text); }
.terminal__line--echo { color: var(--term-bright); }
.terminal__line--note { color: var(--term-dim); text-shadow: none; }
.terminal__line--bad { color: #ff6b6b; text-shadow: 0 0 8px rgb(255 107 107 / 0.35); }

.terminal__sigil { flex: none; color: var(--term-text); }

/* What could come next, from the first letter onwards. Suggestions only —
   clicking one writes it into the line, it never runs anything. */
.terminal__hints {
  flex: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  padding: 0 0.7rem 0.4rem;
  min-height: 0;
}
.terminal__hints:empty { display: none; }

.terminal__hint {
  padding: 0.1rem 0.4rem;
  font: inherit;
  font-size: 0.76rem;
  color: var(--term-dim);
  background: rgb(94 240 138 / 0.07);
  border: 1px solid var(--term-edge);
  border-radius: 3px;
}
.terminal__hint:hover {
  color: var(--term-bright);
  background: rgb(94 240 138 / 0.16);
}

/* Not a suggestion, a count: there are more, keep typing. */
.terminal__hint--more { background: none; border-color: transparent; opacity: 0.7; }

/* Not a suggestion either, but the shape of what to type. */
.terminal__hint--shape {
  background: none;
  border-style: dashed;
  font-style: italic;
  opacity: 0.8;
}

.terminal__prompt {
  display: flex;
  align-items: baseline;
  gap: 0.45rem;
  flex: none;
  padding: 0.55rem 0.7rem;
  border-top: 1px solid var(--term-edge);
  background: rgb(94 240 138 / 0.04);
}

.terminal__input {
  flex: 1;
  min-width: 0;
  padding: 0;
  background: none;
  border: none;
  border-radius: 0;
  color: var(--term-bright);
  font: inherit;
  caret-color: var(--term-text);
  text-shadow: 0 0 8px rgb(94 240 138 / 0.35);
}
.terminal__input:focus-visible { outline: none; box-shadow: none; }

@media (prefers-reduced-motion: reduce) {
  .terminal__pulse { animation: none; }
}

/* --- narrow screens --- */

@media (max-width: 56rem) {
  .layout { grid-template-columns: minmax(0, 1fr); padding: 0.75rem; }
  /* Stacked, so the page scrolls: bound by width only, not by height. */
  .board { width: min(var(--board-size, 100vmax), calc(100vw - 1.5rem)); }
  .panel { position: static; max-height: none; }

  /* Stacked with everything else, and above the board it belongs to. */
  body[data-terminal="open"] .layout { grid-template-columns: minmax(0, 1fr); }
  .terminal { position: static; max-height: 60dvh; order: -1; }
}
