/* Matchday — the recording app.
 *
 * The palette is lifted from docs/sports/index.html so this reads as part of
 * kamhq.com rather than as a tool bolted on: the same ink, the same single green
 * accent, the same restraint about using it.
 *
 * ── There is no webfont, deliberately ──────────────────────────────────────
 *
 * The site loads Inter from Google Fonts. This app does not, for two reasons
 * that point the same way: the CSP here is `font-src 'self' data:` and adding a
 * third-party origin to it would be widening the policy on the one page that
 * holds a live session; and an official at a ground with no signal would get the
 * fallback anyway. So Inter is asked for first — most phones and laptops that
 * have visited the site already have it — and the stack behind it is the
 * platform's own.
 *
 * ── Sizing is for a thumb, not a mouse ─────────────────────────────────────
 *
 * Every target that is pressed during a live match is at least 48px tall. The
 * official is standing up, holding a phone in one hand, watching the pitch and
 * not the screen. A 32px button that is fine on a desktop is a mis-hit at 88
 * minutes, and a mis-hit credits one player with another's work.
 */

* { margin: 0; padding: 0; box-sizing: border-box; }

/* The `hidden` attribute must actually hide things.
 *
 * The browser's own rule is `[hidden] { display: none }`, which ANY author rule
 * setting `display` beats — and this stylesheet sets `display: flex` on both
 * `.sheet` and `.sheet-foot`, which are the two elements toggled by `hidden`.
 * The result was a modal overlay covering the whole app from the moment it
 * loaded: `inset: 0` and `rgba(0,0,0,0.72)`, so every view underneath was
 * dimmed and unclickable while the a11y tree still reported the page as fine.
 *
 * Caught by opening the app in a browser and looking at it. Nothing in the
 * conformance suite could have found it — the server was answering correctly the
 * whole time. */
[hidden] { display: none !important; }

:root {
  --bg:       #111;
  --bg-alt:   #0c0c0c;
  --surface:  #141414;
  --surface-2:#191919;
  --fg:       #fff;
  --fg-dim:   #b0b0b0;
  --fg-mute:  #777;
  --hair:     #242424;

  --accent:      #35d07f;
  --accent-soft: rgba(53,208,127,0.55);
  --accent-hair: rgba(53,208,127,0.16);

  /* Event tones. Muted on purpose — twenty-five saturated buttons is a slot
     machine, and the official needs to find one of them fast. Only `score`
     carries the accent, because a goal is the one thing worth a colour. */
  --tone-score:    #35d07f;
  --tone-positive: #7fc7a4;
  --tone-negative: #d98a8a;
  --tone-caution:  #d9c48a;
  --tone-neutral:  #9a9a9a;

  --font: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
          'Helvetica Neue', Arial, sans-serif;
}

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: var(--font);
  background: var(--bg);
  color: var(--fg);
  -webkit-font-smoothing: antialiased;
  /* The recording screen must not rubber-band while somebody is jabbing at it. */
  overscroll-behavior-y: contain;
  /* The notch and the home indicator. `viewport-fit=cover` in app.php is what
     lets the dark ground reach the edges; without these the top bar sits under
     the notch and the feed's last row under the home bar. */
  padding-top: env(safe-area-inset-top);
  padding-bottom: env(safe-area-inset-bottom);
}

/* ══ Touch ═══════════════════════════════════════════════════════════════
 *
 * This is a phone app first — Kam, 6 Sep 2026. Three properties, each fixing
 * something that costs an entry:
 *
 *   touch-action: manipulation   removes the 300ms wait a browser spends
 *                                deciding whether a tap was a double-tap. On a
 *                                grid pressed at speed that delay reads as the
 *                                app being unresponsive, and the official
 *                                presses again.
 *   user-select: none            a long press on a button otherwise selects the
 *                                label and raises the copy/paste bar over the
 *                                grid.
 *   tap-highlight transparent    the default grey flash fights the tone colour
 *                                that says what kind of event was recorded.
 */
button, .btn, .stat-btn, .player-btn, .side-btn, .group-tab, a, label, input, select {
  touch-action: manipulation;
  -webkit-tap-highlight-color: transparent;
}
button, .btn, .stat-btn, .player-btn, .side-btn, .group-tab {
  -webkit-user-select: none;
  user-select: none;
}

