/* The arcade (P1-313; Ryan, 28 Oct 2026: "an 80s style mall arcade where the different rooms are
   different zones"): the outside at /, the floor at /floor/. A mall arcade, not synthwave: the
   blacklight carpet, backlit marquees, cabinets edged in coloured trim, neon zone signs. Built from
   the site's own parts (site.css: plates, cut corners, keys), with the three licensed faces. The
   room and game pages keep site.css alone until they become the zone and the cabinet (PR 3). */
@font-face { font-family: 'Oxanium'; src: url(/fonts/oxanium-700.woff2) format('woff2'); font-weight: 700; font-display: swap; }
@font-face { font-family: 'Barlow Semi Condensed'; src: url(/fonts/barlow-400.woff2) format('woff2'); font-weight: 400; font-display: swap; }
/* IBM Plex Mono's Latin subsets, named Burn-In Mono inside the files and here: a subset is a
   modified font, and its licence reserves "Plex" for the original (OFL-FAQ 2.6; P1-633's 2c). */
@font-face { font-family: 'Burn-In Mono'; src: url(/fonts/plexmono-400.woff2) format('woff2'); font-weight: 400; font-display: swap; }
@font-face { font-family: 'Burn-In Mono'; src: url(/fonts/plexmono-600.woff2) format('woff2'); font-weight: 600; font-display: swap; }

:root {
  --night: #06070A; --wall: #0B0D12; --alcove: #0A0C10; --cab: #0D1015; --glass: #05070A;
  --sign: 'Oxanium', 'Barlow Semi Condensed', sans-serif;
  --prose: 'Barlow Semi Condensed', system-ui, sans-serif;
  --mono: 'Burn-In Mono', ui-monospace, 'SF Mono', Menlo, monospace;
}
body.arcade { font-family: var(--mono); background: var(--night); }
body.arcade > * { position: relative; z-index: 1; }
/* The stage is the world between the bar and the belt: the street, or the floor's zones. */
body.arcade > main { flex: 1; min-height: 0; }
body.street > main { display: flex; flex-direction: column; }
body.street #top { display: none; }

