/* Home is the room (#73; #72 musts 1–3, should 21; Shell/HomeScreen.cs), in A · Console (#405,
   docs/plans/console-redesign.md 2.1–2.5): the top bar (the logo, a round badge per player, the
   "+" seat, the code chip, the ☰ Menu), the game's picture, the hero with how to join beside Play
   (#554; folded to one line once anyone joins) and the row of games. Nothing scrolls on a TV, with
   8 players at any text size, at 1080p and 720p.
     TV / laptop (from 1200 px wide, 500 px tall): the hero on the start side, the row along the
     bottom. The top bar and the rest stay inside the safe area (5 % on a TV).
     Tablet (600–1200 px wide): the same, smaller; held upright, the hero across the width.
     Phone (under 600 px wide, or under 500 px tall; Shell/PhoneHome.cs): the first screen (#856:
     the code boxes, Play now, every game a tile, On your own), then, once friends are invited or
     the options are opened, the room: code and Share, players, Play on one screen, the strip
     still sideways. */

.home-room {
  position: relative;
  height: 100%;
  min-height: 0;
  overflow: auto;
  padding-block: max(1.1em, var(--safe-top)) max(0.8em, var(--safe-bottom));
  padding-inline: max(2em, var(--safe-start)) max(2em, var(--safe-end));
}

.home3 {
  flex: 1;
  min-height: 0;
  display: grid;
  gap: 1.1em;
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: 'games';
  align-items: start;
}

.home3 > .panel {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.home-games {
  grid-area: games;
}

.join-card {
  grid-area: join;
}

.home-room > .note:empty {
  display: none;
}

/* ------------------------------------------------------------ the top bar (#405, plan 2.1–2.4) */

/* The logo, the badges, the code chip and the ☰ Menu on one row; under 36 ems (200 % text on a
   TV, a phone's width) two rows, the badges under the rest (2.5). Above the games, so a badge's
   name tag and the look card come over them. */
.room-bar {
  position: relative;
  z-index: 3;
  margin-top: 0;
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto auto;
  grid-template-areas: 'logo who chip menu';
  align-items: center;
  column-gap: 1rem;
  row-gap: 0.6rem;
}

.room-bar > .logo {
  grid-area: logo;
}

.room-bar > .who {
  grid-area: who;
}

.room-bar > .room-chip {
  grid-area: chip;
}

/* The bar's end: the round Full screen button (where it shows, #406) and the ☰ Menu. */
.room-bar > .bar-end {
  grid-area: menu;
  display: flex;
  align-items: center;
  gap: 0.6rem;
}

:root[data-fit='narrow'] .room-bar {
  grid-template-areas:
    'logo . chip menu'
    'who who who who';
}

/* ---- the badges: the player's shape on a dark disc, ringed in their colour */

/* The badges sit beside the chip; when the row runs short they overlap a little, each still
   showing its shape (a stack, never hidden, never "+3"). --n: how many, the "+" seat too. Only the
   top bar's (#520): a podium's .who is no container. */
.room-bar > .who {
  position: relative;
  container-type: inline-size;
  min-width: 0;
  --gap: calc(var(--badge) * 0.16);
  --ring-w: calc(var(--badge) * 0.06);
  --overlap: max(
    calc(var(--badge) * -0.4),
    min(var(--gap), calc((100cqi - var(--badge) * var(--n) - 1em) / max(1, var(--n) - 1)))
  );
}

.badges {
  list-style: none;
  margin: 0;
  padding: calc(var(--ring-w) + 0.3em) 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}

:root[data-fit='narrow'] .badges {
  justify-content: flex-start;
  padding-inline-start: var(--ring-w);
}

.badge-slot {
  position: relative;
  flex: none;
}

.badge-slot + .badge-slot {
  margin-inline-start: var(--overlap);
}

/* The one with the focus is on top of the stack. */
.badge-slot:focus-within {
  z-index: 2;
}

.badge {
  position: relative;
  display: grid;
  place-items: center;
  width: var(--badge);
  height: var(--badge);
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: var(--ui-raised);
  color: var(--ui-text);
  /* The ring in the player's colour, and a dark edge so badges in a stack stay apart. */
  box-shadow:
    0 0 0 var(--ring-w) var(--c, var(--ui-seat)),
    0 0 0 calc(var(--ring-w) * 1.8) var(--bg);
  cursor: pointer;
}

.badge[data-nav]:focus {
  outline-offset: calc(var(--ring-w) * 1.8 + var(--ring-gap));
}

.badge-look svg {
  display: block;
  width: calc(var(--badge) * 0.52);
  height: calc(var(--badge) * 0.52);
}

.badge.away .badge-look {
  opacity: 0.5;
}

/* The free seat: a dashed ring with a "+". */
.badge.plus {
  background: none;
  box-shadow: none;
  border: calc(var(--badge) * 0.05) dashed var(--ui-seat);
  color: var(--ui-faint);
  font-size: calc(var(--badge) * 0.5);
  font-weight: 700;
  line-height: 1;
}

/* The host's ★ (outlined, so it shows on a yellow ring) and the away clock, on the badge's edge. */
.badge-mark {
  position: absolute;
  width: calc(var(--badge) * 0.4);
  height: calc(var(--badge) * 0.4);
  inset-inline-end: calc(var(--badge) * -0.1);
}

.badge-mark svg {
  display: block;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.badge-star {
  top: calc(var(--badge) * -0.1);
}

.badge-star path {
  fill: var(--do);
  stroke: var(--bg);
  stroke-width: 3.5;
  stroke-linejoin: round;
  paint-order: stroke;
}

.badge-away {
  bottom: calc(var(--badge) * -0.1);
}

.badge-away circle {
  fill: var(--ui-raised);
  stroke: var(--notice);
  stroke-width: 3;
}

.badge-away path {
  fill: none;
  stroke: var(--ui-text);
  stroke-width: 2.5;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.high-contrast .badge-away circle {
  stroke: var(--ui-text);
}

/* ---- the name tag: on focus, while the look card is open, and for 4 s on news (2.2) */

/* Under its badge, centred, over whatever is below (never moves the bar). Text a TV reads at its
   smallest label size at least. */
.badge-tag {
  position: absolute;
  top: calc(100% + var(--ring-w) * 2 + 0.3em);
  inset-inline: -12em;
  z-index: 3;
  width: max-content;
  max-width: min(16em, 44vw);
  margin-inline: auto;
  padding: 0.3em 0.8em 0.35em;
  border: 2px solid var(--c, var(--ui-line));
  border-radius: var(--r-m);
  background: var(--bg);
  text-align: center;
  line-height: calc(1.2 * var(--line-k));
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
}

/* Under 36 ems the badges have a row of their own from the bar's start, the first at the screen's
   edge: the tag starts at its badge, so it never runs off the screen (a long name in Arabic did). */
:root[data-fit='narrow'] .badge-tag {
  inset-inline: calc(var(--ring-w) * -1) auto;
  margin-inline: 0;
}

/* Where the hero starts right under the bar, a tag hanging under its badge lay over the hero's
   first lines for those 4 s (#538: the yellow meta line on an upright tablet; that and the game's
   name at 200 % on a TV). There the news tag stays in the bar's row (a tag on focus or with the
   look card open still hangs under its badge: it is asked for, and must not lie over the badges
   beside it). Under 36 ems (the badges have a row of their own from the screen's edge) it stands at
   the row's far end, by itself: after the seventh of eight badges it ran off the screen, and before
   a badge it lay over the others (#538 review). On an upright screen it stands before its badge,
   towards the logo (the badges stand at the end of their space). */
:root[data-fit='narrow'] .room-bar .badge-slot:has(> .badge.news:not(:focus, .open)) {
  position: static;
}

:root[data-fit='narrow'] .room-bar .badge.news:not(:focus, .open) + .badge-tag {
  top: 50%;
  translate: 0 -50%;
  inset-inline: auto 0;
}

@media (orientation: portrait) {
  :root:not([data-fit='narrow']) .room-bar .badge.news:not(:focus, .open) + .badge-tag {
    top: 50%;
    translate: 0 -50%;
    inset-inline: auto calc(100% + 0.4em);
    margin-inline: 0;
  }
}

/* A long name wraps (never clipped: a line cut to its height loses an accent's top). */
.badge-tag b,
.badge-tag small {
  display: block;
  overflow-wrap: anywhere;
}

.badge-tag b {
  font-weight: 900;
}

.badge-tag small {
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 800;
  color: var(--ui-faint);
}

.badge:focus + .badge-tag,
.badge.open + .badge-tag {
  opacity: 1;
  visibility: visible;
}

/* News: shows at once and fades at the end (C# gives how far in it starts, so drawing Home again
   does not restart it). One at a time: another badge's focus or card has the place then. */
.badge.news + .badge-tag {
  animation: tag-news 4s linear both;
  animation-delay: var(--tag-at, 0s);
}

.room-bar > .who:is(:focus-within, :has(.badge.open)) .badge.news:not(:focus, .open) + .badge-tag {
  animation: none;
}

@keyframes tag-news {
  0%,
  92% {
    opacity: 1;
    visibility: visible;
  }
  100% {
    opacity: 0;
    visibility: hidden;
  }
}

/* ---- the code chip: the address, the code big enough to read from the sofa, a small QR code */

.room-chip {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding-block: 0.3em;
  padding-inline: 0.8em 0.4em;
  border: 2px solid transparent;
  border-radius: var(--r-m);
  background: var(--ui);
  color: var(--ui-text);
  text-align: start;
  cursor: pointer;
}

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

.chip-words small {
  display: block;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--ui-dim);
  white-space: nowrap;
}

.room-chip :is(.join-code, .join-code-copy) {
  display: block;
  font-size: var(--fs-title);
  font-weight: 900;
  letter-spacing: 0.12em;
  margin-inline-end: -0.12em;
  line-height: 1;
}

.chip-qr {
  flex: none;
  width: calc(var(--fs-title) * 1.35);
  height: calc(var(--fs-title) * 1.35);
}

.chip-qr .qr {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--r-s);
}

/* Hide code: the chip is a Show code button. */
.room-chip.chip-hidden {
  padding-inline: 1em;
  min-height: var(--badge);
  font-weight: 900;
}

/* It came from the big card (one --t-move slide up into the bar; a fade under reduced motion). */
.room-chip.arrive {
  animation: chip-arrive var(--t-move) ease-out both;
  animation-delay: var(--arrive-at, 0s);
}

@keyframes chip-arrive {
  from {
    transform: translateY(30vh) scale(1.4);
    opacity: 0;
  }
}

.reduced-motion .room-chip.arrive {
  animation: chip-fade 0.32s linear both;
  animation-delay: var(--arrive-at, 0s);
}

@keyframes chip-fade {
  from {
    opacity: 0;
  }
}

/* ---- the look card: drops from its badge (2.1) */

/* Under its badge, kept inside the bar's width: the row of badges ends at the bar's end (starts
   at its start under 36 ems), and each badge is a badge and a step on from the one before. */
.look-card {
  --row-start: calc(100cqi - var(--n) * var(--badge) - (var(--n) - 1) * var(--overlap));
  position: absolute;
  /* Under the badge's name tag, which stays in sight above it. */
  top: calc(100% + 3.4em);
  inset-inline-start: clamp(
    0px,
    calc(var(--row-start) + var(--at) * (var(--badge) + var(--overlap)) + var(--badge) / 2 - 11em),
    calc(100% - 22em)
  );
  z-index: 4;
  width: min(22em, 100cqi);
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  padding: 0.8em 0.9em;
  border-radius: var(--r-l);
  border: 2px solid var(--c);
  background: var(--ui-card);
  box-shadow: 0 0.6em 1.6em var(--shadow);
}

:root[data-fit='narrow'] .look-card {
  --row-start: var(--ring-w);
}

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

.look-who {
  display: flex;
  align-items: center;
  gap: 0.6em;
}

.look-icon svg {
  display: block;
  width: 2.2em;
  height: 2.2em;
}

.screen .look-card h2 {
  margin: 0;
  font-size: 1.1em;
  line-height: calc(1.2 * var(--line-k));
  overflow-wrap: anywhere;
}

.look-how {
  margin: 0;
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 800;
  color: var(--ui-faint);
}

.look-rows {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em 0.8em;
}

.look-card .look-swatches {
  padding-inline-start: 0;
}

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

.look-actions.look-end {
  justify-content: flex-end;
}

.look-card .mini.leave {
  color: var(--warn);
}

.look-card .hint {
  margin: 0;
}

/* ---- toasts over the games: who knocks, and the note (why a match ended, a link copied…) */

.home-knocks,
.home-room > .note {
  position: absolute;
  z-index: 2;
  inset-inline: 0;
  bottom: max(1em, var(--safe-bottom));
  width: max-content;
  max-width: min(40em, calc(100% - 4em));
  margin-inline: auto;
  padding: 0.5em 1em;
  border-radius: var(--r-l);
  background: var(--bg);
  border: 2px solid var(--ui-line);
  box-shadow: 0 0.4em 1.2em var(--shadow);
}

.home-knocks {
  bottom: calc(max(1em, var(--safe-bottom)) + 3em);
}

.home-knocks .knocks {
  margin: 0;
}

/* ------------------------------------------------------------ the ways to join here */

.join-here {
  margin-top: 0.6em;
  padding: 0.55em 0.8em;
  border: 2px dashed var(--ui-line);
  border-radius: 0.9em;
}

.join-here-title {
  margin: 0;
  font-weight: 900;
}

.join-here-buttons {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
}

/* The row's gap is their space (a small button's own end margin made two of them in Arabic at 200 %
   miss one row by a hair). */
.join-here-buttons .mini {
  margin: 0;
}

/* A touch screen's only way to play here: big enough for a thumb (#72). */
.join-here .join-tap {
  min-height: var(--touch-min);
  /* Never wider than the panel (a phone at 200 % text). */
  min-width: min(12em, 100%);
  margin-top: 0.3em;
}

/* ------------------------------------------------------------ games: the big pick and the strip (#133) */

/* The chosen game big (its picture, name, pitch, players and length) under the Tournament card,
   and a strip of small tiles below it, one per game, that scrolls sideways (Shell/HomePick.cs,
   js/home.ts). Nothing here counts the games. */
.home-games {
  container-type: inline-size;
}

.games-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  column-gap: 1em;
  margin-bottom: 0.6em;
}