/* ══ Sign-in pages ═══════════════════════════════════════════════════════ */

.gate-body {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}
.gate { width: min(90vw, 380px); text-align: center; }
.gate-label {
  font-size: 10px; font-weight: 700; letter-spacing: 4px;
  text-transform: uppercase; color: var(--fg-mute); margin-bottom: 18px;
}
.gate-h1 { font-size: 26px; font-weight: 300; letter-spacing: -0.5px; line-height: 1.3; margin-bottom: 14px; }
.gate-h1 strong { font-weight: 700; }
.gate-sub { font-size: 13px; font-weight: 300; line-height: 1.6; color: #8a8a8a; margin-bottom: 30px; }
.gate input {
  width: 100%; background: transparent; border: 1px solid #444; color: var(--fg);
  font-family: inherit; font-size: 16px; /* 16px or iOS zooms the page on focus */
  letter-spacing: 0.5px; padding: 14px 16px; text-align: center; outline: none;
  transition: border-color 0.15s;
}
.gate input:focus { border-color: var(--accent); }
.gate button {
  width: 100%; margin-top: 12px; background: var(--accent); color: #0b1a12;
  border: 1px solid var(--accent); font-family: inherit; font-size: 11px;
  font-weight: 700; letter-spacing: 3px; text-transform: uppercase;
  padding: 15px 16px; cursor: pointer; transition: background 0.15s, color 0.15s;
}
.gate button:hover { background: transparent; color: var(--accent); }
.gate-ok {
  border: 1px solid #2f4a3c; background: #16211b; padding: 22px 20px;
  font-size: 13px; font-weight: 300; line-height: 1.65; color: #cfe3d7; text-align: left;
}
.gate-ok strong { font-weight: 600; color: var(--fg); display: block; margin-bottom: 8px; }
.gate-foot {
  margin-top: 22px; font-size: 10px; letter-spacing: 2px;
  text-transform: uppercase; color: #555;
}

/* ══ Top bar ═════════════════════════════════════════════════════════════ */

.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; padding: 12px 18px;
  background: var(--bg-alt); border-bottom: 1px solid var(--hair);
}
.brand {
  display: inline-flex; align-items: center; gap: 8px;
  text-decoration: none; color: var(--fg-mute);
  font-size: 12px; font-weight: 700; letter-spacing: 0.2px;
}
.brand:hover { color: var(--accent); }
.glyph { width: 17px; height: 17px; flex: none; display: block; }
.glyph .die { fill: none; stroke: currentColor; stroke-width: 3.4; stroke-linejoin: round; }
.glyph .core { fill: var(--accent); }
.brand .wm { color: var(--fg); }
.brand .wm i { font-style: normal; color: var(--accent); }

.topbar-right { display: flex; align-items: center; gap: 14px; }
.who { font-size: 11px; color: var(--fg-mute); }
.logout-form { display: inline; }
.linkish {
  background: none; border: none; padding: 0; cursor: pointer;
  font-family: inherit; font-size: 11px; color: var(--fg-mute);
  text-decoration: none; border-bottom: 1px solid transparent;
}
.linkish:hover { color: var(--accent); border-bottom-color: var(--accent-hair); }

/* The sync chip. This is the only place the app tells an official that events
   are sitting on the device unsent, and it is why it lives in the top bar
   rather than in a corner of the recording screen: it must be visible from
   every view, including the one they switch to when they think they are done. */
.sync {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 11px; color: var(--fg-mute);
  padding: 4px 10px; border: 1px solid var(--hair); border-radius: 100px;
}
.sync-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--fg-mute); flex: none;
}
.sync.is-synced .sync-dot { background: var(--accent); }
.sync.is-pending .sync-dot { background: var(--tone-caution); animation: pulse 1.4s ease-in-out infinite; }
.sync.is-offline .sync-dot { background: var(--tone-negative); }
.sync.is-pending, .sync.is-offline { color: var(--fg-dim); border-color: #333; }
@keyframes pulse { 0%,100% { opacity: 1 } 50% { opacity: 0.35 } }

@media (max-width: 640px) {
  .who { display: none; }
  .topbar { padding: 10px 14px; }
}

/* ══ Views ═══════════════════════════════════════════════════════════════ */

.wrap { max-width: 900px; margin: 0 auto; padding: 28px 18px 60px; }
.section-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin-bottom: 24px;
}
.section-head h1 { font-size: 24px; font-weight: 300; letter-spacing: -0.4px; }

