/* ==========================================================================
   CHALLENGES — a room, a projector, and thirty phones
   ==========================================================================

   Three surfaces with three different viewing distances, which is the whole
   of the design brief:

   - the join screen, held at arm's length by somebody who has never used the
     site and is typing a code read out loud;
   - the player screen, on a phone, where every pixel not spent on the game is
     a pixel spent on furniture;
   - the host screen, on a projector, read from the back of a classroom.

   The palette is the site's — square corners, green, one yellow for the thing
   you press. Nothing here introduces a colour, because a challenge is the
   loudest thing on the site already and it should be loud in the site's own
   voice.
   ========================================================================== */

/* ------------------------------------------------------------------ join */

.joincode {
  width: 100%;
  font-family: var(--font-ui);
  font-size: 2.2rem;
  font-weight: 700;
  letter-spacing: .28em;
  text-align: center;
  text-transform: uppercase;
  padding: .7rem .4rem;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--ink-strong);
}
.joincode:focus-visible {
  outline: 3px solid var(--green-700);
  outline-offset: 2px;
  border-color: var(--green-700);
}
.join__error {
  font-weight: 650;
  color: var(--blue);
  border-left: 4px solid var(--blue);
  padding-left: .7rem;
}

/* ------------------------------------------------------------ the player */

/* The player's screen is the game and nothing else.
 *
 * One column the height of the window, with the board given everything the
 * clock and the scores do not need. It matters more here than on the wall: a
 * board that does not fit is a keypad below the fold, and a child scrolling
 * to find the 7 has lost the question.
 */
.play {
  display: flex;
  flex-direction: column;
  gap: .5rem;
}
body.is-room .play {
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  padding: .5rem .6rem .6rem;
  gap: .4rem;
}

/* One line, always in the same place, so a learner's eye never has to hunt
   for the clock while a question is running. */
