/* CS Anchor - 3D replay chrome.
 *
 * Every token below is lifted from the product's design source of truth, the League Ledger
 * export's app-page :root (D:\OneDrive\AI\projects\cs-anchor-design\league-ledger\CS Anchor
 * Ledger.html). The Ledger already contains a "Round replay" module -- 2D, but with the panel,
 * chip, kill-feed and transport language this 3D view now speaks. Nothing here invents a new
 * visual system; the PoC's cyan hologram chrome is gone.
 *
 * Deliberately NO backdrop-filter: the Ledger's panels are opaque with a hairline inset, and a
 * blur behind ten fixed panels is real cost on the software-rendering path.
 */

:root {
  /* --- Ledger app tokens, verbatim --- */
  --color-bg: #0d0f14;
  --color-surface: #15181f;
  --color-text: #e9e9ed;
  --color-accent: #35c9b1;
  --accent-800: #16443f;
  --panel: #14171e;
  --panel-2: #191d26;
  --panel-3: #1f2430;
  --line: rgba(233, 233, 237, 0.10);
  --line-2: rgba(233, 233, 237, 0.16);
  /* The Ledger's two dim steps, opened up one notch in pass 5. The operator's reader
   * "feel[s] the font is not so easy to read", and half of that is contrast: this viewer
   * carries the Ledger's type over a moving 3D scene, at sizes the Ledger never uses.
   * CONTRAST RULE for this surface, unchanged in kind: --dim-2 is DECORATION ONLY (arrows,
   * separators, timeline spans) and now measures 3.5:1 on --panel, up from 2.7:1. Anything
   * a reviewer actually READS -- weapon names, callouts, timestamps, money -- takes --dim,
   * now 6.8:1, up from 5.0:1. The two tiers stay two tiers; both moved up a step. */
  --dim: rgba(233, 233, 237, 0.66);
  --dim-2: rgba(233, 233, 237, 0.42);
  --t: #f0a13a;
  --ct: #4f9df7;
  --win: #35c9b1;
  --loss: #e0574f;
  --mono: ui-monospace, "SF Mono", SFMono-Regular, Menlo, Consolas, monospace;
  --font-body: "Inter", system-ui, sans-serif;
  --font-heading: "Inter", system-ui, sans-serif;
  --radius-sm: 4px;
  --radius-md: 8px;
  --radius-lg: 14px;
  --space-2: 5.6px;
  --space-3: 8.4px;
  --space-4: 11.2px;
  --z-chrome: 3;
  --z-pop: 8;
  --z-warn: 20;
  --z-load: 30;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--color-bg);
  color: var(--color-text);
  font: 400 13px/1.45 var(--font-body);
  overflow: hidden;
  -webkit-font-smoothing: antialiased;
}

button { font: inherit; color: inherit; }

#stage { position: fixed; inset: 0; display: block; width: 100%; height: 100%; }

/* PASS 8. The flat view's own surface. It sits exactly where the WebGL canvas does and only
 * one of the two is ever in the document flow: `hidden` on a canvas is display:none, so the
 * hidden one costs no compositing and no memory traffic at all. */
#stage2d { position: fixed; inset: 0; display: block; width: 100%; height: 100%; cursor: grab; }
#stage2d[hidden] { display: none; }
#stage2d.panning { cursor: grabbing; }
/* Cutaway slices a 3D model along its height. Straight down on a flat schematic there is no
 * roof to open, so the control is not disabled-and-confusing, it is simply not there. */
body.mode-2d .cutwell { display: none; }
/* PASS 9. The style toggle belongs to Overhead and is on screen exactly when Overhead is.
 * `display` is set explicitly on both sides because the well is flex, not inline. */
.stylewell { display: none; }
body.cam-over .stylewell { display: flex; }

/* A ground shade under the chrome so panels always have contrast over the map. */
body::after {
  content: "";
  position: fixed; inset: 0; pointer-events: none; z-index: 1;
  background:
    linear-gradient(180deg, rgba(13, 15, 20, 0.55) 0%, rgba(13, 15, 20, 0) 22%),
    linear-gradient(0deg, rgba(13, 15, 20, 0.6) 0%, rgba(13, 15, 20, 0) 18%);
}

/* ------------------------------------------------------------------ panels */

.panel {
  position: fixed;
  z-index: var(--z-chrome);
  background: var(--panel);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--line), 0 10px 30px rgba(0, 0, 0, 0.45);
  overflow: hidden;
}
.panel-head {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
  background: linear-gradient(90deg, rgba(53, 201, 177, 0.07), transparent);
  box-shadow: inset 0 -1px 0 var(--line);
}
.cap {
  font: 600 10.5px/1 var(--mono);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
}
.num { font-variant-numeric: tabular-nums; font-family: var(--mono); }

/* ------------------------------------------------------------------ buttons */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  cursor: pointer;
  font: 500 12px/1.2 var(--mono);
  /* Pass 5 put a step of size on .btn-sm, and "Skip buy" answered by breaking over two
     lines. A control's label is one line; the bar is wide enough to say so. */
  white-space: nowrap;
  color: var(--color-text);
  background: transparent;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-md);
  padding: var(--space-2) calc(var(--space-3) * 1.2);
  transition: background 0.13s ease, border-color 0.13s ease, color 0.13s ease;
}
.btn:hover { background: rgba(233, 233, 237, 0.07); }
.btn:active { background: rgba(233, 233, 237, 0.14); transform: translateY(1px); }
.btn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.btn-primary { color: var(--color-accent); border-color: var(--color-accent); }
.btn-primary:hover { background: rgba(53, 201, 177, 0.12); }
.btn-primary:active { background: rgba(53, 201, 177, 0.22); }
.btn[aria-pressed="true"], .btn.on {
  color: var(--color-accent);
  border-color: var(--color-accent);
  background: rgba(53, 201, 177, 0.10);
}
.btn[disabled] { opacity: 0.34; pointer-events: none; }
.btn-sm { padding: 3px 6px; font-size: 11.5px; border-radius: var(--radius-sm); }

/* One authored icon family: 20-unit box, 1.8 stroke, round caps and joins -- the stroke
   language of the Ledger's own nav glyphs. No unicode arrows standing in for icons. */
.btn.ico { padding: 3px 5px; }
.btn.ico svg {
  width: 15px; height: 15px; display: block;
  fill: none; stroke: currentColor;
  stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round;
}

/* Every focusable control gets a visible ring, including the bare-button ones
   (round chips, scoreboard rows, layer toggles) that carry no .btn class. */
.chip:focus-visible, .prow:focus-visible, .toggle:focus-visible, .kill:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* ------------------------------------------------------- top bar / identity */

