/* The shell's screens (#69 step 5; the TS app's apps/host/src/shell.css): home with the room and
   the games, a game's setup, how to play, settings, controls, the controller test. The colours
   are tokens.css's (A · Console, #403); the menus are always dark (the Paper theme went in #403). */

.screen {
  min-height: 100%;
  display: flex;
  flex-direction: column;
  gap: 1.1em;
  padding: 1.1em 2em 2em;
}

.screen .row .wrap {
  flex: 1;
  min-width: 0;
}

/* Button hints along the bottom during play, when switched off in the settings. */
.no-hints .pg-hints {
  display: none !important;
}

.topbar {
  margin-top: 0.9em;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 0.6em;
}

.logo {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3em;
  margin: 0;
  font-size: 1.4em;
  font-weight: 900;
  letter-spacing: 0.02em;
}

.logo svg {
  width: 1em;
  height: 1em;
}

.logo span {
  margin-inline-start: 0.3em;
}

.topnav {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6em;
}

/* The ☰ Menu (#405, plan 2.4): one quiet round button at the end of the top bar. Its icon and its
   word in the middle, whatever is beside them (a pad's Start badge). */
.menu-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.45em;
  min-height: 2.6em;
  padding-inline: 1em;
  white-space: nowrap;
}

.menu-btn .menu-icon {
  font-size: 1.15em;
  line-height: 1;
}

.menu-btn .pb {
  margin: 0;
}

/* A quiet button (A · Console): the see-through white pill, as .btn. */
.ghost {
  font-weight: 800;
  background: var(--ui-quiet);
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  padding: 0.4em 0.9em;
  cursor: pointer;
}

.high-contrast .ghost {
  border-color: var(--ui-text);
}

.screen .panel {
  width: auto;
  padding: 1.1em 1.3em;
  border-radius: 1.1em;
}

.screen .panel > h2,
.screen .panel h2 {
  margin: 0 0 0.6em;
  font-size: 1.15em;
  font-weight: 900;
}

.dim {
  color: var(--ui-dim);
}

.hint {
  margin: 0.8em 0 0;
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.85em);
}

.setup-grid {
  flex: 1;
  display: grid;
  grid-template-columns: minmax(20em, 1fr) 2fr;
  gap: 1.2em;
  align-items: start;
}

.setup-grid {
  grid-template-columns: 2fr minmax(17em, 1fr);
}

.setup-grid > * {
  min-width: 0;
}

/* Under 50 lines of text wide (data-fit narrow or medium): one column. */
:root:not([data-fit='wide']) .setup-grid {
  grid-template-columns: 1fr;
}

/* ------------------------------------------------------------ players */

.players {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.6em;
}

.pcard {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.7em;
  padding: 0.55em 0.8em;
  border-radius: 0.9em;
  background: var(--c-16, transparent);
  border: 2px solid var(--c-60, var(--ui-line));
}

.pcard.empty {
  border-style: dashed;
  border-color: var(--ui-line);
  background: transparent;
}

.pcard-icon svg {
  width: 2.1em;
  height: 2.1em;
  display: block;
}

.pcard-body {
  flex: 1;
  min-width: 8em;
}

.pcard-name {
  font-weight: 900;
}

/* The ⋯ (#86) sits at the end of the name row, so it adds no row to the card. */
.pcard-name:has(.pcard-more) {
  display: flex;
  align-items: center;
  gap: 0.3em;
}

.pcard-name-text {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.pcard-device {
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.8em);
}

.pcard-device .away {
  color: var(--notice);
  font-weight: 900;
}

/* The room's host (#86, once the VIP phone of #72 item 25): a yellow ★ with a dark outline and the
   word on a quiet pill. No yellow fill: yellow is the one main action's (#405, plan 2.2). */
.host-badge {
  display: inline-block;
  margin-inline-start: 0.35em;
  padding: 0.05em 0.5em;
  border-radius: 999px;
  font-size: max(var(--fs-small), 0.72em);
  font-weight: 900;
  vertical-align: 0.12em;
  color: var(--ui-text);
  background: var(--ui-key);
  white-space: nowrap;
}

.host-badge > [aria-hidden] {
  color: var(--do);
  text-shadow:
    0 0 1px var(--bg),
    0 0 2px var(--bg);
}

.high-contrast .host-badge {
  border: 2px solid var(--ui-text);
}

/* The line under a setup's title: who can start it from their phone or screen. */
.host-hint {
  margin: 0.3em 0 0;
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 700;
}

.host-hint .host-badge {
  margin-block: 0;
  margin-inline: 0 0.3em;
}

/* Who knocks to come back in: one short framed line each, at the top, with Allow (its one yellow) and No. */
.knocks {
  list-style: none;
  margin: 0 0 0.5em;
  padding: 0;
  display: grid;
  gap: 0.3em;
}

.knock {
  display: flex;
  align-items: center;
  gap: 0.4em;
  padding-block: 0.15em;
  padding-inline: 0.6em 0.3em;
  border: 2px solid var(--ui-line);
  border-radius: 0.8em;
  background: var(--ui-card);
  font-size: max(var(--fs-small), 0.85em);
}

.knock p {
  flex: 1;
  min-width: 0;
  margin: 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.knock-actions {
  display: flex;
  flex: none;
  gap: 0.3em;
}

.knock .mini {
  margin: 0;
}

.mini.primary {
  background: var(--do);
  color: var(--on-do);
}

/* The ⋯ on a player's card: their menu (Make host, Take out). Finger-sized on touch. */
.pcard-more {
  min-width: 2.4em;
  font-size: max(var(--fs-small), 0.95em);
  line-height: calc(1 * var(--line-k));
}

/* No taller than the name beside it (as a plain .mini it made its card's name row 6 px taller). */
.pcard-name .pcard-more {
  margin: 0;
  padding-block: 0.1em;
}

:root[data-touch] :is(.pcard-more, .person-more, .look-more) {
  min-height: var(--touch-min);
  min-width: var(--touch-min);
}

/* A player's menu (ScreenId.Player). */
.player-menu-who {
  display: flex;
  align-items: center;
  gap: 0.8em;
  margin-bottom: 0.8em;
}

.player-menu-who h2 {
  margin: 0;
  overflow-wrap: anywhere;
}

.player-menu-who .hint {
  margin: 0.2em 0 0;
}

.player-menu-who svg {
  width: 3em;
  height: 3em;
  display: block;
}

.player-menu .actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
}

.btn.danger {
  border-color: var(--warn);
  color: var(--warn);
  background: var(--ui-quiet);
}

/* The pause menu's Players page (#86). */
.pause-players {
  list-style: none;
  margin: 0 0 0.6em;
  padding: 0;
  display: grid;
  gap: 0.4em;
}

/* Each player a dark row (A · Console, #415), as the match cards; high contrast: the white edge. */
.pause-player {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.3em 0.8em;
  padding-block: 0.4em;
  padding-inline: 0.9em 0.5em;
  border: 2px solid transparent;
  border-radius: var(--r-m);
  background: var(--ui-card);
  text-align: start;
}

.high-contrast .pause-player {
  border-color: var(--ui-text);
}

.pp-who {
  overflow-wrap: anywhere;
}