.play__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .75rem;
  font-family: var(--font-ui);
}
.play__phase {
  font-weight: 700;
  font-size: 1.05rem;
  color: var(--ink-strong);
}
.play__round {
  font-size: .78rem;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.play__timer {
  font-size: 1.9rem;
  font-weight: 700;
  line-height: 1;
  color: var(--green-700);
  font-variant-numeric: tabular-nums;
  min-width: 2.2ch;
  text-align: right;
}

/* The window, drawn rather than counted. A bar coming down is read without
   being looked at, which is the point: the number is for the last five
   seconds, the bar is for the other fifteen. */
.play__barrail,
.host__barrail {
  height: .4rem;
  background: var(--line-soft);
  overflow: hidden;
}
.play__bar,
.host__bar {
  height: 100%;
  width: 100%;
  background: var(--green-700);
  /* Linear, and only just smoothed. Easing a clock makes it lie. */
  transition: width .12s linear;
}
/* The scores are a phase with a clock on it too, so the bar keeps draining
   rather than snapping back to full and sitting there — which read as the
   question starting again. A different colour because it is a different
   thing: green is time to answer in, and this is not. */
.play__bar.is-reveal,
.host__bar.is-reveal { background: var(--blue); }

/* The board, centred and capped. A game drawn at 520px against the left edge
   of a 900px column reads as something that failed to load. */
.play .sketch {
  margin: 0 auto;
  max-width: 34rem;
}
/* While playing, it takes the height that is left. */
body.is-room .play [data-stage] {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Nothing to scroll: the board is scaled to whatever this box turns out to
     be. Kept as a last resort for a screen so short that even the scaling
     floor cannot fit it — and then it is this box that scrolls, never the
     page. */
  overflow-y: auto;
}
body.is-room .play .sketch {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
}
body.is-room .play .sketch__stage {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
}
body.is-room .play canvas { max-height: 100%; max-width: 100%; }
/* The same trap as on the room's screen: a class rule that sets `display`
   beats the browser's own `[hidden]`. */
body.is-room .play [data-stage][hidden] { display: none !important; }
body.is-room .play .board { min-height: 0; overflow-y: auto; }
.is-hidden { display: none !important; }

/* The host's way back to the room they are running. Quiet on purpose: it is
   the only site chrome on this page and it is not part of the game. */
.play__back {
  margin: 1.25rem 0 0;
  font-family: var(--font-ui);
  font-size: .85rem;
}
.play__back a { color: var(--muted); }

.play__wait {
  margin: 0;
  padding: .9rem 1rem;
  background: var(--bg-soft);
  font-family: var(--font-ui);
  font-weight: 650;
  color: var(--ink);
}

/* What one answer was worth. Sits under the game, holds its height whether
   or not there is anything in it, and never pushes the canvas around
   mid-question. */
.play__result {
  min-height: 1.8rem;
  margin: 0;
  text-align: center;
  font-family: var(--font-ui);
  font-size: 1.15rem;
  font-weight: 700;
  color: var(--muted);
}
/* Sent, and saying nothing else. What it was worth arrives when the question
   shuts — earlier than that is this player telling the room. */
.play__result--sent { color: var(--muted); }
.play__result--right { color: var(--green-700); }
.play__result--wrong { color: var(--blue); }
.play__result--missed { color: var(--muted-2); }

/* ------------------------------------------------------- how it went ----
 *
 * The question, before the leaderboard. A room that only ever sees the top
 * five learns which five children are quick; a room that sees "eleven got it,
 * nine did not" learns something about the question — which is the thing the
 * teacher is about to talk about.
 *
 * Counts, never names. There is no version of this with names on it that is
 * not nine children being told off in public.
 */
.tally {
  margin: .25rem 0 1rem;
}
.tally__title {
  font-family: var(--font-ui);
  font-size: .8rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 .5rem;
}
.tally__bar {
  display: flex;
  height: 1.1rem;
  background: var(--line-soft);
  overflow: hidden;
}
.tally__part {
  display: block;
  height: 100%;
  /* Widths change once a question, so the growth is worth seeing — it is the
     room arriving, not a number being replaced. */
  transition: width .35s ease;
}
.tally__part--right { background: var(--green-700); }
.tally__part--wrong { background: var(--blue); }
.tally__part--missed { background: var(--line); }
/* A mark and a number, twice. Read at a glance from the back of a room, in
   any language, by anybody who has ever been marked — and drawn as paths, so
   the tick and the cross are the same weight as each other at any size, which
   two characters from a font are not. */
.tally__counts {
  display: flex;
  flex-wrap: wrap;
  gap: .3rem 1.6rem;
  justify-content: center;
  align-items: center;
  margin: .6rem 0 0;
  font-family: var(--font-ui);
}
.tally__stat {
  display: inline-flex;
  align-items: center;
  gap: .45rem;
  font-variant-numeric: tabular-nums;
}
.tally__stat b { font-size: 1.5rem; font-weight: 700; line-height: 1; }
.tally__mark { width: 1.5rem; height: 1.5rem; display: block; }
.tally__stat--right { color: var(--green-700); }
.tally__stat--wrong { color: var(--blue); }
.tally__stat--missed { color: var(--muted-2); }
/* The host's live count while a question is still running — the one number
   that says the room is ready to move before it moves itself. */
.tally__live {
  margin: 0;
  font-family: var(--font-ui);
  font-weight: 700;
  color: var(--muted);
}

/* On the projector it is the slide, so it is set two sizes up. */
.host .tally { max-width: 44rem; margin-inline: auto; }
.host .tally__bar { height: 1.8rem; }
.host .tally__counts { gap: .4rem 2.4rem; }
.host .tally__stat b { font-size: clamp(1.8rem, 4vw, 3rem); }
.host .tally__mark {
  width: clamp(1.8rem, 4vw, 3rem);
  height: clamp(1.8rem, 4vw, 3rem);
}

/* --------------------------------------------------------- what they type --
 *
 * Two fields in the whole feature, and both are typed under pressure: a name,
 * by a child with thirty other people waiting, and a room's name by a teacher
 * with a class in front of them. Neither should look like the bottom half of
 * a settings page.
 */
.field {
  display: block;
  margin: 0 0 1rem;
}
.field__label {
  display: block;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: .9rem;
  color: var(--ink-strong);
  margin: 0 0 .35rem;
}
.field__hint {
  display: block;
  font-family: var(--font-ui);
  font-weight: 400;
  font-size: .82rem;
  color: var(--muted);
  margin-top: .15rem;
}
.field__input {
  width: 100%;
  min-height: 3rem;
  padding: .6rem .8rem;
  font-family: var(--font-ui);
  font-size: 1.05rem;
  color: var(--ink-strong);
  background: var(--bg);
  border: 2px solid var(--line);
  border-radius: var(--radius);
}
.field__input::placeholder { color: var(--muted-2); }
.field__input:hover { border-color: var(--muted-2); }
.field__input:focus-visible {
  outline: 3px solid var(--green-700);
  outline-offset: 2px;
  border-color: var(--green-700);
}

/* The name, which is the only thing a learner ever types into this site. Set
   at the size of the thing it is: their own name, about to go on a wall. */
.namefield {
  font-size: 1.6rem;
  font-weight: 700;
  min-height: 3.6rem;
  text-align: center;
}
.name__count {
  margin: .4rem 0 1rem;
  font-family: var(--font-ui);
  font-size: .85rem;
  color: var(--muted);
  text-align: center;
}

/* --------------------------------------------------------- the leaderboard */

.board { margin-top: .5rem; }
.board h2 {
  font-family: var(--font-ui);
  font-size: .8rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 .5rem;
}
.board ol {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: .3rem;
}
.board li {
  display: grid;
  grid-template-columns: 2.2rem 1fr auto;
  align-items: center;
  gap: .6rem;
  padding: .5rem .7rem;
  background: var(--bg-soft);
  font-family: var(--font-ui);
  /* Rows move to their new places rather than being redrawn in them. Long
     enough to follow a name going past another name, short enough that the
     next question is not waiting on it. */
  transition: transform .42s cubic-bezier(.22, .68, .3, 1),
              background-color .3s ease;
}
/* A row that has just moved says which way it went, for as long as it takes to
   look up. The colour is the same green and blue the marks use, at a strength
   that reads as movement rather than as a verdict. */
.board li.is-rising { background: var(--green-100); }
.board li.is-falling { background: var(--bg-soft); }
.board li .rank,
.board li .pts { transition: color .3s ease; }
.board li.is-rising .rank { color: var(--green-700); }
/* Your own line, wherever it is. Everybody sees the top of the room and
   themselves; a full ranking on a projector names whoever is last, out loud,
   every six seconds. */
.board li.is-me {
  background: var(--green-700);
  color: #fff;
}
.board li.is-me .rank,
.board li.is-me .pts { color: #fff; }
.board .rank {
  font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.board .who {
  font-weight: 650;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.board .pts {
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--green-700);
}
/* A row that is not in the top of the room: theirs, with the distance shown
   rather than described. */
.board li.is-adrift { margin-top: 1.1rem; position: relative; }
.board li.is-adrift::before {
  content: "";
  position: absolute;
  left: 1.35rem;
  top: -.95rem;
  width: 0;
  height: .7rem;
  border-left: 2px dotted var(--line);
}

/* --------------------------------------------------------------- the host */

/* The register: who is in the room, before it starts. Names wrap into a
   block rather than a column, because thirty of them down the side of a
   projector is a scrollbar in front of a class. */
/* The names, across the wall.
 *
 * They get the flexible middle of the lobby and use the width of it: a name
 * appearing is how somebody knows their phone worked, and that is worth the
 * whole screen while there is nothing else on it. Wrapping across, never a
 * column, and never behind a scrollbar. */
.roster {
  list-style: none;
  margin: 0;
  padding: .5rem 0;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-wrap: wrap;
  align-content: center;
  justify-content: center;
  gap: .5rem .7rem;
  overflow: hidden;
}
.roster li {
  padding: .45rem 1.1rem;
  background: var(--bg-soft);
  border-bottom: 3px solid var(--green-700);
  font-family: var(--font-ui);
  font-weight: 700;
  /* Big by default and smaller as the room fills, so thirty names still fit
     the screen they are being shown on rather than falling off it. */
  font-size: clamp(1.1rem, 2.6vw, 2rem);
  line-height: 1.15;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.roster:empty { display: none; }
/* A full room: the names step down rather than the list stepping out of the
   screen. */
.roster[data-full="crowd"] li { font-size: clamp(.95rem, 1.6vw, 1.25rem); padding: .35rem .7rem; }
.roster[data-full="packed"] li { font-size: clamp(.85rem, 1.2vw, 1rem); padding: .25rem .55rem; border-bottom-width: 2px; }

/* The room's screen fits the screen.
 *
 * Not a page with a site around it and not a page that scrolls: a projector
 * shows one thing at a time and nobody is going to walk over and scroll it.
 * So the whole surface is one column the height of the viewport with fixed
 * rows top and bottom, and everything that changes with the phase — the
 * question, then the scores — gets the slack between them.
 */
/* The page around the room, removed.
 *
 * The site's layout keeps a gutter above and below its content, which is
 * right everywhere else and is the last 70 pixels between this screen and
 * fitting. Scoped to the room's own body class, so nothing else on the site
 * loses its margins. */
body.is-room .layout,
body.is-room .shell,
body.is-room .shell__content {
  padding: 0 !important;
  margin: 0 !important;
  min-height: 0 !important;
  gap: 0 !important;
}

/* The room's own gutter, which the site's layout rule would otherwise win. */
body.is-room .host { padding-inline: 1.25rem; }

.host {
  text-align: center;
  /* A definite height, not a minimum. Every percentage under this resolves
     against it — which is what lets the question be told to fit the space
     that is left rather than to be its own size and push the rest off. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
  display: flex;
  flex-direction: column;
  gap: .5rem;
  padding: .9rem 1.25rem 1rem;
  margin: 0;
  max-width: none;
}
.host__body {
  flex: 1 1 auto;
  /* Without this a flex child refuses to shrink below its content, which is
     the whole reason a screen like this ends up with a scrollbar. */
  min-height: 0;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: .6rem;
}

/* A `display` in a class rule beats the browser's own `[hidden] { display:
   none }`, which is how a board stayed on screen underneath the scores. Every
   box here that both flips with the phase and sets its own display has to say
   so again. */
.host__stage[hidden],
.tally[hidden],
.board[hidden] { display: none !important; }

/* The question, fitted to whatever height is left.
 *
 * The canvas is drawn at the size the shell chose and then scaled by CSS to
 * fill the row, which keeps every game's layout exactly as its author placed
 * it — and p5 renders at device density, so the upscale stays clean. Height
 * leads and width follows, because the height is the scarce one. */
.host__stage {
  flex: 1 1 auto;
  min-height: 0;
  display: grid;
  place-items: center;
  margin: 0;
  max-width: none;
}
.host__stage .sketch {
  margin: 0;
  height: 100%;
  width: 100%;
  max-width: none;
  display: grid;
  place-items: center;
  min-height: 0;
}
.host__stage .sketch__stage {
  border: 0;
  min-height: 0 !important;
  height: 100%;
  width: 100%;
  display: grid;
  place-items: center;
}
/* Nothing overrides the canvas's own size here, deliberately.
 *
 * The shell measures the box it was given and draws to it — that is what
 * `layout()` is — so the way to make the board fit is to give the box the
 * right height and let the game draw itself into it. Stretching the canvas in
 * CSS afterwards fights the same arithmetic from the other end, and the two of
 * them disagreeing is a board cropped by a scrollbar. */
.host__stage canvas { max-width: 100%; max-height: 100%; }

/* The code, which is the only thing on this screen for the first two
   minutes. Set at the size it has to be read at from the back of a room and
   spaced so that a child copying it letter by letter does not lose their
   place. */
.host__panel {
  background: var(--green-700);
  color: #fff;
  padding: 1.2rem 1rem 1.4rem;
  margin-bottom: 1rem;
}
.host__label {
  margin: 0;
  font-family: var(--font-ui);
  font-size: .8rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  opacity: .85;
}
.host__code {
  margin: .2rem 0 .4rem;
  font-family: var(--font-ui);
  font-size: clamp(3.5rem, 14vw, 8rem);
  font-weight: 700;
  line-height: 1;
  letter-spacing: .12em;
}
.host__where {
  margin: 0;
  font-size: clamp(1rem, 2.2vw, 1.4rem);
}
.host__where strong { font-weight: 700; }

.host__count {
  flex: 0 0 auto;
  font-family: var(--font-ui);
  font-size: clamp(1.2rem, 3vw, 2rem);
  font-weight: 700;
  color: var(--ink-strong);
  margin: 0 0 1rem;
}
.host__count span { color: var(--green-700); }

.host__controls {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  gap: .75rem;
  justify-content: center;
  align-items: center;
  margin-bottom: 1rem;
}
.host .btn--big {
  min-height: 3.4rem;
  padding: .6rem 2.4rem;
  font-size: 1.15rem;
}
.host .btn--big[disabled] { opacity: .5; cursor: default; }
/* Armed, having been pressed once. Yellow because it is now the thing that
   will happen, not because ending is an alarm. */
.btn--end.is-armed {
  background: var(--yellow);
  color: var(--green-900);
  box-shadow: inset 0 0 0 1.5px var(--yellow-dark);
}

/* Where the room is and how long is left, on one line, so the question can
   have the screen. */
.host__head {
  flex: 0 0 auto;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  text-align: left;
}
.host__where-in {
  margin: 0;
  display: flex;
  align-items: baseline;
  gap: .8rem;
  /* One line: the round and the game's name read as a single caption, and
     wrapping them turns a caption into a stack. */
  flex: 1 1 auto;
  min-width: 0;
}
.host__phase {
  font-family: var(--font-ui);
  font-size: clamp(1.2rem, 2.6vw, 2rem);
  font-weight: 700;
  color: var(--ink-strong);
}
/* Who is in and what the code is, kept small and beside the round rather than
   given a row of the screen. */
.host__aside {
  font-family: var(--font-ui);
  font-size: clamp(.8rem, 1.4vw, 1rem);
  color: var(--muted);
  margin-left: .3rem;
}
.host__aside strong { color: var(--ink-strong); letter-spacing: .06em; }

.host__round {
  font-family: var(--font-ui);
  font-size: clamp(.8rem, 1.4vw, 1rem);
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}
.host__timer {
  font-family: var(--font-ui);
  font-size: clamp(2.4rem, 6vw, 4rem);
  font-weight: 700;
  line-height: 1;
  color: var(--green-700);
  font-variant-numeric: tabular-nums;
  margin: 0;
  min-width: 2.5ch;
  text-align: right;
}

/* On a projector the board is the slide, so it is set two sizes up from the
   phone's — but it is inside a screen that does not scroll, so if a room is
   long enough to overflow it is the list that scrolls, not the page. */
.host .board {
  max-width: 44rem;
  margin-inline: auto;
  text-align: left;
  min-height: 0;
  overflow-y: auto;
  width: 100%;
}
.host .board li {
  grid-template-columns: 3rem 1fr auto;
  padding: .7rem 1rem;
  font-size: clamp(1.1rem, 2.6vw, 1.6rem);
}

/* --- fixing your name ----------------------------------------------------
 *
 * In the lobby, and nowhere else. A card rather than a stray field, because
 * it is the only thing on this screen a player can actually do while they
 * wait, and because the name it is about is the one that will be on a wall in
 * front of their class.
 */
.rename {
  margin: .9rem 0 0;
  padding: .9rem 1rem 1rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-top: 3px solid var(--green-700);
  font-family: var(--font-ui);
  max-width: 26rem;
}
.rename__eyebrow {
  margin: 0;
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.rename__now {
  margin: .15rem 0 .7rem;
  font-size: 1.35rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink-strong);
  overflow-wrap: anywhere;
}
.rename__label {
  display: block;
  margin: 0 0 .3rem;
  font-size: .78rem;
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted);
}
.rename__row { display: flex; gap: 0; align-items: stretch; }
/* Type selectors on both, because the site dresses every text input and every
   submit button in every form for a sign-in page, through rules carrying an
   element and an attribute. A class on its own loses to those wherever it
   sits in the file. */
input.rename__input {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  padding: .5rem .6rem;
  border: 1px solid var(--line);
  border-right: 0;
  border-radius: 0;
  background: var(--bg);
  font-family: var(--font-ui);
  font-size: 1rem;
  color: var(--ink-strong);
}
input.rename__input:focus-visible { position: relative; z-index: 1; }
.rename button.rename__go {
  flex: 0 0 auto;
  width: auto;
  min-height: 0;
  padding: .5rem 1rem;
  border: 0;
  border-radius: 0;
  background: var(--green-700);
  color: #fff;
  font-family: var(--font-ui);
  font-size: .82rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  cursor: pointer;
}
.rename button.rename__go:hover { background: var(--green-900); }
.rename__hint {
  margin: .55rem 0 0;
  font-size: .8rem;
  line-height: 1.45;
  color: var(--muted-2);
}

/* --- what the answer was -------------------------------------------------
 *
 * Between questions, once nobody can do anything with it, which is exactly
 * when a learner who got it wrong will read it. Not on the canvas: a room
 * hides the whole board the instant a question shuts.
 *
 * The answer, and not the sum it came out of. They were asked for a number
 * and the number is what they want; the working reads as being made to sit
 * through the question a second time. A game with two blanks has two answers
 * and they get a box each — run together, "16 196" is one wrong number.
 */
.answer {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .35rem .8rem;
  margin: .5rem 0 .8rem;
  padding: .6rem .85rem;
  background: var(--bg-soft);
  border-left: 4px solid var(--green-700);
  font-family: var(--font-ui);
  text-align: left;
}
.answer[hidden] { display: none !important; }
.answer__label {
  font-size: .7rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.answer__values { display: flex; flex-wrap: wrap; gap: .4rem; }
.answer__value {
  display: inline-block;
  padding: .1rem .55rem;
  background: var(--green-700);
  color: #fff;
  font-size: 1.25rem;
  font-weight: 700;
  line-height: 1.35;
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/* Read from the back of a room rather than off a phone. */
.answer--big {
  justify-content: center;
  border-left: 0;
  border-top: 4px solid var(--green-700);
  gap: .5rem 1.2rem;
}
.answer--big .answer__value {
  font-size: clamp(1.5rem, 4vw, 2.6rem);
  padding: .05rem .7rem;
}

/* --- a run of right answers --------------------------------------------- *
 *
 * From three, on the line it belongs to. Two in a row is not yet something
 * anybody is doing on purpose, and a badge on every line is a badge that says
 * nothing at all.
 */
.board .run {
  display: inline-flex;
  align-items: center;
  gap: .15rem;
  padding: .05rem .35rem .05rem .25rem;
  background: var(--yellow);
  color: var(--green-900);
  font-family: var(--font-ui);
  font-variant-numeric: tabular-nums;
  line-height: 1.2;
}
.board .run[hidden] { display: none !important; }
.board .run b { font-size: .82em; font-weight: 700; }
.run__mark { width: .95em; height: .95em; display: block; }
/* On the projector everything on a row grows together. */
.host .board .run { padding: .1rem .45rem .1rem .3rem; }

/* --- the end of the room ------------------------------------------------- *
 *
 * The page has no header and no footer while a challenge is running, which is
 * the whole point of it and a trap the moment the room ends. This is the exit,
 * and it is also the last thing a child sees, so it says how they did in
 * figures rather than in a sentence — where they came is what they will be
 * asked on the way out of the room.
 */
.done {
  margin: 1.25rem 0 0;
  padding: 1.15rem 1.2rem 1.25rem;
  background: var(--bg-soft);
  border-top: 4px solid var(--green-700);
}
.done[hidden] { display: none !important; }
.done__label {
  margin: 0;
  font-family: var(--font-ui);
  font-size: .7rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted);
}
.done__who {
  margin: .2rem 0 .9rem;
  font-family: var(--font-ui);
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--ink-strong);
  overflow-wrap: anywhere;
}
.done__figures {
  display: flex;
  flex-wrap: wrap;
  margin: 0 0 1.1rem;
  padding: .8rem 0;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
}
.done__cell {
  flex: 1 1 6rem;
  min-width: 0;
  padding: 0 1rem;
  border-left: 1px solid var(--line-soft);
}
.done__cell:first-child { border-left: 0; padding-left: 0; }
.done__cell[hidden] { display: none; }
.done__n {
  margin: 0;
  font-family: var(--font-ui);
  font-size: clamp(1.6rem, 5vw, 2.2rem);
  font-weight: 700;
  line-height: 1;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}
/* The one figure of the three they will repeat to somebody. */
.done__cell--lead .done__n { color: var(--green-700); }
.done__cap {
  margin: .4rem 0 0;
  font-family: var(--font-ui);
  font-size: .7rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}
.done__acts { display: flex; flex-wrap: wrap; gap: .6rem; margin: 0; }
.done__note {
  margin: .9rem 0 0;
  font-family: var(--font-ui);
  font-size: .8rem;
  line-height: 1.5;
  color: var(--muted-2);
}

/* ------------------------------------------------------------- the report --
 *
 * What the room was for, after the room. Read on a laptop once the class has
 * gone, and often printed or saved as a PDF — so it is a document rather than
 * a screen: one column, generous rules, and figures set in tabular numerals so
 * a column of scores lines up the way a column of scores should.
 *
 * Colour does exactly one job here and it is the same job it does in the room:
 * green got it, blue did not, grey sat it out. Nothing else on the page is
 * coloured, which is what lets those three read as meaning rather than
 * decoration. (There is no red in this palette on purpose — "wrong" is carried
 * by the blue and by the drawn cross, never by alarm.)
 */
.report {
  max-width: 54rem;
  margin-inline: auto;
  font-family: var(--font-ui);
}

/* --- the masthead ------------------------------------------------------- */
.report__head { margin-bottom: 1.4rem; }
.report__eyebrow {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: .55rem;
  margin: 0 0 .35rem;
  font-size: .78rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
/* The code, set the way it was read off the projector: spaced, so the letters
   are individually legible rather than a word. */
.report__code {
  padding: .2rem .5rem;
  background: var(--green-700);
  color: #fff;
  font-weight: 700;
  letter-spacing: .18em;
}
.report__when { font-variant-numeric: tabular-nums; }
.report__head h1 { margin: 0 0 .3rem; line-height: 1.15; }
.report__games {
  margin: 0;
  color: var(--muted);
  font-size: 1rem;
}
.report__then { color: var(--muted-2); font-style: italic; }

/* --- four figures ------------------------------------------------------- *
 *
 * The answer to "how did that go" before a single row is read. Hairlines
 * between rather than boxes around: four cards would be four things to look
 * at, and this is one thing said four ways.
 */
.figures {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin: 0 0 1.75rem;
  padding: .9rem 0;
  border-top: 2px solid var(--ink);
  border-bottom: 1px solid var(--line);
}
.figures__cell {
  padding: 0 1rem;
  border-left: 1px solid var(--line-soft);
}
.figures__cell:first-child { border-left: 0; padding-left: 0; }
.figures__n {
  margin: 0;
  font-size: clamp(1.9rem, 4.6vw, 2.6rem);
  font-weight: 700;
  line-height: 1;
  color: var(--ink-strong);
  font-variant-numeric: tabular-nums;
}
/* The one figure of the four that is good or bad rather than simply true. */
.figures__cell--lead .figures__n { color: var(--green-700); }
.figures__unit { font-size: .55em; font-weight: 650; margin-left: .06em; }
.figures__of {
  display: block;
  font-size: .34em;
  font-weight: 650;
  letter-spacing: .04em;
  color: var(--muted);
}
.figures__label {
  margin: .45rem 0 0;
  font-size: .74rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--muted);
}

/* --- the finding -------------------------------------------------------- *
 *
 * Everything else on this page is a record; this is the thing to do something
 * about on Monday, so it is the only block on the page with a ground of its
 * own.
 */
.finding {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  margin: 0 0 2rem;
  padding: 1rem 1.25rem;
  background: var(--bg-soft);
  border-left: 5px solid var(--green-700);
}
.finding__body { flex: 1 1 auto; min-width: 0; }
.finding__label {
  margin: 0 0 .3rem;
  font-size: .72rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
}
.finding__what {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: .55rem;
  margin: 0;
}
.finding__n { font-size: 1.2rem; font-weight: 700; color: var(--ink-strong); }
.finding__game { color: var(--muted); }
.finding__says { margin: .25rem 0 0; color: var(--ink); font-size: .95rem; }
/* The share, big, because "5 of 12" and "42%" are the same fact and only one
   of them can be taken in from across a desk. */
.finding__share {
  flex: 0 0 auto;
  margin: 0;
  color: var(--green-700);
  font-variant-numeric: tabular-nums;
  line-height: 1;
  white-space: nowrap;
}
.finding__share b { font-size: 2.4rem; font-weight: 700; }
.finding__share span { font-size: 1rem; font-weight: 650; margin-left: .05em; }

/* --- section furniture -------------------------------------------------- */
.report__section { margin-bottom: 2.25rem; }
.report__lead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: .5rem 1.5rem;
}
.report__title {
  font-size: .8rem;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 .7rem;
  padding-bottom: .4rem;
  border-bottom: 2px solid var(--ink);
  flex: 1 1 auto;
}
.report__lead .report__title { border-bottom: 0; padding-bottom: 0; }
.report__lead { border-bottom: 2px solid var(--ink); padding-bottom: .45rem; margin-bottom: .7rem; }

/* --- the marks ---------------------------------------------------------- *
 *
 * Drawn, not typed: a tick and a cross from a font are two drawings by two
 * different hands and never the same weight as each other.
 */
.mark { display: inline-flex; align-items: center; }
.mark__glyph { width: 1.05rem; height: 1.05rem; display: block; }
.mark--right { color: var(--green-700); }
.mark--wrong { color: var(--blue); }
.mark--missed { color: var(--muted-2); }
.markkey {
  display: flex;
  flex-wrap: wrap;
  gap: .2rem 1.1rem;
  margin: 0;
  font-size: .78rem;
  letter-spacing: .04em;
  color: var(--muted);
}
.markkey__item { display: inline-flex; align-items: center; gap: .3rem; }

/* --- question by question ----------------------------------------------- *
 *
 * The share who got it, drawn, because "9/12" and "5/12" are two numbers and
 * one of them is a lesson.
 */
.qlist { list-style: none; margin: 0; padding: 0; }
/* The game's name once per run, as a heading over its questions. */
.qlist__group {
  padding: 1rem 0 .35rem .5rem;
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--ink-strong);
}
.qlist__group:first-child { padding-top: .35rem; }
.qlist__row {
  display: grid;
  grid-template-columns: 2.2rem minmax(8rem, 1fr) 4.2rem 3.4rem;
  align-items: center;
  gap: .9rem;
  padding: .55rem 0 .55rem .5rem;
  border-bottom: 1px solid var(--line-soft);
  border-left: 3px solid transparent;
}
.qlist__row:hover { background: var(--bg-soft); }
/* The row the finding points at, marked where it lives rather than only in
   the sentence above. */
.qlist__row.is-hardest {
  border-left-color: var(--green-700);
  background: var(--bg-soft);
}
.qlist__n {
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  font-size: .85rem;
  text-align: right;
}
.qlist__bar {
  display: flex;
  height: .85rem;
  background: var(--line-soft);
  overflow: hidden;
}
.qlist__count {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--ink-strong);
}
.qlist__of { font-weight: 400; color: var(--muted); }
.qlist__share {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--green-700);
  font-weight: 650;
}
.qlist__none { padding: .8rem 0; color: var(--muted); }

