/* ---------------------------------------------------------------------------
 * ZoodleSwap - the approved look, held to the two rules that matter on a
 * classroom device: the puzzle and the bank own the screen, and everything a
 * child has to hit is big enough to hit while standing up.
 *
 * Colours are the ones signed off on the mockup: navy ground, teal panels,
 * bright cyan squares, cream-to-gold tiles with dark letters.
 * ------------------------------------------------------------------------- */

:root {
  --navy: #06172a;
  --navy-deep: #020c17;
  --panel: #0a2e43;
  --panel-soft: #0e3b55;
  --cyan: #27cfe4;
  --cyan-dim: #1b8ea0;
  --gold: #ffc247;
  --gold-deep: #e0982a;
  --tile-top: #fffdf0;
  --tile-bottom: #ffe5a8;
  --ink: #16222e;
  --paper: #eaf6fb;
  --muted: #8fb2c4;
  --good: #57d99a;
  --bad: #ff8b7a;

  /* The tile size, and with it the whole game's height.
   *
   * This used to look at viewport WIDTH only, which is why the game ran off
   * the bottom of a school Chromebook: at 1024x600 there is plenty of width,
   * so the tiles stayed at full size and the Submit and Swap buttons sat 173
   * pixels below the fold. A child cannot press a button they cannot see, and
   * they will not think to scroll.
   *
   * The board is four rows and the bank is two, so six tile-heights stack up
   * vertically; 350px covers everything else on the screen INCLUDING the
   * mid-trade panel, which is the tallest state and the one a child is in
   * when they most need the drop spot visible.
   *
   * Measured, not guessed: 0px overflow in that tallest state at 1024x600,
   * 1366x768 and 768x1024. */
  --tile: clamp(34px, min(12vw, (100dvh - 350px) / 6), 62px);
  --radius: 14px;
}

* { box-sizing: border-box; }

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

