/* The layout rules. Every rectangle in the product is an output of this file.
 *
 * ===========================================================================
 * THE ARTBOARDS ARE OUTPUTS OF THESE RULES, NOT INPUTS TO THEM
 * ===========================================================================
 *
 * "Every pixel value on the artboards is an output of these rules at that
 * artboard's size. Build the rules." So no figure a board draws is typed here:
 * the reference table the handoff prints — 1335.5 x 1001.6 at 1920, 1095.5 at
 * 1440, 962.1 at 1280 — is headed "check against these, never type them", and
 * `layout.test.ts` is what checks.
 *
 * ===========================================================================
 * THE BANDS, AND THE THIRD ONE THE BOARDS DRAW IS NOT OURS
 * ===========================================================================
 *
 * The artboards draw a window band with three traffic lights in it. That is a
 * picture of a thing an artboard cannot have: the platform owns a window's own
 * chrome, and in this build that is the browser's. So NOTHING IS RESERVED FOR A
 * TITLEBAR — "the shell owns that" — and there are two slots, not three.
 *
 * The entry document declares them empty and the shell writes into them, because
 * a static page can carry one scene's markup and a scene change is a markup
 * swap. Which HEIGHT each takes is the scene's business and is here; what goes
 * IN them is the screen's.
 *
 * ===========================================================================
 * THE ONE NON-NEGOTIABLE PAIR
 * ===========================================================================
 *
 * "The container never distorts and is never shorter than the stack — those are
 * the two non-negotiables. Assert both at every window size you test: rendered
 * aspect equals the title's aspect, and game height equals rail height."
 *
 * The rule that makes both true has moved three times, and each move was a
 * change three revisions away from the sentence stating it: height was the
 * binding axis, then a `min()` of both constraints, then this. What it is now:
 * the game is `height: 100%` of the body at every window and the rail is the
 * clamped leftover, so NO WIDTH BOUND ON THE GAME EXISTS TO BE WRONG. The trap
 * inside the fix is that `max-width: 100%` on a flex item resolves against the
 * containing block rather than the space left beside its siblings — implemented
 * literally it never binds, the aspect check still passes, and the picture
 * simply overflows. There is none here. */

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

body {
  overflow: hidden;
}

/* ===========================================================================
 * The shell: the content area, and the floor
 * ===========================================================================
 *
 * "Floor 1280x800 of content area, enforced by the native shell — the real
 * window is that plus whatever titlebar the shell draws. No ceiling — the game
 * keeps growing." In a browser there is no native shell to enforce a floor, so
 * what this can do is stop the layout collapsing below it: the content area is
 * at least the floor and the window scrolls rather than crushing the game.
 * Stated rather than silently different, because the desktop product refused
 * the resize and this one cannot. */

.st-shell {
  display: flex;
  flex-direction: column;
  height: 100%;
  min-width: 1280px;
  min-height: 800px;
  background: var(--ground);
  color: var(--calm);
  font-family: var(--font-body);
}

/* THE BASE SIZE IS NOT STATED HERE, and it was: `font-size: var(--type-default)`
 * put 14px on the shell where the boards render 15.
 *
 * The type table's `default` row reads "**Every control label and field**, list
 * rows, dialog body, annotations" -- a role for the elements that take it, not
 * the document's base. The base is `styles.css`'s own `body { font-size: 15px }`,
 * which is what every board inherits because every board loads that sheet, and
 * the elements the `default` row names already carry 14 from the shipped `.btn`,
 * `.input` and `.dialog-body`.
 *
 * ONE ROW OF THE INK ORACLE, 447 DESCENDANT ROWS BEHIND IT. Nothing about it
 * moved a box, and the restatement clause could not see it either: that clause
 * reads rules whose selector is a bare SHIPPED class, and this was our class
 * restating -- wrongly -- a value the shipped sheet sets on `body`. */

.st-shell[data-scene='ingame'] {
  background: var(--ground-game);
}

/* THE SERVED REFUSAL PAGE IS NOT A FIFTH SCENE. It is reached by its own URL
 * rather than by driving the scene machine, so it carries `data-served` and the
 * scene vocabulary keeps its four members — a reader who found five values in
 * one attribute would conclude there were five screens. It takes the join
 * screen's frame, because that is the column the board draws it in.
 *
 * The main band takes everything the bottom bar leaves. */
.st-band {
  flex: 1;
  display: flex;
  min-height: 0;
}

/* The bottom bar's height is the scene's: the join footer and the HUD are 56,
 * the lobby's self bar is 52. One number per scene, here, so no screen carries
 * a height of its own. */
/* The bottom band, and THE INK ON IT IS THE BOARD'S OWN, per scene.
 *
 * Every board draws this as ONE element carrying both the band's height and the
 * bar's ink; we draw two, because the document declares the slot and a scene
 * change is a markup swap into it, so the outer element cannot be the view's.
 * The tree oracle elides our inner wrapper and pairs the children through it --
 * but the INK oracle compares the outer node against the board's single one, and
 * the outer node was bare. A wash nobody drew and a colour that stayed at full
 * strength, on every screen, with no rectangle out of place.
 *
 * So what the board states on that one element is stated here, on the element
 * the boards pair with. Only the inherited and painted properties: the gap and
 * the padding stay on the inner bar, which is what positions the children, and
 * stating them twice would indent every child by 11.2px. */