/* --- everybody ---------------------------------------------------------- */
/* Wide content scrolls inside its own box rather than pushing the page
   sideways — the one thing a table on a phone must never do. */
.report__scroll { overflow-x: auto; }
.report__table {
  width: 100%;
  border-collapse: collapse;
  font-size: .95rem;
}
.report__table th,
.report__table td {
  padding: .55rem .6rem;
  border-bottom: 1px solid var(--line-soft);
  text-align: left;
}
.report__table thead th {
  font-size: .74rem;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
  vertical-align: bottom;
}
.report__table .num { text-align: right; font-variant-numeric: tabular-nums; }
.report__table .markcol { padding-bottom: .4rem; }
.report__table .markcol .mark { justify-content: flex-end; width: 100%; }
.report__table .who { font-weight: 650; color: var(--ink-strong); }
.report__table .strong { font-weight: 700; color: var(--green-700); }
.report__table .quiet { color: var(--muted); }
/* A zero is a fact, not a number to read. Kept in the column so the rows
   stay a grid, and dimmed so the eye goes to the ones that are not zero. */
.report__table .zero { color: var(--line); }
.report__table tbody tr:hover { background: var(--bg-soft); }

.report__table .rank { width: 2.9rem; padding-right: 0; }
.rank__n {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.7rem;
  height: 1.7rem;
  font-variant-numeric: tabular-nums;
  font-size: .85rem;
  color: var(--muted);
}
/* A place, not a medal: a host reads three names out, so three rows carry a
   filled square and nothing below them is decorated at all. */