#topbar {
  top: 12px; left: 12px; right: 12px;
  display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  padding: 8px 12px;
}
.brand { display: flex; align-items: center; gap: 8px; min-width: 0; }
.brand svg { flex: none; }
.brand-txt { line-height: 1.06; min-width: 0; }
.brand-txt b { font: 600 13.5px/1.1 var(--font-heading); letter-spacing: 0.02em; }
.brand-txt small {
  display: block; font: 400 10px/1.3 var(--mono);
  letter-spacing: 0.11em; text-transform: uppercase; color: var(--dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* PASS 12: the match picker. A native <select> on purpose -- it is a list of seven fixed
   things, the platform already draws it correctly on every screen size and with a keyboard,
   and a bespoke dropdown here would be a second popover to maintain for no gain. It is
   styled to sit inside the brand line rather than on top of it. */
.brandline { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.matchpick {
  flex: 1 1 auto; min-width: 0; max-width: 30ch;
  font: 400 10px/1.3 var(--mono); letter-spacing: 0.06em;
  color: var(--dim); background: transparent;
  border: 1px solid var(--line); border-radius: 5px;
  padding: 2px 4px; cursor: pointer;
}
.matchpick:hover { color: var(--fg); border-color: var(--accent); }
.matchpick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.matchpick option { background: #12161d; color: #e9e9ed; }

.score { display: flex; align-items: center; gap: 10px; justify-self: center; }
.score .sc {
  font: 600 22px/1 var(--mono); font-variant-numeric: tabular-nums;
  min-width: 30px; text-align: center;
}
.score .sc.a { color: var(--ct); }
.score .sc.b { color: var(--t); }
.score .vs { color: var(--dim-2); font: 400 13px var(--mono); }
.score .club {
  font: 600 11px/1 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--dim); max-width: 128px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.score .club.a { text-align: right; }

.topright { display: flex; align-items: center; gap: 10px; justify-self: end; }
.clock {
  font: 600 17px/1 var(--mono); font-variant-numeric: tabular-nums;
  letter-spacing: 0.02em;
}
.clock.hot { color: var(--loss); }
.alive { display: flex; align-items: center; gap: 5px; font: 600 13px var(--mono); }
.alive .ct { color: var(--ct); }
.alive .t { color: var(--t); }
.alive .sep { color: var(--dim-2); font-weight: 400; }
.roundtag {
  font: 600 11px/1 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--dim); padding: 4px 7px;
  border-radius: var(--radius-sm); box-shadow: inset 0 0 0 1px var(--line);
}

/* ------------------------------------------------------------- round strip */

#roundstrip {
  top: 62px; left: 50%; transform: translateX(-50%);
  width: min(1000px, calc(100vw - 24px));
  display: flex; align-items: center; gap: 8px;
  padding: 7px 10px;
}
#roundstrip .cap { flex: none; }
.chips { display: flex; gap: 3px; flex: 1; min-width: 0; }
.chip {
  flex: 1; min-width: 15px; padding: 5px 0;
  border: 1px solid var(--line); border-radius: 3px;
  background: transparent; color: var(--dim);
  font: 600 10.5px/1 var(--mono); cursor: pointer;
  position: relative;
  transition: background 0.12s ease, color 0.12s ease;
}
.chip:hover { background: rgba(233, 233, 237, 0.07); color: var(--color-text); }
.chip.ct { color: var(--ct); }
.chip.t { color: var(--t); }
.chip.on {
  color: var(--color-accent);
  box-shadow: inset 0 0 0 1px var(--color-accent);
  background: rgba(53, 201, 177, 0.08);
}
/* one hairline under the chip in the winning side's colour: the score progression
   reads as a band even before you parse the numbers */
.chip::after {
  content: ""; position: absolute; left: 2px; right: 2px; bottom: -1px; height: 2px;
  background: currentColor; opacity: 0.55; border-radius: 1px;
}
.chip.pending::after { opacity: 0.12; }
.chip .kd {
  position: absolute; top: -1px; right: 1px;
  width: 3px; height: 3px; border-radius: 50%; background: var(--loss); opacity: 0.9;
}

/* ------------------------------------------------------------- scoreboards */

#boards {
  position: fixed; z-index: var(--z-chrome);
  left: 12px; top: 108px;
  width: 268px;
  display: flex; flex-direction: column; gap: 8px;
  max-height: calc(100vh - 210px);
}
#boards .panel { position: static; }
.team-head { display: flex; align-items: baseline; gap: 8px; }
.team-head .nm {
  font: 600 11.5px/1 var(--mono); letter-spacing: 0.07em; text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.team-head .side { font: 600 10.5px/1 var(--mono); letter-spacing: 0.09em; color: var(--dim); }
.team-head .eq { margin-left: auto; font: 500 11px/1 var(--mono); color: var(--dim); }
.team.ct .team-head .nm { color: var(--ct); }
.team.t .team-head .nm { color: var(--t); }

.prow {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 46px;
  gap: 2px 8px;
  align-items: center;
  padding: 5px 10px;
  width: 100%;
  border: 0; background: transparent; text-align: left; cursor: pointer;
  box-shadow: inset 0 -1px 0 rgba(233, 233, 237, 0.05);
  transition: background 0.12s ease;
}
.prow:last-child { box-shadow: none; }
.prow:hover { background: rgba(233, 233, 237, 0.05); }
.prow.follow { background: rgba(53, 201, 177, 0.11); box-shadow: inset 2px 0 0 var(--color-accent); }
.prow.dead { opacity: 0.42; }
.prow .who {
  display: flex; align-items: center; gap: 5px; min-width: 0;
  font: 500 12.5px/1.15 var(--font-body);
}
.prow .who span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.prow.dead .who span { text-decoration: line-through; }
.prow .kda { font: 500 11.5px/1 var(--mono); color: var(--dim); text-align: right; }
.prow .kda b { color: var(--color-text); font-weight: 600; }

/* HP bar: no background track for the armour, no rounded pill - a ledger rule, not a game HUD */
.hpbar {
  grid-column: 1; height: 3px; border-radius: 2px;
  background: rgba(233, 233, 237, 0.10); overflow: hidden;
}
.hpbar i { display: block; height: 100%; background: var(--win); transition: width 0.1s linear; }
.hpbar.low i { background: var(--loss); }
.hpbar.mid i { background: var(--t); }
.prow .gear {
  grid-column: 1 / -1;
  display: flex; align-items: center; gap: 6px;
  font: 400 11px/1.2 var(--mono); color: var(--dim);
  min-width: 0;
}
.prow .gear .w {
  color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.prow .gear .money { margin-left: auto; color: var(--win); flex: none; }
.prow .gear .money.broke { color: var(--dim); }
.prow .gear .av { flex: none; color: var(--ct); letter-spacing: 0.02em; }
.prow .gear .kit { flex: none; color: var(--color-accent); }
.prow .flashed { flex: none; color: #f5f0c8; }
.prow .nade { flex: none; letter-spacing: 0.5px; }

/* ---------------------------------------------------------------- kill feed */

#rightcol {
  position: fixed; z-index: var(--z-chrome);
  right: 12px; top: 108px; width: 244px;
  display: flex; flex-direction: column; gap: 8px;
  max-height: calc(100vh - 210px);
}
#feed { display: flex; flex-direction: column; gap: 4px; }

/* --------------------------------------------------------------- event rail
 *
 * PRIORITY 3's home. Closed by default. Rows are hairline-separated, not carded: at this
 * density a box around every event is noise that competes with the 3D scene behind it.
 */
#rail { position: static; display: flex; flex-direction: column; min-height: 0; }
#rail .panel-head { gap: 6px; }
#rail .panel-head .cap { flex: none; }
.tabs { display: flex; gap: 2px; margin-left: auto; }
.tab {
  border: 0; background: transparent; cursor: pointer;
  font: 500 11.5px/1 var(--mono); letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--dim); padding: 4px 7px; border-radius: var(--radius-sm);
  transition: color 0.12s ease, background 0.12s ease;
}
.tab:hover { color: var(--color-text); background: rgba(233, 233, 237, 0.06); }
.tab.on { color: var(--color-accent); background: rgba(53, 201, 177, 0.10); }
.tab:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.btn.ico.close { padding: 3px; width: 22px; height: 22px; border-color: transparent; }
.btn.ico.close svg { width: 13px; height: 13px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
.btn.ico.close:hover { border-color: var(--line-2); }

/* Wraps rather than clips: the chips grew a step in pass 5, and the rail is 244px wide at
   full size and 220px under 1180. A filter you cannot read the name of is not a filter. */
.filters { display: flex; flex-wrap: wrap; gap: 2px; padding: 7px 5px; box-shadow: inset 0 -1px 0 var(--line); }
.fchip {
  border: 1px solid transparent; background: transparent; cursor: pointer;
  font: 500 11px/1 var(--mono); letter-spacing: 0.02em;
  color: var(--dim); padding: 4px 4px; border-radius: var(--radius-sm);
  transition: color 0.12s ease, border-color 0.12s ease;
}
.fchip:hover { color: var(--color-text); }
.fchip.on { color: var(--color-accent); border-color: rgba(53, 201, 177, 0.45); }
.fchip:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

.raillist { overflow-y: auto; scrollbar-width: thin; min-height: 0; max-height: 44vh; }
.erow {
  display: grid; grid-template-columns: 34px 1fr auto; gap: 7px; align-items: baseline;
  width: 100%; text-align: left; cursor: pointer;
  border: 0; border-left: 2px solid transparent; background: transparent;
  padding: 6px 9px 6px 8px;
  box-shadow: inset 0 -1px 0 var(--line);
  transition: background 0.1s ease;
}
.erow:hover { background: rgba(233, 233, 237, 0.05); }
.erow:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.erow.now { background: rgba(53, 201, 177, 0.08); border-left-color: var(--color-accent); }
.erow .at { font: 500 11px var(--mono); color: var(--dim); }
.erow .txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.erow .ttl { font: 500 12.5px/1.25 var(--font-body); color: var(--color-text); }
.erow .sub { font: 400 11px/1.25 var(--mono); color: var(--dim); }
.erow.s-ct .ttl { color: #b7d8ff; }
.erow.s-t .ttl { color: #ffdcae; }
.erow.dmg .ttl, .erow.throw .ttl, .erow.smoke .ttl, .erow.fire .ttl,
.erow.flash .ttl, .erow.he .ttl, .erow.freeze .ttl { color: var(--dim); font-weight: 400; }
.erow .tags { display: flex; gap: 3px; }
.erow .tags i {
  font: 600 9.5px/1 var(--mono); letter-spacing: 0.07em; font-style: normal;
  color: var(--dim); border: 1px solid var(--line-2); border-radius: 3px; padding: 2px 3px;
}
.erow .tags i:first-child { color: var(--color-accent); border-color: rgba(53, 201, 177, 0.4); }

.empty { margin: 0; padding: 12px 10px; font: 400 12px/1.5 var(--font-body); color: var(--dim); }

.notewell { display: flex; gap: 6px; padding: 8px; box-shadow: inset 0 -1px 0 var(--line); }
#noteText {
  flex: 1; min-width: 0;
  background: var(--panel-2); color: var(--color-text);
  border: 1px solid var(--line-2); border-radius: var(--radius-sm);
  padding: 6px 8px; font: 400 12px var(--font-body);
}
#noteText::placeholder { color: var(--dim); }
#noteText:focus { outline: 2px solid var(--color-accent); outline-offset: 0; border-color: transparent; }
.nrow { display: flex; align-items: stretch; box-shadow: inset 0 -1px 0 var(--line); }
.ngo {
  flex: 1; min-width: 0; display: grid; grid-template-columns: 62px 1fr; gap: 7px;
  align-items: baseline; text-align: left; cursor: pointer;
  border: 0; background: transparent; padding: 7px 4px 7px 9px;
}
.ngo:hover { background: rgba(233, 233, 237, 0.05); }
.ngo .at { font: 500 11px var(--mono); color: var(--color-accent); }
.ngo .txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ngo .ttl { font: 500 12.5px/1.3 var(--font-body); }
.ngo .sub { font: 400 11px var(--mono); color: var(--dim); }
.nrm {
  flex: none; width: 28px; border: 0; background: transparent; cursor: pointer;
  color: var(--dim);
}
.nrm:hover { color: var(--loss); }
.nrm svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; }
.ngo:focus-visible, .nrm:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
/* Feed rows seek to their own moment, exactly as the Ledger's replay module does. */
.kill {
  display: grid; grid-template-columns: 34px minmax(0, 1fr); gap: 8px;
  align-items: baseline;
  width: 100%;
  padding: 5px 8px;
  border: 0; text-align: left; font: inherit; color: inherit;
  border-radius: var(--radius-sm);
  background: var(--panel-2);
  box-shadow: inset 0 0 0 1px var(--line);
  animation: killIn 0.18s ease-out;
  transition: opacity 0.15s ease, background 0.12s ease;
}
button.kill { cursor: pointer; }
button.kill:hover { background: var(--panel-3); opacity: 1; }
@keyframes killIn { from { opacity: 0; transform: translateX(10px); } to { opacity: 1; transform: none; } }
/* Old rows recede but stay READABLE: at 0.4 the sub-line fell under 3:1 on the panel. */
.kill.fading { opacity: 0.58; }
.kill .at { font: 500 11px/1.3 var(--mono); color: var(--dim); }
.kill .line {
  display: block; font: 500 12.5px/1.35 var(--mono);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.kill .sub { display: block; font: 400 11px/1.3 var(--mono); color: var(--dim); }
.kill .n-ct { color: var(--ct); }
.kill .n-t { color: var(--t); }
.kill .arrow { color: var(--dim-2); margin: 0 3px; }
.kill .tag { color: var(--loss); }
.kill.evt .line { color: var(--color-accent); }
.kill.evt.explode .line { color: var(--loss); }

/* ---------------------------------------------------------------- transport */

/* Pass 2 widened this bar. The timeline stopped being decoration and became the primary
   navigation, and at 1120px with eleven controls beside it the clock was squeezed to
   150px -- a scrub you cannot aim is not a navigation tool.
 *
 * PASS 16 finished that thought. Pass 2 through 15 kept widening the BAR while the row
 * inside it kept collecting controls, so the timeline's share of it went DOWN anyway. The
 * bar's width is unchanged here. What changed is that it is a COLUMN of two rows now, and
 * the timeline shares its row with four buttons and a clock instead of with everything.
 *
 * Measured at 1440 (pass16_shots.py, same round, same instant, both builds):
 *
 *              timeline of 1416   bar height
 *   follow       244  ->  1012      86 -> 100
 *   overhead     292  ->  1012     110 -> 124
 *
 * 4.1x the horizontal resolution for 14px of bar. At 845 -- the narrowest the app's embed
 * ever shows the viewer -- the strip was already folding onto its own line and stays at
 * 797 of 821, so the narrow end pays nothing and gains Skip buy back (see the 900px block
 * at the foot of this file). */
#transport {
  left: 50%; bottom: 14px; transform: translateX(-50%);
  width: min(1480px, calc(100vw - 24px));
  display: flex; flex-direction: column; gap: 8px;
  padding: 9px 12px;
}
/* Each row wraps on its own. .panel is overflow:hidden, so a row that does not fit has to
   fold rather than be clipped -- Pass 5 learned that with "Settings is not there".
 *
 * .tr-clock's ONE grower is the timeline (.scrub is the only flex-grow in it); everything
 * else there is flex:none, which is what makes "the strip takes the rest" exactly true.
 * .tr-view grows nothing: its controls are all their natural size, and the whole slack
 * goes into one gap -- see the hairline below. */
.tr-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; row-gap: 8px; }
/* The auto margin lives on the SEPARATOR, so the hairline and the three buttons behind it
   travel to the far end as one piece and the gap opens between them and the camera. */
.tr-view .vsep { margin-left: auto; width: 1px; height: 18px; background: var(--line-2); flex: none; }
.panelseg { display: flex; align-items: center; gap: 6px; flex: none; }
#btnPlay { min-width: 74px; }
#btnSpeed { min-width: 46px; }
.time { font: 500 13px var(--mono); font-variant-numeric: tabular-nums; color: var(--dim); flex: none; }
.time b { color: var(--color-text); font-weight: 600; }

/* ------------------------------------------------------------ the timeline
 *
 * Three lanes over one clock. Spans on top, because a smoke is a WINDOW of denied vision
 * and a tick would throw that away; event ticks in the middle; the selected player's
 * damage underneath, its height carrying the size of the hit. Marks take pointer events
 * so they can be clicked and hovered; the slider still owns the space between them.
 */
/* PASS 8. The basis is 280px, not 340px, and that is a LINE-BREAKING number rather than a
 * rendered one: a wrapping flex container collects items onto a line using their flex BASIS,
 * and only then lets flex-shrink squeeze them. So a 340px basis reserved 100px the scrub
 * would happily have given back, and adding a fourth camera button tipped the transport onto
 * a second row at 1744px -- a very ordinary laptop width. At 280px the timeline still grows
 * to fill every pixel of slack (it is `flex-grow: 1`), it just stops claiming space it does
 * not need before the line is decided. Measured: one row again at 1744, still one at 1400. */
/* PASS 9: basis 240 rather than 280, which is its own min-width. Flex line-breaking is
 * decided on BASIS sizes and shrinking only happens afterwards, so a 280px basis was
 * reserving 40px the scrub was never going to keep -- and reserving it was enough to push
 * Settings onto a second row the moment the style well joined the bar. The rendered width is
 * unchanged wherever the row fits, because the scrub is the only item that grows and takes
 * all the free space back. */
/* PASS 16: the basis is still 240 and it still only decides LINE BREAKING. What changed is
 * what it breaks against -- four buttons and a clock rather than the whole bar -- so the
 * rendered width is now the row's whole slack at every desktop width, and the basis only
 * comes back into play inside a narrow embed, where the strip folds onto its own line. */
.scrub { position: relative; flex: 1 1 240px; min-width: 240px; height: 36px; }
.lanes { position: absolute; inset: 0 0 auto; top: 0; height: 24px; }
.lane { position: absolute; left: 0; right: 0; }
.lane.spans { top: 0; height: 5px; }
.lane.evts { top: 7px; height: 10px; }
.lane.dmg { top: 18px; height: 8px; opacity: 0; transition: opacity 0.15s ease; }
.lane.dmg.live { opacity: 1; }

.lane i { position: absolute; cursor: pointer; }
/* PASS 16. THE HIT BOX IS NOT THE MARK. A kill tick is painted 3px wide and a utility tick
 * 2px, and that is right -- widen the paint and a busy round becomes a smear. But 2px is
 * not a thing you can aim a mouse at, and the extra width this pass gave the strip buys
 * SEPARATION between the ticks rather than a bigger target on each one. So every tick
 * carries a transparent pad of 4px either side: the smallest mark becomes a 10px-wide
 * target without one pixel of what is drawn moving.
 *
 * HORIZONTAL ONLY, and that is the careful half. The lanes already sit over the top of the
 * range input (.lanes is 24px tall, #scrub's box starts at 20), and the slider owns the
 * space between the marks -- a vertical pad would take another 6px of the only band left
 * for a plain scrub-anywhere drag. Aiming at a tick is a horizontal problem; this fixes
 * exactly that. The pad is a pseudo-element, so `ev.target` in the tooltip and seek
 * delegate is still the <i> and still carries its dataset. */
.lane i::after { content: ''; position: absolute; inset: 0 -4px; }

.lane.spans i.span {
  top: 1px; height: 3px; border-radius: 2px; min-width: 2px;
  background: var(--dim-2);
}
.lane.spans i.span.smoke { background: rgba(201, 210, 221, 0.5); }
.lane.spans i.span.fire { background: rgba(224, 87, 79, 0.55); }
.lane.spans i.span.bomb { background: rgba(224, 87, 79, 0.9); top: 0; height: 5px; }
.lane.spans i.span:hover { filter: brightness(1.5); }

.lane.evts i {
  top: 2px; width: 2px; height: 7px; border-radius: 1px;
  background: var(--dim); opacity: 0.8;
}
.lane.evts i.k { height: 10px; top: 0; width: 3px; }
.lane.evts i.k-ct { background: var(--ct); }
.lane.evts i.k-t { background: var(--t); }
.lane.evts i.k.trade { box-shadow: 0 0 0 1px var(--color-accent); }
.lane.evts i.u { height: 5px; top: 4px; opacity: 0.62; }
.lane.evts i.u.sm { background: #c9d2dd; }
.lane.evts i.u.fi { background: var(--loss); }
.lane.evts i.u.fl { background: #f2e9c0; }
.lane.evts i.u.he { background: var(--t); }
.lane.evts i.plant { background: var(--loss); height: 10px; top: 0; width: 2px; }
.lane.evts i.defuse { background: var(--color-accent); height: 10px; top: 0; width: 2px; }
.lane.evts i.freeze {
  background: none; height: 12px; top: -1px; width: 1px;
  box-shadow: inset 0 0 0 1px var(--line-2); opacity: 1;
}
.lane.evts i:hover { opacity: 1; transform: scaleX(2); }

.lane.dmg i.d { bottom: 0; width: 2px; border-radius: 1px; background: var(--dim-2); }
.lane.dmg i.d.took { background: var(--loss); }
.lane.dmg i.d.dealt { background: var(--color-accent); }
.lane.dmg i.d:hover { transform: scaleX(2.4); }

#scrub {
  -webkit-appearance: none; appearance: none;
  position: absolute; left: 0; right: 0; bottom: 2px; width: 100%; height: 14px;
  background: transparent; cursor: pointer;
}
#scrub::-webkit-slider-runnable-track {
  height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, var(--color-accent) var(--p, 0%), rgba(233, 233, 237, 0.14) var(--p, 0%));
}
#scrub::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 11px; height: 11px; margin-top: -4px; border-radius: 50%;
  background: var(--color-text); border: 2px solid var(--panel);
}
#scrub:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }

/* The cutaway is the star of this viewer: it gets a labelled well, not a bare slider. */
.cutwell {
  display: flex; align-items: center; gap: 8px; flex: none;
  padding: 4px 9px 4px 10px;
  border-radius: var(--radius-md);
  background: var(--panel-2);
  box-shadow: inset 0 0 0 1px var(--line);
}
.cutwell.focus { box-shadow: inset 0 0 0 1px var(--color-accent); }
.cutwell .cap { color: var(--color-accent); }

/* PASS 9. The overhead style toggle, in the same labelled well as the cutaway and directly
 * beside it: the two controls that answer "how is this drawn" sit together, and in the flat
 * style the cutaway steps aside and leaves this one holding the slot. Segment styling is the
 * shared .seg / .sbtn, so nothing new is invented for it either. */
.stylewell {
  align-items: center; flex: none;
  padding: 3px;
  border-radius: var(--radius-md);
  background: var(--panel-2);
  box-shadow: inset 0 0 0 1px var(--line);
}
.stylewell .seg { background: transparent; padding: 0; gap: 2px; }
/* 6px, not the shared 4: this segment is a primary control in the transport, standing next
 * to 23-pixel switcher buttons, and it has to be the same size thing your finger aims at. */
.stylewell .sbtn { padding: 6px 9px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }
#cut { -webkit-appearance: none; appearance: none; width: 84px; height: 16px; background: transparent; cursor: pointer; }
#cut::-webkit-slider-runnable-track {
  height: 3px; border-radius: 2px;
  background: linear-gradient(90deg, rgba(233, 233, 237, 0.14) var(--c, 100%), var(--color-accent) var(--c, 100%));
}
#cut::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 11px; height: 11px; margin-top: -4px; border-radius: 2px;
  background: var(--color-text); border: 2px solid var(--panel-2);
}
#cut:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 2px; }
#cutVal { font: 500 11px var(--mono); color: var(--dim); min-width: 32px; text-align: right; }

