/* KaPickle. Phone first: the scoreboard is meant to be held in one hand by a
   scorer standing at the net post, and still be readable by the players a few
   steps away. Turning the phone sideways switches it to a big court display. */

:root {
  --app: #F4F7FA; --paper: #E8EEF4; --ink: #10202C; --muted: #5A6B79; --icon: #8B9AA6;
  --line: #DCE5EC; --card: #FFFFFF; --card-line: #E3EAF0;
  --accent: #0F3D5C; --accent-soft: #E1EDF5; --accent-ink: #0F3D5C;
  --ball: #8FA91F; --ball-soft: #F0F5DC; --ball-ink: #5E7010;
  --red: #C0392B; --gold: #B5731A; --gold-soft: #FBF0DC;
  --court: #1D4E72; --court-line: #F2F6EA; --kitchen: #A4402F;
  --good-bg: #E4F1E8; --good-ink: #22633E;
  --bad-bg: #F9E7E5; --bad-ink: #93302A;
  --shadow: rgba(16, 32, 44, 0.13);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --app: #0E1B26; --paper: #132532; --ink: #E7EEF3; --muted: #9DACB8; --icon: #6B7A87;
    --line: #22394A; --card: #162937; --card-line: #263F52;
    --accent: #58A6D8; --accent-soft: #17303F; --accent-ink: #9CCBE8;
    --ball: #B7D336; --ball-soft: #1E2A14; --ball-ink: #C6DE59;
    --red: #E2685A; --gold: #D9A94E; --gold-soft: #2E2716;
    --court: #16405E; --court-line: #DCE7D6; --kitchen: #8E3527;
    --good-bg: #17301F; --good-ink: #8ACFA5;
    --bad-bg: #351F1D; --bad-ink: #F0A79E;
    --shadow: rgba(0, 0, 0, 0.55);
  }
}

* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }
html, body { margin: 0; height: 100%; }
body {
  background: var(--app); color: var(--ink);
  font-family: "Segoe UI", system-ui, -apple-system, Roboto, sans-serif;
  font-size: 16px; line-height: 1.5;
  overscroll-behavior-y: contain;
}
#app { max-width: 560px; margin: 0 auto; min-height: 100dvh; display: flex; flex-direction: column; }
a { color: var(--accent); font-weight: 600; }
.hidden { display: none !important; }

/* ---------- chrome ---------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 10px 14px; background: var(--card); border-bottom: 1px solid var(--line);
}
/* The menu, then Back, then the title all sit together on the left; the auto
   margin pushes everything else to the far right. Without it, space-between
   would strand the title in the middle of the bar. */
.brand { display: flex; align-items: center; gap: 8px; font-weight: 800; font-size: 17px; cursor: pointer; margin-right: auto; }
.brand .logo { width: 30px; height: 30px; flex: 0 0 auto; }
/* Two lines: what you are looking at, and where it lives. A person can be in
   several groups, and a session called "Season 2" could be any of them. */