.rank--top .rank__n {
  background: var(--green-700);
  color: #fff;
  font-weight: 700;
}

.report__table .shapehead { width: 22%; }
.report__table .shape { width: 22%; min-width: 6rem; }
.shape__bar {
  display: flex;
  height: .6rem;
  background: var(--line-soft);
  overflow: hidden;
}

/* --- the foot ----------------------------------------------------------- */
.report__foot {
  margin-top: 2rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--line);
}
.report__actions { display: flex; flex-wrap: wrap; gap: .7rem; }
.report__note {
  margin: 1rem 0 0;
  max-width: 34rem;
  color: var(--muted-2);
  font-size: .85rem;
  line-height: 1.5;
}

@media (max-width: 48rem) {
  .figures { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .9rem 0; }
  .figures__cell:nth-child(odd) { border-left: 0; padding-left: 0; }
  .finding { flex-wrap: wrap; }
  .finding__share b { font-size: 1.9rem; }
}
@media (max-width: 40rem) {
  .qlist__row {
    grid-template-columns: 1.8rem minmax(0, 1fr) 3.4rem;
    row-gap: .45rem;
    gap: .6rem;
  }
  /* The bar drops to its own line rather than being squeezed to a stub, where
     three parts of it would be too narrow to tell apart. */
  .qlist__bar { grid-column: 1 / -1; grid-row: 2; }
  .qlist__count { grid-column: 2 / 3; text-align: left; }
}