.pcard-edit {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
}

/* The gap spaces the steppers here: with their own margin too, Shape and Colour missed sharing a
   row by 3 px on a 1080p TV, and each card took a row more. */
.pcard-edit .look-step {
  margin-inline-end: 0;
}

/* Rename and ✕ stay together on one row. */
.pcard-own {
  display: inline-flex;
  gap: 0.3em;
}

.mini {
  font-size: max(var(--fs-small), 0.78em);
  font-weight: 800;
  background: var(--ui-quiet);
  border: 0;
  border-radius: var(--r-pill);
  padding: 0.35em 0.7em;
  margin-block: 0.15em 0;
  margin-inline: 0 0.2em;
  cursor: pointer;
}

/* High contrast: the white edge every quiet button gets (plan 3.1). */
.high-contrast .mini:not(.primary) {
  border: 2px solid var(--ui-text);
}

/* ------------------------------------------------------------ the room code: read aloud, hidden (#72 item 24) */

.code-row {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.4em;
}

/* The speaker next to the code: round, at least the touch size. */
.speak-code {
  display: inline-grid;
  place-items: center;
  flex: none;
  width: max(var(--touch-min), 2.4em);
  height: max(var(--touch-min), 2.4em);
  padding: 0;
  font-size: max(var(--fs-small), 1em);
  line-height: calc(1 * var(--line-k));
  color: inherit;
  background: var(--ui-quiet);
  border: 2px solid var(--ui-line);
  border-radius: 50%;
  cursor: pointer;
}

.speak-code span {
  font-size: 1.3em;
}

/* In place of the QR code and the code while they are hidden (streaming). */
.code-cover {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3em;
  padding: 0.8em;
  text-align: center;
  border: 2px dashed var(--ui-seat);
  border-radius: 0.5em;
}

.code-cover p {
  margin: 0;
}

.code-cover-title {
  font-size: 1.3em;
  font-weight: 900;
}

.code-cover-why {
  font-size: max(var(--fs-small), 0.85em);
  opacity: 0.8;
}

/* ------------------------------------------------------------ a game's picture (the setup, the tournament's intro; Home: home.css) */

.gcard-art {
  display: block;
  color: var(--ui-text);
}

.gcard-art svg,
.gcard-art img {
  display: block;
  width: 100%;
  height: auto;
  max-height: 8em;
}

/* The rendered pictures keep their shape when max-height trims them, and fade out at the
   edges so a board cut by the frame melts into the card. */
.gcard-art img {
  object-fit: contain;
  user-select: none;
  -webkit-mask-image: radial-gradient(ellipse 72% 72% at 50% 50%, #000 62%, transparent 100%);
}

/* ------------------------------------------------------------ setup, settings, help */

/* A language by its own name (Settings' Language, #84 A6): on a page in another language it is drawn
   in the system's font, so an English page never fetches the Arabic font for one word (plan 2.9). */
:root:not(:lang(ar)) .lang-own {
  font-family: system-ui, sans-serif;
}

.setup-main,
.settings-panel,
.help-panel {
  display: flex;
  flex-direction: column;
  gap: 0.9em;
  align-items: flex-start;
}

.settings-panel,
.help-panel {
  max-width: 54em;
  width: 100%;
  margin: 0 auto;
}

.screen .settings-panel > h2,
.screen .help-panel > h2,
.screen .setup-title h2 {
  margin: 0;
  font-size: 2.2em;
  font-weight: 900;
}

.setup-actions {
  display: flex;
  gap: 0.6em;
}

.setup-title {
  display: flex;
  gap: 1.2em;
  align-items: center;
}

.setup-title .gcard-art {
  /* Never squeezed by a long name or a short row: the title's row makes room instead (#789). */
  flex: none;
  width: 9em;
}

.tagline {
  margin: 0.2em 0 0;
  color: var(--ui-dim);
}

.screen .rows {
  display: flex;
  flex-direction: column;
  gap: 0.15em;
  width: 100%;
}

.screen .row > .label {
  min-width: 8.5em;
}

.screen .rows h3,
.seat-list h3 {
  margin: 0.4em 0 0.2em;
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 900;
  color: var(--ui-dim);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

.seat-list {
  width: 100%;
}

.wrap {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
}

.swatches {
  display: inline-flex;
  gap: 0.2em;
  margin-inline-start: 0.4em;
}

/* Settings in groups (#72 build 20): Display, Sound, Controls, Accessibility on one page. */
.screen .set-group > .hint {
  margin: 0.2em 0 0;
}

/* Settings, Controls and Rename in A · Console (#415, plan 2.6): full screens on the page's solid
   dark, under the top bar. The panel is no box of its own: each group (Settings' four, its share
   row, Controls' three lists) is a dark card with no edge, its heading a quiet capital line, its
   choices quiet pills that turn white when picked. High contrast: the cards and pills get the
   white edge (plan 3.1). */
.screen:is(.settings, .rename) .settings-panel {
  padding: 0;
  border: 0;
  border-radius: 0;
  background: none;
}

.screen:is(.settings, .rename) .settings-panel > h2 {
  font-size: var(--fs-title);
  line-height: calc(1.1 * var(--line-k));
}

.screen.settings .settings-panel > .rows {
  padding: 0.6em 1.1em 0.8em;
  border-radius: var(--r-l);
  border: 2px solid transparent;
  background: var(--ui-card);
}

.screen.settings .rows h3 {
  margin: 0.1em 0 0.3em;
  font-size: var(--fs-label);
  font-weight: 800;
  letter-spacing: 0.05em;
}

.screen.settings .rows .chip:not([aria-pressed='true']) {
  border-color: transparent;
  background: var(--ui-key);
}

/* A folded group (a touch screen) is its heading alone: even all round. */
.screen.settings .settings-panel > .rows:has(> h3:last-child) {
  padding-bottom: 0.6em;
}

.screen.settings .rows > h3:last-child {
  margin-bottom: 0;
}

.high-contrast .screen.settings .settings-panel > .rows,
.high-contrast .screen.settings .rows .chip:not([aria-pressed='true']) {
  border-color: var(--ui-text);
}

.start-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.8em;
}

/* An option focused by a pad scrolls into view above the Start row, not under it. */
#app:has(.setup-main > .start-row) {
  scroll-padding-bottom: 7em;
}

/* The tutorial and challenges, under the Start row. */
.setup-solo {
  display: flex;
  flex-wrap: wrap;
  gap: 0.8em;
}

/* The setup's Start row stays at the bottom of the screen while the options scroll under it:
   Start has the focus from the first frame, and a TV cannot scroll to it (#70). */
.setup-main > .start-row {
  position: sticky;
  bottom: 0;
  z-index: 2;
  align-self: stretch;
  padding: 0.6em 0;
  /* The panel's look (--ui over the page's --bg), solid, so the options do not show through. */
  background: linear-gradient(var(--ui), var(--ui)), var(--bg);
  box-shadow: 0 -0.6em 0.8em -0.3em var(--bg);
}

/* Why Start is off, beside it in the Start row (it stays in sight; #322). It comes before Start in
   the page (for a short screen, below), and after it here. */