.screen .home-games .games-head h2 {
  margin: 0;
}

/* What every game shares, once beside the heading. */
.home-games .games-note {
  margin: 0;
  font-size: max(var(--fs-small), 0.75em);
  font-weight: 800;
  color: var(--ui-dim);
}

/* ---- the big panel: one button, so a click or a tap anywhere on it opens the game */

.gpick-box {
  display: flex;
  min-height: 0;
  margin-bottom: 0.7em;
}

.gpick {
  flex: 1;
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr) auto;
  gap: 0.5em;
  padding: 0.6em 0.9em 0.8em;
  border-radius: 1.1em;
  border: 2px solid var(--ui-line);
  background:
    radial-gradient(ellipse 80% 60% at 50% 38%, var(--ui-quiet), transparent 75%), var(--ui-card);
  text-align: start;
  cursor: pointer;
  animation: gpick-in 0.18s ease-out both;
}

/* A new choice fades in quickly (never slides); reduced motion: at once. */
@keyframes gpick-in {
  from {
    opacity: 0.35;
  }
}

.reduced-motion .gpick {
  animation: none;
}

.gpick:hover {
  border-color: var(--ui-faint);
}

.gpick:focus-visible {
  outline: var(--ring);
  outline-offset: var(--ring-gap);
}

.gpick-art {
  display: block;
  min-height: 0;
}

.gpick-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-mask-image: radial-gradient(ellipse 72% 72% at 50% 50%, #000 62%, transparent 100%);
}

/* A game with its store set (#296): its clip in the picture's place (js/clips.ts), its logo for its name. */
.gpick-clip {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

.gpick-clip img,
.gpick-clip video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Fades into the panel, as the game pictures do. */
  -webkit-mask-image: radial-gradient(ellipse 62% 62% at 50% 50%, #000 62%, transparent 100%);
}

.gpick-clip video {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.gpick-clip.playing video {
  opacity: 1;
}

.gpick-fallback {
  display: none;
}

.gpick-clip.no-still > img {
  display: none;
}

.gpick-clip.no-still .gpick-fallback {
  display: block;
  height: 100%;
}

.gpick-icon[hidden] {
  display: none;
}

.gpick-icon {
  display: inline-block;
  width: 1.15em;
  height: 1.15em;
  margin-inline-end: 0.3em;
  vertical-align: -0.2em;
}

.gpick-body {
  min-width: 0;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    'name play'
    'about about';
  align-items: center;
  column-gap: 0.8em;
  row-gap: 0.25em;
}

.gpick-name {
  grid-area: name;
  font-size: 1.6em;
  font-weight: 900;
  line-height: calc(1.1 * var(--line-k));
  hyphens: auto;
  overflow-wrap: anywhere;
}

.gpick-about {
  grid-area: about;
  display: flex;
  flex-direction: column;
  gap: 0.35em;
}

.gpick-tag {
  color: var(--ui-dim);
  font-weight: 700;
}

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

.gfact {
  padding: 0.05em 0.6em;
  border-radius: 1em;
  background: var(--ui-key);
  font-size: max(var(--fs-small), 0.75em);
  font-weight: 800;
}

/* Play: the yellow pill of the main button; Ⓐ in it for a pad, not on a touch screen. */
.gpick-play {
  grid-area: play;
  display: inline-flex;
  align-items: center;
  gap: 0.3em;
  padding-block: 0.3em;
  padding-inline: 0.45em 1em;
  border-radius: var(--r-pill);
  background: var(--do);
  color: var(--on-do);
  box-shadow: var(--do-glow);
  font-weight: 900;
  white-space: nowrap;
}

.gpick-play .pb {
  margin: 0;
}

:root[data-touch] .gpick-play {
  padding-inline-start: 1em;
}

/* No Ⓐ in it with no pad in the room (#266): the word in the middle, in the same width as before
   (wider, a long name at 200 % text runs out of the panel). */
:root.no-pad:not([data-touch]) .gpick-play {
  padding-inline: 0.725em;
}

:root[data-touch] .gpick-play .pb {
  display: none;
}

/* ---- the strip */

.gstrip-box {
  position: relative;
  min-width: 0;
}

.gstrip {
  /* The padding (taken back by the margin) keeps focus rings (3 px, 3 px out) from being cut. */
  --ring-room: max(0.4em, 7px);
  --fade-start: 0px;
  --fade-end: 0px;
  list-style: none;
  margin: 0 calc(-1 * var(--ring-room));
  padding: var(--ring-room);
  display: flex;
  gap: 0.5em;
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  scrollbar-width: none;
  /* Faded edges while there are more games that way (js/home.ts sets the classes). */
  -webkit-mask-image: linear-gradient(
    to right,
    transparent,
    #000 var(--fade-start),
    #000 calc(100% - var(--fade-end)),
    transparent
  );
}

:root[dir='rtl'] .gstrip {
  -webkit-mask-image: linear-gradient(
    to left,
    transparent,
    #000 var(--fade-start),
    #000 calc(100% - var(--fade-end)),
    transparent
  );
}

.gstrip::-webkit-scrollbar {
  display: none;
}

.more-before > .gstrip {
  --fade-start: 2.4em;
}

.more-after > .gstrip {
  --fade-end: 2.4em;
}

.gstrip > li {
  /* Few games fill the row; many keep this width and scroll (at least 2½ tiles in sight). */
  flex: 1 0 min(6.2em, calc((100% - 1em) / 2.5));
  max-width: 9em;
  display: flex;
  scroll-snap-align: start;
  scroll-margin-inline: 2.5em;
}

.gtile {
  position: relative;
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: 0.15em;
  padding: 0.3em 0.3em 0.35em;
  border-radius: 0.8em;
  border: 2px solid var(--ui-line);
  background: var(--ui-card);
  text-align: center;
  cursor: pointer;
}

.gtile-art img {
  display: block;
  width: 100%;
  height: auto;
  user-select: none;
  opacity: 0.7;
  -webkit-mask-image: radial-gradient(ellipse 75% 75% at 50% 50%, #000 60%, transparent 100%);
}

.gtile-name {
  font-size: max(var(--fs-small), 0.75em);
  font-weight: 800;
  line-height: calc(1.15 * var(--line-k));
  color: var(--ui-dim);
  hyphens: auto;
  overflow-wrap: anywhere;
}

.gtile:hover {
  border-color: var(--ui-faint);
}

/* The chosen game: a solid frame, full colour and a notch pointing up at its panel (never
   colour alone: the frame is thicker, the picture brighter, the name bolder). */
.gtile[aria-current='true'] {
  border: 3px solid var(--ui-text);
  padding: calc(0.3em - 1px) calc(0.3em - 1px) calc(0.35em - 1px);
  background: var(--ui-quiet);
}

.gtile[aria-current='true'] .gtile-art img {
  opacity: 1;
}

.gtile[aria-current='true'] .gtile-name {
  color: var(--ui-text);
  font-weight: 900;
}

.gtile[aria-current='true']::before {
  content: '';
  position: absolute;
  top: -3px;
  left: 50%;
  border: 0.4em solid transparent;
  border-top-color: var(--ui-text);
  transform: translateX(-50%);
}

/* ◀ ▶: more games that way; a press scrolls a page (the pad and the keys go one by one). */
.gstrip-more {
  position: absolute;
  top: 50%;
  z-index: 1;
  display: none;
  place-items: center;
  width: 1.8em;
  height: 1.8em;
  padding: 0;
  border-radius: 50%;
  border: 2px solid var(--ui-line);
  background: var(--ui);
  color: var(--ui-text);
  font-size: max(var(--fs-small), 0.8em);
  line-height: calc(1 * var(--line-k));
  transform: translateY(-50%);
  cursor: pointer;
}

.gstrip-more.prev {
  inset-inline-start: -0.6em;
}

.gstrip-more.next {
  inset-inline-end: -0.6em;
}

.more-before > .gstrip-more.prev,
.more-after > .gstrip-more.next {
  display: grid;
}

/* A touch screen: ◀ ▶ big enough for a thumb (the strip is swiped too). */
:root[data-touch] .gstrip-more {
  width: var(--touch-min);
  height: var(--touch-min);
}

/* A wide games panel that is not a TV's column (a tablet, a phone on its side): the picture
   beside the words. */
@container (min-width: 34em) {
  .gpick {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    grid-template-rows: auto;
    align-items: center;
    column-gap: 1em;
  }

  .gpick-body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      'name'
      'about'
      'play';
    row-gap: 0.5em;
  }

  .gpick-play {
    justify-self: start;
  }

  /* Big text (130 % and up): the words get the larger share, so a long name (Golden Ratio at 200 %
     beside the big Join card) stays on one line and the words inside the panel. */
  .home-games.big-text .gpick {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.3fr);
  }

  /* A phone on its side or a tablet: the picture never taller than most of the screen. */
  .gpick-art img {
    max-height: min(15em, 52vh);
  }
}

/* A narrow one (a phone held upright, big text): Play is a full-width, thumb-sized button. */
@container (max-width: 22em) {
  .gpick-body {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas:
      'name'
      'about'
      'play';
  }

  .gpick-play {
    justify-content: center;
    min-height: var(--touch-min);
    margin-top: 0.3em;
  }

  /* The Tournament card's words go under its shapes rather than one word a line (200 % text). */
  .home-games .tcard {
    flex-wrap: wrap;
  }

  .home-games .tcard-text {
    flex: 1 1 9em;
  }
}

/* Very narrow (a phone upright at 200 % text): a smaller title, and a tile and a bit wide, so
   names stay whole words. */
@container (max-width: 13em) {
  .gpick {
    padding-inline: 0.6em;
  }

  /* The name on one line when it can be: it shrinks with the panel, never under the small labels. */
  .gpick-name {
    font-size: max(var(--fs-small), min(1.25em, 10.5cqi));
  }

  .gstrip > li {
    flex-basis: 72%;
    max-width: none;
  }
}

/* ------------------------------------------------------------ A · Console: the backdrop, the hero and the row (#406) */

/* A TV's, a laptop's and a tablet's Home (Shell/HomePick.cs Hero, Backdrop, Row; plan 2.5, 2.8,
   3.2, 3.3): the picked game's picture fills the screen under the fades, the hero sits on the start
   side over a dark band as wide as its own box, and the row of icon tiles runs along the bottom.
   A phone's Home keeps the panel and strip above (Shell/PhoneHome.cs). Light for a TV browser: no
   masks, blurs or animated gradients here; only opacity, size and transform move. */
.hero-home {
  isolation: isolate;
  display: flex;
  flex-direction: column;
  /* Home's hero box: a little narrower than the token's, so the dark band under it leaves more of
     the picture bright, as in the mock-up (the fades follow it: .hero-shade is .fades). */
  --hero-w: min(44vw, 30em);
}

/* ---- the backdrop: the picture layer (mirrored right to left, no text in it) and the fades */

.hero-bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  overflow: hidden;
  background: var(--bg);
  pointer-events: none;
}

