/* NGS — ngs.telestrator.com
   Three visual languages in one sheet:
     1. the admin chrome (dashboard, games, plays, settings) — plain and dense
     2. .board-*  the LIVE THIS PLAY strip   (?theme=strip)
     3. .bd-* / .ps-*  the three-column Play Stats board (?theme=panel and ?theme=blocks,
                  #10252); .ps-* is its score header and banner
   All board themes are live and selectable per URL (?theme=), because each was
   approved by different people. They are namespaced apart so neither can drift into
   the other, or into the admin pages; since #10249 they share one palette and typeface,
   the --b-* variables set on .board-page / .ps-page, which the admin pages never see. */

/* The admin palette and type (#10257) are the board's (#10249, the --b-* values below):
   near-black neutral surfaces, Archivo, green for live and the primary action. Kept as
   their own names because the admin pages never see .board-page / .ps-page. */
:root {
  --bg: #0b0e12;
  --panel: #12161c;
  --panel-2: #1d222b;
  --sunk: #0f1318;
  --line: #232933;
  --line-strong: #2e3541;
  --ink: #f1f3f6;
  --ink-dim: #a7afbc;
  --ink-faint: #6e7785;
  --accent: #5bd66b;
  --accent-ink: #07210c;
  --accent-dim: #3f9e4c;
  --red: #f0605a;
  --amber: #f2b53f;
  --blue: #5aa7f0;
  --radius: 12px;
  --side-w: 232px;
  --font: "Archivo", "Helvetica Neue", Arial, system-ui, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--ink);
  font: 15px/1.5 var(--font);
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: #8fe69a; }

/* Small uppercase label: sidebar group headings, stat tiles, section heads. */
.eyebrow, .side-group, .stat .k, th {
  font-size: 12px; font-weight: 600; font-stretch: 80%; letter-spacing: .09em;
  text-transform: uppercase; color: var(--ink-faint);
}

/* ---------- sidebar (#10257) ----------
   Fixed down the left of every signed-in page; body.has-side pads the page past it.
   Below 900px it turns into a bar across the top that wraps, so a phone still reaches
   every page. */
.side {
  position: fixed; inset: 0 auto 0 0; z-index: 20; width: var(--side-w);
  display: flex; flex-direction: column; gap: 20px; padding: 20px 14px 16px;
  background: var(--panel); border-right: 1px solid var(--line); overflow-y: auto;
}
body.has-side { padding-left: var(--side-w); }
.side-brand { position: relative; display: flex; align-items: center; gap: 8px; padding: 0 6px; min-height: 36px; }
/* The DEV badge sits in the corner, clear of the lockup's wordmark. */
.side-brand .env-badge { position: absolute; top: -12px; right: -6px; padding: 1px 6px; font-size: 10px; }
.brand { display: flex; align-items: center; min-width: 0; color: var(--ink); }
.brand:hover { color: var(--ink); }
/* The BoothIQ lockup (#10266, the structure is #10257's): sized from --brand, the mark's
   height. The wordmark is Archivo upright and a little narrow, mixed case, "IQ" in the
   Next Gen Stats green of the mark's mic dot. */
.brand-lockup { display: inline-flex; align-items: center; gap: calc(var(--brand) * .3); color: var(--ink); }
.brand-mark { flex-shrink: 0; color: var(--ink); }
.brand-text { display: flex; flex-direction: column; gap: calc(var(--brand) * .1); min-width: 0; }
.brand-word {
  font-size: calc(var(--brand) * .64); line-height: 1; font-weight: 800; font-stretch: 90%; font-style: normal;
  letter-spacing: -.012em; white-space: nowrap;
}
.brand-word em { font-style: normal; color: #8dc63f; }
.brand-powered { display: flex; align-items: center; gap: calc(var(--brand) * .18); }
.brand-powered span {
  font-size: max(8px, calc(var(--brand) * .24)); font-weight: 600; font-stretch: 85%; letter-spacing: .16em;
  text-transform: uppercase; color: var(--ink-faint); white-space: nowrap;
}
.brand-powered img { height: calc(var(--brand) * .44); width: auto; }
.brand-login { margin-bottom: 18px; }
/* The deployment badge (#10247): amber so it reads as a warning, not a nav item; only
   rendered when config.php sets env_label, so production has no element here at all. */
.env-badge {
  margin-left: auto; padding: 2px 7px; border-radius: 5px;
  background: rgba(242, 181, 63, .14); color: var(--amber); font-size: 11px; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; white-space: nowrap;
}
.btn.side-live { display: flex; align-items: center; justify-content: center; gap: 8px; height: 40px; }
.side-nav, .side-foot { display: flex; flex-direction: column; gap: 2px; }
.side-group { padding: 14px 10px 6px; }
.side-group:first-child { padding-top: 0; }
.side-nav a, .side-foot > a {
  display: flex; align-items: center; gap: 12px; height: 38px; padding: 0 10px;
  border-radius: 7px; color: var(--ink-dim); font-size: 14px;
}
.side-nav a:hover, .side-foot > a:hover { background: var(--panel-2); color: var(--ink); }
.side-nav a.on, .side-foot > a.on {
  background: var(--panel-2); color: var(--ink); font-weight: 600; box-shadow: inset 3px 0 0 var(--accent);
}
.side-nav a.on .icon, .side-foot > a.on .icon { color: var(--accent); }
.icon { flex-shrink: 0; }
.side-count { margin-left: auto; font-style: normal; font-size: 12px; font-weight: 600; color: var(--accent); }
.side-count-replays { color: var(--blue); }
.side-foot { margin-top: auto; }
.side-user {
  display: flex; align-items: center; gap: 6px; margin-top: 10px; padding: 12px 0 0 4px;
  border-top: 1px solid var(--line);
}
/* side-who, not who: the strip board's substitution names are .who inside a column flex
   cell, and this rule's flex, padding and hover would reach them on air (review, #10257). */
.side-who { display: flex; align-items: center; gap: 10px; min-width: 0; flex: 1; padding: 4px 6px; border-radius: 7px; color: var(--ink); }
.side-who:hover { background: var(--panel-2); color: var(--ink); }
.avatar {
  display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: 34px; height: 34px;
  border-radius: 50%; background: var(--panel-2); border: 1px solid var(--line-strong); font-size: 13px; font-weight: 700;
}
.who-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.25; }
.who-text strong { font-size: 14px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.who-text em { font-style: normal; font-size: 12px; color: var(--ink-faint); }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }
/* Technical details (#10257): hidden unless the sidebar switch is on in this browser. */
body:not(.show-tech) .tech { display: none !important; }
body.show-tech .no-tech { display: none !important; }
.side-tech { padding: 8px 10px 2px; font-size: 13px; color: var(--ink-faint); }
.side-signout {
  display: flex; padding: 7px; border: 0; border-radius: 7px; background: transparent;
  color: var(--ink-faint); cursor: pointer;
}
.side-signout:hover { background: var(--panel-2); color: var(--ink); }
.inline { display: inline; margin: 0; }

@media (max-width: 900px) {
  body.has-side { padding-left: 0; }
  .side {
    position: static; width: auto; flex-direction: row; flex-wrap: wrap; align-items: center;
    gap: 8px 12px; padding: 12px 16px; border-right: 0; border-bottom: 1px solid var(--line); overflow: visible;
  }
  .side-nav, .side-foot { flex-direction: row; flex-wrap: wrap; width: 100%; margin: 0; }
  .side-group { display: none; }
  .side-nav a, .side-foot > a { height: 34px; }
  .side-user { margin: 0; padding: 0; border: 0; }
  .side-tech { padding: 0 6px; }
  .side-brand .env-badge { position: static; }
}
/* A phone (#10265): the bar above was seven rows on a 390px screen - the nav wrapped to
   three and the account row sat alone under Demo board / FAQ / Technical details. So the
   foot dissolves into the bar itself (display: contents): the account button goes up
   beside the brand, as its avatar alone, and Demo board, FAQ and the switch share a row;
   the nav links pack closer in their own rows; the live board button keeps a row to
   itself as the one primary action. The nav stays a real box: display: contents on a
   <nav> drops its landmark from the accessibility tree in Safari (review, #10265). */
@media (max-width: 600px) {
  .side { gap: 6px 8px; padding: 10px 16px; }
  .side-nav { gap: 6px 8px; }
  .side-foot { display: contents; }
  .side-brand { order: -2; }
  .side-user { order: -1; margin-left: auto; }
  .who-text { display: none; }
  .side-live { flex: 1 1 100%; }
  .side-nav a, .side-foot > a { gap: 8px; padding: 0 8px; }
  .side-tech { padding: 0 4px; }
}

/* ---------- layout ---------- */
.wrap { max-width: 1240px; margin: 0 auto; padding: 32px 40px 60px; }
.wrap-narrow { max-width: 420px; padding-top: 80px; }
.foot {
  display: flex; justify-content: space-between;
  max-width: 1240px; margin: 0 auto; padding: 16px 40px 30px;
  color: var(--ink-faint); font-size: 12px;
}
@media (max-width: 900px) { .wrap, .foot { padding-left: 16px; padding-right: 16px; } }

h1 { font-size: 34px; line-height: 1.15; margin: 0 0 6px; font-weight: 700; letter-spacing: -.01em; }
h2 { font-size: 16px; margin: 28px 0 10px; font-weight: 650; }
.sub { color: var(--ink-dim); font-size: 15px; margin: 0 0 24px; }

/* ---------- cards / panels ---------- */
.card {
  background: var(--panel); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 18px; margin-bottom: 16px;
}
.card h3 { margin: 0 0 12px; font-size: 14px; font-weight: 650; color: var(--ink-dim);
  text-transform: uppercase; letter-spacing: .6px; }

.grid { display: grid; gap: 16px; }
.grid-3 { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); }
.grid-4 { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }

.stat { display: flex; flex-direction: column; gap: 6px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); padding: 16px 18px; }
.stat .v { font-size: 32px; line-height: 1.1; font-weight: 700; font-stretch: 85%; font-variant-numeric: tabular-nums; }
.stat .v.ok { color: var(--accent); }
.stat .v.bad { color: var(--red); }
.stat .v.warn { color: var(--amber); }
.stat .n { font-size: 13px; color: var(--ink-faint); }

/* ---------- tables ---------- */
table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--line); }
th { font-size: 11px; }
tbody tr:hover { background: var(--panel-2); }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: var(--mono); font-size: 13px; }
/* A cell of free text that may hold one long token (the poll-error message, a URL in it):
   wrap it anywhere at every width, or the token sets the table's width (#10265). Not
   "wrap", which is the page container's class. */
td.cell-wrap { overflow-wrap: anywhere; }

/* ---------- forms / buttons ---------- */
label { display: block; font-size: 12px; font-weight: 600; color: var(--ink-dim); margin: 12px 0 6px; text-transform: uppercase; letter-spacing: .06em; font-stretch: 85%; }
input[type=text], input[type=password], input[type=email], select {
  width: 100%; padding: 9px 11px; background: var(--bg); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 8px; font-size: 14px; font-family: inherit;
}
input:focus, select:focus { outline: 2px solid var(--accent-dim); outline-offset: -1px; }
.btn {
  display: inline-block; padding: 8px 14px; border-radius: 8px; border: 1px solid var(--line-strong);
  background: var(--panel-2); color: var(--ink); font-size: 14px; font-weight: 600; font-family: inherit;
  cursor: pointer; text-decoration: none;
}
.btn:hover:not(:disabled) { background: var(--line-strong); color: var(--ink); }
/* A disabled button must look disabled: without this a greyed-out Save read as a live one
   that did nothing (preset editor, #10222). The hover rules are guarded rather than
   pointer-events switched off, so a disabled button's title tooltip stays reachable. */
.btn:disabled { opacity: .4; cursor: default; }
.btn-primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 700; }
.btn-primary:hover:not(:disabled) { background: #7ee28b; border-color: #7ee28b; color: var(--accent-ink); }
.btn-live { border-color: var(--accent-dim); color: var(--accent); }
.btn-quiet { background: transparent; color: var(--ink-faint); }
.btn-sm { padding: 6px 11px; font-size: 13px; border-radius: 7px; }
/* The TV-mode launcher beside a board link (#10268): quiet, since the live board is the
   ordinary way in and this one is for whoever is following from their sofa. */
.btn-tv { display: inline-flex; align-items: center; gap: 6px; color: var(--ink-faint); }
.btn-tv:hover { color: var(--ink); }
.side-launch { display: flex; flex-direction: column; gap: 6px; }
.side-launch .btn-tv { justify-content: center; }
.btn-row { display: flex; gap: 10px; margin-top: 18px; align-items: center; }

/* ---------- page heads, section heads, status pill (#10257) ---------- */
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px 24px; flex-wrap: wrap; margin-bottom: 24px; }
.page-head .sub { margin: 0; }
.pill {
  display: inline-flex; align-items: center; gap: 8px; padding: 8px 14px; border: 1px solid var(--line);
  border-radius: 999px; font-size: 13px; color: var(--ink-dim);
}
.pill b { color: var(--ink); font-weight: 600; }
.pill .dot, .game-state .dot { width: 8px; height: 8px; border-radius: 50%; background: var(--ink-faint); flex-shrink: 0; }
.pill-ok .dot { background: var(--accent); }
.pill-bad { border-color: rgba(240, 96, 90, .45); }
.pill-bad .dot { background: var(--red); }
h2.sec {
  display: flex; align-items: center; gap: 12px; margin: 32px 0 14px;
  font-size: 12px; font-weight: 600; font-stretch: 80%; letter-spacing: .09em; text-transform: uppercase; color: var(--ink-dim);
}
h2.sec::after { content: ""; flex: 1; height: 1px; background: var(--line); order: 1; }
h2.sec .sec-link { order: 2; font-size: 13px; font-weight: 500; letter-spacing: 0; text-transform: none; font-stretch: 100%; }
.tag { padding: 1px 6px; border-radius: 4px; font-size: 11px; font-weight: 700; letter-spacing: .04em; }
.tag-amber { background: rgba(242, 181, 63, .14); color: var(--amber); }
.tag-blue { background: rgba(90, 167, 240, .14); color: var(--blue); }