/* --------------------------------------------------------- the "Why?" panel
 *
 * Contextual: it exists only while a kill is open, so it can take the prime spot directly
 * above the transport. Facts in two columns, no verdicts.
 */
#why {
  left: 50%; transform: translateX(-50%);
  bottom: 74px;
  width: min(760px, calc(100vw - 24px));
}
#why[hidden] { display: none; }
#why .panel-head { gap: 9px; }
.why-title { font: 400 12.5px/1.3 var(--font-body); display: flex; align-items: baseline; gap: 6px; flex-wrap: wrap; }
.why-title b { font-weight: 600; }
.why-title .meta { font: 400 11px var(--mono); color: var(--dim); }
#why .close { margin-left: auto; }
.why-body { padding: 9px 11px 13px; }
/* Both columns size to their own content and the row to the taller of them. Without the
   explicit min-height:0 a long facts list clipped its last row against the panel's
   overflow:hidden, which reads as a rendering bug rather than as more content. */
.why-col { min-width: 0; min-height: 0; }
.why-acts { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-bottom: 9px; }
.why-acts .vsep { width: 1px; height: 16px; background: var(--line-2); margin: 0 3px; }
#whySetup { color: var(--color-accent); border-color: rgba(53, 201, 177, 0.45); }

.why-cols { display: grid; grid-template-columns: 1.05fr 1fr; gap: 14px; align-items: start; }
.why-col h4, .pop h4 {
  margin: 0 0 6px;
  font: 600 10.5px/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
}
.dmglist { max-height: 132px; overflow-y: auto; scrollbar-width: thin; }
.dline {
  display: grid; grid-template-columns: 34px 34px 1fr auto 26px; gap: 6px; align-items: baseline;
  width: 100%; text-align: left; cursor: pointer;
  border: 0; background: transparent; padding: 4px 4px;
  box-shadow: inset 0 -1px 0 var(--line);
  font: 400 11.5px var(--font-body); color: var(--color-text);
}
.dline:hover { background: rgba(233, 233, 237, 0.05); }
.dline:focus-visible { outline: 2px solid var(--color-accent); outline-offset: -2px; }
.dline .at { font: 500 11px var(--mono); color: var(--dim); }
.dline .hp { font: 600 11.5px var(--mono); color: var(--t); }
.dline.took .hp { color: var(--loss); }
.dline.dealt .hp { color: var(--color-accent); }
.dline.fatal .hp { color: var(--loss); }
.dline.fatal { background: rgba(224, 87, 79, 0.07); }
.dline .w { font: 400 11px var(--mono); color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dline .by { font: 500 11.5px var(--font-body); }
.dline .rem { font: 500 11px var(--mono); color: var(--dim); text-align: right; }
.n-ct { color: #8dc4ff; }
.n-t { color: #ffcb8d; }

.facts { margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; align-items: baseline; }
.facts dt { font: 400 11px/1.45 var(--mono); color: var(--dim); }
.facts dd { margin: 0; font: 500 12px/1.45 var(--font-body); }

/* Priority 6's list form, under the scoreboards. */
#dmglog { position: static; }
#dmglog[hidden] { display: none; }
#dmglog .panel-head .nm { font: 500 11.5px var(--font-body); }
#dmglog .panel-head .tot { margin-left: auto; font: 500 11px var(--mono); color: var(--dim); }
#dmglog .dmglist { max-height: 148px; }
#dmglog .dline { grid-template-columns: 34px 34px 1fr auto; padding: 4px 9px; }

/* Priority 8: one line, not a second dashboard. */
#cmpbox {
  position: fixed; z-index: var(--z-chrome);
  left: 50%; transform: translateX(-50%); bottom: 76px;
  display: flex; align-items: baseline; gap: 8px;
  padding: 6px 11px;
  background: var(--panel); border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--line), 0 10px 30px rgba(0, 0, 0, 0.45);
  font: 500 12px var(--font-body);
}
#cmpbox[hidden] { display: none; }
#cmpbox .dim { color: var(--dim); font-weight: 400; }
#cmpbox .v { font: 500 11.5px var(--mono); color: var(--dim); }
#cmpbox .v.los.clear { color: var(--color-accent); }
#cmpbox .v.los.smoke { color: #c9d2dd; }
#cmpbox .v.los.blocked { color: var(--loss); }
#cmpbox .v.los.first { color: var(--t); font-weight: 600; }

/* Priority 4's readouts in the top bar. */
.state {
  font: 600 10.5px/1 var(--mono); letter-spacing: 0.09em;
  padding: 4px 6px; border-radius: var(--radius-sm);
  color: var(--loss); box-shadow: inset 0 0 0 1px rgba(224, 87, 79, 0.4);
}
.state[hidden] { display: none; }
.state.defuse { color: var(--color-accent); box-shadow: inset 0 0 0 1px rgba(53, 201, 177, 0.4); }
.util-left { font: 500 12px var(--mono); font-variant-numeric: tabular-nums; }
.util-left .sep { font-size: 9.5px; letter-spacing: 0.09em; color: var(--dim); margin: 0 3px; text-transform: uppercase; }
.util-left .ct { color: var(--ct); }
.util-left .t { color: var(--t); }

/* Hover context, so the timeline explains itself without a click. */
#tip {
  position: fixed; z-index: var(--z-pop);
  max-width: 300px; padding: 6px 9px;
  background: var(--panel-3); border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--line-2), 0 10px 26px rgba(0, 0, 0, 0.55);
  font: 400 12px/1.45 var(--font-body); color: var(--color-text);
  white-space: pre-line;
  opacity: 0; pointer-events: none; transition: opacity 0.1s ease;
}
#tip.show { opacity: 1; }