.start-row .start-why {
  order: 1;
  flex: 1 1 12em;
  margin: 0;
  font-weight: 700;
  line-height: calc(1.3 * var(--line-k));
}

.start {
  padding: 0.6em 1.6em;
  border-radius: var(--r-pill);
  border: 0;
  background: var(--do);
  color: var(--on-do);
  box-shadow: var(--do-glow);
  font-size: 1.15em;
  font-weight: 900;
  cursor: pointer;
}

.mode-btn {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1em;
  padding: 0.5em 1em;
  border-radius: 0.7em;
  border: 2px solid var(--ui-line);
  background: transparent;
  text-align: start;
  cursor: pointer;
}

.mode-btn span {
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.8em);
}

.start:disabled,
.mode-btn:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

.help-body {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20em), 1fr));
  gap: 0.8em 1.6em;
}

.help-body section h3 {
  margin: 0 0 0.3em;
  font-size: 1.1em;
  font-weight: 900;
}

.help-body p,
.help-body ul {
  margin: 0 0 0.4em;
  color: var(--ui-dim);
  line-height: calc(1.45 * var(--line-k));
}

.help-body b {
  color: var(--ui-text);
}

.help-body ul {
  padding-inline-start: 1.1em;
}

.ctl-row .chip {
  min-width: 12em;
}

.name-input {
  width: 14em;
  padding: 0.4em 0.6em;
  border-radius: var(--r-m);
  border: 2px solid transparent;
  background: var(--ui-raised);
  font-size: 1.2em;
  font-weight: 800;
}

.high-contrast .name-input {
  border-color: var(--ui-text);
}

/* ------------------------------------------------------------ rename: the on-screen keyboard (#72 item 17) */

.screen.rename {
  padding-bottom: 1em;
}

.screen.rename .settings-panel {
  width: min(46em, 100%);
  max-width: 46em;
  gap: 0.6em;
}

.screen.rename .settings-panel > h2 {
  margin: 0;
  font-size: var(--fs-heading);
}

.screen.rename .hint {
  margin: 0;
}

/* Only a touch screen can tap the box for its own keyboard (js/ui.ts). */
.kb-touch-hint {
  display: none;
}

:root[data-touch] .kb-touch-hint {
  display: inline;
}

/* The name, its count and Save stay at the top while the keys scroll under them (a phone, a
   TV at big text), so the name is always in sight while typing. */
.rename-row {
  position: sticky;
  top: 0;
  z-index: 2;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5em;
  align-self: stretch;
  padding: 0.4em 0;
  /* The page's solid dark (the panel has no fill of its own, #415), so the keys do not show through. */
  background: var(--bg);
  box-shadow: 0 0.6em 0.8em -0.3em var(--bg);
}

/* Little room (big text on a phone): the box says "Name" to a screen reader, not on screen, so
   the row stays short. */
:root[data-fit='narrow'] .rename-row > .label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* A key focused by a pad scrolls into view under the name row, not behind it. */
#app:has(.rename-row) {
  scroll-padding-top: 5em;
}

.rename-row .name-input {
  flex: 1 1 6em;
  width: auto;
  min-width: 0;
  max-width: 16em;
}

.rename-row .btn {
  flex: none;
  padding-inline: 0.9em;
}

.rename-count {
  flex: none;
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.8em);
  font-variant-numeric: tabular-nums;
}

.kb {
  display: flex;
  flex-direction: column;
  gap: 0.4em;
  width: 100%;
}

/* The keys wrap to the width (A–Z order reads on at any text size): at most 10 a row, each at
   least 44 px (a phone fits 6 or 7 a row; a phone's own keyboard has 10 smaller ones). */
.kb-keys {
  display: grid;
  grid-template-columns: repeat(
    auto-fill,
    minmax(max(44px, 1.7em, calc((100% - 3.6em) / 10)), 1fr)
  );
  gap: 0.4em;
}

.kb-bottom {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em 0.8em;
  width: 100%;
}

.kb-row {
  display: flex;
  flex: 1 1 14em;
  flex-wrap: wrap;
  gap: 0.4em;
}

.kb-actions {
  flex: 0 1 auto;
  justify-content: flex-end;
}

/* The keys' height follows the screen's height too, so a TV at 150 % text shows them all. Key caps
   (A · Console, #415): the --ui-key fill with no edge but a dark lip under it; a key that is on
   (Shift, the other page) turns white, as a picked choice does. High contrast: the white edge. */
.kb-key {
  min-width: 44px;
  min-height: max(44px, min(2.3em, 7.5vh));
  padding: 0 0.3em;
  border: 2px solid transparent;
  border-bottom: 0.18em solid var(--edge);
  border-radius: var(--r-s);
  background: var(--ui-key);
  font-size: 1.1em;
  font-weight: 900;
  white-space: nowrap;
  cursor: pointer;
  touch-action: manipulation;
}

.kb-key:active {
  transform: scale(0.94);
}

.kb-key.wide {
  flex: 1 1 3em;
  /* Never narrower than its word (Clear runs longer in other languages); the row wraps instead. */
  min-width: max-content;
  font-size: 1em;
}

.kb-key.del {
  flex-basis: 4em;
}

.kb-key.space {
  flex: 3 1 4em;
  font-size: 1em;
}

.kb-key[aria-pressed='true'] {
  border-color: transparent;
  background: var(--ui-text);
  color: var(--ink);
}

.high-contrast .kb-key:not([aria-pressed='true']) {
  border-color: var(--ui-text);
}

.reduced-motion .kb-key:active {
  transform: none;
}

/* A phone: a little less padding around the panel, so 7 keys fit a row. */
@media (max-width: 599.98px) and (orientation: portrait) {
  .screen.rename {
    padding-inline: 0.5em;
  }

  .screen.rename .settings-panel {
    padding-inline: 0.7em;
  }
}

/* No gamepad and no phone: no Ⓧ / Ⓨ / Start / Ⓑ badges on this screen (Shell/App.InputHints.cs). */
.no-pad .rename .pb {
  display: none;
}

/* Phones only have OK and Back: Ⓧ / Ⓨ / Start only with a gamepad (Ⓑ is the phone's Back). */
.no-gamepad .rename :is(.pb.x, .pb.y, .pb-start) {
  display: none;
}

/* ------------------------------------------------------------ the controller test */

/* A side panel at the right, over Home, Settings or the paused match (#72 item 28): the page
   stays in sight, dimmed; a click beside the panel closes it. Wide enough on a TV for two or
   three pad cards in a row, so eight pads fit with little scrolling (a pad cannot scroll it:
   pressing a button brings that pad's card into view instead). Full width on a narrow screen or
   at big text sizes. Above the HUD and the pause dialog, under the hold ring (z-index 60). */
.side-panel {
  position: fixed;
  inset: 0;
  z-index: 45;
}

.side-scrim {
  position: absolute;
  inset: 0;
  background: var(--scrim);
}

.side-box {
  position: absolute;
  top: 0;
  inset-inline-end: 0;
  bottom: 0;
  width: min(56em, 100%);
  display: flex;
  flex-direction: column;
  gap: 0.9em;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding-block: max(1em, var(--safe-top)) max(1.2em, var(--safe-bottom));
  padding-inline: 1.2em max(1.2em, var(--safe-end));
  border-inline-start: 1px solid var(--ui-line);
  background: var(--bg);
  color: var(--ui-text);
  box-shadow: -0.6em 0 2em var(--shadow);
  animation: side-in 0.18s ease-out;
}