.btn {
  font-family: inherit; font-size: 11px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; padding: 12px 18px; cursor: pointer;
  border: 1px solid var(--accent); transition: background 0.15s, color 0.15s;
  min-height: 44px;
}
.btn-solid { background: var(--accent); color: #0b1a12; }
.btn-solid:hover { background: transparent; color: var(--accent); }
.btn-ghost { background: transparent; color: var(--fg-dim); border-color: #3a3a3a; }
.btn-ghost:hover { color: var(--accent); border-color: var(--accent); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }

.empty { color: var(--fg-mute); font-size: 13px; font-weight: 300; padding: 20px 0; }

/* ── Match list ── */
.match-list { display: flex; flex-direction: column; gap: 10px; }
.match-card {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: var(--surface); border: 1px solid var(--hair);
  padding: 16px 18px; font-family: inherit; color: var(--fg);
  transition: border-color 0.15s;
}
.match-card:hover { border-color: var(--accent-hair); }
.match-card-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
}
.match-card-teams { font-size: 15px; font-weight: 600; }
.match-card-meta { font-size: 11px; color: var(--fg-mute); margin-top: 6px; }
.pill {
  font-size: 9px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase;
  padding: 3px 8px; border: 1px solid var(--hair); color: var(--fg-mute); flex: none;
}
.pill.is-live { color: var(--accent); border-color: var(--accent-soft); }
.pill.is-finished { color: var(--fg-mute); }

/* ── Setup form ── */
.form { display: flex; flex-direction: column; gap: 18px; }
.field-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 560px) { .field-row { grid-template-columns: 1fr; } }
.field { display: flex; flex-direction: column; gap: 7px; }
.field > span {
  font-size: 10px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--fg-mute);
}
.field input, .field select {
  background: var(--surface); border: 1px solid var(--hair); color: var(--fg);
  font-family: inherit; font-size: 16px; padding: 12px 14px; outline: none;
  transition: border-color 0.15s; min-height: 46px;
}
.field input:focus, .field select:focus { border-color: var(--accent); }

.check { display: flex; gap: 12px; align-items: flex-start; cursor: pointer; }
.check input { width: 18px; height: 18px; margin-top: 2px; flex: none; accent-color: var(--accent); }
.check span { font-size: 13px; color: var(--fg-dim); }
.check em {
  display: block; font-style: normal; font-size: 11px; font-weight: 300;
  color: var(--fg-mute); margin-top: 5px; line-height: 1.55;
}
.form-actions { margin-top: 6px; }
.form-error {
  color: var(--tone-negative); font-size: 13px; line-height: 1.6;
  border-left: 2px solid var(--tone-negative); padding-left: 12px;
}

/* ══ The live recording screen ═══════════════════════════════════════════ */

.view-live { padding-bottom: 40px; }

.scoreboard {
  position: sticky; top: 49px; z-index: 15;
  display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: 16px;
  padding: 14px 18px;
  background: var(--bg-alt); border-bottom: 1px solid var(--hair);
}
.score { display: flex; align-items: center; justify-content: center; gap: 14px; min-width: 0; }
.score-team { display: flex; align-items: center; gap: 10px; min-width: 0; }
.score-name {
  font-size: 13px; font-weight: 600; color: var(--fg-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 22ch;
}
.score-num { font-size: 26px; font-weight: 700; font-variant-numeric: tabular-nums; }
.score-sep { color: var(--fg-mute); }

.clock-block { text-align: right; }
.clock {
  font-size: 30px; font-weight: 700; letter-spacing: -0.5px;
  font-variant-numeric: tabular-nums; line-height: 1;
}
.clock.is-running { color: var(--accent); }
.clock.is-paused { color: var(--tone-caution); }
.period {
  font-size: 10px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--fg-mute); margin-top: 5px;
}
.clock-controls { display: flex; gap: 8px; }
.clock-controls .btn { padding: 11px 14px; letter-spacing: 1.5px; }

/* ── Phone: the scoreboard must not eat the grid ──
 *
 * The first cut spent about 270px of an 844px screen on the header before a
 * single stat button appeared, so only four of the twelve core buttons were
 * visible and the official scrolled to reach a tackle. Everything here is in
 * service of one number: how many buttons are on screen without scrolling.
 * Compacted, the whole core panel fits.
 *
 * Nothing pressable shrinks below 44px. The savings come from padding, type
 * scale and putting the score on the same row as the back button — not from
 * making targets smaller, which would trade one problem for a worse one. */