/* ------------------------------------------------------------------ flyouts */

.pop {
  position: fixed; z-index: var(--z-pop);
  left: 0; bottom: 66px; width: 246px;   /* placed by anchorPop() on open */
  background: var(--panel-3);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--line-2), 0 14px 34px rgba(0, 0, 0, 0.6);
  padding: 10px 11px;
  display: none;
}
.pop.open { display: block; }
.pop.wide { width: 268px; max-height: min(74vh, 620px); overflow-y: auto; scrollbar-width: thin; }
.pop h4 {
  margin: 0 0 7px;
  font: 600 10.5px/1 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--dim);
}
.pop h4:not(:first-child) { margin-top: 2px; }

/* The sight-line sub-options only exist while sight lines are on. */
.pop .sub { display: none; padding: 2px 0 4px 22px; }
.pop .sub.open { display: block; }
.toggle.small { font-size: 11.5px; padding: 4px 6px; }
.fidelity {
  margin: 5px 0 0; font: 400 11px/1.45 var(--font-body); color: var(--dim);
}

/* Where an analytical answer came from. Two states and no others: engine, or an
   approximation that says so. */
.srcrow { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; padding: 2px 0; }
.srcrow .k { font: 500 11.5px var(--font-body); color: var(--color-text); }
.srcrow .v {
  font: 700 10px/1 var(--mono); letter-spacing: 0.06em; text-transform: uppercase;
  color: #ff8b80; padding: 3px 6px; border-radius: var(--radius-sm);
  background: rgba(255, 106, 92, 0.12);
}
.srcrow .v.approx { color: var(--color-text); background: rgba(233, 233, 237, 0.08); }