.st-band-bottom {
  display: flex;
  align-items: center;
  flex: 0 0 56px;
}

/* Board 1: `display:flex;align-items:center;gap:11.2px;padding:0 22.4px;
 * color:var(--muted);font-size:13px` on a 56px band -- ALL OF IT ON ONE ELEMENT.
 *
 * The gap and the padding were on an `.st-footer` wrapper inside it, and the
 * board draws the dot, the reading and the build as direct children of the band.
 * The wrapper was one level the deck does not have, so every child sat a level
 * deep and the wrapper itself was an EXTRA nobody could see: it has no text and
 * no ink of its own. The join screen's markup is flat now and this rule carries
 * what the wrapper did. */
.st-shell[data-scene='join'] .st-band-bottom,
.st-shell[data-served='unsupported'] .st-band-bottom {
  gap: var(--space-4);
  padding: 0 var(--space-8);
  color: var(--muted);
  font-size: var(--type-small);
}

/* Boards 4-8: `background:var(--wash)` on a 52px band. */
.st-shell[data-scene='lobby'] .st-band-bottom,
.st-shell[data-scene='loading'] .st-band-bottom {
  flex-basis: 52px;
  background: var(--wash);
}

/* Board 10: `color:var(--muted)` on a 56px band, and no wash -- the HUD sits
 * over the game rather than on a bar. */
.st-shell[data-scene='ingame'] .st-band-bottom {
  color: var(--muted);
}

/* ===========================================================================
 * Screen 1 — Join
 * ===========================================================================
 *
 * "Left-aligned column at x=160, vertically centred." The left margin and the
 * column width are the design's own stated exceptions to the spacing scale:
 * "Deliberate exceptions: 44px overlay padding, 160px join left margin, 280px
 * join column." */

.st-shell[data-scene='join'] .st-band,
.st-shell[data-served='unsupported'] .st-band {
  align-items: center;
}

/* Board 1 draws the column `width:760px;padding-left:160px;…;gap:44px`. The
 * width was missing and the column shrank to its content -- 492px against 760 --
 * which moved every child on the screen and collapsed its pairing to 13 of 19
 * nodes. A PARTIAL LIFT rather than a wrong value: the two numbers came from the
 * README's prose, which names the margin and the field column and not the
 * outer width, and nothing was there to notice the third. */
.st-join-column {
  display: flex;
  flex-direction: column;
  gap: 44px;
  width: 760px;
  padding-left: 160px;
}

.st-join-hero {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.st-mark {
  width: 56px;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
}

.st-strapline {
  font-size: var(--type-default);
  letter-spacing: var(--track-strapline);
  text-transform: uppercase;
  color: var(--muted);
}

.st-join-fields {
  display: flex;
  flex-direction: column;
  gap: var(--space-6);
  width: 280px;
}

.st-field {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

/* The refusal note: the errors board's own column, which is a dot beside a
 * block. `align-items: flex-start` and the dot's own top margin are what keep
 * the dot on the first line of a sentence that wraps. */
.st-note {
  display: flex;
  gap: var(--space-4);
  align-items: flex-start;
}

.st-note-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-top: 7px;
  flex: 0 0 auto;
}

.st-note-dot[data-tone='waiting'] {
  background: var(--waiting);
}

.st-note-dot[data-tone='wrong'] {
  background: var(--wrong);
}

.st-note strong {
  font-weight: 500;
}

.st-build {
  margin-left: auto;
  color: var(--dim);
}

/* ===========================================================================
 * Screens 2 and 3 — Lobby, and Starting inside its frame
 * ===========================================================================
 *
 * "Fixed — self bar 52px, body padding 11.2px, grid gap 11.2px, 11.2px to the
 * rail. Clamped — rail `clamp(300px, 20vw, 360px)`. Fluid — the 2x2 grid takes
 * everything else and grows without a cap." */

.st-shell[data-scene='lobby'] .st-band,
.st-shell[data-scene='loading'] .st-band {
  gap: var(--space-4);
  padding: var(--space-4);
}

.st-lobby-grid {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: 1fr 1fr;
  grid-template-rows: 1fr 1fr;
  gap: var(--space-4);
}

.st-lobby-rail {
  flex: 0 0 auto;
  width: clamp(300px, 20vw, 360px);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* "Start game — pinned bottom (`margin-top:auto`). The rail never gains a third
 * panel." The slot is drawn even when it is empty, which is what keeps the two
 * cards in the same places in a participant's rail. */
.st-start {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* The self bar, 52px on `--wash`, with the way out right-aligned — "in the same
 * corner the in-game HUD puts its actions, so the way out is in one place
 * across the product." */
.st-self-bar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  width: 100%;
  height: 100%;
  padding: 0 var(--space-4);
}

.st-bar-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: var(--space-6);
}

.st-bar-relay {
  font-size: 12px;
  color: var(--muted);
}

/* ===========================================================================
 * Screen 4 — In-game
 * ===========================================================================
 *
 * THE RULES BELOW ARE THE DESIGN'S OWN CSS, LIFTED. The handoff prints them as
 * a code block and every clause in it is load-bearing:
 *
 *   - the game is `height: 100%`, never `flex: 1` (width-driven, overruns the
 *     height by 91px at 1920) and never `max-width` (lets it drop below the
 *     stack at 1440);
 *   - `flex: 0 0 auto` stops the rail squeezing it;
 *   - the rail's middle term is the tile's own 16:9 width and the last is what
 *     the game leaves over, so at the ceiling the rail is exactly 16:9 and below
 *     it the tiles are a few percent taller and crop at the sides;
 *   - the stack always fills the game's height, so the two are flush BY
 *     CONSTRUCTION and there is no reflow;
 *   - leftover width goes to `justify-content: center`, which centres the game
 *     and the rail AS A GROUP with the gap intact.
 *
 * `container-type: size` is what makes `100cqh` and `100cqw` the body's own box
 * rather than the window's, which is the difference between reserving the bar
 * and not.
 *
 * THE ASPECT IS THE SHELL'S TO STATE. `--cabinet-w` and `--cabinet-h` are
 * written onto the shell from the catalogue before first paint, and the aspect
 * is derived from them here rather than typed: 4:3 is one title's cabinet
 * monitor, and a ratio in this file would ship a wider title into a distorted
 * container. */

.st-shell[data-scene='ingame'] .st-band {
  gap: var(--space-4);
  padding: var(--space-4);
  align-items: stretch;
  justify-content: center;
  container-type: size;
}

.st-game-rect {
  --game: var(--cabinet-w) / var(--cabinet-h);
  aspect-ratio: var(--game);
  height: 100%;
  flex: 0 0 auto;
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--game-surface);
  box-shadow: var(--shadow-sm);
  display: grid;
  place-items: center;
}