@media (max-width: 820px) {
  .scoreboard {
    grid-template-columns: auto 1fr;
    grid-template-areas: "back clock" "score score" "controls controls";
    gap: 8px 10px;
    padding: 9px 12px;
    top: 45px;
  }
  .btn-back { grid-area: back; justify-self: start; padding: 10px 12px; font-size: 10px; }
  .score { grid-area: score; gap: 10px; }
  .clock-block { grid-area: clock; }
  .clock-controls { grid-area: controls; }
  .clock-controls .btn { flex: 1; padding: 12px 8px; font-size: 10px; letter-spacing: 1px; }
  .score-name { max-width: 11ch; font-size: 11px; }
  .score-num { font-size: 21px; }
  .clock { font-size: 25px; }
  .period { font-size: 9px; margin-top: 3px; }

  .topbar { padding: 8px 12px; }
  .side-btn { padding: 12px 10px; min-height: 46px; font-size: 12px; }
  .group-tab { padding: 12px 14px; min-height: 44px; font-size: 10px; }

  .stat-btn { min-height: 64px; padding: 11px 11px; gap: 6px; }
  .stat-label { font-size: 13px; }
  .stat-key { display: none; }        /* no keyboard on a phone */
  /* With the hotkey chip gone the footer has one child, and space-between drops
     it to the left where it reads as a stray digit. The running count is the
     thing an official glances at to check a press registered, so it belongs on
     the same edge on every button. */
  .stat-foot { justify-content: flex-end; }
  .stat-count { font-size: 12px; }

  .feed-head { padding: 18px 12px 10px; }
  .feed { padding: 0 12px; }
}

/* ── Landscape, and any short viewport ──
 *
 * A phone turned sideways has ~390px of height, and a sticky header sized for
 * portrait leaves almost nothing. Officials do turn the phone — the button
 * labels are easier to read wide — so this has to work rather than merely not
 * break. */
@media (max-height: 520px) {
  .scoreboard { padding: 6px 12px; gap: 6px; }
  .clock { font-size: 20px; }
  .score-num { font-size: 18px; }
  .period { display: none; }
  .side-btn { min-height: 40px; padding: 9px; }
  .group-tab { min-height: 38px; padding: 9px 14px; }
  .stat-btn { min-height: 54px; }
  .sheet-panel { max-height: 96dvh; }
  .sheet-def { display: none; }   /* the definition is on the button's title */
}

/* ── Warnings ──
   Surfaced, never swallowed. The server accepts an event outside a player's
   spell and says so; hiding that would leave a count nobody could explain. */