/* Segmented choice: three or four mutually exclusive options that are not a dropdown. */
.seg { display: inline-flex; gap: 1px; background: var(--panel); border-radius: var(--radius-sm); padding: 1px; }
.sbtn {
  border: 0; background: transparent; cursor: pointer;
  font: 500 10.5px/1 var(--mono); letter-spacing: 0.04em;
  color: var(--dim); padding: 4px 6px; border-radius: 3px;
  transition: color 0.12s ease, background 0.12s ease;
}
.sbtn:hover { color: var(--color-text); }
.sbtn.on { color: var(--color-accent); background: rgba(53, 201, 177, 0.13); }
.sbtn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

.btn-xs {
  font: 500 10.5px/1 var(--mono); letter-spacing: 0.05em;
  padding: 4px 6px; border-radius: var(--radius-sm);
}
.pop .sep { height: 1px; background: var(--line); margin: 9px -11px; }
.row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 3px 0; }
.row .k { font: 400 11px var(--mono); color: var(--dim); }
.row .v { font: 500 11.5px var(--mono); font-variant-numeric: tabular-nums; }
.row .v.bad { color: var(--loss); font-weight: 600; }

.toggle {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 6px; border: 0; border-radius: var(--radius-sm);
  background: transparent; cursor: pointer; text-align: left;
  font: 500 12px var(--font-body);
  transition: background 0.12s ease;
}
.toggle:hover { background: rgba(233, 233, 237, 0.06); }
.toggle .box {
  flex: none; width: 13px; height: 13px; border-radius: 3px;
  box-shadow: inset 0 0 0 1px var(--line-2);
  display: grid; place-items: center;
}
.toggle[aria-pressed="true"] .box { background: var(--color-accent); box-shadow: none; }
.toggle[aria-pressed="true"] .box::after {
  content: ""; width: 6px; height: 3px; border: 1.6px solid #0d0f14;
  border-top: 0; border-right: 0; transform: rotate(-45deg) translate(0, -1px);
}
.toggle .swatch { flex: none; width: 8px; height: 8px; border-radius: 2px; margin-left: auto; }
.toggle .hint { font: 400 10.5px var(--mono); color: var(--dim); margin-left: auto; }

.tiers { display: flex; gap: 4px; margin: 5px 0 3px; }
.tbtn { flex: 1; padding: 4px 0; font-size: 11px; color: var(--dim); }
.tbtn.on { color: var(--color-accent); border-color: var(--color-accent); background: rgba(53, 201, 177, 0.10); }
.tbtn.costly { border-color: rgba(224, 87, 79, 0.4); color: #c9877f; }
.tbtn.costly.on { color: var(--loss); border-color: var(--loss); background: rgba(224, 87, 79, 0.12); }
.gpu-line {
  font: 400 10.5px/1.4 var(--mono); color: var(--dim); word-break: break-word;
  max-height: 58px; overflow: hidden; margin: 3px 0 5px;
}
.gpu-line.bad { color: var(--loss); }
#resScale, #trailLen { -webkit-appearance: none; appearance: none; width: 96px; height: 12px; background: transparent; cursor: pointer; }
#resScale::-webkit-slider-runnable-track, #trailLen::-webkit-slider-runnable-track {
  height: 3px; border-radius: 2px; background: rgba(233, 233, 237, 0.16);
}
#resScale::-webkit-slider-thumb, #trailLen::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 10px; height: 10px; margin-top: -3.5px; border-radius: 2px;
  background: var(--color-text); border: 2px solid var(--panel-3);
}

/* --------------------------------------------------------- software warning */

#gpuwarn { display: none; }
body.is-software #gpuwarn {
  display: block;
  position: fixed; z-index: var(--z-warn); left: 50%; top: 108px; transform: translateX(-50%);
  width: min(680px, calc(100vw - 32px));
  background: var(--panel-3);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px rgba(224, 87, 79, 0.45), 0 14px 40px rgba(0, 0, 0, 0.6);
  padding: 11px 13px;
}
body.is-software #gpuwarn.hide { display: none; }
.warn-head {
  font: 600 11.5px/1 var(--mono); letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--loss); margin-bottom: 6px;
}
.warn-body { font: 400 12px/1.55 var(--font-body); color: rgba(233, 233, 237, 0.84); }
.warn-body b { color: var(--color-text); font-weight: 600; }
.warn-body code {
  font: 400 11.5px var(--mono); color: var(--color-accent);
  background: rgba(53, 201, 177, 0.10); padding: 0 4px; border-radius: 3px;
}
#warnRenderer { color: var(--color-text); font-weight: 600; }
.warn-acts { display: flex; gap: 8px; align-items: center; margin-top: 9px; }
#warnClose { border-color: rgba(224, 87, 79, 0.5); color: #eaa39c; }
#warnClose:hover { background: rgba(224, 87, 79, 0.14); }
/* PASS 8. The measured way out, offered only after this machine has actually been
   measured -- see the fps numbers the button's own note carries. */
#warn2d { display: block; margin-top: 7px; color: rgba(233, 233, 237, 0.84); }
#warn2dNum { color: var(--dim); }

/* -------------------------------------------------------------- hint / keys */

/* bottom is set by layoutBottomStack(); this is the resting value before it runs */
#hint {
  position: fixed; z-index: 6; left: 50%; bottom: 116px; transform: translateX(-50%);
  width: min(760px, calc(100vw - 24px));
  background: var(--panel);
  border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--line), 0 12px 30px rgba(0, 0, 0, 0.5);
  padding: 10px 13px;
  opacity: 1; transition: opacity 0.5s ease, transform 0.5s ease;
}
#hint.gone { opacity: 0; transform: translateX(-50%) translateY(6px); pointer-events: none; }
.hint-top { display: flex; align-items: baseline; gap: 9px; margin-bottom: 7px; }
.hint-top b { font: 500 13.5px var(--font-heading); }
.hint-top span { font: 400 12px var(--mono); color: var(--dim); }
.hint-top button { margin-left: auto; }
.keymap { display: flex; flex-wrap: wrap; gap: 6px 15px; }
.keymap span { font: 400 12px/1.55 var(--font-body); color: var(--dim); white-space: nowrap; }