/* The picture layer runs left to right whatever the page does: right to left it is turned as a
   whole (scaleX(-1)), so the action sits on the left, away from the hero's text (plan 2.8). The
   loader's copy (.loader-pic, index.html, #529) shares these rules, so it lands where Home's does. */
.hero-pic,
.loader-pic {
  position: absolute;
  inset: 0;
  z-index: 0;
  direction: ltr;
}

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

.hero-pic > img,
.hero-pic > video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
}

/* The hero picture fades in over the kept picture once it is in (js/home.ts); at once under
   reduced motion. The clip takes over the same way (js/clips.ts). */
.hero-pic > .hero-still {
  z-index: 1;
  opacity: 0;
  transition: opacity var(--t-move) ease-out;
}

.hero-pic > .hero-still.ready {
  opacity: 1;
}

.hero-pic > video {
  z-index: 2;
  opacity: 0;
  transition: opacity var(--t-move) ease-out;
}

.hero-pic.playing > video {
  opacity: 1;
}

/* The still failed (offline: media/ is not kept): the kept picture alone. */
.hero-pic.no-still > .hero-still {
  display: none;
}

/* The kept picture (the card art, 640 × 400, a scene on nothing): whole, in the end part of the
   screen, where the hero picture has its action. */
.hero-art,
.loader-art {
  position: absolute;
  inset-block: 14% 30%;
  inset-inline: 46% 4%;
}

.hero-art img,
.loader-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

/* The fades (3.3): the top bar's, the row's, and the band under the hero, as wide as the hero's
   box (its start, its width and a margin), whatever the screen's shape. */
.hero-shade,
.loader-shade {
  --hero-end: calc(max(2em, var(--safe-start)) + var(--hero-w) + 2em);
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--fade-top), var(--fade-foot), var(--fade-side);
}

/* Under the top bar a stronger fade than the token's: its round buttons are see-through, and over a
   bright picture (the saved tournament's four games, Dodgecahedron's court) they did not read. */
.hero-shade.fades {
  --fade-top: var(--fade-top-strong);
}

/* High contrast: the band solid where text sits. */
.high-contrast .hero-shade {
  background:
    var(--fade-top), var(--fade-foot),
    linear-gradient(var(--fade-dir), var(--bg) 0 var(--hero-end), transparent var(--hero-end));
}

/* ---- the hero: the meta line, the name big, the tagline, Play and How to play */

.hero-home .home-games {
  display: flex;
  flex-direction: column;
  min-height: 0;
  min-width: 0;
}

.hero {
  container-type: inline-size;
  width: var(--hero-w);
  max-width: 100%;
  margin-block: 1.4em 1.2em;
  /* The name's size fits its box (tokens.css .fs-display; js/home.ts makes sure). */
  --display-box: 100cqi;
}

.hero-meta {
  margin: 0;
  color: var(--do);
  font-size: max(var(--fs-small), 1.05em);
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  line-height: calc(1.2 * var(--line-k));
}

.screen .hero-title {
  margin: 0.04em 0 0.08em;
  font-weight: 1000;
  letter-spacing: -0.03em;
  white-space: nowrap;
}

.hero-tag {
  margin: 0 0 0.85em;
  font-size: max(1.1em, calc(var(--fs-base) * 1.5));
  font-weight: 600;
  line-height: calc(1.25 * var(--line-k));
}

.hero-btns {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6em 0.9em;
}

/* A wide screen keeps Start, Options and How to play on one row: a long translation wraps inside its
   own quiet button, no taller than Start, so the hero never pushes the row of games off the screen
   (batch 5's run: qps-rtl at 1080p, with how to join's lines over it). */
:root[data-fit='wide'] .hero-btns {
  flex-wrap: nowrap;
}

:root[data-fit='wide'] :is(.hero-options, .hero-how) {
  flex: 0 1 auto;
  min-width: 0;
  white-space: normal;
  text-align: center;
}

/* Play: the screen's one yellow, its Ⓐ dark with a yellow letter (app.css); the word in the middle
   when there is no Ⓐ (no pad here, a touch screen). */
.hero-play,
.hero-options,
.hero-how {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 2px solid transparent;
  border-radius: var(--r-pill);
  font-weight: 900;
  white-space: nowrap;
  cursor: pointer;
}

.hero-play {
  gap: 0.4em;
  min-height: var(--touch-min);
  padding-block: 0.45em;
  padding-inline: 0.8em 1.35em;
  background: var(--do);
  color: var(--on-do);
  box-shadow: var(--do-glow);
  font-size: max(1.1em, calc(var(--fs-base) * 1.6));
}

/* Something in the way of Start (one rule for both Starts): the reason on its own line under it. */
.start-why {
  flex: 1 1 100%;
  margin: 0.4em 0 0;
  font-size: max(var(--fs-small), 0.9em);
  color: var(--notice);
  font-weight: 700;
}

/* Start pressed, the match on its way (#945): it says Starting… and waits, focus and all. */
.hero-play[aria-disabled='true'],
.gpick[aria-disabled='true'] {
  cursor: progress;
  filter: saturate(0.7);
}

.hero-play .pb {
  min-width: 0;
  width: 1.9em;
  height: 1.9em;
  padding: 0;
  margin: 0;
  border-radius: 50%;
  font-size: 0.66em;
}

:root.no-pad .hero-play,
:root[data-touch] .hero-play {
  padding-inline: 1.35em;
}

:root[data-touch] .hero-play .pb {
  display: none;
}

/* Options (#945) and How to play: quiet words beside the yellow Start, on its row at any text
   size (the flow report's lobby: "Start   Options   How to play"). */
.hero-options,
.hero-how {
  min-height: var(--touch-menu);
  padding: 0.3em 0.45em;
  background: transparent;
  color: var(--ui-text);
  font-size: max(1em, calc(var(--fs-base) * 1.3));
  font-weight: 800;
  text-decoration: underline;
  text-decoration-thickness: 0.08em;
  text-underline-offset: 0.22em;
}

.high-contrast :is(.hero-how, .hero-options, .hero-btns .btn:not(.primary)) {
  border-color: var(--ui-text);
}

/* The saved tournament as the hero (#90 step 6): the question big, who leads and who is back. */
.screen .hero-ask {
  font-size: max(1.3em, calc(var(--fs-base) * 2.6));
  white-space: normal;
  line-height: calc(1.05 * var(--line-k));
  letter-spacing: -0.02em;
}

.hero-resume .tresume-line {
  margin-block-end: 0.3em;
}

.hero-resume .tresume-who {
  margin: 0 0 0.85em;
  color: var(--ui-dim);
  font-weight: 700;
}

.hero-resume .hero-btns .btn {
  margin: 0;
  min-height: var(--touch-min);
  font-size: max(1.1em, calc(var(--fs-base) * 1.4));
}

.hero-resume .tend-ask {
  flex-basis: 100%;
  font-weight: 800;
}

/* ---- the row: the Tournament tile first, then a tile per game (it scrolls sideways) */

.grow {
  margin-top: auto;
  min-width: 0;
}

