:root {
  --bg: #0b1017;
  --panel: #131b26;
  --panel-2: #18222f;
  --line: #26313f;
  --text: #e6edf7;
  --dim: #8ea0bb;
  --red: #e0413f;
  --blue: #2f7de1;
  --accent: #ffd94a;
  --good: #46c98b;
  --warn: #f0a63c;
  --bad: #e0413f;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;
  --ring: #ffd94a;
  --shadow: 0 1px 2px rgba(0, 0, 0, 0.35), 0 8px 24px -12px rgba(0, 0, 0, 0.6);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: radial-gradient(1200px 600px at 20% -10%, #16202d 0%, var(--bg) 60%);
  color: var(--text);
  font-family: "Segoe UI", "PingFang SC", "Microsoft YaHei", system-ui, sans-serif;
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

h1, h2, h3 { margin: 0; font-weight: 650; letter-spacing: 0.01em; }
h2 { font-size: 15px; }
h3 { font-size: 13px; color: var(--dim); text-transform: uppercase; letter-spacing: 0.08em; margin: 14px 0 8px; }
p { margin: 0; }
button { font: inherit; }

/* Figures in this UI are read as a scoreboard: they must not jitter as digits change. */
.score strong, .clock .timer, .card .v, .chip, .progress span, .events,
td.mono, th.mono, .robot-title, .period-strip, .margin, .legend {
  font-variant-numeric: tabular-nums;
}

:focus-visible {
  outline: 2px solid var(--ring);
  outline-offset: 2px;
  border-radius: 7px;
}
:focus:not(:focus-visible) { outline: none; }

.topbar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--line);
  background: rgba(11, 16, 23, 0.85);
  backdrop-filter: blur(8px);
  position: sticky; top: 0; z-index: 20;
}
.brand { display: flex; align-items: center; gap: 12px; }
.brand h1 { font-size: 16px; }
.sub { color: var(--dim); font-size: 12px; }
.topbar-actions { display: flex; align-items: center; gap: 10px; }
.chip {
  font-family: var(--mono); font-size: 12px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--dim); background: var(--panel);
}
.chip.busy { color: var(--accent); border-color: #5a4a12; }
.chip.ok { color: var(--good); border-color: #1d4d38; }
.chip.err { color: var(--bad); border-color: #56262a; }

/* Three columns, one per job: configure on the left, watch in the middle, read on the right. Each panel
   scrolls inside itself so the FIELD stays on screen while you edit a card — the old full-width setup
   row pushed the replay below the fold on a laptop. */
main {
  display: grid;
  grid-template-columns: minmax(300px, 360px) minmax(0, 1fr) minmax(360px, 460px);
  gap: 14px;
  padding: 14px 18px 40px;
  align-items: start;
}
.panel {
  background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%);
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 14px 16px 16px;
  box-shadow: var(--shadow);
  min-width: 0;
}
/* A rail you can read without the page moving: the setup column has its own scrollbar and the field
   column starts below the header, so both stay put while the inspector scrolls. */
.panel.setup,
.panel.output {
  position: sticky;
  top: 66px;
  max-height: calc(100vh - 82px);
  overflow-y: auto;
  overscroll-behavior: contain;
}
.panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.hint { color: var(--dim); font-size: 12px; }

/* ---- setup ---- */
/* One alliance above the other: the rail is ~340 px wide and two alliance columns of four-up spec
   fields is unreadable. The alliance heading carries a colour bar so the two blocks still scan apart. */
.setup-grid { display: grid; grid-template-columns: 1fr; gap: 12px; }
.alliance-block { border: 1px solid var(--line); border-radius: 12px; padding: 10px 12px; }
.alliance-block.red { border-left: 3px solid var(--red); }
.alliance-block.blue { border-left: 3px solid var(--blue); }
.alliance-block h3 { margin: 0 0 8px; color: var(--text); font-size: 12px; letter-spacing: 0.1em; }
.hp-row { display: flex; align-items: center; gap: 6px; margin: 0 0 8px; font-size: 11px; color: var(--dim); letter-spacing: 0.06em; }
.hp-row input { width: 52px; flex: none; }
.hp-row input + input { margin-left: 4px; }
.roster { display: grid; gap: 8px; }
.robot-card { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px; background: #0f161f; }
.robot-title { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--dim); font-family: var(--mono); margin-bottom: 6px; }
.robot-title em { margin-left: auto; font-style: normal; color: #5f7594; font-size: 10px; }
.prov {
  margin-top: 7px; padding-left: 8px; border-left: 2px solid #33415a;
  font-size: 11px; color: #8ea0bb; display: grid; gap: 1px;
}
.prov span { color: #63768f; }
.dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.dot.red { background: var(--red); }
.dot.blue { background: var(--blue); }
.robot-row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px; align-items: end; }
/* The replay's transports sit on one line, and the scrub grows to take the slack. */
.transport { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.scrub-wrap { position: relative; flex: 1 1 200px; min-width: 140px; }
.scrub-wrap input[type='range'] { width: 100%; }
/* A ruler under the scrub: one tick per foul, climb or fault, click to jump. It is a separate strip
   rather than an overlay on the track because a range input's thumb is inset by half its width, so
   ticks drawn over the track would not line up with the position they claim. */
.scrub-marks { position: relative; height: 9px; margin-top: -3px; }
.scrub-marks i { position: absolute; top: 0; width: 3px; height: 9px; border-radius: 1px; cursor: pointer; }
.scrub-marks i:hover { height: 12px; }
.scrub-marks i.foul { background: #e0413f; }
.scrub-marks i.climb { background: #7ad0a6; }
.scrub-marks i.fault { background: #ffb84a; }
.robot-row label { display: grid; gap: 2px; font-size: 11px; color: var(--dim); }
/* Which numbers below come from a batch of MATCHES and which from the single replay being shown: the
   two scopes used to be mixed under one tab with no label on either. */
.scope {
  margin: 14px 0 6px; padding: 5px 9px; border-left: 3px solid var(--line); background: #131c26;
  color: var(--dim); font-size: 11px; line-height: 1.5;
}
.scope b { color: var(--text); }
.delta { margin: 10px 0 4px; font-size: 11px; color: var(--dim); line-height: 1.7; }
.delta b { color: var(--text); }
.delta .up { color: var(--good); }
.delta .down { color: var(--warn); }
.robot-row label.check { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text); }
.robot-card.bench { opacity: 0.55; }
.robot-card.bench .robot-title em { color: var(--dim); }
input, select {
  background: #0e151d; color: var(--text); border: 1px solid var(--line);
  border-radius: 7px; padding: 5px 7px; font-size: 12px; font-family: var(--mono); width: 100%;
}
input[type="checkbox"] { width: auto; }
input[type="range"] { padding: 0; }
input:disabled, select:disabled { opacity: 0.45; cursor: not-allowed; }
select { font-family: inherit; }
.adv-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px; margin-top: 10px; }
.adv-grid label { display: grid; gap: 3px; font-size: 11px; color: var(--dim); }
.adv-grid label.check { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--text); }
.advanced { margin-top: 12px; border-top: 1px dashed var(--line); padding-top: 10px; }
.advanced summary { cursor: pointer; color: var(--dim); font-size: 12px; }
/* The action bar is docked to the bottom of the setup rail: the roster is taller than the column, and
   the buttons should not be the thing you have to scroll to reach. */
.run-row {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 14px;
  position: sticky; bottom: -16px; padding: 10px 0 4px;
  background: linear-gradient(180deg, rgba(19, 27, 38, 0) 0%, var(--panel) 22%);
}
/* Only the run row sticks; two stacked sticky rows fight for the same bottom edge. */
.store-row { position: static; background: none; margin-top: 6px; padding: 0; }
.store-row #setup-store { flex: 1 1 200px; min-width: 0; }
.btn {
  background: #1d2836; color: var(--text); border: 1px solid var(--line);
  border-radius: 9px; padding: 8px 14px; cursor: pointer;
  transition: background 0.15s, border-color 0.15s, transform 0.06s, filter 0.15s;
}
.btn:hover { border-color: #3d4d63; background: #223044; }
.btn:active:not(:disabled) { transform: translateY(1px); }
.btn.primary { background: linear-gradient(135deg, #b8312f, #265ea8); border-color: transparent; font-weight: 600; }
.btn.primary:hover { filter: brightness(1.12); }
.btn.small { padding: 5px 10px; font-size: 12px; }
.btn.ghost { background: transparent; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.progress { display: flex; align-items: center; gap: 8px; min-width: 200px; flex: 1; }
.progress .bar { flex: 1; height: 6px; background: #0e151d; border-radius: 999px; overflow: hidden; }
.progress .bar > i { display: block; height: 100%; width: 0; background: linear-gradient(90deg, var(--red), var(--accent)); }
.progress span { font-family: var(--mono); font-size: 11px; color: var(--dim); }

/* ---- field ---- */
.scoreboard { display: grid; grid-template-columns: 1fr auto 1fr; gap: 10px; align-items: center; margin-bottom: 10px; }
.score { display: grid; gap: 2px; padding: 8px 12px; border-radius: 10px; background: #0f161f; border: 1px solid var(--line); }
.score.blue { text-align: right; }
.score .tag { font-size: 11px; letter-spacing: 0.2em; }
.score.red .tag { color: var(--red); }
.score.blue .tag { color: var(--blue); }
.score strong { font-family: var(--mono); font-size: 30px; line-height: 1; }
.score .detail { font-size: 11px; color: var(--dim); font-family: var(--mono); line-height: 1.45; }
.clock { text-align: center; font-family: var(--mono); }
.clock .period { font-size: 11px; letter-spacing: 0.18em; color: var(--accent); }
.clock .timer { font-size: 22px; }
.clock .margin {
  display: inline-block; margin-top: 4px; padding: 1px 8px; border-radius: 999px;
  font-size: 11px; border: 1px solid var(--line); color: var(--dim);
}
.clock .margin.red { color: #ffd9d8; border-color: rgba(224, 65, 63, 0.55); background: rgba(224, 65, 63, 0.16); }
.clock .margin.blue { color: #d6e6ff; border-color: rgba(47, 125, 225, 0.55); background: rgba(47, 125, 225, 0.16); }
.hub-strip { display: flex; align-items: center; justify-content: center; gap: 6px; margin-top: 5px; font-size: 11.5px; }
/* The lit arrow is the Active-HUB tell, the way the audience display shows it: it points at the
   ALLIANCE whose HUB will accept FUEL right now, and both arrows light in AUTO / TRANSITION / END GAME. */
.hub-strip .ar { font-style: normal; font-size: 13px; line-height: 1; color: #2b3547; }
.hub-strip .ar.on { color: var(--accent); text-shadow: 0 0 8px rgba(255, 217, 74, 0.6); }
.hub-strip span { padding: 1px 8px; border-radius: 999px; border: 1px solid var(--line); color: var(--dim); }
.hub-strip.red span { color: #ffd9d8; border-color: rgba(224, 65, 63, 0.55); background: rgba(224, 65, 63, 0.16); }
.hub-strip.blue span { color: #d6e6ff; border-color: rgba(47, 125, 225, 0.55); background: rgba(47, 125, 225, 0.16); }
.hub-strip.both span { color: #fff0c2; border-color: rgba(255, 217, 74, 0.5); background: rgba(255, 217, 74, 0.12); }
.score .teams { font-family: var(--mono); font-size: 11px; letter-spacing: 0.04em; color: #c8d5ea; }
canvas#field { width: 100%; display: block; border-radius: 10px; border: 1px solid var(--line); background: #0f1620; }
.transport { display: flex; align-items: center; gap: 10px; margin-top: 10px; }
.transport input[type="range"] { flex: 1; }
.speed { font-size: 12px; color: var(--dim); display: flex; align-items: center; gap: 6px; }
.speed select { width: auto; }
.period-strip { display: grid; grid-template-columns: 20fr 10fr repeat(4, 25fr) 30fr; gap: 3px; margin-top: 10px; }
.period-strip .seg { border-radius: 6px; padding: 4px 2px; text-align: center; font-size: 10px; font-family: var(--mono); border: 1px solid var(--line); color: var(--dim); background: #101823; }
.period-strip .seg.r { background: rgba(224, 65, 63, 0.22); color: #ffd9d8; }
.period-strip .seg.b { background: rgba(47, 125, 225, 0.22); color: #d6e6ff; }
.period-strip .seg.rb { background: linear-gradient(90deg, rgba(224,65,63,0.3), rgba(47,125,225,0.3)); color: var(--text); }
.period-strip .seg.cur { outline: 2px solid var(--accent); }
.period-strip .seg small { display: block; font-size: 9px; opacity: 0.75; }
.auto-note {
  margin-top: 8px; font-size: 12px; color: var(--dim);
  border-left: 2px solid var(--accent); padding: 4px 0 4px 9px; background: rgba(255, 217, 74, 0.05);
  border-radius: 0 8px 8px 0;
}
.auto-note:empty { display: none; }
.auto-note b { color: var(--text); }
.legend { color: var(--dim); font-size: 11px; margin-top: 8px; display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
/* The roster chips are the focus control, so they sit at the end of the legend as buttons. */
.legend .roster-side { font-family: var(--mono); color: var(--dim); margin-left: 6px; }
.legend .roster-chip {
  font-family: var(--mono); font-size: 11px; padding: 1px 7px; border-radius: 999px; cursor: pointer;
  background: #101823; border: 1px solid var(--line); color: #cfdaf0;
}
.legend .roster-chip:hover { border-color: #3d4d63; }
.legend .roster-chip.on { background: rgba(255, 217, 74, 0.16); border-color: rgba(255, 217, 74, 0.6); color: #fff0c2; }
.speed input[type="checkbox"] { width: auto; }
/* The keyboard strip under the heading is a reference, not prose, so it is drawn as keys. */
.kbd {
  font-family: var(--mono); font-size: 10.5px; padding: 1px 5px; border-radius: 5px;
  border: 1px solid var(--line); border-bottom-width: 2px; background: #0f161f; color: #cfdaf0;
}
.howto { margin-top: 8px; }
.howto summary { cursor: pointer; color: var(--dim); font-size: 12px; }
.howto[open] summary { color: var(--text); }
.howto p { margin-top: 6px; line-height: 1.55; }

/* ---- immersive playback -------------------------------------------------------------------
   Two flavours. `body.cinema` (the Fill button, key C) lifts the FIELD panel over the page and hides
   everything else, leaving the browser chrome alone. `:fullscreen` (the Fullscreen button, key F) hands the
   panel to the browser, which removes the chrome. Both just need the panel to report an honest
   content box, because ui.js measures the leftover height there and letterboxes the canvas into it. */
body.cinema { overflow: hidden; }
body.cinema .field-panel {
  position: fixed;
  inset: 0;
  z-index: 60;
  border-radius: 0;
  padding: 12px 16px 14px;
  overflow: hidden;
}
.field-panel:fullscreen {
  padding: 12px 16px 14px;
  overflow: hidden;
  background: linear-gradient(180deg, var(--panel) 0%, var(--panel-2) 100%);
}
body.cinema .field-panel .panel-head,
.field-panel:fullscreen .panel-head { margin-bottom: 6px; }
/* The legend is a reading aid for the page, not part of the picture: in Fill or Fullscreen it is a
   wall of text under the FIELD that steals height the replay wants. Drop it and let ui.js letterbox
   the canvas into what is left. Same for the explanation under the heading and the "How to read the
   picture" details - even collapsed they cost a row, and a viewer who is watching the replay has
   already read them. And the same for the two bars directly under the FIELD: the SHIFT strip and the
   AUTO line are both things you read when deciding what to do, not while watching a MATCH play, and
   the strip's seven segments are where a phone viewer loses a whole row of FIELD. */
body.cinema .field-panel .legend,
.field-panel:fullscreen .legend,
body.cinema .field-panel .howto,
.field-panel:fullscreen .howto,
body.cinema .field-panel #field-sub,
.field-panel:fullscreen #field-sub,
body.cinema .field-panel #period-strip,
.field-panel:fullscreen #period-strip,
body.cinema .field-panel #auto-note,
.field-panel:fullscreen #auto-note { display: none; }
body.cinema #field,
.field-panel:fullscreen #field { border-radius: 8px; }

/* ---- output ---- */
/* Six tabs in a narrow rail would wrap into three rows, so they are a segmented control that scrolls
   sideways instead, with the active one carried by an underline rather than a filled box (a filled box
   on a dark panel reads as a button, not as "you are here"). */
.tabs {
  display: flex; gap: 2px; border-bottom: 1px solid var(--line); padding-bottom: 0;
  overflow-x: auto; scrollbar-width: none; margin-bottom: 4px;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
  background: transparent; border: none; border-bottom: 2px solid transparent; color: var(--dim);
  padding: 6px 10px; cursor: pointer; font-size: 12px; white-space: nowrap; margin-bottom: -1px;
}
.tab:hover { color: var(--text); }
.btn.cancel { color: var(--warn); border-color: var(--warn); }
button:focus-visible, summary:focus-visible, [role="button"]:focus-visible, [role="tabpanel"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.tab.active { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
.tab-body { padding-top: 10px; max-height: calc(100vh - 230px); overflow-y: auto; }
.tab-body h3 { margin: 16px 0 6px; font-size: 13px; }

/* The rule-source prose used to be a wall of paragraphs the tab could not be scanned through. Each
   block is a collapsed <details> now: the summary is the claim, opening it shows the measurement. */
.notes { display: grid; gap: 6px; margin-top: 8px; }
.note { border: 1px solid var(--line); border-radius: 10px; background: #0f161f; }
.note > summary {
  cursor: pointer; list-style: none; padding: 8px 11px; font-size: 12px; color: var(--text);
  display: flex; align-items: baseline; gap: 8px;
}
.note > summary::-webkit-details-marker { display: none; }
.note > summary::before { content: '▸'; color: var(--dim); font-size: 10px; flex: none; }
.note[open] > summary::before { content: '▾'; }
.note > summary:hover { background: rgba(255, 255, 255, 0.035); border-radius: 10px; }
.note > summary b { font-weight: 600; }
.note > summary i { font-style: normal; color: var(--dim); font-size: 11px; }
.note .body { padding: 0 11px 10px 27px; display: grid; gap: 7px; border-top: 1px dashed var(--line); padding-top: 8px; }
.note .body p { margin: 0; font-size: 12px; color: var(--dim); line-height: 1.55; }
.note .body p strong { color: var(--text); font-weight: 600; }
.note .body table { font-size: 11.5px; }

.pick-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.pick-row label { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--dim); }
.pick-note { font-size: 11.5px; color: var(--good); }

/* Before the first run the insight sections would render as bare headings over empty canvases;
   show one message instead and hide the skeleton. The seat and opponent selectors stay visible: they
   decide what the NEXT run means, so hiding them until a run exists is backwards. */
#tab-insight.is-empty > *:not(.empty):not(.pick-row) { display: none; }
.empty {
  display: grid; gap: 6px; padding: 22px 18px; text-align: center;
  border: 1px dashed var(--line); border-radius: 12px; color: var(--dim); font-size: 12.5px;
}
.empty strong { color: var(--text); font-size: 14px; }
.cards { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
.card { background: #0f161f; border: 1px solid var(--line); border-radius: 10px; padding: 9px 11px; }
.card .k { font-size: 11px; color: var(--dim); }
.card .v { font-family: var(--mono); font-size: 20px; }
.card .s { font-size: 11px; color: var(--dim); font-family: var(--mono); }
canvas.chart { width: 100%; height: 120px; display: block; background: #0f161f; border: 1px solid var(--line); border-radius: 10px; }
/* The four single-MATCH charts. One column on a narrow panel (the in-app preview is ~510 px), two on a wide
   screen, so a band chart never has to squeeze six rows into 200 px. */
.charts { display: grid; grid-template-columns: 1fr; gap: 8px; margin: 6px 0 2px; }
@media (min-width: 900px) { .charts { grid-template-columns: 1fr 1fr; } }
canvas.chart.tall { height: 168px; }
.rp-list { display: grid; gap: 6px; }
.rp-row { display: grid; grid-template-columns: 1fr auto; gap: 8px; align-items: center; font-size: 12px; }
.rp-bar { height: 8px; background: #0f161f; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; grid-column: 1 / -1; }
.rp-bar > i { display: block; height: 100%; background: linear-gradient(90deg, #265ea8, var(--good)); }
.plan-view table, .table-wrap table { width: 100%; border-collapse: collapse; font-size: 12px; }
.table-wrap { overflow-x: auto; }
th, td { text-align: left; padding: 5px 7px; border-bottom: 1px solid var(--line); white-space: nowrap; }
th { color: var(--dim); font-weight: 500; font-size: 11px; position: sticky; top: 0; background: var(--panel-2); }
tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.018); }
tbody tr:hover { background: rgba(255, 255, 255, 0.045); }
td.mono, th.mono { font-family: var(--mono); }
.role-SCORE { color: var(--good); }
.role-DEFEND { color: var(--warn); }
.role-CLIMB { color: #b48ce8; }
.posterior { display: grid; gap: 5px; }
.post-row { display: grid; grid-template-columns: 78px 1fr 44px; gap: 8px; align-items: center; font-size: 12px; }
.post-row .bar { height: 8px; background: #0f161f; border: 1px solid var(--line); border-radius: 999px; overflow: hidden; }
.post-row .bar > i { display: block; height: 100%; background: var(--accent); }
.insights { margin: 0; padding-left: 18px; display: grid; gap: 8px; font-size: 12.5px; }
.insights li::marker { color: var(--accent); }
.insights b { color: var(--text); }
.insights .tone-good b { color: var(--good); }
.insights .tone-warn b { color: var(--warn); }
.insights .tone-bad b { color: var(--bad); }
.events { margin: 0; padding-left: 18px; font-size: 12px; font-family: var(--mono); color: var(--dim); max-height: 220px; overflow-y: auto; }

/* ---- fouls, traced to a ROBOT ---------------------------------------------------------------
   A foul is worth 5 or 15 points, which is a whole cycle of travel, so it has to be visible where the
   score is and not only in a table on the far side of the page. The chips sit under the scoreboard and
   each one jumps the replay to the second it happened. */
.foul-strip { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 10px; }
.foul-strip:empty { display: none; }
.foul-chip {
  font-family: var(--mono); font-size: 11px; padding: 3px 9px; border-radius: 999px; cursor: pointer;
  background: #16202c; border: 1px solid var(--line); color: var(--dim);
}
.foul-chip b { color: var(--text); }
.foul-chip.red { border-color: rgba(224, 65, 63, 0.55); color: #ffd9d8; background: rgba(224, 65, 63, 0.14); }
.foul-chip.blue { border-color: rgba(47, 125, 225, 0.55); color: #d6e6ff; background: rgba(47, 125, 225, 0.14); }
.foul-chip:hover { filter: brightness(1.18); }
#foul-note b { color: var(--text); }
#foul-note:not(:empty) { margin-bottom: 8px; }
#foul-table tr.jump { cursor: pointer; }
#foul-table tr.jump:hover td { background: rgba(255, 255, 255, 0.06); }
/* A foul COUNT in the robot table is a question, so it is a control: click it and the clause table
   below narrows to that machine. */
.foul-link {
  font: inherit; font-family: var(--mono); background: none; border: none; padding: 0; cursor: pointer;
  border-bottom: 1px dashed var(--warn); color: var(--warn);
}
.foul-clear {
  font-size: 11px; margin-left: 6px; padding: 2px 9px; border-radius: 999px; cursor: pointer;
  background: #16202c; border: 1px solid var(--line); color: var(--dim); font-family: inherit;
}
.foul-clear:hover { color: var(--text); border-color: #3d4d63; }
.conflicts { display: grid; gap: 8px; }
.conflict { border: 1px solid #5a4a12; background: rgba(240, 166, 60, 0.08); border-radius: 10px; padding: 9px 11px; font-size: 12px; }
.conflict b { color: var(--warn); }
.field-label { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--dim); margin: 10px 0; }
.field-label input { width: 90px; }
footer { padding: 16px 18px 28px; color: var(--dim); font-size: 11.5px; display: flex; flex-direction: column; gap: 4px; border-top: 1px solid var(--line); }

/* Two columns from here down: the setup rail joins the field on top and the inspector keeps its own
   column, because the FIELD and the read-out are the two things you compare. */
/* ---- refactor polish ----------------------------------------------------------------------
   Three panels now scroll inside themselves, so the default light-mode scrollbar would be the loudest
   thing on a dark page. Selection and card rhythm come with it. */
* { scrollbar-width: thin; scrollbar-color: #2f3d51 transparent; }
*::-webkit-scrollbar { width: 9px; height: 9px; }
*::-webkit-scrollbar-thumb { background: #2f3d51; border-radius: 999px; border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: #3d4d63; background-clip: content-box; }
*::-webkit-scrollbar-track { background: transparent; }
::selection { background: rgba(255, 217, 74, 0.28); }
.cards { gap: 7px; }
.card { position: relative; }
.card .v { letter-spacing: -0.01em; }
/* A section heading inside a scrolling column should not float free of what it names. */
.tab-body h3 { position: sticky; top: 0; z-index: 2; background: linear-gradient(180deg, var(--panel-2) 60%, rgba(24, 34, 47, 0)); padding-top: 4px; }
.table-wrap { scrollbar-gutter: stable; }

@media (max-width: 1400px) {
  main { grid-template-columns: minmax(0, 1fr) minmax(340px, 440px); }
  .panel.setup { grid-column: 1 / -1; position: static; max-height: none; overflow: visible; }
  /* With the rail no longer scrolling, two alliance columns are wide enough again. */
  .setup-grid { grid-template-columns: 1fr 1fr; }
  .run-row { position: static; background: none; }
}

@media (max-width: 1100px) {
  /* minmax(0, 1fr), not 1fr: a grid track's automatic minimum is its content's min-content width, and
     the reference tables below use nowrap cells, so a plain 1fr let MAIN push 184 px past the viewport
     on a phone and gave the whole page a sideways scroll. */
  main { grid-template-columns: minmax(0, 1fr); }
  .panel.output { position: static; max-height: none; overflow: visible; }
  .tab-body { max-height: none; }
  .setup-grid { grid-template-columns: 1fr; }
}

@media (max-width: 640px) {
  main { padding: 12px 12px 32px; gap: 12px; }
  .robot-row { grid-template-columns: 1fr 1fr; }
  .cards { grid-template-columns: 1fr; }
  .scoreboard { grid-template-columns: 1fr; }
  .score.blue { text-align: left; }
  .clock { order: -1; }
  .period-strip { grid-template-columns: repeat(4, 1fr); }
  .period-strip .seg { padding: 4px 1px; }
  .topbar { flex-wrap: wrap; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  .btn:active:not(:disabled) { transform: none; }
}

/* For a control the user never looks at but assistive tech and the file picker still have to reach —
   a `hidden` input is removed from the accessibility tree, so the Import picker is clipped instead. */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