@keyframes side-in {
  from {
    transform: translateX(2em);
    opacity: 0;
  }
}

/* Right to left the panel is on the left: it slides in from there, its shadow on its right. */
:root[dir='rtl'] .side-box {
  box-shadow: 0.6em 0 2em var(--shadow);
  animation-name: side-in-rtl;
}

@keyframes side-in-rtl {
  from {
    transform: translateX(-2em);
    opacity: 0;
  }
}

.reduced-motion .side-box {
  animation: none;
}

:root[data-fit='narrow'] .side-box {
  width: 100%;
  border-inline-start: 0;
  padding-inline-start: max(1em, var(--safe-start));
}

.side-head {
  align-self: stretch;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.6em;
}

.side-head h2 {
  margin: 0;
  font-size: 1.3em;
  font-weight: 900;
}

.side-box .hint {
  margin: 0;
}

/* ---- the ☰ menu and the room sheet (#405): the same panel, from the end side */

.side-box.menu-sheet {
  width: min(24em, 100%);
}

.side-box.room-sheet {
  width: min(40em, 100%);
}

/* From the top, the full width of the sheet (a menu's own content is centred). */
.side-box:is(.menu-sheet, .room-sheet) {
  justify-content: flex-start;
}

.side-box:is(.menu-sheet, .room-sheet) > * {
  align-self: stretch;
}

/* Room around each item for its focus ring (the sheet scrolls, so it would cut it). */
.menu-list {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  padding: calc(var(--ring-gap) + max(3px, 0.18em));
}

/* Full screen's wrapper only closes the menu with the click (the browser's own full screen). */
.menu-fs {
  display: contents;
}

.menu-list .menu-item {
  display: flex;
  align-items: center;
  justify-content: flex-start;
  gap: 0.5em;
  width: 100%;
  min-height: var(--touch-menu);
  text-align: start;
}

/* Full screen's own rule centres its icon and word; in the menu it lines up with the rest. */
:root[data-fs] .menu-list .fs-btn {
  justify-content: flex-start;
}

/* The room sheet: the QR code and the code beside the ways to join, then everyone by name. */
.sheet-join {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5em;
  text-align: center;
}

.screen .sheet-join h3,
.sheet-join h3 {
  align-self: stretch;
  margin: 0;
  font-size: 1em;
  text-align: start;
}

.sheet-code {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.3em;
}

.sheet-join .join-here {
  align-self: stretch;
  text-align: start;
}

.sheet-people {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4em;
}

.sheet-person {
  display: flex;
  align-items: center;
  gap: 0.7em;
  padding: 0.45em 0.7em;
  border-radius: var(--r-m);
  background: var(--ui-card);
  border-inline-start: 0.3em solid var(--c);
}

.sheet-person.away {
  border-inline-start-style: dashed;
}

.person-look svg {
  display: block;
  width: 1.8em;
  height: 1.8em;
}

.person-who {
  flex: 1;
  min-width: 0;
  overflow-wrap: anywhere;
}

.person-who b {
  font-weight: 900;
}

.person-who small {
  display: block;
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 800;
  color: var(--ui-faint);
}

.person-acts {
  display: flex;
  flex: none;
  gap: 0.3em;
}

.person-acts .mini {
  margin: 0;
}

.pad-who {
  color: var(--ui-dim);
}

.pads {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 17em), 1fr));
  gap: 1em;
  width: 100%;
}

.pad {
  padding: 0.9em 1em;
  border-radius: 0.9em;
  border: 1px solid var(--ui-line);
  background: var(--ui-card);
}

.pad h3 {
  margin: 0;
  font-size: 1.1em;
}

.pad p {
  margin: 0.2em 0;
}

/* A short card: the title and buttons seen on one line, the name and mapping on the next, the
   stick beside the buttons. */
.pad-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0 0.6em;
}

.pad .pad-id {
  overflow-wrap: anywhere;
}

.pad-body {
  display: flex;
  align-items: center;
  gap: 0.8em;
  margin-top: 0.4em;
}

.pad-body .btns {
  flex: 1;
  min-width: 0;
}

.stick {
  position: relative;
  flex: none;
  width: 4em;
  height: 4em;
  border-radius: 50%;
  border: 2px solid var(--ui-line);
}

.stick span {
  position: absolute;
  left: calc(50% - 0.5em);
  top: calc(50% - 0.5em);
  width: 1em;
  height: 1em;
  border-radius: 50%;
  background: var(--ui-text);
}

.btns {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
}

.pbtn {
  min-width: 2.4em;
  padding: 0.2em 0.4em;
  border-radius: 0.5em;
  border: 2px solid var(--ui-line);
  text-align: center;
  font-size: max(var(--fs-small), 0.85em);
}

.pbtn.seen {
  border-color: var(--ok);
}

.pbtn.down {
  background: var(--ok);
  color: var(--on-ok);
}

.report {
  width: 100%;
  margin: 0;
  padding: 0.8em;
  border-radius: 0.7em;
  background: var(--ui-raised);
  font-size: max(var(--fs-small), 0.8em);
  white-space: pre-wrap;
}

.summary {
  margin: 0;
}

/* Gonfetti Pads (#81): the switch and download under the pad cards. */
.pads-helper {
  padding: 0.8em;
  border-radius: 0.7em;
  background: var(--ui-raised);
}

.pads-helper h3,
.pads-helper p,
.pads-helper ol {
  margin: 0 0 0.5em;
}

.pads-helper ol {
  padding-inline-start: 1.4em;
}

.pads-helper li + li {
  margin-block-start: 0.3em;
}

/* Download is a link (the browser saves the file) that looks like the buttons beside it. */
.pads-helper a.btn {
  text-decoration: none;
}

.pads-helper a.btn:not(.primary) {
  color: inherit;
}

/* Narrow screens (#70, #72: data-fit='narrow', under 36 lines of text wide): the setup's title
   wraps under its picture instead of sticking out at the side, and the page edges get thinner. */
:root[data-fit='narrow'] .screen {
  padding: 0.8em 0.5em 1.2em;
}

:root[data-fit='narrow'] .setup-title {
  flex-wrap: wrap;
  gap: 0.6em;
}

:root[data-fit='narrow'] .setup-title > * {
  min-width: 0;
  max-width: 100%;
}

:root[data-fit='narrow'] .setup-title .gcard-art {
  width: 6em;
}

:root[data-fit='narrow'] .screen .setup-title h2 {
  font-size: 1.5em;
  overflow-wrap: anywhere;
}

/* ------------------------------------------------------------ setup presets (#72 item 18) */

/* Quick / Classic / Long / Custom: four cards in a row, each with what it means in one line;
   two by two when the row is too narrow for four readable cards (a tablet, big text). */
.presets-box {
  flex: 1;
  min-width: 0;
  container: presets / inline-size;
}

.presets {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.5em;
}

.chip.preset {
  flex-direction: column;
  align-items: flex-start;
  justify-content: flex-start;
  gap: 0.1em;
  min-width: 0;
  padding: 0.4em 0.75em;
  text-align: start;
}

