/* --------------------------------------------------------------------------
   AlgeBug.

   Bright, chunky and generous with tap targets: this gets used on an iPad by
   a nine-year-old who is already concentrating hard on the maths, so nothing
   here should need a second try to hit.
   -------------------------------------------------------------------------- */

:root {
  --ink: #1b2440;
  --ink-soft: #5b6784;
  --line: #d9e1ee;
  --card: #ffffff;
  --orange: #f77f00;
  --blue: #4361ee;
  --green: #2a9d8f;
  --red: #d62828;
  /* White text on the brand orange is 2.63:1 and on the brand green 3.32:1 -
     both well under the 4.5:1 that normal-weight text needs, and these are the
     three biggest buttons in the app. The brand colours stay for fills and
     accents; buttons that carry white words use these darker relatives. */
  --orange-btn: #b34e00;
  --green-btn: #1b6b61;
  --grey-btn: #4d5973;
  --gold: #ffd166;
  --radius: 20px;
  --shadow: 0 10px 28px rgba(27, 36, 64, 0.10);
}

* { box-sizing: border-box; }

/* The sheets and screens below set `display`, which would otherwise win over
   the browser's own rule for [hidden] and leave an invisible overlay sitting
   across the whole page swallowing taps. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  font-family: Fredoka, system-ui, -apple-system, "Segoe UI", sans-serif;
  color: var(--ink);
  background:
    radial-gradient(1100px 520px at 12% -8%, #ffe8c7 0%, rgba(255, 232, 199, 0) 60%),
    radial-gradient(900px 480px at 92% 4%, #d9e6ff 0%, rgba(217, 230, 255, 0) 62%),
    #f5f8fd;
  min-height: 100%;
  -webkit-text-size-adjust: 100%;
  /* No double-tap zoom anywhere. On an iPad two quick taps - easy to do by
     accident while moving pieces - zoomed the page right in, and with the
     balance filling the screen there was nowhere to pinch back out (Tim,
     27 Sep 2026). Pinching still works everywhere. */
  touch-action: manipulation;
}

body { padding-bottom: 40px; }

/* --- top bar -------------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 10px clamp(14px, 3vw, 30px);
  flex-wrap: wrap;
}

/* The top bar carries no AlgeBug mark at all. The full logo needs vertical
   room - the balance sits under the word - and a top bar tall enough for it
   eats the screen on every page; a wordmark in the corner was no better. The
   hero on the home screen carries the logo instead, where it has room to be
   read, and the way back to the AlgeBug home screen from a puzzle is the
   Back button on the play screen, not the corner. The corner is the way out
   to ZoodleBug. */
.home-btn { flex: 0 0 auto; }
/* The ZoodleBug Home button is the shared one now, top-left on every page
   (/home-button.js); the top bar starts after it. */
.topbar { padding-left: var(--zb-home-space, 190px); }