.warnbar {
  margin: 0; padding: 12px 18px;
  background: #241d12; border-bottom: 1px solid #3d3018;
  font-size: 12px; line-height: 1.6; color: #e0cfa8;
}
.warnbar strong { color: #fff; font-weight: 600; }
.warnbar ul { margin: 6px 0 0 18px; }
.warnbar .dismiss { float: right; }

/* ── Side toggle ──
   Which team the next event belongs to. Big, and it says the team name rather
   than "home"/"away", because an official under pressure reads a name faster
   than a role. */
.side-toggle {
  display: grid; grid-template-columns: 1fr 1fr; gap: 0;
  border-bottom: 1px solid var(--hair);
}
.side-btn {
  background: var(--bg); border: none; border-bottom: 3px solid transparent;
  color: var(--fg-mute); font-family: inherit; font-size: 13px; font-weight: 600;
  padding: 15px 12px; cursor: pointer; min-height: 52px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.side-btn.is-on { color: var(--fg); border-bottom-color: var(--accent); background: var(--surface); }

/* ── Group tabs ── */
.group-tabs {
  display: flex; gap: 0; overflow-x: auto; -webkit-overflow-scrolling: touch;
  border-bottom: 1px solid var(--hair); background: var(--bg-alt);
}
.group-tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  color: var(--fg-mute); font-family: inherit; font-size: 11px; font-weight: 700;
  letter-spacing: 1.5px; text-transform: uppercase;
  padding: 14px 18px; cursor: pointer; white-space: nowrap; min-height: 46px;
}
.group-tab.is-on { color: var(--accent); border-bottom-color: var(--accent); }

/* ── The stat grid ──
   auto-fill rather than a fixed column count so twelve buttons and four buttons
   both look deliberate, and so a tablet held landscape uses the width. */
.stat-grid {
  display: grid; gap: 10px; padding: 16px 18px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}
@media (max-width: 560px) {
  .stat-grid { grid-template-columns: repeat(2, 1fr); gap: 8px; padding: 12px; }
}
.stat-btn {
  position: relative;
  background: var(--surface); border: 1px solid var(--hair);
  color: var(--fg); font-family: inherit; cursor: pointer;
  padding: 16px 14px; text-align: left; min-height: 74px;
  display: flex; flex-direction: column; justify-content: space-between; gap: 8px;
  transition: border-color 0.12s, background 0.12s, transform 0.06s;
}
.stat-btn:hover { border-color: #3a3a3a; background: var(--surface-2); }
.stat-btn:active { transform: scale(0.985); }
.stat-btn:disabled { opacity: 0.32; cursor: not-allowed; }
.stat-btn:disabled:hover { border-color: var(--hair); background: var(--surface); }
.stat-label { font-size: 14px; font-weight: 600; line-height: 1.25; }
.stat-foot { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.stat-key {
  font-size: 9px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  color: var(--fg-mute); border: 1px solid var(--hair); padding: 2px 6px;
}
.stat-count {
  font-size: 11px; font-weight: 700; color: var(--fg-mute);
  font-variant-numeric: tabular-nums;
}
.stat-btn[data-tone="score"]    { border-left: 3px solid var(--tone-score); }
.stat-btn[data-tone="positive"] { border-left: 3px solid var(--tone-positive); }
.stat-btn[data-tone="negative"] { border-left: 3px solid var(--tone-negative); }
.stat-btn[data-tone="caution"]  { border-left: 3px solid var(--tone-caution); }
.stat-btn[data-tone="neutral"]  { border-left: 3px solid var(--tone-neutral); }

/* ── The feed ── */
.feed-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 22px 18px 12px; border-top: 1px solid var(--hair); margin-top: 12px;
}
.feed-head h2 { font-size: 12px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase; color: var(--fg-mute); }
.feed-actions { display: flex; gap: 16px; }
.feed { list-style: none; padding: 0 18px; display: flex; flex-direction: column; }
.feed-row {
  display: grid; grid-template-columns: 58px 1fr auto; gap: 12px;
  align-items: center; padding: 11px 0; border-bottom: 1px solid var(--hair);
  font-size: 13px;
}
.feed-row.is-voided { opacity: 0.35; }
.feed-row.is-voided .feed-what { text-decoration: line-through; }
.feed-min { font-weight: 700; color: var(--fg-mute); font-variant-numeric: tabular-nums; font-size: 12px; }
.feed-what { min-width: 0; }
.feed-type { font-weight: 600; }
.feed-who { color: var(--fg-mute); font-size: 12px; margin-top: 2px; }
.feed-who .side-tag { color: var(--fg-dim); }
.feed-warn { color: var(--tone-caution); font-size: 11px; margin-top: 4px; line-height: 1.5; }
.feed-undo { font-size: 10px; letter-spacing: 1.5px; text-transform: uppercase; }

/* ══ The player chooser ══════════════════════════════════════════════════
 *
 * A bottom sheet rather than a centred modal: on a phone held one-handed, the
 * bottom half of the screen is the part a thumb reaches, and this is the
 * control the official uses more than any other.
 */

.sheet {
  position: fixed; inset: 0; z-index: 40;
  background: rgba(0,0,0,0.72);
  display: flex; align-items: flex-end; justify-content: center;
}
.sheet-panel {
  width: 100%; max-width: 720px; max-height: 88vh; max-height: 88dvh;
  background: var(--bg-alt); border: 1px solid var(--hair); border-bottom: none;
  display: flex; flex-direction: column;
  animation: rise 0.14s ease-out;
}
@keyframes rise { from { transform: translateY(14px); opacity: 0.6 } to { transform: none; opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .sheet-panel { animation: none; } }

.sheet-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 16px;
  padding: 18px 20px 12px; border-bottom: 1px solid var(--hair);
}
.sheet-kicker {
  font-size: 10px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--accent); margin-bottom: 6px;
}
.sheet-head h2 { font-size: 19px; font-weight: 600; }
.sheet-close {
  background: none; border: none; color: var(--fg-mute); font-size: 30px;
  line-height: 1; cursor: pointer; padding: 0 4px; min-width: 44px; min-height: 44px;
}
.sheet-close:hover { color: var(--fg); }

/* The definition, on the chooser rather than buried in a help page. It is the
   thing that makes two officials' counts comparable, and it is only read if it
   is in front of somebody at the moment they are deciding. */
.sheet-def {
  padding: 12px 20px; font-size: 12px; font-weight: 300; line-height: 1.6;
  color: var(--fg-mute); border-bottom: 1px solid var(--hair);
  max-height: 20vh; overflow-y: auto;
}

.sheet-players {
  padding: 14px 20px 20px; overflow-y: auto; -webkit-overflow-scrolling: touch;
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); gap: 8px;
}
/* The confirm-the-end-of-the-match sheet has no players in it, and the padding
   above left a dead band between the question and the buttons. `:empty` is exact
   here — the grid is either filled with player buttons or has nothing at all. */