.chip.preset b {
  font-weight: 900;
}

.chip.preset b,
.chip.preset span {
  max-width: 100%;
  overflow-wrap: break-word;
}

@container presets (max-width: 30em) {
  .presets {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

/* Siegeometry's five (Quick, Short, Classic, Long, Custom; #91): in a row where the others have
   four in a row, else three and two (Custom twice as wide), so no game takes more rows. */
.presets[data-n='5'] {
  grid-template-columns: repeat(5, minmax(0, 1fr));
}

.presets[data-n='5'] .chip.preset {
  padding-inline: 0.55em;
}

@container presets (max-width: 30em) {
  .presets[data-n='5'] {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .presets[data-n='5'] > :last-child {
    grid-column: span 2;
  }
}

.chip.preset span {
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 700;
  opacity: 0.8;
}

.preset-sum {
  margin: 0 0 0.3em;
}

/* Who plays and the match (#649): "3 friends + 1 bot", then the match in a line, over Start. */
.lineup {
  display: flex;
  flex-direction: column;
  gap: 0.2em;
}

.lineup-who {
  margin: 0;
  font-size: var(--fs-heading);
  font-weight: 900;
  line-height: calc(1.2 * var(--line-k));
}

.lineup .preset-sum {
  margin: 0;
}

/* Game options (#649): beside Back and How to play, quieter than both (no fill, the text underlined). */
.setup-actions {
  flex-wrap: wrap;
}

.screen .setup-more {
  display: inline-flex;
  align-items: center;
  gap: 0.2em;
  min-height: var(--touch-menu);
  margin-inline-start: auto;
  padding-inline: 0.15em;
  background: none;
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.85em);
}

.screen .setup-more:hover {
  color: inherit;
}

.setup-more-text {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.screen .row > .label small {
  font-size: max(var(--fs-small), 0.85em);
}

/* Who sits where: at the top of the room panel when it is beside the options (it stays in sight
   however many players join), else right under the options (one column: a phone, a tablet, big
   text). The page has both; one shows. */
.setup-room .seat-list {
  margin: 0 0 0.8em;
}

:root[data-fit='wide'] .setup-main > .seat-list,
:root:not([data-fit='wide']) .setup-room > .seat-list {
  display: none;
}

/* A TV: the seats two by two, so eight fit beside the options. */
:root[data-bp='tv'] .setup-room .seats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

:root[data-bp='tv'] .setup-room .seat {
  min-width: 0;
}

/* A TV (it cannot scroll to what a pad cannot focus), or a screen under 50 lines of text tall
   (720p, a 768p tablet; data-fit-y): Back, How to play and the title share a line, with a smaller
   picture, so the presets, seats and bots fit above the Start row even with the first-timer's
   tutorial hint. The title's column is never narrower than its picture and the game's name (#789:
   at 1024×768 a long name squeezed the picture to a dot); Back, How to play and Game options wrap
   onto a second line instead. */
:root[data-bp='tv'] .setup .setup-main,
:root:not([data-fit-y='tall'], [data-fit='narrow']) .setup .setup-main {
  display: grid;
  grid-template-columns: auto minmax(min-content, 1fr);
  align-items: center;
  column-gap: 1.2em;
}

/* Where that line is too short for the picture and a long name too (a 1024×768 tablet; #789): no
   picture, rather than one squeezed to a dot or a second line that pushes the bots under Start. */
.setup .setup-main {
  container: setup / inline-size;
}

@container setup (max-width: 35em) {
  .setup .setup-title .gcard-art {
    display: none;
  }
}

:root[data-bp='tv'] .setup .setup-main > :not(.setup-actions, .setup-title),
:root:not([data-fit-y='tall'], [data-fit='narrow'])
  .setup
  .setup-main
  > :not(.setup-actions, .setup-title) {
  grid-column: 1 / -1;
}

:root[data-bp='tv'] .setup .setup-title .gcard-art,
:root:not([data-fit-y='tall'], [data-fit='narrow']) .setup .setup-title .gcard-art {
  width: 4.5em;
}

:root[data-bp='tv'] .screen .setup-title h2,
:root:not([data-fit-y='tall'], [data-fit='narrow']) .screen .setup-title h2 {
  font-size: 1.6em;
}

/* Under 50 lines tall, but not a TV (which has the width for it): no tagline under the title. */
:root:not([data-fit-y='tall'], [data-bp='tv']) .setup .tagline {
  display: none;
}

/* Under 38 lines of text tall (a phone upright or a TV at 150 % text): no top bar (the setup's
   own Back is enough; Home has it), picture, tagline or first-timer's tutorial hint (the
   Tutorial button under Start still glows for them), so the presets, seats and bots stay above
   the Start row. */
:root[data-fit-y='short'] .setup > .topbar,
:root[data-fit-y='short'] .setup .setup-title .gcard-art,
:root[data-fit-y='short'] .setup .tagline,
:root[data-fit-y='short'] .setup .solo-new {
  display: none;
}

/* A phone upright and short at this text size: Advanced beside Start if it fits. */
:root[data-fit-y='short'][data-bp='xs'] .setup .start-row .advanced {
  flex: 1 1 auto;
  text-align: center;
}

/* Narrow (a phone upright, or big text): the cards two by two, labels above their buttons. */
:root[data-fit='narrow'] .presets {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

/* Siegeometry's five: three and two, as above. */
:root[data-fit='narrow'] .presets[data-n='5'] {
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

:root[data-fit='narrow'] .presets[data-n='5'] > :last-child {
  grid-column: span 2;
}

/* Too narrow even for two by two (a phone at 200 % text): one card a row, so no word breaks
   inside ("Cust om", "round s"). As specific as the rules above, and after them. */
@container presets (max-width: 12.5em) {
  :root .presets-box .presets,
  :root .presets-box .presets[data-n='5'] {
    grid-template-columns: minmax(0, 1fr);
  }

  :root .presets-box .presets[data-n='5'] > :last-child {
    grid-column: auto;
  }
}

/* A phone (xs upright, s-land on its side): the setup's own Back is enough, so the top bar goes
   (Home has it), the title gets small, and the options start near the top. */
@media (max-width: 599.98px) and (orientation: portrait),
  (max-height: 499.98px) and (orientation: landscape) {
  :root:not(.tv) .setup > .topbar {
    display: none;
  }

  :root:not(.tv) .setup .setup-main {
    gap: 0.6em;
  }

  :root:not(.tv) .setup .setup-title {
    gap: 0.6em;
  }

  :root:not(.tv) .setup .setup-title .gcard-art {
    width: 4.5em;
  }

  :root:not(.tv) .screen .setup-title h2 {
    font-size: 1.5em;
  }

  :root:not(.tv) .setup .start {
    padding: 0.4em 1.2em;
  }

  :root:not(.tv) .setup-main > .start-row {
    padding: 0.4em 0;
  }

  :root:not(.tv) .setup .solo-new {
    margin: 0;
  }
}

/* A phone upright: no picture by the title, and Start with Advanced beside it when Advanced
   still has room for its words (at big text it goes under Start, full width). */
@media (max-width: 599.98px) and (orientation: portrait) {
  :root:not(.tv) .setup .setup-title .gcard-art {
    display: none;
  }

  :root:not(.tv) .setup .start-row .advanced {
    flex: 1 1 9em;
    text-align: start;
  }
}

/* A phone on its side: little height, so Back, How to play and the title share one line, the
   picture and tagline go, and so does the first-timer's tutorial hint (the Tutorial button
   under Start still glows for them). */
@media (max-height: 499.98px) and (orientation: landscape) {
  :root:not(.tv) .setup {
    padding-top: max(0.5em, var(--safe-top));
  }

  :root:not(.tv) .setup .setup-main {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    align-items: center;
    column-gap: 1em;
  }

  :root:not(.tv) .setup .setup-main > :not(.setup-actions, .setup-title) {
    grid-column: 1 / -1;
  }

  :root:not(.tv) .setup .setup-title .gcard-art,
  :root:not(.tv) .setup .tagline,
  :root:not(.tv) .setup .solo-new {
    display: none;
  }

  :root:not(.tv) .screen .setup-title h2 {
    font-size: 1.3em;
  }
}

/* ------------------------------------------------------------ setup in A · Console (#410, plan 2.6) */

/* The setup's head (Back, How to play, Game options, the title; #787): no box of its own, its
   parts sit in the panel as before, except on a TV or a laptop (below). */
.setup-head {
  display: contents;
}

/* A TV or a laptop (the options beside the room, a screen that is not short): the panel is a
   column as tall as the screen under the top bar, and Start sits at its bottom, on the start side,
   whatever is open above it (#787; #748: a button stays in a fixed corner, never mid-page). Long
   options still scroll under the Start row, which sticks. */
:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-main {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-self: stretch;
}

:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-head {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  align-self: stretch;
  gap: 0.9em;
}

/* Where Back, How to play and the title share a line (a TV, or under 50 lines tall). */
:root[data-fit='wide']:not([data-fit-y='short']):is([data-bp='tv'], :not([data-fit-y='tall']))
  .screen.setup
  .setup-head {
  flex-direction: row;
  align-items: center;
  gap: 1.2em;
}

/* As the grid it replaces (auto, then minmax(0, 1fr)): the buttons at their width, the title the rest. */
:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-head > .setup-actions {
  flex: none;
}

:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-head > .setup-title {
  flex: 1 1 0;
  /* Never narrower than its picture and the game's name (#789): the row wraps instead. */
  min-width: min-content;
}

:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-head {
  flex-wrap: wrap;
}

:root[data-fit='wide']:not([data-fit-y='short']) .screen.setup .setup-main > .start-row {
  margin-top: auto;
}

/* The top bar inside a TV's safe area, as Home's (#405, plan 3.4; the same screens as home.css's
   rule): its logo and ☰ Menu sit where Home's do, at every text size. The panels keep the page's
   2em edges: the safe area's sides would take 80 px from the options on a 1080p TV, and
   Siegeometry's first-timer hint, presets and bots would no longer fit above Start. */
@media (min-width: 1200px) and (min-height: 500px) {
  :root .screen.setup {
    padding-top: max(1.1em, var(--safe-top));
  }

  /* No gap above it, as Home's bar (home.css .room-bar): the padding is the gap. */
  .screen.setup > .topbar {
    margin-top: 0;
    margin-inline: max(0px, calc(var(--safe-start) - 2em)) max(0px, calc(var(--safe-end) - 2em));
  }
}

/* The game's picture behind it all, faded dark: the panels stay solid over it (--ui). Mirrored right
   to left (it has no text); none in high contrast. */
.setup-bg {
  position: fixed;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--bg);
  pointer-events: none;
}

.setup-pic {
  position: absolute;
  inset: 0;
  background-position: center;
  background-size: cover;
  opacity: 0.2;
}

/* The picture itself (ShellScreens.Setup names it in --pic): never on a phone, which fetches no hero
   file (plan 2.8), nor in high contrast. */
:root:not([data-bp='xs'], [data-bp='s-land'], .high-contrast) .setup-pic {
  background-image: var(--pic);
}

:root[dir='rtl'] .setup-pic {
  transform: scaleX(-1);
}

.setup-bg::after {
  content: '';
  position: absolute;
  inset: 0;
  background: var(--fade-top), linear-gradient(var(--fade-dir), var(--bg) 0 30%, transparent 75%);
}

/* Quiet dark panels with no edge (high contrast: the white one). */
.screen.setup .panel {
  border-color: transparent;
}

.high-contrast .screen.setup .panel {
  border-color: var(--ui-text);
}

/* The presets as dark tiles; the chosen one keeps its dark fill and gets a white ring (as the
   picked game's tile on Home), so its line stays easy to read. */
.screen.setup .chip.preset {
  border-color: transparent;
  background: var(--ui-raised);
}

.screen.setup .chip.preset[aria-pressed='true'] {
  border-color: var(--focus);
  background: var(--ui-key);
  color: var(--ui-text);
}

.high-contrast .screen.setup .chip.preset[aria-pressed='true'] {
  box-shadow: inset 0 0 0 2px var(--focus);
}

/* The other choices: quiet pills, white when picked, the same fill as Settings' (--ui-key). */
.screen.setup .rows .chip:not(.preset, [aria-pressed='true']) {
  border-color: transparent;
  background: var(--ui-key);
}

.high-contrast .screen.setup .chip:not([aria-pressed='true']) {
  border-color: var(--ui-text);
}

/* Who sits where: a badge (the shape ringed in its colour, as in the top bar) beside the look's name
   and, under both, who plays it, the card's whole width (so a TV's two columns of seats need no
   third line), on a quiet card. */
.screen.setup .seat-list .seat {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 0.1em 0.4em;
  padding-block: 0.3em;
  padding-inline: 0.4em 0.7em;
  border-inline-start: 0;
  border-radius: var(--r-m);
  background: var(--ui-raised);
  line-height: calc(1.2 * var(--line-k));
}

.seat-badge {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 1.45em;
  height: 1.45em;
  border-radius: 50%;
  background: var(--ui-key);
  box-shadow: 0 0 0 2px var(--c);
}

.seat-badge svg {
  display: block;
  width: 0.95em;
  height: 0.95em;
}

.seat-name {
  font-weight: 900;
  overflow-wrap: anywhere;
}

.screen.setup .seat-list .seat > span {
  grid-column: 1 / -1;
  overflow-wrap: anywhere;
}

/* Your own seat (#860): its colour round it and "You" in full white, so it is found before the bots'. */
.screen.setup .seat-list .seat.you {
  box-shadow: inset 0 0 0 2px var(--c);
}

.screen.setup .seat-list .seat.you > span {
  font-weight: 800;
  color: var(--ui-text);
}

/* A TV's seats, two by two beside the options: a smaller badge and edges, so the look's name fits
   on its line beside the badge (the options keep their width: five presets in a row). */
:root[data-bp='tv'] .screen.setup .setup-room .seat {
  column-gap: 0.3em;
  padding-inline: 0.3em 0.5em;
}

:root[data-bp='tv'] .screen.setup .setup-room .seat-badge {
  width: 1.2em;
  height: 1.2em;
}

:root[data-bp='tv'] .screen.setup .setup-room .seat-badge svg {
  width: 0.8em;
  height: 0.8em;
}

/* Beside the options (wide), the room's players as one-line chips: the badge, the name, the host's ★
   and the ⋯ menu. A player with a seat loses the device line (the seat list above says how they
   play); one without a seat keeps it, and an away player's clock still shows. So eight phones fit
   a TV under the seats without running off (#273). */
:root[data-fit='wide'] .setup-room .players {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
}

:root[data-fit='wide'] .setup-room .pcard {
  flex-wrap: nowrap;
  gap: 0.45em;
  max-width: 100%;
  padding-block: 0.2em;
  padding-inline: 0.3em 0.8em;
  border-color: transparent;
  border-radius: var(--r-pill);
  background: var(--ui-raised);
}

:root[data-fit='wide'] .setup-room .pcard.empty {
  border-color: var(--ui-seat);
  background: transparent;
  padding-inline: 0.8em;
}

:root[data-fit='wide'] .setup-room .pcard-icon {
  display: grid;
  place-items: center;
  width: 1.5em;
  height: 1.5em;
  border-radius: 50%;
  background: var(--ui-key);
  box-shadow: 0 0 0 2px var(--c);
}

:root[data-fit='wide'] .setup-room .pcard-icon svg {
  width: 1em;
  height: 1em;
}

:root[data-fit='wide'] .setup-room .pcard-body {
  min-width: 0;
}

:root[data-fit='wide'] .setup-room .pcard.seated .pcard-device:not(:has(.away)) {
  display: none;
}

.high-contrast .screen.setup :is(.seat-list .seat, .setup-room .pcard) {
  border: 2px solid var(--ui-text);
}

/* Who knocks: in setup (HostScreens.Knocks, whose Allow is .mini.primary elsewhere) and on a screen
   hosting someone's room (HostScreens.GuestHost, .mini.allow), Allow is the quiet pill with a white
   edge, so Start stays the one yellow (#410, decided on #448's review; the knock's line says what it
   asks). The pause menu's Players page keeps its yellow Allow (#415). */
.setup-room .knock .mini.primary,
.mini.allow {
  border: 2px solid var(--ui-text);
  background: var(--ui-quiet);
  color: var(--ui-text);
}

/* ------------------------------------------------------------ look picker (#78: Shell/LookPicker.cs) */

.look-step {
  display: inline-flex;
  margin-inline-end: 0.25em;
}

.look-step .mini {
  margin-inline-end: 0;
}

.look-step .mini.arrow {
  border-radius: 0;
  border-start-start-radius: 0.6em;
  border-end-start-radius: 0.6em;
  padding-inline: 0.5em;
  margin-inline-end: 1px;
}

.look-step .mini:not(.arrow) {
  border-radius: 0;
  border-start-end-radius: 0.6em;
  border-end-end-radius: 0.6em;
}

.look-swatches {
  flex-basis: 100%;
  display: flex;
  flex-wrap: wrap;
  gap: 0.2em 1em;
  padding-inline-start: 2.8em;
}

.swatch-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15em;
}

.swatch {
  display: grid;
  place-items: center;
  width: 1.55em;
  height: 1.55em;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 0.45em;
  background: transparent;
  cursor: pointer;
}

.swatch svg {
  width: 1em;
  height: 1em;
  display: block;
}

.swatch .dot {
  width: 0.95em;
  height: 0.95em;
  border-radius: 50%;
  background: var(--s);
}

.swatch.mine {
  border-color: var(--ui-text);
}

.swatch.taken {
  opacity: 0.25;
  cursor: not-allowed;
  background: repeating-linear-gradient(135deg, transparent 0 3px, var(--ui-line) 3px 5px);
}

.swatch:not(.taken):not(.mine):hover {
  border-color: var(--ui-dim);
}

@media (pointer: coarse) {
  .swatch {
    width: 2.2em;
    height: 2.2em;
  }
}

@media (max-width: 599px) {
  .look-swatches {
    padding-inline-start: 0;
  }
}

/* "Scan the QR code" on Join a game: only where js/scan.ts found a camera it may use. */
.scan-btn {
  display: none;
}

:root.can-scan .scan-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
}

.scan-icon {
  width: 1.1em;
  height: 1.1em;
}

.scan-overlay {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  gap: 1em;
  padding: 1.5em 1em max(1.5em, env(safe-area-inset-bottom));
  background: var(--bg);
}

.scan-overlay video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.scan-frame {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(70vw, 70vh);
  aspect-ratio: 1;
  translate: -50% -50%;
  border: 0.25em solid var(--focus);
  border-radius: 1.2em;
  box-shadow: 0 0 0 100vmax var(--scrim);
}

.scan-note {
  position: relative;
  max-width: 24em;
  margin: 0;
  padding: 0.6em 0.9em;
  border-radius: 0.8em;
  background: var(--ui);
  color: var(--ui-text);
  text-align: center;
  font-weight: 700;
}

/* Over the video. */
.scan-close,
.scan-go {
  position: relative;
}

/* Full screen (#87, js/fullscreen.ts): the button shows only where the page can go full screen
   (data-fs on the root), or, on an iPhone, says how (Add to Home Screen). */
.fs-btn,
.fs-btn .fs-exit,
:root[data-fs='on'] .fs-btn .fs-enter {
  display: none;
}

:root[data-fs] .fs-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4em;
}

:root[data-fs='on'] .fs-btn .fs-exit {
  display: inline;
}

/* Its own focus ring (Tab and the d-pad, #733). */
.fs-btn:focus-visible {
  outline: var(--ring);
  outline-offset: var(--ring-gap);
}

.fs-icon {
  flex: none;
  width: 1.15em;
  height: 1.15em;
}

:root[data-fs='on'] .fs-icon {
  transform: rotate(180deg) scale(0.85);
}

/* A phone (upright or on its side): the top bar's button is the sign alone; its words stay for
   screen readers. */
:root[data-bp='xs'] .topnav .fs-label,
:root[data-bp='s-land'] .topnav .fs-label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

:root[data-bp='xs'] .topnav .fs-btn,
:root[data-bp='s-land'] .topnav .fs-btn {
  min-width: 2.6em;
  padding-inline: 0.5em;
}

/* "Full screen on Play" only where the browser can go full screen. */
:root:not([data-fs='on'], [data-fs='off']) .fs-only {
  display: none;
}

/* The iPhone note: how to get full screen there. */
/* A pad's press the browser refused (#733): how to go full screen, for a few seconds. */
.fs-note {
  position: fixed;
  inset-block-end: max(1.5em, var(--safe-bottom, 0px));
  inset-inline: 0;
  z-index: 1000;
  width: fit-content;
  max-width: calc(100% - 2em);
  margin-inline: auto;
  padding: 0.7em 1.2em;
  border-radius: var(--r-l);
  border: 2px solid transparent;
  background: var(--bg);
  color: var(--fg);
  text-align: center;
  pointer-events: none;
}

.fs-help {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding-block: max(1em, var(--safe-top, 0px)) max(1em, var(--safe-bottom, 0px));
  padding-inline: max(1em, var(--safe-start, 0px)) max(1em, var(--safe-end, 0px));
  background: var(--scrim);
}

.fs-help-box {
  max-width: 26em;
  max-height: 100%;
  overflow: auto;
  padding: 1.2em 1.4em;
  /* Black, no shadow (A · Console, #415); high contrast: the white edge. */
  border-radius: var(--r-l);
  border: 2px solid transparent;
  background: var(--bg);
  color: var(--ui-text);
}

.high-contrast .fs-help-box {
  border-color: var(--ui-text);
}

.fs-help-box h2 {
  margin: 0 0 0.5em;
  font-size: 1.2em;
  font-weight: 900;
}

.fs-help-box p {
  margin: 0 0 0.6em;
}

.fs-help-box ol {
  margin: 0 0 1em;
  padding-inline-start: 1.4em;
  line-height: calc(1.6 * var(--line-k));
}

.fs-share {
  width: 1.1em;
  height: 1.1em;
  vertical-align: -0.15em;
}

.fs-help-ok {
  width: 100%;
}

/* A page and a server on different versions (#86, js/update.ts): a note over everything (the
   hold ring included) in the menus' look, until the page reloads. */
.version-note {
  position: fixed;
  inset: 0;
  z-index: 70;
  overflow-y: auto;
  background: var(--bg);
  color: var(--ui-text);
}

/* ------------------------------------------------------------ touch screens' menus (#271) */

/* Everything to tap in a menu is finger-sized. Min sizes, so a TV, a laptop and the match HUD
   (not inside .screen or .menu) stay as they are; :where, so what asks for more (--touch-min on
   a card's ⋯, home.css) keeps it. */
:root[data-touch]
  :where(.screen, .menu)
  :where(button, a[href], input:not([type='hidden']), select, textarea) {
  min-width: var(--touch-menu);
  min-height: var(--touch-menu);
}

:root[data-touch] :is(.screen, .menu) .chip:not(.preset, .tgame, .tlist-item) {
  justify-content: center;
}

/* The bigger swatches four and four, never seven and one. */
:root[data-touch][data-fit='narrow'] .swatch-row {
  display: grid;
  grid-template-columns: repeat(4, var(--touch-menu));
}

/* ------------------------------------------------------------ what folds on a touch screen (#270, #272) */

.fold {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6em;
  width: 100%;
  min-height: var(--touch-menu);
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  cursor: pointer;
}

.fold-mark {
  font-size: 1.3em;
  line-height: calc(1 * var(--line-k));
}

.screen .set-first {
  margin-top: -0.4em;
}

/* Hidden only on a phone: a touch tablet or TV keeps it. */
:root:is([data-bp='xs'], [data-bp='s-land']) .no-phone {
  display: none;
}

/* A phone: the Back on the panel is enough, as on the setup. */
@media (max-width: 599.98px) and (orientation: portrait),
  (max-height: 499.98px) and (orientation: landscape) {
  :root:not(.tv) .settings.folded > .topbar {
    display: none;
  }
}

/* ------------------------------------------------------------ the Start bar at big text (#268) */

/* Under 38 lines of text tall (a phone at 150 %, a phone on its side, a TV at 150 %): only Start
   stays on screen, on one line, and it keeps the where-it-runs words only if they fit that line
   (they wrap to a second line, which is cut off). Advanced scrolls with the options. */
:root[data-fit-y='short'] .setup-main > .start-row {
  display: contents;
}

:root[data-fit-y='short'] .setup-main > .start-row > .start {
  --bar: max(var(--touch-menu), 1.7em);
  position: sticky;
  /* Inside a TV's safe area (#410). */
  bottom: max(0.3em, var(--safe-bottom));
  z-index: 2;
  grid-column: 1 / -1;
  align-self: stretch;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  column-gap: 0.5em;
  height: var(--bar);
  padding-block: 0;
  overflow: hidden;
  line-height: var(--bar);
  box-shadow: 0 0 0 0.3em var(--bg);
  /* The where-it-runs words that wrap to the second line are dropped whole, on purpose: not a cut
     (the fit checks read --cut, tests/e2e/fit.ts). */
  --cut: by-design;
}

/* Why Start is off stays in sight too, pinned just above Start (#322: it scrolled away, to y = 7900
   on a 320×568 phone at 200 %). */
:root[data-fit-y='short'] .setup-main > .start-row > .start-why {
  position: sticky;
  bottom: calc(max(0.3em, var(--safe-bottom)) + 0.5em + max(var(--touch-menu), 1.7em));
  z-index: 2;
  grid-column: 1 / -1;
  padding-block: 0.2em;
  background: var(--bg);
  box-shadow: 0 0 0 0.3em var(--bg);
}

:root[data-fit-y='short'] .setup-main > .start-row > .start .pb {
  line-height: calc(1.2 * var(--line-k));
}

:root.no-gamepad[data-fit-y='short'] .setup .setup-main .pb {
  display: none;
}

:root[data-fit-y='short'] .setup-main > .start-row > .advanced {
  grid-column: 1 / -1;
  align-self: stretch;
}

:root[data-fit-y='short'] #app:has(.setup-main > .start-row) {
  scroll-padding-bottom: 3em;
}