/* PASS 6. Where the ON SCREEN mark comes from, in one sentence, set apart from the key list
   by a rule rather than by size: it is a provenance note, not a shortcut, and shrinking it
   into a legal footnote would be the softening this project does not do. */
.hint-src {
  margin: 9px 0 0; padding-top: 8px; border-top: 1px solid var(--line-2);
  font: 400 11.5px/1.6 var(--font-body); color: var(--dim); max-width: 68ch;
}

/* THE key chip, and the only one. Operator: "shortkut keys should be in caps" -- so the
 * capitalisation lives on the class rather than in the strings, and every key that is ever
 * rendered through a <kbd>, from the Keys panel to the contextual strip to anything a later
 * pass adds, comes out capitalised without anyone having to remember. The KEY BINDINGS are
 * untouched: this is text-transform, not a change to what the keyboard handler listens for. */
kbd {
  display: inline-block; min-width: 19px; text-align: center;
  padding: 1.5px 5px; margin-right: 5px;
  font: 700 11px/1.35 var(--mono); color: var(--color-text);
  text-transform: uppercase; letter-spacing: 0.03em;
  background: var(--panel-3); border-radius: 3px;
  box-shadow: inset 0 0 0 1px var(--line-2);
}

/* ------------------------------------------------------- the camera switcher
 *
 * Three states, side by side in the transport, current one lit in the accent. The Follow
 * segment carries the name of the man it is pointed at, because "Follow" on its own does
 * not answer the question a reviewer has while looking at it. Radius, type scale and the
 * lit-state treatment are the Ledger's segmented control (.seg / .sbtn) at button size;
 * nothing new is invented here.
 */
.camseg {
  display: inline-flex; flex: none; gap: 1px;
  background: var(--panel-2); border-radius: var(--radius-sm); padding: 1px;
  box-shadow: inset 0 0 0 1px var(--line);
}
.cbtn {
  /* Centred, not top-aligned: only Follow carries a second line, and top-aligning the
     other two left their lit fill with an empty half under the label. */
  display: flex; flex-direction: column; align-items: flex-start; justify-content: center;
  gap: 1px;
  border: 0; background: transparent; cursor: pointer;
  /* 8px. Pass 8 tightened this from 9 to fit a fourth button; pass 9 folded that button
     into Overhead as a style, and the tighter padding stays because the transport now
     carries the style well beside the segment instead. */
  padding: 5px 8px; border-radius: 3px;
  color: var(--dim); text-align: left;
  transition: color 0.12s ease, background 0.12s ease, transform 0.08s ease;
}
.cbtn:hover { color: var(--color-text); background: rgba(233, 233, 237, 0.05); }
.cbtn:active { transform: translateY(1px); }
.cbtn.on { color: var(--color-accent); background: rgba(53, 201, 177, 0.14); }
.cbtn:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }
.cbtn-k { font: 600 11.5px/1.15 var(--mono); letter-spacing: 0.05em; text-transform: uppercase; }
/* --dim is 5.0:1 on the panel; --dim-2 is 2.7:1 and may only carry decoration. This is a
   player's name, which a reviewer reads, so it takes --dim. */
.cbtn-who {
  font: 500 10.5px/1.15 var(--mono); color: var(--dim);
  /* Wide enough for "click a player" unelided: the empty state is an instruction, and an
     instruction with its last word cut off is worse than no instruction. */
  max-width: 108px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.cbtn.on .cbtn-who { color: var(--color-text); }
.cbtn-who.empty { font-style: italic; }
/* PASS 11: A / MID / B. Built out of the shared segment so it is visibly the same KIND of
   control as the overhead style switch, but seated against the camera switcher, because
   what it changes is where the camera is pointed. Deliberately quieter than the switcher:
   a preset is a shortcut, not a state, so it never latches on. */
.siteseg {
  flex: none; align-items: center;
  background: var(--panel-2); padding: 2px;
  box-shadow: inset 0 0 0 1px var(--line);
}
.siteseg .sbtn {
  min-width: 30px; text-align: center;
  padding: 5px 7px; font-weight: 600; letter-spacing: 0.06em;
}
/* The one that was last flown to, so the reviewer can see which frame he is looking at.
   It fades out again the moment he orbits or picks another camera. */
.siteseg .sbtn.at { color: var(--color-accent); background: rgba(53, 201, 177, 0.13); }

#btnPov { flex: none; min-width: 44px; }
/* An explicit `display` beats the UA stylesheet's [hidden] rule, so both of these have to
   say so themselves. POV outside Follow and an ON SCREEN label over an overhead camera are
   both claims about a state the viewer is not in. */
#btnPov[hidden], .onscrchip[hidden] { display: none; }

/* PASS 6: the ON SCREEN chip. It is a claim about where the answer came from, so it lives
   next to the camera that uses it and never says more than the test does. The old
   "spotted / engine" wording named a signal this highlight no longer reads. */
.onscrchip {
  display: inline-flex; align-items: baseline; gap: 5px; flex: none;
  padding: 4px 7px; border-radius: var(--radius-sm);
  background: rgba(255, 106, 92, 0.12);
  box-shadow: inset 0 0 0 1px rgba(255, 106, 92, 0.3);
  /* Small caps over a tinted ground: it takes a brighter step than --dim to clear 4.5:1. */
  font: 500 10.5px/1 var(--mono); letter-spacing: 0.05em; text-transform: uppercase;
  color: rgba(233, 233, 237, 0.82); white-space: nowrap;
}
.onscrchip b { font-weight: 700; color: #ff8b80; }
/* A separator, because without one the chip reads as the single phrase "on screen geometry"
   -- which sounds like a renderer setting rather than a signal and its source. */
.onscrchip b::before { content: '·'; margin-right: 5px; color: rgba(233, 233, 237, 0.45); }

/* --------------------------------------------------- contextual control hint
 *
 * The three or four gestures that matter in the CURRENT camera state, and nothing else.
 * It sits directly above the transport, it changes when the state does, and it goes away
 * when dismissed until the state changes again.
 */
#camhint {
  position: fixed; z-index: 6; left: 50%; bottom: 74px; transform: translateX(-50%) translateY(4px);
  display: flex; align-items: center; gap: 4px;
  max-width: calc(100vw - 24px);
  padding: 5px 5px 5px 12px; border-radius: 999px;
  background: var(--panel); box-shadow: inset 0 0 0 1px var(--line), 0 8px 22px rgba(0, 0, 0, 0.42);
  /* visibility, not opacity alone: an invisible strip whose close button is still in the
     tab order is a control a keyboard user can reach and cannot see. */
  opacity: 0; visibility: hidden; pointer-events: none;
  transition: opacity 0.24s cubic-bezier(0.16, 1, 0.3, 1),
              transform 0.24s cubic-bezier(0.16, 1, 0.3, 1),
              visibility 0s linear 0.24s;
}
#camhint.show {
  opacity: 1; visibility: visible; transform: translateX(-50%) translateY(0);
  pointer-events: auto; transition-delay: 0s;
}
.camhint-keys { display: flex; flex-wrap: wrap; align-items: center; gap: 3px 12px; }
.camhint-keys .ch { font: 400 12px/1.5 var(--font-body); color: var(--dim); white-space: nowrap; }
.camhint-keys .ch.note { color: var(--color-accent); font-family: var(--mono); font-size: 11px; }
.camhint-x {
  /* 24px is the smallest hit box this is allowed to be; the glyph stays 12px. */
  flex: none; display: grid; place-items: center; width: 24px; height: 24px;
  border: 0; border-radius: 50%; background: transparent; color: var(--dim); cursor: pointer;
  transition: color 0.12s ease, background 0.12s ease;
}
.camhint-x:hover { color: var(--color-text); background: rgba(233, 233, 237, 0.09); }
.camhint-x:active { background: rgba(233, 233, 237, 0.14); }
.camhint-x svg { width: 12px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.7; }
.camhint-x:focus-visible { outline: 2px solid var(--color-accent); outline-offset: 1px; }