.sheet-players:empty { display: none; }
/* minmax(0, …), not 1fr: a 1fr track cannot shrink below its longest nowrap
   name, and BeSoccer's full names ("Godspower Akekoromowei") pushed the second
   column off a 390px screen. The name ellipsises instead. */
@media (max-width: 560px) { .sheet-players { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.player-btn {
  display: flex; align-items: center; gap: 10px;
  background: var(--surface); border: 1px solid var(--hair); color: var(--fg);
  font-family: inherit; text-align: left; cursor: pointer;
  padding: 12px 12px; min-height: 56px; min-width: 0;
  transition: border-color 0.12s, background 0.12s;
}
.player-btn:hover { border-color: var(--accent-soft); background: var(--surface-2); }
.player-btn.is-off { opacity: 0.45; }
.player-num {
  font-size: 16px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--accent); min-width: 26px; text-align: center; flex: none;
}
.player-body { min-width: 0; }
.player-name {
  font-size: 13px; font-weight: 500; line-height: 1.25;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.player-pos { font-size: 10px; color: var(--fg-mute); letter-spacing: 1px; text-transform: uppercase; margin-top: 2px; }

/* The outcome chooser — what a corner produced. One column, full width, because
   there are only ever four of them and a two-up grid of short words reads as a
   keypad the thumb has to aim at. Tone comes from the catalogue, drawn the same
   way as the stat buttons so "straight out" is the same red everywhere. */
.choice-btn {
  grid-column: 1 / -1;
  display: flex; align-items: center;
  background: var(--surface); border: 1px solid var(--hair); color: var(--fg);
  font-family: inherit; text-align: left; cursor: pointer;
  padding: 12px 14px; min-height: 56px;
  transition: border-color 0.12s, background 0.12s;
}
.choice-btn:hover { border-color: var(--accent-soft); background: var(--surface-2); }
.choice-btn[data-tone="score"]    { border-left: 3px solid var(--tone-score); }
.choice-btn[data-tone="positive"] { border-left: 3px solid var(--tone-positive); }
.choice-btn[data-tone="negative"] { border-left: 3px solid var(--tone-negative); }
.choice-btn[data-tone="caution"]  { border-left: 3px solid var(--tone-caution); }
.choice-btn[data-tone="neutral"]  { border-left: 3px solid var(--tone-neutral); }
.choice-label { font-size: 15px; font-weight: 600; }

.squad-head {
  grid-column: 1 / -1; font-size: 10px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--fg-mute); padding: 10px 0 2px;
}
.squad-head:first-child { padding-top: 0; }

.sheet-foot {
  padding: 14px 20px; border-top: 1px solid var(--hair);
  display: flex; gap: 10px; justify-content: flex-end;
}

/* ══ Toast ═══════════════════════════════════════════════════════════════ */

.toast {
  position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%);
  z-index: 60; max-width: min(92vw, 520px);
  background: var(--surface-2); border: 1px solid var(--hair);
  border-left: 3px solid var(--accent);
  padding: 13px 18px; font-size: 13px; line-height: 1.5; color: var(--fg-dim);
  box-shadow: 0 8px 30px rgba(0,0,0,0.5);
}
.toast.is-bad { border-left-color: var(--tone-negative); color: #f0d5d5; }
.toast strong { color: var(--fg); font-weight: 600; }

/* A match list served from the device rather than the server. Stated plainly
   rather than left to the sync chip: the list may be short or stale, and an
   official who does not know that will read a missing fixture as data loss. */
.offline-note {
  border: 1px solid #3d3018; background: #241d12; color: #e0cfa8;
  padding: 12px 14px; font-size: 12px; line-height: 1.55; margin-bottom: 12px;
}

/* ══ Team sheets ═════════════════════════════════════════════════════════ */

.squad-hint {
  font-size: 12px; font-weight: 300; line-height: 1.65; color: var(--fg-mute);
  margin: 16px 0 12px;
}
.squad-hint code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; background: var(--surface-2); border: 1px solid var(--hair);
  padding: 1px 5px; color: var(--fg-dim);
}
.squad-hint strong { color: var(--fg-dim); font-weight: 600; }