/* A phone, short at this text size: the fewest lines above the options. */
:root[data-fit='narrow'] .setup-actions {
  flex-wrap: wrap;
}

:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .screen.setup {
  padding: 0.2em;
}

:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .setup .setup-main {
  gap: 0.3em;
  padding: 0.3em;
}

:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .tsetup-art {
  display: none;
}

:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .setup .setup-actions .ghost {
  padding: 0.1em 0.5em;
  font-size: var(--fs-small);
  white-space: nowrap;
}

/* Game options open on a short phone (#649): only its ▾ shows, so Back, How to play and it keep one
   line and the options their room; its words stay its name for a screen reader. On its side the
   title shares that line too, so there it is only its mark open or closed (▾ or ▸). */
:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short']
  .setup-more[aria-expanded='true']
  .setup-more-text,
:root[data-bp='s-land'][data-fit-y='short'] .setup-more .setup-more-text {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .screen .setup-more {
  min-width: var(--touch-menu);
  justify-content: center;
  margin-inline-start: 0;
}

/* On its side the title shares that line, with no room for the ▾ too: open options take the button
   off that row, so they keep half the screen (#268), and "Hide options ▴" at their end closes them
   (#649's review: there is always a way back). Elsewhere the button beside Back does it. */
:root[data-bp='s-land'][data-fit-y='short'] .screen .setup-more[aria-expanded='true'] {
  display: none;
}

.screen .setup-less {
  display: none;
}

:root[data-bp='s-land'][data-fit-y='short'] .screen .setup-less {
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  min-height: var(--touch-menu);
  margin-block-start: 0.4em;
  justify-self: start;
}

/* About the same size at any text size, never under the page's own. */
:root:is([data-bp='xs'], [data-bp='s-land'])[data-fit-y='short'] .screen .setup-title h2 {
  font-size: max(1em, calc(1.5em / var(--scale)));
}

/* On its side the title shares its line with Back and How to play. */
:root[data-bp='s-land'][data-fit-y='short'] .screen .setup-title h2 {
  font-size: max(var(--fs-small), calc(1.5em / var(--scale)));
}