.hero-home .games-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: start;
  column-gap: 1.2em;
  row-gap: 0.1em;
  margin: 0 0 0.35em;
}

.screen.hero-home .games-head h3 {
  margin: 0;
  font-size: max(var(--fs-small), 1.05em);
  font-weight: 800;
  color: var(--ui-dim);
}

.hero-home .games-count {
  padding: 0;
  background: none;
  font-size: inherit;
}

.hero-home .games-note {
  margin: 0;
  font-size: var(--fs-small);
  font-weight: 700;
  color: var(--ui-faint);
}

/* All games (#286): small, quiet, to the public pages in a new tab; after the note, so it stays on
   the dark band (over a bright picture at the row's end it would not read). */
.all-games {
  font-size: var(--fs-small);
  font-weight: 800;
  color: var(--ui-dim);
  text-decoration: underline;
  text-underline-offset: 0.15em;
}

.all-games:hover {
  color: var(--ui-text);
}

/* The ↗ points out of the page at the line's end: right to left, the other way. */
.all-games-out {
  display: inline-block;
  margin-inline-start: 0.25em;
}

:root[dir='rtl'] .all-games-out {
  transform: scaleX(-1);
}

/* The room around the tiles for the picked one's ring and the focus ring past it (the strip keeps
   it inside its scrolling box; the Tournament tile sits as high, so the row is one line). */
.grow-tiles {
  --row-ring: calc(var(--fs-base) * 0.55);
  display: flex;
  align-items: flex-end;
  gap: calc(var(--fs-base) * 0.85);
  min-width: 0;
}

.hero-home .grow-tiles > .ttile {
  margin-block: var(--row-ring);
}

/* A tile: the game's icon, square. The chosen one is larger, fully opaque and has a white ring
   (thinner than focus's, which comes out past it). */
.hero-home .gstrip-box {
  flex: 1;
  min-width: 0;
}

.hero-home .gstrip {
  --ring-room: var(--row-ring);
  align-items: flex-end;
  gap: calc(var(--fs-base) * 0.85);
  scroll-snap-type: none;
}

.hero-home .gstrip > li {
  flex: none;
  max-width: none;
  scroll-margin-inline: calc(var(--tile) * 0.6);
}

.hero-home .gtile,
.hero-home .grow-tiles > .ttile {
  position: relative;
  flex: none;
  display: block;
  width: var(--tile);
  height: var(--tile);
  padding: 0;
  border: 0;
  border-radius: var(--r-l);
  background: var(--ui-raised);
  overflow: hidden;
}

.hero-home .gtile[aria-current='true'],
.hero-home .grow-tiles > .ttile[aria-current='true'] {
  width: var(--tile-on);
  height: var(--tile-on);
  padding: 0;
  border: 0;
  background: var(--ui-raised);
  box-shadow:
    0 0 0 max(3px, 0.14em) var(--focus),
    0 0.4em 1.4em var(--shadow);
}

.hero-home .gtile[aria-current='true']::before {
  content: none;
}

.hero-home .gtile:focus-visible,
.hero-home .grow-tiles > .ttile:focus-visible {
  outline: var(--ring);
  outline-offset: calc(max(3px, 0.14em) + var(--ring-gap));
}

.hero-home .gtile-art {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0.75;
}

.hero-home .gtile[aria-current='true'] .gtile-art {
  opacity: 1;
}

.hero-home .gtile-art img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 1;
  -webkit-mask-image: none;
}

/* The icon over the card art once it is in (js/home.ts); offline, the card art stays. */
.hero-home .gtile-art .gtile-icon {
  opacity: 0;
  transition: opacity var(--t-move) ease-out;
}

.hero-home .gtile-art .gtile-icon.ready {
  opacity: 1;
}

/* Once the icon is in, the card art under it goes (its square corners showed past the icon's
   round ones), after the icon's fade, so the tile never blinks empty. */
.gtile-art.icon-in > img:not(.gtile-icon) {
  visibility: hidden;
  transition: visibility 0s var(--t-move);
}

.hero-home .gtile-icon:not([src]) {
  display: none;
}

/* The Tournament tile: the row's one yellow-to-orange (plan 2.5), a picture with no word (#620, the
   owner's pick C3; its name is its label and title): three dark game cards in a row, rising, "1",
   "2" and a star on them, over a bar that fills to a saved tournament's progress (Shell/HomePick.cs
   TourneyPicture). */
.hero-home .grow-tiles > .ttile {
  display: grid;
  place-items: center;
  margin-inline: 0;
  padding: 0;
  border: 0;
  background: linear-gradient(135deg, var(--do), var(--do-end));
  cursor: pointer;
}

.hero-home .grow-tiles > .ttile[aria-current='true'] {
  padding: 0;
  background: linear-gradient(135deg, var(--do), var(--do-end));
}

.ttile-pic {
  display: block;
  width: 100%;
  height: 100%;
}

.ttile-pic .ttc {
  transform-box: fill-box;
  transform-origin: 50% 100%;
}

.ttile-pic .ttc rect {
  fill: var(--bg);
}

.ttile-pic .ttc1 {
  opacity: 0.55;
}

.ttile-pic .ttc2 {
  opacity: 0.75;
}

.ttile-num {
  fill: none;
  stroke: var(--do);
  stroke-width: 2.6;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ttile-star {
  fill: var(--do);
  transform-box: fill-box;
  transform-origin: 50% 50%;
}

.ttile-track,
.ttile-fill {
  fill: var(--bg);
}

.ttile-track {
  opacity: 0.35;
}

.ttile-fill {
  transform-box: fill-box;
  transform-origin: 0 50%;
  scale: var(--p) 1;
}

/* Right to left the first card and the last change places, each with its height, so the cards rise
   the way the page reads and 1, 2, ★ run that way too; the digits are not mirrored. The bar fills
   from the right. */
:root[dir='rtl'] .ttile-pic .ttc1 {
  translate: 36px 0;
}

:root[dir='rtl'] .ttile-pic .ttc3 {
  translate: -36px 0;
}

:root[dir='rtl'] .ttile-fill {
  transform-origin: 100% 50%;
}

/* The tile focused or chosen (js/home.ts adds ttile-play once per move onto it, not when Home is
   drawn again): the cards pop up one after another, the star twinkles once, the bar fills to its
   value. Nothing moves while it rests. Reduced motion: the picture as it ends, at once. */
.ttile-play .ttc {
  animation: ttile-pop 0.42s cubic-bezier(0.2, 0.9, 0.3, 1.25) both;
}

.ttile-play .ttc2 {
  animation-delay: 0.12s;
}

.ttile-play .ttc3 {
  animation-delay: 0.24s;
}

.ttile-play .ttile-star {
  animation: ttile-twinkle 0.5s ease-in-out 0.62s both;
}

.ttile-play .ttile-fill {
  animation: ttile-fill 0.6s ease-out 0.2s both;
}

@keyframes ttile-pop {
  from {
    opacity: 0;
    transform: translateY(10px) scale(0.8);
  }
}

@keyframes ttile-twinkle {
  50% {
    transform: scale(1.4) rotate(18deg);
  }
}

@keyframes ttile-fill {
  from {
    scale: 0 1;
  }
}

.reduced-motion .ttile-play :is(.ttc, .ttile-star, .ttile-fill) {
  animation: none;
}

/* High contrast: the bar's track as clear as its fill's edge. */
.high-contrast .ttile-track {
  opacity: 0.6;
}

.high-contrast :is(.hero-home .gtile, .hero-home .ttile) {
  outline: 2px solid var(--ui-line);
}

/* ◀ ▶ at the row's ends, beside the tiles rather than over them (a click on a tile is never
   taken by them), level with the tiles' middles. */
.hero-home .gstrip-box {
  padding-inline: 0.5em 1.5em;
}

.hero-home .gstrip-more {
  top: auto;
  bottom: calc(var(--row-ring) + var(--tile) / 2);
  transform: translateY(50%);
  width: 2.1em;
  height: 2.1em;
  background: var(--bg);
}

.hero-home .gstrip-more.prev {
  inset-inline-start: -0.4em;
}

.hero-home .gstrip-more.next {
  inset-inline-end: -0.4em;
}

/* ---- the layouts (2.5): a tablet as a TV, smaller; under 36 ems the hero spans its column */

/* The hero and the row across the whole width (#554: how to join is in the hero, no card beside
   it); the row at the bottom of the screen. */
.hero-home .home3 > .home-games {
  align-self: stretch;
}

/* Under 36 ems (200 % text on a TV, data-fit narrow): the hero spans its column over a full 92 %
   layer (the top bar takes two rows; the row stays one row, nothing scrolls). */
/* A tablet held upright: the hero (how to join in it) and the row across the width. */
@media (max-width: 1199.98px) and (orientation: portrait) {
  .hero-home {
    --hero-w: min(72vw, 30em);
  }
}

:root[data-fit='narrow'] .hero {
  width: 100%;
  margin-block: 0.5em 0.6em;
}

/* There the name is a little smaller and the tiles too, so the hero, the row and the bar's two
   rows fit a TV whole (still far over the body text). */
:root[data-fit='narrow'] .screen .hero-title {
  font-size: min(var(--fs-display), calc(var(--fs-base) * 3.6));
}

:root[data-fit='narrow'] .hero-tag {
  margin-block-end: 0.5em;
}

:root[data-fit='narrow'] .hero-home {
  --tile: calc(var(--fs-base) * 5.2);
  --tile-on: calc(var(--fs-base) * 6.4);
}

:root[data-fit='narrow'] .hero-shade {
  background: var(--fade-top), var(--fade-foot), linear-gradient(var(--ui), var(--ui));
}

/* ---- Home's round Full screen button, beside ☰ (#406) */

.fs-round {
  display: none;
  place-items: center;
  width: 2.6em;
  height: 2.6em;
  padding: 0;
}

:root:is([data-fs='on'], [data-fs='off']) .room-bar .fs-round {
  display: grid;
}

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

/* ------------------------------------------------------------ the Join card */

.join-card {
  align-items: center;
  text-align: center;
  gap: 0.35em;
}

.join-card h2 {
  align-self: stretch;
  text-align: start;
}

.join-qr,
.join-qr-copy {
  /* At least 22 % of the screen's height (#72 should 21), never wider than the card. */
  height: 24vh;
  min-height: 7.5em;
  max-width: 100%;
  aspect-ratio: 1;
}

.join-qr .qr,
.join-qr-copy .qr {
  display: block;
  width: 100%;
  height: 100%;
  border-radius: 0.5em;
}

.join-qr.empty {
  border: 2px dashed var(--ui-line);
  border-radius: 0.5em;
}

.join-how,
.join-other,
.join-locked,
.join-problem {
  margin: 0;
  font-size: max(var(--fs-small), 0.85em);
  color: var(--ui-dim);
  overflow-wrap: anywhere;
}

.join-how b,
.join-other b,
.join-link-copy {
  color: var(--ui-text);
}

.join-cover {
  /* About the QR code's height, so the card keeps its size. */
  align-self: stretch;
  min-height: min(24vh, 12em);
}

.join-code,
.join-code-copy {
  font-size: 3em;
  font-weight: 900;
  letter-spacing: 0.25em;
  margin-inline-end: -0.25em;
  line-height: calc(1.1 * var(--line-k));
}

.code-row :is(.join-code, .join-code-copy) {
  /* In the row, the last letter's spacing is the gap to the speaker. */
  margin-inline-end: 0;
  /* A code of wide letters (WWWW) at 200 % text fits a phone (#153); a TV keeps its size. */
  font-size: min(3em, 13vw);
}

.join-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.3em;
}