body {
  background:
    radial-gradient(120% 80% at 50% 0%, #0b2135 0%, var(--navy) 55%, var(--navy-deep) 100%);
  color: var(--paper);
  font-family: Fredoka, system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-weight: 500;
  -webkit-text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}

.screen {
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 16px;
  gap: 14px;
}

[hidden] { display: none !important; }

/* --- shared controls ------------------------------------------------------ */

.big-btn, .check-btn, .ghost-btn, .swap-btn, .icon-btn, .link-btn {
  font-family: inherit;
  font-weight: 600;
  border: 0;
  cursor: pointer;
  border-radius: var(--radius);
  transition: transform .08s ease, filter .15s ease;
}

.big-btn:active, .check-btn:active, .ghost-btn:active, .swap-btn:active { transform: translateY(1px); }

.big-btn, .check-btn {
  background: linear-gradient(180deg, var(--gold) 0%, var(--gold-deep) 100%);
  color: #331f00;
  font-size: 1.15rem;
  padding: 14px 26px;
  min-height: 52px;
  box-shadow: 0 4px 0 #a86b12;
}

.big-btn:disabled, .check-btn:disabled { filter: saturate(.4) brightness(.8); cursor: default; }

.ghost-btn {
  background: transparent;
  color: var(--paper);
  border: 2px solid var(--cyan-dim);
  padding: 11px 20px;
  min-height: 48px;
  font-size: 1rem;
}

.ghost-btn.small { padding: 8px 14px; min-height: 40px; font-size: .9rem; }
.ghost-btn.quiet { border-color: #1d3a4d; color: var(--muted); margin-top: auto; }

.link-btn {
  background: none;
  color: var(--cyan);
  text-decoration: underline;
  padding: 6px;
  font-size: .9rem;
}

.icon-btn {
  background: var(--panel-soft);
  color: var(--cyan);
  border: 2px solid #17506d;
  width: 48px;
  height: 48px;
  font-size: 1.3rem;
  flex: none;
}

/* --- joining -------------------------------------------------------------- */

.join { justify-content: center; }

.join-logo {
  width: min(320px, 74vw);
  height: auto;
  margin-bottom: 4px;
}

.join-card {
  width: min(420px, 100%);
  background: var(--panel);
  border: 1px solid #14465f;
  border-radius: 20px;
  padding: 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  box-shadow: 0 18px 40px rgba(0, 0, 0, .35);
}

.field { display: flex; flex-direction: column; gap: 6px; }
.field-label { font-size: .85rem; color: var(--cyan); text-transform: uppercase; letter-spacing: .08em; }
.field-note { font-size: .78rem; color: var(--muted); }

.code-input, .name-input {
  font-family: inherit;
  font-size: 1.2rem;
  padding: 14px;
  min-height: 54px;
  border-radius: 12px;
  border: 2px solid #1d5a78;
  background: #061e2e;
  color: var(--paper);
}

.code-input {
  font-size: 1.9rem;
  letter-spacing: .28em;
  text-align: center;
  text-transform: uppercase;
}

.code-input:focus, .name-input:focus { outline: 3px solid var(--cyan); outline-offset: 1px; }

.join-msg { margin: 0; min-height: 1.2em; color: var(--gold); font-size: .95rem; text-align: center; }
.join-hint { margin: 0; text-align: center; color: var(--muted); font-size: .85rem; }

/* --- waiting -------------------------------------------------------------- */

.wait { justify-content: center; }

.wait-card {
  text-align: center;
  background: var(--panel);
  border-radius: 24px;
  padding: 36px 28px;
  width: min(420px, 100%);
  border: 1px solid #14465f;
}

.wait-logo { width: min(260px, 64vw); margin-bottom: 14px; }
.wait-mark { font-size: 3rem; display: block; }
.wait-kicker { margin: 10px 0 4px; font-size: 2rem; color: var(--cyan); letter-spacing: .04em; }
.wait-name { margin: 0; font-size: 1.3rem; font-weight: 600; }
.wait-msg { color: var(--muted); }
.wait-level { color: var(--gold); font-size: .9rem; margin: 0; }

/* --- playing -------------------------------------------------------------- */

.play { justify-content: flex-start; gap: 10px; padding: 10px 12px 18px; }

.play-head {
  width: min(560px, 100%);
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(10, 46, 67, .7);
  border-radius: 999px;
  padding: 7px 16px;
  font-size: .95rem;
}

.who { font-weight: 600; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.stat { color: var(--gold); font-weight: 600; }
.stat small { color: var(--muted); font-weight: 400; font-size: .72rem; }
.clock { font-variant-numeric: tabular-nums; color: var(--cyan); }
.clock.low { color: var(--bad); }

.board-wrap { display: flex; justify-content: center; width: 100%; }

/* Four words, one per row, shortest first. Left-aligned rather than centred
 * so the growing line lengths read as a ladder - the six-letter word is
 * visibly the big one, which is the point of it. */
.board {
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: flex-start;
}

.slot {
  display: flex;
  gap: 6px;
}

.cell {
  width: var(--tile);
  height: var(--tile);
  border-radius: 10px;
  border: 3px solid var(--cyan);
  background: rgba(39, 207, 228, .07);
  color: var(--ink);
  font-family: inherit;
  font-size: calc(var(--tile) * .5);
  font-weight: 600;
  cursor: pointer;
  padding: 0;
}

.cell.filled {
  background: linear-gradient(180deg, var(--tile-top) 0%, var(--tile-bottom) 100%);
  border-color: var(--gold-deep);
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, .12);
}

.board-msg {
  margin: 0;
  min-height: 1.4em;
  text-align: center;
  color: var(--gold);
  font-size: .98rem;
}

.board-msg.good { color: var(--good); font-weight: 600; }

.bank-wrap {
  width: min(560px, 100%);
  display: flex;
  align-items: center;
  gap: 10px;
  background: rgba(10, 46, 67, .55);
  border-radius: 18px;
  padding: 10px;
}

.bank {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
  justify-content: center;
  min-height: var(--tile);
}

.tile {
  width: var(--tile);
  height: var(--tile);
  border-radius: 10px;
  border: 0;
  background: linear-gradient(180deg, var(--tile-top) 0%, var(--tile-bottom) 100%);
  box-shadow: 0 3px 0 #c99a3c;
  color: var(--ink);
  font-family: inherit;
  font-size: calc(var(--tile) * .5);
  font-weight: 600;
  cursor: pointer;
  padding: 0;
}

.tile.picked {
  outline: 4px solid var(--cyan);
  outline-offset: 2px;
  transform: translateY(-4px);
}

.actions { display: flex; gap: 12px; align-items: center; }

/* --- swapping ------------------------------------------------------------- */

.swap {
  width: min(560px, 100%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  margin-top: 2px;
}

.swap-btn {
  background: linear-gradient(180deg, var(--cyan) 0%, var(--cyan-dim) 100%);
  color: #032430;
  font-size: 1.2rem;
  padding: 14px 44px;
  min-height: 54px;
  box-shadow: 0 4px 0 #11626f;
  letter-spacing: .03em;
}

.swap-searching {
  display: flex;
  align-items: center;
  gap: 10px;
  color: var(--cyan);
  font-size: 1.05rem;
}

.spinner {
  width: 18px;
  height: 18px;
  border: 3px solid rgba(39, 207, 228, .25);
  border-top-color: var(--cyan);
  border-radius: 50%;
  animation: spin .9s linear infinite;
}

@keyframes spin { to { transform: rotate(360deg); } }

.swap-matched {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 10px;
  background: var(--panel);
  border: 2px solid var(--cyan-dim);
  border-radius: 18px;
  padding: 14px 20px;
  width: min(360px, 100%);
}

.partner { margin: 0; font-size: 1.05rem; }
.partner b { color: var(--cyan); font-size: 1.2rem; }

/* One tile-sized square. Not a panel, not a sidebar, not a view of anyone
 * else's letters - just the place the given-away tile goes. */
.drop-spot {
  width: var(--tile);
  height: var(--tile);
  border-radius: 10px;
  border: 3px dashed var(--gold);
  background: rgba(255, 194, 71, .08);
  display: grid;
  place-items: center;
  cursor: pointer;
  color: var(--muted);
  font-size: .62rem;
  text-align: center;
  padding: 2px;
}

.drop-spot.full {
  border-style: solid;
  background: linear-gradient(180deg, var(--tile-top) 0%, var(--tile-bottom) 100%);
  color: var(--ink);
  font-size: calc(var(--tile) * .5);
  font-weight: 600;
}

.swap-actions { display: flex; gap: 8px; }
.swap-msg { margin: 0; min-height: 1.1em; font-size: .85rem; color: var(--muted); }

/* --- the end -------------------------------------------------------------- */

.done { justify-content: center; }

.done-card {
  background: var(--panel);
  border-radius: 24px;
  padding: 30px 26px;
  width: min(440px, 100%);
  text-align: center;
  border: 1px solid #14465f;
}

.done-card h1 { margin: 0; font-size: 2.4rem; color: var(--gold); }
.done-score { font-size: 1.15rem; }
.done-score b { color: var(--cyan); font-size: 1.7rem; }

.done-board { list-style: none; margin: 18px 0; padding: 0; text-align: left; }

.done-board li {
  display: flex;
  justify-content: space-between;
  padding: 9px 14px;
  border-radius: 10px;
  counter-increment: place;
}

.done-board li:nth-child(odd) { background: rgba(255, 255, 255, .04); }
.done-board li.me { background: rgba(39, 207, 228, .16); color: var(--cyan); font-weight: 600; }
.done-board b { color: var(--gold); }

/* --- bigger screens -------------------------------------------------------
 * A tablet or a Chromebook gets bigger squares rather than more furniture.
 */
/* Short screens give up their spare padding before the tiles shrink further.
 * A classroom Chromebook is 1024x600 and a laptop is 1366x768; both land
 * here, phones in portrait mostly do not. */
@media (max-height: 820px) {
  .play { gap: 6px; padding: 6px 12px 10px; }
  .board { gap: 5px; }
  .slot { gap: 4px; }
  .board-msg { min-height: 1.1em; font-size: .88rem; }
  .play-head { padding: 5px 14px; font-size: .88rem; }
  .bank-wrap { padding: 6px 8px; }
}

/* A wide screen may have big tiles - but only if it is also TALL enough for
 * them. This block used to set a flat 74px on anything wider than 700px,
 * which is every Chromebook, every iPad and every laptop, and it silently
 * overrode the height-aware size above. That single line is what pushed the
 * buttons off the bottom of the screen. */
@media (min-width: 700px) and (min-height: 860px) {
  :root { --tile: 74px; }
  .play { gap: 16px; padding-top: 18px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

.teacher-link { margin: 4px 0 0; text-align: center; }
.teacher-link a { color: var(--muted); font-size: .85rem; text-decoration: underline; }

/* The artwork is a proper cutout - it was only ever boxed in because an
 * earlier resize of mine flattened the alpha channel onto black. Nothing to
 * hide now, so it sits straight on the navy. */
.join-logo { filter: drop-shadow(0 10px 22px rgba(0, 0, 0, .45)); }

/* --- the ten seconds before the letters change ----------------------------
 * Covers the whole screen on purpose. This is the one moment in the game
 * where nobody should be mid-thought about their own puzzle: the point is
 * that thirty children look up at the same time.
 */
.boom {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  background: rgba(2, 12, 23, .93);
  text-align: center;
  padding: 20px;
}

.boom-what {
  margin: 0;
  color: var(--cyan);
  font-size: clamp(1.1rem, 4vw, 2rem);
  letter-spacing: .04em;
}

.boom-count {
  margin: 0;
  font-size: clamp(6rem, 34vw, 20rem);
  line-height: .95;
  font-weight: 700;
  color: var(--gold);
  font-variant-numeric: tabular-nums;
}

/* The last three seconds. */
.boom.urgent .boom-count {
  color: var(--bad);
  animation: boom-pulse .9s ease-out infinite;
}

@keyframes boom-pulse {
  0%   { transform: scale(1);    opacity: 1; }
  60%  { transform: scale(1.14); opacity: .85; }
  100% { transform: scale(1);    opacity: 1; }
}

/* Zero. */
.boom.boom-go {
  animation: boom-flash .7s ease-out;
}

.boom.boom-go .boom-count { color: var(--paper); }
.boom.boom-go .boom-what { color: var(--gold); }

@keyframes boom-flash {
  0%   { background: rgba(255, 244, 214, .97); }
  22%  { background: rgba(255, 168, 60, .9); }
  100% { background: rgba(2, 12, 23, .93); }
}

.boom #boomCancel { margin-top: 14px; }

/* A child who asked for less motion still gets the countdown, just calmly. */
@media (prefers-reduced-motion: reduce) {
  .boom.urgent .boom-count,
  .boom.boom-go { animation: none; }
  .boom.boom-go { background: rgba(255, 168, 60, .28); }
}

/* --- the value in the corner ----------------------------------------------
 * Scrabble's own numbers, and Scrabble's own placement: small, bottom-right,
 * out of the way of the letter. Big enough to read on a Chromebook held at
 * arm's length, small enough that the letter is still what you see first.
 */
.tile, .cell.filled, .drop-spot.full {
  position: relative;
}

.tile-letter { line-height: 1; }

.tile-value {
  position: absolute;
  right: 5px;
  bottom: 2px;
  font-size: calc(var(--tile) * .22);
  font-weight: 600;
  line-height: 1;
  color: #8a6b2e;
  pointer-events: none;
}

/* On the board the tile sits on a gold ground, so the number needs a touch
 * more contrast to stay legible against it. */
.cell.filled .tile-value { color: #7a5a1e; }

@media (min-width: 700px) {
  .tile-value { right: 6px; bottom: 3px; }
}

/* --- the multiplier square ------------------------------------------------
 * Marked before anything is on it, so a child can see where the expensive
 * letter wants to go while they are still deciding. Steering a J onto the
 * four-times square is the decision this scoring exists to create, and it
 * cannot be made if the square only reveals itself afterwards.
 *
 * The colour rises with the prize: cyan for double, violet for triple, gold
 * for the quadruple that only the six-letter word has.
 */
.cell.bonus {
  border-style: dashed;
  border-width: 3px;
}

.cell.bonus[data-factor="2"] { border-color: var(--cyan); background: rgba(39, 207, 228, .12); }

.cell.bonus[data-factor="3"] { border-color: var(--gold); background: rgba(255, 194, 71, .18); }

/* The seven-letter row's quadruple squares. Hot pink, because it has to read
 * as "better than gold" at a glance from a child's seat, and it is the one
 * colour on this screen not already used for something else. */
.cell.bonus[data-factor="4"] { border-color: #FF4FA3; background: rgba(255, 79, 163, .18); }

.bonus-mark {
  font-size: calc(var(--tile) * .34);
  font-weight: 700;
  line-height: 1;
  opacity: .95;
}

.cell.bonus[data-factor="2"] .bonus-mark { color: var(--cyan); }
.cell.bonus[data-factor="3"] .bonus-mark { color: var(--gold); }
.cell.bonus[data-factor="4"] .bonus-mark { color: #FF4FA3; }


/* Once a letter lands on it the square keeps its colour in the border, so the
 * child can still see the word is carrying a bonus while they decide whether
 * to submit. */
.cell.bonus.filled {
  border-style: solid;
  box-shadow: inset 0 -3px 0 rgba(0, 0, 0, .12), 0 0 0 2px rgba(255, 255, 255, .12);
}

/* The background has to be restated here, not just the border.
 *
 * The empty-square rules above are `.cell.bonus[data-factor="3"]`, which
 * outranks plain `.cell.filled` - so a letter landing on a bonus square kept
 * the dark tinted background and was drawn in the tile's near-black ink on
 * top of it. Unreadable, and worst on exactly the squares a child most wants
 * to check before they submit. */
.cell.bonus.filled[data-factor="2"],
.cell.bonus.filled[data-factor="3"],
.cell.bonus.filled[data-factor="4"] {
  background: linear-gradient(180deg, var(--tile-top) 0%, var(--tile-bottom) 100%);
  color: var(--ink);
}

.cell.bonus.filled[data-factor="2"] { border-color: var(--cyan); }
.cell.bonus.filled[data-factor="3"] { border-color: var(--gold); }
.cell.bonus.filled[data-factor="4"] { border-color: #FF4FA3; }


/* --- what this word is worth, so far --------------------------------------
 * Sits at the end of its row and climbs as letters go down. It is the reason
 * a child trades for a better letter rather than only for a missing one -
 * and, since the tiles carry their values, a sum they can check themselves.
 */
.slot-score {
  align-self: center;
  min-width: 2.2em;
  margin-left: 6px;
  font-size: calc(var(--tile) * .34);
  font-weight: 700;
  line-height: 1;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  transition: color .15s ease, transform .15s ease;
}

.slot-score.empty { opacity: 0; }

/* A finished word turns gold - the cue that this one is ready to submit. */
.slot-score.ready {
  color: var(--gold);
  transform: scale(1.12);
}

/* The Swap button while a trade is owed. It should pull the eye without
 * shouting - the child is not in trouble, they just have somewhere to be. */
.swap-btn.urgent {
  animation: swap-nudge 1.6s ease-in-out infinite;
  box-shadow: 0 4px 0 #11626f, 0 0 0 4px rgba(39, 207, 228, .25);
}

@keyframes swap-nudge {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-4px); }
}

@media (prefers-reduced-motion: reduce) {
  .swap-btn.urgent { animation: none; }
}

/* The child's team, worn on the play screen so they never have to ask. */
.team-chip {
  padding: 2px 10px;
  border-radius: 999px;
  border: 2px solid var(--team, var(--cyan));
  color: var(--team, var(--cyan));
  font-size: .78rem;
  font-weight: 700;
  white-space: nowrap;
}


/* ===========================================================================
 * Handoff, September 2026: back link, word tray, row lengths, and the
 * non-blocking Letter Swap banner. Scoped to new class names so the teacher
 * screen (which also loads this file) is untouched.
 * ========================================================================= */

/* --- a visible way out of the join screen --------------------------------- */
.back-link {
  align-self: flex-start;
  color: var(--cyan);
  font-size: 1rem;
  font-weight: 600;
  text-decoration: none;
  padding: 8px 14px;
  border: 2px solid #17506d;
  border-radius: 999px;
  background: var(--panel-soft);
}
.back-link:hover, .back-link:focus-visible { border-color: var(--cyan); }

/* --- the word tray ---------------------------------------------------------- */
.tray-wrap {
  width: min(560px, 100%);
  display: flex;
  align-items: center;
  gap: 8px;
  border: 2px dashed #17506d;
  border-radius: 18px;
  padding: 6px 8px;
  min-height: calc(var(--tile) + 16px);
}
.tray-wrap.has-word { border-style: solid; border-color: var(--cyan); background: rgba(39, 207, 228, .07); }

.tray {
  flex: 1;
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  justify-content: center;
  align-items: center;
  min-height: var(--tile);
}
.tray-empty { color: var(--muted); font-size: .92rem; text-align: center; }
.tray-tools { display: flex; gap: 6px; flex: none; }
.icon-btn.small { width: 40px; height: 40px; font-size: 1.05rem; }
.icon-btn:disabled { opacity: .35; cursor: default; }

.tray-info {
  margin: -2px 0 0;
  min-height: 1.2em;
  text-align: center;
  font-size: .95rem;
  color: var(--muted);
}
.tray-info b { color: var(--paper); letter-spacing: .12em; font-size: 1.1em; }
.tray-info .n { color: var(--cyan); font-weight: 700; }

/* --- rows: word length label, fit and conflict ------------------------------ */
.slot { align-items: center; cursor: pointer; }
.slot-len {
  flex: none;
  width: calc(var(--tile) * .6);
  text-align: right;
  margin-right: 4px;
  font-size: calc(var(--tile) * .42);
  font-weight: 700;
  color: var(--cyan);
  font-variant-numeric: tabular-nums;
  line-height: 1;
}
/* While a word is in the tray, the rows it would fit glow a little. */
.slot.fits .cell:not(.filled) { background: rgba(39, 207, 228, .22); box-shadow: 0 0 0 2px rgba(39, 207, 228, .35); }
.slot.fits .slot-len { color: var(--gold); }

.slot.conflict .cell.clash {
  border-color: var(--bad) !important;
  background: rgba(255, 139, 122, .28) !important;
  animation: row-shake .4s ease-in-out;
}
.slot.conflict .slot-len { color: var(--bad); }
@keyframes row-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-4px); }
  75% { transform: translateX(4px); }
}

/* --- round 2 (Sept 2026) ----------------------------------------------------- */

/* The bank keeps its shape: a used letter leaves a tile-sized gap. */
.tile-gap {
  width: var(--tile);
  height: var(--tile);
  border-radius: 10px;
  border: 2px dashed rgba(143, 178, 196, .28);
  background: transparent;
  flex: none;
  padding: 0;
}
.tile-gap.in-tray { border-color: rgba(39, 207, 228, .55); cursor: pointer; }

/* Three swap slots, the counts, and the Swap button. */
.swap-slots { display: flex; gap: 8px; justify-content: center; }
.drop-spot .drop-hint { font-size: calc(var(--tile) * .3); color: rgba(255, 194, 71, .55); font-weight: 600; }
.drop-spot.sending { opacity: .6; }
.swap-counts { margin: 0; font-size: .92rem; color: var(--paper); text-align: center; }
.swap-counts b { color: var(--cyan); }
.swap-counts .ok { color: var(--good); font-weight: 600; }
.swap-counts.mismatch b { color: var(--bad); }
.swap-msg.bad { color: var(--bad); font-weight: 600; }
.check-btn.small { padding: 8px 18px; min-height: 42px; font-size: 1rem; }
.check-btn.waiting { background: var(--panel-soft); color: var(--gold); box-shadow: none; border: 2px solid var(--gold); }
.swap-msg { text-align: center; }

/* Taps are taps: no double-tap zoom, no text selection when a child
 * hammers a tile. */
.tile, .tile-gap, .cell, .drop-spot, .icon-btn, .check-btn, .ghost-btn, .swap-btn, .slot-len {
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
}

/* --- the page does not move while playing -------------------------------------
 * On an iPhone the whole game could be dragged up and down loosely: the play
 * screen was min-height 100dvh inside a page that could rubber-band. While
 * playing, the page is pinned to exactly the visible height (--app-h, kept
 * in step with visualViewport by app.js) and cannot bounce. If a very small
 * screen still cannot fit everything at the smallest tile size, the play
 * screen - not the page - scrolls, and it does not chain into a bounce. */
html, body { overscroll-behavior: none; }

html.playing,
html.playing body {
  height: var(--app-h, 100dvh);
  overflow: hidden;
}

html.playing body {
  position: fixed;
  inset: 0;
  width: 100%;
}

html.playing .screen.play {
  min-height: 0;
  height: var(--app-h, 100dvh);
  overflow-x: hidden;
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-bottom: max(10px, env(safe-area-inset-bottom));
}

/* Messages above the bank never change its height: a message appearing, or
 * wrapping onto a second line, used to nudge every letter below it down a
 * little - enough to turn a tap into a mis-tap. Both lines have a fixed
 * height now. */
.board-msg {
  min-height: 0;
  height: 2.5em;
  line-height: 1.25em;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
}
.tray-info {
  min-height: 0;
  height: 1.35em;
  line-height: 1.35em;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* --- iPad and other touch screens: room for Safari's own bars -------------
 * On an iPad, Safari's tab bar floats over the bottom of the page and the
 * home bar sits below that. A little breathing space at the bottom keeps the
 * Swap and Pass buttons clear of both; the tiles shrink a touch to pay for it. */
@media (pointer: coarse) {
  html.playing .screen.play {
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 36px);
  }
}
/* Held upright, the tab bar sits at the bottom - exactly where the trade
 * panel opens - so upright gets the bigger margin. */
@media (pointer: coarse) and (orientation: portrait) and (min-width: 700px) {
  html.playing .screen.play {
    padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 80px);
  }
}

/* --- filling the screen ---------------------------------------------------
 * The tile size is set by app.js to the biggest that fits this screen, and
 * everything else is sized from the tile, so the whole game grows and shrinks
 * together on any screen. Whatever height is left over is shared above and
 * below the game, so it sits in the middle instead of jammed against the top
 * with a band of empty space underneath. */
.tray-wrap,
.bank-wrap,
.swap { width: min(100%, calc(var(--tile) * 7 + 130px)); }
.play-head { width: min(100%, max(560px, calc(var(--tile) * 7 + 130px))); }

html.playing .screen.play > .board-wrap { margin-top: auto; }
html.playing .screen.play > .swap { margin-bottom: auto; }

/* --- the trade panel on a tablet or laptop ----------------------------------
 * Laid out sideways - name and slots on the left, counts and buttons on the
 * right - so it is only a little taller than one tile. The Swap button keeps
 * that much room even before a trade opens, so opening one never makes the
 * rest of the game jump smaller. */
@media (min-width: 600px) {
  .swap {
    min-height: calc(var(--tile) + 84px);
    justify-content: center;
  }
  .swap-matched {
    width: auto;
    max-width: 100%;
    display: grid;
    grid-template-columns: auto minmax(0, 260px);
    grid-template-areas:
      "partner counts"
      "slots   actions"
      "slots   msg";
    align-items: center;
    justify-items: center;
    column-gap: 18px;
    row-gap: 6px;
    padding: 10px 16px;
  }
  .swap-matched .partner { grid-area: partner; }
  .swap-matched .swap-slots { grid-area: slots; align-self: start; }
  .swap-matched .swap-counts { grid-area: counts; }
  .swap-matched .swap-actions { grid-area: actions; }
  .swap-matched .swap-msg { grid-area: msg; font-size: .8rem; line-height: 1.2; }
}

/* --- sideways screens: the puzzle beside the letters ----------------------
 * Held sideways, an iPad (or a Chromebook) is wide and short. The puzzle,
 * with Submit and Clear right under it, takes the left half; the tray, the
 * letters and the swap panel take the right: same pieces, half the height. The empty rows at the top and
 * bottom share out any spare height, which is what keeps it centred. */
@media (orientation: landscape) and (min-width: 900px) {
  html.playing .screen.play,
  .screen.play {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    grid-template-rows: auto 1fr auto auto auto auto auto 1fr;
    grid-template-areas:
      "head  head"
      ".     ."
      "board msg"
      "board tray"
      "board info"
      "board bank"
      "actions swap"
      ".     .";
    align-items: start;
    justify-items: center;
    column-gap: 24px;
    row-gap: 8px;
    width: 100%;
    max-width: 1400px;
    margin: 0 auto;
  }

  html.playing .screen.play > * { margin-top: 0; margin-bottom: 0; }
  .play-head { grid-area: head; width: min(100%, 1100px); }
  .board-wrap { grid-area: board; align-self: center; }
  .board-msg { grid-area: msg; width: 100%; }
  .tray-wrap { grid-area: tray; }
  .tray-info { grid-area: info; }
  .bank-wrap { grid-area: bank; }
  .actions { grid-area: actions; align-self: center; }
  .swap { grid-area: swap; }
}

/* --- the logo in the puzzle's corner -----------------------------------------
 * The words run short to long, which leaves an empty triangle at the top right
 * of the puzzle. The logo sits there, sized from the tile so it grows and
 * shrinks with the board, and never takes a tap meant for a square. */
.board { position: relative; }

.slot-spacer { height: var(--tile); flex: none; }

.board-logo {
  position: absolute;
  top: 0;
  right: 0;
  width: calc((var(--tile) + 6px) * var(--corner, 2) - 6px);
  height: calc(var(--tile) * 2 + 8px);
  object-fit: contain;
  object-position: center;
  pointer-events: none;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .45));
}

/* --- the numbers game ----------------------------------------------------------
 * Two equation lines where the word rows would be, and the signs underneath.
 * Everything is sized from the tile like the rest of the game. A line's own
 * tiles (--lt) shrink if an equation gets long, so the = and the total always
 * stay on the line, right after the last thing placed. */
html.math-mode .tray-wrap,
html.math-mode .tray-info { display: none; }

html.math-mode .board {
  width: min(100%, calc(var(--tile) * 8.6 + 40px));
  align-items: stretch;
  gap: 10px;
}

.eq-hint {
  margin: 0;
  text-align: center;
  color: var(--muted);
  font-size: clamp(.9rem, calc(var(--tile) * .22), 1.25rem);
}

.eq-line {
  --lt: var(--tile);
  display: flex;
  align-items: center;
  gap: 5px;
  min-height: calc(var(--tile) + 22px);
  padding: 8px 12px;
  border: 3px dashed #17506d;
  border-radius: 16px;
  background: rgba(10, 46, 67, .45);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.eq-line.active { border-style: solid; border-color: var(--cyan); background: rgba(39, 207, 228, .08); }
.eq-line.correct {
  border-style: solid;
  border-color: var(--good);
  background: rgba(87, 217, 154, .2);
  box-shadow: 0 0 0 3px rgba(87, 217, 154, .25);
}

.eq-body { display: flex; align-items: center; gap: 5px; flex: 0 1 auto; min-width: 0; }
.eq-empty { color: var(--muted); font-size: .95rem; padding: 0 6px; white-space: nowrap; }

.eq-tok {
  height: var(--lt);
  flex: none;
  padding: 0;
  border-radius: 10px;
  font-family: inherit;
  font-weight: 700;
  cursor: pointer;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
}
.eq-num {
  width: var(--lt);
  border: 0;
  background: linear-gradient(180deg, var(--tile-top) 0%, var(--tile-bottom) 100%);
  box-shadow: 0 3px 0 #c99a3c;
  color: var(--ink);
  font-size: calc(var(--lt) * .55);
}
.eq-sign {
  width: calc(var(--lt) * .75);
  border: 2px solid #17506d;
  background: var(--panel-soft);
  color: var(--gold);
  font-size: calc(var(--lt) * .55);
}
.eq-sign.eq-paren { width: calc(var(--lt) * .5); color: var(--cyan); }

.eq-equals {
  flex: none;
  width: calc(var(--lt) * .55);
  text-align: center;
  font-size: calc(var(--lt) * .62);
  font-weight: 700;
  color: var(--paper);
}
.eq-target {
  flex: none;
  min-width: calc(var(--lt) * 1.3);
  height: var(--lt);
  padding: 0 6px;
  display: grid;
  place-items: center;
  border-radius: 12px;
  border: 3px solid var(--gold);
  color: var(--gold);
  font-size: calc(var(--lt) * .56);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.eq-line.correct .eq-target { background: var(--good); border-color: var(--good); color: #05351f; }
.eq-points {
  flex: none;
  margin-left: 4px;
  color: var(--good);
  font-weight: 700;
  font-size: calc(var(--lt) * .42);
  white-space: nowrap;
}

.eq-keys { display: flex; gap: 8px; align-items: center; }
.eq-key {
  width: var(--tile);
  height: var(--tile);
  flex: none;
  padding: 0;
  border-radius: 12px;
  border: 2px solid #17506d;
  background: var(--panel-soft);
  color: var(--gold);
  font-family: inherit;
  font-size: calc(var(--tile) * .55);
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 3px 0 #0a3346;
  touch-action: manipulation;
  -webkit-user-select: none;
  user-select: none;
}
.eq-key:active { transform: translateY(1px); }
.eq-key.eq-paren { width: calc(var(--tile) * .7); color: var(--cyan); }
.eq-logo {
  margin-left: auto;
  height: calc(var(--tile) * 1.25);
  width: auto;
  max-width: calc(var(--tile) * 2.4);
  object-fit: contain;
  pointer-events: none;
  filter: drop-shadow(0 6px 14px rgba(0, 0, 0, .45));
}

/* --- How to Play links --------------------------------------------------------- */
.howto-link { text-decoration: none; text-align: center; }
.wait-card .howto-link { display: inline-block; margin: 6px 0 4px; }
.howto-q {
  display: inline-grid;
  place-items: center;
  width: 28px;
  height: 28px;
  flex: none;
  border-radius: 50%;
  border: 2px solid var(--cyan-dim);
  color: var(--cyan);
  font-weight: 700;
  text-decoration: none;
  font-size: .95rem;
}

/* --- the logo in the numbers game ------------------------------------------------
 * Where the word tray sits in the word game. Sized from the tile like
 * everything else, so it grows with the screen. */
.play-logo { display: none; }

html.math-mode .play-logo {
  display: block;
  height: calc(var(--tile) * 2.6);
  width: auto;
  max-width: 100%;
  object-fit: contain;
  pointer-events: none;
  filter: drop-shadow(0 10px 22px rgba(0, 0, 0, .45));
}
html.math-mode .eq-logo { display: none; }

/* The row of signs spans the same width as the equation lines above it. */
html.math-mode .eq-key { flex: 1 1 0; width: auto; min-width: 0; }
html.math-mode .eq-key.eq-paren { flex: .7 1 0; width: auto; }

/* A phone has less height to spare: a slightly smaller logo, and the
 * instructions line tucked closer. */
@media (max-width: 599px) {
  html.math-mode .play-logo { height: calc(var(--tile) * 2.1); }
  html.math-mode .play { gap: 6px; }
}

@media (orientation: landscape) and (min-width: 900px) {
  .play-logo { grid-area: tray; justify-self: center; }
}

/* Nothing on the play screen may be squashed to make room. The message line
 * clips its own overflow, which let the browser squeeze it to a few pixels on
 * a phone - so the score message vanished behind the row of signs. Every
 * piece keeps its height, and the tile size (app.js) shrinks instead. */
html.playing .screen.play > * { flex-shrink: 0; }
/* The one exception: the little hint under the word tray may give way. */
html.playing .screen.play > .tray-info { flex-shrink: 1; }

/* On a phone the message line is smaller type, so keeping it costs the
 * tiles as little as possible. */
@media (max-width: 599px) {
  .board-msg { font-size: .82rem; }
}

/* ===========================================================================
 * Training Camp's look, in the class game (Tim, Oct 2026: "mirror Training
 * Camp as closely as possible"). Everything here is scoped to the student
 * page (body.zs-student) because Training Camp loads this stylesheet too and
 * has its own copies of these rules.
 * ======================================================================== */

/* The Hint button, and the tiles a hint lights up. */
.zs-student .hint-btn { border-color: var(--gold-deep); color: var(--gold); }
.zs-student .tile.hinted { outline: 3px dashed var(--gold); outline-offset: 2px; }

/* Swapping is allowed any time; until one is due the button is quieter, and
   a line under it says where the child stands. */
.zs-student .swap-btn.optional { filter: saturate(.55) brightness(.85); }
.zs-student .swap-status {
  margin: 0;
  min-height: 1.2em;
  font-size: .85rem;
  color: var(--muted);
  text-align: center;
}
.zs-student .swap-status.due { color: var(--gold); font-weight: 600; }

/* A big clock, so a child always knows how long is left. */
.zs-student .play-head .clock { min-width: 2.6em; text-align: right; font-size: 3.3em; font-weight: 700; line-height: 1; }
.zs-student .play-head .clock.low { color: var(--bad); }
@media (max-width: 599px) {
  .zs-student .play-head .clock { font-size: 2.7em; }
}

/* The last ten seconds, huge, over the board. Taps go straight through. */
.zs-countdown {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: grid;
  place-items: center;
  pointer-events: none;
  font-family: Fredoka, system-ui, sans-serif;
  font-weight: 700;
  font-size: min(42vw, 42vh);
  line-height: 1;
  color: rgba(255, 194, 71, .55);
  text-shadow: 0 0 40px rgba(0, 0, 0, .6);
}
.zs-countdown.tick { animation: zs-tick .9s ease-out; }
.zs-countdown.time { font-size: min(22vw, 26vh); color: rgba(255, 139, 122, .9); }
@keyframes zs-tick {
  from { transform: scale(1.35); opacity: 1; }
  to { transform: scale(1); opacity: .85; }
}
@media (prefers-reduced-motion: reduce) {
  .zs-countdown.tick { animation: none; }
}

/* --- the end of a game: Training Camp's screen ------------------------------- */
.zs-student .done-card { display: flex; flex-direction: column; align-items: stretch; gap: 12px; }
.zs-student .done-head, .zs-student .done-main, .zs-student .done-side {
  display: flex; flex-direction: column; align-items: stretch; gap: 12px;
}
.zs-student .done-head > * { margin: 0; }
.zs-student .done-card .ghost-btn { text-align: center; text-decoration: none; }
.zs-student .done-card .ghost-btn.quiet { margin-top: 0; }
.zs-student .done-words { margin: 0; color: var(--muted); font-size: 1.05rem; }
.zs-student .done-next { margin: 0; color: var(--muted); font-size: .95rem; text-align: center; }
/* Every word (or equation) from the game, with its points. A long list
   scrolls inside its box so the buttons stay on screen. */
.zs-student .done-list {
  list-style: none;
  margin: 0;
  padding: 6px;
  max-height: max(120px, min(320px, calc(100dvh - 440px)));
  overflow-y: auto;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
  gap: 6px;
  background: var(--panel-soft);
  border: 1px solid #17506d;
  border-radius: 14px;
}
.zs-student .done-list.math { grid-template-columns: 1fr; }
.zs-student .done-list li {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding: 5px 9px;
  border-radius: 9px;
  background: rgba(255, 255, 255, .05);
  font-weight: 600;
  letter-spacing: .04em;
  white-space: nowrap;
}
.zs-student .done-list li i { font-style: normal; font-size: .85em; }
.zs-student .done-list li b { color: var(--gold); font-size: .95em; }

/* Big screens fill up: two columns on a laptop or a sideways iPad, one big
   column on an upright iPad, everything sized from --f. */
@media (min-width: 900px) and (min-height: 560px), (min-width: 700px) and (min-height: 900px) {
  .zs-student .done-card {
    --f: clamp(15px, min(1.35vw, 2.5vh), 32px);
    font-size: var(--f);
    padding: 1.6em 2.2em;
    gap: 1.1em;
  }
  .zs-student .done-head { gap: .3em; }
  .zs-student .done-side { gap: .8em; justify-content: center; }
  .zs-student .done-card h1 { font-size: 3.2em; line-height: 1.05; }
  .zs-student .done-card .done-score { font-size: 1.5em; }
  .zs-student .done-score b { font-size: 1.6em; }
  .zs-student .done-words { font-size: 1.15em; }
  .zs-student .done-next { font-size: 1em; }
  .zs-student .done-list {
    font-size: 1.3em;
    max-height: calc(100dvh - var(--f) * 14 - 72px);
    grid-template-columns: repeat(auto-fill, minmax(9em, 1fr));
    gap: .4em;
    padding: .45em;
  }
  .zs-student .done-list.math { grid-template-columns: repeat(auto-fill, minmax(15em, 1fr)); }
  .zs-student .done-list li { padding: .35em .65em; }
  .zs-student .done-side .big-btn { font-size: 1.35em; min-height: 2.6em; }
  .zs-student .done-side .ghost-btn { font-size: 1.1em; min-height: 2.7em; }
}
@media (min-width: 900px) and (min-height: 560px) {
  .zs-student .done-card {
    width: min(1500px, 100%);
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
    grid-template-areas: "head head" "main side";
    column-gap: 2.2em;
    align-items: start;
  }
  .zs-student .done-head { grid-area: head; }
  .zs-student .done-main { grid-area: main; }
  .zs-student .done-side { grid-area: side; }
}
@media (min-width: 700px) and (max-width: 899px) and (min-height: 900px) {
  .zs-student .done-card { --f: clamp(16px, 2.4vw, 22px); width: min(720px, 100%); }
}

/* ===========================================================================
 * TEAM PLAY (Tim, Oct 2026). Teams of 3-4 sit together; everyone's points go
 * into one team score; teammates swap one letter at a time, any time.
 * Big, colour-coded targets for Chromebooks and tablets.
 * ======================================================================== */

.zs-student .team-total b { font-size: 1.5em; color: var(--team, var(--gold)); }
.zs-student .team-total small { color: var(--team, var(--muted)); font-weight: 700; }

.zs-student .team-zone { width: 100%; display: flex; flex-direction: column; gap: 8px; }

/* Teammates: one big card each, your own "I need" card first. */
.zs-student .team-mates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
  gap: 8px;
}
.zs-student .mate {
  font-family: inherit;
  min-height: max(58px, calc(var(--tile) * 1.05));
  padding: 6px 10px;
  border-radius: 14px;
  border: 3px solid var(--team, var(--cyan-dim));
  background: var(--panel);
  color: var(--paper);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  cursor: pointer;
  text-align: center;
  line-height: 1.15;
}
.zs-student .mate .mate-name { font-weight: 700; font-size: 1.02rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.zs-student .mate .mate-line { font-size: .85rem; color: var(--muted); display: flex; align-items: center; gap: 5px; flex-wrap: wrap; justify-content: center; }
.zs-student .mate .mate-line i { font-style: normal; opacity: .8; }
.zs-student .mate.me { border-style: dashed; background: rgba(255, 255, 255, .03); }
.zs-student .mate.selected {
  background: var(--team, var(--cyan));
  color: #06172a;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, .55), 0 0 22px var(--team, var(--cyan));
}
.zs-student .mate.selected .mate-line { color: #06172a; }
.zs-student .mate.busy { border-style: dotted; }
.zs-student .mate-undo { text-decoration: underline; color: var(--gold); font-weight: 700; }

/* A small letter tile inside text: "Needs [W]". */
.zs-student .mini-tile {
  display: inline-grid;
  place-items: center;
  min-width: 1.7em;
  height: 1.7em;
  padding: 0 .25em;
  border-radius: 6px;
  background: linear-gradient(180deg, var(--tile-top), var(--tile-bottom));
  color: var(--ink);
  font-weight: 700;
  font-size: 1.05em;
  line-height: 1;
}

.zs-student .team-msg { margin: 0; min-height: 1.2em; font-size: .88rem; color: var(--muted); text-align: center; }

/* A letter coming in from a teammate. */
.zs-student .team-incoming {
  background: linear-gradient(180deg, #4a3507, #2c2005);
  border: 3px solid var(--gold);
  border-radius: 16px;
  padding: 10px 14px;
  text-align: center;
  box-shadow: 0 0 24px rgba(255, 194, 71, .35);
  animation: zs-arrive .35s ease-out;
}
.zs-student .team-incoming p { margin: 0; }
.zs-student .team-incoming .ti-main { font-size: 1.2rem; font-weight: 700; color: #fff4d6; }
.zs-student .team-incoming .ti-sub { font-size: .92rem; color: #f3e2b6; margin-top: 4px; }
.zs-student .team-incoming .ti-actions { display: flex; gap: 10px; justify-content: center; align-items: center; margin-top: 6px; }
.zs-student .team-incoming .ti-more { font-size: .85rem; color: var(--gold); }
@keyframes zs-arrive { from { transform: scale(.94); opacity: 0; } to { transform: scale(1); opacity: 1; } }

/* While choosing a letter to send or send back, the bank lights up. */
html.team-picking .zs-student .bank-wrap,
.zs-student.team-picking .bank-wrap { box-shadow: 0 0 0 3px var(--gold), 0 0 22px rgba(255, 194, 71, .45); border-radius: 16px; }
html.team-picking .bank-wrap { box-shadow: 0 0 0 3px var(--gold), 0 0 22px rgba(255, 194, 71, .45); border-radius: 16px; }
/* The letter a teammate asked for glows; letters a teammate needs get a dot. */
.zs-student .tile.wanted { outline: 4px solid var(--gold); outline-offset: 2px; animation: zs-glow 1s ease-in-out infinite alternate; }
@keyframes zs-glow { from { box-shadow: 0 0 0 rgba(255, 194, 71, 0); } to { box-shadow: 0 0 18px rgba(255, 194, 71, .9); } }
.zs-student .tile.mate-needs { position: relative; }
.zs-student .tile.mate-needs::after {
  content: '';
  position: absolute;
  top: 4px;
  left: 4px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--gold);
  box-shadow: 0 0 0 2px #fff;
}

/* "Which letter do you need?" */
.need-picker {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: grid;
  place-items: center;
  padding: 16px;
  background: rgba(2, 12, 23, .82);
}
.need-card {
  background: var(--panel);
  border: 1px solid #14465f;
  border-radius: 20px;
  padding: 18px;
  width: min(560px, 100%);
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.need-title { margin: 0; text-align: center; font-size: 1.25rem; font-weight: 700; color: var(--gold); }
.need-keys { display: grid; grid-template-columns: repeat(7, 1fr); gap: 7px; }
.need-keys.math { grid-template-columns: repeat(5, 1fr); }
.need-key {
  font-family: inherit;
  font-weight: 700;
  font-size: 1.35rem;
  min-height: 52px;
  border-radius: 10px;
  border: 0;
  background: linear-gradient(180deg, var(--tile-top), var(--tile-bottom));
  color: var(--ink);
  cursor: pointer;
}
.need-key.on { outline: 4px solid var(--gold); outline-offset: 2px; }
.need-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; }

/* The waiting room: your team, so you can sit together. */
.zs-student .wait-team {
  border: 3px solid var(--team, var(--cyan));
  border-radius: 16px;
  padding: 12px 14px;
  text-align: center;
}
.zs-student .wait-team p { margin: 0; }
.zs-student .wait-team .wt-name { font-size: 1.3rem; }
.zs-student .wait-team .wt-name b { color: var(--team, var(--cyan)); }
.zs-student .wait-team .wt-sit { color: var(--muted); font-size: .9rem; margin-top: 6px; }
.zs-student .wait-team .wt-list { font-weight: 700; font-size: 1.1rem; }

/* The end of the game: the team's score. */
.zs-student .done-team { margin: 0; font-size: 1.2em; }
.zs-student .done-team span { color: var(--team, var(--cyan)); font-weight: 700; }
.zs-student .done-team b { color: var(--gold); font-size: 1.3em; }
/* Phones: the teammates fit on one row so the board keeps its room. */
@media (max-width: 599px) {
  .zs-student .team-mates { grid-template-columns: repeat(auto-fit, minmax(72px, 1fr)); gap: 6px; }
  .zs-student .mate { min-height: 52px; padding: 4px 4px; border-radius: 12px; }
  .zs-student .mate .mate-name { font-size: .88rem; }
  .zs-student .mate .mate-line { font-size: .72rem; }
  .zs-student .mate .mate-line i { display: none; }
  .zs-student .team-incoming { padding: 8px 10px; }
  .zs-student .team-incoming .ti-main { font-size: 1.05rem; }
  .zs-student .team-incoming .ti-sub { font-size: .82rem; }
}

/* --- iPad stability (Tim, Oct 2026) ------------------------------------------
 * No double-tap zoom anywhere, nothing wider than the screen, and room for
 * the ZoodleBug Home button above the game's header. */
html {
  touch-action: manipulation;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}
html, body { overflow-x: hidden; max-width: 100%; }
.zs-student .screen.play { padding-top: max(var(--zb-home-top, 0px), env(safe-area-inset-top, 0px)); }
.zoom-note {
  margin: 0;
  padding: 6px 14px;
  border-radius: 999px;
  background: var(--gold);
  color: var(--ink);
  font-weight: 700;
  font-size: .9rem;
  text-align: center;
}

/* --- Full Screen buttons (Tim, Oct 2026): easy to find, not shouting ---------- */
.join-extras { display: flex; gap: 10px; flex-wrap: wrap; justify-content: center; }
.fs-btn { font-size: .95rem; }
button.howto-q {
  background: none;
  padding: 0;
  font-family: inherit;
  cursor: pointer;
  line-height: 1;
}
.fs-icon { font-size: 1rem; }
@media (pointer: coarse) {
  .howto-q { width: 36px; height: 36px; }
}
@media (max-width: 480px) {
  .play-head { gap: 6px; padding-left: 10px; padding-right: 10px; }
  .play-head .howto-q { width: 32px; height: 32px; }
}

/* --- one screen, always (Tim, Oct 2026): the join screen on a short phone --- */
.join-logo { width: auto; max-width: min(320px, 74vw); max-height: 24vh; }
@media (max-height: 760px) {
  .screen.join { gap: 8px; padding-top: max(8px, var(--zb-home-top, 0px)); }
  .join-card { padding: 14px; gap: 10px; }
  .join-card .field-note, .join-hint { display: none; }
}

/* --- Live Join (ZoodleBug Pro, Tim Oct 2026) ----------------------------------
 * A student who taps Join on their ZoodleBug home page arrives with the code
 * and their class name, and is put in the game by itself: the form stays out
 * of sight while it joins. Once in, there is no way out of the waiting room
 * but the game starting, the teacher ending it, or closing the page. */
html.zs-autojoin .join-card .field,
html.zs-autojoin #joinBtn,
html.zs-autojoin .join-hint,
html.zs-autojoin .join-extras,
html.zs-autojoin .teacher-link { display: none; }
html.zs-autojoin .join-msg { font-size: 1.3rem; text-align: center; }
html.zs-invited-wait #zbHomeButton { display: none !important; }