.topbar-right {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.sound-btn {
  border: 2px solid var(--line);
  cursor: pointer;
  font-family: inherit;
  line-height: 1;
}

.sound-btn[aria-pressed="false"] { opacity: .5; }

/* The account pill when progress is not reaching the account. Quiet, but not
   invisible - silence about a failed save is what let a broken write go
   unnoticed for months. */
.who.save-warn {
  border-color: var(--orange-btn);
  color: var(--orange-btn);
}
.who.save-warn::after { content: ' \26A0'; }

.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

.score, .who {
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 7px 15px;
  font-weight: 600;
  font-size: 15px;
  white-space: nowrap;
}

.score b { color: var(--orange); }
.who { color: var(--ink-soft); max-width: 44vw; overflow: hidden; text-overflow: ellipsis; }

/* --- buttons -------------------------------------------------------------- */

.big-btn, .ghost-btn, .check-btn, .back-btn {
  font-family: inherit;
  font-weight: 600;
  border-radius: 999px;
  cursor: pointer;
  border: 2px solid transparent;
  transition: transform .08s ease, box-shadow .12s ease, background .12s ease;
}

.big-btn {
  background: var(--orange-btn);
  color: #fff;
  border-color: #8f3e00;
  padding: 13px 26px;
  font-size: 18px;
  box-shadow: 0 5px 0 #7d3600;
}

.big-btn:active { transform: translateY(3px); box-shadow: 0 2px 0 #7d3600; }
.big-btn.alt { background: var(--blue); border-color: #3651c9; box-shadow: 0 5px 0 #2f47b4; }
.big-btn.alt:active { box-shadow: 0 2px 0 #2f47b4; }

.ghost-btn {
  background: var(--card);
  color: var(--ink);
  border-color: var(--line);
  padding: 10px 18px;
  font-size: 15px;
  text-decoration: none;
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

.ghost-btn.big { padding: 13px 24px; font-size: 17px; }
.ghost-btn:hover { border-color: #b8c6dd; }
.ghost-btn:disabled { opacity: .4; cursor: default; }

.check-btn {
  background: var(--green-btn);
  color: #fff;
  border-color: #145148;
  padding: 13px 30px;
  font-size: 18px;
  box-shadow: 0 5px 0 #11463f;
}

.check-btn:active { transform: translateY(3px); box-shadow: 0 2px 0 #11463f; }
.check-btn.live { background: var(--grey-btn); border-color: #3c4760; box-shadow: 0 5px 0 #343e54; }

.back-btn {
  background: none;
  color: var(--ink-soft);
  padding: 8px 4px;
  font-size: 16px;
}

.back-btn:hover { color: var(--ink); }

/* --- screens -------------------------------------------------------------- */

.screen { padding: 0 clamp(14px, 3vw, 30px); max-width: 1180px; margin: 0 auto; }

/* Two things side by side: the mark carries the name, so the words beside it
   are free to be about the lessons rather than about the brand. The mark sits
   where the heading used to, and the heading has gone over to the buttons. */
.hero {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(18px, 4vw, 48px);
  flex-wrap: wrap;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(22px, 4vw, 38px);
  margin-top: 6px;
}

.hero-mark { flex: 0 1 auto; display: flex; align-items: center; }

.hero-logo {
  display: block;
  width: clamp(190px, 30vw, 360px);
  height: auto;
}

.hero-say {
  flex: 1 1 300px;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  text-align: right;
}

.hero h1 {
  margin: 0 0 16px;
  font-size: clamp(26px, 5vw, 40px);
  letter-spacing: -0.8px;
}

.hero p { margin: 0 0 18px; font-size: clamp(15px, 2.2vw, 18px); color: var(--ink-soft); max-width: 62ch; }

.inline-hex {
  display: inline-block;
  background: var(--ink);
  color: #fff;
  font-weight: 700;
  padding: 1px 9px;
  border-radius: 6px;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  min-width: 26px;
  text-align: center;
}

.hero-actions { display: flex; gap: 12px; flex-wrap: wrap; justify-content: flex-end; }

.signin-note { margin: 22px 0 0; font-size: 14px; color: var(--ink-soft); max-width: 46ch; }
.signin-note a { color: var(--blue); }

.section-title { margin: 30px 0 14px; font-size: 22px; }

/* --- the level list -------------------------------------------------------- */

.levels-head {
  position: relative;
  margin: 30px 0 14px;
}

.levels-title { margin: 0; font-size: 23px; }

.levels-count {
  position: absolute;
  top: 50%;
  right: 0;
  transform: translateY(-50%);
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 4px 13px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--ink-soft);
}

.level-card.paper .level-num { background: #fff; color: var(--tint); box-shadow: inset 0 0 0 3px var(--tint); }

/* --- level cards ---------------------------------------------------------- */

.levels {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr));
  gap: 14px;
  align-items: stretch;
}

.level-card {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  text-align: left;
  background: var(--card);
  border: 3px solid var(--line);
  border-radius: var(--radius);
  padding: 16px;
  cursor: pointer;
  font-family: inherit;
  box-shadow: var(--shadow);
  transition: transform .1s ease, border-color .12s ease;
}

.level-card:hover { transform: translateY(-3px); border-color: var(--tint); }

.level-num {
  flex: 0 0 auto;
  width: 46px;
  height: 46px;
  border-radius: 14px;
  background: var(--tint);
  color: #fff;
  font-size: 24px;
  font-weight: 700;
  display: grid;
  place-items: center;
}

.level-body { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.level-name { font-size: 19px; font-weight: 600; }
.level-blurb-sm { font-size: 13.5px; color: var(--ink-soft); line-height: 1.35; }

.bar { display: block; height: 9px; border-radius: 999px; background: #eef2f8; margin-top: 6px; overflow: hidden; }
.bar-fill { display: block; height: 100%; background: var(--tint); border-radius: 999px; transition: width .3s ease; }
.level-count { font-size: 12.5px; color: var(--ink-soft); }

.level-tick {
  position: absolute;
  top: 12px; right: 14px;
  color: var(--green);
  font-size: 22px;
  font-weight: 700;
}

/* The beta strip. Loud enough to be found when something goes wrong, quiet
   enough not to be the first thing a child reads. */
.beta-strip {
  background: #fff6ec;
  border: 2px solid #f4c79a;
  border-radius: var(--radius);
  padding: 16px 20px;
  margin: 26px 0 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  flex-wrap: wrap;
}

.beta-strip p { margin: 0; flex: 1 1 320px; color: #6b4a22; font-size: 15px; }

.fb-row { margin-bottom: 14px; display: flex; flex-direction: column; gap: 6px; }
.fb-row label { font-weight: 600; font-size: 15px; }
.fb-row input, .fb-row textarea {
  font-family: inherit;
  font-size: 16px;
  color: var(--ink);
  border: 2px solid var(--line);
  border-radius: 12px;
  padding: 10px 12px;
  background: #fbfcff;
  width: 100%;
}
.fb-row textarea { resize: vertical; min-height: 110px; }
.fb-turnstile { margin: 4px 0 10px; min-height: 66px; }

/* Tutoring. */
.tutor-strip {
  background: #eef7f5;
  border: 2px solid #9fcfc7;
  border-radius: var(--radius);
  padding: 14px 18px;
  margin: 26px 0 0;
  display: flex;
  align-items: center;
  gap: 12px 18px;
  flex-wrap: wrap;
}

.tutor-now, .tutor-add { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tutor-label { font-weight: 700; font-size: 15px; color: #14695c; }
.tutor-count { font-size: 13.5px; color: var(--ink-soft); }
.tutor-msg { width: 100%; margin: 0; font-size: 14px; color: #14695c; }
.tutor-msg.bad { color: #98211b; }

#studentSelect, #studentNameInput {
  font-family: inherit;
  font-size: 15px;
  color: var(--ink);
  border: 2px solid var(--line);
  border-radius: 10px;
  padding: 8px 10px;
  background: #fbfcff;
  min-height: 44px;
}

#studentNameInput { width: 190px; }

/* Acting as a child is a mode, and modes that look like normal are how people
   put work in the wrong place. The name badge goes green and stays loud. */
.who.as-student {
  background: #14695c;
  border-color: #14695c;
  color: #fff;
}

.teacher-strip {
  background: var(--card);
  border: 3px dashed var(--line);
  border-radius: var(--radius);
  padding: 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
}

.teacher-strip p { margin: 0; color: var(--ink-soft); }

/* --- level screen --------------------------------------------------------- */

.level-heading { margin: 8px 0 4px; font-size: clamp(24px, 4vw, 32px); color: var(--tint); }
.level-blurb { margin: 0 0 20px; color: var(--ink-soft); max-width: 68ch; }

/* The lobby's top row: the way out on the left, the way onward on the right. */
.level-top {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 4px;
}
.level-top .back-btn { margin-bottom: 0; }
.level-next { white-space: nowrap; font-weight: 700; }
.level-next:hover { border-color: var(--blue); color: var(--blue); }

/* The two sets on a level screen: the practice problems a student works
   through, and the Teaching Kit a teacher shows on the way in. */
.set-heading {
  margin: 0 0 12px;
  font-size: 15px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.teach-kit {
  margin-top: 26px;
  border: 3px dashed var(--line);
  border-radius: 18px;
  padding: 0 16px;
  background: #fbfcfe;
}

.teach-kit > summary {
  display: flex;
  align-items: baseline;
  gap: 10px;
  padding: 14px 4px;
  cursor: pointer;
  list-style: none;
  font-weight: 700;
}
.teach-kit > summary::-webkit-details-marker { display: none; }
.teach-kit > summary::before {
  content: '\25B8';
  color: var(--ink-soft);
  transition: transform .15s ease;
}
.teach-kit[open] > summary::before { transform: rotate(90deg); }
.teach-kit > summary:focus-visible { outline: 3px solid var(--tint, #2d7dd2); outline-offset: 3px; border-radius: 10px; }

.teach-title { font-size: 15px; letter-spacing: .04em; text-transform: uppercase; }
.teach-count { font-size: 13px; font-weight: 600; color: var(--ink-soft); }
.teach-note { margin: 0 4px 14px; font-size: 13.5px; color: var(--ink-soft); max-width: 62ch; }
.teach-kit .problem-grid { padding-bottom: 18px; }

.problem-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: 12px;
}

/* The tile plus the two things that sit on top of it: when it was solved, and
   the educator's un-solve. Both are siblings rather than children because the
   tile is itself a button. */
.tile-wrap { position: relative; display: flex; }
.tile-wrap .problem-tile { flex: 1 1 auto; }

.tile-when {
  position: absolute;
  left: 14px;
  bottom: 5px;
  font-size: 11.5px;
  font-weight: 600;
  color: #4d8a7d;
  pointer-events: none;
}

/* Small, quiet, and off in the corner: un-solving is a correction, not
   something to invite. It grows and reddens on hover so it is unmistakable
   once you have reached for it. */
.tile-undo {
  position: absolute;
  top: 5px;
  right: 6px;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: 8px;
  background: transparent;
  color: #9aa6bd;
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  line-height: 1;
  cursor: pointer;
}

.tile-undo:hover,
.tile-undo:focus-visible {
  background: #ffe3e0;
  border-color: var(--red);
  color: #98211b;
}

.problem-tile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--card);
  border: 3px solid var(--line);
  border-radius: 16px;
  padding: 14px 16px;
  font-family: inherit;
  cursor: pointer;
  transition: transform .1s ease, border-color .12s ease;
}

.problem-tile:hover { transform: translateY(-2px); border-color: var(--tint); }
.problem-tile.done { background: #f2fbf8; border-color: #b7e4d8; }

.tile-num {
  flex: 0 0 auto;
  width: 30px; height: 30px;
  border-radius: 9px;
  background: #eef2f8;
  color: var(--ink-soft);
  font-size: 14px;
  font-weight: 600;
  display: grid;
  place-items: center;
}

.problem-tile.done .tile-num { background: var(--green); color: #fff; }
.tile-eq { font-size: 19px; font-weight: 600; }
.tile-tick { margin-left: auto; color: var(--green); font-size: 19px; font-weight: 700; }

/* Room for the date along the bottom, and for the un-solve in the corner. */
.problem-tile.done { padding-bottom: 22px; }
body.as-educator .problem-tile.done .tile-tick { margin-right: 24px; }

.empty { color: var(--ink-soft); grid-column: 1 / -1; }

/* --- play screen ---------------------------------------------------------- */

.play-head {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin: 6px 0 10px;
}

.play-title { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

.chip {
  background: var(--tint, var(--blue));
  color: #fff;
  border-radius: 999px;
  padding: 6px 14px;
  font-size: 14px;
  font-weight: 600;
}

/* The equation is the thing the student is actually working on, so it is the
   biggest text on the screen by a distance. There is dead space between the
   header and the balance and this is what it is for. */
.equation {
  font-size: clamp(40px, 8.5vw, 76px);
  font-weight: 700;
  letter-spacing: -1px;
  line-height: 1.05;
}

.next-level-btn { padding: 9px 18px; font-size: 16px; white-space: nowrap; }

/* Whose record this lesson is going into. It lives in the gap between the
   equation and the arrows - space that was doing nothing - and it is large and
   faint rather than small and solid, because a small badge in a corner is
   exactly the sort of thing people stop noticing after a week. It never takes
   space from the equation: it shrinks first and disappears before anything
   else has to move. */
.as-ghost {
  flex: 0 1 auto;
  min-width: 0;
  margin: 0 auto;
  padding: 0 10px;
  font-size: clamp(18px, 2.6vw, 34px);
  font-weight: 700;
  letter-spacing: -0.5px;
  color: #14695c;
  opacity: .26;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
  user-select: none;
}

/* The arrows keep their own corner whether the ghost is there or not. */
.play-nav { margin-left: auto; }
.play-nav { margin-left: auto; display: flex; gap: 8px; }

/* The two smallest targets in the app, and the two easiest to grow without
   costing the board any height: they sit in rows that are already taller than
   they are. (The wide-but-short buttons - Check, "I know X!" - are left alone;
   they clear WCAG 2.2's 24x24 comfortably and raising them would eat into the
   board on a phone, which the fit-to-window layout cannot spare.) */
.play-nav .ghost-btn, .sound-btn { min-width: 44px; min-height: 44px; }
body.compact .play-nav .ghost-btn { padding: 6px 13px; min-height: 40px; }
body.compact .sound-btn { min-height: 40px; }

.board-wrap {
  position: relative;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 10px;
}

#board {
  display: block;
  width: 100%;
  height: auto;
  /* One finger drags pieces, so the board keeps the browser's hands off it -
     except for a two-finger pinch, so a page that has been zoomed can always
     be pinched back out, even with the board filling the screen. `none` is
     the fallback where pinch-zoom is not understood. */
  touch-action: none;
  touch-action: pinch-zoom;
  border-radius: 14px;
}

.mode-pill {
  position: absolute;
  left: 18px;
  top: 18px;
  background: #eef2f8;
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 6px 16px;
  font-size: 13.5px;
  font-weight: 500;
  white-space: nowrap;
  max-width: calc(100% - 28px);
  overflow: hidden;
  text-overflow: ellipsis;
  pointer-events: none;
}

.mode-pill.live { background: #e7f4ff; color: #1d4e89; }

.verdict {
  position: absolute;
  left: 50%;
  top: 14px;
  transform: translateX(-50%);
  border-radius: 999px;
  padding: 10px 22px;
  font-size: clamp(15px, 2.4vw, 18px);
  font-weight: 600;
  box-shadow: var(--shadow);
  max-width: 92%;
  text-align: center;
}

.verdict.good { background: #ddf6ee; color: #14695c; border: 2px solid #8fd8c6; }
.verdict.bad { background: #ffe3e0; color: #98211b; border: 2px solid #f2a7a1; }

/* --- paper levels ---------------------------------------------------------- */

.paper-card {
  position: relative;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: clamp(28px, 6vw, 56px) 20px;
  text-align: center;
  border: 3px dashed var(--line);
}

.paper-tag {
  display: inline-block;
  background: #eef2f8;
  color: var(--ink-soft);
  border-radius: 999px;
  padding: 5px 14px;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 18px;
}

.paper-eq {
  margin: 0 0 16px;
  font-size: clamp(34px, 8vw, 62px);
  font-weight: 700;
  letter-spacing: -1px;
}

.paper-note { margin: 0 auto; max-width: 46ch; color: var(--ink-soft); font-size: 15.5px; }

.answer-strip { margin-top: 14px; display: flex; justify-content: center; }

/* --- the build step -------------------------------------------------------- */

/* From lesson 2 the pans arrive empty and the student sets the problem up
   themselves, so the board needs a line telling them what they are aiming at
   and a button to say they are done. */
.build-bar {
  margin-top: 14px;
  background: #fff8ec;
  border: 2px solid #ffd9a8;
  border-radius: var(--radius);
  padding: 14px 18px;
  display: flex;
  align-items: center;
  gap: 14px 18px;
  flex-wrap: wrap;
}

.build-lead {
  margin: 0;
  flex: 1 1 280px;
  font-size: 15.5px;
  line-height: 1.45;
  color: #6b4410;
}

.build-lead b { color: var(--ink); }

.build-actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

.build-msg {
  margin: 0;
  flex: 1 0 100%;
  min-height: 0;
  font-size: 15px;
  color: var(--ink-soft);
}

.build-msg:empty { display: none; }
.build-msg.bad { color: var(--red); font-weight: 600; }

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

.controls {
  display: flex;
  gap: 14px;
  justify-content: space-between;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 14px;
}

.tray {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 8px 16px;
}

.tray-label { font-weight: 600; color: var(--ink-soft); font-size: 15px; }
.tray-hint { font-size: 12.5px; color: var(--ink-soft); }

.tray-chip {
  width: 46px;
  height: 46px;
  border: none;
  background: var(--ink);
  color: #fff;
  font-family: inherit;
  font-size: 19px;
  font-weight: 700;
  cursor: pointer;
  display: grid;
  place-items: center;
  transition: transform .08s ease;
}

.tray-chip:active { transform: scale(.92); }

/* Every control a child taps is at least 44x44. The tray chips had shrunk to
   36 under `compact` and the sign buttons were 30 - both under the size a
   finger reliably hits, and the tray chips are the main manipulative. The
   PAINTED chip can stay small; the touchable box is what grows. */
.tray-chip { position: relative; }
.tray-chip::after {
  content: '';
  position: absolute;
  left: 50%; top: 50%;
  width: 44px; height: 44px;
  transform: translate(-50%, -50%);
}

/* Check mode: the balance is free, so nothing can go on it. The tray fades
   rather than disappearing - a student needs to see that the pieces are still
   there, just not available while the pans are swinging. */
/* .32 put the glyph on a disabled red chip at 1.68:1 - the comment below said
   a student should still see the pieces are there, and at that ratio they
   could not. .55 keeps it clearly "off" while staying legible. */
.tray-chip:disabled { opacity: .55; cursor: not-allowed; }
.tray-chip:disabled:active { transform: none; }
body.held-still .tray-label,
body.held-still .tray-hint { opacity: .45; }
body.held-still .board-wrap #board { cursor: default; }
.tray-chip.red { background: var(--red); }
.tray-chip.hex { clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%); }
.tray-chip.sq { border-radius: 11px; }

/* The guess strip. Sits between the tray and the buttons, and takes the whole
   width of the row when there is not enough to sit beside them - thirteen
   numbers and a tray do not share a phone. */
.guesses {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: 999px;
  padding: 6px 12px;
}

.guesses-label {
  font-size: 13px;
  font-weight: 700;
  color: var(--ink-soft);
  margin-right: 2px;
}

.guess {
  position: relative;
  min-width: 34px;
  height: 34px;
  padding: 0 4px;
  border: 2px solid var(--line);
  border-radius: 9px;
  background: #fbfcff;
  color: var(--ink);
  font-family: inherit;
  font-size: 16px;
  font-weight: 700;
  cursor: pointer;
}

.guess:active { transform: scale(.92); }

/* The slash is drawn rather than typed, so it sits across the digit at a
   consistent angle whether the number is one character or two. */
.guess.crossed {
  color: var(--ink-soft);
  background: #eef2f8;
  border-color: #c3cede;
}

.guess.crossed::after {
  content: '';
  position: absolute;
  left: 4px; right: 4px; top: 50%;
  height: 3px;
  background: var(--red);
  border-radius: 2px;
  transform: rotate(-18deg);
}

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

/* --- sheets --------------------------------------------------------------- */

.sheet {
  position: fixed;
  inset: 0;
  background: rgba(27, 36, 64, 0.45);
  display: grid;
  place-items: center;
  padding: 16px;
  z-index: 60;
}

.sheet-card {
  position: relative;
  background: var(--card);
  border-radius: 24px;
  padding: clamp(20px, 4vw, 30px);
  width: min(560px, 100%);
  max-height: 88vh;
  overflow: auto;
  box-shadow: 0 24px 60px rgba(27, 36, 64, 0.35);
}

.sheet-card.narrow { width: min(330px, 100%); text-align: center; }

.sheet-card h3 { margin: 0 0 14px; font-size: 24px; }

.sheet-close {
  position: absolute;
  top: 12px; right: 14px;
  border: none;
  background: none;
  font-size: 30px;
  line-height: 1;
  color: var(--ink-soft);
  cursor: pointer;
  font-family: inherit;
}

.answer-row {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 22px;
  font-weight: 600;
  margin-bottom: 8px;
}

/* The two side totals, always side by side. As a wrapping flex row the second
   box dropped onto its own line on a phone and the balance lost a row it could
   not spare, so this is a two-column grid at every size. */
.answer-row.two {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 10px;
  font-size: 18px;
  /* A two-digit total does not need half a laptop screen. */
  max-width: 470px;
}

.side-field { display: flex; align-items: center; gap: 8px; min-width: 0; }
.side-field label { flex: 0 0 auto; font-weight: 600; }
.side-field .num-wrap { flex: 1 1 auto; min-width: 0; }
.side-field input.num { width: 100%; }

.num-wrap { position: relative; display: inline-flex; align-items: center; }

.sign-btn {
  position: absolute;
  right: 5px;
  border: none;
  background: #eef2f8;
  color: var(--ink-soft);
  border-radius: 10px;
  /* 44x44: it sits inside a text field, and a mis-tap silently negates the
     child's answer, so it needs to be hit on purpose rather than by accident. */
  width: 44px;
  height: 44px;
  font-family: inherit;
  font-size: 17px;
  font-weight: 700;
  cursor: pointer;
}

.sign-btn:active { background: #dde5f0; }

input.num, input.eq {
  font-family: inherit;
  font-size: 22px;
  font-weight: 600;
  color: var(--ink);
  border: 3px solid var(--line);
  border-radius: 14px;
  padding: 12px 56px 12px 14px;
  width: 150px;
  text-align: center;
  background: #fbfcff;
}

input.eq { width: auto; flex: 1 1 200px; text-align: left; font-size: 20px; }
/* The number fields used to drop the outline for a border-colour change
   alone - a colour-only focus cue on the three boxes a child types into. */
input.num:focus-visible, input.eq:focus-visible { border-color: var(--blue); }

/* One focus ring everywhere, instead of relying on the browser default, which
   is nearly invisible on the orange and green buttons. */
:where(button, a, input, [tabindex]):focus-visible {
  outline: 3px solid #1d4ed8;
  outline-offset: 3px;
  border-radius: 6px;
}
.tray-chip:focus-visible { outline-offset: 5px; }

.check-block {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 2px dashed var(--line);
}

.check-lead { margin: 0 0 12px; color: var(--ink-soft); font-size: 15px; line-height: 1.45; }
.check-lead code { background: #eef2f8; border-radius: 6px; padding: 1px 6px; font-size: 14px; }

.sheet-msg { margin: 14px 0 0; font-size: 16px; min-height: 22px; color: var(--ink-soft); }
.sheet-msg.bad { color: var(--red); font-weight: 600; }
.sheet-msg.good { color: var(--green); font-weight: 600; }

.sheet-actions { margin-top: 18px; display: flex; gap: 10px; justify-content: flex-end; flex-wrap: wrap; }

/* A button with a long label and a short one; the CSS picks which fits. */
.say-short { display: none; }

/* Putting the balance back is the quiet option, so it sits away from the one
   that submits the answer. */
#sheetResetBtn { margin-right: auto; }

/* --- the docked answer sheet ----------------------------------------------- */

/* Proving an answer means reading the balance while you type, so this one sits
   along the bottom instead of over the board, and the board shrinks to fit the
   room that is left (see --board-room, set in app.js). */
#answerSheet.docked {
  inset: auto 0 0 0;
  background: none;
  display: block;
  padding: 0;
  pointer-events: none;
}

#answerSheet.docked .sheet-card {
  pointer-events: auto;
  width: min(760px, 100%);
  margin: 0 auto;
  border: 2px solid var(--line);
  border-bottom: none;
  border-radius: 24px 24px 0 0;
  max-height: 54vh;
  padding: 18px clamp(18px, 4vw, 28px) 20px;
  box-shadow: 0 -18px 50px rgba(27, 36, 64, 0.28);
}

#answerSheet.docked h3 { font-size: 21px; margin-bottom: 10px; }
#answerSheet.docked .check-lead { font-size: 14.5px; margin-bottom: 10px; }
#answerSheet.docked .sheet-msg { margin-top: 10px; }
/* "Check my answer" sat below the fold on every phone size - the card scrolls
   inside itself and the button was the last thing in it, so at 812x375 only
   31% of the card was visible and the primary action was 178px past the edge.
   The action row is pinned to the bottom of the card instead: the middle
   scrolls under it, and the way out is always on screen. */
#answerSheet.docked .sheet-actions {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin-top: 14px;
  padding: 10px 0 2px;
  background: var(--card);
  box-shadow: 0 -12px 14px -10px rgba(27, 36, 64, 0.22);
}

body.answer-open .turn-hint { display: none; }

/* The tray and the buttons are not usable while an answer is being typed, so
   they step out of the way. They keep no space: the board takes it instead. */
body.answer-open .controls,
body.answer-open .build-bar,
body.answer-open .answer-strip { display: none; }

/* --- fitting the play screen to the screen -------------------------------- */

/* A student reaching for a piece at the bottom of the tray should not lose
   sight of the equation at the top, so while a puzzle is open the whole screen
   is laid out to the height of the window and the board takes whatever is
   left. Nothing scrolls unless the window is too small for even a small board,
   and then only as a last resort. */
/* The page itself becomes the column: top bar, then the play screen filling
   what is left. Nothing has to measure the top bar's height, which matters
   because it wraps onto three rows on a phone and changes height again when
   the compact rules below come on. */
body.playing {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  padding-bottom: 0;
}

body.playing .topbar { flex: 0 0 auto; }

body.playing #app {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

body.playing .screen.play {
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding-bottom: 8px;
  overflow: auto;
}

body.playing .play-head { margin: 2px 0 0; flex: 0 0 auto; }
body.playing .paper-card,
body.playing .build-bar,
body.playing .controls,
body.playing .answer-strip,
body.playing .turn-hint { flex: 0 0 auto; margin-top: 0; }

/* sizeBoard() sets the flex basis from the room the rest of the screen leaves.
   The auto margins then share out whatever is spare - which on a tall narrow
   screen is a lot, because the board runs out of width first - so the board
   sits centred and the tray stays down by the thumb. */
body.playing .board-wrap {
  flex: 1 1 auto;
  min-height: 130px;
  margin-block: auto;
  display: grid;
  place-items: center;
}

/* The canvas is 2:1 and its own intrinsic size would fight any attempt to let
   CSS fit it in both directions at once, so sizeBoard() in app.js measures the
   box and sets the pixels. */
body.playing .board-wrap #board {
  width: auto;
  height: auto;
  max-width: 100%;
  max-height: 100%;
}

/* When the room runs short, everything round the board gives up some of its
   size before the board does. */
body.compact .topbar { padding: 5px clamp(10px, 2vw, 20px); gap: 8px; }
/* The tighter padding must still leave room for the ZoodleBug Home button, or
   Back and the level chip slide under it (Tim, iPad on its side, 7 Oct 2026). */
body.compact .topbar { padding-left: var(--zb-home-space, 190px); }
/* Until the button has measured itself, guess its phone size on a phone. */
@media (max-width: 520px) {
  .topbar, body.compact .topbar { padding-left: var(--zb-home-space, 110px); }
}
body.compact .score,
body.compact .who,
body.compact .topbar .ghost-btn { padding: 4px 11px; font-size: 13px; }
body.compact .play-head { gap: 8px; margin: 0; }
/* Still far bigger than it was, but the compact screens are the ones fighting
   for height, so it gives some back. */
body.compact .equation { font-size: clamp(28px, 6vw, 44px); }
body.compact .next-level-btn { padding: 6px 13px; font-size: 14px; }
body.compact .as-ghost { font-size: clamp(15px, 2vw, 22px); }
body.compact .chip { padding: 4px 11px; font-size: 12.5px; }
body.compact .mode-pill { display: none; }
body.compact .board-wrap { padding: 5px; border-radius: 14px; }
body.compact .tray { padding: 5px 11px; gap: 7px; }
body.compact .guesses { padding: 4px 9px; gap: 4px; }
body.compact .guess { min-width: 29px; height: 29px; font-size: 14px; }
body.compact .guesses-label { display: none; }
body.compact .tray-chip { width: 36px; height: 36px; font-size: 15px; }
body.compact .tray-label,
body.compact .tray-hint,
body.compact .turn-hint { display: none; }
body.compact .big-btn,
body.compact .ghost-btn,
body.compact .check-btn { padding: 8px 17px; font-size: 15px; }
body.compact .build-bar { padding: 8px 13px; gap: 8px 12px; }
body.compact .build-lead { font-size: 13.5px; }
body.compact .build-msg { font-size: 13.5px; }
body.compact .verdict { padding: 7px 15px; }

/* Narrow and short at once - a phone held upright - is the tightest case there
   is, because the top bar and the play head both wrap onto two rows. */
body.playing.compact .screen.play { gap: 5px; padding-bottom: 5px; }
body.compact .controls { gap: 8px; }
body.compact .actions { gap: 8px; }
body.compact .build-bar { padding: 7px 11px; gap: 6px 10px; }
body.compact .build-actions { gap: 8px; }
body.compact .answer-strip .big-btn { padding: 9px 20px; }
body.compact .play-nav .ghost-btn { padding: 6px 13px; }
body.compact .back-btn { padding: 6px 12px; font-size: 14px; }

/* --- number pad ----------------------------------------------------------- */

.num-display {
  font-size: 42px;
  font-weight: 700;
  border: 3px solid var(--line);
  border-radius: 14px;
  padding: 6px;
  margin-bottom: 12px;
  background: #fbfcff;
}

.num-pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }

.num-key {
  font-family: inherit;
  font-size: 24px;
  font-weight: 600;
  padding: 14px 0;
  border: 2px solid var(--line);
  border-radius: 14px;
  background: #fff;
  cursor: pointer;
}

.num-key:active { background: #eef2f8; }
.num-key.soft { background: #f2f5fa; color: var(--ink-soft); }

/* --- my problems ---------------------------------------------------------- */

.own-list { list-style: none; margin: 16px 0 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }

.own-list li {
  display: flex;
  align-items: center;
  gap: 10px;
  background: #f7f9fd;
  border: 2px solid var(--line);
  border-radius: 12px;
  padding: 10px 14px;
}

.own-list li.empty { justify-content: center; color: var(--ink-soft); border-style: dashed; }
.own-eq { font-size: 18px; font-weight: 600; }

.own-del {
  margin-left: auto;
  border: none;
  background: none;
  color: var(--ink-soft);
  font-size: 22px;
  cursor: pointer;
  font-family: inherit;
  line-height: 1;
}

.own-del:hover { color: var(--red); }

/* --- confetti ------------------------------------------------------------- */

.confetti { position: fixed; inset: 0; pointer-events: none; overflow: hidden; z-index: 70; }

.confetti i {
  position: absolute;
  top: -20px;
  width: 11px;
  height: 16px;
  border-radius: 2px;
  animation: fall 2.2s linear forwards;
}

@keyframes fall {
  to { transform: translateY(110vh) rotate(680deg); opacity: 0; }
}

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

/* --- narrow screens ------------------------------------------------------- */

.turn-hint {
  display: none;
  margin: 10px 0 0;
  text-align: center;
  font-size: 14px;
  color: var(--ink-soft);
}

@media (max-width: 620px) {
  .as-ghost { display: none; }
  .hero { flex-direction: column; align-items: center; text-align: center; }
  .hero-say { align-items: center; text-align: center; }
  .hero-actions { justify-content: center; }
  .hero-logo { width: min(74vw, 320px); }
  .tray-hint, .mode-pill { display: none; }
  /* On a phone the answer sheet has to leave the balance visible - proving an
     answer means reading it while you type - so the boxes come down to the
     size a number actually needs. */
  #answerSheet.docked .sheet-card { max-height: 46vh; padding: 12px 14px 14px; }
  #answerSheet.docked h3 { font-size: 18px; margin-bottom: 8px; }
  #answerSheet.docked .check-lead { font-size: 13.5px; margin-bottom: 8px; line-height: 1.35; }
  #answerSheet.docked .check-block { margin-top: 12px; padding-top: 10px; }
  #answerSheet.docked .answer-row { font-size: 18px; margin-bottom: 0; }
  #answerSheet.docked .answer-row.two { gap: 8px; font-size: 16px; }
  /* The boxes are only as big as a number: two or three digits, never half the
     card. Everything they give up goes to the balance above them, which is the
     thing the child is reading while they type. */
  #answerSheet.docked input.num {
    font-size: 18px;
    padding: 6px 34px 6px 8px;
    border-width: 2px;
    border-radius: 11px;
  }
  #answerSheet.docked .answer-row.two { display: flex; gap: 16px; max-width: none; }
  #answerSheet.docked .side-field { flex: 0 0 auto; gap: 7px; }
  #answerSheet.docked .side-field .num-wrap { flex: 0 0 auto; }
  #answerSheet.docked .side-field input.num { width: 104px; }
  #answerSheet.docked .answer-row.one .num-wrap { flex: 0 0 auto; }
  #answerSheet.docked .answer-row.one input.num { width: 112px; }
  /* Still its own target - a mis-tap silently negates the child's answer - but
     sized to the smaller box rather than driving it. */
  #answerSheet.docked .sign-btn {
    width: 30px; height: 30px; right: 3px; font-size: 14px; border-radius: 8px;
  }
  /* Both on one row. Stacked, the action row was 96px of a 388px sheet - a
     row the board was paying for. */
  #answerSheet.docked .sheet-actions {
    margin-top: 10px;
    padding-top: 8px;
    flex-wrap: nowrap;
    gap: 8px;
    align-items: center;
  }
  #answerSheet.docked #sheetResetBtn { font-size: 14px; padding: 10px 16px; white-space: nowrap; }
  #answerSheet.docked #sheetResetBtn .say-long { display: none; }
  #answerSheet.docked #sheetResetBtn .say-short { display: inline; }
  #answerSheet.docked #submitBtn { font-size: 16px; padding: 11px 18px; white-space: nowrap; }
  #answerSheet.docked .sheet-msg { font-size: 14.5px; min-height: 18px; margin-top: 8px; }
  .build-bar { flex-direction: column; align-items: stretch; }
  .build-actions { justify-content: center; }
  /* `flex: 1 1 280px` sizes the width in a row and the HEIGHT in a column, so
     the basis has to go once the bar stacks or the lead swells to 280px tall. */
  .build-lead, .build-msg { flex: 0 0 auto; }
  .levels-count { position: static; transform: none; display: inline-block; margin-top: 8px; }
  .turn-hint { display: block; }
  .controls { flex-direction: column; align-items: stretch; }
  .tray { justify-content: center; border-radius: 18px; }
  .actions { justify-content: center; }
  .play-nav { margin-left: 0; }
}

/* --- the class dashboard --------------------------------------------------- */

.teacher-bar,
.assign-bar {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin-bottom: 12px;
}

.assign-bar { background: #fff8ec; border-color: #ffd9a8; }
.assign-now { margin: 0; flex: 1 1 260px; font-size: 15px; color: #6b4410; }
.assign-controls { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }

.teacher-label { font-weight: 600; color: var(--ink-soft); font-size: 15px; }

.teacher-select {
  font-family: inherit;
  font-size: 15px;
  padding: 9px 12px;
  border-radius: 12px;
  border: 2px solid var(--line);
  background: var(--card);
  color: var(--ink);
  max-width: 320px;
}

.teacher-select.narrow { max-width: 170px; }

.teacher-note { color: var(--ink-soft); font-size: 15.5px; }
.teacher-note.bad { color: var(--red); font-weight: 600; }
.teacher-summary { margin: 0 0 10px; color: var(--ink-soft); font-size: 14.5px; }

/* The grid is wide on purpose - one column per level - so it scrolls sideways
   inside its own box with the names pinned, rather than squeezing 29 columns
   into the page width and making every one unreadable. */
.grid-scroll {
  overflow: auto;
  max-height: 62vh;
  border: 2px solid var(--line);
  border-radius: var(--radius);
  background: var(--card);
}

.class-grid { border-collapse: separate; border-spacing: 0; font-size: 13.5px; }

.class-grid th,
.class-grid td {
  padding: 0;
  text-align: center;
  border-bottom: 1px solid var(--line);
  background: var(--card);
}

.class-grid thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  height: 34px;
  font-weight: 600;
  color: var(--ink-soft);
  border-bottom: 2px solid var(--line);
}

.sticky-col {
  position: sticky;
  left: 0;
  z-index: 3;
  min-width: 158px;
  max-width: 158px;
  text-align: left !important;
  padding: 6px 10px !important;
  border-right: 2px solid var(--line);
}

.class-grid thead .sticky-col { z-index: 4; }

.student-btn {
  border: none;
  background: none;
  font-family: inherit;
  font-size: 14.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  padding: 0;
  text-align: left;
  width: 100%;
}

.student-btn:hover { color: var(--blue); text-decoration: underline; }
.not-started { display: block; font-size: 11.5px; color: var(--ink-soft); font-weight: 400; }

.total-col { min-width: 46px; font-weight: 700; color: var(--ink-soft); }
.lvl-col { min-width: 30px; max-width: 30px; font-size: 12px; }

.lvl-cell {
  min-width: 30px;
  max-width: 30px;
  height: 32px;
  font-weight: 600;
  color: #1b5e52;
}

/* Read the class by colour first, by number second. */
.lvl-cell.some { background: #e4f5f0; }
.lvl-cell.most { background: #bfe9dd; }
.lvl-cell.full { background: #6fcdb6; color: #06382e; }
.lvl-cell.stuck { box-shadow: inset 0 -4px 0 var(--gold); }

.lvl-col.assigned, .lvl-cell.assigned { outline: 2px solid var(--orange); outline-offset: -2px; }
.class-grid tr.open th, .class-grid tr.open td { background: #eef4ff; }

/* --- one student opened up ------------------------------------------------- */

.student-detail {
  margin-top: 14px;
  background: var(--card);
  border: 2px solid var(--line);
  border-radius: var(--radius);
  padding: 16px 18px;
  box-shadow: var(--shadow);
}

.detail-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.detail-head h3 { margin: 0; font-size: 21px; }
.detail-when { font-size: 13.5px; color: var(--ink-soft); }
.detail-lead { margin: 8px 0 14px; font-size: 15.5px; line-height: 1.45; }

.detail-levels { display: flex; flex-direction: column; gap: 7px; }
.detail-level { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.detail-level-name { flex: 0 0 210px; font-size: 14px; color: var(--ink-soft); }
.detail-chips { display: flex; gap: 6px; flex-wrap: wrap; }

.chip-p {
  min-width: 34px;
  padding: 4px 7px;
  border-radius: 9px;
  background: #eef2f8;
  color: var(--ink-soft);
  font-size: 12.5px;
  font-weight: 600;
  text-align: center;
}

.chip-p.solved { background: #ddf6ee; color: #14695c; }
.chip-p.solved-hard { background: #ffe9c2; color: #8a5200; }
.chip-p.onit { background: #ffe3e0; color: #98211b; }

.detail-key { margin: 14px 0 0; font-size: 12.5px; color: var(--ink-soft); }
.detail-key .chip-p { display: inline-block; min-width: 20px; margin-right: 3px; }

/* --- what a student has been set ------------------------------------------- */

.assigned-card {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  background: #fff8ec;
  border: 3px solid var(--gold);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-top: 18px;
}

.assigned-tag {
  background: var(--orange);
  color: #fff;
  border-radius: 999px;
  padding: 5px 14px;
  font-size: 13px;
  font-weight: 600;
}

.assigned-what { margin: 0; flex: 1 1 220px; font-size: 17px; font-weight: 600; }

@media (max-width: 620px) {
  .detail-level-name { flex: 1 1 100%; }
  .grid-scroll { max-height: 56vh; }
  .sticky-col { min-width: 120px; max-width: 120px; }
}

/* --- working together ------------------------------------------------------ */

/* The bar now sits above the screens rather than inside the play one, because
   a shared session outlives any one puzzle: the host can walk the guest from
   the level list to a puzzle and on to the next without it disappearing. */
.live-bar {
  /* Centred on the same 1180px column as a screen, with the same gutters. */
  max-width: 1180px;
  width: calc(100% - 2 * clamp(14px, 3vw, 30px));
  margin: 0 auto 10px;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  background: #eef4ff;
  border: 2px solid #b9cdf5;
  border-radius: var(--radius);
  padding: 10px 16px;
}

.live-what { margin: 0; flex: 1 1 240px; font-size: 15px; color: #1d3f7d; font-weight: 600; }

.live-dot { width: 12px; height: 12px; border-radius: 50%; background: #9aa6bd; flex: 0 0 auto; }
.live-dot.on { background: var(--green); box-shadow: 0 0 0 4px rgba(42, 157, 143, 0.22); }
.live-dot.waiting { background: var(--gold); animation: livePulse 1.4s ease-in-out infinite; }

@keyframes livePulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(255, 209, 102, 0.7); }
  50% { box-shadow: 0 0 0 7px rgba(255, 209, 102, 0); }
}

.join-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  flex: 1 1 100%;
  margin-top: 4px;
}

.join-row label { font-size: 15px; font-weight: 600; color: var(--ink-soft); }

.code-input {
  font-family: inherit;
  font-size: 20px;
  font-weight: 700;
  letter-spacing: 4px;
  text-transform: uppercase;
  width: 130px;
  padding: 9px 12px;
  border-radius: 12px;
  border: 2px solid var(--line);
  text-align: center;
}

.join-msg { font-size: 14px; color: var(--ink-soft); }

/* On the play screen the column owns the spacing, and the bar is a flex item
   in it like everything else. */
body.playing .live-bar {
  flex: 0 0 auto;
  width: calc(100% - 2 * clamp(10px, 2vw, 20px));
  margin: 0 auto 8px;
}
body.compact .live-bar { padding: 7px 12px; }
body.compact .live-what { font-size: 13.5px; }

/* On a short screen the sheet has to give the board more room than it does
   elsewhere, or the two meet in the middle. It scrolls inside itself. This
   sits at the end on purpose: the narrow-screen block above also sets a
   max-height, and at equal specificity the last one wins. */
@media (max-height: 700px) {
  #answerSheet.docked .sheet-card { max-height: 44vh; }
}

/* Phone landscape: 375px of height, and the sheet was showing its title and
   almost nothing else. The action row is pinned so the way out is always
   there, but the X box has to be reachable without a scroll too, so the
   sheet takes a larger share and everything inside it tightens up. The board
   is only a couple of hundred pixels here whatever we do. */
/* The equation is deliberately huge, but on a phone held sideways there are
   only 375px of height for the whole play screen and it pushes the buttons off
   the bottom. Height, not width, is what has to decide here. */
@media (max-height: 560px) {
  .equation, body.compact .equation { font-size: clamp(22px, 3.4vw, 30px); }
  .guesses { padding: 3px 8px; gap: 3px; }
  .guess { min-width: 26px; height: 26px; font-size: 13px; }
}

@media (max-height: 480px) {
  #answerSheet.docked .sheet-card {
    max-height: 66vh;
    padding: 10px clamp(14px, 3vw, 20px) 12px;
  }
  #answerSheet.docked h3 { font-size: 17px; margin-bottom: 6px; }
  #answerSheet.docked .check-lead { font-size: 13px; margin-bottom: 6px; }
  #answerSheet.docked .answer-row { margin: 0; }
  #answerSheet.docked .check-block { margin-top: 10px; padding-top: 10px; }
  #answerSheet.docked .sheet-actions { margin-top: 8px; padding-top: 8px; }
  #answerSheet.docked input.num { padding-top: 5px; padding-bottom: 5px; font-size: 18px; }
  #answerSheet.docked .sign-btn { width: 28px; height: 28px; }
}

/* --- the finished-level screen -------------------------------------------
   The one moment in the app that has earned a fuss. The card carries the
   level's own tint, so finishing level 3 and finishing level 27 do not look
   like the same event. */

/* The splash fills the window and never scrolls: the page becomes a flex
   column, the card is the stretching child, and inside the card the mascot is
   the stretching child. So the mascot is always as big as the room left over
   by the words and the buttons, at any screen size, with no measuring in
   JavaScript. */
body.splash {
  display: flex;
  flex-direction: column;
  height: 100dvh;
  overflow: hidden;
  padding-bottom: 0;
}
body.splash .topbar { flex: 0 0 auto; }
body.splash #app { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }

.done-screen {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: clamp(8px, 2vh, 18px);
}

.done-card {
  --tint: #4361ee;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  display: flex;
  flex-direction: column;
  /* The card grows with the screen rather than sitting as a small postcard in
     the middle of a big monitor. */
  width: min(100%, clamp(560px, 46vw, 820px));
  max-height: 100%;
  text-align: center;
  background: var(--card);
  border: 4px solid var(--tint);
  border-radius: 28px;
  padding: clamp(12px, 2.2vh, 26px) 26px clamp(16px, 2.6vh, 30px);
  box-shadow: var(--shadow);
  animation: done-pop .45s cubic-bezier(.2, 1.3, .5, 1) both;
}

/* Everything except the mascot keeps its own size; the mascot takes the rest. */
.done-card > *:not(.done-mascot):not(.done-rays) { flex: 0 0 auto; }

@keyframes done-pop {
  from { opacity: 0; transform: scale(.88) translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* A slow wheel of colour behind everything, tinted to the level. */
.done-rays {
  position: absolute;
  inset: -60%;
  z-index: -1;
  opacity: .14;
  background: repeating-conic-gradient(
    from 0deg,
    var(--tint) 0deg 14deg,
    transparent 14deg 28deg);
  animation: done-spin 28s linear infinite;
}

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

/* Him, as big as the screen allows. `min-height: 0` is what lets a flex child
   shrink below its own picture; `object-fit: contain` keeps him in proportion
   whatever shape the space ends up. */
.done-mascot {
  /* As tall as the window allows, and allowed to shrink when it does not.
     `flex-shrink: 1` with a viewport height is what makes him the thing that
     gives way on a small screen rather than the buttons. The cap keeps him
     close to the artwork's own size, so he never goes soft. */
  flex: 0 1 auto;
  height: min(46vh, 520px);
  min-height: 90px;
  width: 100%;
  object-fit: contain;
  margin-bottom: clamp(2px, 1vh, 10px);
  animation: done-bob 2.6s ease-in-out infinite;
}

@keyframes done-bob {
  0%, 100% { transform: translateY(0) rotate(-1deg); }
  50%      { transform: translateY(-9px) rotate(1deg); }
}

.done-hooray {
  margin: 0;
  font-size: clamp(22px, 5vw, 30px);
  font-weight: 700;
  color: var(--orange-btn);
  letter-spacing: .01em;
}

.done-what {
  margin: 2px 0 0;
  /* One line, always: "You finished Level 29" broken over two lines reads as a
     mistake rather than as a fanfare. */
  white-space: nowrap;
  font-size: clamp(22px, 5.2vw, 40px);
  font-weight: 700;
  color: var(--tint);
  line-height: 1.1;
}

.done-name {
  margin: 4px 0 0;
  font-size: clamp(16px, 3.6vw, 21px);
  font-weight: 600;
  color: var(--ink-soft);
}

.done-stars {
  display: flex;
  justify-content: center;
  gap: 6px;
  margin: clamp(6px, 1.6vh, 16px) 0 clamp(4px, 1vh, 10px);
  font-size: clamp(22px, 5vw, 30px);
  line-height: 1;
}

.done-star {
  color: var(--gold);
  text-shadow: 0 2px 0 rgba(27, 36, 64, .18);
  animation: done-drop .5s cubic-bezier(.2, 1.5, .5, 1) both;
}

.done-star.dim { color: var(--line); text-shadow: none; }

@keyframes done-drop {
  from { opacity: 0; transform: translateY(-22px) scale(.4) rotate(-40deg); }
  to   { opacity: 1; transform: none; }
}

.done-tally {
  margin: 0 0 clamp(10px, 2.2vh, 22px);
  font-size: 14.5px;
  color: var(--ink-soft);
}

/* All three on one line: the order left to right is the message, and a button
   that wraps onto its own row stops being third of three. */
.done-actions {
  display: flex;
  flex-wrap: nowrap;
  justify-content: center;
  align-items: center;
  gap: 10px;
}
.done-actions .big-btn { padding: 13px 18px; font-size: 17px; white-space: nowrap; }

/* Three fixed colours, the same on every splash screen whatever the level's
   own tint is: purple all the way out, blue back to this level, green onward.
   A child who has seen two of these knows which one is which without reading
   them. All three carry white words, so all three are the darker relatives -
   see the note on --orange-btn at the top. */
.done-actions .done-home {
  background: #6a1fa8;
  border-color: #4d1479;
  box-shadow: 0 5px 0 #43106a;
}
.done-actions .done-home:active { box-shadow: 0 2px 0 #43106a; }

.done-actions .done-here {
  background: #3651c9;
  border-color: #2b40a1;
  box-shadow: 0 5px 0 #24378c;
}
.done-actions .done-here:active { box-shadow: 0 2px 0 #24378c; }

/* The way onward is the biggest thing here, and the last thing on the row. */
.done-actions .done-go {
  background: var(--green-btn);
  border-color: #114b44;
  box-shadow: 0 5px 0 #0e3f39;
  font-size: 19px;
  padding: 15px 24px;
}
.done-actions .done-go:active { box-shadow: 0 2px 0 #0e3f39; }

/* On a phone the three buttons stacked take a third of the screen, which is a
   third the mascot does not get. The way onward keeps its own row; the two
   lobbies share the next one. */
@media (max-width: 520px) {
  .done-card { padding: 12px 12px 16px; border-radius: 22px; }
  .done-what { font-size: clamp(21px, 7vw, 31px); white-space: nowrap; }
  .done-actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 9px;
  }
  .done-actions .big-btn { padding: 11px 10px; font-size: 15.5px; }
  /* The two lobbies side by side, the way onward across the bottom. */
  .done-actions .done-go { grid-column: 1 / -1; padding: 13px 22px; font-size: 18px; }
}

/* A short screen - a phone held sideways, or a small laptop - has nothing to
   give away, so the trimmings go first and the mascot keeps what is left. */
@media (max-height: 700px) {
  .done-stars { display: none; }
  .done-tally { font-size: 12.5px; margin-bottom: 10px; }
  .done-hooray { font-size: clamp(15px, 3.4vw, 20px); }
  .done-name { font-size: 15px; }
}

@media (max-height: 470px) {
  .done-tally, .done-name { display: none; }
  .done-what { font-size: clamp(20px, 5vw, 28px); }
}

/* Sideways, there is width to spare and no height at all, so the card turns
   into two columns: him down the left at full height, the words and the way
   onward beside him. */
@media (max-height: 620px) and (min-width: 620px) {
  .done-card {
    display: grid;
    grid-template-columns: minmax(110px, 30%) 1fr;
    /* Five explicit rows - hooray, heading, name, tally, buttons - because the
       mascot spans them with `1 / -1`, and `-1` counts lines in the EXPLICIT
       grid. Without these it means line 1, the mascot lands in row 1 alone and
       drives the whole card taller than the screen. */
    grid-template-rows: repeat(5, auto);
    align-content: center;
    align-items: center;
    column-gap: clamp(14px, 3vw, 26px);
    text-align: left;
    padding: 12px 20px;
  }
  .done-mascot {
    grid-column: 1;
    grid-row: 1 / -1;
    height: 100%;
    min-height: 0;
    max-height: 100%;
    margin: 0;
  }
  .done-hooray, .done-what, .done-name, .done-tally, .done-actions { grid-column: 2; }
  .done-name, .done-tally { display: block; }
  .done-hooray { font-size: 17px; }
  .done-what { font-size: clamp(20px, 3vw, 31px); }
  .done-name { font-size: 14.5px; }
  .done-tally { font-size: 12px; margin-bottom: 12px; }
  .done-stars { display: none; }
  /* All three on one line, or the buttons take the height the mascot needs. */
  .done-actions { justify-content: flex-start; gap: 8px; flex-wrap: nowrap; }
  .done-actions .big-btn { padding: 9px 12px; font-size: 14px; white-space: nowrap; }
  .done-actions .done-home { box-shadow: 0 4px 0 #43106a; }
  .done-actions .done-here { box-shadow: 0 4px 0 #24378c; }
  .done-actions .done-go { padding: 9px 14px; font-size: 15px; box-shadow: 0 4px 0 #0e3f39; }
}

/* The top bar is two rows on a narrow phone. On the splash that is the mascot's
   room, so it gets one compact row instead. */
body.splash .topbar { padding-block: 6px; }
@media (max-width: 520px) {
  body.splash .home-btn, body.splash .who, body.splash .score {
    font-size: 13px; padding: 6px 10px;
  }
}

/* Nothing here has to move for anyone who would rather it did not. */
@media (prefers-reduced-motion: reduce) {
  .done-card, .done-mascot, .done-rays, .done-star { animation: none; }
}

/* --- 24 Sep 2026: progress counter, bigger build text, picking several,
       and some ZoodleBug colour on the puzzle screen -------------------------- */

/* "3 of 6", under the two arrows. */
.nav-arrows { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.nav-row { display: flex; gap: 8px; }
.nav-count {
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  line-height: 1.1;
  white-space: nowrap;
}
body.compact .nav-count { font-size: 12.5px; }

/* The build instruction was 15.5px and squeezed onto one line. It gets two
   lines and a size a child can read from arm's length. */
.build-lead {
  font-size: clamp(18px, 2.1vw, 22px);
  line-height: 1.3;
  font-weight: 500;
}
body.compact .build-lead { font-size: 19px; line-height: 1.25; }
@media (max-width: 620px) {
  body.compact .build-lead { font-size: 16.5px; }
}
/* A phone on its side has almost no height to give. */
@media (max-height: 450px) {
  body.compact .build-lead { font-size: 15.5px; }
}

/* The level's own colour runs along the top of the board, so the puzzle
   screen carries the same tint as the level card it came from. */
/* An inset shadow rather than a border: a border would eat into the room
   sizeBoard() has already measured for the canvas. */
body.playing .board-wrap {
  box-shadow: inset 0 6px 0 var(--tint, var(--green)), var(--shadow);
  background: linear-gradient(180deg, #f2f8ff 0%, #ffffff 42%);
}
body.playing.compact .board-wrap {
  box-shadow: inset 0 4px 0 var(--tint, var(--green)), var(--shadow);
}

/* The mascot in the board's empty top corner - above the right-hand pan's
   string, where nothing is ever drawn. Decoration only. */
.board-bug {
  position: absolute;
  right: 2.2%;
  top: 6%;
  height: 32%;
  max-height: 170px;
  width: auto;
  pointer-events: none;
  user-select: none;
  filter: drop-shadow(0 4px 6px rgba(27, 36, 64, 0.18));
  transform-origin: 50% 100%;
  animation: bug-sway 4.5s ease-in-out infinite;
}
.board-bug.cheer { animation: bug-cheer 0.9s ease-out 2, bug-sway 4.5s ease-in-out 1.8s infinite; }
@keyframes bug-sway {
  0%, 100% { transform: rotate(-3deg); }
  50% { transform: rotate(3deg); }
}
@keyframes bug-cheer {
  0% { transform: translateY(0) scale(1); }
  30% { transform: translateY(-22%) scale(1.08) rotate(-6deg); }
  60% { transform: translateY(0) scale(0.96); }
  100% { transform: translateY(0) scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .board-bug, .board-bug.cheer { animation: none; }
}
body.answer-open .board-bug { display: none; }

/* A picked group: how many, and a Clear button. */
.picked-bar {
  position: absolute;
  left: 18px;
  top: 54px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 10px;
  background: #fff6d6;
  border: 2px solid #f7b801;
  color: #6b4e00;
  border-radius: 999px;
  padding: 4px 6px 4px 14px;
  font-size: 14px;
  font-weight: 600;
  box-shadow: var(--shadow);
  max-width: calc(100% - 36px);
}
body.compact .picked-bar { top: 10px; left: 10px; font-size: 13px; }
.picked-clear {
  font: inherit;
  border: 0;
  border-radius: 999px;
  background: #f7b801;
  color: #3a2a00;
  padding: 6px 14px;
  min-height: 32px;
  cursor: pointer;
}

/* The answer button and sheet get some personality too. */
.answer-strip .big-btn::before,
.board-row .big-btn::before {
  content: 'X';
  display: inline-grid;
  place-items: center;
  width: 1.5em;
  height: 1.5em;
  margin-right: 0.5em;
  font-size: 0.8em;
  color: #fff;
  background: var(--ink);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  vertical-align: middle;
}
#answerSheet .sheet-card::before {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 7px;
  background: linear-gradient(90deg, #34a0a4, #2d7dd2, #5a4fcf, #b5179e, #f77f00);
  pointer-events: none;
}
#answerTitle { color: #2d3a8c; }

/* --- 25 Sep 2026: the top row on a puzzle, the X piece in words, and the
       "select more pieces" tip -------------------------------------------- */

/* Back and the level go in the top row, only on a puzzle. */
.topbar-play { display: none; }
body.playing .topbar-play {
  display: flex;
  align-items: center;
  gap: 10px;
  flex: 1 1 auto;
  min-width: 0;
  order: 0;
}
body.playing .topbar { justify-content: flex-start; }
body.playing .topbar-right { margin-left: auto; }
.topbar-play .back-btn { padding: 6px 4px; }
.play-level-name {
  font-size: 14px;
  font-weight: 500;
  color: var(--ink-soft);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
body.compact .play-level-name { font-size: 12.5px; }
body.compact .topbar-play { gap: 7px; }
@media (max-width: 480px) {
  .play-level-name { display: none; }
}

/* The X piece, anywhere AlgeBug says what x is: the same hexagon as on the
   board, never a typed letter. `.neg` is the red one. */
.x-piece {
  display: inline-grid;
  place-items: center;
  width: 1.55em;
  height: 1.7em;
  vertical-align: -0.45em;
  font-size: 0.85em;
  font-weight: 700;
  line-height: 1;
  color: #fff;
  background: var(--ink);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}
.x-piece.neg { background: var(--red); }
.x-equals { white-space: nowrap; font-weight: 700; }
/* In a sentence, [X] = 5 sits centred on the line of text around it. Lowered
   by a fixed amount it dropped below the words next to it (Tim, 27 Sep 2026). */
.x-equals {
  display: inline-flex;
  align-items: center;
  gap: 0.2em;
  vertical-align: middle;
  line-height: 1;
}
.x-equals .x-piece { vertical-align: 0; }
.answer-row label .x-piece { font-size: 0.8em; margin-right: 0.15em; }

/* "Ctrl + Click to select more pieces" - top left of the board, where the
   picked-count bar appears once they do it. */
.pick-tip {
  position: absolute;
  left: 18px;
  top: 54px;
  z-index: 2;
  display: flex;
  align-items: center;
  gap: 6px;
  background: #fffbea;
  border: 2px dashed #f7b801;
  color: #6b4e00;
  border-radius: 999px;
  padding: 3px 4px 3px 12px;
  font-size: 13.5px;
  font-weight: 600;
  max-width: calc(100% - 36px);
}
body.compact .pick-tip { top: 10px; left: 10px; font-size: 12.5px; }
.pick-tip-close {
  font: inherit;
  font-size: 16px;
  line-height: 1;
  border: 0;
  background: none;
  color: #6b4e00;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  cursor: pointer;
}
.pick-tip-close:hover { background: #fdecb0; }
body.held-still .pick-tip { display: none; }

/* --- 26 Sep 2026: the two tray boxes sit evenly across the answer sheet ----
   They used to be a 470px-wide grid pinned to the left of the sheet, leaving a
   gap on the right. Now the two tray fields share the full width evenly, with
   room on both outer edges, and wrap onto two centred rows only when a phone
   is too narrow for both. */
#answerSheet .answer-row.two {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  align-items: center;
  gap: 10px 24px;
  max-width: none;
  padding-inline: clamp(8px, 4vw, 36px);
}
#answerSheet .side-field { flex: 0 0 auto; }
#answerSheet .side-field .num-wrap { flex: 0 0 auto; }
#answerSheet .side-field input.num { width: 150px; }
@media (max-width: 620px) {
  #answerSheet.docked .answer-row.two { gap: 8px 12px; padding-inline: 0; }
  #answerSheet.docked .side-field input.num { width: 104px; }
}
/* A phone held upright: slimmer boxes so both trays still share one row. */
@media (max-width: 430px) {
  #answerSheet.docked .side-field { gap: 5px; }
  #answerSheet.docked .answer-row.two { column-gap: 6px; }
  #answerSheet.docked .side-field input.num { width: 78px; }
}

/* --- 26 Sep 2026: the puzzle screen rearranged ------------------------------
   Top row: Undo / Reset / Work together on the left, the equation in the
   middle, the puzzle arrows on the right. The pieces sit on the board under
   the base of the balance, with "I know X!" at the left margin and Check at
   the right (see placeRow in app.js). */

body.playing .play-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: 14px;
}
.head-actions {
  grid-column: 1;
  grid-row: 1;
  justify-self: start;
  display: flex;
  gap: 8px;
  flex-wrap: wrap;
  align-items: center;
}
body.playing .play-title { grid-column: 2; grid-row: 1; justify-self: center; }
body.playing .play-nav { grid-column: 3; grid-row: 1; justify-self: end; margin-left: 0; }
body.playing .as-ghost { grid-column: 3; grid-row: 1; justify-self: start; margin: 0; }
.head-actions .ghost-btn { white-space: nowrap; }
body.compact .head-actions { gap: 6px; }

/* A phone: the equation and the arrows share the first row, the three
   buttons get the second. */
@media (max-width: 620px) {
  body.playing .play-head { grid-template-columns: minmax(0, 1fr) auto; row-gap: 6px; }
  body.playing .play-title { grid-column: 1; justify-self: start; }
  body.playing .play-nav { grid-column: 2; }
  .head-actions { grid-column: 1 / -1; grid-row: 2; justify-self: center; }
}

/* The row on the board. It is laid over the canvas in board coordinates by
   positionRow(), so everything in it scales with the balance. */
.board-row {
  position: absolute;
  z-index: 1;
  display: flex;
  align-items: center;
  justify-content: space-between;
  pointer-events: none;
}
.board-row > * { pointer-events: auto; }
.board-row .tray {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  flex-wrap: nowrap;
  gap: calc(var(--row-h, 50px) * 0.16);
  padding: 3px calc(var(--row-h, 50px) * 0.2);
  box-shadow: 0 2px 8px rgba(27, 36, 64, 0.08);
}
.board-row .tray-label,
.board-row .tray-hint { display: none; }
.board-row .tray-chip {
  width: clamp(30px, calc(var(--row-h, 50px) * 0.74), 50px);
  height: clamp(30px, calc(var(--row-h, 50px) * 0.74), 50px);
  font-size: clamp(14px, calc(var(--row-h, 50px) * 0.32), 20px);
}
.board-row .big-btn,
.board-row .check-btn {
  height: clamp(34px, calc(var(--row-h, 50px) * 0.84), 54px);
  padding: 0 clamp(14px, calc(var(--row-h, 50px) * 0.4), 26px);
  font-size: clamp(14px, calc(var(--row-h, 50px) * 0.34), 19px);
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
}
body.answer-open .board-row { visibility: hidden; }

/* The build bar sits a little lower, clear of the board. */
body.playing .build-bar { margin-top: 6px; }

/* An upright tablet: the equation keeps the middle of the first row and the
   three buttons drop to a row of their own, on the left. Squeezed beside a
   big equation they stacked on top of each other. */
@media (min-width: 621px) and (max-width: 900px) {
  .head-actions { grid-row: 2; grid-column: 1 / -1; justify-self: start; flex-wrap: nowrap; }
  body.playing .play-head { row-gap: 4px; }
}
.board-row .big-btn,
.board-row .check-btn { height: clamp(34px, calc(var(--row-h, 50px) * 0.78), 52px); }

/* --- review puzzles, the mat and the answer boxes (27 Sep 2026) ----------- */

/* A review puzzle says which earlier level it comes from - on its tile and
   beside the equation while it is being played. */
.tile-review {
  position: absolute;
  top: 5px;
  right: 10px;
  font-size: 11.5px;
  font-weight: 600;
  color: var(--ink-soft);
  letter-spacing: 0.02em;
}
.problem-tile.review { border-style: dashed; padding-top: 20px; }
.review-tag {
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  background: #eef2f8;
  border-radius: 999px;
  padding: 3px 10px;
  white-space: nowrap;
}

/* Evaluate: the value they are given, then the expression. */
.ask-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 16px;
  margin: 0 0 10px;
  font-size: 20px;
  font-weight: 600;
}
.ask-line .ask-given {
  background: #fff4d6;
  border-radius: 10px;
  padding: 2px 10px;
}

/* Two X boxes side by side where there is room; the second one wraps under the
   first on a narrow phone rather than squeezing either. */
#x2Row:not([hidden]) { margin-top: 6px; }
@media (min-width: 560px) {
  #answerSheet .sheet-card:has(#x2Row:not([hidden])) .answer-row.one {
    display: inline-flex;
    margin-right: 22px;
  }
}

/* Long equations - the later levels have plenty - step down a size or two. */
.play-title .equation.long { font-size: clamp(28px, 5.6vw, 54px); }
.play-title .equation.longer { font-size: clamp(22px, 4.2vw, 40px); letter-spacing: -0.5px; }
.play-title .equation.longest { font-size: clamp(20px, 3.3vw, 34px); }

/* --- the Continue card, after a solve (27 Sep 2026) ------------------------ */
.solved-card {
  position: fixed;
  left: 50%;
  /* Set by placeSolved() in app.js: centred in the room under the board, or
     tucked inside the board's bottom edge when there is no room there. */
  bottom: 40px;
  transform: translateX(-50%);
  z-index: 40;
  width: min(560px, calc(100vw - 32px));
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 10px 18px;
  padding: 14px 18px;
  background: #ddf6ee;
  color: #14695c;
  border: 3px solid #8fd8c6;
  border-radius: 20px;
  box-shadow: 0 12px 30px rgba(20, 105, 92, 0.22);
  animation: solved-in .25s ease-out;
}
.solved-card[hidden] { display: none !important; }
.solved-msg { margin: 0; font-size: 21px; font-weight: 700; text-align: center; flex: 1 1 260px;
              line-height: 1.5; }
.solved-note { margin: 0; flex: 1 1 100%; text-align: center; font-weight: 600; font-size: 15px; }
.solved-card .big-btn { flex: 0 0 auto; }
@keyframes solved-in { from { opacity: 0; transform: translate(-50%, 16px); } }
/* The tray, answer button and Check are no use once solved; the card sits
   where they were rather than on top of them. */
body.solved-open .tray,
body.solved-open .answer-strip,
body.solved-open #checkBtn,
body.solved-open #answerBtn,
body.solved-open .guesses { visibility: hidden; }

/* --- the login gate, for guests (27 Sep 2026) ------------------------------ */
.login-gate { display: grid; place-items: center; min-height: 70vh; padding: 16px; }
.login-gate[hidden] { display: none !important; }
.login-card {
  max-width: 460px;
  text-align: center;
  background: var(--card);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 28px 24px 22px;
}
.login-logo { width: min(260px, 70%); height: auto; }
.login-card h2 { margin: 12px 0 8px; font-size: 26px; }
.login-card p { color: var(--ink-soft); font-size: 17px; line-height: 1.45; margin: 0 0 18px; }
.login-card .big-btn { display: inline-block; text-decoration: none; }
.login-home { margin: 16px 0 0 !important; font-size: 15px !important; }

/* --- the Free Balance (5 Oct 2026) ----------------------------------------- */
.big-btn.free-btn { background: #2a9d8f; border-color: #1f7a6f; box-shadow: 0 5px 0 #1b6a60; }
.big-btn.free-btn:active { box-shadow: 0 2px 0 #1b6a60; }
/* One puzzle at a time: no arrows, no "1 of 1", no level name beside the chip. */
body.free-balance .nav-arrows { visibility: hidden; }

/* Typing an equation in on the Free Balance. */
.type-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 10px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 2px dashed #f0d9a8;
}
.type-row[hidden] { display: none !important; }
.type-label { font-weight: 600; }
.type-input {
  flex: 1 1 220px;
  min-width: 0;
  font: inherit;
  font-size: 20px;
  font-weight: 600;
  padding: 8px 12px;
  border: 2px solid var(--line);
  border-radius: 12px;
}
.type-keys { display: flex; flex-wrap: wrap; gap: 6px; }
.type-key {
  font: inherit;
  font-weight: 700;
  font-size: 18px;
  min-width: 42px;
  padding: 6px 10px;
  border: 2px solid var(--line);
  border-radius: 10px;
  background: #fff;
  cursor: pointer;
}

/* --- the Free Balance layout (6 Oct 2026) ---------------------------------- */
/* Tim: "the balance is so small and there is a ton more width". The Free
   Balance has one board and nothing else to look at, so it may use a wide
   screen's full width. */
body.playing.free-balance .screen.play { max-width: 1680px; }

/* Two ways in, side by side under the board: build it | or | type it. */
.free-ways {
  flex: 1 1 100%;
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1.5fr);
  gap: 12px 16px;
  align-items: stretch;
}
.free-ways[hidden] { display: none !important; }
.free-way {
  display: flex;
  flex-direction: column;
  gap: 8px;
  background: #fff;
  border: 2px solid #f0d9a8;
  border-radius: 16px;
  padding: 12px 14px;
  min-width: 0;
}
.free-way.type-row {
  margin: 0;
  padding-top: 12px;
  border-top: 2px solid #f0d9a8;
  flex-wrap: nowrap;
  align-items: stretch;
}
.free-way-title {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 18px;
  color: var(--ink);
}
.free-way-title label { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }
.way-num {
  display: inline-grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: #2a9d8f;
  color: #fff;
  font-size: 15px;
  font-weight: 700;
}
.free-way-text { margin: 0; font-size: 15px; line-height: 1.4; color: #6b4410; flex: 1 1 auto; }
.free-way .type-input { flex: 0 0 auto; width: 100%; box-sizing: border-box; }
.free-way-go { margin-top: auto; display: flex; }
.free-way-go .check-btn { flex: 1 1 auto; }
.free-or {
  display: grid;
  place-items: center;
  font-weight: 700;
  color: #a4772c;
  text-transform: uppercase;
  font-size: 13px;
  letter-spacing: 0.08em;
}
.build-actions[hidden] { display: none !important; }

/* A phone held upright: one above the other. */
@media (max-width: 620px) {
  .free-ways { grid-template-columns: minmax(0, 1fr); gap: 6px; }
  .free-or { padding: 0; }
}
body.compact .free-way { padding: 8px 11px; gap: 6px; }
body.compact .free-way-title { font-size: 16px; }
body.compact .free-way-text { font-size: 13.5px; }
body.compact .type-input { font-size: 18px; padding: 6px 10px; }
body.compact .type-key { font-size: 16px; min-width: 36px; padding: 4px 8px; }

/* A landscape screen: the two ways go in a column beside the board, so the
   board keeps the whole height (wantSideBuild() in app.js). */
body.side-build .screen.play {
  position: relative;
  --side-w: clamp(250px, 24vw, 360px);
  padding-right: calc(var(--side-w) + clamp(14px, 3vw, 30px) + 14px);
}
body.side-build .build-bar {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  max-height: calc(100% - 14px);
  right: clamp(14px, 3vw, 30px);
  width: var(--side-w);
  box-sizing: border-box;
  margin: 0;
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
  justify-content: center;
  overflow: auto;
}
body.side-build .build-lead { flex: 0 0 auto; }
body.side-build .build-actions { justify-content: stretch; }
body.side-build .build-actions .check-btn { flex: 1 1 auto; }
body.side-build .build-msg { flex: 0 0 auto; }
body.side-build .free-ways { flex: 0 0 auto; grid-template-columns: minmax(0, 1fr); gap: 8px; }
body.side-build .free-way-go .check-btn { flex: 1 1 auto; }
/* Upright, the board runs out of width first: keep the bar snug under the
   board and share the spare height out above and below the pair. */
body.playing.free-balance:not(.side-build) .board-wrap:has(+ .build-bar:not([hidden])) { margin-bottom: 0; }
body.playing.free-balance:not(.side-build) .build-bar:not([hidden]) { margin-bottom: auto; }

/* Short of room - a phone, a laptop with little height - the two ways keep
   only what they need: no explanation, "Build it" beside its button, and the
   symbol keys on one line. */
body.compact .free-way-text { display: none; }
body.compact #freeBuildWay { flex-direction: row; align-items: center; justify-content: space-between; }
body.compact #freeBuildWay .free-way-go { margin: 0; }
body.compact .free-or { font-size: 11px; line-height: 1; }
body.compact .type-keys { flex-wrap: nowrap; gap: 4px; }
body.compact .type-key { flex: 1 1 0; min-width: 0; padding: 4px 2px; }
body.compact .key-word { display: none; }
body.compact .free-way .check-btn { padding: 7px 14px; }
@media (max-width: 620px) {
  body.compact .free-ways { gap: 4px; }
  body.compact .free-way.type-row { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 6px; }
  body.compact .free-way.type-row[hidden] { display: none !important; }
  body.compact .free-way.type-row .free-way-title,
  body.compact .free-way.type-row .type-keys { grid-column: 1 / -1; }
  body.compact .free-way.type-row .type-input { grid-row: 2; }
  body.compact .free-way.type-row .free-way-go { grid-row: 2; grid-column: 2; margin: 0; }
}
.free-way .check-btn, .free-way-title { white-space: nowrap; }
.go-short { display: none; }
@media (max-width: 620px) {
  body.compact .go-long { display: none; }
  body.compact .go-short { display: inline; }
}

/* --- fraction answers on the Free Balance (6 Oct 2026) --------------------- */
.frac-help { margin: 6px 0 0; font-size: 14.5px; color: var(--ink-soft); display: flex;
             align-items: center; gap: 8px; flex-wrap: wrap; }
.frac-help[hidden] { display: none !important; }
.frac-key { min-width: 40px; padding: 2px 10px; font-size: 18px; }
.frac-help .frac-key { flex: 0 0 auto; min-width: 44px; padding: 2px 10px; }

/* --- the answer sheet on a phone (6 Oct 2026) ------------------------------ */
/* Tim: "You can't see the balance while entering the value of x". The phone's
   own keyboard covered the balance, so on a phone the sheet brings its own
   keys (body.pad-mode, see setPadMode in app.js), the sheet is allowed the
   height it needs, and the chrome above the board steps aside while it is
   open - the equation stays, the board takes the rest. */
.sheet-pad[hidden] { display: none !important; }
.sheet-pad {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
  margin-top: 10px;
}
.sheet-pad button {
  font: inherit;
  font-weight: 700;
  font-size: 20px;
  height: 44px;
  min-width: 0;
  padding: 0;
  border: 2px solid var(--line);
  border-radius: 11px;
  background: #fff;
  color: var(--ink);
  cursor: pointer;
  touch-action: manipulation;
}
.sheet-pad button:active { background: #eef2f8; }
.sheet-pad .pad-next { font-size: 15px; background: #eef2f8; }
.sheet-pad:not(.frac) .pad-frac { display: none; }
.sheet-pad:not(.frac) .pad-next { grid-column: span 2; }

body.pad-phone #answerSheet.docked .sheet-card { max-height: none; }
body.pad-phone #answerSheet.docked .sheet-actions { margin-top: 8px; }
body.pad-phone.answer-open .topbar,
body.pad-phone.answer-open .head-actions,
body.pad-phone.answer-open .play-nav,
body.pad-phone.answer-open .review-tag { display: none !important; }
body.pad-phone.answer-open .play-title .equation { font-size: 26px; }
body.pad-phone.answer-open .pick-tip { display: none !important; }
@media (max-height: 480px) {
  .sheet-pad { gap: 4px; margin-top: 6px; }
  .sheet-pad button { height: 36px; font-size: 17px; }
}

/* The pop-up over the board was boxed into half the board's width (left: 50%
   leaves only the right half for shrink-to-fit), so on a phone it stood four
   lines tall. It takes the width it needs now, and placeVerdict() in app.js
   puts it beside the board on a small screen. */
.verdict { width: max-content; max-width: min(92%, 640px); box-sizing: border-box; }
@media (max-width: 620px) {
  .verdict { font-size: 14.5px; padding: 7px 14px; border-radius: 16px; line-height: 1.3; }
}

/* A phone on its side: the sheet stands down the right-hand side, the board
   keeps the left (sheetBeside() in app.js). */
@media (orientation: landscape) {
  body.pad-phone #answerSheet.docked { inset: 0 0 0 auto; width: min(54vw, 460px); }
  body.pad-phone #answerSheet.docked .sheet-card {
    width: 100%;
    height: 100%;
    max-height: none;
    box-sizing: border-box;
    border: 2px solid var(--line);
    border-right: none;
    border-radius: 20px 0 0 20px;
    padding: 8px 12px 6px;
    box-shadow: -14px 0 40px rgba(27, 36, 64, 0.24);
  }
  body.pad-phone #answerSheet.docked h3 { font-size: 16px; margin-bottom: 4px; }
  body.pad-phone #answerSheet.docked .sheet-close { top: 2px; right: 6px; }
  body.pad-phone #answerSheet.docked .check-block { margin-top: 6px; padding-top: 6px; }
  body.pad-phone #answerSheet.docked .check-lead { font-size: 12.5px; margin-bottom: 4px; line-height: 1.3; }
  body.pad-phone #answerSheet.docked .side-field input.num,
  body.pad-phone #answerSheet.docked .answer-row.one input.num { width: 92px; }
  body.pad-phone #answerSheet.docked input.num { padding-top: 4px; padding-bottom: 4px; font-size: 17px; }
  body.pad-phone #answerSheet.docked .answer-row.two { gap: 10px; font-size: 14px; }
  body.pad-phone .sheet-pad { gap: 4px; margin-top: 6px; }
  body.pad-phone .sheet-pad button { height: 34px; font-size: 16px; border-radius: 9px; }
  body.pad-phone .sheet-pad .pad-next { font-size: 13px; }
  body.pad-phone #answerSheet.docked .sheet-actions { margin-top: 4px; padding-top: 4px; }
  body.pad-phone #answerSheet.docked #submitBtn { padding: 8px 14px; font-size: 15px; }
  body.pad-phone #answerSheet.docked #sheetResetBtn { padding: 7px 12px; font-size: 13px; }
  body.pad-phone #answerSheet.docked #sheetResetBtn .say-long { display: none; }
  body.pad-phone #answerSheet.docked #sheetResetBtn .say-short { display: inline; }
  body.pad-phone #answerSheet.docked .sheet-msg { font-size: 13px; margin-top: 4px; min-height: 0; }
}
@media (max-height: 640px) and (orientation: portrait) {
  body.pad-phone .sheet-pad { gap: 5px; margin-top: 8px; }
  body.pad-phone .sheet-pad button { height: 38px; font-size: 18px; }
  body.pad-phone #answerSheet.docked .check-lead { font-size: 12.5px; margin-bottom: 6px; }
  body.pad-phone #answerSheet.docked .check-block { margin-top: 8px; padding-top: 8px; }
  body.pad-phone #answerSheet.docked h3 { font-size: 16px; margin-bottom: 4px; }
}
body.pad-phone #answerSheet.docked .sheet-msg:empty { display: none; }

/* --- the compact answer sheet (7 Oct 2026) --------------------------------- */
/* Tim: "make the I've got it popup way smaller and cut down on all the text...
   about 1/3 the size". All that is left: X = [ ], the two tray boxes when the
   puzzle asks for proof, the keys on a touch screen, and the two buttons -
   flowing onto as few rows as the width allows. The title stays for screen
   readers only. */
#answerSheet.docked .sheet-card {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 8px 18px;
  max-height: none;
  padding: 10px 46px 10px 16px;
  border-radius: 18px 18px 0 0;
}
#answerSheet.docked .sheet-close { top: 6px; right: 8px; width: 34px; height: 34px; font-size: 26px; }
#answerSheet.docked .answer-row { margin: 0; order: 1; }
#answerSheet.docked .ask-line { flex: 1 0 100%; order: 0; margin: 0; justify-content: center; font-size: 17px; }
#answerSheet.docked .check-block {
  order: 1;
  margin: 0;
  padding: 0;
  border: none;
  display: flex;
}
#answerSheet.docked .check-block[hidden] { display: none; }
#answerSheet.docked .answer-row.two { display: flex; gap: 8px 16px; margin: 0; max-width: none; }
#answerSheet.docked .side-field { gap: 6px; }
#answerSheet.docked .sheet-msg { order: 2; flex: 1 0 100%; margin: 0; min-height: 0; text-align: center; font-size: 15px; }
#answerSheet.docked .sheet-msg:empty { display: none; }
#answerSheet.docked .sheet-pad { order: 3; flex: 1 0 100%; margin: 0; }
#answerSheet.docked .sheet-actions {
  order: 4;
  position: static;
  margin: 0;
  padding: 0;
  background: none;
  box-shadow: none;
  flex: 0 0 auto;
  gap: 8px;
  flex-wrap: nowrap;
}
#answerSheet.docked #submitBtn { padding: 9px 18px; font-size: 16px; }
#answerSheet.docked #sheetResetBtn { padding: 8px 14px; font-size: 14px; }
#answerSheet.docked input.num { padding-top: 6px; padding-bottom: 6px; }
/* The keys in one row where there is room, two rows of seven on a phone. */
@media (min-width: 560px) {
  #answerSheet.docked .sheet-pad { display: flex; gap: 5px; }
  #answerSheet.docked .sheet-pad button { flex: 1 1 0; height: 42px; }
}
/* One row where it fits: X = [ ]  Left Tray [ ]  Right Tray [ ]  buttons. */
#answerSheet.docked .sheet-card { width: min(1120px, 100%); box-sizing: border-box; }
#answerSheet.docked .answer-row { align-items: center; }
#answerSheet.docked .answer-row label,
#answerSheet.docked .side-field label { display: inline-flex; align-items: center; gap: 6px; }
#answerSheet.docked .answer-row.one input.num,
#answerSheet.docked .side-field input.num { width: 112px; }
#answerSheet.docked .sheet-card { gap: 8px 14px; }
#answerSheet.docked .answer-row.one input.num,
#answerSheet.docked .side-field input.num { width: 100px; }
@media (max-width: 620px) {
  #answerSheet.docked .sheet-card { padding: 8px 10px 10px; gap: 6px 10px; }
  #answerSheet.docked .sheet-close { top: 2px; right: 4px; }
  #answerSheet.docked .answer-row.two { flex-wrap: nowrap; gap: 10px; font-size: 14px; }
  #answerSheet.docked .side-field { gap: 4px; }
  #answerSheet.docked .side-field input.num { width: 84px; }
  #answerSheet.docked .sheet-pad { gap: 5px; }
  #answerSheet.docked .sheet-pad button { height: 40px; }
}
/* With the top bar stepped aside on a phone, the equation keeps clear of the
   ZoodleBug Home button in the corner. */
body.pad-phone.answer-open .play-head { padding-left: var(--zb-home-space, 110px); }
/* A phone on its side: the sheet down the right-hand side, packed at its top
   and middle rather than spread over the full height. */
@media (orientation: landscape) {
  body.pad-phone #answerSheet.docked .sheet-card { align-content: center; padding: 8px 10px; }
  body.pad-phone #answerSheet.docked .sheet-pad {
    display: grid;
    grid-template-columns: repeat(7, minmax(0, 1fr));
    gap: 4px;
  }
  body.pad-phone #answerSheet.docked .sheet-pad button { height: 34px; }
  body.pad-phone.answer-open .play-title .equation { font-size: 20px; }
}

/* --- the answer sheet floats over the bench (8 Oct 2026) ------------------ */
/* fitBoardToAnswer() lifts it off the bottom edge to sit over the bottom of
   the board card, so it is a card in its own right: rounded all round. */
#answerSheet.docked.floating .sheet-card {
  border-bottom: 2px solid var(--line);
  border-radius: 18px;
  box-shadow: 0 14px 40px rgba(27, 36, 64, 0.28);
}

/* The "can never balance" pop-up on the Free Balance stays until it is
   closed (Tim, 8 Oct 2026). */
.verdict-close {
  margin-left: 10px;
  border: none;
  background: rgba(255, 255, 255, 0.75);
  color: inherit;
  font: inherit;
  font-size: 20px;
  line-height: 1;
  width: 32px;
  height: 32px;
  border-radius: 50%;
  cursor: pointer;
  vertical-align: middle;
}
.verdict-close:hover { background: #fff; }