.join-locked {
  color: var(--notice);
  font-weight: 900;
}

/* ---- how to join, in Home's hero (#554, the owner's option A, "Join beside Play") */

/* While the room is empty: the QR on the start side, about 19 % of the screen's height (still read
   from the sofa; docs/decisions.md), and beside it "Join on your phone", the code big with its
   speaker and Hide code, the link with "· Press a button or key to join", and the ways to join
   here, over Play. The words are no taller than the QR. The game's picture keeps the rest. */
.hero-join.big-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  justify-items: start;
  align-items: start;
  column-gap: 1em;
  row-gap: 0;
  margin-block: 0.4em 0.6em;
  text-align: start;
}

.hero-join.big-card > * {
  grid-column: 2;
  margin: 0;
}

/* The words spaced by their own margins, not the grid's row gap: the QR spans more rows than there
   are words, and the empty rows' gaps would make the block taller than the QR. */
.hero-join.big-card > :is(.code-row, .join-how, .join-here, .join-locked, .join-problem) {
  margin-top: 0.15em;
}

.hero-join.big-card > :is(.join-qr, .join-qr-copy, .join-cover) {
  grid-column: 1;
  grid-row: 1 / span 9;
  height: 19vh;
  min-height: 6em;
}

/* Hide code (#72 later 24): the cover in the QR's place and as big (with no width of its own it took
   the words' room, #559 review): "Code hidden" and Show code. Why (for streaming, turn it off in
   Settings) is in the room sheet's cover and in Settings: at the QR's width it took five lines and
   pushed the row off the screen. */
.hero-join.big-card > .join-cover {
  box-sizing: border-box;
  inline-size: 19vh;
  min-inline-size: 6em;
  padding: 0.5em;
}

.hero-join .code-cover-title {
  font-size: 1em;
}

.hero-join .code-cover-why {
  display: none;
}

.hero-join .code-show {
  padding-inline: 0.8em;
  font-size: 0.85em;
  white-space: nowrap;
}

.hero-join h3 {
  font-size: 1em;
  font-weight: 900;
}

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

.hero-join .code-row :is(.join-code, .join-code-copy) {
  font-size: min(1.8em, 11vw);
}

.hero-join .speak-code {
  width: 1.9em;
  height: 1.9em;
}

.hero-join .join-how {
  font-size: var(--fs-small);
  line-height: 1.25;
}

/* "· Press a button or key to join", after the link: the ways to join here need no title. */
.join-press {
  color: var(--ui-dim);
}

/* The ways to join here: a row of small buttons, not a box (the hero's box is Play). */
.hero-join .join-here {
  padding: 0;
  border: 0;
}

/* No QR (a page on an address phones cannot open, #70): the note in the QR's place, a dashed box
   about as big, so the hero is no taller for it (six lines at most; the room sheet has it whole). */
.hero-join.big-card > .join-lan {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 6;
  overflow: hidden;
  grid-column: 1;
  grid-row: 1 / span 9;
  inline-size: 19vh;
  min-inline-size: 6em;
  margin: 0.5em;
  padding: 0;
  outline: 2px dashed var(--ui-line);
  outline-offset: 0.4em;
  border-radius: 0.2em;
  /* The lines after the sixth are dropped whole, on purpose (the fit checks read --cut,
     tests/e2e/fit.ts). The frame is an outline, so no padding shows a part of the next line. */
  --cut: by-design;
}

/* With how to join in it (big or folded, the same both ways so the row does not move when it
   folds), the hero is a little tighter, so the hero, how to join and the row fit a screen whole:
   less room above, the name, Play and the row's tiles a little smaller (the name still far over
   the body text; 200 % text has its own cap below). */
.screen.hero-home.join-hero .hero {
  margin-block: 0 0.2em;
}

.screen.hero-home.join-hero .hero-tag {
  margin-block-end: 0;
  font-size: max(1em, calc(var(--fs-base) * 1.3));
}

.hero-home.join-hero .hero-play {
  padding-block: 0.3em;
}

:root:not([data-fit='narrow']) .screen.hero-home.join-hero .hero-title:not(.hero-ask) {
  font-size: calc(var(--fs-display) * 0.58);
}

/* The saved tournament's question on one line (it takes two at its own size). */
.screen.hero-home.join-hero .hero-ask {
  font-size: max(1.3em, calc(var(--fs-base) * 1.9));
}

.hero-home.join-hero {
  --tile: calc(var(--fs-base) * 4.4);
  --tile-on: calc(var(--fs-base) * 5.5);
}

.hero-home.join-hero .hero-btns {
  font-size: 0.84em;
}

/* At 200 % on a TV or a laptop (data-fit narrow) the hero spans its column: Play and How to play go
   beside how to join (under it when there is no room), the name capped lower. */
:root[data-fit='narrow'] .hero-home.join-hero #home-pick:not(.hero-tourney),
:root[data-fit='narrow'] .hero-home.join-hero .tresume {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  column-gap: 1.6em;
}

:root[data-fit='narrow'] .hero-home.join-hero #home-pick:not(.hero-tourney) > *,
:root[data-fit='narrow'] .hero-home.join-hero .tresume > * {
  flex: 1 0 100%;
}