/* Printed and saved as a PDF at least as often as it is read on a screen, so
   the page furniture goes and the marks are made to survive a printer that
   drops background colours: the bars get a border, and the three parts stay
   tellable apart by more than hue. */
@media print {
  .topbar, .rail-col, footer, .report__foot { display: none !important; }
  .report { max-width: none; }
  .report__table tbody tr:hover { background: none; }
  .qlist__row:hover { background: none; }
  .report__section { break-inside: avoid; }
  .qlist__row, .report__table tr { break-inside: avoid; }
  .finding, .figures, .report__code, .rank--top .rank__n,
  .tally__part { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  .qlist__bar, .shape__bar { border: 1px solid var(--line); }
}

/* ---------------------------------------------------- setting one up ----
 *
 * The only page in this feature a teacher meets before a class is watching,
 * and the only one where a mistake is cheap. Built as a workbench: the games
 * are the posters the arcade already shows, the running order is a list you
 * can see, and the panel beside it says what you have made.
 */

.setup { max-width: 66rem; margin-inline: auto; }

.setup__head { margin-bottom: 1.5rem; }
.setup__head h1 { margin-bottom: .3rem; }
.setup__head .lede { max-width: 46rem; }

.setup__grid {
  display: grid;
  gap: 1.25rem;
  align-items: start;
  grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 60rem) {
  .setup__grid { grid-template-columns: minmax(0, 1fr) 19rem; }
}
.setup__main { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }

/* --- the three steps --- */

.card {
  border: 1px solid var(--line);
  background: var(--bg);
  padding: 1.1rem 1.2rem 1.3rem;
}
.card__title {
  display: flex;
  align-items: center;
  gap: .6rem;
  margin: 0 0 .3rem;
  font-size: 1.15rem;
}
/* Numbered because these genuinely are a sequence — you cannot set the clock
   on a challenge that has no games in it yet. */
.card__step {
  display: inline-grid;
  place-items: center;
  width: 1.6rem; height: 1.6rem;
  background: var(--green-700); color: #fff;
  font-family: var(--font-ui); font-size: .85rem; font-weight: 700;
}
.card__note { margin: 0 0 .9rem; color: var(--muted); font-size: .92rem; }

/* --- picking games --- */

.picker {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: .8rem;
  grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
}
/* The poster on top and the name under it, which is how the arcade shows
   these and therefore how a teacher already knows them. Cropped small and set
   beside the words, a screenshot of a whole game board is a grey smudge. */
.picker__card {
  position: relative;
  display: flex;
  flex-direction: column;
  width: 100%;
  padding: 0 0 .7rem;
  text-align: left;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  overflow: hidden;
  transition: border-color .12s ease, box-shadow .12s ease;
}
.picker__card:hover { border-color: var(--green-700); box-shadow: 0 2px 0 var(--green-700); }
.picker__card:focus-visible { outline: 3px solid var(--green-700); outline-offset: 2px; }
.picker__card.is-chosen { border-color: var(--green-700); box-shadow: inset 0 0 0 1px var(--green-700); }
.picker__shot {
  display: block; width: 100%; aspect-ratio: 16 / 10;
  background: var(--bg-soft); border-bottom: 1px solid var(--line-soft);
  overflow: hidden;
}
.picker__shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.picker__body { padding: .55rem .7rem 0; min-width: 0; }
.picker__name {
  display: block; font-family: var(--font-ui); font-weight: 700;
  font-size: .95rem; line-height: 1.25; color: var(--ink-strong);
}
.picker__hook {
  display: block; margin-top: .15rem;
  font-size: .8rem; line-height: 1.35; color: var(--muted);
  /* Two lines, then stop: the hook is a reminder, not the description. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
/* The affordance, in the corner where a badge goes — and the same shape once
   it is chosen, so the card changes state rather than growing a new part. */
.picker__add {
  position: absolute; top: .4rem; right: .4rem;
  width: 1.7rem; height: 1.7rem;
  display: grid; place-items: center;
  background: var(--bg); border: 1px solid var(--line);
  font-family: var(--font-ui); font-size: 1.1rem; font-weight: 700;
  color: var(--green-700); line-height: 1;
}
.picker__card:hover .picker__add { border-color: var(--green-700); }
.picker__card.is-chosen .picker__add {
  background: var(--green-700); border-color: var(--green-700); color: #fff;
}

/* --- the running order --- */

.order { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.order__row {
  padding: .5rem .6rem .6rem;
  background: var(--bg-soft);
}
/* The line that says which game and how many questions. */
.order__head {
  display: grid;
  grid-template-columns: 1.7rem minmax(0, 1fr) auto auto;
  align-items: center;
  gap: .7rem;
}

/* --- fine-tuning one game --------------------------------------------- *
 *
 * Under the game it belongs to, not behind a disclosure. A teacher setting a
 * room up is deciding what to test — which tables, adding or taking away, how
 * hard, how long they get — and putting that behind a chevron would say it is
 * an edge case when it is the actual question they came to answer.
 *
 * Every control is the same control: a question, and a row of answers with
 * one of them on. Real radios, hidden but present, so the arrow keys move
 * through a group and a screen reader says what is being chosen between.
 */
.order__tune {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.4rem;
  margin: .55rem 0 0 2.4rem;
  padding-top: .55rem;
  border-top: 1px solid var(--line);
}
.tune { border: 0; margin: 0; padding: 0; min-width: 0; }
.tune__label {
  padding: 0;
  font-family: var(--font-ui);
  font-size: .7rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--muted);
}
.tune__opts { display: flex; flex-wrap: wrap; gap: .3rem; margin-top: .25rem; }
/* Hidden from the eye, not from the keyboard: focus has to be able to land
   here or the row becomes mouse-only. */
.tune__radio {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.tune__opt {
  display: inline-block;
  padding: .22rem .55rem;
  background: var(--bg);
  border: 1px solid var(--line);
  font-family: var(--font-ui);
  font-size: .82rem;
  line-height: 1.3;
  color: var(--ink);
  cursor: pointer;
}
.tune__opt:hover { border-color: var(--green-700); color: var(--green-700); }
.tune__radio:checked + .tune__opt {
  background: var(--green-700);
  border-color: var(--green-700);
  color: #fff;
  font-weight: 650;
}
.tune__radio:focus-visible + .tune__opt {
  outline: 2px solid var(--blue);
  outline-offset: 2px;
}

/* The teacher who wants thirty-eight. Sits on the end of the presets rather
   than under them, because it is one more way of answering the same question
   and not a second question. */
.tune__own { display: inline-flex; align-items: center; gap: .35rem; }
.tune__or {
  font-family: var(--font-ui);
  font-size: .78rem;
  color: var(--muted);
}
/* `input.tune__field` rather than `.tune__field`, on purpose. The site
   dresses every text input the same way — full width, generous padding —
   which is right for a sign-in form and would make this three times the
   height of the chips it sits beside. A type selector is what that rule uses,
   so a class alone loses to it however far down the file it sits.

   The focus ring is deliberately not overridden: it is the site's yellow one,
   the same as every other field a teacher meets. */
input.tune__field {
  width: 4.4rem;
  padding: .22rem .4rem;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 0;
  font-family: var(--font-ui);
  font-size: .82rem;
  line-height: 1.3;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 34rem) {
  .order__tune { margin-left: 0; }
}
.order__n {
  display: grid; place-items: center;
  width: 1.7rem; height: 1.7rem;
  background: var(--bg); border: 1px solid var(--line);
  font-family: var(--font-ui); font-weight: 700; font-size: .85rem;
  font-variant-numeric: tabular-nums;
}
.order__name {
  font-family: var(--font-ui); font-weight: 650;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.order__count { display: flex; align-items: center; gap: .35rem; font-family: var(--font-ui); }
.order__count b { min-width: 1.4ch; text-align: center; font-variant-numeric: tabular-nums; }
.order__word { color: var(--muted); font-size: .82rem; }
.order__step,
.order__moves button {
  width: 1.7rem; height: 1.7rem;
  display: grid; place-items: center;
  background: var(--bg); border: 1px solid var(--line);
  font: inherit; line-height: 1; cursor: pointer; color: var(--ink);
}
.order__step:hover,
.order__moves button:hover { border-color: var(--green-700); color: var(--green-700); }
.order__moves { display: flex; gap: .25rem; }

/* --- the clocks --- */

.field__label {
  margin: 1.1rem 0 .45rem;
  font-family: var(--font-ui); font-weight: 700; font-size: .9rem;
}
.card > .field__label:first-of-type { margin-top: 0; }
.ticks { display: flex; flex-wrap: wrap; gap: .35rem; }
.ticks button {
  min-width: 3rem; min-height: 2.3rem; padding: .3rem .7rem;
  background: var(--bg); border: 1px solid var(--line);
  font-family: var(--font-ui); font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--ink); cursor: pointer;
}
.ticks button:hover { border-color: var(--green-700); }
.ticks button.is-on { background: var(--green-700); border-color: var(--green-700); color: #fff; }
.ticks button:focus-visible { outline: 3px solid var(--green-700); outline-offset: 2px; }
/* The way out for the teacher who wants 25. Deliberately quiet: it is the
   escape hatch, not the control.

   Selector weight on purpose: form inputs on this site are full width, which
   is right for a name and absurd for a two-digit number. */
.card input.ticks__own {
  display: block;
  width: 5.5rem; margin-top: .45rem;
  font-variant-numeric: tabular-nums;
}

/* Two ways to run the same room, told apart by what they are for rather than
   by their names. The chosen one is a filled card, not a dot. */
.pacing {
  display: grid; gap: .6rem;
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  margin: .4rem 0 0;
}
.pacing label {
  display: block; padding: .8rem .9rem;
  border: 2px solid var(--line); cursor: pointer; background: var(--bg);
}
.pacing label:has(input:checked) { border-color: var(--green-700); background: var(--green-100); }
.pacing label:has(input:focus-visible) { outline: 3px solid var(--green-700); outline-offset: 2px; }
.pacing input { margin-right: .45rem; }
.pacing b { font-family: var(--font-ui); font-weight: 700; }
.pacing span { display: block; margin-top: .25rem; font-size: .85rem; color: var(--muted); }

/* --- what you have made --- */

.summary {
  border: 1px solid var(--line);
  border-top: 4px solid var(--green-700);
  background: var(--bg);
  padding: 1.1rem 1.2rem 1.2rem;
}
@media (min-width: 60rem) {
  .summary { position: sticky; top: calc(var(--topbar-height) + 1rem); }
}
.summary__title {
  margin: 0 0 .8rem; font-size: .8rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--muted); font-family: var(--font-ui);
}
.summary .field { display: block; font-family: var(--font-ui); font-weight: 650; font-size: .9rem; }
.summary input { width: 100%; margin: .35rem 0 1rem; }
.summary__facts { margin: 0 0 .9rem; display: flex; flex-direction: column; gap: .5rem; }
.summary__facts > div {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: .75rem; padding-bottom: .45rem; border-bottom: 1px solid var(--line-soft);
}
.summary__facts dt { color: var(--muted); font-size: .9rem; }
.summary__facts dd {
  margin: 0; font-family: var(--font-ui); font-weight: 700;
  font-variant-numeric: tabular-nums; text-align: right;
}
.summary__pacing { margin: 0 0 1rem; font-size: .85rem; color: var(--muted); }
.summary .btn[disabled] { opacity: .45; cursor: not-allowed; }

/* --- rooms opened before --- */

.rooms { margin-top: 1.5rem; }
.rooms__list { list-style: none; margin: 0; padding: 0; }
.rooms__list li {
  display: flex; align-items: baseline; flex-wrap: wrap; gap: .7rem;
  padding: .6rem 0; border-bottom: 1px solid var(--line-soft);
}
.rooms__code {
  font-family: var(--font-ui); font-weight: 700; letter-spacing: .08em;
  font-size: 1.05rem; color: var(--green-700); text-decoration: none;
}
.rooms__code:hover { text-decoration: underline; }
.rooms__name { font-weight: 650; }
.rooms__meta { color: var(--muted); font-size: .85rem; margin-left: auto; }
.chip {
  font-family: var(--font-ui); font-size: .72rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  padding: .15rem .5rem; background: var(--bg-soft); color: var(--muted);
}
.chip--running { background: var(--green-700); color: #fff; }
.chip--lobby { background: var(--yellow); color: var(--green-900); }

/* The plain fallback, for a browser with no JavaScript. */
.hostrow {
  display: grid; grid-template-columns: 1fr 6rem; gap: .6rem; margin-bottom: .5rem;
}
.hostrow select, .hostrow input { min-height: 2.6rem; }

/* --- on a phone ----------------------------------------------------------
 *
 * A teacher sets a room up on whatever is in their hand, which is often the
 * same phone they are about to read the code off. Nothing here is a different
 * page for small screens; it is the same one with the parts that only work
 * side by side allowed to stack.
 */
@media (max-width: 40rem) {
  .setup__head h1 { font-size: 1.6rem; }
  .card { padding: .9rem .8rem 1rem; }
  .card__title { font-size: 1.05rem; }

  /* Two across rather than one: eight games in a single column is a lot of
     scrolling to see what there is. */
  .picker { grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); gap: .5rem; }
  .picker__name { font-size: .88rem; }
  .picker__hook { display: none; }

  /* The row breaks in two — the game on one line, its count and controls on
     the next — rather than squeezing four columns into 320px.

     Flex rather than a re-declared grid: wrapping is the browser's decision
     from the widths themselves, so there is no set of column lines to keep in
     step and nothing to overlap when a game's name runs long. */
  .order__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    row-gap: .5rem;
  }
  .order__name { flex: 1 1 8rem; }
  .order__count { flex: 1 1 auto; }
  .order__moves { margin-left: auto; }

  .ticks button { min-width: 2.6rem; padding: .3rem .5rem; }
  .pacing { grid-template-columns: minmax(0, 1fr); }

  /* The panel goes to the bottom of the flow on a phone, so it must not try
     to stick to anything. */
  .summary { position: static; }
  .rooms__list li { gap: .4rem; }
  .rooms__meta { margin-left: 0; width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  .play__bar, .host__bar { transition: none; }
  /* The board still reorders; it simply does not travel to get there. */
  .board li, .tally__part { transition: none; }
}