/* Freecam look: the cursor says the drag is a look, and the drag never captures anything. */
#stage.looking { cursor: grabbing; }
/* PASS 11. The scene is clickable and has to say so: the same pointer a scoreboard row
   gives you, over a man in the world. `looking` wins -- while you are dragging the camera
   the cursor is a grab, whatever happens to be under it. */
#stage.pickable { cursor: pointer; }
#stage.pickable.looking { cursor: grabbing; }
.toggle.muted { opacity: 0.45; }

/* The bottom of the screen is a STACK: transport, then the pair readout, then the "Why?"
   panel, then transient toasts. Heights vary with content, so the offsets are measured in
   layoutBottomStack() rather than guessed here. These are the resting values. */
#toast {
  position: fixed; z-index: 7; left: 50%; bottom: 76px; transform: translateX(-50%);
  padding: 6px 12px; border-radius: var(--radius-sm);
  background: var(--panel-3); box-shadow: inset 0 0 0 1px var(--line-2);
  font: 500 12px var(--mono); color: var(--color-text);
  opacity: 0; transition: opacity 0.22s ease; pointer-events: none;
}
#toast.show { opacity: 1; }

/* ------------------------------------------------------------------- loader */

#loader {
  position: fixed; inset: 0; z-index: var(--z-load);
  background:
    radial-gradient(1100px 520px at 12% -12%, #1a1f2c 0%, rgba(13, 15, 20, 0) 62%),
    radial-gradient(900px 500px at 92% 4%, rgba(18, 36, 35, 0.5) 0%, rgba(13, 15, 20, 0) 58%),
    var(--color-bg);
  display: grid; place-items: center;
  transition: opacity 0.5s ease;
}
#loader.done { opacity: 0; pointer-events: none; }
.loader-inner { width: min(440px, 82vw); }
.loader-mark { display: flex; align-items: center; gap: 9px; margin-bottom: 16px; }
.loader-mark b { font: 600 15px var(--font-heading); letter-spacing: 0.02em; }
.loader-title { font: 400 13px/1.5 var(--font-body); color: var(--dim); margin-bottom: 16px; }
.loader-bar { height: 2px; background: rgba(233, 233, 237, 0.12); border-radius: 2px; overflow: hidden; }
.loader-fill { height: 100%; width: 0%; background: var(--color-accent); transition: width 0.25s ease; }
.loader-status { margin-top: 11px; font: 400 11.5px var(--mono); color: var(--dim); }

/* --------------------------------------------------------------- responsive */

@media (max-width: 1180px) {
  #boards { width: 226px; }
  #rightcol { width: 220px; }
  .why-cols { grid-template-columns: 1fr; gap: 9px; }
  #why { width: calc(100vw - 500px); min-width: 420px; }
}

/* Phones are out of scope for the prototype, but a split window or a small laptop must get
   a REDUCED viewer, never an overlapping one. Below 900px the scoreboards and feed drop out,
   the identity line collapses to the score, and the round chips scroll the way the Ledger's
   own round strip does (its chips live in an .xscroll rail with a min-width, not squashed). */
@media (max-width: 900px) {
  #boards, #rightcol, #hint, #cmpbox { display: none; }
  /* The "Why?" panel is the one analytical surface worth keeping narrow: it is the
     answer to a question the reviewer just asked, not ambient chrome. */
  #why { width: calc(100vw - 24px); min-width: 0; bottom: auto; top: 108px; }
  .why-cols { grid-template-columns: 1fr; gap: 8px; }
  .dmglist, #dmglog .dmglist { max-height: 104px; }
  .util-left { display: none; }

  #topbar { gap: 8px; padding: 6px 10px; }
  .brand-txt small { display: none; }
  .score { gap: 6px; }
  .score .club { display: none; }
  .score .sc { font-size: 18px; min-width: 22px; }
  .topright { gap: 7px; }
  .roundtag, #btnHelp { display: none; }
  .clock { font-size: 15px; }

  #roundstrip { width: calc(100vw - 24px); }
  .chips { overflow-x: auto; scrollbar-width: thin; padding-bottom: 3px; }
  .chip { flex: 0 0 auto; min-width: 22px; padding: 5px 4px; }

  /* PASS 16. Through Pass 15 this block hand-sorted ELEVEN items with `order` to fake two
     rows out of one wrapping line, and it half-worked: the site-camera segment was never
     given an order, so it sat among the transport buttons at narrow widths by accident.
     The bar is a column of two real rows now, so the ordering is structural and all of
     that goes away. What is left here is the narrow-width sizing, plus one deliberate
     fold inside the clock row. */
  #transport { bottom: 10px; }
  /* THE STRIP TAKES THE WHOLE ROW, and this is the one place `order` still earns its keep.
     Sharing the line with the buttons and the readout would leave the timeline ~415px at
     845; folding it onto its own line inside the row gives it 797 of the bar's 821. This
     is what Pass 8 did for the whole bar, one level down. */
  .tr-clock .time { order: 1; }
  .tr-clock .scrub { order: 2; flex-basis: 100%; margin-top: 2px; }
  /* AND `#btnFreeze { display: none }` IS GONE FROM HERE. Pass 8 hid Skip buy at this
     breakpoint to win a slot on a single eleven-item row that no longer exists. With the
     strip on its own line the clock row's first line is six items -- Play, speed, the two
     kill steps, Skip buy, the clock -- and measures ~370 of 821 at the narrowest width the
     app's embed ever shows the viewer. Hiding a control to save space you have is a
     control lost for nothing. (B always reached it even while it was invisible, which is
     the other half of why this was worth undoing rather than leaving alone.) */
  .cutwell { flex: 1 1 auto; }
  #cut { flex: 1; width: auto; }
  .stylewell { padding: 3px; }
  .stylewell .sbtn { padding: 6px 7px; }
  .cbtn { padding: 4px 7px; }
  .cbtn-who { max-width: 9ch; }
  .pop.wide { width: calc(100vw - 24px); max-width: 300px; }
}

@media (max-width: 560px) {
  .time { font-size: 12px; min-width: 0; }
  #btnPlay { min-width: 58px; }
  /* Two gaps to tighten now, not one: the column gap BETWEEN the rows and the gap between
     the controls inside each of them. */
  #transport { gap: 7px; padding: 8px 10px; }
  .tr-row { gap: 7px; row-gap: 7px; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: 0.001s !important; transition-duration: 0.001s !important; }
}