:root[data-fit='narrow']
  .hero-home.join-hero
  :is(#home-pick, .tresume)
  > :is(.hero-join, .hero-btns) {
  flex: 0 1 auto;
}

/* Its long lines wrap inside it, so Play keeps its place beside it. */
:root[data-fit='narrow'] .hero-home.join-hero :is(#home-pick, .tresume) > .hero-join {
  max-inline-size: 60%;
}

:root[data-fit='narrow'] .hero-home.join-hero :is(#home-pick, .tresume) > .hero-btns {
  margin-block-end: 0.6em;
}

:root[data-fit='narrow'] .screen.hero-home.join-hero .hero-title:not(.hero-ask) {
  font-size: min(calc(var(--fs-display) * 0.62), calc(var(--fs-base) * 3));
}

/* Once anyone is in: one line ("Join: RJJF") that opens it big again; the code is in the chip. */
.hero-join.folded {
  margin-block: 0.4em 0.6em;
}

.join-line {
  padding: 0.3em 0.9em;
  border: 0;
  border-radius: var(--r-pill);
  background: var(--ui);
  color: var(--ui-dim);
  font-weight: 800;
  cursor: pointer;
}

.join-line .join-code-copy {
  margin: 0;
  font-size: 1.15em;
  letter-spacing: 0.15em;
  color: var(--ui-text);
}

/* Beside the folded line: which key takes the keyboard's next free seat (#883). Small and quiet:
   it is for the second person at a shared laptop, not for the one who just joined. */
.hero-join .join-keys {
  /* One piece: beside "Join: CODE" where it fits, else whole on the next line (150 % zoom),
     never its key cap alone there. */
  display: inline-block;
  text-wrap: balance;
  margin-inline-start: 0.8em;
  font-size: var(--fs-small);
  color: var(--ui-dim);
}

.join-line:hover {
  color: var(--ui-text);
}

/* ------------------------------------------------------------ the setup's Start line */

.start-where {
  display: block;
  font-size: max(var(--fs-small), 0.6em);
  font-weight: 700;
  opacity: 0.85;
}

/* ------------------------------------------------------------ TV and laptop: no scrolling */

@media (min-width: 1200px) and (min-height: 500px) {
  .home-room {
    overflow: hidden;
    /* A TV cannot scroll: Home's text grows with the text setting only up to 1/38 of the screen
       height (28 px at 1080p, 19 px at 720p), its smallest labels likewise (#72 must 3 and 4). */
    font-size: min(1em, 100vh / 38);
    --fs-small: min(var(--fs-min) * var(--scale), 100vh / 38 * 0.857);
  }

  /* Inside the safe area (5 % on a TV) at any text size: the narrow screens' thinner edges
     (shell.css, data-fit narrow, 200 % on a TV) are for phones, whose Home is its own. */
  :root .screen.home-room {
    padding-block: max(1.1em, var(--safe-top)) max(0.8em, var(--safe-bottom));
    padding-inline: max(2em, var(--safe-start)) max(2em, var(--safe-end));
  }

  /* The games across the screen (how to join is in the hero, #554). */
  .home3 {
    grid-template-rows: minmax(0, 1fr);
    align-items: start;
  }

  .home3 > .panel {
    min-height: 0;
    max-height: 100%;
    overflow: hidden;
  }

  /* The hero and the row fill their column (#406): the hero at the top, the row at the bottom, so
     both always fit a 1080p or 720p TV whole, at any text size (home-pick.spec.ts measures it). */
  .home3 > .home-games {
    align-self: stretch;
  }

  .home-games > * {
    flex: none;
  }

  /* Tight lines in the hero's join block, so the hero and the row fit a TV whole: the code's line is
     its letters' height (Latin capitals). */
  .hero-join .code-row :is(.join-code, .join-code-copy) {
    line-height: 1.05;
  }
}

/* ------------------------------------------------------------ a phone's Home (#255, #256) */

.phone-room > * {
  grid-area: auto;
}

.phone-first,
.phone-room {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 0.6em;
}

.phone-home > .topbar {
  margin-top: 0.3em;
}

.phone-home .logo svg:not(:first-child) {
  display: none;
}

.phone-home .topnav .ghost {
  padding: 0.3em 0.6em;
}

.phone-home .panel {
  padding: 0.6em 0.8em;
}

.phone-home .note:empty {
  display: none;
}

.phone-home .note {
  margin: 0;
}

/* A phone's first screen (#856, UX round 3 step 6; Shell/PhoneHome.cs), top to bottom: the code,
   the one yellow Play now with a quiet Invite friends, the games (one tap plays), On your own. */
.door-top {
  display: flex;
  flex-direction: column;
  gap: 0.6em;
}

/* "Got a code?" and its four boxes in one compact row; the label goes over them when there is no
   room for both (big text). The line about the code and a room found near sit under the row. */
.door-join {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

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

.door-how {
  display: flex;
  flex-direction: column;
  font-weight: 800;
}

/* What the code is for, in small words under it. */
.door-how small {
  color: var(--ui-faint);
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 700;
}

/* The code: one real text box over four cells that show its letters (js/door.ts). The box itself
   is not drawn (no text, no caret); the cell the next letter goes in has the ring while it has the
   focus. Left to right in Arabic too: a code reads that way. 16 px at least, or an iPhone zooms. */
.code-boxes {
  position: relative;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 0.35em;
  flex: 1 1 9.5em;
  max-width: 12.5em;
  min-width: min(100%, 9.5em);
  direction: ltr;
  scroll-margin: 4em 0 1em;
}

.code-boxes .code-input {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  opacity: 0;
  font-size: max(1em, 16px);
  letter-spacing: 0;
}

.code-cell {
  display: grid;
  place-items: center;
  min-height: max(var(--touch-menu), 1.9em);
  border: 2px solid var(--ui-line);
  border-radius: var(--r-s);
  background: var(--ui-raised);
  font-size: 1.3em;
  font-weight: 900;
}

.code-boxes:focus-within .code-cell.at {
  border-color: var(--ui-text);
  box-shadow: 0 0 0 2px var(--ui-text);
}

.code-boxes[data-state='asking'] .code-cell {
  border-color: var(--ui-text);
  animation: door-asking 0.9s ease-in-out infinite alternate;
}

.code-boxes[data-state='wrong'] .code-cell {
  border-color: var(--warn);
}

.code-boxes.shake {
  animation: door-shake 0.3s;
}

.reduced-motion .code-boxes.shake,
.reduced-motion .code-boxes[data-state='asking'] .code-cell {
  animation: none;
}

@keyframes door-asking {
  to {
    opacity: 0.55;
  }
}

@keyframes door-shake {
  25% {
    transform: translateX(-0.3em);
  }

  75% {
    transform: translateX(0.3em);
  }
}

/* One line under the boxes: looking for the room, or why the code did not work. */
.door-note {
  margin: 0;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.door-note:empty {
  display: none;
}

.door-note[role='alert'] {
  color: var(--warn);
}

/* A room found near (Shell/NearbyCard.cs Compact): quiet rows with the code. Yellow is the way on
   only (the flow report's rule 1): the label is not, and a row is only for a room this phone was in. */
.door-join .nearby {
  width: 100%;
}

.door-join .nearby-found {
  color: var(--ui-faint);
}

.nearby-list.compact .nearby-row.mine {
  background: var(--do);
  color: var(--on-do);
  box-shadow: var(--do-glow);
}

.nearby-row.mine :is(.nearby-row-line, .nearby-mine) {
  color: inherit;
}

.nearby-row.mine .nearby-row-join {
  border-color: currentColor;
}

/* Play now, the page's one yellow, with the game it plays in small words; Invite friends quiet
   under it. A clear gap between them, so nothing sits on the yellow's glow (#406). */
.door-play {
  display: flex;
  flex-direction: column;
  gap: max(var(--touch-gap), 0.9em);
}

.phone-start {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.1em;
  width: 100%;
  min-height: var(--touch-min);
  font-size: 1.2em;
}

.phone-start small {
  font-size: max(var(--fs-small), 0.65em);
  font-weight: 700;
}

.door-invite {
  width: 100%;
  min-height: var(--touch-menu);
}

/* The games: every one a tile with its picture and name, three to a row (two at big text); one
   tap plays. The Tournament's tile is the last, its picture the big screen's (HomePick). */
.door-games,
.door-solo {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  min-width: 0;
}

.screen :is(.door-games, .door-solo) > .label {
  margin: 0.2em 0 0;
}

.door-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.7em 0.5em;
}

.phone-home.big-text .door-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.ptile {
  width: 100%;
  min-height: var(--touch-menu);
  display: flex;
  flex-direction: column;
  gap: 0.3em;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: center;
  cursor: pointer;
}

.ptile-art {
  display: grid;
  place-items: center;
  overflow: hidden;
  aspect-ratio: 16 / 10;
  border-radius: var(--r-m);
  background: var(--ui-raised);
}

.ptile-art img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.ptile-art .ttile-pic {
  height: 86%;
}

/* "Good alone" on the four games that are: a small word on the picture's corner. At big text it
   would cover the picture, so it is a line between the picture and the name there. */
.ptile {
  position: relative;
}

.ptile-mark {
  position: absolute;
  inset-block-start: 0.25em;
  inset-inline-start: 0.25em;
  padding: 0.05em 0.45em;
  border-radius: 999px;
  background: var(--ui-card);
  color: var(--ui-text);
  font-size: var(--fs-small);
  font-weight: 800;
}

.phone-home.big-text .ptile-mark {
  position: static;
  padding: 0;
  background: none;
  color: var(--ui-muted);
}

.ptile-name {
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 800;
  /* A long name on a narrow phone breaks with a hyphen, not mid-word bare. */
  hyphens: auto;
  overflow-wrap: anywhere;
}

/* A phone's name line under Play (#825), until this phone's player has a name: "Friends see you
   as Red Cube." and its link-style button in one short line of small text, so the fourth game
   stays in sight on an iPhone (#645). It sits in How to play's space below its words, in place of
   the games' gap (0.6em at their size). On a touch screen the button is 44 px tall (shell.css,
   #271), so the row is too. */
.name-nudge {
  margin: -0.7em 0 0;
  font-size: max(var(--fs-small), 0.85em);
  line-height: 1.25;
}

.name-nudge .name-add {
  padding: 0 0.2em;
  border: 0;
  background: transparent;
  color: var(--ui-text);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

.door-more,
.phone-more,
.phone-back {
  align-self: center;
}

.phone-back {
  align-self: flex-start;
}

/* Under Invite friends: the one quiet line into playing alone (Challenges, or Carry on with the
   next one), then what a returning visitor left (a saved tournament, Play again). Nothing here is
   yellow: Play now is the page's one. */
.door-back {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

.carry-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.2em 0.8em;
  position: relative;
  min-height: var(--touch-menu);
  padding: 0.5em 0.9em;
  padding-inline-end: 2em;
  border: 2px solid var(--ui-line);
  border-radius: var(--r-m);
  background: var(--ui-card);
  color: var(--ui-text);
  font: inherit;
  font-weight: 900;
  text-align: start;
  cursor: pointer;
}

.door-again {
  width: 100%;
  min-height: var(--touch-menu);
}

/* The saved tournament's card among them: one column, its buttons a row. */
.door-back .tresume {
  width: 100%;
}

/* On your own's heading carries what is earned in all, once there is more than one place to earn. */
.door-solo > .label {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0 0.8em;
}

.solo-total {
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 900;
}

.door-offline {
  margin: 0;
  text-align: center;
}

/* On your own: each a card with a picture, what it is and what was earned there, to come back to. */
.door-solo-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5em;
}

.solo-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.7em;
  width: 100%;
  min-height: var(--touch-min);
  padding: 0.5em;
  padding-inline-end: 2em;
  border: 2px solid transparent;
  border-radius: var(--r-m);
  background: var(--ui-card);
  color: var(--ui-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.solo-art {
  flex: none;
  width: min(5.5em, 30%);
  overflow: hidden;
  border-radius: var(--r-s);
  background: var(--ui-raised);
}

.solo-art img {
  display: block;
  width: 100%;
  height: auto;
}

/* The challenges' picture: a star, in a box the size of a game's picture. */
.solo-star {
  display: grid;
  place-items: center;
  aspect-ratio: 16 / 10;
  font-size: 2em;
  line-height: 1;
}

.solo-words {
  display: flex;
  flex: 1;
  flex-direction: column;
  min-width: 0;
  overflow-wrap: break-word;
}

.solo-title {
  font-weight: 900;
}

.solo-line {
  color: var(--ui-faint);
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 700;
}

.solo-stars {
  font-weight: 900;
}

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

.phone-home.big-text > .topbar,
.phone-home.big-text .topnav {
  flex-wrap: nowrap;
}

/* No room for the name beside the buttons at big text on a small phone. And so that Play now is
   whole on the first screen of a 320 px phone at 200 %, in Arabic's taller lines too: the small
   lines under "Got a code?" and in Play now go, the code's cells are lower, the gaps closer. */
.phone-home.big-text .logo,
.phone-home.big-text .door-how small,
.phone-home.big-text .phone-start small {
  display: none;
}

.phone-home.big-text .door-top {
  gap: 0.4em;
}

.phone-home.big-text .door-join {
  padding-block: 0.25em;
}

.phone-home.big-text .code-cell {
  min-height: max(var(--touch-menu), 1.5em);
}

/* The room bar (#645, A · Compact room): the code, Players and Invite friends in one row, the
   games straight under it; Invite takes a line of its own when the row has no room for it (a
   small phone, big text). Sharing tools open below it, each a line of its own. */
.phone-room .join-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35em 0.5em;
  text-align: center;
}

.phone-room .join-card > p,
.phone-room .join-card > .join-cover {
  flex: 0 0 100%;
  order: 6;
}

.phone-room .join-card h2 {
  margin: 0;
  font-size: 1em;
}

.phone-room .join-qr {
  flex: 0 0 100%;
  order: 3;
  height: auto;
  min-height: 0;
}

.phone-room .join-qr svg {
  display: block;
  inline-size: min(6.5em, 100%);
  margin-inline: auto;
}

.phone-room .phone-invite {
  flex: 1 1 5em;
  order: 2;
  min-block-size: var(--touch-min);
  padding-inline: 0.6em;
  white-space: normal;
}

.phone-room .code-row {
  flex: 0 1 auto;
  order: 0;
  justify-content: center;
}

.phone-room .code-row :is(.join-code, .join-code-copy) {
  font-size: min(1.35em, 7vw);
}

/* Big text: the code grows with it (no cap by the screen's width; the row wraps instead). */
.phone-home.big-text .phone-room .code-row :is(.join-code, .join-code-copy) {
  font-size: 1.35em;
}

/* Players: up to three of their shapes, overlapping a little, and the count. */
.phone-players {
  flex: none;
  order: 1;
  display: flex;
  align-items: center;
  gap: 0.3em;
  min-block-size: var(--touch-min);
  padding-inline: 0.45em;
}

/* Room for three shapes from the first: the bar keeps its width as people join (a join never
   pushes Invite friends onto a line of its own). */
.rb-looks {
  display: flex;
  min-inline-size: 2.5em;
}

.rb-looks svg {
  inline-size: 1.1em;
  block-size: 1.1em;
}

.rb-looks svg + svg {
  margin-inline-start: -0.4em;
}

.phone-players .pcount {
  font-weight: 800;
}

.phone-share {
  flex: 0 0 100%;
  order: 4;
  min-height: var(--touch-min);
}

.phone-room .join-actions {
  flex: 0 0 100%;
  order: 5;
}

.phone-room .join-cover {
  min-height: 0;
}

/* Players: your seat, and everyone else as small chips. */
.phone-room .home-players {
  gap: 0.35em;
}

.phone-room .home-players h2 {
  margin: 0;
  font-size: 1em;
}

.phone-room .home-players .hint {
  margin: 0;
}

.my-seat {
  display: flex;
  flex-direction: column;
  gap: 0.35em;
  min-width: 0;
}

.my-row {
  display: flex;
  align-items: center;
  gap: 0.5em;
  min-width: 0;
}

.my-row > svg {
  flex: none;
}

.my-name {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 900;
  overflow-wrap: anywhere;
}

.my-look {
  flex: none;
  min-height: max(var(--touch-menu), 2.6em);
  padding: 0.2em 0.8em;
}

.pmini {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.3em;
}

/* Three to a row on a normal phone, so eight players take three rows at most; a long name is cut
   (the ⋯ menu says it in full). */
.pmini-chip {
  flex: 0 1 auto;
  max-width: max(calc(33.33% - 0.2em - 1px), 6em);
  min-width: 0;
}

.home-players .pmini-none {
  display: flex;
  align-items: center;
  min-height: max(var(--touch-menu), 2.5em);
}

.pmini-chip.pop {
  animation: pcard-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
}

.reduced-motion .pmini-chip.pop {
  animation: none;
}

.pmini-chip.away {
  opacity: 0.55;
}

.pmini-in {
  display: flex;
  align-items: center;
  gap: 0.2em;
  width: 100%;
  min-height: max(var(--touch-menu), 2.5em);
  padding: 0.1em 0.35em;
  border: 2px solid var(--c-60);
  border-radius: 999px;
  background: transparent;
  color: inherit;
  font: inherit;
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 700;
}

.pmini-in svg {
  flex: none;
}

.pmini-name {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.pmini-host {
  color: var(--do);
}

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

/* The games in A · Console's phone room (#406, plan 2.10): the chosen one first as a big picture
   (its card art: a phone fetches nothing from media/) with its name, its line and Play under it,
   then the others as a grid of four tiles a row, then the Tournament card. Play is the room's one
   yellow button. */
.phone-room .home-games {
  display: flex;
  flex-direction: column;
  gap: 0.6em;
}

.phone-room .games-head {
  display: none;
}

/* The card (Start) over the whole width; Options and How to play share the row under it (#945). */
.phone-room .gpick-box {
  order: -1;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.2em 0.6em;
  margin: 0;
}

.phone-room .gpick {
  flex: 1 1 100%;
  position: relative;
  display: block;
  padding: 0;
  border: 0;
  border-radius: var(--r-l);
  overflow: hidden;
  background: var(--ui-card);
  animation: none;
}

/* The chosen game's panel (#651): its name, the yellow Play and How to play. Its row in the list
   already shows the picture, the idea, players and length. */
.phone-room :is(.gpick-art, .gpick-about) {
  display: none !important;
}

.phone-room :is(.gpick-how, .gpick-options) {
  align-self: center;
  min-block-size: var(--touch-menu);
  padding: 0.2em 0.9em;
  border: 0;
  background: transparent;
  color: var(--ui-text);
  font: inherit;
  font-weight: 700;
  text-decoration: underline;
  text-underline-offset: 0.2em;
  cursor: pointer;
}

/* The picture zoomed a little, clipped to its own box: it never runs over the name under it. */
.phone-room .gpick-art {
  display: block;
  height: min(calc(var(--fs-base) * 5), 18vh);
  overflow: hidden;
}

.phone-room .gpick-art img {
  object-fit: cover;
  transform: scale(1.3);
  -webkit-mask-image: none;
}

/* Stretched, not the wide panel's centring: the words from the start, Play the panel's width. */
.phone-room .gpick-body {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  padding: 0.5em 0.9em 0.9em;
}

.phone-room .gpick-name {
  display: block;
  font-size: max(1.2em, min(calc(var(--fs-base) * 2.2), 9cqi));
  font-weight: 1000;
  line-height: calc(1.05 * var(--line-k));
  letter-spacing: -0.02em;
}

.phone-room .gpick-about {
  margin-top: 0.15em;
}

.phone-room .gpick-tag {
  color: var(--ui-text);
  font-weight: 600;
}

.phone-room .gpick-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25em 0.7em;
  margin-block-start: 0.35em;
}

/* The choosing preview needs the player range and usual duration. Quick-mode details and
   learning advice remain in setup, where the match options are chosen (#651). */
.phone-room .gfact:nth-child(n + 3) {
  display: none;
}

/* Play: the room's one yellow, as wide as the panel, under the name and line (the panel's own
   press: a tap on it opens the chosen game). */
.phone-room .gpick-play {
  display: flex;
  justify-content: center;
  order: 3;
  min-height: var(--touch-min);
  margin-top: 0.6em;
  padding-inline: 1em;
  border-radius: var(--r-l);
  font-size: max(1.1em, calc(var(--fs-base) * 1.5));
  white-space: normal;
}

/* Named rows (#651, the owner's pick): a row a game, its small picture beside its name, its
   one-line idea, its players and length (and Good first game while this browser has played no
   match). The chosen one is ringed in white. Nothing to scroll sideways. */
.phone-room .gstrip-box {
  order: 0;
}

.phone-room .gstrip {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 0.45em;
  margin: 0;
  padding: max(3px, 0.16em);
  overflow: visible;
  -webkit-mask-image: none;
}

.phone-room .gstrip > li {
  display: block;
  max-width: none;
}

.phone-room .gstrip-more {
  display: none !important;
}

.phone-room .gtile {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 0.7em;
  width: 100%;
  padding: 0;
  border: 0;
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--ui-raised);
  text-align: start;
}

.phone-room .gtile[aria-current='true'] {
  padding: 0;
  border: 0;
  background: var(--ui-raised);
  box-shadow: 0 0 0 max(3px, 0.16em) var(--focus);
}

.phone-room .gtile[aria-current='true']::before {
  content: none;
}

.phone-room .gtile-art {
  display: block;
  flex: none;
  align-self: stretch;
  inline-size: calc(var(--fs-base) * 4.2);
  min-block-size: calc(var(--fs-base) * 4.2);
  overflow: hidden;
}

.phone-room .gtile-text {
  display: flex;
  flex: 1;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.1em;
  min-inline-size: 0;
  padding-block: 0.45em;
  padding-inline-end: 0.6em;
}

.phone-room .gtile-name {
  padding: 0;
  color: var(--ui-text);
  font-size: max(var(--fs-small), 1em);
  overflow-wrap: break-word;
}

.phone-room .gtile-idea {
  color: var(--ui-text);
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 600;
}

.phone-room .gtile-facts {
  color: var(--ui-dim);
  font-size: max(var(--fs-small), 0.8em);
  font-weight: 700;
}

/* A short, easy game for a group that has never played (#651): a small tag, never yellow (Play
   is the room's one yellow). */
.phone-room .gtile-first {
  margin-block-start: 0.15em;
  padding: 0.05em 0.55em;
  border: 1.5px solid var(--ok);
  border-radius: 999px;
  color: var(--ok);
  font-size: max(var(--fs-small), 0.75em);
  font-weight: 800;
}

.high-contrast .phone-room .gtile-first {
  border-color: var(--ui-text);
  color: var(--ui-text);
}

.phone-room .gtile-art img {
  height: 100%;
  object-fit: cover;
  transform: scale(1.25);
  opacity: 0.8;
  -webkit-mask-image: none;
}

.phone-room .gtile[aria-current='true'] .gtile-art img {
  opacity: 1;
}

/* Big text (130 % and up): the words take the row's whole width (no picture), so a name keeps
   its words whole and the rows stay short. */
.phone-home.big-text .phone-room .gtile-art {
  display: none;
}

.phone-home.big-text .phone-room .gtile-text {
  padding-inline-start: 0.7em;
}

/* The Tournament card under the game rows, laid out as one of them (the orchestrator, 2026-11-10):
   its picture (the TV's tile, on its yellow) the size of theirs at their start edge, then its name,
   its line and how many can play; the same face and corners. */
.phone-room .tcard:not(.tresume) {
  order: 1;
  gap: 0.7em;
  width: auto;
  margin: 0 max(3px, 0.16em) 0.8em;
  padding: 0;
  border: 0;
  border-radius: var(--r-m);
  overflow: hidden;
  background: var(--ui-raised);
}

.phone-room .tcard:not(.tresume) .tcard-pic {
  align-self: stretch;
  border-radius: 0;
}

.phone-room .tcard:not(.tresume) .tcard-text {
  flex: 1;
  align-items: flex-start;
  gap: 0.1em;
  padding-block: 0.45em;
  padding-inline-end: 0.6em;
}

.phone-room .tcard:not(.tresume) .tcard-name {
  font-size: max(var(--fs-small), 1em);
  overflow-wrap: break-word;
}

.phone-room .tcard:not(.tresume) .tcard-tag {
  color: var(--ui-text);
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 600;
}

/* Big text: the rows leave their pictures out (above), and so does the card; a yellow edge at its
   start keeps it the Tournament's. */
.phone-home.big-text .phone-room .tcard:not(.tresume) .tcard-pic {
  display: none;
}

.phone-home.big-text .phone-room .tcard:not(.tresume) {
  border-inline-start: 0.35em solid var(--do);
}

.phone-home.big-text .phone-room .tcard:not(.tresume) .tcard-text {
  padding-inline-start: 0.7em;
}

/* The host's line under their own name (2.10): "★ You pick the game", in yellow. */
.my-vip {
  display: block;
  color: var(--do);
  font-size: var(--fs-small);
  font-weight: 800;
}

/* Big text on a small phone: one column, so nothing runs off sideways. */
.phone-home.big-text .my-row {
  flex-wrap: wrap;
}

/* On its side: the code and players beside the games. */
:root[data-bp='s-land']:not([data-fit='narrow']) .phone-room {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
}

:root[data-bp='s-land']:not([data-fit='narrow']) .phone-room .home-games {
  grid-column: 2;
  grid-row: 1 / span 4;
}

:root[data-bp='s-land']:not([data-fit='narrow']) .phone-room > :not(.home-games) {
  grid-column: 1;
}

/* The first screen sideways: the code beside Play now and Invite, the games six to a row. */
:root[data-bp='s-land']:not([data-fit='narrow']) .door-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  gap: 0.6em 1em;
}