/* The blacklight carpet: dots and rings in the four inks, under the dark. Drawn, never a file. */
body.arcade::before, body.arcade::after { content: ''; position: fixed; inset: 0; z-index: 0; pointer-events: none; }
body.arcade::before {
  opacity: .6; transition: opacity .25s linear;
  background-image:
    radial-gradient(circle at 20px 30px, #7B4DFF 0 2.5px, transparent 3px),
    radial-gradient(circle at 92px 82px, transparent 0 5px, #18C8B8 5.5px 7px, transparent 7.5px),
    radial-gradient(circle at 140px 22px, #FF3D8B 0 2px, transparent 2.5px),
    radial-gradient(circle at 58px 128px, transparent 0 9px, #3A2A8C 9.5px 11px, transparent 11.5px),
    radial-gradient(circle at 126px 118px, #D9F04A 0 1.6px, transparent 2px),
    radial-gradient(circle at 30px 96px, #18C8B8 0 1.5px, transparent 2px),
    radial-gradient(circle at 150px 150px, transparent 0 3px, #FF3D8B 3.5px 4.5px, transparent 5px);
  background-size: 160px 160px;
}
body.arcade::after { background: radial-gradient(ellipse at 50% 42%, rgba(6,7,10,.45) 0%, rgba(6,7,10,.93) 78%); transition: opacity .25s linear; }
.arcade .plate { background: rgba(13,17,22,.94); }

/* ---- the set's scenes (P1-314) -------------------------------------------------------------- */
/* A render of the arcade's set behind the live parts (scenes.js): a still behind the stage, a clip
   over it with the bar and the belt still on top. The drawn carpet gives way to the set's own. */
body.arcade > .scene { position: fixed; inset: 0; z-index: 0; pointer-events: none; }
body.arcade > .scene img, body.arcade > video.scene { display: block; width: 100%; height: 100%; object-fit: cover; }
body.arcade > .scene.clip { z-index: 2; background: var(--night); }
/* A still's loop (scenes.js loopOver) shows once it plays: until then its still is the picture. */
body.arcade > video.scene.loop:not(.on) { visibility: hidden; }
body.arcade > #top, body.arcade > .belt { z-index: 3; }
/* While a clip plays its skip is said beside the belt, and the belt's and the bar's keys wait, dimmed:
   a press or a tap skips. */
body.arcade > .clip-skip { position: fixed; right: 28px; bottom: 76px; z-index: 3; display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; }
body.arcade > .clip-skip .key { font-size: 12px; }
body.clip-on > .belt, body.clip-on > #top { opacity: .4; }
body.has-scene::before { display: none; }
/* Under the power-on clip the floor waits, then comes up over the lit floor the clip ends on. */
body.arcade > main { transition: opacity .25s linear; }
body.clip-power > main { opacity: 0; }
body.has-scene::after { background: radial-gradient(ellipse at 50% 45%, rgba(6,7,10,.15) 0%, rgba(6,7,10,.72) 85%); }
/* A zone's and a cabinet's page stand in the room: its scene is dimmed and soft behind the plates,
   whose words sit on it too (the zone's line, its coming games' names). */
body.zone-page.has-scene::after, body.cabinet-page.has-scene::after { background: rgba(6,7,10,.62); }
body.zone-page > .scene.still img, body.cabinet-page > .scene.still img { filter: blur(6px); transform: scale(1.05); }   /* at 3 px the marquee read as a second heading */
/* Behind the floor's zones, softer: its own signs showed between them as stray letters (an "A" of
   SERVER HALL between two plates). The clip ends crisp, and the zones fade up as it softens. */
body[data-page="floor"] > .scene.still img { filter: blur(2px); transform: scale(1.02); transition: filter .25s linear, transform .25s linear; }
body.clip-power > .scene.still img { filter: none; transform: none; }
/* The plates are glass over the set: what stands behind glows through, the words stay on the dark. */
body.has-scene .plate { background: rgba(13,17,22,.84); backdrop-filter: blur(6px); }
body.has-scene .zone { background: linear-gradient(180deg, rgba(12,15,20,.86), rgba(9,11,15,.88)); backdrop-filter: blur(5px); }

/* ---- the outside ---------------------------------------------------------------------------- */
/* The storefront is the set's render: the sign, the glass and the doors are in the picture. */
body.street::before { opacity: 0; }
body.street.has-scene::after { background: linear-gradient(transparent 60%, rgba(6,7,10,.55)); }
/* The door, the floor's signs and a zone's cabinets are keys laid on the picture as drawn: a layer
   the picture's size, cropped as it is (object-fit: cover), so a share of it is a share of the
   picture at any window's shape (scenes.js spotLayer). Each key is centred on its box, a finger's
   size at least. */
body.arcade > .spot-layer { position: fixed; inset: 0; z-index: 1; container-type: size; pointer-events: none; }
.spot-layer .frame { position: absolute; left: 50%; top: 50%; translate: -50% -50%; width: max(100cqw, 177.78cqh); height: max(100cqh, 56.25cqw); }
.spot-layer .frame.tall { width: max(100cqw, 56.25cqh); height: max(100cqh, 177.78cqw); }   /* the upright framing: scenes.js TALL */
.spot { position: absolute; translate: -50% -50%; min-width: 44px; min-height: 44px; pointer-events: auto; cursor: pointer; }
.door { padding: 0; color: inherit; font: inherit;
  background: transparent; border: 2px solid transparent; transition: border-color .12s linear, box-shadow .12s linear; }
.door:hover, .door:focus-visible { border-color: var(--amber); box-shadow: 0 0 18px rgba(255,178,63,.35), inset 0 0 26px rgba(255,178,63,.14); }
.door:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.door .enter { position: absolute; left: 50%; bottom: 10%; transform: translateX(-50%); display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 10px; background: rgba(6,7,10,.78); pointer-events: none; white-space: nowrap; }
.door .enter .key { border-color: var(--amber); color: var(--amber); font-size: 14px; padding: 5px 10px; }
.door .enter .hint { color: var(--amber); font-size: 13px; }
.street-greet { position: absolute; left: 0; bottom: 0; display: flex; flex-direction: column; gap: 4px; padding: 10px 16px;
  max-width: min(420px, 36%); }
.street-greet b { font-family: var(--sign); font-size: 17px; letter-spacing: .12em; overflow-wrap: anywhere; }
.street-greet span { font-family: var(--prose); font-size: 15px; color: #B9C6CE; }
.street-greet .count { font-family: var(--mono); font-size: 12px; letter-spacing: .08em; }
.street-greet .tagline { font-family: var(--prose); font-size: 12px; letter-spacing: .24em; text-transform: uppercase; color: #FF8FC6; }
/* Walking in: the street's plates go, and the walk-in plays over the picture. */
body.street.entering .street-greet, body.street.entering > .spot-layer { opacity: 0; transition: opacity .12s linear; }

/* ---- the floor ------------------------------------------------------------------------------ */
.zones { flex: 1; min-height: 0; display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); grid-template-rows: repeat(2, minmax(0, 1fr)); gap: 12px; }
.zone { position: relative; display: flex; flex-direction: column; min-height: 0; overflow: hidden; cursor: pointer;
  background: linear-gradient(180deg, rgba(12,15,20,.96), rgba(9,11,15,.96)); border: 1px solid #1E2731; }
.zone.sel { border: 2px solid var(--amber); }
.zone:hover { border-color: #3A4A55; }
.zone.sel:hover { border-color: var(--amber); }
.zone .pool { position: absolute; left: 0; right: 0; bottom: 0; height: 60%; pointer-events: none; opacity: 0; transition: opacity .3s linear;
  background: radial-gradient(ellipse at 30% 100%, color-mix(in srgb, var(--neon) 12%, transparent), transparent 70%); }
.zone.lit .pool { opacity: 1; }
.sign { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 12px 16px 8px; }
.sign h2 { font-family: var(--sign); font-weight: 700; font-size: clamp(18px, 1.9vw, 30px); letter-spacing: .16em; text-transform: uppercase; color: #8C96A2; white-space: nowrap; }
.zone.lit .sign h2 { color: var(--neon); text-shadow: 0 0 6px var(--neon), 0 0 20px color-mix(in srgb, var(--neon) 60%, transparent); }
.sign .count { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--quiet); white-space: nowrap; }
.zone.lit .sign .count { color: var(--up); }
.valance { display: block; height: 2px; margin: 0 16px; background: color-mix(in srgb, var(--neon) 28%, transparent); }
.zone.lit .valance { background: var(--neon); box-shadow: 0 0 10px var(--neon); }
.zone .blurb { padding: 10px 16px 0; font-family: var(--prose); font-size: 15px; line-height: 1.35; color: #9AA8B2; }
.aisle { flex: 1; min-height: 0; display: flex; gap: 16px; padding: 12px 16px 0; }
.about { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; padding-bottom: 12px; }
.about h3 { font-family: var(--sign); font-weight: 700; font-size: 17px; letter-spacing: .1em; text-transform: uppercase; }
.about .what { font-family: var(--prose); font-size: 15px; line-height: 1.35; color: #B9C6CE; overflow: hidden; display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; }
.about .chip { align-self: flex-start; }
.soon { margin-top: auto; display: flex; flex-direction: column; gap: 6px; }
.soon p { font-size: 12px; line-height: 1.5; color: var(--quiet); }
.soon p b { color: #B9C6CE; font-weight: 400; }
.ghosts { display: flex; gap: 8px; align-items: flex-end; }
.ghost { width: 34px; height: 62px; background: #0F1318; border: 1px solid #232C36; display: flex; flex-direction: column; align-items: center; gap: 5px; padding-top: 5px; }
.ghost::before { content: ''; width: 24px; height: 7px; background: #1A2029; }
.ghost::after { content: ''; width: 24px; height: 20px; background: #07090C; border-radius: 3px; }
.zone.dark .aisle { flex-direction: column; align-items: center; justify-content: center; gap: 10px; padding-bottom: 12px; }
.zone.dark .ghost { width: 50px; height: 92px; }
.zone.dark .soon { margin-top: 0; align-items: center; text-align: center; }

/* A cabinet, front on: the marquee, the screen in its bezel, the control panel, the coin door. Its
   trim is its game's colour. It scales with the room it stands in. */
.cabinet { --trim: #8496A4; height: 100%; max-height: 400px; aspect-ratio: 128 / 210; flex: none; display: flex; flex-direction: column; align-items: center;
  filter: drop-shadow(0 12px 16px rgba(0,0,0,.7)); }
.cabinet .marquee { position: relative; width: 100%; aspect-ratio: 4 / 1; overflow: hidden; background: #000; border: 2px solid var(--trim); border-bottom-width: 0; }
.cabinet .marquee img { display: block; width: 100%; height: 100%; object-fit: cover; opacity: .25; filter: saturate(.4); transition: opacity .2s linear, filter .2s linear; }
.cabinet .hood { width: 94%; flex: 1.7; min-height: 0; background: var(--alcove); border: 2px solid var(--trim); border-top: 0; border-bottom: 0; padding: 7% 6% 5%; }
.screen { position: relative; width: 100%; height: 100%; border-radius: 7px; overflow: hidden; background: #020304; box-shadow: 0 0 0 2px #000; }
.screen .shot { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; opacity: 0; transition: opacity .6s linear; }
.screen .title { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8%; text-align: center; padding: 4px;
  font-family: var(--sign); font-weight: 700; font-size: 12px; letter-spacing: .1em; color: #fff; opacity: 0; background: #020304; transition: opacity .6s linear; }
.screen .title i { font-style: normal; font-family: var(--mono); font-size: 12px; letter-spacing: .2em; color: var(--amber); }
.cabinet.booted .screen .title.on { opacity: 1; }
.cabinet .panel { width: 103%; height: 9%; min-height: 12px; background: #161B22; border: 2px solid var(--trim); display: flex; align-items: center; justify-content: center; gap: 6%; }
.cabinet .panel i { display: block; height: 42%; aspect-ratio: 1; border-radius: 50%; background: #2A323C; }
.cabinet .body { width: 94%; flex: 1; min-height: 0; background: var(--cab); border: 2px solid var(--trim); border-top: 0; display: flex; align-items: center; justify-content: center; }
.cabinet .coin { padding: 3px 8px; border: 1px solid #2B3540; background: #08090C; font-family: var(--sign); font-weight: 700; font-size: 14px; color: var(--amber); }
/* Lit: its marquee glows, and its screen boots. */
.cabinet.lit .marquee { box-shadow: 0 0 18px color-mix(in srgb, var(--trim) 60%, transparent); }
.cabinet.lit .marquee img { opacity: 1; filter: none; }
.cabinet.lit .panel i:nth-child(1) { background: var(--trim); }
.cabinet.lit .panel i:nth-child(2) { background: var(--accent-1, var(--trim)); }
.cabinet.lit .panel i:nth-child(3) { background: var(--accent-2, var(--trim)); }
.cabinet.booted .screen .shot.on { opacity: 1; }
.cabinet.booting .screen { animation: crt .28s ease-out both; }
.cabinet.booting .screen .title { opacity: 1; }
@keyframes crt { from { transform: scaleY(.02); filter: brightness(3); } to { transform: none; filter: none; } }

/* ---- the zone (/room/#<id>) ---------------------------------------------------------------- */
/* Its neon sign over the row: each game a cabinet beside its back-cover plate, the coming ones dark. */
.zone-sign { flex: none; display: flex; align-items: baseline; gap: 22px; padding: 2px 6px 0; }
.zone-sign h1 { font-family: var(--sign); font-weight: 700; font-size: clamp(30px, 4vw, 58px); line-height: 1; letter-spacing: .18em; text-transform: uppercase; color: #8C96A2; }
.zone-sign.lit h1 { color: var(--neon); text-shadow: 0 0 8px var(--neon), 0 0 28px color-mix(in srgb, var(--neon) 60%, transparent); }
.zone-sign p { font-family: var(--prose); font-size: 17px; color: #C9D4DA; }
.zone-sign .count { margin-left: auto; font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--quiet); white-space: nowrap; }
.zone-sign.lit .count { color: var(--up); }
.row { display: flex; align-items: flex-end; gap: 22px; }
.bay { height: 100%; max-height: 780px; display: flex; align-items: stretch; gap: 16px; cursor: pointer; }
.bay .cabinet { height: 100%; max-height: 780px; align-self: flex-end; }
.bay .back { width: clamp(340px, 27vw, 500px); display: flex; flex-direction: column; gap: 8px; min-height: 0; overflow: hidden; }
.bay.sel .back { border: 2px solid var(--amber); }
.bay:hover .back { border-color: #3A4A55; }
.bay.sel:hover .back { border-color: var(--amber); }
.back h2 { font-family: var(--sign); font-weight: 700; font-size: 22px; letter-spacing: .1em; text-transform: uppercase; }
.back .line { font-family: var(--prose); font-size: 16px; line-height: 1.35; color: #C9D4DA; }
.back p { font-size: 13px; line-height: 1.5; }
.back .see { margin-top: auto; }
/* Its facts, as the game page's are (room/index.html's own rule went with its inline styles). */
.back .facts { display: flex; flex-wrap: wrap; gap: 6px; }
.chip.dev { color: var(--amber); border-color: #5a4a2a; }
.prog { display: flex; gap: 8px; flex-wrap: wrap; }
.tile { border: 1px solid var(--rule); padding: 6px 8px; min-width: 0; flex: 1 1 0; }
.tile .nm { display: block; font-size: 12px; letter-spacing: .06em; color: var(--quiet); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tile .st { display: block; font-size: 13px; margin-top: 2px; }
.rankline { font-size: 13px; line-height: 1.5; }
.rankline b { color: var(--amber); font-size: 18px; margin-right: 6px; }
/* Tops aligned: a name that wraps two lines lifted its cabinet out of the row (qa finding 12). */
.coming-row { display: flex; gap: 14px; align-items: flex-start; margin-left: auto; }
.covered { width: 104px; display: flex; flex-direction: column; align-items: center; gap: 6px; text-align: center; }
.covered .ghost { width: 96px; height: 190px; }
.covered .ghost::before { width: 76px; height: 20px; }
.covered .ghost::after { width: 76px; height: 54px; }
.covered span { font-family: var(--sign); font-weight: 700; font-size: 12px; letter-spacing: .06em; color: #8C96A2; line-height: 1.25; }
.covered small { font-size: 12px; color: var(--quiet); letter-spacing: .1em; text-transform: uppercase; }
/* Over the set's picture a coming game's name sits on a dark card of its own: on a phone the
   cabinet's glow behind it took it to 3.3:1 (P1-314 PR 2's sweep). */
body.has-scene .covered span, body.has-scene .covered small { background: rgba(6,7,10,.82); padding: 1px 5px; }
.zone-page .empty { align-self: center; display: flex; flex-direction: column; gap: 8px; max-width: 420px; }

/* ---- the cabinet (/game/#<id>) -------------------------------------------------------------- */
/* Standing at it: the marquee across the top, the screen and its shots, START on the control panel;
   its trim is the game's colour. The panels on the right are the game page's own (game/index.html). */
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
.cabinet-page .cover { --trim: #8496A4; }
.cabinet-page .cover .marquee { flex: none; width: 100%; max-width: 100%; aspect-ratio: 4 / 1; overflow: hidden; background: #000;
  border: 2px solid var(--trim); box-shadow: 0 0 22px color-mix(in srgb, var(--trim) 50%, transparent); }
.cabinet-page .cover .marquee img { display: block; width: 100%; height: 100%; object-fit: cover; }
.cabinet-page .cover .hood { flex: 1; min-height: 90px; display: flex; flex-direction: column; gap: 6px; padding: 10px;
  background: var(--alcove); border: 2px solid var(--trim); border-top: 0; margin-top: -10px; }
.cabinet-page .screen { flex: 1; min-height: 60px; }
.cabinet-page .screen .shot { transition: none; }
.cabinet-page .screen .shot.on { opacity: 1; }
/* A shot shows the game's own screen, its keys and all, so it says it's a picture: on 7 Nov Ryan read
   one's in-game keys as the page's only way to play (2026-11-07-ryans-arcade-walk#3). */
.cabinet-page .screen::after { content: 'Screenshot'; position: absolute; left: 6px; top: 6px; z-index: 1; pointer-events: none;
  font-size: 12px; letter-spacing: .18em; text-transform: uppercase; color: #D7E1E6; background: rgba(6,7,10,.86); padding: 2px 8px; }
.caption { font-size: 12px; line-height: 1.4; color: #B9C6CE; }
.shots { flex: none; display: flex; align-items: center; gap: 10px; }
.strip { flex: 1; min-width: 0; display: flex; gap: 6px; }
.thumb { flex: 1 1 0; min-width: 0; max-width: 96px; aspect-ratio: 16 / 9; object-fit: cover; border: 1px solid #2B3540; cursor: pointer; opacity: .7; }
.thumb.on { border-color: var(--trim); opacity: 1; }
/* The whole panel is START: its glow promised a button wider than the cap (#199's qa-playtester).
   Under it, the other ways in, smaller: the tutorial, and the levels (P1-630). */
.cabinet-page .play { padding: 0; background: #161B22; border: 2px solid var(--trim); display: flex; flex-direction: column; }
.cabinet-page .play .ctl { width: 100%; padding: 10px 12px; }
.cabinet-page .play > .ctl .key { font-size: 18px; padding: 8px 16px; background: rgba(255,178,63,.12); box-shadow: 0 0 14px rgba(255,178,63,.35); }
.cabinet-page .play .ways { display: flex; border-top: 1px solid color-mix(in srgb, var(--trim) 45%, transparent); }
.cabinet-page .play .ways .ctl { flex: 1 1 0; min-width: 0; width: auto; padding: 6px 10px; }
.cabinet-page .play .ways .ctl + .ctl { border-left: 1px solid color-mix(in srgb, var(--trim) 45%, transparent); }
/* A hint wraps rather than cut: at 721 px wide the tutorial's level name ran out (#264). */
.cabinet-page .play .ways .hint { font-size: 12px; letter-spacing: .1em; min-width: 0; line-height: 1.3; }
/* Where the cover has no room for a second row under START (a phone, a short screen, a tablet held
   upright: there it cost the panel its last lines, P1-630's fit check), the Tutorial key is on the belt
   instead, and choosing a level is the Levels panel, whose tab is in view. */
.cabinet-page #belt .start-belt { display: none; }
@media (max-width: 720px), (max-height: 640px), (min-width: 721px) and (max-width: 900px) and (max-aspect-ratio: 4/5) and (min-height: 980px) {
  .cabinet-page .play .ways { display: none; }
  .cabinet-page #belt .start-belt { display: inline-flex; }
}
/* The ladder as achievements: the selected level's rungs, lit as far as its best reached. */
.badges { grid-column: 2 / -1; display: flex; flex-wrap: wrap; gap: 6px; margin-top: 4px; }
.badge { font-size: 12px; padding: 3px 8px; border: 1px solid var(--rule); color: var(--quiet); }
.badge.got { border-color: var(--up); color: var(--up); }
/* A panel's name in a word, shown only where its full name has no room (below). */
.cabinet-page .tab .short { display: none; }
/* The high-score table's own keys, apart: side by side they read "LEVELM BOARD" (#199's qa-playtester). */
.cabinet-page .rkeys { display: inline-flex; gap: 14px; flex: none; }

/* ---- the floor and a zone as the set (P1-314 PR 3) ----------------------------------------- */
/* Ryan, 29 Oct 2026: the floor is the room seen from the door, each zone under its hanging sign, a
   click on a sign walks into the zone, and a zone is its row of cabinets. The picture is the page:
   crisp, undimmed; the keys lie on what it shows; one card says what's picked. */
body.scene-floor::after, body.scene-zone::after { background: radial-gradient(ellipse at 50% 45%, transparent 40%, rgba(6,7,10,.5) 100%); }
body.scene-floor > .scene.still img, body.scene-zone > .scene.still img { filter: none; transform: none; }
/* The stage holds only its card: the keys under it, on the picture, take the pointer. */
body.scene-floor > main, body.scene-zone > main { z-index: 2; pointer-events: none; }
.scene-stage { position: relative; flex: 1; min-height: 0; }
.scene-stage > * { pointer-events: auto; }
/* While the power comes on, the keys and the card wait: the clip, or the lights coming up. */
body.powering > .spot-layer, body.powering > main { opacity: 0; }
body.scene-floor > .spot-layer, body.scene-floor > main { transition: opacity .3s linear; }
/* Without the clip, the dark floor's still is under the lit one, which comes up over it. */
body.arcade > .scene.still.under { z-index: 0; }
body.arcade > .scene.still.dark img { opacity: 0; }
body.arcade > .scene.still img { transition: opacity .9s linear; }

/* A sign on the floor: its frame lights when picked, and a tag under it says what's in the zone. */
.sign-key { padding: 0; color: inherit; font: inherit; background: transparent; border: 2px solid transparent;
  transition: border-color .12s linear, box-shadow .12s linear; }
.sign-key:hover { border-color: color-mix(in srgb, var(--neon) 75%, transparent); box-shadow: 0 0 16px color-mix(in srgb, var(--neon) 35%, transparent); }
.sign-key.sel, .sign-key:focus-visible { border-color: var(--amber); box-shadow: 0 0 18px rgba(255,178,63,.4), inset 0 0 22px rgba(255,178,63,.12); }
.sign-key:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
.sign-key .tag { position: absolute; left: 50%; top: calc(100% + 5px); translate: calc(-50% + var(--nudge, 0px)) 0; display: inline-flex; align-items: baseline; gap: 6px;
  padding: 2px 7px; background: rgba(6,7,10,.86); white-space: nowrap; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: #9AA8B2; pointer-events: none; }
.sign-key.live .tag { color: var(--up); }
.sign-key .tag .nm { display: none; font-family: var(--sign); font-weight: 700; color: var(--neon); letter-spacing: .08em; }
.sign-key .tag .key { display: none; font-size: 12px; padding: 0 5px; border-color: var(--amber); color: var(--amber); }
.sign-key.sel .tag .key { display: inline-block; }
@media (pointer: coarse) { .sign-key.sel .tag .key { display: none; } }
/* A sign is wide and short: its key is a finger's width, and only as tall as keeps the signs that
   hang one over another on a phone apart, and clear of the cabinets under the back signs (24 px,
   WCAG 2.5.8's minimum; 26 ran 1 px into Tidy's key at 390×844). */
.sign-key { min-height: 24px; }
/* A far cabinet in a row seen on a slant is narrow: its key keeps its width, so its neighbours'
   don't run into it (24 px at least, WCAG 2.5.8). */
.cab-key { min-width: 24px; }
/* Upright, the room is narrow and its signs small: only the picked sign's tag shows, and it names
   its zone too; on a short screen, only the picked one's (the tags ran over the signs at 844×390). */
.frame.tall .sign-key .tag .nm { display: inline; }
.frame.tall .sign-key:not(.sel) .tag { display: none; }
@media (max-height: 520px) { .sign-key:not(.sel) .tag { display: none; } }
.sign-key.sel .tag { outline: 1px solid var(--amber); }

/* The zone picked, in the floor's corner: its name in its neon, what's in it and your results. */
.zone-card { position: absolute; left: 0; bottom: 0; width: min(400px, 38vw); display: flex; flex-direction: column; gap: 7px;
  padding: 12px 16px 14px; border-left: 3px solid var(--neon); }
/* Or at the foot's middle, or its other corner, where it covers least of the picture's keys (floor-scene.js). */
.zone-card[data-side="centre"] { left: 50%; translate: -50% 0; }
.zone-card[data-side="right"] { left: auto; right: 0; }
.zone-card header { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
.zone-card h2 { font-family: var(--sign); font-weight: 700; font-size: 22px; letter-spacing: .14em; text-transform: uppercase; color: #C9D4DA; }
.zone-card.lit h2 { color: var(--neon); text-shadow: 0 0 8px color-mix(in srgb, var(--neon) 60%, transparent); }
.zone-card .count { font-size: 12px; letter-spacing: .12em; text-transform: uppercase; color: var(--quiet); white-space: nowrap; }
.zone-card.lit .count { color: var(--up); }
.zone-card .blurb { font-family: var(--prose); font-size: 15px; line-height: 1.35; color: #B9C6CE; }
.zone-card .games { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.zone-card .games li { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.zone-card .games b { font-family: var(--sign); font-size: 15px; letter-spacing: .08em; text-transform: uppercase; }
/* Named apart from the drawn floor's .soon, whose rules hide it on small screens. */
.zone-card .next { font-size: 12px; line-height: 1.5; color: var(--quiet); }
.zone-card .next b { color: #B9C6CE; font-weight: 400; }
.zone-card .see { margin-top: 2px; }

/* A zone: its name in a plate over the picture, its cabinets keys, the card beside the one picked. */
/* No wider than the gap a back zone's sign leaves in its shot, which hangs from the middle. */
body.scene-zone .zone-sign { align-self: flex-start; display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 16px; max-width: min(520px, 100%);
  padding: 8px 14px; background: rgba(13,17,22,.84); backdrop-filter: blur(6px); border-left: 3px solid var(--neon); }
body.scene-zone .zone-sign h1 { font-size: clamp(24px, 3vw, 40px); }
body.scene-zone .zone-sign p { font-size: 15px; flex-basis: 100%; order: 3; }
body.scene-zone .zone-sign .count { margin-left: 0; }
body.scene-zone .zone-sign .where { font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--quiet); white-space: nowrap; }
/* A step to the next stop: its still comes in over the last, moving the way the player turned, as a
   short walk would (zone-scene.js go); with reduced motion it fades (below). */
body.scene-zone > .scene.still.arriving { opacity: 0; translate: var(--from, 0) 0; }
body.scene-zone > .scene.still.arriving.arrived { opacity: 1; translate: 0 0; transition: opacity .32s ease-out, translate .42s ease-out; }
/* The walk keys at the window's edges, each named for the cabinet it walks to, over the picture. */
.walk-key { position: fixed; z-index: 2; top: 74%; translate: 0 -50%; display: flex; align-items: center; gap: 8px; max-width: min(240px, 34vw);
  padding: 8px 12px; background: rgba(13,17,22,.84); backdrop-filter: blur(6px); border: 1px solid var(--edge); clip-path: var(--cut); cursor: pointer;
  pointer-events: auto; color: var(--ink); font: inherit; }
.walk-key.to-left { left: 16px; }
/* On a phone held upright the card docks over the belt: the walk keys stand above it. On a short
   screen there's no room for them beside the card and the cabinet: the arrows, the belt's ← → and a
   swipe walk there. */
@media (max-width: 720px) and (min-height: 521px) {
  .walk-key { top: 36%; padding: 4px 8px; }
  /* A name of three lines fits the gap between the zone's sign and the cabinet (the NOC, 390×844). */
  .walk-key .hint { line-height: 1.15; }
}
@media (max-height: 520px) { .walk-key { display: none; } }
.walk-key.to-right { right: 16px; flex-direction: row-reverse; }
/* A name wraps rather than cut: on a phone every one ran out ("RAIL RO…"; P1-628's walk). */
.walk-key .hint { font-size: 12px; line-height: 1.25; letter-spacing: .12em; text-transform: uppercase; min-width: 0; }
/* The belt's pair of walks is a phone on its side's alone: with touch, where the walk keys go. */
.belt .walk-belt { display: none; }
@media (max-height: 520px) and (pointer: coarse) { .belt .walk-belt:not([hidden]) { display: inline-flex; } }
/* The page takes no sideways pan, so a swipe walks (zone-scene.js). */
body.scene-zone { touch-action: pan-y; }
.walk-key:hover, .walk-key:focus-visible { border-color: color-mix(in srgb, var(--neon, var(--amber)) 75%, transparent); }
/* Its press: the edge lit and the key pressed in, 80 ms back (game-feel's key press). */
.walk-key { transition: scale 80ms ease-out, border-color 80ms; }
.walk-key.armed { border-color: var(--amber); scale: .96; }
.walk-key:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
/* The zone in plain words (P1-629): its name in full, a line under each game's pitch, a coming game's
   idea, and the panel that says what the zone is, open on a first visit. */
body.scene-zone .zone-sign .ctl { order: 4; }
body.scene-zone .zone-sign .full { font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: #C9D4DA; flex-basis: 100%; order: 2; }
.cab-card .plain { font-family: var(--prose); font-size: 15px; line-height: 1.35; color: #DCE6EA; }
.cab-card .plain b { color: var(--amber); font-weight: 600; }
.cab-card .later { font-size: 12px; color: var(--quiet); }
.zone-about { position: fixed; z-index: 4; left: 50%; top: 50%; translate: -50% -50%; width: min(560px, calc(100vw - 32px));
  max-height: calc(100dvh - 160px); overflow: hidden; display: flex; flex-direction: column; gap: 10px; border: 2px solid var(--neon, var(--amber)); }
.zone-about h2 { font-family: var(--sign); font-weight: 700; font-size: 26px; letter-spacing: .1em; text-transform: uppercase; color: var(--neon, var(--ink)); }
.zone-about .what { font-family: var(--prose); font-size: 17px; line-height: 1.45; color: #DCE6EA; }
.zone-about .here { display: flex; flex-direction: column; gap: 6px; }
.zone-about .item { display: flex; flex-direction: column; gap: 2px; padding-left: 10px; border-left: 2px solid var(--edge); }
.zone-about .item.lit { border-left-color: var(--amber); }
.zone-about .item b { font-size: 13px; letter-spacing: .1em; text-transform: uppercase; }
.zone-about .item span { font-family: var(--prose); font-size: 15px; color: #C9D4DA; }
/* Too tall for its room (zone-scene.js fitAbout): the coming games by name alone, then the list beside
   the words, then every game by name alone. */
.zone-about.lean .item:not(.lit) span, .zone-about.leaner .item span { display: none; }
.zone-about.beside { display: grid; grid-template-columns: 1fr 1fr; column-gap: 20px; align-content: start; width: min(860px, calc(100vw - 32px)); }
.zone-about.beside > :not(.here) { grid-column: 1; }
.zone-about.beside > .here { grid-column: 2; grid-row: 1 / span 4; }
.zone-about .keys { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.zone-about .turn { display: none; }
.zone-about.paged .turn { display: flex; }
.zone-about.paged:not(.on-list) > .here, .zone-about.paged.on-list > .what { display: none; }
/* On a short screen its words a size smaller, so its Close keeps above the belt (667×375). */
@media (max-height: 520px) {
  .zone-about { gap: 6px; padding: 10px 14px; }
  .zone-about h2 { font-size: 20px; }
  .zone-about .what { font-size: 15px; line-height: 1.35; }
}
body.about-open::after { background: rgba(6,7,10,.7); }
body.about-open > .spot-layer, body.about-open .walk-key, body.about-open .cab-card, body.about-open .zone-banners { visibility: hidden; }
/* The high scores, banners hung over the scene at its top right (P1-629), each edged in its game's
   trim; zone-scene.js hangs them just under the bar. */
.zone-banners { position: fixed; z-index: 2; top: 92px; right: 16px; display: flex; gap: 10px; pointer-events: none; }
.banner { width: 210px; padding: 10px 12px 18px; background: rgba(13,17,22,.88); backdrop-filter: blur(6px); border-top: 3px solid var(--trim, var(--amber));
  clip-path: polygon(0 0, 100% 0, 100% 100%, 50% calc(100% - 10px), 0 100%); }
.banner small { display: block; font-size: 12px; letter-spacing: .16em; text-transform: uppercase; color: var(--quiet); }
.banner h3 { font-family: var(--sign); font-weight: 700; font-size: 15px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink); margin: 2px 0 6px; }
.banner .tops { list-style: none; padding: 0; display: flex; flex-direction: column; gap: 3px; }
.banner .tops li { display: flex; align-items: baseline; gap: 8px; font-size: 13px; }
.banner .tops b { font-family: var(--sign); font-size: 16px; color: var(--amber); min-width: 14px; }
.banner .tops .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.banner .tops .st { color: var(--quiet); }
.banner .tops .none { font-size: 12px; color: var(--quiet); }
/* No room for banners beside the card and the cabinet on a phone or a short screen: the game's page
   has its high scores. */
@media (max-width: 900px), (max-height: 640px) { .zone-banners { display: none; } }
/* With touch, no cap: an arrow points the way, as the cards' keys drop theirs (P1-307's one rule for caps). */
@media (pointer: coarse) {
  .walk-key .key { display: none; }
  .walk-key.to-left::before { content: '‹'; font-size: 22px; line-height: 1; color: var(--amber); }
  .walk-key.to-right::before { content: '›'; font-size: 22px; line-height: 1; color: var(--amber); }
}
.cab-key { padding: 0; background: transparent; border: 2px solid transparent; transition: border-color .12s linear, box-shadow .12s linear; }
.cab-key.game:hover { border-color: color-mix(in srgb, var(--neon, #8496A4) 80%, transparent); }
.cab-key.sel, .cab-key:focus-visible { border-color: var(--amber); box-shadow: 0 0 20px rgba(255,178,63,.35), inset 0 0 26px rgba(255,178,63,.1); }
.cab-key.coming.sel { border-style: dashed; }
.cab-key:focus-visible { outline: 2px solid var(--amber); outline-offset: 3px; }
/* At most 38% of the window: it stands beside the cabinet, which stands in the middle (P1-628). */
.cab-card { position: fixed; z-index: 2; width: min(400px, 38vw); display: flex; flex-direction: column; gap: 8px;
  max-height: calc(100dvh - 180px); overflow: hidden; border: 2px solid var(--amber); }
.cab-card.coming { border: 2px dashed #5A6570; }
.cab-card h2 { font-family: var(--sign); font-weight: 700; font-size: 22px; letter-spacing: .1em; text-transform: uppercase; }
.cab-card .line { font-family: var(--prose); font-size: 16px; line-height: 1.35; color: #C9D4DA; }
.cab-card p { font-size: 13px; line-height: 1.5; }
.cab-card .facts { display: flex; flex-wrap: wrap; gap: 6px; }
.cab-card .when { font-size: 12px; letter-spacing: .14em; text-transform: uppercase; color: var(--quiet); }
/* The card lies over the cabinets beside the one picked: a mouse goes through it to them, all but
   its own key (the loadout-review's mouse replay: two keys 60–68% covered at 1280×800), the key
   alone and not its row (the key itself still lies over two Server Hall cabinets' centres at
   1440×900, retro 19). A finger doesn't, on a touch screen of any kind: a tap on the card's words
   landed on the cabinet under them (#208's and #209's gate-reviews). On a touch-screen laptop that
   holds for its mouse too: the media query can't tell the two apart (#210's loadout-review). */
@media (hover: hover) and (pointer: fine) {
  .scene-stage > .cab-card { pointer-events: none; }
  .cab-card .see .ctl { pointer-events: auto; }
}
@media (any-pointer: coarse) { .scene-stage > .cab-card { pointer-events: auto; } }
/* The floor's games, keys over their cabinets: lit with their zone's sign. */
.floor-cab.sel { border-color: color-mix(in srgb, var(--amber) 70%, transparent); box-shadow: 0 0 14px rgba(255,178,63,.3); }
.cab-card .see { margin-top: 2px; }
/* A card taller than the room it has sheds what it can spare (zone-scene.js place). */
.cab-card.squeezed .practise, .cab-card.squeezed .prog { display: none; }
.cab-card.squeezed-more .facts { display: none; }
.cab-card.squeezed-most .rankline { display: none; }
/* Last, the pitch where the plain words say the game: they're what a newcomer reads (P1-629). */
.cab-card.squeezed-pitch .line:has(+ .plain) { display: none; }
/* And on a phone on its side, its progress line: its name, its idea and its Step up stay (667×375). */
.cab-card.squeezed-last > small:not([class]) { display: none; }
/* A coming cabinet's last line to go is "on its way": its own line says what it'll be about. */
.cab-card.coming.squeezed-pitch .line + .later { display: none; }
/* Upright on a phone, the card sits over the belt, full width, where a hand doesn't cover it. */
@media (max-width: 720px) and (min-height: 521px) {
  .zone-card { width: 100%; }
  .zone-card .blurb { display: none; }
  .cab-card { left: 12px; right: 12px; bottom: 74px; width: auto; max-height: 46dvh; }
  .cab-card .practise, .cab-card .prog { display: none; }
}
/* On a short screen (a phone on its side) the cards keep to their names and what matters most. */
@media (max-height: 520px) {
  .zone-card { width: min(340px, 44vw); padding: 8px 12px; gap: 4px; }
  .zone-card .blurb, .zone-card .next { display: none; }
  .cab-card { max-height: calc(100dvh - 120px); gap: 5px; padding: 8px 12px; }
  /* Beside a cabinet in the middle it has 38% of the window: its name on one line (667×375). */
  .cab-card h2 { font-size: 18px; }
  .cab-card .line, .cab-card .plain { font-size: 14px; line-height: 1.3; }
  body.scene-zone .zone-sign h1 { font-size: 20px; }
  .cab-card .practise, .cab-card .prog, .cab-card .facts { display: none; }
  body.scene-zone .zone-sign p { display: none; }
}
/* At 380 px tall or less (640×360, 800×360) the card's own spacing closes a little more. */
@media (max-height: 380px) {
  .cab-card { gap: 3px; padding: 6px 10px; }
}
@media (prefers-reduced-motion: reduce) {
  body.arcade > .scene.still img, .sign-key, .cab-key, body.scene-floor > .spot-layer, body.scene-floor > main { transition: none; }
  body.scene-zone > .scene.still.arriving { translate: 0 0; }
  body.scene-zone > .scene.still.arriving.arrived { transition: opacity .2s linear; }
  .walk-key.armed { scale: none; }   /* the edge alone shows the press */
}

/* ---- the neon's life (P1-632) ----------------------------------------------------------------
   Ryan, 7 Nov 2026: "The sounds, neon lights, and items you would normally expect to see in an
   archade in the 80s are not there." A lit tube isn't still: now and then it catches, dims for a
   moment and comes back. Each sign at its own pace, so they never dip together, and a dip at most
   every few seconds (WCAG 2.3.1 allows three flashes a second); his ask is the exception to an idle
   screen's stillness, as the cabinets' showcase was (P1-313). Steady with reduced motion. */
@keyframes neon-life { 0%, 93%, 95.5%, 100% { opacity: 1; } 93.5% { opacity: .55; } 94.5% { opacity: .82; } }
@keyframes neon-glow { 0%, 89%, 92%, 100% { filter: none; } 90% { filter: brightness(.7); } 91% { filter: brightness(.9); } }
body.scene-zone .zone-sign.lit h1, .zone-sign.lit h1 { animation: neon-life 7.3s steps(1, end) infinite; }
/* Not while the drawn power-on's strike runs (buzz, taken off once it ends): it would take the
   strike's place, or stretch it to the flicker's length (#272's gate-review). :where() adds no weight,
   so reduced motion's rule below still wins (#272's round 2). */
.zone.lit:where(:not(.buzz)) .sign h2 { animation: neon-life 9.1s steps(1, end) infinite; }
.zone.lit:where(:not(.buzz)):nth-child(2n) .sign h2 { animation-duration: 11.7s; }
.cabinet-page .cover .marquee { animation: neon-glow 8.6s steps(1, end) infinite; }
.banner { animation: neon-glow 10.3s steps(1, end) infinite; }
@media (prefers-reduced-motion: reduce) {
  body.scene-zone .zone-sign.lit h1, .zone-sign.lit h1, .zone.lit .sign h2, .cabinet-page .cover .marquee, .banner { animation: none; }
}

/* ---- the power ----------------------------------------------------------------------------- */
/* Walking in through the door turns the power on (Ryan: "turned the main floor power on and
   everything comes to life"). Until it does, the floor is dark; the room's lights flicker on once,
   no more than three flashes a second (WCAG 2.3.1), then each lit zone's sign, then its cabinets. */
/* The dark is on what's drawn (the bar, the zones, the belt), not under it: the carpet sits beneath
   them, so dimming it alone left the floor lit (#198's gate-review). */
body.power-off::before { opacity: .08; }
body.power-off > * { filter: brightness(.1); }
body.power-off.lamp > * { filter: brightness(.6); }
.zone.buzz .sign h2 { animation: buzz .26s linear both; }
@keyframes buzz { 0% { opacity: 0; } 25% { opacity: 1; } 50% { opacity: .55; } 100% { opacity: 1; } }

@media (prefers-reduced-motion: reduce) {
  /* The beats and the sound stay; the movement goes: no flicker, no buzz, no boot, and no clips (scenes.js). */
  body.power-off.lamp > * { filter: brightness(.1); }
  .zone.buzz .sign h2, .cabinet.booting .screen { animation: none; }
  .screen .shot, .screen .title { transition: none; }
}

/* ---- small screens ------------------------------------------------------------------------- */
@media (pointer: coarse) { .door .enter .key { display: none; } }
/* Where the bar is short of room, the floor's crumbs keep the step out and the name, not the tip. */
@media (max-height: 520px) { .crumbs .tip { display: none; } }
/* A phone on its side, or any short screen: each zone its sign and its games' marquees and results;
   the screens and bodies wait for the zone's own page. A short screen is short whatever its width:
   these rules started at 721 px, and a phone on its side under that (667×375) got the upright
   phone's stacked floor, under the belt (P1-307). */
@media (max-height: 520px) {
  .zones { gap: 8px; }
  /* Short of width too: the count keeps what's to play, and a long name wraps rather than clips. */
  .sign { padding: 8px 12px 6px; flex-wrap: wrap; row-gap: 2px; }
  .sign .count .more { display: none; }
  .sign h2 { font-size: 16px; }
  .zone .blurb, .soon, .about .what { display: none; }
  .aisle { padding: 8px 12px 0; gap: 10px; align-items: flex-start; }
  .cabinet { height: auto; width: 120px; aspect-ratio: auto; }
  .cabinet .hood, .cabinet .panel, .cabinet .body { display: none; }
  .zone.dark .aisle { display: none; }
  .about h3 { font-size: 14px; }
  /* The street's plate keeps what the arcade is and what's in it; the welcome waits for the floor. */
  .street-greet { padding: 6px 12px; gap: 2px; }
  .street-greet b, .street-greet span:not(.count) { display: none; }
}
@media (max-width: 900px) { .cabinet-page .cover .marquee { width: 180px; } }
/* A tablet held upright: the cover runs across the top, its screen and strip a row each. */
@media (max-width: 900px) and (min-width: 721px) and (max-aspect-ratio: 4/5) and (min-height: 980px) {
  .cabinet-page .cover .hood { flex: 1 1 100%; height: 24vh; margin-top: 0; border-top: 2px solid var(--trim); }   /* 30vh left the panel 84 px at 768×1024 */
  .cabinet-page .shots { flex: 1 1 100%; }
}
/* A phone, either way up: the cabinet keeps its marquee and START; the screen waits for a bigger one. */
@media (max-width: 720px), (max-height: 520px) {
  .cabinet-page .cover .marquee { width: 180px; }
  .cabinet-page .cover .hood, .cabinet-page .shots { display: none; }
}
/* A short screen: the cabinet's name as text beside its marquee, since the crumbs drop it there. */
@media (max-height: 520px) {
  .cabinet-page .cover h1.sr { position: static; width: auto; height: auto; overflow: visible; clip-path: none; white-space: normal; }
  /* In place of the zone's name, which the crumbs keep, so START stays on the plate. */
  .cabinet-page .cover > small { display: none; }
}
/* A zone on a short screen: the bay keeps its marquee and its plate's name, line and facts. */
@media (max-height: 520px) {
  .zone-sign h1 { font-size: 28px; }
  .zone-sign p { display: none; }
  .bay { align-items: flex-start; }
  .bay .cabinet { height: auto; width: 150px; aspect-ratio: auto; align-self: flex-start; }
  .back .practise, .prog, .rankline, .coming-row { display: none; }
}
/* A phone held upright: one zone a row, its sign and its games' marquees and results. Upright only:
   on its side a phone is a short screen, above. */
@media (max-width: 720px) and (min-height: 521px) {
  .zones { grid-template-columns: 1fr; grid-template-rows: none; grid-auto-rows: min-content; align-content: start; gap: 8px; }
  .sign { padding: 10px 12px 6px; }
  .sign h2 { font-size: 18px; }
  .zone .blurb, .soon, .about .what, .valance { display: none; }
  /* The zone picked says what its game is, the line the others leave for their own page. */
  .zone.sel .about .what { display: -webkit-box; font-size: 13px; line-height: 1.4; }   /* keeps its 3-line clamp */
  .aisle { padding: 6px 12px 10px; gap: 10px; align-items: center; }
  .cabinet { height: auto; width: 112px; aspect-ratio: auto; }
  .cabinet .hood, .cabinet .panel, .cabinet .body { display: none; }
  .about { padding-bottom: 0; }
  .about h3 { font-size: 14px; }
  .zone.dark .aisle { display: none; }
  .street-greet { position: static; order: -1; margin-bottom: 10px; align-self: stretch; max-width: none; }
  /* A zone on a phone: one bay a screen's width, its marquee over its plate. */
  .zone-sign { flex-wrap: wrap; gap: 4px 14px; }
  .zone-sign h1 { font-size: 26px; }
  .zone-sign p { font-size: 14px; flex-basis: 100%; order: 3; }
  .bay { flex-direction: column; height: auto; gap: 8px; }
  .bay .cabinet { height: auto; width: 100%; max-width: 360px; aspect-ratio: auto; align-self: flex-start; }
  .bay .back { width: auto; }
  /* Its plate keeps the totals line and the overall place, and drops the level tiles and the best
     level's place (the cabinet's Levels panel has both), so the row ends above the belt (#199's
     loadout-review: it ran under it). */
  .back .practise, .back .prog, .back .rankline + .rankline { display: none; }
}
/* The cabinet's level card on a phone, either way up: the selected card fits a page of the panel,
   since the pager can't cut one card and clipped it. Its badges give way to its stars and the Rules
   tab's ladder, and "Its rankings" to the High scores tab's 4 just above it; its result and keys
   take the card's full width. */
@media (max-width: 720px), (max-height: 520px) {
  .cabinet-page .lvr .badges, .cabinet-page .lvr .acts [data-key="4"] { display: none; }
  .cabinet-page .lvr .sub, .cabinet-page .lvr .acts { grid-column: 1 / -1; }
}
@media (max-height: 520px) { .cabinet-page .lvr { padding: 4px 10px; } }
/* A phone held upright, in its browser (390×664, 360×560): the cover's marquee and START took the
   height a page of the panel needs, with five panels named in full on two rows, and the panel was
   0 px tall (P1-307). The panels are named in a word each, as on its side, the zone's name is the
   crumbs', and START sits lower, its words on one line, the panel picked lit amber. With their caps
   the five names still took two rows at 375 px, so at 400 px wide or less they drop them (below). */
/* One rule for caps: with touch the cabinet's keys drop theirs, as the belt and the crumbs do (its
   panels, START, a level's keys, the pager's N, a cover's shots, High scores' level and board; on a
   touch screen they kept them while the crumbs dropped theirs), and so do the floor's, a zone's and
   a room's cards' ↵. The panel picked is lit too, its underline marking it at every size. A mouse
   keeps every cap, since its keys can be pressed, but for the panels' at 400 px wide or less, for
   want of a row (P1-307, `2026-10-28-site-phones#6`). */
@media (pointer: coarse) {
  .cabinet-page .tab .key { display: none; }
  .cabinet-page .play .key { display: none; }
  .cabinet-page .lvr .acts .key { display: none; }
  .cabinet-page .pager .key { display: none; }
  .cabinet-page .shots .key { display: none; }
  .cabinet-page .rkeys .key { display: none; }
  .see .key { display: none; }
  /* A card's ↵ is a phone's way in (floor-scene.js): without its cap it keeps an edge, as a tap key. */
  .see .ctl { border: 1px solid var(--amber); padding: 6px 10px; min-height: 40px; }
  .cabinet-page .tab.on .hint { color: var(--amber); }
  /* Without its cap a key keeps an edge, as the belt's taps do, or it reads as a label: High scores'
     Level and Board sat over the table like its columns' names (#220's gate-review). */
  .cabinet-page .lvr .acts .ctl, .cabinet-page .pager .ctl, .cabinet-page .shots .ctl, .cabinet-page .rkeys .ctl { border: 1px solid var(--edge); padding: 4px 10px; }
  /* Its press lights that edge, as site.css's `.ctl.armed` does the belt's; the edge above outweighs it,
     a level's keys by a class more (#223's reviews: Play's edge stayed grey). */
  .cabinet-page .lvr .acts .ctl.armed, .cabinet-page .ctl.armed { border-color: var(--amber); }
  /* A zone's about key, and its panel's keys, the same (#268's reviews: I and N showed on a phone). */
  body.scene-zone .zone-sign .key, .zone-about .key { display: none; }
  body.scene-zone .zone-sign .ctl, .zone-about .ctl { border: 1px solid var(--edge); padding: 4px 10px; min-height: 40px; }
  /* Their press lights the edge, as the cabinet's keys' does (#268's reviews). */
  body.scene-zone .zone-sign .ctl.armed, .zone-about .ctl.armed { border-color: var(--amber); }
  /* A phone on its side has no height to spare: the sign's key 30 px, or the NOC's card ran 6 px
     under the belt at 800×360 (P1-634's fit check). */
  @media (max-height: 520px) { body.scene-zone .zone-sign .ctl { min-height: 30px; padding: 2px 10px; } }
}
@media (max-width: 400px) and (min-height: 521px) {
  .cabinet-page .tab .key { display: none; }
}
@media (max-width: 720px) and (min-height: 521px) {
  .cabinet-page .cover { gap: 6px 14px; }
  .cabinet-page .cover > small { display: none; }
  .cabinet-page .play .ctl { padding: 6px 10px; }
  .cabinet-page .play .ctl .key { font-size: 14px; padding: 4px 10px; }
  .cabinet-page .play .hint { font-size: 13px; letter-spacing: .08em; }
  .cabinet-page .tabs { gap: 4px 14px; }
  .cabinet-page .tab.on .hint { color: var(--amber); }
  .cabinet-page .tab .full { display: none; }
  .cabinet-page .tab .short { display: inline; }
  /* A touch screen's "Made for a mouse." keeps to the marquee's row: in full it took a line of its
     own, and Tidy's last level card held 108 px in 89 at 683×657 (`2026-10-28-site-half-screen#9`). */
  .cabinet-page .warn .more { display: none; }
  /* A zone: its marquee narrower over its plate. */
  .bay .cabinet { max-width: 240px; }
}
/* A phone's browser up to 760 px tall: the zone's line gives way, as its sign's count says what's
   there, and its marquee narrows, so the bay and the games coming end above the belt (they ran 14 px
   under at 390×664). */
@media (max-width: 720px) and (min-height: 521px) and (max-height: 760px) {
  .zone-sign p { display: none; }
  .bay .cabinet { max-width: 180px; }
}
/* A small phone's browser, up to 700 px tall: the cabinet's START beside a narrower marquee, "Made
   for a mouse." under both, the panel's text a size down and a level card's results line left to
   High scores and Your record, as on a short screen, so a page of it fits (a How to play block was
   141 px in a 26 px page at 375×553); a zone's games coming left to its sign's count; the floor's
   lit zones closer, the picked one's line left to its zone's page. */
@media (max-width: 720px) and (min-height: 521px) and (max-height: 700px) {
  .zone.sel .about .what { display: none; }
  .cabinet-page .cover .marquee { width: 120px; }
  .cabinet-page .cover .play { flex: 1 1 0; min-width: 0; }
  .cabinet-page .cover .warn { order: 1; flex-basis: 100%; }
  .cabinet-page .body p, .cabinet-page .body li { font-size: 12px; line-height: 1.4; }
  .cabinet-page .lvr .sub { display: none; }
  .coming-row { display: none; }
  .zones .cabinet { width: 96px; }
  .aisle { padding: 4px 12px 8px; }
}
/* A zone on a screen narrower than its row, held upright (a phone or a tablet): the bay across the
   screen, and the games coming one line of names under it, not a row of dark cabinets beside it
   (#199: at 768 px the plate ran off the screen, and on a phone the row ran under the belt). */
@media (max-width: 900px) and (min-height: 521px) {
  .row { flex-direction: column; align-items: stretch; gap: 10px; }
  .coming-row { margin-left: 0; flex-wrap: wrap; gap: 4px 14px; align-items: baseline; }
  .coming-row .ghost, .covered small { display: none; }
  .coming-row::before { content: 'Coming soon'; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--quiet); }
  .covered { width: auto; flex-direction: row; gap: 8px; }
}
/* Too narrow for three zones (`TWO_UP`: a tablet either way up, a small laptop window): the floor
   in two columns, its rows set by dashboard.js (even on an upright tablet), its covered cabinets
   shorter, so a dark zone's blurb and names fit above and below them. At 1024×768 in three, Tidy's
   name and the counts ran out of their zones (P1-307). */
@media (min-width: 721px) and (max-width: 1180px) and (min-height: 521px) {
  .zones { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .zone.dark .ghost { width: 44px; height: 76px; }
  .zone.lit .soon .ghosts { display: none; }   /* a lit zone names its coming games; its game's line needs the row */
  .about .what { -webkit-line-clamp: 2; }   /* the room it has, so its cut ends in an ellipsis */
  /* Its two lines kept whole, and the coming games' names clamped instead: a returning player's belt
     takes a second row, and the line was squeezed to a third of its second (#200's loadout-review). */
  .zone.lit .about .what { flex-shrink: 0; }
  .zone.lit .soon p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
}
/* A laptop's browser, over 520 px tall but under 761 (1366×657, 1280×650, 1536×730, the most common
   windows): every page ran past its room (P1-307). On the floor a dark zone's covered cabinets stand
   beside its names, not over its blurb, and a lit zone's line keeps two lines; a zone's plate leaves
   its level tiles and its best place to the cabinet's Levels panel; the cabinet's cover leaves its
   pitch to How to play's theme, and its caption takes a line. */
@media (min-width: 721px) and (min-height: 521px) and (max-height: 760px) {
  .sign { padding: 8px 14px 6px; }
  .zone .blurb { padding-top: 6px; }
  .aisle { padding-top: 8px; }
  .zone.dark .aisle { flex-direction: row; justify-content: flex-start; align-items: center; gap: 14px; padding-bottom: 8px; }
  .zone.dark .ghost { width: 34px; height: 62px; }
  .zone.dark .soon { align-items: flex-start; text-align: left; }
  .zone.lit .soon .ghosts { display: none; }
  .about { padding-bottom: 8px; }
  .about .what { -webkit-line-clamp: 2; }
  .zone.lit .about .what { flex-shrink: 0; }
  .zone.lit .soon p { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
  .back .prog, .back .rankline + .rankline { display: none; }
  .bay .back { gap: 6px; }
  .cabinet-page .cover { gap: 8px; }
  .cabinet-page .cover .line { display: none; }
  .cabinet-page .caption { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
}
/* A laptop's browser 640 px tall or less (1280×560, a 1280×720 screen's; 1920×600): the cabinet's
   cover ran its START 4 to 87 px past its foot, the wider the more, since its marquee grows with the
   width (P1-307, after #214's gate-review). It leaves its zone's name to the crumbs and its facts to
   the panels, as a short screen's does, but for a draft's "In development", which nothing else on
   its page says (#215's gate-review), and its marquee keeps to 320 px (at 400 the kept chip ran
   Header Drop's 15 px over at 1920×530). */
@media (min-width: 721px) and (min-height: 521px) and (max-height: 640px) {
  .cabinet-page .cover > small, .cabinet-page .cover .facts:not(.draft), .cabinet-page .cover .facts .chip:not(.dev) { display: none; }
  .cabinet-page .cover .marquee { max-width: 320px; align-self: center; }
  /* And the picked level's card, taller than a page of the panel at 880×540 and 721×600, cut its
     keys: it leaves Its rankings to the tabs' 4, which does the same, and its rows sit closer
     (P1-307, `2026-10-28-site-half-screen#5`). */
  .cabinet-page .lvr .acts .ctl + .ctl:last-child { display: none; }
  .cabinet-page .lvr { padding: 6px 10px; gap: 2px 10px; }
}
/* With a mouse it leaves Watch to the belt's W as well. Not on touch, where the belt keeps only its
   tap keys and W isn't one: there the card's Watch was the only one on the page (#217's reviews),
   and the belt's single row leaves the card the room for it. */
@media (min-width: 721px) and (min-height: 521px) and (max-height: 640px) and (pointer: fine) {
  .cabinet-page .lvr .acts .ctl + .ctl { display: none; }
}
/* Between a tablet and a laptop (901–1180 px wide): the floor's dark zones in its shorter rows, their
   covered cabinets beside their names; a zone's coming games a column of names beside its bay,
   since their covered cabinets ran the row off the screen at 1024 px; a game's crumbs keep the two
   steps up, its cover naming it, since the whole path cut at 960 px (P1-307). The floor's hint stays,
   on a line of its own (site.css). */
@media (min-width: 901px) and (max-width: 1180px) and (min-height: 521px) {
  .zone.dark .aisle { flex-direction: row; justify-content: flex-start; align-items: center; gap: 14px; }
  .zone.dark .soon { align-items: flex-start; text-align: left; }
  .coming-row { flex-direction: column; gap: 8px; align-self: center; }
  .coming-row .ghost, .covered small { display: none; }
  .coming-row::before { content: 'Coming soon'; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: var(--quiet); }
  .covered { width: auto; max-width: 160px; align-items: flex-start; text-align: left; }
  .crumbs .sep ~ .sep, .crumbs .sep ~ .sep + .here { display: none; }
}
/* Two columns on a laptop-height screen (1024×650, an iPad on its side): a dark zone is its sign,
   blurb and names, its covered cabinets left for its own page, since centred in what height was
   left they ran up over its blurb (P1-307). At 640 px tall or less a lit zone's coming games give
   way too, its zone's page naming them, and a zone's plate clamps its line to three. */
@media (min-width: 721px) and (max-width: 1180px) and (min-height: 521px) and (max-height: 760px) {
  .zone.dark .ghosts { display: none; }
}
@media (min-width: 721px) and (max-width: 1180px) and (min-height: 521px) and (max-height: 640px) {
  .zone.lit .soon { display: none; }
  .zone.dark .aisle { padding-top: 4px; }
  .zones { gap: 8px; }
  .sign { padding: 6px 14px 4px; }   /* with the gap, 901×600's lit zones fit (4 px over) */
  .cabinet-page .cover > small { display: none; }   /* the crumbs name the zone: Header Drop's cover ran 12 px over at 800×600 */
  .back .line { display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden; }
}
/* A tablet: the cabinet beside its plate, at a width the plate can stand beside. */
@media (max-width: 900px) and (min-width: 721px) and (max-aspect-ratio: 4/5) and (min-height: 980px) {
  .bay { height: auto; align-items: flex-start; }
  .bay .cabinet { height: auto; width: 40%; max-width: 340px; align-self: flex-start; }
  .bay .back { flex: 1; width: auto; min-width: 0; }
}
/* A short screen: the plate wider and its gaps tighter, so its facts take one line and it ends above
   the belt (#199: Header Drop's ran 26 px under it at 844×390). */
@media (max-height: 520px) {
  .bay .back { width: min(480px, 62vw); gap: 6px; }
}
/* A phone on its side under 721 px wide (667×375), or any screen 380 px tall or less (800×360, a
   wide Android's): the cabinet's panels named in a word each, so they take one row and leave the
   panel room for a page; a level card's results line gives way to High scores and Your record,
   which carry it; the zone's marquee narrower, so its plate takes the width and ends above the
   belt (P1-307: the panel was 0 px tall, and the zone ran under the belt). */
@media (max-width: 720px) and (max-height: 520px), (max-height: 380px) {
  .cabinet-page .cover { gap: 4px; }
  .cabinet-page .play .ctl { padding: 6px 10px; }
  .cabinet-page .warn .more { display: none; }   /* "Made for a mouse.", as the zone's chip says */
  .cabinet-page .tabs { gap: 4px 10px; }
  .cabinet-page .tab .full { display: none; }
  .cabinet-page .tab .short { display: inline; }
  .cabinet-page .lvr .sub { display: none; }
  .cabinet-page .rhead { flex-wrap: wrap; }
  .cabinet-page .body p, .cabinet-page .body li { font-size: 12px; line-height: 1.4; }
  /* A lit zone on the floor: its marquee over its result, the marquee's own lettering naming the
     game (its heading then for a screen reader, below); beside it the name and result had no room. */
  .zones .aisle { flex-direction: column; align-items: flex-start; gap: 4px; padding-top: 6px; }
  .zones .cabinet { width: min(140px, 100%); }
  .zones .sign { padding: 6px 12px 4px; }
  .about { padding-bottom: 6px; }   /* with the sign's 6 px, 640×360's Server Hall fits (it ran 3 px over) */
  .zone-sign h1 { font-size: 22px; }
  .bay .cabinet { width: 110px; }
  .bay .back { flex: 1; width: auto; min-width: 0; padding: 8px 12px; }
  .back h2 { font-size: 18px; }
}
/* There, the marquee's lettering names the game, so its heading is for a screen reader only; a game
   with no marquee art keeps it, each game by its own cabinet (#200's and #201's gate-reviews).
   Apart from the block above, since a browser without `:has` drops a whole rule that names it, and
   keeps the heading. */
@media (max-width: 720px) and (max-height: 520px), (max-height: 380px) {
  .zones .cabinet:has(.marquee img) + .about h3 { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
}
/* A half-screen window (721 to 900 px wide, over 520 tall, and not an upright tablet, the exact
   complement of its query: squarer than 4/5, or under 980 px tall; 800×600, 768×753, 800×780, half a
   laptop's screen): a tablet's stacked cover left the cabinet's panel 0 px tall, and its bay, a cabinet 40%
   of the width, ran under the belt (P1-307, `2026-10-28-site-laptops#10`). The cover goes back
   beside the panel (game/index.html), the bay takes the row's height, its cabinet as tall as the
   bay, and a dark zone's covered cabinets stand beside its names, as from 901 px. */
@media (min-width: 721px) and (max-width: 900px) and (min-height: 521px) and ((aspect-ratio > 4/5) or (height < 980px)) {
  .crumbs .sep ~ .sep, .crumbs .sep ~ .sep + .here { display: none; }   /* a game's, as from 901 px */
  .zone.dark .aisle { flex-direction: row; justify-content: flex-start; align-items: center; gap: 14px; }
  .zone.dark .soon { align-items: flex-start; text-align: left; }
  .cabinet-page .cover .marquee { width: 100%; }
  .bay { flex: 1; min-height: 0; height: auto; align-items: stretch; }
  .bay .cabinet { height: 100%; width: auto; max-height: none; align-self: stretch; }
  .bay .back { flex: 1; width: auto; min-width: 0; }
}
/* A narrow window not much taller than wide (683×657, half a 1366×768 laptop's screen), and so a
   small upright phone too (375×667): the upright phone's stacked floor ran its last zone 14 px under
   the belt at 683×657, so its rows sit closer (P1-307). */
@media (max-width: 720px) and (min-height: 521px) and (max-height: 700px) {
  .zones { gap: 6px; }
  .sign { padding: 6px 12px 4px; }
}
/* A half-screen window up to 800 px tall (768×761, 800×780): its dark zones leave their covered
   cabinets to the zone's page, as two columns do on a laptop's screen, since beside the names they
   ran the zone 2 to 15 px over (P1-307). */
@media (min-width: 721px) and (max-width: 900px) and (min-height: 521px) and (max-height: 800px) {
  .zone.dark .ghosts { display: none; }
}