#squad-text {
  width: 100%; background: var(--surface); border: 1px solid var(--hair);
  color: var(--fg); font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  /* 16px, or iOS zooms the page the moment this is focused — and a zoomed page
     with a keyboard up is how an official loses the Save button. */
  font-size: 16px; line-height: 1.7; padding: 14px; outline: none; resize: vertical;
  transition: border-color 0.15s;
}
#squad-text:focus { border-color: var(--accent); }

.squad-preview { margin-top: 16px; }
.squad-count {
  font-size: 11px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
  color: var(--fg-mute); margin-bottom: 10px;
}
.squad-problem {
  border-left: 2px solid var(--tone-caution); background: #241d12;
  color: #e0cfa8; padding: 10px 12px; font-size: 12px; line-height: 1.55;
  margin-bottom: 8px;
}
.squad-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px; }
@media (max-width: 560px) { .squad-list { grid-template-columns: 1fr; } }
.squad-row {
  display: grid; grid-template-columns: 32px 1fr auto; gap: 10px; align-items: center;
  background: var(--surface); border: 1px solid var(--hair);
  border-left: 3px solid var(--hair);
  padding: 9px 11px; font-size: 13px;
}
/* The starting eleven carry the accent edge, so miscounting the "--" line is
   visible at a glance rather than only in the warning text. */
.squad-row.is-xi { border-left-color: var(--accent); }
.squad-num { font-weight: 700; color: var(--accent); text-align: center; font-variant-numeric: tabular-nums; }
.squad-row:not(.is-xi) .squad-num { color: var(--fg-mute); }
.squad-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.squad-pos { font-size: 10px; letter-spacing: 1px; color: var(--fg-mute); text-transform: uppercase; }

.squad-actions { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 20px; }

.squad-team { margin-top: 16px; }

/* Single-team mode: the side toggle becomes a label naming the team being
   recorded. Not hidden entirely — an official glancing down mid-match needs to
   see whose tackle they are about to record. */