:root[data-bp='s-land']:not([data-fit='narrow']) .door-grid {
  grid-template-columns: repeat(6, minmax(0, 1fr));
}

:root[data-bp='s-land']:not([data-fit='narrow']) .door-solo-list {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}

/* ------------------------------------------------------------ "Join a game": the code and this screen's name */

.join-form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.4em;
  max-width: 100%;
  min-width: 0;
}

.join-form .row {
  max-width: 100%;
  min-width: 0;
}

.join-form input {
  max-width: 100%;
  min-width: 0;
}

.name-input {
  width: 12em;
  padding: 0.4em 0.6em;
  border-radius: 0.6em;
  border: 2px solid var(--ui-line);
  background: var(--ui-raised);
  font-weight: 800;
}

/* ------------------------------------------------------------ the screens near this one (#408) */

/* Shell/NearbyCard.cs, above the code on Join a game and a phone's first page: the phone page's
   join states (controller/src/join-view.css) in the big screen's tokens. */
.nearby {
  display: flex;
  flex-direction: column;
  gap: 0.6em;
  width: min(100%, 26em);
  text-align: start;
}

.nearby p {
  margin: 0;
}

/* Join a game: what was found (or the steps) beside the code on a wide screen, so a TV's Join is in
   sight at 100 %; above it when there is no room for both. */