.brand.stack {
  flex-direction: column; align-items: flex-start; gap: 0;
  line-height: 1.2; min-width: 0; overflow: hidden;
}
.brand.stack small {
  font-size: 11px; font-weight: 600; color: var(--muted);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topbar .right { display: flex; align-items: center; gap: 8px; }
.chip {
  background: var(--accent-soft); color: var(--accent-ink);
  padding: 6px 11px; border-radius: 99px; font-size: 12.5px; font-weight: 700;
  border: none; cursor: pointer; white-space: nowrap;
}
.chip.ball { background: var(--ball-soft); color: var(--ball-ink); }
.chip.live { background: var(--bad-bg); color: var(--bad-ink); }
.back { background: none; border: none; color: var(--muted); font-size: 14px; font-weight: 600; cursor: pointer; padding: 4px 0; }

.screen { flex: 1; padding: 14px; }
h2 { font-size: 20px; margin: 4px 0 6px; }
h3 { font-size: 15px; margin: 18px 0 8px; }
.sub { color: var(--muted); font-size: 13px; margin: 0 0 14px; }
.center { display: flex; flex-direction: column; align-items: center; gap: 12px; padding-top: 8vh; text-align: center; }

/* ---------- controls ---------- */
.btn {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  border: none; border-radius: 12px; padding: 13px 16px;
  font-size: 15px; font-weight: 700; cursor: pointer; width: 100%;
  background: var(--accent); color: #fff; font-family: inherit;
}
.btn:active { transform: scale(0.99); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
.btn.ghost { background: transparent; color: var(--ink); border: 1px solid var(--card-line); }
.btn.ball { background: var(--ball); color: #12200A; }
.btn.red { background: var(--red); color: #fff; }
.btn.gold { background: var(--gold); color: #fff; }
.btn.small { width: auto; padding: 9px 13px; font-size: 13px; border-radius: 10px; }
.btn.tiny { width: auto; padding: 6px 10px; font-size: 12px; border-radius: 8px; }
.row { display: flex; gap: 8px; }
.row > * { flex: 1; }
.row.tight { gap: 6px; }
.wrap { display: flex; flex-wrap: wrap; gap: 8px; }

.field { margin-bottom: 12px; }
.field label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 5px; font-weight: 700; }
.field .hint { font-size: 11.5px; color: var(--muted); font-weight: 400; margin-top: 4px; }
input, select, textarea {
  width: 100%; padding: 12px; border-radius: 10px;
  border: 1px solid var(--card-line); background: var(--app); color: var(--ink);
  font-size: 15px; font-family: inherit;
}
textarea { min-height: 96px; resize: vertical; }
.switch { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; font-size: 14px; }

/* A real toggle, not a tick box. Standing at the net with one thumb, a switch
   reads as on or off at a glance and is a bigger thing to hit. Used for the
   check-in list and every yes/no setting. */
input[type="checkbox"].tgl, .switch input[type="checkbox"] {
  -webkit-appearance: none; appearance: none; margin: 0;
  width: 48px; height: 28px; flex: 0 0 auto; border-radius: 999px;
  background: var(--paper); border: 1px solid var(--card-line);
  position: relative; cursor: pointer; padding: 0;
  transition: background .15s ease, border-color .15s ease;
}
input[type="checkbox"].tgl::after, .switch input[type="checkbox"]::after {
  content: ""; position: absolute; top: 2px; left: 2px;
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--icon);
  transition: transform .15s ease, background .15s ease;
}
input[type="checkbox"].tgl:checked, .switch input[type="checkbox"]:checked {
  background: var(--ball-soft); border-color: var(--ball);
}
input[type="checkbox"].tgl:checked::after, .switch input[type="checkbox"]:checked::after {
  transform: translateX(20px); background: var(--ball);
}
input[type="checkbox"].tgl:disabled { opacity: .45; cursor: not-allowed; }
.switch .sw-label { flex: 1; }
.switch .sw-label small { display: block; color: var(--muted); font-size: 11.5px; }

.segs { display: flex; flex-wrap: wrap; border: 1px solid var(--card-line); border-radius: 10px; overflow: hidden; background: var(--card); }
.seg { flex: 1 1 auto; border: none; background: transparent; color: var(--muted); padding: 10px 8px; font-size: 13px; font-weight: 700; cursor: pointer; font-family: inherit; }
.seg.on { background: var(--accent); color: #fff; }
/* A row of layout glyphs: the shape of the button is the shape you get. */
.segs.icons .seg { display: flex; align-items: center; justify-content: center; padding: 9px 8px; min-height: 44px; }
.segs.icons .li { width: 22px; height: 22px; display: block; }

/* ---------- cards & lists ---------- */
.card { background: var(--card); border: 1px solid var(--card-line); border-radius: 14px; padding: 13px; margin-bottom: 10px; }
.card.tap { cursor: pointer; }
.card .ttl { font-weight: 800; font-size: 15px; }
.card .meta { color: var(--muted); font-size: 12.5px; }
.empty { text-align: center; color: var(--muted); font-size: 13.5px; padding: 26px 12px; }

.plist { display: flex; flex-direction: column; gap: 6px; }

/* Somebody waiting on the admin to answer. A row rather than a card, because
   there can be several and each one is a single decision. */
.ask-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 10px 0; border-top: 1px solid var(--card-line);
}
.ask-row:first-of-type { border-top: 0; }
.ask-row .nm { flex: 1; min-width: 140px; font-weight: 700; font-size: 14px; }
.ask-row .nm small { display: block; color: var(--muted); font-size: 11.5px; font-weight: 400; }
.ask-row .wrap { flex: 0 0 auto; }
.prow {
  display: flex; align-items: center; gap: 10px;
  background: var(--card); border: 1px solid var(--card-line);
  border-radius: 12px; padding: 9px 11px;
}
.prow.off { opacity: 0.5; }
/* Four choices rather than three, so they wrap on a narrow phone instead of
   squeezing the words out of shape. */
.segs.four { flex-wrap: wrap; }
.segs.four .seg { flex: 1 1 40%; }
/* One person on the roster twice, once as a guest and once as the account they
   made later. Set apart at the foot of the sheet, because it is not a setting
   like the others above it and it cannot be undone. */
.ps-merge {
  margin-top: 16px; padding-top: 13px; border-top: 1px solid var(--card-line);
}
.ps-merge .ttl { font-size: 13.5px; font-weight: 800; }
.ps-merge .meta { font-size: 11.5px; color: var(--muted); margin-top: 3px; }
.ps-merge + * { margin-top: 0; }
/* An admin taps a person to see and change which groups they run. Nothing on
   the row said so, so the chevron does. */
.prow[data-person] { cursor: pointer; }
.prow[data-person]:active { transform: scale(0.995); }
.prow .chev { flex: 0 0 auto; width: 15px; height: 15px; color: var(--icon); opacity: .6; }
.prow .nm { flex: 1; min-width: 0; font-weight: 600; font-size: 14.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.prow .nm small { display: block; color: var(--muted); font-size: 11.5px; font-weight: 400; }
/* An avatar is a photo, or an emoji, or initials on a colour worked out from
   the account. Same three tiers and the same colours as KaBingo and KaLaro, so
   one person looks like themselves in all three. Guests have no account at all
   and always land on initials. */
.av, .avatar {
  width: 34px; height: 34px; flex: 0 0 auto; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  color: #fff; font-weight: 800; font-size: 13px;
  overflow: hidden; line-height: 1;
}
.av img, .avatar img { width: 100%; height: 100%; object-fit: cover; }
/* An emoji needs the page background behind it, not a colour block. */
.av.emoji, .avatar.emoji {
  background: var(--app); border: 1px solid var(--card-line); font-size: 17px;
}
.avatar.big.emoji { font-size: 40px; }
.avatar.tap { cursor: pointer; }
.avatar.big {
  width: 76px; height: 76px; font-size: 27px;
  border: 2px solid var(--card-line);
}

.prof-top { display: flex; align-items: center; gap: 14px; margin-bottom: 14px; }
.prof-top .hint { margin-top: 4px; }

.avgrid { display: grid; grid-template-columns: repeat(5, 1fr); gap: 8px; }
.avgrid button {
  aspect-ratio: 1; border-radius: 12px; font-size: 21px; line-height: 1;
  background: var(--card); border: 1px solid var(--card-line); color: var(--ink);
  cursor: pointer; font-family: inherit; display: flex;
  align-items: center; justify-content: center;
}
.avgrid button.on { border-color: var(--accent); box-shadow: inset 0 0 0 2px var(--accent); }
.avgrid button.initials { font-size: 13px; font-weight: 800; }
.tag { font-size: 10.5px; font-weight: 800; padding: 3px 7px; border-radius: 99px; letter-spacing: .3px; }
.tag.guest { background: var(--paper); color: var(--muted); }
.tag.you { background: var(--accent-soft); color: var(--accent-ink); }
.tag.scorer { background: var(--ball-soft); color: var(--ball-ink); }
.tag.org { background: var(--gold-soft); color: var(--gold); }
.tag.rest { background: var(--paper); color: var(--muted); }

/* skill shown as filled pips, 1.0 to 5.0 in half steps */
.pips { display: inline-flex; gap: 2px; align-items: center; }
.pip { width: 7px; height: 7px; border-radius: 50%; background: var(--line); }
.pip.on { background: var(--ball); }
.pip.half { background: linear-gradient(90deg, var(--ball) 50%, var(--line) 50%); }

/* ---------- run screen: courts ---------- */
.court-card { background: var(--card); border: 1px solid var(--card-line); border-radius: 14px; padding: 12px; margin-bottom: 10px; }
.court-card.live { border-color: var(--ball); box-shadow: 0 0 0 1px var(--ball) inset; }
.court-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 9px; }
.court-no { font-weight: 900; font-size: 15px; }
.vs-row { display: grid; grid-template-columns: 1fr auto 1fr; align-items: center; gap: 8px; }
.vs-team { min-width: 0; }
.vs-team .p { font-weight: 700; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.vs-team .sk { color: var(--muted); font-size: 11.5px; }
.vs-team.right { text-align: right; }
.vs-mid { font-size: 11px; font-weight: 800; color: var(--muted); }
.vs-score { font-size: 22px; font-weight: 900; font-variant-numeric: tabular-nums; }
.win { color: var(--good-ink); }

/* ---------- the scoreboard ---------- */
.board { display: flex; flex-direction: column; min-height: 100dvh; background: var(--app); }
.board-top { padding: 10px 14px 0; }
.board-ctx { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; color: var(--muted); font-weight: 700; }
.board-teams { display: flex; align-items: baseline; justify-content: center; gap: 10px; margin-top: 2px; font-size: 13.5px; font-weight: 700; }

/* the call: the three numbers the scorer shouts */
.callbox { text-align: center; padding: 6px 12px 10px; }
.call {
  font-size: clamp(56px, 19vw, 104px); font-weight: 900; line-height: 0.95;
  font-variant-numeric: tabular-nums; letter-spacing: -2px; color: var(--ink);
}
.call .dim { color: var(--icon); }
.call-key { font-size: 11px; font-weight: 800; letter-spacing: 1.4px; color: var(--muted); margin-top: 2px; }

.sides { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 0 14px; }
.side-box { background: var(--card); border: 1px solid var(--card-line); border-radius: 12px; padding: 9px 11px; text-align: center; }
.side-box.serving { border-color: var(--ball); background: var(--ball-soft); }
.side-box .lbl { font-size: 10.5px; font-weight: 800; letter-spacing: 1px; color: var(--muted); }
.side-box .num { font-size: 34px; font-weight: 900; line-height: 1; font-variant-numeric: tabular-nums; }
.side-box .who { font-size: 12px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.serve-line { margin: 10px 14px 0; background: var(--card); border: 1px solid var(--card-line); border-radius: 12px; padding: 10px 12px; font-size: 13.5px; }
.serve-line b { color: var(--ball-ink); }
.serve-line .row2 { color: var(--muted); font-size: 12.5px; }

.board-actions { margin-top: auto; padding: 12px 14px calc(12px + env(safe-area-inset-bottom)); display: flex; flex-direction: column; gap: 8px; }
.bigbtn {
  width: 100%; border: none; border-radius: 16px; cursor: pointer; font-family: inherit;
  font-size: 23px; font-weight: 900; letter-spacing: 1px; padding: 22px 12px;
  background: var(--ball); color: #12200A;
}
.bigbtn.dark { background: var(--accent); color: #fff; font-size: 18px; padding: 18px 12px; }
.bigbtn:active { transform: scale(0.99); }
.bigbtn:disabled { opacity: 0.4; }

.nextbar { margin: 10px 14px 0; background: var(--paper); border-radius: 12px; padding: 10px 12px; font-size: 12.5px; }
.nextbar .lbl { font-size: 10.5px; font-weight: 800; letter-spacing: 1px; color: var(--muted); }

/* the drawn court, shown in landscape and on wide screens */
.courtview { display: none; }
.courtbox {
  display: grid; grid-template-columns: 1fr 14px 14px 1fr;
  background: var(--court); border-radius: 10px; overflow: hidden;
  border: 2px solid var(--court-line); min-height: 210px;
}
.cside { display: grid; grid-template-rows: 1fr 1fr; gap: 3px; padding: 3px; }
.cbox {
  border: 2px solid var(--court-line); border-radius: 4px;
  display: flex; align-items: center; justify-content: center; text-align: center;
  color: #fff; font-weight: 800; font-size: clamp(13px, 2.4vw, 21px);
  padding: 4px; position: relative; min-width: 0;
}
.cbox span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
.cbox.server { background: rgba(191, 215, 59, 0.34); border-color: var(--ball); }
.cbox.receiver { background: rgba(255, 255, 255, 0.12); border-style: dashed; }
.cbox .role { position: absolute; top: 3px; left: 5px; font-size: 9px; letter-spacing: 1px; opacity: .85; font-weight: 800; }
.kitchen { background: var(--kitchen); }

/* The swap arrows sit on the lines they act on: between a pair's two boxes,
   and on the net for changing ends. Hidden unless you are the one scoring a
   live game, so a watcher's board stays a plain drawing. */
.courtbox { position: relative; }
.cswap {
  display: none; position: absolute; top: 50%; width: 36px; height: 36px; padding: 0;
  transform: translate(-50%, -50%); border-radius: 50%; cursor: pointer;
  background: rgba(14, 27, 38, 0.92); color: var(--ball); border: 2px solid var(--ball);
  align-items: center; justify-content: center; z-index: 2;
}
.cswap svg { width: 20px; height: 20px; }
.cswap:active { transform: translate(-50%, -50%) scale(0.92); }
.cswap.ends { background: #E7EEF3; color: #0F3D5C; border-color: #0F3D5C; }
#cbSwapL { left: 25%; }
#cbEnds { left: 50%; }
#cbSwapR { left: 75%; }
.courtbox.scoring .cswap { display: flex; }

/* The same court, shrunk to sit in a list. The scoreboard version is sized to
   be read from across a court; this one has three of them stacked on a phone. */
.courtbox.mini { min-height: 0; border-width: 1px; border-radius: 8px; margin-top: 9px; }
.courtbox.mini .cbox {
  border-width: 1px; font-size: 13px; padding: 9px 6px; min-height: 40px;
}
.courtbox.mini .cside { gap: 2px; padding: 2px; }
@media (max-width: 380px) {
  .courtbox.mini .cbox { font-size: 12px; padding: 7px 4px; min-height: 34px; }
}

/* ---------- the board turned sideways: a big display ----------
   Rotate the phone, or open it on a tablet or laptop, and the board stops
   being a one-handed control and becomes something players read from across
   the court. The score spans the full width, flanked by the two team totals,
   with the court underneath and the two actions side by side.

   Only the board breaks out of the phone-width column (body.board-mode),
   because every other screen is a list and reads better narrow. */
@media (orientation: landscape), (min-width: 820px) {
  body.board-mode #app { max-width: none; }
  /* The wall of courts is the other screen meant to be looked at from across a
     room, so it breaks out of the phone column too. Left at 560px it puts
     postage-stamp scores in the middle of a television. */
  body.wall-mode #app { max-width: none; }
  /* Only the courts want the whole width. The switcher, the session line and
     the standings stay readable rather than stretching across a television. */
  /* One width for the whole page: courts, switcher, waiting and standings share
     a centred column, instead of courts running wall to wall above a narrow
     table. Full screen lifts the cap and fills the glass. */
  body.wall-mode #lvLayout,
  body.wall-mode #lvWrap > * {
    max-width: 1100px; margin-left: auto; margin-right: auto;
  }
  body.is-fullscreen.wall-mode #lvWrap > * { max-width: none; }

  /* side A | the call | side B, then the serve line across the bottom */
  body.board-mode .board-portrait {
    display: grid;
    grid-template-columns: 1fr minmax(280px, 1.3fr) 1fr;
    align-items: center;
    gap: 14px;
    padding: 0 18px;
  }
  body.board-mode .board-portrait .sides { display: contents; }
  body.board-mode #bdSideA { order: 1; }
  body.board-mode .callbox { order: 2; padding: 0; }
  body.board-mode #bdSideB { order: 3; }
  body.board-mode .serve-line { order: 4; grid-column: 1 / -1; margin: 4px 0 0; }

  /* Sized off height as well as width so a rotated phone never overflows. */
  body.board-mode .call { font-size: min(13vw, 26vh); letter-spacing: -3px; }
  body.board-mode .call-key { font-size: 12px; }
  body.board-mode .side-box { padding: 10px 12px; }
  body.board-mode .side-box .num { font-size: min(8vw, 17vh); }
  body.board-mode .side-box .lbl { font-size: min(1.5vw, 15px); }
  body.board-mode .side-box .who { font-size: min(1.4vw, 14px); }
  body.board-mode .board-teams { font-size: 15px; }

  /* Let the court take the room between the score and the buttons instead of
     leaving a gap: on a laptop that gap was most of the screen. */
  body.board-mode .courtview {
    display: flex; flex-direction: column; flex: 1 1 auto;
    padding: 12px 18px 0; min-height: 0;
  }
  body.board-mode .courtbox { flex: 1 1 auto; min-height: min(26vh, 200px); max-height: 52vh; }
  body.board-mode .cbox { font-size: min(2.6vw, 30px); }
  body.board-mode .cswap { width: 48px; height: 48px; }
  body.board-mode .cswap svg { width: 26px; height: 26px; }

  body.board-mode .board-actions {
    flex-direction: row; align-items: stretch; gap: 10px; padding: 12px 18px;
    padding-bottom: calc(12px + env(safe-area-inset-bottom));
  }
  body.board-mode .board-actions .bigbtn { flex: 2 1 0; font-size: min(3.2vw, 32px); padding: 18px 10px; }
  body.board-mode .board-actions .bigbtn.dark { font-size: min(2.4vw, 23px); }
  body.board-mode .board-actions .row.tight { flex: 1 1 0; }
}

/* A short landscape phone: the score on the left, the court on the right, so
   the drawing and its swap arrows stay on screen where the scorer holds the
   phone sideways. The court used to be dropped here, and with it the only
   sign that the server had moved to the other box after a point. */
@media (orientation: landscape) and (max-height: 460px) {
  body.board-mode .board {
    display: grid; grid-template-columns: 1.1fr 1fr; grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas: "top top" "score court" "acts acts"; column-gap: 10px;
    height: 100dvh; min-height: 0;
  }
  body.board-mode .board-top { grid-area: top; padding-top: 6px; }
  body.board-mode .board-teams { font-size: 12.5px; margin-top: 0; }
  body.board-mode .board-portrait {
    grid-area: score; display: flex; flex-direction: column; justify-content: center; gap: 6px;
    padding: 0 0 0 14px; min-height: 0;
  }
  body.board-mode .board-portrait .sides { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; padding: 0; }
  body.board-mode .callbox { order: 0; padding: 0; }
  body.board-mode .call { font-size: min(9vw, 15vh); letter-spacing: -2px; }
  body.board-mode .call-key { font-size: 10px; }
  body.board-mode .side-box { padding: 5px 8px; }
  body.board-mode .side-box .num { font-size: min(6vw, 11vh); }
  body.board-mode .side-box .lbl { font-size: 10.5px; }
  body.board-mode .side-box .who { font-size: 11px; }
  body.board-mode .serve-line { order: 4; grid-column: auto; margin: 0; padding: 5px 10px; font-size: 12px; }
  body.board-mode .serve-line .row2 { font-size: 11px; }
  body.board-mode .courtview { grid-area: court; display: flex; padding: 0 14px 0 0; min-height: 0; }
  body.board-mode .courtbox { flex: 1 1 auto; min-height: 0; max-height: none; }
  body.board-mode .cbox { font-size: min(2.4vw, 20px); }
  body.board-mode .cswap { width: 36px; height: 36px; }
  body.board-mode .cswap svg { width: 20px; height: 20px; }
  body.board-mode .nextbar { display: none; }
  body.board-mode .board-actions { grid-area: acts; padding: 8px 14px calc(8px + env(safe-area-inset-bottom)); }
  body.board-mode .board-actions .bigbtn { padding: 12px 8px; font-size: min(3vw, 24px); }
  body.board-mode .board-actions .bigbtn.dark { font-size: min(2.3vw, 18px); }
}

@media (min-width: 720px) {
  .courtview { display: block; padding: 10px 14px 0; }
}
/* On a tall phone in portrait there is dead space between the score and the
   buttons. Put the drawn court there: it shows who is standing where, which is
   what the scorer is looking at anyway. */
@media (orientation: portrait) and (min-height: 700px) {
  .courtview { display: block; padding: 12px 14px 0; }
  .courtview .courtbox { min-height: 180px; }
}

/* Portrait, one hand, standing at the net post. Whatever height is left over
   belongs to the score: the players are a few steps away and reading it off a
   phone held at arm's length. Nothing above the buttons used to grow, so the
   slack collected in a dead band between the court and POINT. */
@media (orientation: portrait) {
  .board-portrait { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
  .sides { flex: 1 1 auto; min-height: 0; margin-top: 8px; }
  .side-box {
    display: flex; flex-direction: column; justify-content: center; gap: 2px;
    padding: 8px 10px;
  }
  /* Sized off the height as well as the width, so a tall phone gets a big
     number and a short one does not push the buttons off the screen. */
  .side-box .num { font-size: clamp(40px, 14vh, 128px); }
  .side-box .lbl { font-size: clamp(10.5px, 1.5vh, 14px); }
  .side-box .who { font-size: clamp(11px, 1.6vh, 15px); }
  .call { font-size: clamp(52px, min(19vw, 13vh), 132px); }
  .callbox { padding: 4px 12px 6px; }
  .courtview { flex: 0 0 auto; }
  .board-actions { margin-top: 0; }
}

/* ---------- standings, podium, report ---------- */
.table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.table th {
  text-align: right; font-size: 10.5px; letter-spacing: .6px; color: var(--muted);
  font-weight: 800; padding: 6px 5px; border-bottom: 1px solid var(--line);
}
.table th:nth-child(1), .table th:nth-child(2) { text-align: left; }
.table td { padding: 8px 5px; border-bottom: 1px solid var(--line); text-align: right; font-variant-numeric: tabular-nums; }
.table td:nth-child(1) { text-align: left; color: var(--muted); font-weight: 800; width: 26px; }
.table td:nth-child(2) { text-align: left; font-weight: 700; }
.table tr.me td { background: var(--accent-soft); }
.table tr.unq td { opacity: .55; }
.table td small { color: var(--muted); font-weight: 400; }

.podium { display: flex; flex-direction: column; gap: 8px; margin-bottom: 14px; }
.pod {
  display: flex; align-items: center; gap: 11px;
  background: var(--card); border: 1px solid var(--card-line);
  border-left: 5px solid var(--line); border-radius: 12px; padding: 11px 13px;
}
.pod.p1 { border-left-color: var(--gold); background: var(--gold-soft); }
.pod.p2 { border-left-color: var(--icon); }
.pod.p3 { border-left-color: #A9743B; }
.pod .pos { font-size: 22px; font-weight: 900; width: 26px; color: var(--muted); font-variant-numeric: tabular-nums; }
.pod.p1 .pos { color: var(--gold); }
.pod .who { flex: 1; min-width: 0; }
.pod .who b { display: block; font-size: 16px; }
.pod .who small { color: var(--muted); font-size: 12px; }
.pod .score { text-align: right; font-size: 13px; font-weight: 800; }
.pod .score small { display: block; color: var(--muted); font-weight: 400; font-size: 11.5px; }

.awards { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; margin-bottom: 14px; }
.award { background: var(--card); border: 1px solid var(--card-line); border-radius: 12px; padding: 10px 11px; }
.award .lbl { font-size: 10px; font-weight: 800; letter-spacing: .8px; color: var(--muted); }
.award .nm { font-weight: 800; font-size: 14.5px; margin-top: 2px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.award .det { font-size: 11.5px; color: var(--muted); }

.log { font-size: 12.5px; }
.log .lg { display: flex; justify-content: space-between; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--line); }
.log .lg .t { flex: 1; min-width: 0; }
.log .lg .s { font-weight: 800; font-variant-numeric: tabular-nums; }

/* ---------- misc ---------- */
.toast {
  position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%) translateY(20px);
  background: var(--ink); color: var(--app); padding: 11px 17px; border-radius: 12px;
  font-size: 13.5px; font-weight: 600; opacity: 0; pointer-events: none;
  transition: opacity .2s, transform .2s; z-index: 100; max-width: 90vw; text-align: center;
}
.toast.show { opacity: 1; transform: translateX(-50%) translateY(0); }

.sheet {
  position: fixed; inset: 0; background: rgba(8, 18, 26, .55); z-index: 60;
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet-inner {
  background: var(--card); width: 100%; max-width: 560px;
  border-radius: 18px 18px 0 0; padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
  max-height: 88dvh; overflow-y: auto;
}
.sheet h3 { margin-top: 0; }

.banner { background: var(--accent-soft); color: var(--accent-ink); border-radius: 12px; padding: 11px 13px; font-size: 13px; margin-bottom: 10px; }
.banner.warn { background: var(--gold-soft); color: var(--gold); }
.foot { text-align: center; color: var(--muted); font-size: 12px; padding: 20px 10px 26px; }
.foot a { color: var(--muted); margin: 0 7px; }
.spin { display: inline-block; width: 15px; height: 15px; border: 2px solid var(--line); border-top-color: var(--accent); border-radius: 50%; animation: sp .7s linear infinite; }
@keyframes sp { to { transform: rotate(360deg); } }

@media print {
  .topbar, .board-actions, .btn, .foot { display: none !important; }
  body { background: #fff; color: #000; }
  .card, .pod, .award { border-color: #bbb; break-inside: avoid; }
}

/* ---------- full screen ---------- */
/* Icon-only chips are square and big enough to hit with a thumb. The menu was
   31x27, which courtside and one-handed is a miss more often than a tap. */
.chip.fsbtn, .chip.navbtn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0; min-width: 44px; height: 44px; border-radius: 12px;
}
.fsi { width: 19px; height: 19px; display: block; }
.ctx-right { display: inline-flex; align-items: center; gap: 8px; }
/* Nothing floats above the content once the browser chrome is gone. */
body.is-fullscreen { background: var(--app); }
/* The identity chip follows you onto every screen, but full screen is for the
   court: propped up at the net, the score should own the glass. */
body.is-fullscreen .me-avatar, body.is-fullscreen #meChip { display: none; }

/* ---------- the big screen ----------
   A wall of courts for a tablet or TV at the venue. The column count comes from
   the layout switcher, and the score is sized off the viewport so it stays
   readable from the far side of a court however many are on screen. */
.wall {
  display: grid;
  grid-template-columns: repeat(var(--wall-cols, 1), minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}
/* A scoreboard is a wide thing: two big numbers side by side. Left to stretch
   it becomes a tall column with the score marooned in the middle of it, so a
   tile keeps a landscape shape and only fills the height when full screen has
   a known number of rows to divide. */
.wall-court {
  background: var(--card); border: 1px solid var(--card-line); border-radius: 14px;
  padding: 10px 12px;
  display: flex; flex-direction: column; gap: 6px;
  aspect-ratio: 16 / 9; min-height: 112px;
}
.wall-court.live { border-color: var(--ball); }
.wc-name {
  font-weight: 800; font-size: 12.5px; letter-spacing: .4px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 0 0 auto;
}
.wc-idle {
  flex: 1; display: flex; align-items: center; justify-content: center;
  color: var(--muted); font-size: 13px;
}
/* A court with a game waiting on it says so, rather than "Free" while the same
   game is listed somewhere else on the page. */
.wall-court.ready { border-color: var(--accent); }
.wc-next {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; gap: 2px;
}
.wc-tag {
  font-size: 10.5px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--accent-ink); background: var(--accent-soft);
  padding: 2px 8px; border-radius: 99px; margin-bottom: 4px;
}
.wc-pair {
  font-weight: 700; line-height: 1.2; max-width: 100%;
  font-size: clamp(13px, calc(min(9vw, 16vh) / var(--wall-cols, 1)), 30px);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.wc-vs { color: var(--muted); font-size: 11px; font-weight: 700; }
.wc-score { flex: 1; min-height: 0; display: flex; gap: 8px; align-items: center; }
.wc-side {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center;
}
/* Sized off the tile, not the page, so it stays right whatever the layout. */
.wc-pts {
  font-weight: 900; line-height: 1;
  font-size: clamp(30px, calc(min(34vw, 62vh) / var(--wall-cols, 1)), 150px);
}
/* Names wrap onto a second line rather than being cut off mid-word. */
.wc-who {
  margin-top: 5px; color: var(--muted); font-size: 11.5px; line-height: 1.25;
  max-width: 100%; display: -webkit-box; -webkit-line-clamp: 2;
  -webkit-box-orient: vertical; overflow: hidden;
}

.lv-head {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-bottom: 10px; font-size: 15px;
}
.lv-head span { color: var(--muted); font-size: 12.5px; }

/* Full screen: the courts fill exactly what is left and nothing scrolls. The
   page is a column - heading, courts, the rest - and the rest is put away,
   because a wall propped up at the venue is for the scores, not the table. */
/* Scoped to the wall, so full screen on any other screen still scrolls. */
body.is-fullscreen.wall-mode #app { height: 100dvh; overflow: hidden; }
body.is-fullscreen.wall-mode #scLive {
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
}
body.is-fullscreen.wall-mode #scLive .foot { display: none; }
/* #lvExtras in full screen: see TV mode at the end of this file. */
body.is-fullscreen.wall-mode #lvWrap {
  flex: 1; min-height: 0; display: flex; flex-direction: column;
}
/* Keep the landscape shape and centre the rows, rather than stretching one row
   of courts over the whole screen and marooning the score in the middle of it. */
/* The rows share the height that is actually there, and each tile fits inside
   its cell while keeping its shape. Doing the fitting here rather than in
   arithmetic means an estimate that is slightly off cannot clip a court off
   the bottom of the screen. */
body.is-fullscreen.wall-mode #lvWrap .wall {
  flex: 1; min-height: 0; margin-bottom: 0;
  grid-auto-rows: minmax(0, 1fr);
}
/* The rows are 1fr, so they already fit by construction and a tile can safely
   take the whole cell. It keeps a landscape shape where there is room for one,
   and letterboxes rather than shrinking when the screen is short. */
body.is-fullscreen.wall-mode .wall-court {
  width: 100%; height: auto;
  aspect-ratio: 16 / 9;
  max-height: 100%; min-height: 0;
  align-self: stretch;
}
body.is-fullscreen.wall-mode .lv-head { margin-bottom: 8px; }
/* The list layout still scrolls in full screen, because it is a list. */
body.is-fullscreen.wall-mode #lvWrap .lv-list { flex: 1; min-height: 0; overflow-y: auto; }

/* A session that is over, or one that has not begun. The wall is for a game in
   progress, and neither of these is one: a grid of empty courts reads as "any
   moment now" to somebody who has just walked in. */
.lv-over {
  background: var(--card); border: 1px solid var(--card-line); border-radius: 14px;
  padding: 22px 16px; text-align: center; margin-bottom: 16px;
}
.lv-over h2 { font-size: 22px; font-weight: 900; margin: 6px 0 2px; }
.lv-over .meta { color: var(--muted); font-size: 12.5px; }
.lv-over-tag {
  display: inline-block; font-size: 10.5px; font-weight: 800; letter-spacing: 1px;
  text-transform: uppercase; padding: 3px 10px; border-radius: 99px;
  background: var(--ball-soft); color: var(--ball-ink);
}
.lv-over-tag.wait { background: var(--accent-soft); color: var(--accent-ink); }
/* Landscape on a phone is short, so the chrome has to give way to the courts. */
@media (orientation: landscape) and (max-height: 520px) {
  .wall-court { min-height: 96px; }
  .wc-name { font-size: 11px; }
  .wc-who { -webkit-line-clamp: 1; font-size: 10.5px; }
  body.is-fullscreen .wall-court { min-height: 0; }
}

/* The menu sits first in the bar, so give it a little room from what follows. */
.chip.navbtn { margin-right: 2px; }
.chip.navbtn .fsi { width: 22px; height: 22px; }

/* ---------- the menu sheet ----------
   Destinations in a grid with an icon each, so it reads at a glance instead of
   being a column of identical pills you have to read one by one. */
.navsec {
  font-size: 11px; font-weight: 800; letter-spacing: .8px; text-transform: uppercase;
  color: var(--muted); margin: 14px 2px 8px;
}
.navsec:first-child { margin-top: 0; }
.navgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.navgrid.courts { grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); }
.navtile {
  display: flex; flex-direction: column; align-items: flex-start; gap: 3px;
  background: var(--card); border: 1px solid var(--card-line); border-radius: 12px;
  padding: 11px 12px; cursor: pointer; font-family: inherit; color: var(--ink);
  font-size: 13.5px; font-weight: 700; text-align: left; min-height: 62px;
  justify-content: center;
}
.navtile:active { transform: scale(0.99); }
.navtile .ni { width: 19px; height: 19px; color: var(--icon); }
.navtile small { color: var(--muted); font-size: 11.5px; font-weight: 500; }
.navtile.court.ready { border-color: var(--ball); }
.navtile.court.ready small { color: var(--ball-ink); font-weight: 800; }
.navtile.court.live { border-color: var(--red); }
.navtile.court.live small { color: var(--red); font-weight: 800; font-size: 14px; }
/* Last in the menu and full width, so leaving is the end of the list rather
   than a tile your thumb can catch while reaching for Profile. */
.navtile.wide {
  width: 100%; flex-direction: row; align-items: center; justify-content: center;
  gap: 9px; margin-top: 14px; min-height: 52px;
}
#navOut { color: var(--red); }
#navOut .ni { color: var(--red); }

/* A group is a row rather than a tile, so its name has room and the cog for
   the ones you run sits beside it, one tap from wherever you are. */
.navrows { display: flex; flex-direction: column; gap: 8px; }
.navrow { display: flex; gap: 8px; }
.navrow-main {
  flex: 1; min-width: 0; display: flex; align-items: center; gap: 10px;
  background: var(--card); border: 1px solid var(--card-line); border-radius: 12px;
  padding: 12px; cursor: pointer; font-family: inherit; color: var(--ink);
  font-size: 14px; font-weight: 700; text-align: left; min-height: 52px;
}
.navrow-main span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.navrow-main .ni { flex: 0 0 auto; width: 19px; height: 19px; color: var(--icon); }
.navrow-cog {
  flex: 0 0 auto; width: 52px; display: flex; align-items: center; justify-content: center;
  background: var(--card); border: 1px solid var(--card-line); border-radius: 12px;
  cursor: pointer; color: var(--icon);
}
.navrow-main:active, .navrow-cog:active { transform: scale(0.99); }
.navrow-cog .ni { width: 20px; height: 20px; }
/* The help pages, which used to be reachable only from the foot of Home. */
.navlinks {
  display: flex; flex-wrap: wrap; gap: 6px 14px; justify-content: center;
  margin: 16px 2px 2px; font-size: 12.5px;
}
.navlinks a { color: var(--muted); font-weight: 600; }

/* ---------- Up next, and the games already played ----------
   A heading with its hint attached, rather than a whole card of explanation
   above every group of cards. */
.up-sec {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin: 16px 2px 8px;
}
.up-sec:first-child { margin-top: 2px; }
.up-sec span {
  font-size: 11.5px; font-weight: 800; letter-spacing: .8px;
  text-transform: uppercase; color: var(--muted);
}
.up-sec small { color: var(--muted); font-size: 11.5px; opacity: .85; }

.up-card {
  background: var(--card); border: 1px solid var(--card-line);
  border-radius: 14px; padding: 11px 13px; margin-bottom: 8px;
}
.up-card.live { border-color: var(--ball); }
.up-head { display: flex; align-items: center; gap: 8px; margin-bottom: 9px; }
.up-court { font-weight: 800; font-size: 14.5px; }
.up-even { color: var(--muted); font-size: 12px; margin-left: auto; }
.up-score { font-weight: 900; font-size: 17px; margin-left: auto; font-variant-numeric: tabular-nums; }
.up-teams { display: flex; align-items: center; gap: 10px; }
.up-side { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: 5px; }
.up-side.right { align-items: flex-end; }
.up-vs { color: var(--muted); font-size: 11px; font-weight: 700; flex: 0 0 auto; }
.up-p {
  max-width: 100%; border: 1px solid var(--card-line); background: var(--app);
  color: var(--ink); border-radius: 9px; padding: 6px 10px;
  font-size: 12.5px; font-weight: 700; font-family: inherit; cursor: default;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  text-align: center;
}
.up-p:disabled { cursor: default; opacity: .75; }
/* Dashed only while the mode is on: this is the whole "you may tap me" signal,
   and it should not be shouting on a screen nobody is editing. */
.up-p.pick {
  cursor: pointer; border-style: dashed; border-color: var(--accent);
}
.up-p.pick:active { transform: scale(0.98); }
.up-p.picked {
  border-style: solid; border-color: var(--accent);
  background: var(--accent-soft); color: var(--accent-ink);
}

/* The card whose button started the change, so it is obvious which game the
   mode belongs to even after scrolling. */
.up-card.editing { border-color: var(--accent); }

/* One line at the top of the tab: what to do, and how to stop. Sticky, because
   the queue can be well below the game being changed. */
.swapbar {
  position: sticky; top: 0; z-index: 5;
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 12px; padding: 11px 13px;
  background: var(--accent-soft); border: 1px solid var(--accent);
  border-radius: 12px; color: var(--accent-ink); font-size: 12.5px;
}
.swapbar span { flex: 1; min-width: 0; }
.swapbar b { font-weight: 800; }

/* The head pushes the score and the state to the right without either of them
   needing margin tricks. */
.up-gap { flex: 1 1 auto; }

/* The card is the button now, so it has to look like one: it lifts off the
   page a little, and presses when you press it. */
.up-card.tappable {
  cursor: pointer; -webkit-tap-highlight-color: transparent;
  transition: transform .1s ease, border-color .1s ease;
}
.up-card.tappable:active { transform: scale(0.992); }
.up-card.tappable:hover { border-color: var(--accent); }
.up-card.tappable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* One line carrying how even the game is, and what tapping will do. The right
   half is what the button used to say, in the place a button would have been. */
.up-level {
  display: flex; align-items: center; gap: 10px;
  margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--card-line);
  font-size: 11.5px; color: var(--muted); letter-spacing: .2px;
}
.up-level > span:first-child { flex: 1; min-width: 0; }
.up-go {
  display: inline-flex; align-items: center; gap: 3px; flex: 0 0 auto;
  color: var(--accent-ink); font-weight: 800; font-size: 12.5px;
}
.up-card.live .up-go { color: var(--ball-ink); }
.up-go .chev { width: 14px; height: 14px; }

.up-acts { display: flex; gap: 8px; margin-top: 11px; }
.up-acts .btn { flex: 1; }

/* Who holds the phone, and the way in to everything you might do about it.
   One line and one target, rather than three small buttons on every card. */
.up-scorer {
  display: flex; align-items: center; gap: 8px; width: 100%;
  margin-top: 11px; padding: 10px 0 0;
  border: 0; border-top: 1px solid var(--card-line);
  background: none; color: var(--muted);
  font-family: inherit; font-size: 12px; text-align: left; cursor: pointer;
}
.up-scorer span { flex: 1; min-width: 0; }
.up-scorer .chev { flex: 0 0 auto; width: 15px; height: 15px; opacity: .6; }
.up-scorer:active { opacity: .7; }
.up-scorer.flat { cursor: default; }
/* Inside a card that is itself a button, so it needs to read as a separate
   thing rather than part of the same target. */
.up-card.tappable .up-scorer { margin-top: 9px; }
.up-card.tappable .up-scorer:hover span { color: var(--ink); }
/* Only when it actually blocks somebody: the scorer is out playing. */
.up-scorer.warn span { color: var(--gold); font-weight: 700; }

/* The stack of choices behind that line. */
.sheet-acts { display: flex; flex-direction: column; gap: 8px; }
.up-idle { color: var(--muted); font-size: 13px; padding: 6px 0 2px; }

/* The game log: court, both teams, the score. Reads down the page like a
   results column in a newspaper. */
.game-row {
  display: flex; align-items: center; gap: 10px;
  background: var(--card); border: 1px solid var(--card-line);
  border-radius: 12px; padding: 9px 12px; margin-bottom: 6px;
}
.game-row[data-game] { cursor: pointer; }
.game-row.void { opacity: .55; }
/* Court and clock stack in the left gutter, so a row says which court and when
   without spending a third of the width on it. */
.g-when {
  flex: 0 0 auto; min-width: 46px; display: flex; flex-direction: column; gap: 1px;
}
.g-court {
  font-size: 11px; font-weight: 800; color: var(--muted); letter-spacing: .5px;
}
.g-time {
  font-size: 10.5px; color: var(--muted); opacity: .75;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.g-teams { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.g-side {
  font-size: 13px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.g-side.won { color: var(--ink); font-weight: 700; }
.g-score {
  flex: 0 0 auto; font-size: 15px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.g-score b { color: var(--ink); }

/* Who is scoring, next to the court name on the board. On a live court that is
   also who is allowed to score it. */
.bd-scorer {
  display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .4px;
  color: var(--muted); text-transform: none;
}
.bd-scorer.me { color: var(--ball-ink); }

/* ---------- the queue ----------
   Who goes on next and in what order. The first group is the one people care
   about, so it is the one that stands out. */
.queue-card {
  background: var(--card); border: 1px solid var(--card-line);
  border-radius: 12px; padding: 10px 12px; margin-bottom: 8px;
}
.queue-card.up { border-color: var(--ball); }
.q-head { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.q-when { font-size: 11.5px; font-weight: 800; letter-spacing: .5px; text-transform: uppercase; color: var(--muted); }
.queue-card.up .q-when { color: var(--ball-ink); }

/* ---------- the big screen, re-cut ----------
   The tile used to be a 16:9 box of its column with the score sized off the
   window: one court on a wide screen was a page-high card with 150px digits
   marooned in it and 11px grey names. Now the type is sized off the TILE
   (container units), so one court is a real board and four across still read
   from the far side of a court, and a tile is never taller than the window. */
.wall-court { max-height: calc(100vh - 220px); overflow: hidden; }
.wc-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 8px;
  flex: 0 0 auto; min-width: 0;
}
.wc-top .wc-name { flex: 1 1 auto; }
.wc-to {
  flex: 0 0 auto; color: var(--muted); font-weight: 800; letter-spacing: .6px;
  text-transform: uppercase; font-size: 10.5px; white-space: nowrap;
}
/* Names above the numbers, in ink rather than grey: they are half the point. */
.wc-who {
  color: var(--ink); font-weight: 800; margin-top: 0; margin-bottom: 2%;
  font-size: clamp(12px, calc(min(4vw, 7vh) / var(--wall-cols, 1)), 40px);
}
.wc-side.serving .wc-who { color: var(--ball-ink); }
.wc-ball {
  display: inline-block; vertical-align: middle; width: .55em; height: .55em;
  margin-right: .35em; border-radius: 50%; background: var(--ball);
  box-shadow: 0 0 0 .15em var(--ball-soft);
}
.wc-mid {
  flex: 0 0 auto; align-self: stretch; display: flex; flex-direction: column;
  align-items: center; justify-content: center; padding: 0 3%; min-width: 0;
  border-left: 1px solid var(--card-line); border-right: 1px solid var(--card-line);
}
.wc-call {
  font-weight: 900; color: var(--muted); letter-spacing: 1px; white-space: nowrap;
  font-size: clamp(11px, calc(min(3vw, 5vh) / var(--wall-cols, 1)), 30px);
}
@supports (container-type: size) {
  .wall-court { container-type: size; }
  .wc-name { font-size: clamp(11px, 5cqh, 26px); }
  .wc-to { font-size: clamp(10px, 4cqh, 18px); }
  .wc-pts { font-size: 50cqh; }
  .wc-who { font-size: clamp(11px, 7.5cqh, 48px); -webkit-line-clamp: 2; }
  .wc-pair { font-size: clamp(13px, 9cqh, 48px); }
  .wc-tag, .wc-vs { font-size: clamp(10px, 4cqh, 18px); }
  .wc-call { font-size: clamp(11px, 6cqh, 34px); }
  .wc-idle { font-size: clamp(12px, 8cqh, 40px); }
}

/* ---------- TV mode: the wall in full screen ----------
   Everything fits the glass and nothing scrolls. The courts take one side and
   the standings a panel on the other (underneath on a phone held upright);
   rows that do not fit crawl slowly down and start again (app.js). The bar and
   the switcher get out of the way; a touch brings them back for a moment. */
body.is-fullscreen.wall-mode #scLive .topbar,
body.is-fullscreen.wall-mode #lvLayout { display: none; }
/* While peeked, the bar and the switcher take their own room at the top and the
   wall re-lays under them (the tiles are sized off their region, so they
   simply shrink a little), instead of floating over the court names. */
body.is-fullscreen.wall-mode.show-chrome #scLive .topbar {
  display: flex; position: static; flex: 0 0 auto; margin: -14px -14px 8px !important;
}
body.is-fullscreen.wall-mode.show-chrome #lvLayout {
  display: flex; position: static; flex: 0 0 auto; transform: none;
  width: min(92vw, 560px); margin: 0 auto 8px;
}
body.is-fullscreen.wall-mode .lv-head { font-size: 14px; margin: 0 0 6px; flex: 0 0 auto; }
.lv-live { display: none; }
body.is-fullscreen.wall-mode .lv-live {
  display: inline-block; margin-left: auto; font-size: 10.5px; font-weight: 900;
  letter-spacing: 1px; color: #fff; background: var(--red); padding: 2px 8px; border-radius: 99px;
}
body.is-fullscreen.wall-mode .lv-body { flex: 1 1 auto; min-height: 0; display: flex; gap: 12px; }
body.is-fullscreen.wall-mode .lv-courts {
  flex: 1 1 auto; min-width: 0; min-height: 0; display: flex; flex-direction: column;
}
body.is-fullscreen.wall-mode #lvExtras {
  display: flex; flex-direction: column; min-height: 0; min-width: 0;
  background: var(--card); border: 1px solid var(--card-line); border-radius: 14px;
  padding: 10px 12px;
}
body.is-fullscreen.wall-mode #lvExtras h3 { margin: 4px 0 6px; flex: 0 0 auto; }
body.is-fullscreen.wall-mode #lvExtras .card { padding: 8px 10px; margin-bottom: 8px; flex: 0 0 auto; }
body.is-fullscreen.wall-mode .lv-scroll { flex: 1 1 auto; min-height: 0; overflow: hidden; }
body.is-fullscreen.wall-mode .lv-scroll thead th {
  position: sticky; top: 0; background: var(--card); z-index: 1;
}
body.is-fullscreen.wall-mode .lv-scroll .table { font-size: 15px; }
@media (orientation: landscape) {
  body.is-fullscreen.wall-mode .lv-body { flex-direction: row; }
  body.is-fullscreen.wall-mode #lvExtras { flex: 0 0 32%; }
}
@media (orientation: portrait) {
  body.is-fullscreen.wall-mode .lv-body { flex-direction: column; }
  body.is-fullscreen.wall-mode #lvExtras { flex: 0 0 45%; }
}
/* One court at a time: a dot per court, the lit one is on screen. */
.lv-dots { display: none; }
body.is-fullscreen.wall-mode .lv-dots {
  display: flex; justify-content: center; gap: 7px; padding: 8px 0 2px; flex: 0 0 auto;
}
.lv-dots i { width: 9px; height: 9px; border-radius: 50%; background: var(--card-line); }
.lv-dots i.on { background: var(--ball); }

/* TV mode geometry. Auto margins on the cross axis of a flex column make an
   item shrink to its content, so the 1100px column's centring rule left the
   courts and the panel 350px wide on a 1900px screen: cleared here. The tile
   is then sized off the courts region itself (container units) so every court
   keeps 16:9 and all of them fit the region, letterboxed and centred, however
   many rows and columns the layout chose. */
body.is-fullscreen.wall-mode #lvWrap > * { margin-left: 0; margin-right: 0; }
body.is-fullscreen.wall-mode .lv-courts { container-type: size; position: relative; }
body.is-fullscreen.wall-mode .wall-court {
  width: min(
    calc((100cqw - 10px * (var(--wall-cols, 1) - 1)) / var(--wall-cols, 1)),
    calc((100cqh - 10px * (var(--wall-rows, 1) - 1)) / var(--wall-rows, 1) * 16 / 9));
  height: auto; aspect-ratio: 16 / 9; max-height: none; min-height: 0;
  align-self: center; justify-self: center;
}
body.is-fullscreen.wall-mode .lv-dots { position: absolute; left: 0; right: 0; bottom: 2px; padding: 0; }

/* ---------- the court, drawn on the big-screen tile ----------
   Two boxes a side and the kitchen between, sized off the tile like the
   digits, with the serving box lit as on the scorer's board. */
.wc-court {
  flex: 1 1 auto; min-height: 0; display: grid;
  grid-template-columns: 1fr 12px 12px 1fr;
  background: var(--court); border: 2px solid var(--court-line); border-radius: 10px;
  overflow: hidden; margin-top: 2%;
}
.wc-court .cside { display: grid; grid-template-rows: 1fr 1fr; gap: 3px; padding: 3px; min-height: 0; min-width: 0; }
.wc-court .kitchen { background: var(--kitchen); }
.wc-court .cbox { padding: 2px 6px; min-height: 0; gap: .3em; font-size: clamp(11px, 5.5cqh, 30px); }
.wc-court .cbox .wc-ball { width: .5em; height: .5em; margin: 0; flex: 0 0 auto; }
/* With the court carrying the names, the score row gives it room. */
.wall-court.live .wc-score { flex: 0 0 auto; }
.wall-court.live .wc-pts { font-size: 34cqh; }
.wall-court.live .wc-pts .wc-ball { width: .22em; height: .22em; margin-right: .12em; vertical-align: middle; }
.wall-court.ready .wc-top { justify-content: space-between; }
.wall-court.ready .wc-tag { margin: 0; }
/* The ball beside the big digit is a plain dot: the halo the box version
   wears turned into a dark blob at digit size. */
.wall-court.live .wc-pts .wc-ball { box-shadow: none; width: .16em; height: .16em; margin-right: .1em; }

/* Three small buttons under the board (Undo, Type score, More) share the row. */
.board-actions .row.tight .btn { flex: 1 1 0; padding: 12px 6px; white-space: nowrap; }

/* ---------- court colours ----------
   A chip in the court's own colour wherever a court is named. */
.court-chip {
  display: inline-block; background: var(--cc, var(--ball)); color: #0E1B26;
  font-weight: 900; font-size: 11px; letter-spacing: .7px; text-transform: uppercase;
  padding: 3px 9px; border-radius: 99px; line-height: 1.3; white-space: nowrap; vertical-align: middle;
}
.court-chip.up-court { font-size: 11.5px; }
.court-chip.wc-name {
  color: #0E1B26; font-size: clamp(10px, 4.5cqh, 22px); overflow: hidden;
  text-overflow: ellipsis; max-width: 72%; flex: 0 1 auto;
}
#bdCtx .court-chip { margin-right: 6px; }
.navtile.court { border-left: 4px solid var(--cc, var(--card-line)); }
.navtile.court .ni { color: var(--cc, var(--icon)); }

/* The serving box pulses when the board paints it, on the scoreboard only;
   the big screen repaints every few seconds and must not twitch. */
@keyframes serve-pop {
  0% { transform: scale(1); box-shadow: 0 0 0 0 rgba(183, 211, 54, .75); }
  60% { transform: scale(1.025); box-shadow: 0 0 0 10px rgba(183, 211, 54, 0); }
  100% { transform: scale(1); box-shadow: 0 0 0 0 rgba(183, 211, 54, 0); }
}
#scBoard .cbox.server { animation: serve-pop .45s ease-out; }

/* The rule a game is played under, one quiet line under the teams. */
.bd-rule { text-align: center; font-size: 11px; font-weight: 700; letter-spacing: .4px; color: var(--muted); margin-top: 1px; }
.bd-rule:empty { display: none; }

/* ---------- the court in its own colour, and a ball for the server ----------
   The frame and the four boxes take the court's colour; the kitchen stays red,
   the serving box stays lit and carries the ball, the receiving box stays
   dashed. Works on the Courts tab, the scoreboard and the big screen alike. */
.courtbox, .wc-court { border-color: var(--cc, var(--court-line)); }
.courtbox .cbox, .wc-court .cbox { border-color: var(--cc, var(--court-line)); }
@supports (background: color-mix(in srgb, red 10%, blue)) {
  .courtbox .cbox, .wc-court .cbox { background: color-mix(in srgb, var(--cc, var(--court)) 16%, var(--court)); }
  .courtbox .cbox.receiver, .wc-court .cbox.receiver { background: color-mix(in srgb, #ffffff 12%, var(--court)); }
}
.courtbox .cbox.server, .wc-court .cbox.server { background: rgba(191, 215, 59, 0.34); border-color: var(--ball); }
.pb { width: 1em; height: 1em; vertical-align: -0.15em; flex: 0 0 auto; }
.cbox span .pb { width: .95em; height: .95em; margin-right: .3em; }
.wc-pts .pb { width: .3em; height: .3em; margin-right: .08em; vertical-align: middle; }

/* Up next draws the game on its court. A box that is a button (Change players
   is on) keeps the court look and only borrows the dashed "tap me" signal. */
.courtbox .cbox.up-p {
  background: var(--court); color: #fff; border-radius: 4px; padding: 2px 6px;
  font-size: inherit; cursor: pointer; opacity: 1;
}
@supports (background: color-mix(in srgb, red 10%, blue)) {
  .courtbox .cbox.up-p { background: color-mix(in srgb, var(--cc, var(--court)) 16%, var(--court)); }
}
.courtbox .cbox.up-p.pick { border-style: dashed; border-color: #fff; }
.courtbox .cbox.up-p.picked { border-style: solid; border-color: var(--accent); background: rgba(88, 166, 216, 0.55); }
.up-card .courtbox.mini { margin-top: 2px; }

@media (orientation: landscape) and (max-height: 460px) {
  body.board-mode .board { grid-template-columns: minmax(220px, 34%) minmax(0, 1fr); }
  body.board-mode .board-portrait { align-items: stretch; padding: 0 0 0 12px; }
  body.board-mode .courtview { padding: 0 12px 0 0; }
}

/* A box on the scorer's drawing is a tap target: it hands the ball to that
   player. Said quietly, in the corner, only where there is a scorer. */
.courtbox.scoring .cbox:not(.server) { cursor: pointer; }
.courtbox.scoring .cbox:not(.server)::after {
  content: "tap to serve"; position: absolute; right: 6px; bottom: 3px;
  font-size: 8.5px; letter-spacing: .5px; font-weight: 800; opacity: .5; text-transform: uppercase;
}
.courtbox.scoring .cbox:not(.server):active { transform: scale(0.985); }

/* ---------- the portrait board fits any phone ----------
   The digits were sized off the SCREEN height (14vh), so on a 390x844 phone
   the score could not shrink and the buttons were pushed below the edge once
   the rule line was added. The score boxes are now a size container: they take
   exactly the height left between the court and the buttons, and the digits
   are sized off that room. Tall phone, big digits; short phone, smaller digits;
   never a button off the screen. */
@media (orientation: portrait) {
  .sides { container-type: size; }
  .side-box .num { font-size: clamp(34px, 60cqh, 128px); }
  .side-box .lbl { font-size: clamp(10px, 7cqh, 14px); }
  .side-box .who { font-size: clamp(11px, 8cqh, 15px); }
  .courtview .courtbox { min-height: 150px; }
  .courtbox.scoring .cbox:not(.server)::after { opacity: .35; }
}
/* Container units only resolve against a DEFINITE height, and a flex item's
   height is definite only when its container's is. .board had min-height
   alone, so cqh read as zero and the digits collapsed to their minimum.
   A real height makes the whole column definite; the score boxes shrink to
   fit, so nothing spills on a phone of any sane height. */
@media (orientation: portrait) {
  .board { height: 100dvh; }
}
/* The court drawing on every portrait phone, not only tall ones. It used to be
   shown from 700px of height up, because below that the score could not give
   way. The score now takes whatever is left, so the court can stay: on an
   iPhone SE (375x667) it is a little shorter and the digits a little smaller,
   and the swap arrows and the serving box are still there. */
@media (orientation: portrait) {
  .courtview { display: block; padding: 10px 14px 0; }
}
@media (orientation: portrait) and (max-height: 700px) {
  .courtview { padding-top: 8px; }
  .courtview .courtbox { min-height: 124px; }
  .serve-line { margin-top: 8px; padding: 6px 10px; font-size: 12.5px; }
  .serve-line .row2 { font-size: 11.5px; }
  .callbox { padding: 2px 12px 4px; }
  .board-actions { gap: 6px; padding-top: 8px; }
  .bigbtn { padding: 16px 12px; font-size: 21px; }
  .bigbtn.dark { padding: 14px 12px; font-size: 17px; }
}
/* On a live game the drawing says who serves and who receives; the serve line
   under the score said it again, in words, and cost a row the court could
   use. It stays on the ready board, where it carries the START hint. */
#scBoard.live .serve-line { display: none; }

/* DEUCE and GAME POINT: a gold flag wherever the score is shown. */
.call-flag:empty { display: none; }
.call-flag, .up-flag, .wc-flag {
  display: inline-block; background: var(--gold-soft); color: var(--gold);
  font-weight: 900; font-size: 11px; letter-spacing: 1.2px; text-transform: uppercase;
  padding: 3px 10px; border-radius: 99px; line-height: 1.3;
}
.call-flag { margin-top: 6px; }
.up-flag { margin-left: 8px; font-size: 10px; padding: 2px 8px; }
.wc-flag { margin-top: 6%; font-size: clamp(9px, 4cqh, 18px); white-space: nowrap; }

/* A court held on its result (0035): gold until Next game or three minutes. */
.tag.final { background: var(--gold-soft); color: var(--gold); }
.up-card.final, .court-card.final, .wall-court.final, .navtile.court.final { border-color: var(--gold); }
.wall-court.final .wc-score { flex: 0 0 auto; }
.wall-court.final .wc-pts { font-size: 34cqh; }
.up-card.final .up-go { color: var(--gold); }
.wall-court.final .wc-side.won .wc-pts { color: var(--gold); }
.wall-court.final .wc-flag { background: var(--gold-soft); color: var(--gold); }
.navtile.court.final small { color: var(--gold); font-weight: 800; }

/* Season leaderboards (0036): weekday picker and the board table. */
.wkrow { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 4px; }
.wk {
  border: 1px solid var(--card-line); background: var(--card); color: var(--muted);
  border-radius: 99px; padding: 6px 10px; font-size: 12.5px; font-weight: 700;
}
.wk.on { background: var(--accent-soft); color: var(--accent-ink); border-color: var(--accent); }
.hint { display: block; color: var(--muted); font-size: 11.5px; margin-top: 4px; }
.sn-rule { margin: 8px 0 0; }
.sn-table td small { display: block; color: var(--muted); font-size: 11px; font-weight: 500; }
.sn-table td:nth-child(2) { text-align: left; }

/* TV mode for a leaderboard (0037): the bar goes, the table fills the glass,
   rows that do not fit crawl down and start again (app.js). A touch brings the
   bar back for a moment, as on the wall. */
body.fs-real.season-mode #app { height: 100dvh; overflow: hidden; }
body.fs-real.season-mode .tvscreen {
  display: flex; flex-direction: column; min-height: 0; height: 100%; overflow: hidden;
}
/* The bar, the meta line and the tabs stay: one row each. Only the table
   fills the rest and crawls (app.js installSeasonTv). */
body.fs-real.season-mode .tvscreen .topbar { flex: 0 0 auto; }
body.fs-real.season-mode .tvscreen .foot { display: none; }
body.fs-real.season-mode .meta-line { flex: 0 0 auto; }
body.fs-real.season-mode .tvwrap { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }
body.fs-real.season-mode .tvwrap > .card { flex: 0 0 auto; padding: 8px 12px; margin-bottom: 8px; }
body.fs-real.season-mode .tvwrap .sn-scroll { flex: 1 1 auto; min-height: 0; overflow: hidden; }
body.fs-real.season-mode .tvwrap .sn-scroll thead th { position: sticky; top: 0; background: var(--app); z-index: 1; }
body.fs-real.season-mode .tvwrap .table { font-size: clamp(15px, 2.6vh, 28px); }
body.fs-real.season-mode .tvwrap .table td { padding: .45em .35em; }
body.fs-real.season-mode .tvwrap .sn-table td small { font-size: .7em; }
body.fs-real.season-mode .tvwrap .up-sec { flex: 0 0 auto; }

/* Team tournaments (0038): team chips, tie rows, colour swatches. */
.team-chip { display: inline-flex; align-items: center; gap: 6px; font-weight: 800; min-width: 0; }
.team-chip::before { content: ""; width: 10px; height: 10px; border-radius: 50%; background: var(--tc); flex: 0 0 auto; }
.team-chip.big { font-size: 16px; }
.swatch { width: 28px; height: 28px; border-radius: 50%; border: 2px solid transparent; background: var(--tc); }
.swatch.on { border-color: var(--ink); box-shadow: 0 0 0 2px var(--card); }
.tie-row { display: flex; align-items: center; gap: 8px; }
.tie-team { flex: 1; min-width: 0; font-weight: 700; overflow: hidden; text-overflow: ellipsis; }
.tie-team.right { text-align: right; }
.tie-team.right .team-chip { flex-direction: row-reverse; }
.tie-team.won { color: var(--ball-ink); }
.tie-score { font-weight: 900; font-size: 18px; font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.tie-row.big .tie-score { font-size: 26px; }
.tie-tag { margin-left: 6px; }
.card.void { opacity: .55; }
.tn-table td:nth-child(2) { text-align: left; }
body.fs-real.season-mode .sn-note { display: none; }
/* The tournament's tabs stay in TV mode, one small row. */
body.fs-real.season-mode #scTourney .segs { flex: 0 0 auto; margin-bottom: 8px; }

/* The thin line under the bar on a leaderboard or a tournament: what it is,
   who may see it, Edit. The name is in the bar already. */
.meta-line {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  font-size: 12.5px; color: var(--muted); margin: -4px 0 10px; min-height: 22px;
}
.meta-line { flex-wrap: nowrap; }
.meta-line .ml-text {
  flex: 1 1 auto; min-width: 0; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.meta-line .ml-acts { flex: 0 0 auto; display: flex; align-items: center; gap: 6px; }
.meta-line .chip.small { padding: 5px 12px; font-size: 12px; min-height: 32px; }
/* Tap targets on the pickers and the chips: a finger, not a mouse. */
.wk, .swatch { min-height: 36px; }
.swatch { width: 36px; height: 36px; }

/* The matchup screen (0038): a scoreboard tile on top, a tile per game. */
.tie-head .tie-row.big { margin-bottom: 8px; }
.tie-sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.tie-sub .meta { flex: 1 1 auto; min-width: 0; }
.tie-game .tg-head {
  display: flex; justify-content: space-between; align-items: center; gap: 8px;
  font-size: 11px; color: var(--muted); font-weight: 800; letter-spacing: .4px; text-transform: uppercase;
  margin-bottom: 8px;
}
.tg-row { display: flex; align-items: center; gap: 8px; }
.tg-side { flex: 1; min-width: 0; font-weight: 700; line-height: 1.35; overflow: hidden; text-overflow: ellipsis; }
.tg-side.right { text-align: right; }
.tg-side.won { color: var(--ball-ink); }
.tg-mid { flex: 0 0 auto; text-align: center; min-width: 64px; }
.tg-score { font-weight: 900; font-size: 22px; font-variant-numeric: tabular-nums; }
.tie-game.live { border-color: var(--ball); }
.tie-game .up-level { margin-top: 8px; }

/* The match day card on a session's Courts tab (0038). */
.md-card { border-color: var(--gold); }
.md-tie {
  display: flex; align-items: center; gap: 8px; width: 100%; padding: 8px 2px;
  border: 0; border-top: 1px solid var(--line); background: transparent; color: inherit;
  font: inherit; font-weight: 700; text-align: left; cursor: pointer;
}
.md-tie:first-of-type { border-top: 0; }
.md-tie b { flex: 0 0 auto; font-weight: 900; font-variant-numeric: tabular-nums; }
.md-team { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; overflow: hidden; white-space: nowrap; }
.md-team.right { justify-content: flex-end; }
.md-team i { width: 10px; height: 10px; border-radius: 50%; background: var(--tc); flex: 0 0 auto; }

/* A shared place on a leaderboard still needs a draw (0041). */
.draw-needed { color: var(--gold); font-weight: 800; }

/* The search box above a list of people (0070). */
.search {
  display: block; width: 100%; box-sizing: border-box; margin: 0 0 10px; padding: 10px 12px;
  border: 1px solid var(--card-line); border-radius: 12px; background: var(--card); color: var(--ink);
  font: inherit; font-size: 15px; min-height: 40px;
}
.search:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

/* The queue on the wall (0043): compact in TV mode so the standings keep room. */
body.is-fullscreen.wall-mode #lvExtras .queue-card { flex: 0 0 auto; padding: 6px 10px; margin-bottom: 6px; }
body.is-fullscreen.wall-mode #lvExtras .queue-card .up-p { font-size: 12px; padding: 3px 8px; }
#lvExtras .lv-later { flex: 0 0 auto; margin: 0 0 8px; }