.side-toggle.is-single { display: block; }
.side-single {
  display: flex; align-items: baseline; justify-content: center; gap: 10px;
  padding: 13px 12px; background: var(--surface);
  border-bottom: 3px solid var(--accent);
}
.side-single-label {
  font-size: 9px; font-weight: 700; letter-spacing: 2px;
  text-transform: uppercase; color: var(--fg-mute);
}
.side-single-team {
  font-size: 14px; font-weight: 600; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.field > span em {
  font-style: normal; font-weight: 400; letter-spacing: 0.5px;
  text-transform: none; color: #5c5c5c;
}

/* The tracked side on the scoreboard. Only one team is recorded, and this is
   what says which — the opponent is named but their number is made entirely of
   our own keeper's goal_conceded events. */
.score-team.is-tracked .score-name { color: var(--fg); font-weight: 700; }
.score-team:not(.is-tracked) .score-name { color: var(--fg-mute); }
.score-team:not(.is-tracked) .score-num { color: var(--fg-dim); }

/* A field whose value the server will override. Disabled rather than silently
   ignored — see wireSetup(). */
.field input.is-overridden, .field input:disabled {
  opacity: 0.45; cursor: not-allowed; border-style: dashed;
}

/* ── Both teams (27 Oct 2026) ──────────────────────────────────────────────── */

.sides-pick { border: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 12px; }
.sides-pick legend {
  font-size: 10px; font-weight: 700; letter-spacing: 2px; text-transform: uppercase;
  color: var(--fg-mute); margin-bottom: 10px; padding: 0;
}
.check input[type="radio"] { border-radius: 50%; }

/* Both squads side by side in the player chooser, home left as on the scoreboard.
   Two columns even at 390px: eleven 56px rows per side is what makes "the shirt
   you tap is the team" work — a stacked list would push the away XI off screen. */
.sheet-cols {
  grid-column: 1 / -1; display: grid; grid-template-columns: 1fr 1fr; gap: 10px;
}
.sheet-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.sheet-col-head {
  font-size: 12px; font-weight: 700; color: var(--fg); padding-bottom: 6px;
  border-bottom: 2px solid var(--accent-soft);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sheet-col[data-side="away"] .sheet-col-head { border-bottom-color: var(--tone-neutral); }
.sheet-col .player-btn { padding: 10px 8px; gap: 8px; min-height: 52px; }
.sheet-col .squad-head { padding-top: 6px; }
.sheet-col-empty { font-size: 12px; color: var(--fg-mute); padding: 8px 0; }

/* The second official's phone has no clock buttons; it says whose clock it is. */
.clock-note { font-size: 11px; color: var(--fg-mute); align-self: center; max-width: 24ch; line-height: 1.4; }

.match-card-role {
  font-size: 10px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase;
  color: var(--accent); margin-top: 6px;
}

.official-form { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 10px; }
.official-form input {
  background: var(--surface); border: 1px solid var(--hair); color: var(--fg);
  font-family: inherit; font-size: 15px; padding: 12px; min-height: 46px;
}
.official-form input:focus { border-color: var(--accent); outline: none; }

/* Away rows in the feed carry a quiet marker so two teams' entries do not read
   as one list. Home stays unmarked: on a one-team match nothing changes. */
.feed-row[data-side="away"] .side-tag { border-bottom: 1px dashed var(--fg-mute); }

.squad-tabs { margin-bottom: 14px; }

/* ══ BeSoccer loader ═════════════════════════════════════════════════════ */

.bs-load { margin-top: 14px; }
.bs-panel { margin-top: 16px; }
.bs-panel select { width: 100%; }
#bs-count.is-over { color: var(--tone-caution); }
.bs-list { display: flex; flex-direction: column; gap: 6px; }
.bs-row {
  display: grid; grid-template-columns: 48px 1fr; align-items: stretch;
  background: var(--surface); border: 1px solid var(--hair);
  border-left: 3px solid var(--hair);
}
/* The same edge as a parsed sheet: accent for the XI, muted for the bench. */
.bs-row[data-state="xi"] { border-left-color: var(--accent); }
.bs-row[data-state="bench"] { border-left-color: var(--fg-mute); }
.bs-num {
  /* 16px, or iOS zooms on focus. */
  width: 100%; background: transparent; border: none; border-right: 1px solid var(--hair);
  color: var(--accent); font-weight: 700; font-size: 16px; text-align: center;
  font-variant-numeric: tabular-nums; outline: none;
  -webkit-user-select: text; user-select: text;
}
.bs-num:focus { background: var(--surface-2); }
/* A number BeSoccer does not list: amber, so it is found before Use is pressed. */
.bs-num:placeholder-shown { background: #241d12; }
.bs-num.is-dup { background: #241d12; color: var(--tone-caution); }
.bs-body {
  display: grid; grid-template-columns: 1fr auto 44px; gap: 10px; align-items: center;
  min-height: 48px; padding: 0 12px; background: none; border: none; color: var(--fg);
  font: inherit; font-size: 14px; text-align: left; cursor: pointer;
}
.bs-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bs-row[data-state=""] .bs-name { color: var(--fg-dim); }
.bs-pos { font-size: 10px; letter-spacing: 1px; color: var(--fg-mute); }
.bs-tag {
  font-size: 9px; font-weight: 700; letter-spacing: 1.5px; text-transform: uppercase;
  text-align: right; color: var(--fg-mute);
}
.bs-row[data-state="xi"] .bs-tag::after { content: "XI"; color: var(--accent); }
.bs-row[data-state="bench"] .bs-tag::after { content: "Bench"; }
.bs-source { font-size: 11px; color: var(--fg-mute); margin-top: 12px; }