/* The whole menu's width, not its content's: sized by its content, the card's min(100%, 26em)
   counts as its words' width, the row comes out too narrow for the card and the code side, and it
   wraps even on a 1080p TV, Join below the screen (#408's review). */
.join-with {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: flex-start;
  gap: 1em 2.5em;
  width: 100%;
  max-width: 100%;
}

.join-side {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 0.8em;
  min-width: 0;
  max-width: 100%;
}

/* Focused by the page or the d-pad: in view with its ring (big text on a TV scrolls). */
.nearby :is(.nearby-join, .nearby-row) {
  scroll-margin: 0.6em;
}

.nearby-found {
  color: var(--do);
  font-size: var(--fs-label);
  font-weight: 900;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}

/* Spaced letters break Arabic's joined ones. */
:root:lang(ar) .nearby-found {
  letter-spacing: 0;
}

.nearby-title {
  margin: 0;
  font-size: var(--fs-heading);
  font-weight: 900;
}

.nearby-looking {
  display: flex;
  align-items: center;
  gap: 0.6em;
  color: var(--ui-dim);
  font-weight: 800;
}

.nearby-spin {
  flex: none;
  width: 1.1em;
  height: 1.1em;
  border: 0.18em solid var(--ui-key);
  border-block-start-color: var(--ui-text);
  border-radius: 50%;
  animation: spin 0.9s linear infinite;
}

.reduced-motion .nearby-spin {
  animation-duration: 3s;
}

/* One screen: its game's picture with the look of whoever plays there, and how many play. */
.nearby-card {
  overflow: hidden;
  border-radius: var(--r-l);
  background: var(--ui-card);
}

.nearby-pic,
.nearby-thumb {
  position: relative;
  display: grid;
  place-items: center;
  aspect-ratio: 8 / 5;
}

.nearby-pic {
  max-height: 30vh;
}

.nearby-pic img,
.nearby-thumb img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.nearby-badge {
  position: absolute;
  inset-block-start: 0.6em;
  inset-inline-start: 0.6em;
  display: grid;
  place-items: center;
  width: 15%;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--ui-raised);
  box-shadow: 0 0 0 3px var(--c);
}

.nearby-badge svg {
  width: 62%;
  height: 62%;
}

/* No game on show: the look alone, big, on the dark card; neither: the outline of a screen. */
.no-art .nearby-badge {
  position: static;
  width: 26%;
}

.nearby-blank {
  width: 42%;
  aspect-ratio: 16 / 10;
  border: 0.15em solid var(--ui-seat);
  border-radius: var(--r-s);
}

.nearby-lines {
  display: flex;
  flex-direction: column;
  gap: 0.1em;
  padding: 0.6em 0.9em;
  font-weight: 800;
}

.nearby-mine {
  color: var(--do);
  font-weight: 900;
}

/* Two classes: more than the touch menus' 44 px (shell.css, #271). */
.nearby .nearby-join {
  width: 100%;
  min-height: var(--touch-min);
}

/* A few: the whole row is the button; no yellow, the row itself is the choice. */
.nearby-list {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  margin: 0;
  padding: 0;
  list-style: none;
}

/* Narrow (big text on a phone): the words go under the picture; the chevron stays at the end. */
.nearby-row {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3em 0.7em;
  width: 100%;
  min-height: var(--touch-menu);
  padding: 0.4em;
  padding-inline-end: 2em;
  border: 2px solid transparent;
  border-radius: var(--r-m);
  background: var(--ui-card);
  color: var(--ui-text);
  font: inherit;
  text-align: start;
  cursor: pointer;
}

.nearby-thumb {
  flex: none;
  width: min(5em, 32%);
  overflow: hidden;
  border-radius: var(--r-s);
  background: var(--ui-raised);
}

.nearby-thumb .nearby-badge {
  inset-block-start: 6%;
  inset-inline-start: 4%;
  width: 28%;
  box-shadow: 0 0 0 2px var(--c);
}

.nearby-thumb.no-art .nearby-badge {
  width: 38%;
}

.nearby-row-words {
  display: flex;
  flex: 1 1 7em;
  flex-direction: column;
  min-width: 0;
  overflow-wrap: break-word;
}

.nearby-row-title {
  font-weight: 900;
}

.nearby-row .nearby-players {
  color: var(--ui-faint);
  font-weight: 700;
}

/* A phone's first page (#650): the row's second line names the game on the screen and who is
   in; its Join is a quiet pill at the end (Play now is the page's one yellow). */
.nearby-row-line {
  color: var(--ui-faint);
  font-size: max(var(--fs-small), 0.85em);
  font-weight: 700;
}

.nearby-row-line .nearby-players {
  color: inherit;
}

/* The Join pill keeps clear of the row's end edge (#680 review). */
.nearby-list.compact .nearby-row {
  padding-inline-end: 0.6em;
}

.nearby-row-join {
  flex: none;
  margin-inline-start: auto;
  padding: 0.3em 0.9em;
  border: 2px solid var(--ui-key-edge);
  border-radius: 999px;
  font-weight: 800;
}

/* A chevron towards the end of the line: it turns round right to left. */
.nearby-go {
  position: absolute;
  inset-block-start: calc(50% - 0.25em);
  inset-inline-end: 0.9em;
  width: 0.5em;
  height: 0.5em;
  border-block-start: 0.15em solid var(--ui-faint);
  border-inline-end: 0.15em solid var(--ui-faint);
  transform: rotate(45deg);
}

:root[dir='rtl'] .nearby-go {
  transform: rotate(-45deg);
}

/* None: the three steps, the drawing of the TV beside the last. */
.nearby-crowded {
  color: var(--notice);
  font-weight: 800;
}

.nearby-steps {
  display: flex;
  flex-direction: column;
  gap: 0.5em;
  margin: 0;
  padding: 0;
  list-style: none;
}

.nearby-steps li {
  display: grid;
  grid-template-columns: 1.6em minmax(0, 1fr);
  column-gap: 0.6em;
  align-items: start;
  font-weight: 800;
}

.nearby-step-n {
  display: grid;
  place-items: center;
  width: 1.6em;
  height: 1.6em;
  border-radius: 50%;
  background: var(--ui-key);
  font-weight: 900;
}

.nearby-tvs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5em;
  margin-block-start: 0.5em;
}

.nearby-tv {
  display: flex;
  flex: 1 1 5em;
  flex-direction: column;
  min-width: 0;
  align-items: center;
  gap: 0.25em;
}

.tv-face {
  position: relative;
  display: grid;
  place-items: center;
  width: 100%;
  aspect-ratio: 16 / 9;
  border: 2px solid var(--ui-key);
  border-radius: var(--r-s);
  background: var(--ui-raised);
}

.tv-big,
.tv-chip {
  border: 3px solid var(--do);
  border-radius: var(--r-s);
  background: var(--bg);
  font-weight: 900;
  line-height: 1.2;
}

.tv-big {
  padding: 0.1em 0.4em;
  font-size: 1.2em;
  letter-spacing: 0.08em;
}

/* Later: the players' badges along the top, the code's chip in the corner. */
.tv-bar {
  position: absolute;
  inset-block-start: 0.3em;
  inset-inline: 0.35em;
  display: flex;
  align-items: center;
  gap: 0.2em;
}

/* Short of room (big text), the badges shrink, still round. */
.tv-bar i {
  flex: 0 1 0.5em;
  min-width: 0.25em;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--ui-faint);
}

.tv-chip {
  margin-inline-start: auto;
  padding: 0 0.25em;
  border-width: 2px;
}

.tv-when {
  color: var(--ui-dim);
  font-size: var(--fs-label);
  font-weight: 700;
}

.nearby-again {
  align-self: center;
}

.high-contrast :is(.nearby-card, .nearby-row, .tv-face) {
  border: 2px solid var(--ui-text);
}

/* ------------------------------------------------------------ lobby energy (#72 should 15) */

/* The players counter: a pill that bumps when someone comes or goes. The games one (where the
   chosen game is in the strip, #133) looks the same but never bumps. */
.pcount,
.games-count {
  display: inline-block;
  margin-inline-start: 0.3em;
  padding: 0.05em 0.6em;
  border-radius: 1em;
  background: var(--ui-key);
  color: var(--ui-text);
  font-variant-numeric: tabular-nums;
}

.pcount.bump {
  animation: pcount-bump 0.5s ease-out both;
  animation-delay: var(--pop-at, 0s);
}

@keyframes pcount-bump {
  0% {
    transform: scale(1);
    background: var(--focus);
    color: var(--bg);
  }
  35% {
    transform: scale(1.25);
  }
  100% {
    transform: scale(1);
  }
}

/* A new player's badge (or a setup's card) pops in (C# gives how far in it starts, so drawing Home again
   does not restart it). */
.pcard.pop,
.badge.pop {
  animation: pcard-pop 0.5s cubic-bezier(0.34, 1.56, 0.64, 1) both;
  animation-delay: var(--pop-at, 0s);
}

@keyframes pcard-pop {
  0% {
    transform: scale(0.6);
    opacity: 0;
  }
  60% {
    opacity: 1;
  }
  100% {
    transform: scale(1);
    opacity: 1;
  }
}

/* Reduced motion: no growing or bouncing; the new card fades in with a brief outline instead. */
.reduced-motion .pcount.bump {
  animation-name: pcount-flash;
}

.reduced-motion .pcard.pop,
.reduced-motion .badge.pop {
  animation-name: pcard-flash;
  animation-timing-function: linear;
}

@keyframes pcount-flash {
  0% {
    background: var(--focus);
    color: var(--bg);
  }
}

@keyframes pcard-flash {
  0% {
    opacity: 0;
    outline: 3px solid var(--c, var(--focus));
  }
  40% {
    opacity: 1;
    outline: 3px solid var(--c, var(--focus));
  }
  100% {
    outline: 3px solid transparent;
  }
}