/* ---------- Dashboard: followed game cards, matchup cells (#10257) ---------- */
.game-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 440px), 1fr)); gap: 16px; }
.game-card { display: flex; flex-direction: column; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.game-card-head {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
  padding: 12px 16px; border-bottom: 1px solid var(--panel-2); font-size: 13px;
}
.game-state { display: flex; align-items: center; gap: 7px; font-weight: 600; color: var(--accent); }
.game-state .dot { width: 7px; height: 7px; background: var(--accent); }
.game-state.is-replay { color: var(--blue); }
.game-clock { color: var(--ink-dim); }
.game-clock.is-stale { color: var(--amber); }
.num { font-variant-numeric: tabular-nums; }
.game-card-teams { display: flex; flex-direction: column; gap: 10px; padding: 16px; }
.game-team { display: flex; align-items: center; gap: 12px; }
.game-team img { width: 36px; height: 36px; object-fit: contain; }
.game-team-abbr { width: 36px; text-align: center; font-weight: 700; font-stretch: 85%; color: var(--ink-dim); }
.game-team-name { font-size: 17px; font-weight: 600; }
.game-score { margin-left: auto; font-size: 24px; font-weight: 700; font-stretch: 85%; }
.game-score.is-behind { color: var(--ink-dim); }
.game-card-foot {
  display: flex; align-items: center; gap: 8px 18px; flex-wrap: wrap; margin-top: auto; padding: 12px 16px;
  background: var(--sunk); border-top: 1px solid var(--panel-2); font-size: 13px; color: var(--ink-faint);
}
.game-card-foot b { color: var(--ink-dim); font-weight: 600; }
.game-card-foot .badge { text-transform: none; letter-spacing: 0; font-size: 12px; }
.game-card-actions { display: flex; gap: 8px; margin-left: auto; }
.table-scroll { overflow-x: auto; }
/* A phone (#10265). The stat tiles go two per row (the 180px minimum fitted one column
   under 376px, a full screen of tiles before the games; Today's two tiles get the same),
   the Open a board form stays one column; the heading and the page padding give back
   some height, and the form's spacer label - there to line the button up with the
   selects in the desktop row - is not drawn. Tables: cells pad less, the matchup cell is
   its text alone, and a column marked .wide-only (the Latest plays measurements, which
   the Plays page carries in full) is left out, so the four that remain fit the width
   rather than sit off the edge of a card that scrolls sideways with nothing to say so.
   The first five rules - the page padding, h1, .page-head, h2.sec and the cell padding -
   are site-wide phone rules on purpose: every signed-in page has the same head and
   tables. The signed-out pages (.wrap-narrow: sign-in, register, forgot, reset, verify)
   keep their own 80px top, since the padding rule leaves them out. The rest of the block
   is Dashboard-only: its hooks exist on no other page. */
@media (max-width: 600px) {
  .wrap:not(.wrap-narrow) { padding-top: 20px; padding-bottom: 40px; }
  h1 { font-size: 26px; }
  .page-head { margin-bottom: 16px; }
  h2.sec { margin-top: 24px; }
  .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; }
  .open-board .grid-4 { grid-template-columns: minmax(0, 1fr); gap: 4px; }
  .stat { padding: 12px 14px; gap: 4px; }
  .stat .v { font-size: 26px; }
  .stat .n { font-size: 12px; }
  .field-spacer { display: none; }
  th, td { padding: 8px; }
  td.cell-wrap { min-width: 200px; }   /* the poll-error text: readable lines, not one word wide */
  .matchup img, .matchup .at { display: none; }
  .matchup b { margin-left: 0; }
  .wide-only { display: none; }
}
.game-detail { font-size: 13px; color: var(--ink-faint); }
/* The refresh writes the name alone (textContent), so the label is drawn, and only for a name. */
.game-watcher:not(:empty)::before { content: "Watched by "; }
.game-detail:empty { display: none; }
.today-cards .game-card-head .badge { font-size: 12px; text-transform: none; letter-spacing: 0; }
.status-guide { margin-top: 20px; font-size: 13px; color: var(--ink-dim); max-width: 980px; }
.status-guide summary { cursor: pointer; color: var(--accent); }
.status-guide p { margin: 10px 0 0; }
/* ---------- Games: the schedule, day by day (#10257) ---------- */
h2.sec .sec-count { font-weight: 500; color: var(--ink-faint); letter-spacing: 0; }
.sched-day { margin-bottom: 18px; }
.sched-date { display: flex; align-items: baseline; gap: 10px; margin: 0 0 8px 2px; font-size: 14px; }
.sched-date b { font-weight: 600; }
.sched-date span { font-size: 13px; color: var(--ink-faint); }
.card.sched-list { margin: 0; padding: 0; overflow: hidden; }
.sched-row { display: flex; align-items: center; gap: 8px 20px; flex-wrap: wrap; padding: 10px 16px; }
.sched-row + .sched-row { border-top: 1px solid var(--panel-2); }
.sched-row:hover { background: #161a21; }
.sched-time { width: 64px; flex-shrink: 0; font-size: 14px; color: var(--ink-dim); }
.sched-teams { display: flex; align-items: center; gap: 12px; flex: 1 1 420px; min-width: 0; }
.sched-team { display: flex; align-items: center; gap: 10px; flex: 1 1 0; min-width: 0; font-size: 15px; font-weight: 600; }
.sched-team span:last-child { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sched-team img { width: 28px; height: 28px; object-fit: contain; flex-shrink: 0; }
.sched-abbr { width: 28px; text-align: center; font-size: 12px; font-weight: 700; color: var(--ink-dim); }
.sched-at { color: var(--ink-faint); font-size: 13px; flex-shrink: 0; }
.sched-meta { font-size: 13px; color: var(--ink-faint); white-space: nowrap; }
.sched-meta.mono { font-size: 12px; }

.matchup { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.matchup img { width: 22px; height: 22px; object-fit: contain; }
.matchup .at { color: var(--ink-faint); }
.matchup b { margin-left: 4px; font-weight: 600; }

/* ---------- settings forms: My account (#10257) ---------- */
.acct-head { justify-content: flex-start; align-items: center; gap: 18px; }
.avatar-lg { width: 64px; height: 64px; font-size: 22px; }
.acct { display: flex; flex-direction: column; gap: 16px; max-width: 920px; }
.form-card { margin: 0; padding: 20px 24px 4px; }
.form-card-head h2 { margin: 0; font-size: 18px; font-weight: 700; }
.form-card-head p { margin: 4px 0 16px; font-size: 14px; color: var(--ink-dim); }
.form-row { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 6px 24px; align-items: start; padding: 16px 0; border-top: 1px solid var(--panel-2); }
.form-row-label label, .form-row-title {
  display: block; margin: 0; padding-top: 9px; font-size: 14px; font-weight: 600; color: var(--ink);
  text-transform: none; letter-spacing: 0; font-stretch: 100%;
}
.form-row-label small { display: block; margin-top: 3px; font-size: 13px; color: var(--ink-faint); }
.form-row-control > input, .form-row-control > select { max-width: 440px; }
.form-row-control .switch { padding-top: 9px; }
.form-row-control .switch + .switch { margin-top: 10px; }
.switch.switch-wrap { white-space: normal; align-items: flex-start; font-size: 14px; color: var(--ink); line-height: 1.4; }
.switch.switch-wrap input + span { margin-top: 1px; }
.form-note { margin: 0; padding: 14px 0 16px; border-top: 1px solid var(--panel-2); font-size: 13px; color: var(--ink-faint); overflow-wrap: anywhere; }
.form-actions { display: flex; justify-content: flex-end; }
@media (max-width: 700px) { .form-row { grid-template-columns: 1fr; } .form-row-label label, .form-row-title { padding-top: 0; } }

/* ---------- flash / badges ---------- */
.flash { padding: 11px 14px; border-radius: 8px; margin-bottom: 18px; font-size: 14px; border: 1px solid; }
/* The supervisor banner is toggled by today.php's refresh. .flash does not set `display`,
   so the UA sheet's [hidden] already wins - but only until someone gives .flash a
   `display`, at which point the banner would come back silently and permanently. Two
   classes of specificity, no !important. */
.flash[hidden] { display: none; }
/* Same trap, same cure (#10201): .inline and .btn DO set `display`, so an author-origin
   declaration beats the UA sheet's [hidden] whatever the specificity — the Replays
   page's Replay/Stop swap and the preset editor's Cycle button toggle by the attribute. */
.inline[hidden], .btn[hidden] { display: none; }
/* The global `select` is a form-card control: full width, 9px padding, 14px text. In the
   Replays actions cell that fills the cell — pushing the row past the card edge — and stands
   taller than the btn-sm buttons beside it. Same override the preset editor's preview bar uses. */
#replays select { width: auto; padding: 5px 8px; font-size: 13px; vertical-align: middle; }
.flash-success { background: rgba(91,214,107,.10); border-color: var(--accent-dim); color: #b8f088; }
.flash-error { background: rgba(240,96,90,.10); border-color: var(--red); color: #ffb4b4; }

.badge { display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 11px;
  text-transform: uppercase; letter-spacing: .5px; font-weight: 600; }
.badge-on { background: rgba(91,214,107,.14); color: var(--accent); }
.badge-off { background: var(--panel-2); color: var(--ink-faint); }
.badge-err { background: rgba(240,96,90,.15); color: var(--red); }

.empty { color: var(--ink-faint); padding: 30px; text-align: center; font-size: 14px; }

/* ============================================================
   The board (#10249) — both themes share one look: dark neutral surfaces so the club
   logos carry the colour, one typeface (Archivo, condensed for numbers and labels, with
   tabular figures so a changing value does not shift its neighbours), green for a gain
   or a player coming in, red for a loss or a player going out, amber for a change worth
   noticing. Archivo is self-hosted (assets/fonts, SIL OFL): a board opened in a control
   room must not wait on, or fail because of, a font CDN. The file is Google's latin
   range plus the arrows U+2190-2193, cut from the upstream variable font with pyftsubset
   (both axes kept): Google's own latin subset leaves out U+2192, and the board draws "→"
   on every change row ("Cover 1 Man → Cover 3 Zone", "WR 2 → 3", "11 → 12"), which would
   otherwise come from the fallback face mid-value. The unicode-range below must list
   exactly what the file holds, and nothing in the repo checks that: a range wider than
   the file suppresses the fallback and draws tofu. It was verified when the file was cut
   by reading the woff2's cmap with fontTools (every codepoint in the range that upstream
   Archivo has is in the file, 235 of 235, and nothing outside it); re-cut or edit the
   range, re-check the same way.
   ============================================================ */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-display: swap;
  src: url("../fonts/archivo-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2190-2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

.board-page, .ps-page {
  --b-bg: #07090c;
  --b-surface: #12161c;
  --b-raised: #171c23;
  --b-sunk: #0e1217;
  --b-line: #232933;
  --b-line-soft: #1d222b;
  --b-ink: #f1f3f6;
  --b-ink-2: #a7afbc;
  --b-ink-3: #6e7785;
  --b-gain: #5bd66b;
  --b-loss: #f0605a;
  --b-note: #f2b53f;
  --b-flag: #f5c400;      /* the penalty flag's yellow (#10295) */
  --b-flag-ink: #17140a;  /* text on it: the score and clock stay readable */
  --b-font: "Archivo", "Helvetica Neue", Arial, system-ui, sans-serif;
}

/* ============================================================
   LIVE THIS PLAY strip — the ticket 10021 layout, restyled (#10249).
   Sized in vw/clamp so it scales to a control-room display.
   ============================================================ */
.board-page {
  background: var(--b-bg); min-height: 100vh; margin: 0;
  display: flex; align-items: center; justify-content: center; padding: 3vh 2vw;
}
.board { width: 100%; max-width: 1240px; font-family: var(--b-font); color: var(--b-ink); }

.board-main {
  border: 1px solid var(--b-line); border-radius: 14px 14px 0 0; overflow: hidden;
  background: var(--b-surface); box-shadow: 0 24px 60px rgba(0, 0, 0, .55);
}
.board-title {
  color: var(--b-ink-3); font-weight: 600; font-stretch: 80%; letter-spacing: .1em; text-transform: uppercase;
  font-size: clamp(12px, 1.1vw, 15px); margin: 0; padding: clamp(10px, 1.2vw, 16px) clamp(14px, 1.8vw, 22px);
  background: var(--b-raised); border-bottom: 1px solid var(--b-line);
}
.board-title[hidden] { display: none; }

/* The score header (#10249): away | home | clock | down and spot, one row. */
.board-score {
  display: flex; align-items: stretch; flex-wrap: wrap;
  background: var(--b-raised); border-bottom: 1px solid var(--b-line);
}
.board-score[hidden] { display: none; }
.board-team {
  display: flex; align-items: center; gap: clamp(8px, .9vw, 12px);
  padding: clamp(8px, .9vw, 12px) clamp(12px, 1.5vw, 20px);
}
.board-home { flex-direction: row-reverse; border-left: 1px solid var(--b-line); }
.board-logo { width: clamp(26px, 2.8vw, 38px); height: clamp(26px, 2.8vw, 38px); object-fit: contain; }
.board-id { display: flex; flex-direction: column; gap: 3px; }
.board-home .board-id { align-items: flex-end; }
.board-abbr {
  display: flex; align-items: center; gap: 6px;
  font-size: clamp(13px, 1.3vw, 17px); font-weight: 700; font-stretch: 85%; letter-spacing: .02em;
}
.board-poss { width: 7px; height: 7px; border-radius: 50%; background: var(--b-note); }
.board-tos { display: flex; gap: 3px; }
.board-tos i { width: 12px; height: 3px; border-radius: 2px; background: #39404b; }
.board-tos i.on { background: var(--b-ink); }
.board-pts {
  font-size: clamp(22px, 2.3vw, 30px); font-weight: 700; font-stretch: 82%; line-height: 1;
  font-variant-numeric: tabular-nums;
}
.board-clock {
  order: 3; display: flex; align-items: center; padding: 0 clamp(12px, 1.6vw, 22px);
  border-left: 1px solid var(--b-line); color: var(--b-ink-2);
  font-size: clamp(13px, 1.25vw, 16px); font-variant-numeric: tabular-nums;
}
.board-clock:empty { display: none; }
.board-down {
  order: 4; display: flex; align-items: center; padding: 0 clamp(12px, 1.6vw, 22px);
  font-size: clamp(13px, 1.25vw, 16px); font-weight: 700;
}
.board-home { order: 2; }

.board-stats { display: flex; align-items: stretch; background: var(--b-line); gap: 1px; }
.board-stat {
  flex: 1; min-width: 0; background: var(--b-surface);
  padding: clamp(8px, 1vw, 14px) clamp(12px, 1.6vw, 22px);
}
.board-stat .k {
  color: var(--b-ink-2); font-size: clamp(10px, .95vw, 13px); line-height: 1.2;
  /* A stat name wraps to a second line rather than losing its end: six tiles across
     left "BALL CARRIER TOP SP..." on air (#10210). Every label reserves the two lines
     so the values below stay on one baseline, and a third line is clamped. */
  min-height: 2.4em; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; overflow-wrap: anywhere;
}
.board-stat .v {
  color: var(--b-ink); font-weight: 700; font-stretch: 82%; margin-top: clamp(4px, .6vw, 8px);
  font-size: clamp(17px, 2.1vw, 30px); line-height: 1.15; font-variant-numeric: tabular-nums;
}
.board-stat .v.dim { color: var(--b-ink-3); font-weight: 600; font-stretch: 100%; font-size: clamp(14px, 1.5vw, 20px); }

/* situation line + personnel + substitutions bar */
.board-subs {
  display: flex; flex-wrap: wrap; gap: 1px; background: var(--b-line);
  border: 1px solid var(--b-line); border-top: 0; border-radius: 0 0 14px 14px; overflow: hidden;
  min-height: clamp(48px, 5vw, 72px);
}
.board-subs.none { display: none; } /* nothing to draw: every cell hidden, or only an absent situation left (#10081, #10216) */
/* With nothing under it the tiles' frame closes its own corners. A class set from the
   same count that collapses the bar (ngs_strip_cells_drawn() / cellsDrawn()), not
   :has(): OBS 28-30 embed Chromium 103, which drops a :has() rule wholesale. */
.board-main.board-main-closed { border-radius: 14px; }
/* hide=subs: no dead space beside a lone personnel cell — with or without the situation
   line above it, so "last cell" rather than "only child". Scoped to the wide layout so it
   cannot outrank the <=720px column rule below (media queries add no specificity). */
@media (min-width: 721px) { .board-subs > .board-personnel:last-child { flex: 1; } }
.board-cell { display: flex; flex-direction: column; justify-content: center; align-items: flex-start;
  padding: clamp(8px, 1vw, 14px) clamp(12px, 1.6vw, 22px); background: var(--b-sunk); }
/* The game situation (#10216): one line across the top of the bar — down and distance at
   the ball, clock, score — drawn only when there is no score header to carry it (#10249).
   Declared AFTER .board-cell: the element carries both classes at equal specificity, so
   this order is what lets the slimmer padding win. */
.board-situation { flex: 0 0 100%; color: var(--b-ink); font-weight: 700;
  font-size: clamp(13px, 1.4vw, 18px); letter-spacing: .02em;
  padding: clamp(6px, .8vw, 10px) clamp(12px, 1.6vw, 22px); }
.board-personnel { flex: 0 0 30%; color: var(--b-ink);
  font-weight: 700; font-size: clamp(15px, 1.7vw, 22px); font-stretch: 90%; }
.board-personnel.none { color: var(--b-ink-3); font-weight: 600; font-size: clamp(11px, 1.1vw, 14px); }
/* Top-aligned, so In and Out start on one line however long either list runs. */
.board-in, .board-out { flex: 1; justify-content: flex-start; }
.board-cell .hd {
  font-size: clamp(10px, .95vw, 13px); font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; margin-bottom: 4px;
}
.board-in .hd  { color: var(--b-gain); }
.board-out .hd { color: var(--b-loss); }
.board-cell .who {
  color: var(--b-ink-2); font-size: clamp(12px, 1.2vw, 16px);
  line-height: 1.35; display: block; font-variant-numeric: tabular-nums;
}
.board-cell .none { color: var(--b-ink-3); font-weight: 600; font-size: clamp(10px, 1.05vw, 14px); }

/* status line under the board */
.board-status {
  display: flex; justify-content: space-between; align-items: center;
  margin-top: clamp(8px, 1.2vw, 14px); padding: 0 4px;
  color: var(--b-ink-3); font-size: clamp(10px, 1vw, 13px);
}
.board-status .live { color: var(--b-gain); font-weight: 700; letter-spacing: .06em; }
.board-status .stale { color: var(--b-note); font-weight: 700; letter-spacing: .06em; }
.board-status .down { color: var(--b-loss); font-weight: 700; letter-spacing: .06em; }
.board-status a { color: var(--b-ink-3); }

@media (max-width: 720px) {
  .board-stats { flex-wrap: wrap; }
  .board-stat { flex: 0 0 calc(50% - 1px); }
  .board-subs { flex-direction: column; }
  .board-situation { flex: none; }
  .board-personnel { flex: none; padding: 12px; }
  /* The teams keep the first row; the clock and the down line share the second. */
  .board-team { flex: 1 1 0; }
  .board-clock { flex: 0 0 auto; order: 5; padding: 6px 14px; border-left: 0; border-top: 1px solid var(--b-line); }
  .board-down { flex: 1 1 0; order: 6; padding: 6px 14px 6px 0; border-top: 1px solid var(--b-line); }
}

/* An outcome-tagged tile value (the "passed to" tile can read COMPLETE/INCOMPLETE), a
   demo banner, and a placeholder tile for when nothing has data yet. */
.board-demo {
  background: rgba(242, 181, 63, .12); color: var(--b-note); text-align: center;
  font-size: clamp(10px, 1.05vw, 13px); font-weight: 700; letter-spacing: .1em;
  text-transform: uppercase; padding: 6px 10px; border-radius: 8px;
  margin-bottom: clamp(8px, 1.2vw, 14px);
}
.board-complete   { color: var(--b-gain); }
.board-incomplete { color: var(--b-loss); }

/* ============================================================
   The three-column board (#10252) — ?theme=panel and ?theme=blocks.

   One 1920x1080 screen with nothing scrolling: the score header and the down-and-spot
   banner across the top, then the last completed play, its substitutions and personnel,
   and the play in progress (or the outlook for the next snap). Every size is written in
   design pixels of that screen through --u, one design pixel, so the board is exactly
   the mockup at 1920x1080 and scales as a whole on any other screen: --u follows the
   tighter axis, so nothing reflows, since a reflow is what put rows off the bottom of
   the old panel. On a desktop the root still fills the screen — on a monitor that is
   not 16:9 the columns run to the bottom and the sizes are the tighter axis's — which
   is what a control-room display wants. The preset editor's preview frame is this same
   page at 1920x1080, scaled down (pages/presets.php).

   Nothing below can grow the page: the columns are rows of a grid the height of the
   board — the screen, or at phone width the design height (the block after .bd) — and
   clip their own overflow, which is the backstop behind the substitution cap
   (NGS_SUBS_MAX), as the old window's max-height was (#10228).
   ============================================================ */
.ps-page {
  --u: min(calc(100vw / 1920), calc(100vh / 1080));
  margin: 0;
  height: 100vh;
  overflow: hidden;
  background: var(--b-bg);
}
.bd {
  box-sizing: border-box;
  height: 100vh;
  padding: calc(24 * var(--u)) calc(32 * var(--u));
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);   /* a footer, when there is one, takes an implicit auto row */
  gap: calc(16 * var(--u));
  font-family: var(--b-font);
  color: var(--b-ink);
}
/* A phone (#10277): filling the screen there is the wrong picture. A portrait phone is
   width-driven, so --u is about a fifth of design size, and the full-height grid put a
   24px header over three columns stretched down the whole screen with type at a fifth.
   So at phone width the root is boxed to the 1920x1080 design screen in --u pixels and
   centred — the desktop board, letterboxed, exactly as the preset editor's preview draws
   it (aspect-ratio 16/9, scaled whole): about 390x219 in portrait, 693x390 in landscape.
   The breakpoint is the sidebar's (900px, the bar across the top), so a tablet in
   portrait gets it too; a wider screen is untouched. fitColumn() in pages/board.php
   measures each column's own box, so the fit is the same inside the smaller box.
   The block sits after .bd on purpose: its .ps-page .bd wins on specificity, and source
   order backs that up. The height is the phone's *dynamic* viewport (dvh, the height
   actually visible — svh is the small one): 100vh there is the height with the browser
   bars collapsed, and a box exactly that tall in landscape (where --u is height-driven)
   would put the board's bottom strip behind the URL bar, unreachable under overflow:
   hidden; the board never scrolls, so the bars stay put and dvh is what is on screen. The dvh override is gated on @supports rather
   than declared twice, because a custom property is not validated at parse time — a
   second --u line with a unit the engine lacks would replace the fallback with an
   invalid value, not be dropped. */
@media (max-width: 900px) {
  .ps-page { display: grid; place-items: center; }
  .ps-page .bd { width: calc(1920 * var(--u)); height: calc(1080 * var(--u)); }
  @supports (height: 100dvh) {
    .ps-page { --u: min(calc(100vw / 1920), calc(100dvh / 1080)); height: 100dvh; }
  }
}

/* The top: away | clock over banner | home. The score header's own element is display:
   contents, so the parts ngs_scorebug_html() draws take their places in this grid. */
.bd-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  grid-template-areas: "away clock home" "away banner home";
  align-items: center;
  column-gap: calc(24 * var(--u));
  min-height: calc(120 * var(--u));
  padding: 0 calc(32 * var(--u));
  background: var(--b-surface);
  border: 1px solid var(--b-line);
  border-radius: calc(16 * var(--u));
}
.bd-score { display: contents; }
.bd-score[hidden] { display: none; }
.bd-top .ps-away  { grid-area: away; }
.bd-top .ps-home  { grid-area: home; }
.bd-top .ps-clock { grid-area: clock; align-self: end; text-align: center; }
.bd-banner        { grid-area: banner; align-self: start; }
.ps-team { display: flex; align-items: center; gap: calc(18 * var(--u)); min-width: 0; }
.ps-home { flex-direction: row-reverse; }
.ps-logo { width: calc(76 * var(--u)); height: calc(76 * var(--u)); object-fit: contain; flex: none; }
.ps-id { display: flex; flex-direction: column; gap: calc(8 * var(--u)); min-width: 0; }
.ps-home .ps-id { align-items: flex-end; }
.ps-abbr { display: flex; align-items: center; gap: calc(10 * var(--u)); font-size: calc(30 * var(--u)); font-weight: 700; letter-spacing: .02em; }
.ps-home .ps-abbr { flex-direction: row-reverse; }
.ps-poss { width: calc(11 * var(--u)); height: calc(11 * var(--u)); border-radius: 50%; background: var(--b-note); }
.ps-tos { display: flex; gap: calc(5 * var(--u)); }
.ps-tos i { width: calc(22 * var(--u)); height: calc(5 * var(--u)); border-radius: calc(3 * var(--u)); background: #39404b; }
.ps-tos i.on { background: var(--b-ink); }
.ps-pts { margin-left: auto; font-size: calc(72 * var(--u)); font-weight: 700; font-stretch: 80%; line-height: 1; font-variant-numeric: tabular-nums; }
.ps-home .ps-pts { margin-left: 0; margin-right: auto; }
.ps-clock { color: var(--b-ink-2); font-size: calc(22 * var(--u)); font-weight: 600; font-stretch: 88%; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* The down and the spot (#10230's banner, #10249's narrowing to those two rows): one line
   by contract, ellipsed rather than wrapped. `display` is set, so [hidden] is restated
   (the .flash trap above). A bullet separates parts, not the middot the timeouts use. */
.ps-situation { display: block; text-align: center; font-size: calc(30 * var(--u)); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ps-situation[hidden] { display: none; }
.ps-sit-part + .ps-sit-part::before { content: "\2022"; margin: 0 .6em; color: var(--b-ink-3); }
.ps-complete   { color: var(--b-gain); }
.ps-incomplete { color: var(--b-loss); }

/* No situation to build a header from: the title and the matchup, across the whole top. */
.bd-titlebar { grid-column: 1 / -1; grid-row: 1 / -1; display: flex; align-items: center; justify-content: center; gap: calc(24 * var(--u)); }
.bd-titlebar[hidden] { display: none; }
.bd-title { font-size: calc(30 * var(--u)); font-weight: 700; }
.bd-matchup { font-size: calc(22 * var(--u)); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; color: var(--b-ink-3); }

/* The pinned band (#10298): one row across the foot of the board, a cell per pinned stat -
   the label, the value large, and which play it came from. Sized in --u like the top: it
   is not in a column, so it never shrinks with one; the columns give it the height. The
   value alone is scaled by the cell's own --fit, which fitBand() in pages/board.php lowers
   until a long name is whole. Every line's height is fixed in --u - the value's whatever its
   --fit, the tag's whether it says anything yet - so nothing the band draws changes its
   height, and the columns above never need refitting for it. */
.bd-pinned {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: calc(12 * var(--u));
}
.bd-pinned[hidden] { display: none; }
.bd-pin {
  display: flex; flex-direction: column; gap: calc(4 * var(--u)); min-width: 0;
  padding: calc(10 * var(--u)) calc(16 * var(--u));
  background: var(--b-surface); border: 1px solid var(--b-line); border-radius: calc(12 * var(--u));
}
.bd-pin-k, .bd-pin-v, .bd-pin-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-pin-k { font-size: calc(15 * var(--u)); line-height: calc(19 * var(--u)); font-weight: 600; font-stretch: 80%; letter-spacing: .08em; text-transform: uppercase; color: #8a93a1; }
.bd-pin-v { font-size: calc(34 * var(--u) * var(--fit, 1)); font-weight: 700; font-stretch: 80%; line-height: calc(36 * var(--u)); font-variant-numeric: tabular-nums; }
.bd-pin-t { font-size: calc(14 * var(--u)); font-weight: 600; color: var(--b-ink-3); line-height: calc(18 * var(--u)); height: calc(18 * var(--u)); }
.bd-pin.is-live .bd-pin-t { color: var(--b-gain); }

/* The pinned players (#10299): a card per player under the stats band - number and name,
   position and club, then the totals the crew picked for him. Every line's height is fixed
   in --u, like the band's, so the row's height never depends on what it holds and the
   columns are refitted only when it comes or goes. */
.bd-players {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr);
  gap: calc(12 * var(--u));
}
.bd-players[hidden] { display: none; }
.bd-plr {
  display: flex; flex-direction: column; gap: calc(6 * var(--u)); min-width: 0;
  padding: calc(10 * var(--u)) calc(16 * var(--u));
  background: var(--b-surface); border: 1px solid var(--b-line); border-radius: calc(12 * var(--u));
}
.bd-plr-id { display: flex; align-items: baseline; gap: calc(10 * var(--u)); min-width: 0; height: calc(26 * var(--u)); }
.bd-plr-name { font-size: calc(22 * var(--u)); line-height: calc(26 * var(--u)); font-weight: 700; font-stretch: 85%;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.bd-plr-pos { flex: none; font-size: calc(14 * var(--u)); font-weight: 600; letter-spacing: .08em; color: #8a93a1; }
.bd-plr-stats { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); gap: calc(8 * var(--u)); }
.bd-plr-s { display: flex; flex-direction: column; min-width: 0; }
/* A total too wide for its cell is shrunk alone by fitPlayers() (#10335), --fit as the pinned
   band's cells (.bd-pin-v above): the line height stays, so the row's height never moves. */
.bd-plr-s b { font-size: calc(28 * var(--u) * var(--fit, 1)); line-height: calc(30 * var(--u)); font-weight: 700; font-stretch: 80%;
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* A snap count's "/25" - the team's snaps on that unit (#10335) - smaller than the played
   number, and floored at 12 design pixels (review): shrunk by the cell's --fit off a base
   already 61% of the number's, it would reach 10 at the fitter's floor, in the quiet ink,
   and be the first thing on the screen to go illegible - the played number carries the rest
   of the shrink instead. */
.bd-plr-s b small { font-size: max(calc(12 * var(--u)), calc(17 * var(--u) * var(--fit, 1))); font-weight: 600; color: var(--b-ink-3); }
.bd-plr-s i { font-style: normal; font-size: calc(12 * var(--u)); line-height: calc(15 * var(--u)); font-weight: 600;
  letter-spacing: .06em; text-transform: uppercase; color: var(--b-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* The injury tracker's line on a tracked card (#10406): whether he is on the field, and on a
   return the alert production asked for. Its height is fixed in --u like every other line
   here, so a card's height depends on whether it is TRACKED and never on what the line says -
   the one thing that can change it is a crew edit on the Players page (renderPlayers()
   refits the columns for that, as it does when the row itself comes or goes). The return is
   drawn big - the whole card takes the colour - because nothing on a board can be clicked to
   see more; and it takes itself off after AVAIL_BACK_MS, since nothing can dismiss it either. */
.bd-plr-av {
  display: flex; align-items: center; min-width: 0; height: calc(20 * var(--u));
  padding: 0 calc(8 * var(--u)); border-radius: calc(6 * var(--u));
  background: rgba(110, 119, 133, .14);
}
.bd-plr-avt {
  font-size: calc(14 * var(--u)); line-height: calc(20 * var(--u)); font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--b-ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bd-plr-av[data-av="off"] { background: rgba(242, 181, 63, .16); }
.bd-plr-av[data-av="off"] .bd-plr-avt { color: var(--b-note); }
.bd-plr-av[data-av="back"] { background: var(--b-gain); }
.bd-plr-av[data-av="back"] .bd-plr-avt { color: #07160a; }
.bd-plr[data-av="off"] { border-color: rgba(242, 181, 63, .45); }
.bd-plr[data-av="back"] {
  border-color: var(--b-gain); background: rgba(91, 214, 107, .12);
  animation: bd-plr-back 1s ease-in-out 2;
}
@keyframes bd-plr-back { 50% { background: rgba(91, 214, 107, .3); border-color: #8ce89a; } }


.bd-foot { display: flex; align-items: center; }
.bd-demo {
  padding: calc(6 * var(--u)) calc(14 * var(--u)); border-radius: calc(8 * var(--u));
  background: rgba(242, 181, 63, .12); color: var(--b-note);
  font-size: calc(15 * var(--u)); font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
}

/* ---------- the columns ---------- */
.bd-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, .8fr) minmax(0, 1fr); gap: calc(16 * var(--u)); min-height: 0; }
/* Two columns (#10258, a preset's option): no play in progress and no predictions, so the
   last play and the substitutions take the width. */
.bd-cols.is-two { grid-template-columns: minmax(0, 1fr) minmax(0, .9fr); }
/* The booth view (#10284, a preset's option): a flank at each edge with a team's players
   on the field, the three columns between them. */
.bd-cols.is-booth { grid-template-columns: minmax(0, .68fr) minmax(0, 1fr) minmax(0, .8fr) minmax(0, 1fr) minmax(0, .68fr); }
.bd-cols.is-booth.is-two { grid-template-columns: minmax(0, .68fr) minmax(0, 1fr) minmax(0, .9fr) minmax(0, .68fr); }
.bd-col[hidden] { display: none; }
.bd-col {
  display: flex; flex-direction: column; min-height: 0; overflow: hidden;
  padding: calc(20 * var(--u)) calc(24 * var(--u));
  background: var(--b-surface); border: 1px solid var(--b-line); border-radius: calc(16 * var(--u));
}
.bd-col-live { border-color: rgba(240, 96, 90, .45); }
.bd-col-head {
  display: flex; align-items: center; gap: calc(12 * var(--u)); min-height: calc(32 * var(--u));
  padding-bottom: calc(12 * var(--u)); margin-bottom: calc(16 * var(--u)); border-bottom: 1px solid var(--b-line);
}
.bd-eyebrow { font-size: calc(16 * var(--u)); font-weight: 600; font-stretch: 80%; letter-spacing: .1em; text-transform: uppercase; color: #8a93a1; }
.bd-col-live .bd-eyebrow { color: var(--b-ink); }
.bd-state { margin-left: auto; display: flex; align-items: center; gap: calc(9 * var(--u)); font-size: calc(16 * var(--u)); color: var(--b-ink-2); }
.bd-state[hidden] { display: none; }
.bd-state::before { content: ""; width: calc(9 * var(--u)); height: calc(9 * var(--u)); border-radius: 50%; background: var(--b-ink-3); }
.bd-col-live .bd-state {
  padding: calc(5 * var(--u)) calc(12 * var(--u)); border-radius: calc(7 * var(--u));
  background: rgba(240, 96, 90, .12); color: var(--b-loss); font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
}
.bd-col-live .bd-state::before { background: var(--b-loss); animation: bd-pulse 1.6s ease-in-out infinite; }
@keyframes bd-pulse { 0%, 100% { box-shadow: 0 0 0 0 rgba(240, 96, 90, .55); } 50% { box-shadow: 0 0 0 calc(7 * var(--u)) rgba(240, 96, 90, 0); } }
.bd-col-head .pv-nav { margin-left: auto; }
.bd-col:hover .pv-nav, .bd-col:focus-within .pv-nav { opacity: 1; }

/* The column's content is sized in --s, a design pixel times --fit: fitColumn() in
   pages/board.php lowers --fit until everything the column holds is inside it (#10252). */
.bd-col-body { --fit: 1; --s: calc(var(--u) * var(--fit)); flex: 1 1 auto; display: flex; flex-direction: column; gap: calc(16 * var(--s)); min-height: 0; overflow: hidden; }
.bd-section { display: flex; flex-direction: column; gap: calc(10 * var(--s)); }
.bd-heading {
  display: flex; align-items: center; gap: calc(12 * var(--s)); margin: 0;
  font-size: calc(14 * var(--s)); font-weight: 600; font-stretch: 80%; letter-spacing: .1em; text-transform: uppercase; color: var(--b-ink-3);
}
.bd-heading::after { content: ""; flex: 1; height: 1px; background: var(--b-line); }

/* Big-number tiles (NGS_BOARD_TILE_KEYS) and label/value lines. */
.bd-tiles { display: grid; grid-template-columns: repeat(auto-fill, minmax(calc(170 * var(--s)), 1fr)); gap: calc(10 * var(--s)); }
.bd-tile { display: flex; flex-direction: column; gap: calc(2 * var(--s)); min-width: 0; padding: calc(10 * var(--s)) calc(16 * var(--s)); background: var(--b-raised); border-radius: calc(12 * var(--s)); }
/* A long stat name wraps to a second line rather than losing its end, and every label
   reserves the two lines so the values in a row of tiles share one baseline (as #10210
   did for the strip's labels). */
.bd-tile-k { font-size: calc(16 * var(--s)); line-height: 1.2; min-height: 2.4em; color: var(--b-ink-2);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; overflow-wrap: anywhere; }
.bd-tile-v { font-size: calc(36 * var(--s)); font-weight: 700; font-stretch: 82%; line-height: 1.1; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-row { display: flex; align-items: baseline; justify-content: space-between; gap: calc(16 * var(--s)); padding: calc(6 * var(--s)) calc(2 * var(--s)); border-bottom: 1px solid var(--b-line-soft); font-size: calc(20 * var(--s)); }
.bd-row:last-child { border-bottom: 0; }
.bd-row-k { color: var(--b-ink-2); white-space: nowrap; }
.bd-row-v { font-weight: 600; text-align: right; min-width: 0; font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The description row (#10261) is a sentence, not a figure: the type on its own line, the
   summary's text under it, wrapping, where every other value is one nowrap figure. */
.bd-row[data-k="description"] { flex-direction: column; align-items: flex-start; gap: calc(2 * var(--s)); }
.bd-row[data-k="description"] .bd-row-v { text-align: left; white-space: normal; overflow: visible; font-weight: 500; line-height: 1.3; }
.bd-complete   { color: var(--b-gain); }
.bd-incomplete { color: var(--b-loss); }
.bd-note { padding: calc(12 * var(--s)) calc(2 * var(--s)); font-size: calc(20 * var(--s)); color: var(--b-ink-3); }

/* ---------- the centre column: substitutions and personnel, as the mockup (#10252) ----------
   A block per team under its logo, a line per player with a green + coming in and a red −
   going out; then a card per unit with the package large and what it was under it. The
   one design for both panel and blocks (blocks' In/Out stacks, #10168, became these). */
.bd-team { display: flex; flex-direction: column; gap: calc(2 * var(--s)); }
.bd-team + .bd-team { padding-top: calc(12 * var(--s)); border-top: 1px solid var(--b-line); }
.bd-team-hd { display: flex; align-items: center; gap: calc(10 * var(--s)); margin-bottom: calc(4 * var(--s)); font-size: calc(20 * var(--s)); font-weight: 700; color: var(--b-ink-2); letter-spacing: .02em; }
.bd-team-logo { width: calc(34 * var(--s)); height: calc(34 * var(--s)); object-fit: contain; flex: none; }
.bd-sub { display: flex; align-items: baseline; gap: calc(12 * var(--s)); min-width: 0; padding: calc(3 * var(--s)) 0; font-size: calc(26 * var(--s)); }
.bd-sub-sign { flex: none; width: calc(18 * var(--s)); font-weight: 700; }
.bd-sub-in  .bd-sub-sign { color: var(--b-gain); }
.bd-sub-out .bd-sub-sign { color: var(--b-loss); }
.bd-sub-no { flex: none; width: calc(36 * var(--s)); color: var(--b-ink-2); font-variant-numeric: tabular-nums; }
.bd-sub-name { min-width: 0; font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-sub-out .bd-sub-name { color: var(--b-ink-2); }
.bd-sub-pos { margin-left: auto; padding-left: calc(8 * var(--s)); font-size: calc(18 * var(--s)); color: var(--b-ink-3); }
.bd-sub-more .bd-sub-name { color: var(--b-ink-3); font-size: calc(20 * var(--s)); }
/* A team's In and Out lists: one under the other, or side by side when fitColumn() needs
   the height back (.bd-split on the column). */
.bd-sub-lists { display: flex; flex-direction: column; gap: calc(2 * var(--s)); }
.bd-split .bd-sub-lists { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: calc(20 * var(--s)); align-items: start; }
/* Sticky lines (#10286, the sticky_subs option): the two lists are always side by side, so one
   growing never moves the other, and a name that left its line leaves it empty — the same
   height as a name, so nothing below it moves either. */
.bd-sub-sticky { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: calc(20 * var(--s)); align-items: start; }
.bd-sub-gap { visibility: hidden; }
/* The booth view's flanks (#10284): a team's players on the field as the booth lists them,
   position, number, name a line, a rule where the unit changes. Not sticky: arranged by
   unit, a swap inside a unit changes one line and nothing else moves. */
.bd-flank .bd-col-head { gap: calc(10 * var(--u)); }
/* The head is outside the body, where --s is set, so its logo is sized in --u like the rest of the head. */
.bd-flank .bd-col-head .bd-team-logo { width: calc(34 * var(--u)); height: calc(34 * var(--u)); }
.bd-flank .bd-eyebrow { font-size: calc(20 * var(--u)); color: var(--b-ink-2); }
.bd-onfield { display: flex; flex-direction: column; }
.bd-of { display: flex; align-items: baseline; gap: calc(10 * var(--s)); min-width: 0; padding: calc(4 * var(--s)) 0; font-size: calc(23 * var(--s)); }
.bd-of-unit { margin-top: calc(6 * var(--s)); padding-top: calc(10 * var(--s)); border-top: 1px solid var(--b-line); }
.bd-of-pos { flex: none; width: calc(42 * var(--s)); font-size: calc(16 * var(--s)); font-weight: 600; letter-spacing: .04em; color: var(--b-ink-3); }
.bd-of .bd-sub-no { width: calc(36 * var(--s)); }
.bd-units { display: grid; grid-template-columns: repeat(auto-fit, minmax(calc(190 * var(--s)), 1fr)); gap: calc(10 * var(--s)); }
.bd-unit { display: flex; flex-direction: column; gap: calc(4 * var(--s)); min-width: 0; padding: calc(12 * var(--s)) calc(16 * var(--s)); background: var(--b-raised); border-radius: calc(12 * var(--s)); }
.bd-unit-hd { display: flex; align-items: center; gap: calc(8 * var(--s)); font-size: calc(16 * var(--s)); color: var(--b-ink-2); }
.bd-unit-hd .bd-team-logo { width: calc(24 * var(--s)); height: calc(24 * var(--s)); }
.bd-unit-v { font-size: calc(30 * var(--s)); font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bd-unit-sub { font-size: calc(20 * var(--s)); font-weight: 500; color: var(--b-ink-2); }
.bd-unit-was, .bd-unit-counts { font-size: calc(16 * var(--s)); color: var(--b-ink-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ---------- transitions (#10252) ----------
   The refresh adds .bd-fade to a column whose play changed and to a single value that did
   (swap() in pages/board.php), on both board designs; nothing unchanged animates. */
.bd-fade { animation: bd-fade .35s ease-out; }
@keyframes bd-fade { from { opacity: .1; } to { opacity: 1; } }
/* The flag (#10295): while the play the board is showing as just finished drew a penalty,
   the top bar fades to flag yellow - the score header on the three-column board, the
   header row on the strip - with its text dark so the score and clock stay readable, and
   fades back at the next snap. `is-flag` is on the board's root (pages/board.php, draw()). */
/* Only the bar's own surface fades, and front-loaded: most of the way to yellow in the first
   ~100 ms, settling over the rest. The text flips at once. Fading both on one clock left a
   moment where the two had crossed - dark text on a bar not yet yellow, then light text on a
   bar still yellow at the snap - that is, the header unreadable at exactly the point it
   changed (review). Nothing inside the bar transitions: the pips and the possession dot are
   drawn with background, and a timeout used or possession changing switches as it always
   has rather than cross-fading on top of swap()'s own fade. */
.bd-top, .board-score, .board-title {
  transition: background-color .6s cubic-bezier(.15, .85, .25, 1), border-color .6s cubic-bezier(.15, .85, .25, 1);
}
.bd.is-flag .bd-top { background: var(--b-flag); border-color: var(--b-flag); }
.bd.is-flag .bd-top, .bd.is-flag .bd-top * { color: var(--b-flag-ink); }
.board.is-flag .board-score, .board.is-flag .board-title { background: var(--b-flag); border-color: var(--b-flag); }
.board.is-flag .board-score, .board.is-flag .board-score *, .board.is-flag .board-title { color: var(--b-flag-ink); }
/* ...and the parts drawn with background get flag colours of their own. The ordinary pips
   are light-on-dark: on the yellow the REMAINING timeouts (light) all but vanish while the
   used ones (dark) stay legible, so the header would read backwards; and the possession dot
   is amber, the flag's own colour, so it would disappear exactly while the bar is yellow. */
.bd.is-flag .ps-tos i, .board.is-flag .board-tos i { background: rgba(23, 20, 10, .28); }
.bd.is-flag .ps-tos i.on, .board.is-flag .board-tos i.on { background: var(--b-flag-ink); }
.bd.is-flag .ps-poss, .board.is-flag .board-poss { background: var(--b-flag-ink); }

@media (prefers-reduced-motion: reduce) {
  /* The return keeps its green card and line (#10406) - it is the alert; only the pulse goes. */
  .bd-fade, .bd-col-live .bd-state::before, .bd-plr[data-av="back"] { animation: none; }
  .bd-top, .board-score, .board-title { transition: none; }
}

/* ---------- previous-play pane on the strip (?prev=1, #10175) ----------
   The strip gains a second strip above the live one (#10241). The stepping controls fade
   in on hover / focus so nothing extra is on air in a browser source, and stay visible
   while the pane is stepped back (.pv-on) so an operator cannot mistake a pinned pane for
   a following one. On the three-column board they sit in the left column's head. */
.board-stack { display: flex; flex-direction: column; gap: clamp(14px, 2.4vh, 26px); width: 100%; max-width: 1240px; }
.board-stack .board { width: 100%; }
/* The previous strip must not read as the live one: muted frame, muted title. */
.board-prev .board-main { opacity: .92; }
.board-prev .board-title { color: var(--b-ink-3); }
.board-head { display: flex; align-items: center; justify-content: space-between; gap: 12px;
  background: var(--b-raised); border-bottom: 1px solid var(--b-line); padding-right: 12px; }
.board-head .board-title { background: none; border: 0; }

.pv-nav { display: inline-flex; align-items: center; gap: 6px; opacity: 0; transition: opacity .15s; }
.pv-nav.pv-on,
.board-prev:hover .pv-nav, .board-prev:focus-within .pv-nav { opacity: 1; }
.pv-btn { background: rgba(255, 255, 255, .06); border: 1px solid var(--b-line); color: var(--b-ink);
  border-radius: 6px; font: inherit; font-size: 15px; line-height: 1; padding: 3px 10px; cursor: pointer; }
.pv-btn:hover:not(:disabled) { background: rgba(255, 255, 255, .12); }
.pv-btn:disabled { opacity: .35; cursor: default; }
.pv-live { color: var(--b-gain); border-color: rgba(91, 214, 107, .45); font-size: 11px; font-weight: 700;
  letter-spacing: .08em; padding: 5px 10px; }

/* TV mode (#10268): the sync bar, and the badge that never goes away. This board is a
   reference screen and not a source going to air, so its controls belong on it — and a
   board held half a minute behind the game must never be mistaken for a live one, which
   is what the badge is for. Sized in px, not --u: it is read at arm's length by the one
   person using it, not across a control room. */
/* The board makes room for it: the bar is fixed, and a column fitted against a box it
   covers reports that everything fits with its last rows hidden behind it - the one crop
   with no visual hint that anything is missing. --tv-bar is the bar's own measured height
   (pages/board.php), since it wraps on a narrow screen. */
body.tv-on .bd { padding-bottom: calc(24 * var(--u) + var(--tv-bar, 56px) + 12px); }
body.tv-on.board-page { padding-bottom: calc(3vh + var(--tv-bar, 56px) + 12px); }
.tv-bar {
  position: fixed; left: 16px; bottom: 16px; z-index: 9;
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  padding: 8px 12px; border-radius: 10px;
  background: rgba(7, 9, 12, .94); border: 1px solid var(--b-line);
  font-size: 13px; color: var(--b-ink-2);
}
.tv-badge {
  padding: 4px 9px; border-radius: 6px; background: var(--b-raised); color: var(--b-ink);
  font-weight: 700; letter-spacing: .04em; white-space: nowrap;
}
/* Not synced yet: the board is holding the waiting model on purpose, and amber says so. */
.tv-badge.tv-cold { background: rgba(240, 180, 60, .16); color: #f2c14e; }
.tv-meas { color: var(--b-ink-3); white-space: nowrap; }
.tv-sep { width: 1px; align-self: stretch; background: var(--b-line); }
.tv-btn {
  background: rgba(255, 255, 255, .06); border: 1px solid var(--b-line); color: var(--b-ink);
  border-radius: 6px; font: inherit; line-height: 1; padding: 6px 10px; cursor: pointer;
}
.tv-btn:hover { background: rgba(255, 255, 255, .12); }
.tv-tap { font-weight: 600; }
.tv-trim { display: inline-flex; gap: 4px; }
.tv-clock { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.tv-clock input, .tv-where {
  background: var(--b-sunk); border: 1px solid var(--b-line); color: var(--b-ink);
  border-radius: 6px; font: inherit; padding: 5px 8px;
}
.tv-clock input { width: 62px; text-align: center; font-variant-numeric: tabular-nums; }
.tv-leave { color: var(--b-ink-3); }
.tv-leave:hover { color: var(--b-ink); }

/* ---------- shared controls (#10257): segmented buttons, link buttons, switches ---------- */
.seg { display: inline-flex; padding: 3px; border-radius: 9px; background: var(--bg); border: 1px solid var(--line); }
.seg button {
  padding: 6px 12px; border: 0; border-radius: 6px; background: transparent; color: var(--ink-faint);
  font: inherit; font-size: 13px; font-weight: 600; white-space: nowrap; cursor: pointer;
}
.seg button:hover { color: var(--ink); }
.seg button.on { background: var(--panel-2); color: var(--ink); }
.seg-lg { border-radius: 10px; background: var(--panel); }
.seg-lg button { padding: 8px 16px; font-size: 14px; border-radius: 7px; }
.seg-lg button.on { box-shadow: inset 0 -2px 0 var(--accent); }
.link-btn {
  display: inline-flex; align-items: center; gap: 6px; padding: 0; border: 0; background: none;
  color: var(--accent); font: inherit; font-size: 13px; cursor: pointer;
}
.link-btn:hover:not(:disabled) { color: #8fe69a; }
.link-btn:disabled { color: var(--ink-faint); opacity: .5; cursor: default; }
/* A switch is a checkbox: the input stays in the page (focus, keyboard, the change event
   the scripts listen for) and only its box is drawn over. */
.switch { position: relative; display: inline-flex; align-items: center; gap: 8px; margin: 0; cursor: pointer;
  text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 500; color: var(--ink-dim); white-space: nowrap; }
.switch input { position: absolute; opacity: 0; width: 36px; height: 20px; margin: 0; cursor: pointer; }
.switch input + span {
  flex-shrink: 0; width: 36px; height: 20px; border-radius: 999px; background: var(--line-strong); position: relative;
  transition: background .15s;
}
.switch input + span::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--ink-faint); transition: transform .15s, background .15s;
}
.switch input:checked + span { background: var(--accent); }
.switch input:checked + span::after { transform: translateX(16px); background: var(--accent-ink); }
.switch input:focus-visible + span { outline: 2px solid var(--accent-dim); outline-offset: 2px; }
.switch input:disabled, .switch input:disabled + span { cursor: default; opacity: .45; }

/* ---------- board preset editor (#10148; laid out again in #10257) ----------
   The library down the left; the name, sharing and Save above the board preview, large,
   at the board's own shape; below, the tabs and the sections as cards in two columns, the
   opened one across both. */
.pe { display: grid; grid-template-columns: 250px minmax(0, 1fr); gap: 20px; align-items: start; }
.pe-help { max-width: 900px; font-size: 14px; color: var(--ink-dim); }
.pe-help p { margin: 0 0 10px; }
.pe-help p:last-child { margin: 0; }
.pe-lib { display: flex; flex-direction: column; gap: 6px; padding: 12px; position: sticky; top: 16px; max-height: calc(100vh - 32px); overflow-y: auto; }
.pe-lib input[type=search] {
  width: 100%; padding: 8px 11px; margin-bottom: 6px; background: var(--bg); color: var(--ink);
  border: 1px solid var(--line-strong); border-radius: 8px; font: inherit; font-size: 13px;
}
.pe-lib-group { display: flex; flex-direction: column; gap: 2px; }
.pe-lib-group + .pe-lib-group { margin-top: 10px; }
.pe-lib-group .eyebrow { padding: 4px 10px; }
.pe-lib-empty { margin: 0; padding: 4px 10px; font-size: 13px; color: var(--ink-faint); }
.pe-lib-group[hidden], .pe-items li[hidden] { display: none; }
.pe-items { list-style: none; margin: 0; padding: 0; }
.pe-items a { display: flex; flex-direction: column; gap: 1px; padding: 8px 10px; border-radius: 7px; color: var(--ink-dim); font-size: 14px; }
.pe-items a:hover { background: var(--panel-2); color: var(--ink); }
.pe-items a.on { background: var(--panel-2); color: var(--ink); font-weight: 600; box-shadow: inset 3px 0 0 var(--accent); }
.pe-items em { font-style: normal; font-size: 12px; font-weight: 400; color: var(--ink-faint); }
.pe-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.pe-stage { display: flex; flex-direction: column; gap: 12px; margin: 0; padding: 16px; }
.pe-head-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pe-stage input.pe-name {
  flex: 1 1 260px; min-width: 0; width: auto; padding: 6px 10px; margin-left: -10px; border: 1px solid transparent;
  background: transparent; font-size: 22px; font-weight: 700; border-radius: 8px;
}
.pe-stage input.pe-name:hover { border-color: var(--line); }
.pe-stage input.pe-name:focus { background: var(--bg); border-color: var(--line-strong); }
.pe-dirty { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--amber); white-space: nowrap; }
.pe-dirty::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--amber); }
.pe-dirty[hidden] { display: none; }
.pe-actions { display: flex; gap: 8px; margin-left: auto; }
.pe-status { margin: 0; font-size: 13px; color: var(--ink-faint); }
.pe-status:empty { display: none; }
.pe-options { display: flex; flex-direction: column; gap: 8px; padding: 12px 0 2px; border-top: 1px solid var(--panel-2); }
.pe-options .switch { font-size: 13px; color: var(--ink-dim); }
/* The pinned band's stats (#10298): a wrapping set of ticks, each with its place on the band. */
.pe-pins { padding: 2px 0 0 44px; }
.pe-pins[hidden] { display: none; }
.pe-pins-head { margin: 0 0 6px; font-size: 12px; color: var(--ink-dim); }
.pe-pin-list { display: flex; flex-wrap: wrap; gap: 6px; }
.pe-pin { display: inline-flex; align-items: center; gap: 6px; padding: 4px 10px; border: 1px solid var(--panel-2); border-radius: 999px; font-size: 12px; cursor: pointer; }
.pe-pin:has(input:checked) { border-color: var(--accent); }
.pe-pin:has(input:disabled) { opacity: .45; cursor: default; }
.pe-pin input { margin: 0; }
.pe-pin-n { min-width: 1ch; font-weight: 700; font-variant-numeric: tabular-nums; }
.pe-pin-n:empty { display: none; }
.pe-preview-bar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; font-size: 13px; color: var(--ink-dim); }
.pe-preview-bar select { width: auto; max-width: 260px; padding: 7px 10px; font-size: 13px; }
.pe-steps { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.pe-steps[hidden] { display: none; }   /* an author display beats the UA [hidden], as .btn[hidden] above (#10201) */
.pe-steps .btn { padding: 4px 10px; line-height: 1.2; white-space: nowrap; }
.pe-steps + .btn, .pe-preview-bar > .btn { white-space: nowrap; }
.pe-preview-bar > #peSteps[hidden] + .btn { margin-left: auto; }
.pe-frame-wrap { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; background: #07090c; }
.pe-frame-wrap iframe { display: block; border: 0; width: 100%; }
/* A panel is drawn 1:1 in a tall frame. When the loaded board is the 1920x1080 three-
   column layout (#10252) the page script adds is-scaled: the frame is that size, scaled to
   the wrap's width, and the wrap keeps the board's shape. */
.pe-frame-wrap.is-panel iframe { height: 620px; }
.pe-frame-wrap.is-panel.is-scaled { position: relative; aspect-ratio: 16 / 9; }
/* Out of flow, so a 1920px-wide frame can never widen the preview column it is scaled into. */
.pe-frame-wrap.is-panel.is-scaled iframe { position: absolute; top: 0; left: 0; width: 1920px; height: 1080px; transform-origin: 0 0; }
.pe-frame-wrap.is-strip iframe { height: 300px; }
.pe-tabs-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.pe-tabnote { font-size: 13px; color: var(--ink-faint); }
.pe-sections { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 10px; align-items: start; }
.pe-section { background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius); min-width: 0; }
.pe-section.open { grid-column: 1 / -1; border-color: var(--line-strong); }
.pe-section.off .pe-sec-text, .pe-section.off .pe-sec-count { opacity: .5; }
.pe-section.dragging, .pe-row.dragging { opacity: .4; }
.pe-section.drop-before { box-shadow: 0 -3px 0 var(--accent); }
.pe-section.drop-after { box-shadow: 0 3px 0 var(--accent); }
.pe-section-head { display: flex; align-items: center; gap: 10px; padding: 10px 12px; }
.pe-section.open .pe-section-head { border-bottom: 1px solid var(--panel-2); }
.pe-grip {
  display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; width: 22px; height: 28px; padding: 0;
  border: 0; border-radius: 5px; background: transparent; color: #4a5260; cursor: grab;
}
.pe-grip:hover:not(:disabled), .pe-grip:focus-visible { color: var(--ink-dim); background: var(--panel-2); }
.pe-grip:focus-visible { outline: 2px solid var(--accent-dim); }
.pe-grip:disabled { opacity: .3; cursor: default; }
.pe-sec-toggle {
  display: flex; align-items: center; gap: 12px; flex: 1; min-width: 0; padding: 2px 0; border: 0; background: none;
  color: var(--ink); font: inherit; text-align: left; cursor: pointer;
}
.pe-sec-text { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.pe-sec-name { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.pe-sec-name b { font-size: 15px; font-weight: 600; white-space: nowrap; }
.pe-sec-name em { font-style: normal; font-size: 12px; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pe-sec-sum { font-size: 13px; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pe-sec-count { font-size: 13px; color: var(--ink-dim); white-space: nowrap; font-variant-numeric: tabular-nums; }
.pe-chev { flex-shrink: 0; color: var(--ink-faint); transition: transform .15s; }
.pe-section.open .pe-chev { transform: rotate(90deg); }
.pe-section-body { display: flex; flex-direction: column; gap: 14px; padding: 14px; }
.pe-lbl { margin: 0; font-size: 13px; font-weight: 600; color: var(--ink-dim); text-transform: none; letter-spacing: 0; font-stretch: 100%; }
.pe-heading-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.pe-heading-row .pe-lbl { width: 64px; }
.pe-heading-row input.pe-title-in { flex: 1 1 240px; width: auto; min-width: 0; padding: 7px 10px; font-size: 14px; }
.pe-heading-row input.pe-title-in:disabled { opacity: .5; }
.pe-rows-bar { display: flex; align-items: center; gap: 10px; }
.pe-hint { font-size: 12px; color: var(--ink-faint); }
.pe-rows-bar .link-btn:first-of-type { margin-left: auto; }
.pe-rows { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2px 8px; }
.pe-row { display: flex; align-items: center; gap: 6px; min-height: 36px; padding: 0 10px 0 2px; border-radius: 7px; }
.pe-row:not(.off) { background: #171c23; }
.pe-row.drop-before { box-shadow: inset 0 3px 0 var(--accent); }
.pe-row.drop-after { box-shadow: inset 0 -3px 0 var(--accent); }
.pe-row label.check { margin: 0; flex: 1 1 auto; min-width: 0; font-size: 14px; color: var(--ink); }
.pe-row.off label.check { color: var(--ink-faint); }
.pe-fusion-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--blue); flex-shrink: 0; }
@media (max-width: 1100px) {
  .pe { grid-template-columns: 1fr; }
  .pe-lib { position: static; max-height: none; }
  .pe-sections, .pe-rows { grid-template-columns: 1fr; }
}

/* Inline checkbox row (login "keep me signed in"). Overrides the block `label`
   rule above, which is written for stacked form fields. */
label.check {
  display: flex; align-items: center; gap: 9px;
  margin: 16px 0 0; text-transform: none; letter-spacing: 0;
  font-size: 14px; color: var(--ink-dim); cursor: pointer;
}
label.check input { width: auto; margin: 0; accent-color: var(--accent-dim); }

/* ---------- The Players page (#10308): pinned players, set up per game ---------- */
.pl-layout { display: grid; grid-template-columns: 280px minmax(0, 1fr); gap: 16px; align-items: start; }
.pl-games { padding: 10px; }
.pl-games h3 { margin: 12px 8px 6px; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-dim); }
.pl-games h3:first-child { margin-top: 4px; }
.pl-game { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; padding: 7px 10px; border-radius: 8px; color: inherit; text-decoration: none; }
.pl-game:hover { background: var(--panel-2); }
.pl-game.on { background: var(--panel-2); box-shadow: inset 3px 0 0 var(--accent); }
.pl-note, .pl-count { font-size: 12px; color: var(--ink-dim); }
.pl-note { color: var(--accent); }
.pl-main { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.pl-main .muted, .pl-pin .muted { color: var(--ink-dim); }
.pl-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.pl-head h2 { margin: 0; }
.pl-head .btn { margin-left: auto; }
.pl-sub { display: flex; gap: 10px; align-items: baseline; margin: 16px 0 8px; font-size: 13px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-dim); }
.pl-pin { padding: 12px; margin-bottom: 10px; border: 1px solid var(--panel-2); border-radius: 10px; }
.pl-pin-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.pl-acts { margin-left: auto; display: flex; gap: 6px; }
.pl-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-top: 10px; }
.pl-stat { display: inline-flex; align-items: center; gap: 5px; padding: 3px 9px; border: 1px solid var(--panel-2); border-radius: 999px; font-size: 13px; cursor: pointer; }
.pl-stat.on { border-color: var(--accent); }
.pl-stat input { margin: 0; }
.pl-hint { font-size: 12px; flex-basis: 100%; }
.pl-roster { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 24px; }
.pl-team h3 { margin: 0 0 4px; }
.pl-team h4 { margin: 14px 0 4px; font-size: 12px; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-dim); }
.pl-row { display: flex; align-items: center; gap: 10px; padding: 5px 0; border-bottom: 1px solid var(--panel-2); }
.pl-num { width: 2.2em; text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-dim); }
.pl-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@media (max-width: 900px) { .pl-layout { grid-template-columns: minmax(0, 1fr); } }
.pl-missing { font-size: 12px; color: var(--amber); }
/* What the board says about a tracked player (#10406), so the analyst setting it up reads the
   same words the truck does. */
.pl-inj { font-size: 12px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; }
.pl-inj-on { color: var(--ink-dim); }
.pl-inj-off { color: var(--amber); }
.pl-inj-unknown { color: var(--ink-dim); }
.pl-from { margin: 0 0 10px; font-size: 13px; }