/* NOTHING IS DRAWN AT A GUESSED ASPECT. Without the two custom properties the
 * rectangle has no aspect to take, so it is not drawn at all — which is the
 * design's own refusal for a boot that got no catalogue.
 *
 * IT IS SCOPED TO THE RECTANGLE AND NOT TO THE SHELL, deliberately. The tree
 * this is ported from hid the whole shell, and the first named journey is that
 * first paint draws the join screen with its room-code field rather than a blank
 * window — and the join screen needs no aspect. So the refusal covers exactly
 * the element that needs the value. */
.st-shell:not([data-cabinet]) .st-game-rect {
  display: none;
}

/* The picture FILLS the container edge to edge. "The container is already the
 * title's display aspect (the cabinet's monitor, not the 416x224 framebuffer),
 * so stretching the emulator output to it is exactly the pixel-aspect correction
 * a CRT applied. No letterbox, no pillarbox, nothing clipped. Do not preserve
 * square pixels — that would show the game 39% wider than it ever was." */
.st-game-frame {
  position: absolute;
  inset: 0;
}

.st-game-frame canvas {
  width: 100%;
  height: 100%;
  object-fit: fill;
  display: block;
}

.st-rail {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  width: clamp(
    240px,
    calc((100cqh - 33.6px) / 4 * 16 / 9),
    calc(100cqw - 11.2px - 100cqh * (var(--cabinet-w) / var(--cabinet-h)))
  );
}

/* "The rail column is exactly four tiles in every state; a fifth child would
 * shrink them and break the flush edge." */
.st-rail > .st-tile {
  flex: 1;
  min-height: 0;
}

/* The HUD is unpainted: "it sits directly on `--ground-game`, with no fill and
 * no border, 11.2px side padding." */
/* The in-game HUD, lifted: the board draws the band
 * `display:flex;align-items:center;gap:16.8px;padding:0 11.2px`.
 *
 * THE GAP WAS `--space-4`, WHICH IS THE PADDING'S NUMBER AND NOT THE GAP'S. The
 * first control sat right and every control after it 5.6px left of where three
 * boards draw it -- one declaration, fifteen differences, and nothing visibly
 * wrong on any screen. */
.st-hud {
  display: flex;
  align-items: center;
  gap: var(--space-6);
  width: 100%;
  height: 100%;
  padding: 0 var(--space-4);
}

.st-hud-right {
  margin-left: auto;
  display: flex;
  gap: var(--space-4);
}

.st-hud .btn {
  white-space: nowrap;
}

/* ===========================================================================
 * The overlay layer, outside the shell
 * ===========================================================================
 *
 * A dialog's scrim covers the whole window, "bars included", so it is a sibling
 * of the shell rather than a child of a band. Empty except while a dialog is
 * open, and it must not swallow a press when it is empty. */

#overlays:empty {
  display: none;
}

#media {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}
