/* ══════════════════════════════════════════════════════════════════════
   DeepReview — Unified Review screen styling.
   A faithful port of the design system's "Unified Review" mockup, layered
   on top of the shared styles.css. Loaded ONLY by review.html, so every
   rule here is review-scoped (including the :root token redefinitions).

   Strategy: the design's token palette is defined below AND the app's
   older token names are aliased onto it (--panel→--surface, --muted→
   --text-muted, --kill→--green, …). That makes the existing review CSS
   in styles.css adopt the indigo/slate design palette automatically; the
   component rules here then refine the structure to match the mockup.
   Behavioural customisations (timeline row heights, click-top-seeks /
   click-bottom-edits phase cards, HP-from-start, CS removed) are kept.
   ══════════════════════════════════════════════════════════════════════ */

@import url('https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;500;600;700;800&family=Spline+Sans+Mono:wght@400;500;600&display=swap');

:root {
  /* ── Design palette ─────────────────────────────────────────────── */
  --indigo-200: #c7c7f5; --indigo-300: #a5a5f0;
  --indigo-400: #8080ea; --indigo-500: #5e5ee2; --indigo-600: #4444d4;
  --indigo-700: #3636b0;
  --slate-950: #0c0d13; --slate-900: #14151e; --slate-850: #1b1d29;
  --slate-border: #292c3c; --slate-border-strong: #363a4e; --slate-hover: #252839;
  --slate-400: #8b8fa6; --slate-100: #e7e9f2;

  --green: #4dd17a; --gold: #f0c244; --red: #ff5d5d; --cyan: #00d4d4;
  --note-purple: #b173ff; --note-blue: #4ea1ff; --note-green: #4dd17a;
  /* Objectives read PINK everywhere (neutrals were purple, too close to the
     fights' purple). Fights keep --note-purple; only objectives use this. */
  --obj-pink: #ff5db1;
  --note-gold: #f0c244; --note-orange: #ff8a4c; --note-red: #ff5d5d;
  --note-pink: #ff7ec0; --note-teal: #00d4d4; --note-gray: #a8b3c4; --note-white: #e6e9ef;

  /* Surfaces / borders / text (design semantic aliases) */
  --bg: var(--slate-950);
  --surface: var(--slate-900);
  --surface-raised: var(--slate-850);
  --surface-hover: var(--slate-hover);
  --border: var(--slate-border);
  --border-strong: var(--slate-border-strong);
  --text: var(--slate-100);
  --text-muted: var(--slate-400);
  --text-faint: #5c6075;
  --brand: var(--indigo-600);
  --brand-hover: var(--indigo-500);
  --accent: var(--indigo-400);
  --accent-soft: rgba(68, 68, 212, 0.14);
  --accent-border: rgba(96, 96, 226, 0.45);
  --overlay: rgba(8, 9, 14, 0.62);

  /* Motion + elevation (design tokens; ledger.css carries the same values) */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --dur-fast: 0.1s;
  --transition-colors: background-color var(--dur-fast) var(--ease),
                       border-color var(--dur-fast) var(--ease),
                       color var(--dur-fast) var(--ease);
  --shadow-modal: 0 20px 60px rgba(0, 0, 0, 0.6);

  /* Fonts */
  --font-sans: "Hanken Grotesk", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --font-mono: "Spline Sans Mono", ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  /* Text surfaces that can hold inline annotation refs: the sans stack behind
     the editor ▣ face (styles.css), whose 0.9em gap plus the digits is what
     the mirror's painted `[n]` needs. */
  --font-edit: "DR Anno Ref Edit", var(--font-sans);

  /* ── Alias the app's older token names onto the design palette ───── */
  --panel: var(--surface);
  --panel-2: var(--surface-raised);
  --muted: var(--text-muted);
  --kill: var(--green);
  --assist: var(--gold);
  --death: var(--red);
  --note: var(--note-purple);
}

body { font-family: var(--font-sans); -webkit-font-smoothing: antialiased; }
.mono, .subtle .mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
button { font-family: inherit; }

/* Shared button restyle (scoped to review page) */
button, .btn {
  background: var(--surface-raised); border: 1px solid var(--border);
  border-radius: 6px; transition: background-color .1s, border-color .1s, color .1s;
}
button:hover, .btn:hover { background-color: var(--surface-hover); border-color: var(--border-strong); }
.primary, button.primary {
  background: var(--brand); border-color: var(--brand); color: #fff; font-weight: 600;
}
.primary:hover, button.primary:hover { background-color: var(--brand-hover); border-color: var(--brand-hover); }
.subtle { color: var(--text-muted); }

/* ════════════════════════════════════════════════════════════════════
   HEADER
   ════════════════════════════════════════════════════════════════════ */
/* The top bar IS the window titlebar now: a split deck — 24px system strip
   (titlebar.css owns it; it's the Tauri drag region) over the 36px game-context
   toolbar. Docked full-width, flush with the window top — bottom hairline
   only, with 10px of page background above the middle row (matching the
   middle↔timeline row-gap). */
.header {
  /* align-items/justify-content reset the base .header rule (styles.css) —
     its align-items:center would shrink the column's children to content
     width instead of stretching them across the deck. */
  display: flex; flex-direction: column; align-items: stretch; justify-content: flex-start;
  padding: 0; flex: none; background: var(--surface);
  border: none; border-bottom: 1px solid var(--border);
  margin: 0 0 10px;
}
.header .tb-toolbar { padding: 0 12px 0 10px; gap: 10px; }
.header .back {
  /* min-width keeps the hit target honest when the label is short (the pool
     viewer's "‹ vods") — the button must not shrink to the word. Centered so
     a short label sits mid-button; "‹ library" already fills past the min. */
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  height: 30px; padding: 0 13px; min-width: 94px;
  border: 1px solid var(--border); background: var(--surface-raised);
  color: var(--text-muted); font-family: var(--font-mono); font-size: 12px;
  font-weight: 600; white-space: nowrap; flex: none;
}
.header .back:hover { border-color: var(--accent-border); color: var(--text); text-decoration: none; }
/* Load-failure screen's escape hatch (the header back stays too — same look). */
.err-back {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  margin-top: 12px; height: 30px; padding: 0 13px; min-width: 94px;
  border: 1px solid var(--border); background: var(--surface-raised);
  color: var(--text-muted); font-family: var(--font-mono); font-size: 12px;
  font-weight: 600; white-space: nowrap; letter-spacing: .02em;
}
.err-back:hover { border-color: var(--accent-border); color: var(--text); text-decoration: none; }
/* Multi-POV switcher (pool VODs with more than one uploader in the game):
   one champion tile per POV with its F-key, allies underlined accent,
   enemies red, the POV being watched outlined. */
.header .pov-bar { display: flex; align-items: center; gap: 4px; flex: none; }
.header .pov-lab { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); letter-spacing: .02em; margin-right: 4px; white-space: nowrap; }
.header .pov-lab .caret { color: var(--text-faint); margin-left: 4px; }
.header .pov-btn {
  position: relative; width: 28px; height: 28px; padding: 0; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--surface-raised); color: var(--text-muted);
  cursor: pointer; transition: border-color .1s, box-shadow .1s;
}
.header .pov-btn img { width: 100%; height: 100%; object-fit: cover; display: block; }
.header .pov-btn .pov-ph { font-family: var(--font-mono); font-size: 12px; font-weight: 600; }
.header .pov-btn kbd {
  position: absolute; right: -1px; bottom: -1px; padding: 1px 2px;
  font: 600 8px/1 var(--font-mono); color: var(--text-muted);
  background: var(--bg); border: 1px solid var(--border);
}
.header .pov-btn.ally { box-shadow: inset 0 -2px 0 var(--accent); }
.header .pov-btn.enemy { box-shadow: inset 0 -2px 0 var(--red); }
.header .pov-btn:hover { border-color: var(--accent-border); }
.header .pov-btn.on { border-color: var(--accent); }
.header .pov-btn.on kbd { color: var(--accent); border-color: var(--accent-border); }
/* Clock-sync mark (top-left square): green = frame-synced; gold = only the
   Live Client's start anchor (±1–2 s) or a hand-set offset — the POV that
   will read seconds off the others in a fight review. Absent while unknown. */
.header .pov-btn .pov-sync { position: absolute; left: -1px; top: -1px; width: 7px; height: 7px; background: var(--gold); border: 1px solid var(--bg); pointer-events: none; }
.header .pov-btn[data-sync="exact"] .pov-sync,
.header .pov-btn[data-sync="manual"] .pov-sync { background: var(--green); }
/* Last frame of the outgoing POV, held over the stage until the incoming one
   has seeked — a switch never shows black. */
/* POV standbys: full-size invisible siblings of #video (also inside the
   theater host) — adoption is a class flip, never a resize or reload. */
video.pov-standby { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; opacity: 0 !important; pointer-events: none !important; z-index: 0; }
/* The incoming POV's warm standby, shown over the stage from the moment it has
   seeked until the main element has caught up — the switch never renders a
   decoder-teardown black frame, and never depends on canvas frame capture. */

/* Champion badge — square, hidden until the match is known. */
.header .rv-champ { width: 24px; height: 24px; object-fit: cover; background: var(--bg); border: 1px solid var(--border); flex: none; }
.header .rv-champ[hidden] { display: none; }
.header h1 {
  font-size: 14px; font-weight: 700; margin: 0; letter-spacing: -.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 40vw;
  min-width: 0; flex: none;
}
.header .crumbs { display: flex; align-items: center; gap: 7px; font-size: 13px; color: var(--text-muted); white-space: nowrap; overflow: hidden; }
.crumbs .res-win { color: var(--green); font-weight: 700; }
.crumbs .res-loss { color: var(--red); font-weight: 700; }
.crumbs .sep { color: var(--text-faint); }
.crumbs .kda { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.crumbs .kda .k { color: var(--text); } .crumbs .kda .d { color: var(--red); }
.crumbs .kda .a { color: var(--text-muted); } .crumbs .kda .slash { color: var(--text-faint); }
.crumbs .mono { color: var(--text); }
.header .hdr-spacer { flex: 1; }
/* Save read-out — mono 12px; the bracket chrome comes from the global .save
   instrument-tag rules further down. */
.header .save { font-size: 12px; font-weight: 600; color: var(--text-muted); display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.header .save .save-dot { width: 7px; height: 7px; border-radius: 50%; background: var(--green); }

/* ════════════════════════════════════════════════════════════════════
   LAYOUT — video + side, full-width timeline below
   ════════════════════════════════════════════════════════════════════ */
/* Column widths, side padding and the gaps around the video are computed by
   layoutVideoPane() (review.js) from the video's native aspect ratio — the
   values here are the no-JS defaults. Height subtracts the header deck's
   full vertical footprint: 74px docked deck (33px strip incl. its rule +
   40px toolbar + 1px bottom hairline) + 10px bottom margin. No top padding (the
   middle windows grow up to the header's 10px margin); 6px bottom padding =
   the timeline's gap to the screen bottom. Gaps read as the app background,
   never the black page backdrop. */
.review-layout {
  position: relative;   /* anchors the load overlay */
  flex: 1; min-height: 0; display: grid;
  grid-template-columns: 1fr 408px; grid-template-rows: 1fr auto;
  row-gap: 10px; column-gap: 0;
  padding: 0;
  height: calc(100dvh - 84px);
  background: var(--bg);
}

/* ── Public web viewer (deepreview.lol/vods/<id>) ──────────────────────
   Its layout + chrome overrides are NOT here. They are inlined into the
   page head by relay-server/lib/vods-web.js, because this file is a
   separately-cached asset: a visitor holding an older copy of it would fall
   back to the calc() height above and get a dead strip under the timeline,
   which is exactly what happened twice. Anything that must be in step with
   the served markup belongs with the markup. */

/* Full-content loading state while the review fetches (review.js removes it). */
.review-loading { position: absolute; inset: 0; z-index: 20; display: flex;
  align-items: center; justify-content: center; background: var(--bg); }
/* min-width 0 keeps the pane inside its grid track (the default min-width:auto
   would let the fixed-width video column blow the layout out to the right). */
.video-pane { grid-column: 1; grid-row: 1; display: flex; flex-direction: column; min-height: 0; min-width: 0; overflow: hidden; background: var(--bg); }
.notes-pane {
  grid-column: 2; grid-row: 1; border-left: 1px solid var(--border); min-height: 0;
  display: flex; flex-direction: column; background: var(--surface); position: relative;
}
/* Docked: the strip runs edge to edge and sits on the window's bottom edge,
   the way the header deck sits on its top. The side insets layoutVideoPane()
   computes belong to the middle row only (margins on the two panes). */
.timeline-pane { grid-column: 1 / 3; grid-row: 2; }

/* Middle row: empty window on the left + the video player pinned to the right.
   The vp-split gap (left window ↔ video) and the grid column-gap (video ↔
   notes) are kept equal by layoutVideoPane(); the left window absorbs the
   rest of the slack via flex:1. */
.video-pane .vp-split { flex: 1; min-height: 0; min-width: 0; display: flex; gap: 10px; }
.vp-window {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border);
}
.vp-window-body { flex: 1; min-height: 0; }
/* The player column: JS sets its width to the frame's exact width, so the
   control bar spans the frame and their corners line up. */
.vp-right { flex: none; display: flex; flex-direction: column; min-height: 0; max-width: 100%; }
.vp-right .video-stage { align-items: center; justify-content: center; }
/* One line always — a wrapping control bar would change the column height and
   make the frame fit oscillate. */
.vp-right .video-controls { flex-wrap: nowrap; }

/* ── Video stage ─────────────────────────────────────────────────── */
.video-stage { position: relative; background: #000; overflow: hidden; flex: 1; min-height: 0; display: flex; }
/* Letterbox bars (object-fit:contain leaves the unused area) are pitch black —
   still the video element itself, so clicks on the bars still hit the video. */
#video { width: 100%; height: 100%; object-fit: contain; background: #000; }

.video-controls {
  display: flex; align-items: center; gap: 6px; padding: 8px 12px;
  background: var(--surface); border-top: 1px solid var(--border); flex: none;
}
.vc-btn:not([hidden]) {
  min-width: 31px; height: 31px; padding: 0 9px; border: 1px solid var(--border);
  background: var(--surface-raised); color: var(--text-muted); border-radius: 6px;
  font-family: var(--font-mono); font-size: 12px; display: inline-flex;
  align-items: center; justify-content: center; gap: 5px;
}
.vc-btn:hover { border-color: var(--border-strong); color: var(--text); background: var(--surface-raised); }
.vc-btn.vc-play { background: var(--brand); border-color: var(--brand); color: #fff; width: 40px; }
/* Solid Glyph transport faces: the skip icons carry their number inside the ring. */
.vc-btn svg { display: block; }
.vc-ico { position: relative; display: inline-flex; width: 16px; height: 16px; align-items: center; justify-content: center; }
.vc-ico > b { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; padding-top: 1px; font: 700 6.5px var(--font-mono); letter-spacing: -.05em; }
.vc-btn#clipBtn { display: inline-flex; align-items: center; gap: 5px; }
.vc-btn.vc-play:hover { background: var(--brand-hover); }
/* #drawBtn deliberately has no special styling — it's always usable, so the
   old accent "glow" just made it look like a toggled state that wasn't. */
.vc-time { font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); margin-left: 4px; }
.vc-spacer { flex: 1; }
.vc-select {
  height: 31px; padding: 0 9px; border: 1px solid var(--border);
  background: var(--surface-raised); color: var(--text); border-radius: 6px;
  font-family: var(--font-mono); font-size: 12px;
}

/* ── Drawing toolbar (handoff 17 rework) ──────────────────────────────
   Lives in .video-controls and overlays the transport while drawing;
   relocated into the theater controls (.dt-theater) in fullscreen.
   Overrides the legacy top-center placement from styles.css. */
.video-controls { position: relative; }
.draw-bar {
  position: absolute; left: 0; right: 0; bottom: 0; top: auto; height: max-content;
  /* At least the transport's own height, and the same padding around the same
     31px buttons (.video-controls / .vc-btn): the bar then REPLACES the
     transport edge for edge instead of sitting 3px short of it with the
     transport peeking out above. Bottom-anchored so a wrapped second row still
     grows upward over the video. */
  min-height: 100%; box-sizing: border-box;
  transform: none; z-index: 8; border-radius: 0; box-shadow: none;
  display: flex; align-items: center; flex-wrap: wrap; gap: 6px; row-gap: 6px; padding: 8px 12px;
  background: var(--surface); border: none; border-top: 1px solid var(--border);
  backdrop-filter: none;
}
.draw-bar[hidden] { display: none; }

/* ── Clip toolbar ─────────────────────────────────────────────────────
   Same trick as .draw-bar: an opaque absolute sibling that paints over the
   transport. The covered controls are made inert while a bar is up
   (setTransportCovered in review.js), so they're not clickable or focusable
   underneath. */
.clip-bar {
  position: absolute; inset: 0; z-index: 8;
  display: flex; align-items: center; gap: 12px; padding: 12px 18px;
  background: var(--surface); box-sizing: border-box;
}
.clip-bar[hidden] { display: none; }
.clip-bar .cb-lab { flex: none; display: inline-flex; align-items: center; gap: 7px;
  font-family: var(--font-mono); font-size: 12px; font-weight: 600; color: var(--accent); }
.clip-bar .cb-keys { flex: 1; min-width: 0; overflow: hidden; display: flex; gap: 12px; align-items: center;
  font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); white-space: nowrap; }
.clip-bar .cb-keys > span { display: inline-flex; align-items: center; gap: 5px; }
.cb-k { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); background: var(--surface);
  border: 1px solid var(--border-strong); border-bottom-width: 2px; padding: 1px 5px; line-height: 1.4; }
.clip-bar .cb-btn { flex: none; height: 36px; padding: 0 16px; background: var(--surface-raised);
  border: 1px solid var(--border); color: var(--text); font-size: 14px; font-weight: 600; cursor: pointer;
  transition: var(--transition-colors); }
.clip-bar .cb-btn:hover { background: var(--surface-hover); }
.clip-bar .cb-btn.primary { padding: 0 18px; background: var(--brand); border-color: var(--brand); color: #fff;
  display: inline-flex; align-items: center; gap: 7px; }
.clip-bar .cb-btn.primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.clip-bar .cb-btn.primary .cb-k { color: rgba(255, 255, 255, .85); background: rgba(0, 0, 0, .22); border-color: rgba(255, 255, 255, .3); }

/* The ✂ Clip transport button, lit while clip mode holds the bar. */
#clipBtn.tool-active { background: var(--brand); border-color: var(--brand); color: #fff; }

/* Header chip — stands in for the Saved pill while clipping. */
.clip-chip { display: inline-flex; align-items: center; gap: 7px; padding: 4px 11px;
  font-family: var(--font-mono); font-size: 12px; font-weight: 600; letter-spacing: .02em;
  background: var(--accent-soft); border: 1px solid var(--accent-border); color: var(--accent); }
.clip-chip[hidden] { display: none; }
.draw-bar .dgrp { display: flex; align-items: center; gap: 4px; flex-wrap: nowrap; }
.draw-bar .dsep { width: 1px; height: 22px; background: var(--border); margin: 0 1px; flex: none; }
/* Consistent tool buttons: line-art icon + keybind badge. */
.dtool {
  /* 31px like .vc-btn, so a tool sits exactly where a transport button did. */
  height: 31px; min-width: 31px; padding: 0 8px; border: 1px solid var(--border);
  border-radius: 6px; background: var(--surface-raised); color: var(--text-muted);
  font-size: 12px; font-weight: 600; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px; white-space: nowrap;
}
.dtool:hover { color: var(--text); border-color: var(--text-muted); }
.dtool.on { color: var(--accent); border-color: var(--accent); background: rgba(78,161,255,.12); }
.dtool.dsave { background: var(--accent); border-color: var(--accent); color: #0e1015; padding: 0 10px; flex: none; }
.di { width: 15px; height: 15px; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; flex: none; }
.dk {
  font-family: var(--font-mono); font-size: 9px; font-weight: 700; line-height: 1; padding: 2px 4px;
  border: 1px solid currentColor; border-radius: 3px !important; opacity: .55;
}
.dtool.on .dk { opacity: .85; }
.dtool.dsave .dk { opacity: .7; border-color: rgba(14,16,21,.55); }
.draw-bar #drawCancel { margin-left: auto; }
.dsw { width: 19px; height: 19px; border-radius: 5px; border: 1px solid rgba(255,255,255,.18); cursor: pointer; }
.dsw.on { outline: 2px solid #fff; outline-offset: 1px; }
/* Current-color / current-size buttons with popups above the bar. */
.dcolor, .dsize { position: relative; }
.dcolor-current, .dsize-current {
  height: 31px; width: 31px; padding: 0; border: 1px solid var(--border); border-radius: 6px;
  background: var(--surface-raised); cursor: pointer; display: inline-flex; align-items: center; justify-content: center;
}
.dcolor-current:hover, .dsize-current:hover { border-color: var(--text-muted); }
.dcolor-dot { width: 15px; height: 15px; border-radius: 50%; border: 1px solid rgba(255,255,255,.28); }
.dcolor-pop {
  position: absolute; left: 0; bottom: calc(100% + 8px);
  display: grid; grid-template-columns: repeat(5, 19px); gap: 6px; padding: 8px;
  background: rgba(20,21,30,.98); border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 -12px 40px -16px rgba(0,0,0,.85); z-index: 9;
}
.dcolor-pop[hidden] { display: none; }
.dsize-dot { border-radius: 50% !important; background: var(--text-muted); display: block; }
.dsize-pop {
  position: absolute; left: 0; bottom: calc(100% + 8px);
  display: flex; align-items: center; gap: 4px; padding: 6px 8px;
  background: rgba(20,21,30,.98); border: 1px solid var(--border); border-radius: 9px;
  box-shadow: 0 -12px 40px -16px rgba(0,0,0,.85); z-index: 9;
}
.dsize-pop[hidden] { display: none; }
/* padding:0 is load-bearing: the global `button` rule (styles.css) supplies
   `padding: 8px 14px`, and under border-box that ate this button's ENTIRE 30px
   of width (30 - 2 border - 28 padding = 0), so every size dot rendered 0px
   wide — five empty boxes. Any fixed-size icon button here needs the reset. */
.dsize-opt {
  width: 30px; height: 30px; padding: 0; border: 1px solid transparent; border-radius: 6px; background: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.dsize-opt:hover { background: rgba(255,255,255,.08); }
.dsize-opt.on { border-color: var(--accent); background: rgba(78,161,255,.12); }
.dsize-opt span { border-radius: 50% !important; background: var(--text); display: block; }
.draw-readout {
  font-family: var(--font-mono); font-size: 11px; color: var(--cyan);
  background: rgba(12,13,19,.8); border: 1px solid rgba(0,212,212,.5);
  border-radius: 5px; padding: 2px 6px;
}
/* Compact strip while the toolbar is hosted in the theater controls, where it
   simply takes the transport row's place. Kept at the SAME height as that row
   (no vertical padding, no border, no panel of its own) and the scrubber stays
   visible above it, so entering and leaving draw mode never changes the
   control bar's height — the frame underneath never moves. */
/* position: relative, NOT static — layout-identical (no offsets), but it makes
   the bar the offset parent its popups assume. Static handed them to
   .th-controls, so they opened at the far LEFT of the whole bar instead of
   under their button. */
.draw-bar.dt-theater {
  position: relative; left: auto; right: auto; bottom: auto; height: 32px;
  width: fit-content; margin: 0 auto; padding: 0;
  border: none; background: none; backdrop-filter: none;
  justify-content: center; align-items: center; flex-wrap: nowrap; gap: 6px;
}
/* …and the control bar has to out-rank the draw canvas while it hosts the
   toolbar. The popups open UPWARD, over the video, where .draw-layer (z 5)
   sits; .th-controls is z 3, so the popup was painted UNDER a transparent
   canvas — you could see it and every click on it drew a stroke instead. */
.theater.drawing .th-controls { z-index: 6; }
.draw-bar.dt-theater .dtool { background: rgba(255,255,255,.06); border-color: rgba(255,255,255,.16); color: rgba(255,255,255,.82); }
.draw-bar.dt-theater .dtool:hover { background: rgba(255,255,255,.14); }
.draw-bar.dt-theater .dtool.on { color: var(--accent); border-color: var(--accent); background: rgba(78,161,255,.16); }
.draw-bar.dt-theater .dtool.dsave { background: var(--accent); border-color: var(--accent); color: #06131f; }
.draw-bar.dt-theater .dsep { background: rgba(255,255,255,.18); }
.draw-bar.dt-theater #drawCancel { margin-left: 0; }

/* ════════════════════════════════════════════════════════════════════
   TIMELINE — keeps the app's row heights (30 / 72 / 32) + behaviour
   ════════════════════════════════════════════════════════════════════ */
/* user-select: none — a drag on the strip scrubs the playhead; without it the
   drag selected the header/label text around the timeline instead. */
.timeline-pane { padding: 0 16px 7px; background: var(--surface); border: 0; border-top: 1px solid var(--border); flex: none; user-select: none; }
/* ── Timeline header (game clock · gold + drakes · pit timers · scope · visibility) ── */
.timeline-header {
  position: relative; display: flex; align-items: center; gap: 12px; height: 44px;
  margin: 0 -16px 9px; padding: 0 10px 0 12px; border-bottom: 1px solid var(--border);
}
.tl-vr { flex: none; width: 1px; height: 26px; background: var(--border); }
.tl-clockwrap { flex: none; position: relative; display: flex; align-items: center; gap: 8px; }
.tl-clock {
  display: inline-flex; align-items: center; justify-content: center; width: 64px; height: 26px; padding: 0;
  background: var(--surface-raised); border: 1px solid var(--border); color: var(--text-muted);
  font-family: var(--font-mono); font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; cursor: pointer;
}
.tl-clock:hover { background: var(--surface-raised); border-color: var(--accent-border); }
.tl-clock.on { border-color: var(--accent-border); }
/* Read-only reviews (pool VODs, the coach POV): the clock still reads, never opens. */
.tl-clock.ro { cursor: default; }
.tl-clock.ro:hover { border-color: var(--border); }
/* Opens UPWARD — the header sits at the bottom of the screen. */
.tl-offpop {
  position: absolute; bottom: calc(100% + 8px); left: 0; z-index: 60; width: 250px; padding: 11px;
  background: var(--surface-raised); border: 1px solid var(--border-strong); box-shadow: 0 10px 28px rgba(0,0,0,.6);
  display: flex; flex-direction: column; gap: 9px;
}
.tl-offpop[hidden] { display: none; }
.tl-offpop-head { display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 9px; letter-spacing: .06em; color: var(--text-muted); }
.tl-offpop-head i { flex: 1; height: 1px; background: var(--border); }
.tl-offpop-len { color: var(--text-faint); font-variant-numeric: tabular-nums; }
.tl-offpop-len span { color: var(--text-muted); }
.tl-offpop p { margin: 0; font-family: var(--font-sans); font-size: 11px; line-height: 1.4; color: var(--text-muted); }
.tl-offpop-row { display: flex; gap: 6px; }
.tl-offpop-row button {
  height: 28px; padding: 0; background: var(--surface); border: 1px solid var(--border); color: var(--text);
  font-family: var(--font-mono); font-size: 13px; cursor: pointer;
}
.tl-offpop-row button:hover { background: var(--surface); border-color: var(--accent-border); }
.tl-offpop-step { width: 30px; }
.tl-offpop-val {
  flex: 1; display: flex; align-items: center; justify-content: center; height: 28px;
  background: var(--surface); border: 1px solid var(--border); font-family: var(--font-mono); font-size: 12px; color: var(--text); font-variant-numeric: tabular-nums;
}
.tl-offpop-row .tl-offpop-ghost { flex: 1; height: 26px; padding: 0 8px; font-size: 10px; white-space: nowrap; color: var(--text-muted); }
.tl-offpop-row .tl-offpop-ghost:hover { color: var(--text); }
.tl-offpop-row .tl-offpop-done { height: 26px; padding: 0 12px; background: var(--brand); border-color: var(--brand); color: #fff; font-size: 10px; }
.tl-offpop-row .tl-offpop-done:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.tl-mid { flex: 1; min-width: 0; display: flex; align-items: center; justify-content: flex-start; gap: 10px; padding-left: 10px; }
.tl-gold { flex: none; width: 46px; font-family: var(--font-mono); font-size: 12px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.tl-gold.b { text-align: right; }
/* Under ~860px of header the gold figures give way to the drakes (design breakpoint). */
.timeline-header.narrow .tl-gold { display: none; }
.tl-dots { flex: none; display: flex; align-items: center; gap: 5px; }
.tl-dots i { width: 10px; height: 10px; box-sizing: border-box; border-radius: 50% !important; background: transparent; border: 1px solid rgba(231,233,242,.24); }
.tl-vs { flex: none; font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); }
.tl-objs { flex: none; display: flex; align-items: center; gap: 14px; white-space: nowrap; }
.tl-obj { display: flex; align-items: baseline; gap: 7px; }
.tl-obj-dot { flex: none; width: 6px; height: 6px; border-radius: 50% !important; background: rgba(231,233,242,.2); }
/* Fixed boxes: the label (drake/elder, grubs/herald/baron) and the value (m:ss, up)
   change width as they tick, and the whole row used to shuffle with them. */
.tl-obj-lbl { width: 6ch; flex: none; font-family: var(--font-mono); font-size: 9px; letter-spacing: .06em; color: var(--text-muted); }
.tl-obj-val { width: 6ch; flex: none; font-family: var(--font-mono); font-size: 13px; font-weight: 600; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.tl-obj-val.na { color: var(--text-muted); }
.tl-right { flex: none; display: flex; align-items: center; gap: 8px; }
.view-toggle {
  display: inline-flex; background: var(--surface-raised); border: 1px solid var(--border);
  border-radius: 6px; padding: 2px; gap: 2px;
}
.view-btn {
  background: transparent; border: none; border-radius: 4px; color: var(--text-muted);
  font-size: 11px; font-weight: 600; padding: 4px 11px;
}
.view-btn.active { background: var(--brand); color: #fff; }
.view-btn:disabled { opacity: .4; cursor: default; }

/* overflow: visible so the death rails can hang off the bottom edge (they latch
   ONTO that line — see .gt-dead). Nothing else needs the clip: visibleRange()
   already clamps bars to 0..100%, and the squared/telemetry reset zeroes the
   radius, so there are no rounded corners left to clip against. */
.timeline-track { position: relative; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); overflow: visible; }
/* ── Zoom bar under the strip ─────────────────────────────────────────
   Inset to the lane tracks (74px label + the strip's 1px border; the border
   alone on the right) so the thumb's edges sit under the instants they stand for. */
.tl-zoom { margin-top: 3px; padding: 0 1px 0 75px; }
.tlz-track { position: relative; height: 10px; background: var(--bg); border: 1px solid var(--border); touch-action: none; }
.tlz-thumb { position: absolute; top: 0; bottom: 0; min-width: 16px; background: rgba(255,255,255,.09); cursor: grab; }
.tlz-thumb:hover, .tl-zoom.drag .tlz-thumb { background: rgba(255,255,255,.14); }
.tl-zoom.drag .tlz-thumb { cursor: grabbing; }
.tlz-h { position: absolute; top: 0; bottom: 0; width: 7px; background: rgba(255,255,255,.22); cursor: ew-resize; }
.tlz-h.l { left: 0; } .tlz-h.r { right: 0; }
.tlz-h:hover { background: var(--accent); }
.tlz-now { position: absolute; top: -1px; bottom: -1px; width: 1px; background: var(--accent); pointer-events: none; }
.timeline-track.panning, .timeline-track.panning * { cursor: grabbing !important; }

.gt-row { position: relative; border-bottom: 1px solid var(--border); }
.gt-row:last-child { border-bottom: none; }
.gt-row .rl {
  position: absolute; left: 0; top: 0; bottom: 0; width: 74px; border-right: 1px solid var(--border);
  display: flex; align-items: center; padding-left: 11px; font-family: var(--font-mono);
  font-size: 9px; letter-spacing: .09em; color: var(--text-faint);
  background: var(--surface); z-index: 3;
}
.gt-track { position: absolute; left: 74px; right: 0; top: 0; bottom: 0; }
/* Every row clips sideways only: phases, notes, markers and fights keep their
   true position at a zoomed edge and are cut there, so they read as running
   on past it. Captions and hover furniture still overflow up and down. */
.gt-track { overflow-x: clip; }
/* row-time bands, top to bottom (44px total — the v2 design height; every
   marker is sized to fit THAT, the row is never grown to fit the markers):
     -4..4   highlight rails, lane 0 straddling the notes/time divider
      0..8   the OBJ caption strip
     10..34  the fight shape — ONE svg path, upper 10..25 for the K/D/A,
              lower 25..34 carrying the XvX count (the stem of a T)
     11..33  the icon row, CENTRED on y=22 whatever the square size (22px, or
              16.5px for a champion portrait). 22 is the centre of the ICON
              BAND ALONE — the arrow underneath is NOT part of the marker and
              must not pull it up; icons centre first, ticks hang after.
     34..39  anchor ticks, at the instant each event actually happened
     40..44  minute ticks
     ~40..48 death rails AND ward diamonds, both riding the bottom line
   Wards used to hold a lane of their own at y=35 and now share the bottom line
   with the death rails, which is what freed the height for the shape. */
/* Time row is 60 (was 48, was 44): +4 was headroom at the TOP so the OBJ
   caption clears the highlight rails that straddle the notes/time divider;
   the +12 is air at the BOTTOM — every top-anchored band (caption, icons,
   anchor ticks; ICON_MID/TICK_TOP in review.js, .gt-cap / .gt-tri tops here)
   keeps its line, while the bottom-anchored furniture (minute ticks, ward
   diamonds, death rails) rides the lower bottom line, so the marker band
   gets clear separation instead of crowding the bottom furniture. */
/* The notes row: bars carry no labels (hover names the note), and a coached
   review swaps pools via the lane-label toggle instead of splitting — the
   strip is the same height on every review. */
.row-phase { height: 30px; } .row-note { height: 50px; } .row-time { height: 60px; }
/* Top-row picker: the label is the anchor (phases / time / teammate's phases). */
.gt-row .rl.rl-menu { cursor: pointer; }
.gt-row .rl.rl-menu:hover, .gt-row .rl.rl-menu.open { color: var(--text-muted); }
/* Time mode: a ruler in the top row — minute ticks along the bottom, m:ss
   labels on the labelled minutes (step chosen per width, renderTopRuler). */
.gt-track.gt-ruler { overflow: hidden; cursor: pointer; background: var(--surface); }   /* #14151e, like the label column (user rule) */
.gt-rul-tick { position: absolute; bottom: 0; width: 1px; height: 6px; background: rgba(90,96,128,.9); transform: translateX(-50%); pointer-events: none; }
.gt-rul-tick.major { height: 10px; background: var(--text-muted); }
.gt-rul-lab { position: absolute; top: 3px; transform: translateX(-50%); font-family: var(--font-mono); font-size: 9px; letter-spacing: .04em; color: var(--text-muted); white-space: nowrap; pointer-events: none; }
.gt-rul-lab.edge-l { transform: none; margin-left: 2px; }
.gt-rul-lab.edge-r { transform: translateX(-100%); margin-left: -2px; }
/* The picker's active option: its icon sits in a blue box, the title takes
   the accent (no check column, no underline). Teammate rows: masked role
   glyph, champion square, role name. */
.cm-item .cm-col.tl-on .a, .cm-item .cm-lbl.tl-on { color: var(--accent); }
.cm-item > .cm-ico.on { border-radius: 2px; box-shadow: 0 0 0 3px var(--surface-raised), 0 0 0 4px var(--accent); }   /* a 1px accent box, 3px clear of the icon */
.cm-item > .cm-ico.tl-rolebox > .tl-role { display: block; width: 100%; height: 100%; background: #8b8fa6; -webkit-mask-size: contain; mask-size: contain; -webkit-mask-repeat: no-repeat; mask-repeat: no-repeat; -webkit-mask-position: center; mask-position: center; }
.cm-item > .cm-ico.tl-rolebox.on > .tl-role { background: var(--accent); }
.cm-item > .cm-ico.tl-champ { background-size: cover; }
/* Teammate's phases still loading. */
.gt-tophint { position: absolute; left: 6px; top: 0; bottom: 0; display: flex; align-items: center; font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); pointer-events: none; }

.gt-phase {
  position: absolute; top: 0; bottom: 0; border-radius: 3px; font-size: 10px;
  display: flex; align-items: center; justify-content: center; color: var(--text-muted);
  overflow: hidden; white-space: nowrap; cursor: pointer;
}
.gt-phase.wave-0 { background: rgba(78,161,255,.10); border: 1px solid rgba(78,161,255,.20); }
.gt-phase.wave-1 { background: rgba(78,161,255,.19); border: 1px solid rgba(78,161,255,.30); }
.gt-phase.obj { background: rgba(77,209,122,.10); border: 1px solid rgba(77,209,122,.28); color: var(--green); border-radius: 4px; }
.gt-phase.recall { box-shadow: inset 0 -3px 0 var(--accent); }
.gt-phase.sel { outline: 1.5px solid var(--accent); outline-offset: -1px; background: var(--accent-soft); color: var(--text); z-index: 2; }
.gt-phase.has-note::after { content: ""; position: absolute; top: 2px; right: 2px; width: 4px; height: 4px; border-radius: 50%; background: var(--accent); }
.gt-phase:hover { filter: brightness(1.35); }
/* Your-notes lane: the tail rail (design 2026-09-26). One SVG per lane, one
   path per note in the note's colour: the latest-started active note owns the
   full bar, every other running note drops to an 8px rail 2px below it and
   the bar shrinks only where rails are present (layoutNoteLane). No hover
   fade of the other notes, no caps, no icon boxes (user 2026-09-27: the icon
   shows in the hover tooltip instead). z-index 1 keeps the faint recall
   lines behind the bars. */
.gt-notes { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; z-index: 1; }
.gt-bar { fill-opacity: .28; stroke-width: 1; cursor: pointer; pointer-events: visiblePainted; }
.gt-bar:hover { fill-opacity: .4; }
.gt-bar.active { stroke: #fff; stroke-width: 1.5; }
.tl-tooltip .tt-ico { vertical-align: -2px; margin-right: 5px; }
.gt-notes-plus { font: 600 9px var(--font-mono, ui-monospace, monospace); fill: var(--text-muted); text-anchor: middle; dominant-baseline: central;
  paint-order: stroke; stroke: var(--bg); stroke-width: 3px; stroke-linejoin: round; pointer-events: none; }
/* Minute ticks: hairline per minute, taller + brighter every 5. Unlabelled by
   design — the markers carry the meaning and get the space the labels used. */
.gt-rtick { position: absolute; bottom: 0; width: 1px; height: 2px; background: rgba(54,58,78,.9); transform: translateX(-50%); }
.gt-rtick.major { height: 4px; background: rgba(90,96,128,.9); }
/* styles.css draws the old labelled tick's hairline as a ::before — the element
   IS the hairline now, so the pseudo would float a second one above it. */
.gt-rtick::before { content: none; }
/* Time grid (zoomed views): dashed 1px hairlines under the notes and events.
   Never solid, never 2px: that is the recall line's look (.gt-recall-line). */
.gt-grid { position: absolute; top: 0; bottom: 0; width: 1px; transform: translateX(-50%); pointer-events: none; z-index: 0;
  background: repeating-linear-gradient(to bottom, rgba(124,134,176,.34) 0 2px, transparent 2px 6px); }
/* Highlight rails: one lane per overlapping run, line + a dot at each end.
   The line runs the FULL width and the dots are centred ON the endpoints
   (−2.5px = half the dot past the edge), not tucked inside them. Both matter
   for contiguous chains (level, tempo): the old 2px line inset left a visible
   notch at every shared point, and on a segment a few px wide it consumed the
   whole line, so short runs rendered as orphan dots with nothing connecting
   them. A dot centred on the boundary is also simply the true instant. */
/* No min-width: the JS passes the segment's TRUE width, and the endpoint
   dots/diamonds carry visibility + hover for sliver-thin segments — a floor
   here pushed a short rail's rendered end past the mark that closes it. */
.gt-thread { position: absolute; height: 8px; cursor: pointer; z-index: 4; }
.gt-thread .ln { position: absolute; left: 0; right: 0; top: 50%; margin-top: -1px; height: 2px; background: var(--hl); }
.gt-thread.sev2 .ln { margin-top: -1.5px; height: 3px; }
.gt-thread .d { position: absolute; top: 50%; width: 5px; height: 5px; margin-top: -2.5px; background: var(--hl); border-radius: 50% !important; }
.gt-thread.sev2 .d { width: 6px; height: 6px; margin-top: -3px; }
.gt-thread .d.l { left: -2.5px; } .gt-thread .d.r { right: -2.5px; }
.gt-thread.sev2 .d.l { left: -3px; } .gt-thread.sev2 .d.r { right: -3px; }
.gt-thread:hover { filter: brightness(1.35); }
/* Relative tempo's recall marks: solid diamonds centred on the same divider
   line the lane-0 rails ride (rails sit at top −4px with height 8, so their
   midline is the track's top edge) — teal for the player, orange for the ally,
   the same pairing the rails use for who-shopped-last. They are the rails'
   endpoints (tempo rails render dotless). Solid + above the rails (z-5): a
   mark sits exactly where a desync rail starts, and it has to read on top of
   the line, not blend into it. Ward diamonds live on the row's BOTTOM line and
   are outlined, so the two stay distinguishable at a glance. */
.gt-tempo-mark { position: absolute; top: -3.5px; width: 7px; height: 7px; margin-left: -3.5px; transform: rotate(45deg); background: #3ad6c4; cursor: pointer; z-index: 5; }
.gt-tempo-mark.ally { background: #ff8a4c; }
.gt-tempo-mark:hover { filter: brightness(1.35); }
/* Ward placements: cyan diamond centred ON the row's bottom line, sharing that
   line with the death rails rather than holding a lane inside the row. */
.gt-ward { position: absolute; top: auto; bottom: -3px; width: 7px; height: 7px; margin-left: -3.5px; transform: rotate(45deg); border: 1px solid #00d4d4; background: rgba(0,212,212,.22); cursor: pointer; z-index: 3; }
.gt-ward.many { background: rgba(0,212,212,.55); }
.gt-ward:hover { background: rgba(0,212,212,.7); }
/* Death → respawn: two dots joined by a line (same language as the highlight
   rails), riding the bottom line of the Time row. The background/border resets
   are load-bearing — styles.css still carries the old full-height red wash and
   would otherwise paint a square behind the rail. */
.gt-dead {
  position: absolute; bottom: -4px; top: auto; height: 8px; min-width: 8px;
  background: none; border: none; cursor: pointer; z-index: 3;
}
.gt-dead .ln { position: absolute; left: 2px; right: 2px; top: 50%; margin-top: -1px; height: 2px; background: rgba(255,93,93,.85); }
.gt-dead .d { position: absolute; top: 50%; width: 5px; height: 5px; margin-top: -2.5px; background: var(--red); border-radius: 50% !important; }
.gt-dead .d.l { left: 0; } .gt-dead .d.r { right: 0; }
.gt-dead:hover { filter: brightness(1.3); }
/* Champion-portrait pins. The portrait is who it happened TO (your kills and
   assists) or who did it (your deaths); an execute/turret death has no champion
   and falls back to .noimg. Above the highlight rails on z so it stays legible
   when a rule is threading the same stretch. */
/* left / top / width / height come from renderTimeline: the marker layout works
   in px against the live track width, so the box is placed by its own edges
   rather than centred with a negative margin. */
.gt-pin {
  position: absolute; box-sizing: border-box;
  background-color: var(--bg); background-size: cover; background-position: center;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid; cursor: pointer; z-index: 5;
}
.gt-pin.kill { border-color: rgba(77,209,122,.9); box-shadow: 0 0 0 1px rgba(77,209,122,.3); }
.gt-pin.death { border-color: rgba(255,93,93,.9); box-shadow: 0 0 0 1px rgba(255,93,93,.3); filter: grayscale(.4); }
.gt-pin.assist { border-color: rgba(240,194,68,.9); }
/* A kill or an assist shows the portrait of whoever DIED, so it's DESATURATED —
   the champion reads as gone without a mark drawn over the art. (A struck-out
   diagonal was tried and rejected: on a 16px square it fought the portrait.)
   It must NOT be done with filter: grayscale — that greys the whole element,
   outline and count badge included, and the outline colour is what still says
   kill vs assist. Blending the art against a flat achromatic backdrop in
   luminosity mode desaturates the ART ONLY; the border keeps its colour. */
.gt-pin.kill, .gt-pin.assist { background-color: #7d7d7d; background-blend-mode: luminosity; }
/* Count badge shown when a cluster collapses several events into one marker. */
.gt-n { font: 700 10px/1 var(--font-mono); color: var(--text); text-shadow: 0 1px 2px #000; pointer-events: none; }
.gt-trade .gt-n { position: absolute; left: 50%; top: 50%; transform: translate(-50%,-50%); }
.gt-pin.noimg { background-color: rgba(255,255,255,.07); }
.gt-pin:hover { filter: brightness(1.3); }
/* Hover restores the colour on every desaturated pin — same specificity as the
   rules above, so these have to come after them to win. */
.gt-pin.death:hover { filter: grayscale(0) brightness(1.15); }
.gt-pin.kill:hover, .gt-pin.assist:hover {
  background-color: var(--bg); background-blend-mode: normal; filter: brightness(1.15);
}
/* Shared anchor tick, marking the instant an event actually happened. Grouping
   packs the squares together, so a tick is often OFFSET from the square it
   belongs to — that offset is what keeps the real timing readable. A marker
   mounted on a fight gets none: the band is the timing. */
.gt-tri {
  position: absolute; top: 50px; transform: translateX(-50%); width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent; pointer-events: none; z-index: 4;
}
.gt-tri.kill { border-top: 5px solid var(--green); }
.gt-tri.death { border-top: 5px solid var(--red); }
.gt-tri.assist { border-top: 5px solid var(--gold); opacity: .82; }
.gt-tri.trade { border-top: 5px solid var(--note-orange); opacity: .82; }
/* An objective's tick is ALWAYS bright purple — who took it is the square's job
   (ally/enemy tint), the tick's job is only to say "a neutral happened here". */
/* The objective ARROW is red (the icon carries ally/enemy; the arrow only says
   "a neutral happened here"). This reverts a spell where it was forced purple. */
.gt-tri.obj.ally, .gt-tri.obj.enemy { border-top: 5px solid var(--red); }
/* Hairline stem closing the gap between a SMALLER square and the tick line.
   Sizes differ per kind but the ticks never do — a kill portrait packed beside
   a full-size objective keeps its centre on the row and grows a stem down to
   the shared tick row instead of dropping to meet it. Height/top are inline. */
.gt-stem {
  position: absolute; width: 1px; transform: translateX(-50%);
  pointer-events: none; z-index: 4; opacity: .85;
}
.gt-stem.kill { background: var(--green); }
.gt-stem.death { background: var(--red); }
.gt-stem.assist { background: var(--gold); }
.gt-stem.trade { background: var(--note-orange); }
/* The objective stem is the shaft of that red arrow — same colour as its head. */
.gt-stem.obj { background: var(--red); }
/* When the tick IS the marker (champion icons off) the stem is the shaft of an
   arrow rather than a hairline joining two things, so it carries real weight —
   but a touch slimmer (2.25px, ~25% off the old 3px) so it reads as a stem, not
   a bar. */
.gt-stem.bare { width: 2.25px; opacity: 1; }
/* Invisible hover/click target over a bare tick, used when "show champion
   icons" is off — the arrow alone is far too small to aim at, but the event
   still has to be inspectable. top/height come from renderTimeline and match
   the arrow's actual extent: it must NEVER be sized to the whole icon band,
   because where the stem is suppressed that band belongs to another event and
   this pad (appended last, same z) would swallow its clicks. */
.gt-hit {
  position: absolute; width: 15px;
  transform: translateX(-50%); background: none; cursor: pointer; z-index: 5;
}
/* Caption strip above a marker: "OBJ" over a standalone objective, and over a
   fight when one fell inside its window. This `top` is tuned to sit flush above
   the OBJECTIVE ICON row (its top ~15). A fight box sits a little lower (top
   ~17.75), so the fight's caption overrides `top` inline in renderTimeline to
   stay flush on the box instead of floating above it. */
.gt-cap {
  position: absolute; top: 18px; transform: translateX(-50%); display: flex; gap: 3px; align-items: center;
  font: 700 6px/8px var(--font-mono); letter-spacing: .08em; white-space: nowrap;
  color: var(--text-muted); pointer-events: none; z-index: 4;
}
.gt-cap .obj { color: var(--obj-pink); font-style: normal; }
/* Objective(s) all lost → the OBJ tag greys out (pink stays if any was won). */
.gt-cap .obj.lost { color: var(--text-muted); }
/* Objective portrait — one per monster type. Rounded on purpose: it's the one
   marker that isn't a champion, and the corner radius separates a neutral from
   a portrait at a glance while still packing flush beside them in a group. The
   !important is load-bearing — the squared/telemetry reset zeroes every radius
   on the page (see the round-again allowlist at the bottom of this file). */
.gt-objg {
  position: absolute; box-sizing: border-box; border-radius: 6px !important;
  display: flex; align-items: center; justify-content: center; line-height: 1;
  font-family: var(--font-mono); font-weight: 700; border: 1px solid; cursor: pointer; z-index: 5;
  overflow: hidden;
}
.gt-objg.ally { color: var(--obj-pink); border-color: rgba(255,93,177,.65); background: rgba(255,93,177,.16); }
.gt-objg.enemy { color: #cf6b6b; border-color: rgba(207,107,107,.6); background: rgba(207,107,107,.14); }
/* An objective LOST is slightly desaturated — muted, but nowhere near a death's
   full greyscale, so which neutral it was still reads. */
.gt-objg.enemy { filter: grayscale(.5) brightness(.92); }
.gt-objg.enemy:hover { filter: grayscale(0) brightness(1.35); }
/* Real minimap art fills the box; the tinted background stays as the ally/enemy
   read behind it, and a grub count sits on top with a shadow to stay legible. */
.gt-objg.has-icon { background-size: cover; background-position: center; background-repeat: no-repeat; text-shadow: 0 1px 3px #000; }
.gt-objg:hover { filter: brightness(1.35); }
/* Trade: the headline is the health the trade COST — red, and with no minus
   sign, so the two digits and the % get the biggest type the square can hold.
   The strip under it is where that left you: solid = kept, translucent = what
   this trade took, bare = already missing.
   THE OUTLINE BELONGS TO THE BAR, NOT THE MARKER. A box drawn around the whole
   thing put two verticals up either side of the number and read as a container;
   the number is meant to float free above the bar it describes. */
.gt-trade {
  position: absolute; box-sizing: border-box;
  display: flex; flex-direction: column; justify-content: space-between;
  border: none; background: none; cursor: pointer; z-index: 5;
}
.gt-trade .pc {
  flex: 1 1 auto; display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-weight: 700; line-height: 1; letter-spacing: -.04em;
  color: var(--red); white-space: nowrap;
}
/* 7px = the 5px of fill the bar has always had, plus its own 1px outline. */
.gt-trade .bar {
  position: relative; flex: none; height: 7px;
  border: 1px solid rgba(255,138,76,.7); background: rgba(255,138,76,.10);
}
.gt-trade .kept, .gt-trade .lost { position: absolute; top: 0; bottom: 0; }
.gt-trade .kept { left: 0; background: rgba(255,138,76,.92); }
.gt-trade .lost { background: rgba(255,138,76,.34); }
.gt-trade:hover { filter: brightness(1.35); }
/* Fight / skirmish box — a single rectangle the height of a small champion
   portrait, holding the K/D/A. Its width is the LARGER of that caption and the
   fight's true duration, so a long brawl reads as wide. Two .gt-fedge marks
   below pin the real start/end. The fill is the timeline background itself
   (#0c0d13 = --slate-950), so the box reads as an outline around the score; the
   purple border is the only tone, one for every fight — win/loss is not what
   the box says. */
.gt-fight {
  position: absolute; box-sizing: border-box; overflow: hidden; cursor: pointer; z-index: 1;
  display: flex; align-items: center; justify-content: center;
  background: #0c0d13; border: 1px solid #463166;
}
.gt-fight.tf { border-color: #6f4aa1; }
/* 1vX — the player alone against 2+ enemies — wears the kill green. */
.gt-fight.solo { border-color: var(--green); }
/* The player's K/D/A, filling the box. A figure colours itself once non-zero. */
.gt-fight .kda {
  font: 700 10px var(--font-mono); letter-spacing: -.02em; color: var(--text-muted);
  line-height: 1; white-space: nowrap; pointer-events: none;
}
.gt-fight .kda .k { color: var(--green); }
.gt-fight .kda .d { color: var(--red); }
.gt-fight .kda .a { color: var(--gold); }
.gt-fight:hover { filter: brightness(1.3); }
/* The fight's true start and end: two vertical marks (NOT a rail — no connecting
   line), one at each end of the real span. Same 1px width and colour as the
   box's own outline (per tone). They run from just under the box to the FULL
   bottom of the row, and sit at z-index 0 — under recall lines, death rails,
   arrows and every other marker — so they read as quiet context everything else
   draws over. Purely informational (the box takes the click); radius reset must
   not round them. */
.gt-fedge {
  position: absolute; top: 46px; bottom: 0; width: 1px; transform: translateX(-50%);
  background: #463166; border-radius: 0 !important; pointer-events: none; z-index: 0;
}
.gt-fedge.tf { background: #6f4aa1; }
.gt-fedge.solo { background: var(--green); }
/* Recall lines: light-gray full-height ticks at a recall's first purchase.
   z-index 0 keeps them UNDER every marker and fight shape — they're context for
   the row, not something that should cut across an event box. */
.gt-recall-line { position: absolute; top: 0; bottom: 0; width: 2px; transform: translateX(-50%); background: rgba(206,210,228,.30); cursor: pointer; z-index: 0; }
.gt-recall-line:hover { background: rgba(206,210,228,.6); }
/* The continuation through the notes lane(s): same line, faded, and inert so it
   can't intercept a click meant for a note bar underneath it. */
.gt-recall-line.faint { background: rgba(206,210,228,.10); pointer-events: none; z-index: 0; }
/* (Objectives now render as per-type glyphs — see .gt-objg above.) */
/* ── Timeline hover card ─────────────────────────────────────────────────── */
/* The Events-tab card for a pin, floated over the timeline. Fixed + JS-placed
   (see positionHoverCard) so it escapes the pane and flips below when needed. */
.gt-hover {
  position: fixed; z-index: 70; width: 300px; pointer-events: none;
  background: var(--surface-raised); border: 1px solid var(--border-strong);
  box-shadow: 0 16px 40px -16px rgba(0,0,0,.85);
}
.gt-hover[hidden] { display: none; }
/* Fallback bodies (objective / ward / rail) hug their content instead of
   holding the 300px a real .qc needs. min-width keeps the head's kind and
   time apart — margin-left:auto collapses to nothing at max-content. */
.gt-hover.compact { width: max-content; min-width: 190px; max-width: 300px; }
/* A real .qc lives inside for feed-backed events. It normally stretches as a
   flex child of .ef-feed, so the popover supplies the width; drop its own box
   chrome (the popover already has it) but keep the left type spine. */
.gt-hover .qc { background: none; border-top: none; border-right: none; border-bottom: none; margin: 0; }
.gt-hover .gh-head {
  display: flex; align-items: center; gap: 8px; padding: 7px 10px;
  border-bottom: 1px solid var(--border); border-left: 3px solid var(--gh, var(--accent));
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .04em;
}
.gt-hover .gh-kind { color: var(--gh, var(--accent)); }
.gt-hover .gh-time { margin-left: auto; color: var(--text-faint); }
.gt-hover .gh-body { padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
.gt-hover .gh-row { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: var(--text); }
.gt-hover .gh-row b { font-weight: 600; }
.gt-hover .gh-t { margin-left: auto; font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); }
.gt-hover .gh-sub { font-family: var(--font-mono); font-size: 9.5px; color: var(--text-muted); }
.gt-hover .gh-port { width: 20px; height: 20px; flex: none; object-fit: cover; border: 1px solid var(--border); background: var(--bg); }
.gt-hover .gh-ports { display: flex; flex-wrap: wrap; gap: 3px; }
.gt-hover .gh-ports .gh-port { width: 18px; height: 18px; }
.gt-hover .gh-stats { display: flex; gap: 12px; font-size: 11px; color: var(--text-muted); }
.gt-hover .gh-stats b { color: var(--text); font-size: 12.5px; }
.gt-hover .gh-glyph { width: 20px; text-align: center; font-family: var(--font-mono); font-weight: 700; color: inherit; }
/* Objectives taken during a fight have no marker of their own, so the fight's
   card is where they surface — as the full fight-card tags. `.fx-objs` normally
   lives INSIDE a padded .qc; here it's a sibling of the .qc, directly in the
   unpadded popover, so it needs its own inset or it hugs the left/bottom edge. */
.gt-hover .fx-objs { margin: 0; padding: 0 10px 8px; border-top: none; }
.gt-hover .gh-tags { display: flex; flex-wrap: wrap; gap: 4px; padding: 0 10px 8px; }
.gt-hover .gh-tag {
  font: 600 9px var(--font-mono); letter-spacing: .04em; padding: 2px 5px;
  border: 1px solid rgba(255,93,177,.5); color: #ff8fca; background: rgba(255,93,177,.12);
}
.gt-hover .gh-tag.enemy { border-color: rgba(207,107,107,.5); color: #e0a0a0; background: rgba(207,107,107,.12); }

/* ── Visibility button + popover (timeline header) ───────────────────────── */
.tl-hl-btn {
  display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px;
  font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; letter-spacing: .02em;
  border: 1px solid var(--border); background: var(--surface-raised); color: var(--text-muted); cursor: pointer;
}
/* The eye takes the button's own colour, so it lights up with the label. */
.tl-hl-eye { width: 13px; height: 13px; flex: none; display: block; }
.tl-hl-btn:hover { color: var(--text); }
.tl-hl-btn.on { border-color: var(--accent-border); background: var(--accent-soft); color: var(--accent); }
.tl-hl-n { font-size: 9px; font-weight: 600; color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border); padding: 1px 5px; margin-left: 6px; }
.tl-hl-n[hidden] { display: none; }
/* Fixed, not absolute: the timeline header sits at the bottom of the screen, so
   an absolutely-positioned panel could only ever open downward off-viewport.
   placeHlPop() flips it above the button and clamps it horizontally. */
.tl-hl-pop {
  position: fixed; left: 0; top: 0; width: 354px; z-index: 60; padding: 2px 14px 10px;
  max-height: min(74vh, 620px); overflow-y: auto;
  background: var(--surface-raised); border: 1px solid var(--border-strong); box-shadow: 0 16px 40px -16px rgba(0,0,0,.8);
}
.tl-hl-pop[hidden] { display: none; }
.tl-hl-pop .sp { flex: 1; }
.tl-hl-head, .tl-hl-head2 { display: flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .04em; color: var(--text-faint); }
.tl-hl-head { padding: 10px 0 7px; border-bottom: 1px solid var(--border); }
.tl-hl-sub { color: var(--text-faint); font-weight: 400; }
.tl-hl-rule { padding: 9px 0 10px; border-bottom: 1px solid var(--border); }
.tl-hl-r1 { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.tl-hl-rule.off .tl-hl-r1 { cursor: default; opacity: .5; }
.tl-hl-r2 { display: flex; align-items: center; gap: 6px; margin: 7px 0 0 23px; }
.tl-hl-name { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: .02em; color: var(--text); }
.tl-hl-sw { width: 12px; height: 2px; flex: none; }
.tl-hl-lab, .tl-hl-why { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.tl-hl-cb {
  width: 15px; height: 15px; flex: none; display: inline-flex; align-items: center; justify-content: center;
  font-size: 10px; box-sizing: border-box; background: var(--bg); border: 1px solid var(--border-strong); color: transparent;
}
.tl-hl-cb.on { background: var(--accent); border-color: var(--accent); color: #fff; }
.tl-hl-pop select, .tl-hl-pop input[type="number"] {
  height: 24px; padding: 0 6px; box-sizing: border-box; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: var(--font-mono); font-size: 11px;
}
/* Direction controls. A two-state one is a single button that flips on click; a
   three-state one puts all three on screen — either way it's one click, never a
   menu you have to open to change a setting this small. */
.tl-hl-tog {
  height: 24px; padding: 0 9px; box-sizing: border-box; border: 1px solid var(--border);
  background: var(--bg); color: var(--text); font-family: var(--font-mono); font-size: 11px;
  cursor: pointer;
}
.tl-hl-tog:hover { border-color: var(--border-strong); }
.tl-hl-tog.on { background: var(--accent-soft); border-color: var(--accent-border); color: var(--accent); }
.tl-hl-seg { display: inline-flex; }
.tl-hl-seg .tl-hl-tog + .tl-hl-tog { margin-left: -1px; }
/* Tempo's ally picker: icon + role short-label, in one dropdown button. Custom
   rather than a <select> because options carry the champion icon. The list
   overlays the rows beneath it inside the panel (position:relative anchor,
   absolute list) — four rows always fit above the pins section. */
.tl-hl-champ { display: inline-flex; align-items: center; gap: 4px; }
.tl-hl-champ img { width: 14px; height: 14px; display: block; }
.tl-hl-car { font-style: normal; font-size: 8px; color: var(--text-muted); margin-left: 2px; }
.tl-hl-dd { position: relative; display: inline-flex; }
.tl-hl-dd-list {
  display: none; position: absolute; left: 0; top: calc(100% + 2px); z-index: 5;
  min-width: 100%; flex-direction: column;
  background: var(--surface-raised); border: 1px solid var(--border-strong);
  box-shadow: 0 10px 24px -12px rgba(0,0,0,.8);
}
.tl-hl-dd.open .tl-hl-dd-list { display: flex; }
.tl-hl-dd.open [data-tempo-dd-btn] { border-color: var(--border-strong); }
.tl-hl-dd-opt {
  height: 24px; padding: 0 9px; border: none; box-sizing: border-box;
  background: none; color: var(--text); font-family: var(--font-mono); font-size: 11px;
  cursor: pointer; text-align: left;
}
.tl-hl-dd-opt:hover { background: var(--bg); }
.tl-hl-dd-opt.on { background: var(--accent-soft); color: var(--accent); }
.tl-hl-pins { padding: 9px 0 2px; display: flex; flex-direction: column; gap: 8px; }
/* A display option, not an event kind: it sits above the pins with a rule under
   it and takes the muted colour so the eye reads the pin list as one group and
   this as a setting that governs it. */
.tl-hl-r1.opt { padding-bottom: 9px; border-bottom: 1px solid var(--border); }
.tl-hl-r1.opt .tl-hl-name { color: var(--text-muted); font-weight: 500; }
.gt-overlay { position: absolute; left: 74px; right: 0; top: 0; bottom: 0; pointer-events: none; z-index: 2; }
/* Moved by transform (review.js positionPlayhead), so `left` stays 0 here. */
.gt-playhead { position: absolute; top: 0; bottom: 0; left: 0; width: 2px; background: var(--accent); box-shadow: 0 0 6px rgba(128,128,234,.7); will-change: transform; }
/* The knob is a real element (draggable, hover timestamp); clip-path keeps it round under the border-radius reset. */
.gt-ph-knob { position: absolute; top: -1px; left: -4px; width: 10px; height: 10px; background: var(--accent); clip-path: circle(50%); pointer-events: auto; cursor: ew-resize; }
.gt-ph-knob:hover { transform: scale(1.25); }
/* The line's own drag strip: 8px wide down the full height (the 2px line is
   too thin to grab). Sits under the knob. */
.gt-ph-grab { position: absolute; top: 0; bottom: 0; left: -3px; width: 8px; pointer-events: auto; cursor: ew-resize; }
/* Ghost playhead: where a click on whatever you're hovering WOULD land. Dotted
   and faint so it never competes with the real playhead — it's a preview of an
   action, not a state of the video. Drawn as a repeating gradient rather than a
   dashed border because a 1px border-left draws nothing on a zero-width box. */
.gt-ghost {
  position: absolute; top: 0; bottom: 0; width: 1px; transform: translateX(-50%);
  background: repeating-linear-gradient(to bottom,
    rgba(128,128,234,.75) 0 3px, transparent 3px 6px);
  pointer-events: none;
}
.gt-ghost[hidden] { display: none; }

/* ── Clip mode: the range selection lives in .gt-overlay ─────────────────
   The overlay is pointer-events:none so the lanes below stay clickable —
   only the two handles opt back in. */
/* The in/out labels sit ABOVE the strip (the design's placement), and the
   handles overhang it by 6px — both are outside .timeline-track, which clips
   by default. Stop clipping while clip mode is up, or the timestamps the whole
   interaction depends on are invisible. Scoped to clip mode so the normal
   strip keeps its clean edges. */
.timeline-track.clipping { overflow: visible; }
/* ...and lift the overlay over the lane-label column (z-index 3), the way the
   design's clip track does. A handle at game 0 sits at the column's edge and
   its grip hangs 9px left of it — under the default z-index the column buries
   half the thing you're supposed to grab. */
.timeline-track.clipping .gt-overlay { z-index: 4; }

.gt-dim { position: absolute; top: 0; bottom: 0; background: rgba(8, 9, 14, .6); }
.gt-clipsel { position: absolute; top: 0; bottom: 0; background: var(--accent-soft);
  border-left: 2px solid var(--brand); border-right: 2px solid var(--brand); box-sizing: border-box; }
.gt-handle { position: absolute; top: -6px; bottom: -6px; width: 0; z-index: 6;
  cursor: ew-resize; pointer-events: auto; }
.gt-handle.on { z-index: 7; }
.gt-handle .bar { position: absolute; top: 0; bottom: 0; left: -1.5px; width: 3px; background: var(--border-strong); }
.gt-handle.on .bar { background: var(--brand); }
.gt-handle .grip { position: absolute; top: 50%; left: -9px; transform: translateY(-50%);
  width: 18px; height: 32px; background: var(--surface); border: 1px solid var(--border-strong);
  display: flex; align-items: center; justify-content: center;
  font-family: var(--font-mono); font-size: 9px; font-weight: 600; color: var(--text-muted); }
.gt-handle.on .grip { background: var(--brand); border-color: var(--brand-hover); color: #fff;
  box-shadow: 0 0 0 3px var(--accent-soft); }
/* Not ew-resize: this one selects + seeks, it doesn't drag (see renderClipOverlay). */
.gt-handle .lab { cursor: ew-resize; position: absolute; bottom: calc(100% - 5px); white-space: nowrap;
  font-family: var(--font-mono); font-size: 10px; font-weight: 600; color: var(--text);
  background: var(--surface-raised); border: 1px solid var(--border-strong); padding: 2px 6px;
  box-shadow: 0 2px 6px rgba(8, 9, 14, .6); z-index: 8; }
.gt-handle.on .lab { color: #fff; background: var(--brand); border-color: var(--brand-hover); }
.gt-handle.in .lab { right: 7px; }
.gt-handle.out .lab { left: 7px; }
/* The label hops when Space seeks the playhead to this point — it's the only
   feedback that the jump was to THIS handle and not the other one. */
@keyframes gtHandleJump { 0% { transform: translateY(0) } 35% { transform: translateY(-7px) } 65% { transform: translateY(1px) } 100% { transform: translateY(0) } }
.gt-handle.on .lab.jump { animation: gtHandleJump .32s var(--ease); }
@media (prefers-reduced-motion: reduce) { .gt-handle.on .lab.jump { animation: none } }

/* ════════════════════════════════════════════════════════════════════
   SIDE PANEL — tabs + panes
   ════════════════════════════════════════════════════════════════════ */
.notes-tabs { display: flex; align-items: center; padding: 0 8px; border-bottom: 1px solid var(--border); flex: none; gap: 0; }
.notes-tabs .tab:not([hidden]) {
  display: inline-flex; flex-direction: column; align-items: center; gap: 1px; line-height: 1.15;
  background: none; border: none; padding: 8px 13px; font-size: 13px; font-weight: 600;
  color: var(--text-muted); border-bottom: 2px solid transparent; border-radius: 0;
}
.notes-tabs .tab.active { color: var(--text); border-bottom-color: var(--brand); background: none; }
.notes-tabs .tab:hover:not(.active) { color: var(--text); background: none; }
.notes-tabs .tab .cnt { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); margin-left: 5px; }
/* Live count shown UNDER the Notes / Phases tab labels (mono instrument figure). */
.tab-count { font-family: var(--font-mono); font-size: 11px; font-weight: 600; color: var(--text-faint); line-height: 1; }
.tab-count:empty { display: none; }
.notes-tabs .tab.active .tab-count { color: var(--accent); }
.form-toggle { margin-left: 6px; height: 28px; width: 28px; padding: 0; display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); }

.notes-tab-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.notes-tab-body[hidden] { display: none; }

/* ════════════════════════════════════════════════════════════════════
   STANDARD NOTES — list (scrolls) + compose pinned to the bottom
   ════════════════════════════════════════════════════════════════════ */
.notes-list { flex: 1; min-height: 0; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 9px; }

/* rich note cards — progress-spine design (Note Card Final port).
   styles.css still carries a legacy .note block (library-era), so every
   geometry/color property it sets is explicitly overridden here. */
.note {
  position: relative; box-shadow: none;
  /* Growth curve on a widening pane: cards grow to 440px, then HOLD while
     the side gutters grow from the list's 10px up to 15px each (the auto
     margins split the 100%−(100%−10px) slack), and only past that do they
     grow again. Auto cross-axis margins opt a flex item out of stretch, so
     width:100% keeps the card full-width whenever the cap isn't binding. */
  width: 100%; max-width: max(440px, calc(100% - 10px)); margin: 0 auto;
  border: 1px solid var(--border); border-radius: 0;
  background: var(--surface-raised);
  padding: 11px 12px 11px 16px;
  display: flex; flex-direction: column; gap: 7px;
}
.note:hover { border-color: var(--accent-border); }
/* The accent (blue) highlight means SELECTED — .active, the note you seeked
   to / are navigating with the 1/2/3 hotkeys. Playing (.playing = the
   playhead is inside the note's window, set by updateNoteSpines) is NOT a
   highlight: it reads from the progress spine and the ● live dot, both in
   the note's own colour, so the two states never compete. */
/* The selected note: a faint wash laid OVER the card's own surface (a gradient
   layer, so the card colour stays underneath), the border does the rest.
   The full `background` shorthand on purpose: styles.css also has a
   .note.active background (a blue .1 wash) that must not show through. */
.note.active { background: linear-gradient(rgba(68,68,212,.07), rgba(68,68,212,.07)), var(--surface-raised); border-color: var(--accent-border); box-shadow: none; }
.note.editing { border-color: var(--accent-border); }

/* Progress spine: solid = played, dim = still ahead; while playing the solid
   part grows over the dim part with a seam tick (top = --spine-pct). */
.note-spine { position: absolute; left: 0; top: 0; bottom: 0; width: 4px; pointer-events: none; }
.note-spine .sp-done { position: absolute; inset: 0; background: var(--note-color, var(--border-strong)); }
.note[data-spine="upcoming"] .sp-done { background: color-mix(in srgb, var(--note-color, var(--border-strong)) 45%, transparent); }
.note.playing .sp-done { bottom: auto; height: var(--spine-pct, 0%); }
.note-spine .sp-rest, .note-spine .sp-tick { display: none; }
.note.playing .sp-rest { display: block; position: absolute; left: 0; right: 0; top: var(--spine-pct, 0%); bottom: 0; background: color-mix(in srgb, var(--note-color, var(--border-strong)) 45%, transparent); }
.note.playing .sp-tick { display: block; position: absolute; left: 0; right: 0; top: calc(var(--spine-pct, 0%) - 1px); height: 2px; background: var(--bg); z-index: 1; }

/* header row: swatch · title (seeks) · [ time ] (seeks) */
.note .note-title { display: flex; align-items: center; gap: 7px; margin: 0; font-size: 14px; font-weight: 600; cursor: default; }
.note .note-title .nt-text { flex: 1; min-width: 0; overflow-wrap: anywhere; cursor: pointer; user-select: text; -webkit-user-select: text; }
.note .note-title .nt-text:hover { color: var(--accent); }
/* Inline at the head of the title text: the note's icon (in its colour) leads
   the first word and wrapped lines flow underneath it (it is NOT a flex
   sibling of the text). */
.note-icon { display: inline-block; width: 14px; height: 14px; margin-right: 7px; vertical-align: -2px; }
.note .note-time {
  margin: 0; flex: none; align-self: baseline; white-space: nowrap; cursor: pointer;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--text-muted);
}
.note .note-time:hover { color: var(--accent); text-decoration: underline; }
.note.active .note-time { color: var(--accent); }
/* Note text and titles are selectable (the page is user-select:none); a
   release over selected text is not a seek (selectionInside in review.js). */
.note .note-body { margin: 0; font-size: 13px; line-height: 1.6; color: var(--text); white-space: pre-wrap; user-select: text; -webkit-user-select: text; cursor: text; }
/* Inline annotation reference in a RENDERED body: the `▣n` ref shown as its
   bracketed NUMBER — [n], the Wikipedia footnote — accent, mono, raised. The
   span's TEXT is still the whole ref (▣ plus the number), so a copied
   selection carries exactly those characters and the body's textContent
   equals the raw text; the ▣ simply has no ink and no advance ("DR Anno Ref",
   styles.css) and the brackets are pseudo-elements (never copied). A ref with
   no annotation at that index (pasted from another note) is inert and reads
   as a faint [n]. */
.anno-inline {
  font-family: "DR Anno Ref", var(--font-mono); font-size: 10px; font-weight: 600;
  color: var(--accent); vertical-align: 3px; white-space: nowrap; cursor: pointer;
}
.anno-inline::before { content: "["; }
.anno-inline::after { content: "]"; }
.anno-inline:hover { text-decoration: underline; }
.anno-inline.dead { color: var(--text-faint); cursor: default; }
.anno-inline.dead:hover { text-decoration: none; }
.anno-svg-ico::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  transform: translate(-50%, -50%); width: 10px; height: 9px;
  background: var(--accent);
  -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 14'%3E%3Cpath fill-rule='evenodd' d='M0 0h16v14H0zM2 2v10h12V2z'/%3E%3Cpath d='M3 11l3-4.2 2.2 2.8L10 7.5l3 3.5z'/%3E%3Ccircle cx='10.6' cy='4.6' r='1.4'/%3E%3C/svg%3E") center / contain no-repeat;
  mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 14'%3E%3Cpath fill-rule='evenodd' d='M0 0h16v14H0zM2 2v10h12V2z'/%3E%3Cpath d='M3 11l3-4.2 2.2 2.8L10 7.5l3 3.5z'/%3E%3Ccircle cx='10.6' cy='4.6' r='1.4'/%3E%3C/svg%3E") center / contain no-repeat;
}
/* Standalone icon for chrome (carry ghost, hint bar) — same SVG, no text. */
.anno-svg-ico { position: relative; display: inline-block; width: 14px; height: 12px; }

/* Note-body highlight overlay: a mirror layer behind a transparent-text
   textarea so attachment-linked text (accent) and the active draw target (teal)
   can be colored inside the editing box. Grid-stacks the backdrop + textarea so
   the backdrop tracks the textarea's size; `.fill` is the flex:1 notebook prose. */
/* The backdrop is OUT OF FLOW: as a grid item it used to size the cell by
   the mirror's text, so a textarea with rows=4 was stretched to its whole
   content while it held a ref and snapped back to four rows the moment the
   last ref went (the mirror empties) -- a 250px jump in the notes list. The
   textarea alone decides the height now; the backdrop just tracks it. */
.ta-wrap { display: grid; position: relative; }
.ta-wrap.fill { flex: 1 1 0; min-height: 0; grid-template-rows: 1fr; }
/* The wrap takes over the textarea's layout role where it matters:
   event-card inputs hide with their card state (otherwise an empty mirror
   box lingers on closed cards), and phase-note inputs are the flex fill of
   their row. */
.qc:not(.note-open) .ta-wrap { display: none; }
.ph-note-inner .ta-wrap { flex: 1; min-width: 0; }
/* Once the compose #noteBody gets wrapped, the wrapper inherits its flex role
   in .notes-header (see #noteBody below) so the compose-grip drag still hands
   extra height to the textarea; undragged, the 1fr row sizes to content. */
.notes-header .ta-wrap { flex: 1 1 auto; min-height: 52px; grid-template-rows: 1fr; }
/* The textarea must PAINT ABOVE the backdrop: a positioned element stacks
   over in-flow siblings whatever the DOM order, so an absolute backdrop with
   the field's opaque background hid the caret, and the field's own text the
   moment the mirror emptied ("Backspace deleted everything"). Positioning the
   textarea too (later in the DOM, same z-index: auto) puts it back on top;
   its text and background are transparent while the mirror is on, so the
   mirror still shows through. */
.ta-wrap > textarea { grid-area: 1 / 1; min-width: 0; position: relative; }
.ta-backdrop { position: absolute; inset: 0; overflow: hidden; pointer-events: none; }
/* The mirror is as tall as its text (never clipped by its own box) and is
   moved with a transform to follow the textarea's scroll -- review.js
   _hlSync; the backdrop clips. Its width is set per paint (_hlFitWidth). */
.ta-mirror { width: 100%; min-height: 100%; height: auto; margin: 0; box-sizing: border-box; border-style: solid; border-color: transparent; white-space: pre-wrap; overflow-wrap: break-word; word-break: break-word; color: var(--text); }
.ta-mirror mark { background: transparent; color: inherit; }
/* A ref inside an EDITOR: ONE private-use character (review.js encodeRefs)
   whose glyph is empty and whose advance (--font-edit, 1.45em one digit /
   1.95em two) fits the bracketed NUMBER painted here. The mirror sits
   pixel-aligned behind the real textarea, so the mark must be METRIC-NEUTRAL:
   its own text goes transparent and "[n]" comes from an absolutely
   positioned ::after: out of flow, so it adds no width, no soft-wrap
   opportunity and no line-box height (an inline-block or a transform would
   have added the first; a smaller font on the text itself the second). The
   number hugs the word before it and the text after starts clear of the `]`.
   The old font-weight:600 was NOT neutral: the fallback ▣ was 1px wider in
   bold, and every character after a ref sat right of where the caret was. */
.ta-mirror mark.ta-anno { position: relative; color: transparent; }
.ta-mirror mark.ta-anno::after {
  /* 6px (px on purpose — an em here is the ::after's OWN 10px): with
     line-height:1 the number's 12px content area then starts exactly at the
     13px text's content-area top, the same place the rendered card's
     `vertical-align: 3px` puts it (measured: mark box 17px, 17 - 10 - 1). */
  content: "[" attr(data-n) "]"; position: absolute; left: 0; bottom: 6px;
  font-family: "DR Anno Ref", var(--font-mono); font-size: 10px; font-weight: 600; line-height: 1;
  color: var(--accent); white-space: nowrap; pointer-events: none;
}
/* Nothing at that index on this note — inert, so it reads as a faint [n]. */
.ta-mirror mark.ta-anno.dead::after { color: var(--text-faint); }
textarea.ta-hl-on { background: transparent !important; color: transparent !important; caret-color: var(--accent); }
textarea.ta-hl-on::selection { background: rgba(128,128,234,.35); }

/* ── Annotation placement (carry + click-to-place) ─────────────────────── */
body.anno-carrying, body.anno-carrying * { cursor: none !important; }
/* While carrying, registered editors hand hit-testing to their mirror so
   caret-from-point can find the word-gap drop line inside textareas. */
body.anno-carrying textarea.ta-hl-input { pointer-events: none; }
body.anno-carrying .ta-mirror { pointer-events: auto; }
#annoGhost {
  position: fixed; z-index: 4000; pointer-events: none; display: none;
  transform: translate(-50%, -50%);
}
#annoGhost.on { display: block; }
#annoGhost .ghost-icon {
  position: relative; width: 34px; height: 30px; display: grid; place-items: center;
  font-size: 15px; background: var(--surface-2, var(--surface));
  border: 1px solid var(--accent); border-radius: 7px; box-shadow: 0 6px 20px rgba(0,0,0,.5);
}
#annoGhost.valid .ghost-icon { background: var(--accent-soft); }
#annoGhost.nav .ghost-icon { border-color: var(--border-strong); opacity: .9; }
#annoGhost.invalid .ghost-icon { border-color: var(--red, #ff5d5d); opacity: .45; filter: grayscale(.5); }
#annoGhost .badge {
  position: absolute; right: -8px; bottom: -8px; width: 18px; height: 18px;
  border-radius: 50%; display: grid; place-items: center; font-size: 11px; color: #fff;
}
#annoGhost.valid .badge { background: var(--accent); }
#annoGhost.invalid .badge { background: var(--red, #ff5d5d); }
#annoGhost.nav .badge { background: var(--border-strong); font-size: 9px; }
#annoGhost .cap {
  position: absolute; left: 50%; top: 38px; transform: translateX(-50%);
  white-space: nowrap; font-size: 10.5px; font-family: var(--font-mono);
  padding: 2px 6px; border-radius: 5px; background: rgba(8, 9, 14, .9);
  color: var(--accent); border: 1px solid var(--accent-border);
}
#annoGhost.invalid .cap { color: var(--red, #ff5d5d); border-color: var(--red, #ff5d5d); }
#annoGhost.nav .cap { color: var(--text-muted); border-color: var(--border); }
/* Over a text surface: swap the boxed icon for a bare accent crosshair so
   the drop line underneath reads through. */
#annoGhost.over-text .ghost-icon {
  background: transparent; border: none; box-shadow: none;
  width: 26px; height: 26px;
}
#annoGhost.over-text .badge { display: none !important; }
#annoGhost.over-text .anno-svg-ico { display: none; }
#annoGhost.over-text .ghost-icon::before {
  content: "+"; font-size: 30px; font-weight: 400; line-height: 26px;
  color: var(--accent); text-shadow: 0 0 5px rgba(8, 9, 14, .9);
}
#annoCaret {
  position: fixed; z-index: 3990; width: 2px; pointer-events: none; display: none;
  background: var(--accent); box-shadow: 0 0 6px var(--accent);
}
#annoCaret.on { display: block; }
#annoCaret::before, #annoCaret::after { content: ""; position: absolute; left: -2px; width: 6px; height: 2px; background: var(--accent); }
#annoCaret::before { top: -2px; }
#annoCaret::after { bottom: -2px; }
.anno-drop-target { outline: 2px solid var(--accent) !important; outline-offset: -1px; background: var(--accent-soft) !important; }
.video-stage.anno-drop-target { outline: 3px solid var(--accent) !important; outline-offset: -3px; }
#annoHint {
  position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%);
  z-index: 3980; display: none; align-items: center; gap: 14px; pointer-events: none;
  background: rgba(12, 13, 19, .96); border: 1px solid var(--border-strong); border-radius: 9px;
  padding: 8px 14px; font-size: 12px; color: var(--text-muted); box-shadow: 0 8px 30px rgba(0,0,0,.5);
}
#annoHint.on { display: flex; }
#annoHint b { color: var(--text); }
#annoHint .h-ico {
  width: 22px; height: 20px; display: grid; place-items: center; border-radius: 5px;
  background: var(--accent-soft); border: 1px solid var(--accent-border); font-size: 12px;
}
#annoHint .k { font-family: var(--font-mono); background: var(--surface); border: 1px solid var(--border); border-radius: 4px; padding: 1px 6px; font-size: 11px; }
#annoHint .hsep { width: 1px; height: 16px; background: var(--border); }
/* ── Note footer: annotation thumb strip + mono text actions ─────────────── */
.note-foot {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  border-top: 1px solid var(--border); padding-top: 7px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
}
.note.active .note-foot { border-top-color: var(--accent-border); }
.nf-spacer { flex: 1; }
.nf-thumb {
  width: 28px; height: 16px; object-fit: cover; display: block; flex: none;
  border: 1px solid var(--border-strong); background: var(--bg);
  cursor: pointer; user-select: none; -webkit-user-drag: none;
}
.nf-thumb.inline-ref { border-color: var(--accent-border); }
.nf-thumb:hover { border-color: var(--accent); }
/* The selected annotation — the one currently being manipulated (click,
   right-click, or drag selects it; double-click opens). */
.nf-thumb.sel { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.note-foot button {
  border: none; background: none; padding: 0; margin: 0; height: auto; cursor: pointer; white-space: nowrap;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--text-muted);
}
.note-foot button:hover { color: var(--accent); background: none; border: none; }
/* U+25B8 is the SMALL triangle (the full-size one renders as emoji on
   Windows), so it's scaled up to sit with ✎ ✂ ✕. Both numbers are MEASURED
   off the rasterised glyphs, not the layout boxes — line-height:0 (which
   keeps the bigger glyph from growing the footer row) makes that box
   degenerate, so only the ink tells the truth:
     · size — at 2.15em the triangle inks 9×9, matching ✎ and ✕ (9 wide, and
       within a pixel on height); the old 1.8em inked 7×7, visibly the runt
       even though its ADVANCE matched, which is what fooled the first pass.
     · offset — the ink then centres 7px above its own baseline while ✎ ✂ ✕
       average 4.33px above theirs, so the box drops by that 2.67px
       difference (0.113em of 23.65px), landing all four on one band. */
.note-foot .nf-play { display: inline-flex; vertical-align: -2px; }
.note-foot button svg { vertical-align: -2px; }
.note-foot button[data-act="delete"]:hover { color: var(--red); }
.note.coach .note-foot button:hover { color: var(--gold); }
.note-foot button[data-act="ok"] { color: #fff; background: var(--brand); padding: 3px 10px; font-weight: 600; }
.note-foot button[data-act="ok"]:hover { color: #fff; background: var(--brand-hover, #5e5ee2); }
.note.editing[data-author="coach"] button[data-act="ok"] { background: var(--gold); color: #17150c; }
.note.editing[data-author="coach"] button[data-act="ok"]:hover { background: var(--gold); color: #17150c; filter: brightness(1.06); }

/* ── In-place editor: [ start now → end now ] · // editing ──────────────── */
.note .edit-form { display: flex; flex-direction: column; gap: 7px; }
.edit-times {
  display: flex; align-items: center; gap: 5px;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; color: var(--text-muted);
}
.edit-times .et-brk { color: var(--text-faint); }
.et-pair { display: inline-flex; }
.note .edit-form .et-pair input {
  width: 48px; padding: 2px 4px; text-align: center; border-radius: 0;
  font-family: var(--font-mono); font-size: 11px; font-weight: 500;
  background: var(--bg); border: 1px solid var(--border); color: var(--text);
}
.note .edit-form .et-pair input:focus { outline: none; border-color: var(--accent); }
.note .edit-form .btn-now {
  padding: 2px 6px; border: 1px solid var(--border); border-left: 0; border-radius: 0;
  background: var(--bg); color: var(--accent); font-family: var(--font-mono); font-size: 10px; font-weight: 500;
}
.note .edit-form .btn-now:hover { color: var(--text); background: var(--surface-hover, #252839); }
.et-hint { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); white-space: nowrap; }
.edit-head { display: flex; align-items: center; gap: 6px; }
.note .edit-form .edit-head input[data-field="title"] { flex: 1; min-width: 0; font-size: 14px; padding: 4px 8px; }
/* the color control: a bare 16px swatch (the cpick popover grid unchanged) */
.edit-head .cpick-current { height: 16px; width: 16px; padding: 0; border: 1px solid var(--border-strong); background: none; display: block; }
.edit-head .cpick-current:hover { border-color: #fff; }
.edit-head .cpick .sw-dot { width: 14px; height: 14px; border: none; }
.edit-head .cpick-caret { display: none; }
/* editor footer thumbs carry a × removal badge */
.edit-thumbs { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.et-thumb { position: relative; display: inline-flex; }
.et-x {
  position: absolute; top: -5px; right: -5px; width: 12px; height: 12px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--surface-raised); border: 1px solid var(--border-strong);
  color: var(--text-muted); font-size: 9px; line-height: 1; cursor: pointer;
}
.et-x:hover { color: var(--red); border-color: var(--red); background: var(--surface-raised); }

/* ── Right-click menu (note / annotation) ───────────────────────────────── */
.note-menu {
  position: fixed; z-index: 4100; min-width: 132px; max-width: 240px;
  display: flex; flex-direction: column; padding: 0 0 4px;
  background: var(--surface-raised); border: 1px solid var(--border-strong);
  box-shadow: 0 8px 24px rgba(0, 0, 0, .5);
}
.note-menu .nm-title {
  display: block; padding: 5px 12px; margin-bottom: 4px;
  font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: .03em;
  color: var(--text-muted); border-bottom: 1px solid var(--border);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.note-menu .nm-item {
  display: block; padding: 5px 12px; border: none; background: none; text-align: left;
  font-family: var(--font-sans); font-size: 12px; font-weight: 600; color: var(--text); cursor: pointer;
}
.note-menu .nm-item:hover { background: var(--surface-hover, #252839); }
.note-menu .nm-item.danger:hover { color: var(--red); }

/* compose (pinned bottom). Flex column so a dragged-taller panel hands the
   extra height to the Detailed-notes textarea (#noteBody flex:1). */
.compose { position: relative; border-top: 1px solid var(--border); background: var(--surface); flex: none; display: flex; flex-direction: column; min-height: 0; }
/* Fully-minimized compose (grip yanked down past the minimum): only the
   color · start · end · Add-note row survives — title and body drop out, and
   the panel sits at its natural single-row height. There is deliberately no
   state between this and the normal minimum (the grip snaps). */
.compose.min { height: auto !important; }
.compose.min #noteTitle, .compose.min #noteBody, .compose.min .notes-header .ta-wrap { display: none; }
.compose-grip { flex: none; height: 11px; cursor: ns-resize; display: flex; align-items: center; justify-content: center; }
.compose-grip span { width: 34px; height: 3px; border-radius: 2px; background: var(--border-strong); }
.compose-grip:hover span { background: var(--accent); }
/* Drag feedback. The panel follows the pointer 1:1 through the whole
   row↔minimum band (.pulling / .snapping clip it while its height runs below
   the minimum: the header anchors to its END so the color · start · end ·
   Add-note row stays put and it is the title/body that slide away under the
   grip). --pull (0 at the minimum, 1 at the row height) squeezes the grip bar
   as the panel drops; .armed turns it accent = "letting go now collapses".
   The snap itself only happens on release, as a height tween. */
.compose-grip span { transform: scaleX(calc(1 - .35 * var(--pull, 0))); transition: background-color 120ms; }
.compose.armed .compose-grip span { background: var(--accent); }
.compose.pulling, .compose.snapping { overflow: hidden; }
.compose.pulling .notes-header, .compose.snapping .notes-header { overflow: hidden; justify-content: flex-end; }
.compose.snapping { transition: height 240ms cubic-bezier(.2, .85, .25, 1); }
@media (prefers-reduced-motion: reduce) { .compose.snapping { transition: none; } }
/* padding-bottom is re-synced by syncComposeBand() so the Detailed-notes
   textarea's bottom edge lines up with the top of the video control bar. */
.notes-header { flex: 1; min-height: 0; padding: 2px 12px 8px; display: flex; flex-direction: column; gap: 8px; }
.notes-header #noteBody { flex: 1 1 auto; min-height: 52px; }
.notes-header #currentTime { font-family: var(--font-mono); font-size: 11px; color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: 6px; padding: 4px 8px; align-self: flex-start; }
.notes-header input, .notes-header textarea, .edit-form input, .edit-form textarea {
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text);
  font-family: var(--font-sans); font-size: 13px; padding: 8px 10px;
}
/* --font-edit: the same sans, with the ▣ of an inline ref zero-width. */
.notes-header textarea, .edit-form textarea { resize: none; line-height: 1.5; min-height: 52px; font-family: var(--font-edit); }
.notes-header .row, .edit-form .edit-row { display: flex; gap: 8px; }
.input-with-button { display: flex; align-items: center; flex: 1; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); }
.input-with-button input { border: none; background: transparent; flex: 1; }
.btn-now { height: 30px; padding: 0 9px; border: none; border-left: 1px solid var(--border); border-radius: 0 6px 6px 0; background: transparent; color: var(--text-muted); font-size: 11px; font-weight: 600; }
.btn-now:hover { color: var(--accent); background: var(--accent-soft); }
.color-row { display: flex; align-items: center; gap: 8px; }
.palette { display: inline-flex; }
/* Compose action row: color, start, end and Add note on one line. */
/* Everything on the row is 32px tall: the scope square (team mode), the
   icon/colour picker, the two time fields and Add note. The fields used to
   take their height from the input's padding (35px) and the picker kept the
   30px of every other cpick, so the row read as four different boxes. */
.compose-row { display: flex; align-items: center; gap: 6px; }
.compose-row .palette { flex: none; }
/* The icon/colour square: 32px, the glyph fills it, no caret (the edit
   form's 16px square hides its caret the same way). */
.compose-row .cpick-current { height: 32px; width: 32px; padding: 0; justify-content: center; }
.compose-row .cpick .sw-dot, .compose-row .cpick .sw-dot svg { width: 24px; height: 24px; }
.compose-row .cpick-caret { display: none; }
.compose-row .input-with-button { flex: 1; min-width: 0; height: 32px; }
.compose-row .input-with-button input { min-width: 0; height: 100%; padding: 0 10px; }
.compose-row .btn-now { height: 100%; }
.compose-row #addNoteBtn { flex: none; height: 32px; padding: 0 12px; }

/* Collapsible color picker (cpick): a single square showing the current
   colour; clicking opens a fixed-position popup grid. Used for note colour
   (single-select) and note filtering (multi-select). */
.cpick { position: relative; display: inline-flex; }
.cpick-current { display: inline-flex; align-items: center; gap: 5px; height: 30px; padding: 0 8px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); }
.cpick-current:hover { border-color: var(--border-strong); }
/* The collapsed square shows the note icon painted in the picked colour. */
.cpick .sw-dot { width: 15px; height: 15px; display: inline-flex; align-items: center; justify-content: center; color: var(--pick-color, var(--text)); }
.cpick-caret { font-size: 9px; color: var(--text-faint); }
.cpick-pop { display: none; position: fixed; z-index: 50; padding: 8px; background: var(--surface-raised); border: 1px solid var(--border-strong); border-radius: 9px; box-shadow: 0 16px 40px -16px rgba(0,0,0,.8); grid-template-columns: repeat(5, 1fr); gap: 7px; }
.cpick.open .cpick-pop { display: grid; }
.cpick-pop .sw-pick { width: 22px; height: 22px; border-radius: 5px; border: 1px solid rgba(255,255,255,.14); padding: 0; cursor: pointer; }
.cpick-pop .sw-pick:hover { border-color: rgba(255,255,255,.4); }
.cpick-pop .sw-pick.on { outline: 2px solid var(--text); outline-offset: 1px; }
.cpick-pop .sw-pick.off { opacity: .3; }
/* note variant: the ten icons (5×2) over the ten colours (5×2) */
.cpick.open .cpick-pop-note { display: flex; flex-direction: column; gap: 8px; }
.cpick-icons { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
/* the icon buttons are the swatches' size; a raised surface and a 16px glyph so the box reads as big as a colour block */
.cpick-pop .ic-pick { width: 22px; height: 22px; padding: 0; border-radius: 5px; border: 1px solid rgba(255,255,255,.14); background: var(--surface-hover); color: var(--text); display: inline-flex; align-items: center; justify-content: center; cursor: pointer; }
.cpick-pop .ic-pick:hover { border-color: rgba(255,255,255,.4); color: var(--text); }
.cpick-pop .ic-pick.on { color: var(--pick-color, var(--text)); outline: 2px solid var(--text); outline-offset: 1px; }
.cpick-pop .ic-pick.off { opacity: .3; }
.cpick-filter .ic-pick.on { color: var(--text); }
/* filter variant: swatch grid + an All/None foot row, stacked */
.cpick.open .cpick-pop-filter { display: flex; flex-direction: column; gap: 8px; }
.cpick-swatches { display: grid; grid-template-columns: repeat(5, 1fr); gap: 7px; }
.cpick-pop-foot { display: flex; gap: 6px; }
.cpick-pop-foot .btn-tiny { flex: 1; }
.cpick-filter .cpick-current { gap: 6px; }
.cpick-filter .filter-glyph { font-size: 12px; color: var(--text-muted); line-height: 1; }
.cpick-filter .filter-glyph-icon { display: inline-flex; }
.cpick-filter .filter-count { font-size: 11px; color: var(--text-muted); font-weight: 600; white-space: nowrap; }
.cpick-filter .filter-count:empty { display: none; }

/* Thin filter/actions bar at the top of Standard mode. */
.note-filter-bar { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border-bottom: 1px solid var(--border); flex: none; }
.note-filter-bar .fbar-spacer { flex: 1; }
.btn-tiny { height: 28px; padding: 0 10px; font-size: 11px; font-weight: 600; border-radius: 6px; border: 1px solid var(--border); background: var(--surface-raised); color: var(--text-muted); white-space: nowrap; }
.btn-tiny:hover { border-color: var(--border-strong); color: var(--text); }
/* Save Clips — one button opening a vertical action menu (reuses .cpick). */
/* Same height as the three filter pickers it shares the bar with. */
.cpick-save .cpick-current { height: 30px; padding: 0 10px; gap: 6px; background: var(--surface-raised); border-color: var(--border); color: var(--text-muted); font-size: 11px; font-weight: 600; }
.cpick-save .cpick-current:hover { border-color: var(--border-strong); color: var(--text); }
.cpick.open .cpick-pop-save { display: flex; flex-direction: column; gap: 5px; min-width: 152px; }
.cpick-pop-save .btn-tiny { width: 100%; text-align: left; }

/* ════════════════════════════════════════════════════════════════════
   QUICK REVIEW
   ════════════════════════════════════════════════════════════════════ */
.qr-list { flex: 1; min-height: 0; overflow-y: auto; padding: 10px; display: flex; flex-direction: column; gap: 8px; }
/* Deaths / Trades / Teamfights switcher at the top of the quick-review list. */
.qr-tabs { display: flex; gap: 6px; }
.qr-tab { flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 5px; padding: 6px 8px; font-size: 11.5px; font-weight: 600; color: var(--text-muted); background: var(--surface-raised); border: 1px solid var(--border); border-radius: 7px; cursor: pointer; }
.qr-tab:hover { color: var(--text); }
.qr-tab.on { color: var(--text); border-color: var(--accent-border); background: var(--accent-soft); }
.qr-tab-n { font-family: var(--font-mono); font-size: 10px; opacity: .85; background: rgba(255,255,255,.10); padding: 0 5px; border-radius: 99px; }
/* align-items:start so a multi-line note box grows downward while the check /
   time / seek stay pinned near its first line. */
.qr-row { display: grid; grid-template-columns: auto 56px 1fr auto; align-items: start; gap: 10px; padding: 10px 12px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-raised); }
.qr-row.done { border-color: var(--accent-border); background: var(--accent-soft); }
.qr-check { width: 20px; height: 20px; margin-top: 5px; border-radius: 5px; border: 1.5px solid var(--border-strong); display: flex; align-items: center; justify-content: center; color: transparent; font-size: 12px; }
.qr-row.done .qr-check { background: var(--brand); border-color: var(--brand); color: #fff; }
.qr-time { font-family: var(--font-mono); font-size: 12px; color: var(--red); cursor: pointer; margin-top: 7px; }
/* 2-line default; JS auto-grows it (overflow hidden, no manual resize). */
.qr-input { height: auto; min-height: 48px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); font-family: var(--font-edit); font-size: 12.5px; line-height: 1.4; padding: 6px 9px; width: 100%; box-sizing: border-box; resize: none; overflow: hidden; display: block; }
.qr-seek { color: var(--text-faint); font-size: 13px; padding: 4px; cursor: pointer; margin-top: 4px; }
.qr-count { padding: 6px 4px; font-size: 11px; }

/* ── Quick-review event cards (Deaths / Trades / Teamfights) ───────────────── */
.qc { border: 1px solid var(--border); border-radius: 8px; background: var(--surface-raised); padding: 10px 12px; }
/* A filled (noted) card is signaled by its check mark + type spine, not a loud
   accent wash — just a whisper of tint so it's still glanceable. */
.qc.done { border-color: var(--border); background: rgba(255,255,255,.022); }
.qc-top { display: flex; align-items: center; gap: 9px; }
.qc-check { width: 20px; height: 20px; border-radius: 5px; border: 1.5px solid var(--border-strong); display: flex; align-items: center; justify-content: center; color: transparent; font-size: 12px; flex: none; }
.qc.done .qc-check { background: var(--brand); border-color: var(--brand); color: #fff; }
/* The check is clickable (mark a moment reviewed without writing a note). */
.qc-check { cursor: pointer; transition: border-color 120ms, color 120ms, background-color 120ms; }
.qc:not(.done) .qc-check:hover { border-color: var(--brand); color: var(--brand); }
.qc.done .qc-check:hover { filter: brightness(1.15); }
.qc-time { font-family: var(--font-mono); font-size: 12px; cursor: pointer; flex: none; }
.qc-time.death { color: var(--red); }
.qc-time.trade { color: var(--note-orange); }
.qc-time.fight { color: var(--note-purple); }
.qc-head-meta { display: flex; align-items: center; gap: 7px; margin-left: 2px; min-width: 0; }
.qc-by { font-size: 12px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.qc-by b { color: var(--text); font-weight: 700; }
.qc-seek { margin-left: auto; color: var(--text-faint); font-size: 12px; cursor: pointer; flex: none; }
.dx + .qc-seek { margin-left: 6px; }
.qc .qr-input { margin-top: 10px; }
/* Collapsed note affordance — "Add note" button until there's text (or the
   single-filter flat view, which renders every card note-open). */
.qc:not(.note-open) .qr-input { display: none; }
.qc.note-open .qr-add { display: none; }
.qr-add { display: inline-flex; align-items: center; gap: 5px; margin-top: 7px; padding: 3px 9px; font: inherit; font-size: 11px; color: var(--text-muted); background: var(--surface-raised); border: 1px solid var(--border); border-radius: 6px; cursor: pointer; transition: color .12s, border-color .12s, background .12s; }
.qr-add:hover { color: var(--text); border-color: var(--border-strong); background: rgba(255,255,255,.04); }
.qr-add .pl { font-size: 13px; line-height: 1; }
/* Kill / assist events: green "Killed/Assist …" text (spine already green). */
.qc[data-k="kill"] .qc-by, .qc[data-k="killassist"] .qc-by,
.qc[data-k="kill"] .qc-by b, .qc[data-k="killassist"] .qc-by b { color: var(--green); }
/* Event tags are colored text only — no box (background/border/padding). */
.badge { font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .04em; line-height: 1.5; white-space: nowrap; flex: none; }
.badge.wave { color: var(--note-orange); }
.badge.fight { color: var(--note-purple); }
.badge.solo { color: var(--green); }
.port { display: block; object-fit: cover; border-radius: 7px; border: 1px solid var(--border); flex: none; background-color: var(--bg); }
/* Deaths — killer (red-ringed, skull) + assists cluster in the header. */
.dx { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: none; }
.dx-port { position: relative; }
.dx-port .port { width: 22px; height: 22px; border-radius: 5px; }
.dx-port.killer .port { width: 26px; height: 26px; border-color: var(--red); box-shadow: 0 0 0 1.5px var(--red); }
.dx-port.killer { margin-right: 2px; z-index: 1; }
.dx-skull { position: absolute; right: -4px; bottom: -4px; width: 13px; height: 13px; border-radius: 50%; background: var(--red); border: 1.5px solid var(--surface-raised); display: flex; align-items: center; justify-content: center; font-size: 7px; color: #fff; }
/* Trades — damage taken + an HP bar (green left / hatched-red drop). */
.tx { margin-top: 10px; display: grid; grid-template-columns: auto 1fr; align-items: center; gap: 13px; }
.tx-dmg { display: flex; flex-direction: column; gap: 2px; }
.tx-dmg .v { font-family: var(--font-mono); font-size: 22px; font-weight: 600; line-height: 1; color: var(--red); letter-spacing: -.01em; white-space: nowrap; }
.tx-dmg .l { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: .1em; color: var(--text-faint); }
.tx-hp { display: flex; flex-direction: column; gap: 5px; }
.tx-hp-top { display: flex; align-items: baseline; justify-content: space-between; font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.tx-hp-top .arrow { color: var(--text-faint); }
.tx-hp-top .to { color: var(--red); font-weight: 600; }
.tx-bar { height: 8px; border-radius: 5px; background: var(--bg); border: 1px solid var(--border); overflow: hidden; position: relative; }
.tx-bar .rem { position: absolute; left: 0; top: 0; bottom: 0; background: var(--green); }
.tx-bar .lost { position: absolute; top: 0; bottom: 0; background: repeating-linear-gradient(45deg, rgba(255,93,93,.55) 0 4px, rgba(255,93,93,.28) 4px 8px); }
/* Compact inline trade: from%→to% + the HP bar, on the badge/timestamp line. */
.tx-inline { display: flex; align-items: center; gap: 8px; flex: 1; min-width: 0; margin-left: 2px; }
.txi-pct { display: inline-flex; align-items: baseline; gap: 4px; font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); white-space: nowrap; flex: none; }
.txi-pct .arrow { color: var(--text-faint); }
.txi-pct .to { color: var(--red); font-weight: 600; }
.tx-inline .tx-bar { flex: 1; min-width: 40px; }
/* Recall divider — a ruled break between trades where you went back. */
.recall-div { display: flex; align-items: center; gap: 11px; padding: 5px 2px; }
.recall-div .ln { flex: 1; height: 1px; background: linear-gradient(90deg, transparent, var(--border-strong)); }
.recall-div .ln.r { background: linear-gradient(90deg, var(--border-strong), transparent); }
.recall-pill { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 9px; letter-spacing: .12em; color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: 999px; padding: 3px 9px; white-space: nowrap; }
.recall-pill .gl { font-size: 11px; line-height: 1; }
.recall-pill .t { color: var(--text-muted); }
/* Teamfights — compact stat strip (kills · dmg dealt · outcome) + both rosters. */
.fx-stats { margin-top: 11px; display: flex; align-items: center; gap: 11px; }
.fx-stat { display: flex; align-items: baseline; gap: 5px; }
.fx-stat .v { font-family: var(--font-mono); font-size: 17px; font-weight: 600; line-height: 1; letter-spacing: -.01em; color: var(--text); }
.fx-stat.kills.won .v { color: var(--green); }
.fx-stat.kills.lost .v { color: var(--red); }
.fx-stat .u { font-family: var(--font-mono); font-size: 8.5px; letter-spacing: .1em; color: var(--text-faint); }
.fx-sep { width: 1px; height: 17px; background: var(--border); flex: none; }
.fx-stats .fx-out { margin-left: auto; }
.fx-out { font-family: var(--font-mono); font-size: 8.5px; font-weight: 700; letter-spacing: .06em; padding: 2px 7px; border-radius: 999px; white-space: nowrap; }
.fx-out.won { color: var(--green); background: rgba(77,209,122,.12); border: 1px solid rgba(77,209,122,.34); }
.fx-out.lost { color: var(--red); background: rgba(255,93,93,.12); border: 1px solid rgba(255,93,93,.36); }
.fx-out.even { color: var(--text-muted); background: rgba(255,255,255,.06); border: 1px solid var(--border); }
.fx-rosters { margin-top: 10px; display: flex; flex-direction: column; gap: 8px; }
.fx-team { display: flex; align-items: center; gap: 7px; }
.fx-team .tlab { font-family: var(--font-mono); font-size: 8px; letter-spacing: .08em; width: 30px; flex: none; line-height: 1.2; }
.fx-team.you .tlab { color: var(--accent); }
.fx-team.enemy .tlab { color: var(--red); }
.fx-champs { display: flex; gap: 6px; flex: 1; }
.fx-champ { position: relative; }
.fx-champ .port { width: 30px; height: 30px; border-radius: 6px; }
.fx-team.you .fx-champ .port { box-shadow: inset 0 0 0 1.5px var(--accent-border); }
.fx-team.enemy .fx-champ .port { box-shadow: inset 0 0 0 1.5px rgba(255,93,93,.4); }
.fx-champ.me .port { box-shadow: 0 0 0 2px var(--accent); }
.fx-champ.dead .port { filter: grayscale(.85) brightness(.5); }

/* ── Skirmishes (jungler feed) — kill/assist · skirmish · combat ───────────── */
.qc-time.kill { color: var(--green); }
.qc-time.combat { color: var(--note-orange); }
.qc-time.multi { color: var(--note-purple); }
.badge.combat { color: var(--note-orange); }
.badge.skirmish { color: var(--note-purple); }
.qc-by .you { color: var(--green); font-weight: 700; }
/* Kill / assist — just the champion icons involved, plain: no rings, no skull/
   sword markers, no killer indicator. */
.ka-ics { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: none; }
.ka-ics .port { width: 22px; height: 22px; border-radius: 5px; }
.ka-ics + .qc-seek { margin-left: 6px; }
/* Single kill / assist — the enemy who died + the allies credited, inline. */
.sk-vic { margin-left: auto; display: flex; align-items: center; gap: 4px; flex: none; }
.sk-vp { position: relative; }
.sk-vp .port { width: 22px; height: 22px; border-radius: 5px; box-shadow: 0 0 0 1.5px var(--accent-border); }
.sk-vp.victim .port { width: 26px; height: 26px; border-color: var(--red); box-shadow: 0 0 0 1.5px var(--red); filter: grayscale(.85) brightness(.55); }
.sk-vp.killer .port { width: 24px; height: 24px; }
.sk-sep { width: 1px; height: 16px; background: var(--border); margin: 0 3px; flex: none; }
.sk-mk { position: absolute; right: -4px; bottom: -4px; width: 13px; height: 13px; border-radius: 50%; border: 1.5px solid var(--surface-raised); display: flex; align-items: center; justify-content: center; font-size: 7px; color: #fff; font-family: var(--font-mono); }
.sk-mk.die { background: var(--red); }
.sk-mk.kill { background: var(--accent); }
.sk-vic + .qc-seek, .sk-dmg + .qc-seek { margin-left: 6px; }
/* Multi-kill skirmish — damage dealt + just the fallen. */
.sk-dmg { margin-left: auto; font-family: var(--font-mono); font-size: 14px; font-weight: 600; color: var(--green); flex: none; }
.sk-dmg .u { font-size: 9px; color: var(--text-faint); font-weight: 600; margin-left: 3px; }
/* All participating champions on ONE line: your team · divider · theirs
   (you ringed accent, the fallen greyed). */
.sk-fallen { margin-top: 9px; display: flex; align-items: center; gap: 7px; }
.sk-fchamps { display: flex; gap: 4px; }
.sk-fchamps .port { width: 24px; height: 24px; border-radius: 5px; }
.sk-fchamps.ally .port { box-shadow: inset 0 0 0 1.5px var(--accent-border); }
.sk-fchamps.enemy .port { box-shadow: inset 0 0 0 1.5px rgba(255,93,93,.4); }
.sk-fchamps .port.dead { filter: grayscale(.85) brightness(.5); }
.sk-vs { width: 1px; height: 20px; background: var(--border-strong); flex: none; margin: 0 2px; }
/* Combat — compact inline dealt/taken metric (smallest skirmish-feed card). */
.ck { margin-left: auto; display: flex; align-items: baseline; gap: 11px; flex: none; font-family: var(--font-mono); }
.ck .m { font-size: 14px; font-weight: 600; }
.ck .m.dealt { color: var(--green); }
.ck .m.recv { color: var(--red); }
.ck .m .u { font-size: 9px; color: var(--text-faint); font-weight: 600; margin-left: 2px; }
.ck + .qc-seek { margin-left: 6px; }

/* ════════════════════════════════════════════════════════════════════
   UNIFIED EVENTS FEED — phases are containers, events nested inside
   (merges the old Phases + Events tabs into one). Ported from the
   "Unified Events Tab" design handoff.
   ════════════════════════════════════════════════════════════════════ */
.ef-head { display: flex; align-items: center; gap: 8px; padding: 11px 12px 7px; flex: none; }
.ef-head .ef-title { font-size: 12.5px; font-weight: 700; color: var(--text); }
/* Pulsing yellow dot while the background OCR + minimap passes still read the
   recording — CS counts / locations may still fill in. (Square, like every
   other dot: the squared design zeroes all radii via `body *`.) */
/* Blinked by review.js (setAnalysisBusy toggles .dim every 700 ms) — not an
   infinite CSS pulse, which dirtied every display frame for the whole
   minutes-long analysis pass. */
.ef-head .ef-busy { width: 8px; height: 8px; flex: none; background: var(--gold, #f0c244); box-shadow: 0 0 6px rgba(240, 194, 68, .55); }
.ef-head .ef-busy.dim { opacity: .35; }
.ef-head .ef-sub { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); margin-left: auto; }
.ef-filters { display: flex; flex-wrap: wrap; gap: 5px; padding: 0 12px 9px; border-bottom: 1px solid var(--border); flex: none; }
.ef-chip { display: inline-flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 10px; font-weight: 600; color: var(--text-muted); background: var(--surface-raised); border: 1px solid var(--border); border-radius: 999px; padding: 3px 9px; cursor: pointer; user-select: none; }
.ef-chip .d { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.ef-chip.off { opacity: .4; }
/* The selected filter must read at a glance: accent ring + tint + full text. */
.ef-chip.on { color: var(--text); font-weight: 700; border-color: var(--accent-border); background: var(--accent-soft); box-shadow: 0 0 0 1px var(--accent-border); }
/* flat list shown when a single event type is selected */
.ef-flat { display: flex; flex-direction: column; gap: 7px; }
/* Jungler-start banner — where each team's jungler started (ally left, enemy right). */
.jgs-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 7px 9px; margin-bottom: 4px; background: var(--surface-raised); border: 1px solid var(--border); }
.jgs { display: flex; align-items: center; gap: 6px; font-size: 12px; min-width: 0; }
.jgs .port { width: 22px; height: 22px; border-radius: 5px; flex: none; }
.jgs .nm { font-weight: 700; color: var(--text); white-space: nowrap; }
.jgs.enemy { justify-content: flex-end; text-align: right; }
.jgs.enemy .nm { color: var(--red); }
.jgs .vb { font-family: var(--font-mono); font-size: 11px; }
.jgs .vb.started { color: var(--accent); }
.jgs .vb.invaded { color: var(--red); font-weight: 700; }
.jgs .loc { color: var(--text); font-weight: 600; white-space: nowrap; }
.ef-feed { flex: 1; min-height: 0; overflow-y: auto; padding: 6px 10px 14px; display: flex; flex-direction: column; gap: 4px; }
.ef-empty { padding: 14px 12px; }

/* Phase section: a header (always shown) + its nested events. */
/* The phase-number glyph is a button that doubles as the color-mark trigger.
   No per-kind color — wave/obj/jg/recall all render neutral; a mark applies its
   color inline (to both glyph and name). */
/* Phase-name color mark — a dot before the name opens a swatch popover. */
.ph-sub { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.ph-cs { font-family: var(--font-mono); font-size: 9.5px; color: var(--text-muted); background: var(--bg); border: 1px solid var(--border); border-radius: 4px; padding: 1px 5px; white-space: nowrap; flex: none; }
.ph-out { font-family: var(--font-mono); font-size: 8.5px; font-weight: 700; letter-spacing: .05em; padding: 2px 7px; border-radius: 999px; white-space: nowrap; }
.ph-out.good { color: var(--green); background: rgba(77,209,122,.12); border: 1px solid rgba(77,209,122,.32); }
.ph-out.bad  { color: var(--red); background: rgba(255,93,93,.12); border: 1px solid rgba(255,93,93,.34); }

/* Stage divider (Laning / Objectives). */

/* Objective marker — NEUTRAL by default; each take colors ITSELF by its own
   result (secured/lost/early), independent of the phase's weighted spine. So a
   secured Drake stays green even if a higher-weight Baron was lost in the phase. */
.obj-take { display: flex; align-items: center; gap: 9px; padding: 6px 10px; border: 1px solid var(--border); border-radius: 7px; background: rgba(255,255,255,.02); }
/* background-COLOR, not the `background` shorthand: the shorthand resets
   background-size back to `auto`, which made the .has-img portrait render at its
   native 128px (cropped huge) on won/lost cards. */
.obj-take .ot-icon { width: 21px; height: 21px; border-radius: 5px; display: flex; align-items: center; justify-content: center; font-size: 11px; color: var(--text-muted); background-color: var(--bg); border: 1px solid var(--border); flex: none; }
/* Real objective portrait (same art as the timeline marker) instead of the glyph. */
.obj-take .ot-icon.has-img { background-size: cover; background-position: center; color: transparent; }
.obj-take .ot-name { font-size: 12px; font-weight: 700; color: var(--obj-pink); white-space: nowrap; }
/* Objective names read in the objective purple wherever they appear. */
/* "Sequence X of Y" header above each sequence (.trip) in the events feed. */
.obj-take .ot-sub { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.obj-take .ot-res { display: inline-flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: .05em; }
.obj-take .ot-res .d { width: 6px; height: 6px; border-radius: 50%; }
.obj-take .ot-time { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); cursor: pointer; flex: none; }
/* per-objective result — secured(green) / lost(red); early-game stays neutral grey */
/* Secured/lost keep the green/red OUTLINE but NOT a colour wash — the body reads
   the same neutral as a normal event row (only the border + result tag are tinted). */
.obj-take.won { border-color: rgba(77,209,122,.3); background: rgba(255,255,255,.02); }
.obj-take.won .ot-icon { color: var(--green); background-color: rgba(77,209,122,.12); border-color: rgba(77,209,122,.3); }
.obj-take .ot-res.won { color: var(--green); }
.obj-take .ot-res.won .d { background: var(--green); }
.obj-take.lost { border-color: rgba(255,93,93,.28); background: rgba(255,255,255,.02); }
.obj-take.lost .ot-icon { color: var(--red); background-color: rgba(255,93,93,.1); border-color: rgba(255,93,93,.28); }
.obj-take .ot-res.lost { color: var(--red); }
.obj-take .ot-res.lost .d { background: var(--red); }
.obj-take .ot-res.early { color: var(--text-muted); }
.obj-take .ot-res.early .d { background: var(--text-muted); }

/* Recall — head-to-head CS / KDA table attached to the wave it happened in. */

/* ════════════════════════════════════════════════════════════════════
   EVENTS FEED v2 — visual hierarchy. Three reading tiers, by color + size:
     1 · TRIPS    — base→recall runs banded into one neutral container with the
                    recall summary as its headline (objectives flag win/loss). Read first.
     2 · CAUSES   — event cards carry a colored type spine (death/trade/kill/…).
     3 · DETAIL   — timestamps + raw damage demoted to faint. Read last.
   Layered after the base feed so it re-weights it. From the v2 handoff.
   ════════════════════════════════════════════════════════════════════ */
/* Outcome palette — set on the trip, inherited by its children. --spine is the
   left-border color: red on a lane/recall run that contains a death, slate
   otherwise, the win/loss color on objectives. --oc tints only contained bits
   (recall summary, obj-take) and stays NEUTRAL on lane runs, so a death reddens
   the spine WITHOUT washing the block (the earlier over-coloring complaint). */
/* Objective spines are toned (≈50% alpha) so they read as a quiet accent, not a
   shout — they were pulling focus from the events themselves. */

/* TIER 1 — every trip carries a colored left SPINE only (no background wash): a
   death reddens a lane/recall run, objectives show their win/loss, the rest stay
   neutral slate. The recall summary inside stays neutral on lane runs. */
/* inside a trip the per-phase recall divider is redundant — the trip bounds it */

/* The recall summary is the trip headline: a solid card (neutral on lane runs —
   no outcome implied) with a labelled head bar (↺ Recall + CS/K/D/A). */
/* Right-pad the labels by their data cell's trailing track (CS frac = 22px, KDA
   delta+gap = 19px) so each label sits directly over its NUMBER, not the column
   edge — holds for laners (CS "52/62") and junglers (plain CS) alike. */
/* Objective phase-end summary reuses the recall-sum shell; its header is a quiet
   "PHASE END" label (no ↺ glyph). */

/* Quiet-wave group — a small collapsible "Waves N–M" table; each row is a
   compact .ph-sec.wave-row (still notable + recolorable). */

/* (obj-take coloring is per-objective now — see the .obj-take.won/.lost rules
   above; the phase's weighted outcome shows only on the trip spine.) */

/* TIER 2 — causes: a colored type spine on every event card carries the kind. */
.qc { border-left-width: 3px; border-left-style: solid; }
.qc, .qc.done { border-left-color: var(--type-c, var(--border)) !important; }
.qc[data-k="death"]    { --type-c: var(--red); }
.qc[data-k="trade"]    { --type-c: var(--note-gold); }
.qc[data-k="kill"]     { --type-c: var(--green); }
.qc[data-k="fight"]    { --type-c: var(--note-purple); }
.qc[data-k="skirmish"] { --type-c: var(--note-gold); }
/* A 1vX files under Fights (data-k="fight") but keeps the kill green. */
.qc.solo               { --type-c: var(--green); }
/* A skirmish/teamfight the player died in reads as a death — red spine. */
.qc.me-died            { --type-c: var(--red); }
/* Keyboard-nav (1/2/3) selection — the current seek stop in the feed. A clear,
   unmistakable "selected" state: accent outline + soft fill so it reads at a
   glance on any card, including a single sub-event (map pin / no-events row). */
.kb-sel { outline: 2px solid var(--accent); outline-offset: 1px; border-radius: 6px; background: var(--accent-soft); }
/* Sub-events selected on their own (a map pin, a no-events sample row) get a
   left accent bar + fill so it's obvious WHICH entry inside the card is picked. */
/* Out of focus the feed passively follows the playhead: its highlight reads as
   the playhead position (dashed) rather than a deliberate user selection (solid). */
/* A selected stop must never be left in a dimmed (out-of-focus) sequence — e.g.
   a Sidelaning / Clearing Mid card the user just navigated to. Keep its sequence
   at full strength so the selection is visible even before the focus scroll lands. */
/* A dimmed sequence holding the CURRENT selection stays discoverable (the
   original "highlight-through" fix) but must still read as dimmed — full
   opacity here made the sequence above look like it "failed to dim" after
   scrolling away from a selection. Matches the hover-raise level. */
/* trade + combat tags go gold (skirmishes are Fights now → purple, set earlier) */
.badge.wave, .badge.combat { color: var(--note-gold); }

/* TIER 3 — detail: timestamps go quiet (no semantic color) + warm on hover; raw
   damage is demoted so it no longer out-shouts the outcome. */
.qc-time { color: var(--text-faint) !important; font-weight: 400; font-size: 11px; }
.qc:hover .qc-time { color: var(--text-muted) !important; }
.obj-take .ot-time { font-weight: 400; }
.tx-dmg .v { font-size: 13px; font-weight: 500; color: var(--text-muted); }
.qc .ck .m.dealt, .qc .ck .m.recv { font-size: 12px; font-weight: 500; color: var(--text-muted); }
.qc .ck .m .u { color: var(--text-faint); }
.fx-stats .fx-stat:not(.kills) .v { font-size: 13px; font-weight: 500; color: var(--text-muted); }
.fx-stat .v, .sk-dmg, .tx-hp-top { white-space: nowrap; }
/* the recall pill on the wave header is redundant beside the recall head bar */

/* ── Events Tab — role variants (wards / base / no-events / locations) ────── */
/* Ward marker — cyan eye + count; on wave headers and fight cards. */
.ph-ward { display: inline-flex; align-items: center; gap: 2px; height: 20px; padding: 0 5px; border-radius: 5px; color: var(--cyan); cursor: pointer; border: 1px solid transparent; flex: none; }
.ph-ward:hover { background: rgba(0,212,212,.12); border-color: rgba(0,212,212,.34); }
.ph-ward svg { width: 14px; height: 14px; display: block; }
.ph-ward .n { font-family: var(--font-mono); font-size: 9px; font-weight: 700; }

/* Ward-placement modal — one framed screenshot per ward. */
.modal-card:has(.ward-shots) { max-width: 600px; width: 92vw; }
.modal-card:has(.ward-shots) .modal-body { max-height: 76vh; overflow-y: auto; }
.ward-shots { display: flex; flex-direction: column; gap: 16px; }
.ward-shot { margin: 0; }
.ward-frame { position: relative; aspect-ratio: 16 / 9; border-radius: 8px; overflow: hidden; border: 1px solid var(--border); background: radial-gradient(130% 130% at 32% 22%, #161925, #090a0f 72%); display: flex; align-items: center; justify-content: center; }
.ward-frame::after { content: ""; position: absolute; inset: 0; pointer-events: none; background-image: repeating-linear-gradient(135deg, rgba(255,255,255,.022) 0 11px, transparent 11px 22px); }
.ward-frame.has-frame::after { background-image: none; }
.ward-frame.has-frame .ward-eye { display: none; }
/* Frame canvas from the hidden-video grab (same pipeline as the quick
   switcher) — displayed directly; a tainted canvas still renders fine. */
.ward-canvas { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.ward-eye { position: relative; z-index: 1; color: var(--cyan); filter: drop-shadow(0 0 12px rgba(0,212,212,.55)); }
.ward-eye svg { width: 42px; height: 42px; display: block; }
.ward-stamp { position: absolute; left: 10px; bottom: 9px; z-index: 1; font-family: var(--font-mono); font-size: 10px; letter-spacing: .1em; color: var(--text-muted); background: rgba(8,9,14,.62); border: 1px solid var(--border); border-radius: 4px; padding: 2px 7px; }
.ward-drop { position: absolute; right: 10px; bottom: 9px; z-index: 1; font-family: var(--font-mono); font-size: 9px; letter-spacing: .06em; color: var(--text-faint); }
.ward-cap { display: flex; align-items: center; gap: 10px; margin-top: 8px; }
.ward-cap .wt { font-size: 12.5px; font-weight: 700; color: var(--cyan); }
.ward-cap .wv { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); }
.ward-cap .wtime { margin-left: auto; font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); cursor: pointer; }
.ward-cap .wtime:hover { color: var(--text); }

/* Vision event card (support objective phases). */
.qc-time.ward { color: var(--cyan); }
.badge.ward { color: var(--cyan); }

/* Base event (buy/respawn → where the champ was 25s later). */
/* The ⌂ glyph's ink sits low in its em box (measured +1.5px vs the row's digits
   once the boxes are centered), so nudge it up to truly centre with the text. */
.qc-time.base { color: var(--gold); }
.badge.base { color: var(--gold); font-weight: 700; }
/* Destination confirmed by the minimap tracker (video read, not a coarse
   match-v5 anchor) — brighter, with a tiny accent tick. */
/* Tracker refused the recording (starved calibration) — quiet one-liner. */
.trk-note { font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); padding: 4px 8px; margin-bottom: 8px; border: 1px dashed var(--border); }

/* No-events gap card. */
/* No-events sample stack — timestamp + location trace across the quiet window. */
/* Row location read off the minimap track (video, not a coarse match-v5
   anchor) — same treatment as .base-loc.video. */
/* When the stack merges under a Base card, tint the guide gold; the card itself
   stops being one big seek target (its rows seek instead). */



/* Teamfight / skirmish location (relative region name). */
.fx-loc { font-size: 11px; color: var(--text-muted); display: inline-flex; align-items: center; gap: 4px; white-space: nowrap; }
.fx-loc::before { content: ""; width: 5px; height: 5px; border-radius: 50%; border: 1.5px solid var(--note-purple); }

/* ════════════════════════════════════════════════════════════════════
   EVENTS TAB — handoff 18: recolored events/locations, farming + no-events
   minimap + spawn cards, kill/death locations, and sequence-focus docked
   scrolling. Layered LAST so its color re-weighting wins over the base feed.
   ════════════════════════════════════════════════════════════════════ */
/* Location indicators read white across the feed (kill/death + fight/skirmish). */
.fx-loc { color: var(--text) !important; }
.fx-loc::before { border-color: var(--text) !important; }
.fx-loc.kd-loc { margin-left: 1px; }
/* In an event row the location must YIELD before the play button (.qc-seek) does
   — on a narrow feed a nowrap location + fixed-width champion icons used to push
   the ▶ past the feed's clipped right edge and off-screen. As a shrinkable,
   ellipsising flex item it gives up width first, so the ▶ always stays visible.
   Scoped to .qc-top so the fight/skirmish detail's .fx-loc is untouched. The dot
   becomes an inline-block marker (the flex `gap` no longer applies once it's a
   block). */
.qc-top .fx-loc { display: block; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.qc-top .fx-loc::before {
  display: inline-block; vertical-align: middle; margin: -1px 4px 1px 0; flex: none;
}
/* Safety net for a genuinely tiny window: once the location has fully collapsed,
   the champion-icon group is the next thing that would push the ▶ off. Let it
   shrink and clip its rightmost icons instead — losing an icon is far better
   than losing the play button. Only ever engages under overflow, so normal
   widths are unchanged. */
.qc-top .ka-ics { flex-shrink: 1; min-width: 0; overflow: hidden; }
/* Same last-resort for the event badge (No events / Teamfight / …): it's the
   final fixed-width block that can push the ▶ off on very narrow rows, so let it
   ellipsise too rather than clip the play button. Engages only under overflow. */
.qc-top .badge { flex-shrink: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Trades + Combat read orange (spine + badge). */
.qc[data-k="trade"], .qc[data-k="skirmish"] { --type-c: var(--note-orange); }
.badge.wave, .badge.combat { color: var(--note-orange) !important; }
/* Kill / assist events: green "Killed/Assist …" text (spine is already green). */
.qc[data-k="kill"] .qc-by, .qc[data-k="killassist"] .qc-by,
.qc[data-k="kill"] .qc-by b, .qc[data-k="killassist"] .qc-by b { color: var(--green); }
/* Base + spawn identity → blue (was gold). */
.base-ic, .qc-time.base { color: var(--note-blue); }
.badge.base { color: var(--note-blue); font-weight: 700; }

/* Farming event (jungle "Farmed X CS in Y" · lane "Sidelaning/Clearing"). */
/* Sidelaning/Clearing: the title reads WHITE and the CS count GREEN (the regular
   farming card keeps its green title — this only re-weights the side-lane card). */
/* Every duration/time amount across the feed is blue (spawn's countdown already
   is, via .spawn-label b). `b.t-amt` ties .farm-label b / .farm-path b on
   specificity, so it must sit AFTER them to win. */
b.t-amt { color: var(--note-blue); font-weight: 700; }

/* Objective spawn countdown ("60 seconds until X spawns"). */

/* No-Events minimap card — map on the left, numbered seekable list on the
   right. Headerless (handoff 19): no title row, and the spine reads neutral
   (border color) instead of blue — the minimap + pins ARE the card. */
.mini-map { flex: none; width: 124px; height: 124px; border-radius: 10px; }
/* CS gained by each No-Events entry (cumulative from the window start). */
/* Support: a ward placed during a No-Events pin's span shows its vision icon
   where CS sits for laners (far right of the row). */
/* A quiet wave whose HP fell ≥5% from the previous wave reads orange + bold. */

/* Objective secured/lost during a fight — a tag under the fight's breakdown. */
.fx-objs { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--border); }
.fx-obj-tag { display: inline-flex; align-items: center; gap: 6px; padding: 3px 9px 3px 7px; border-radius: 6px; font-size: 11px; font-weight: 600; border: 1px solid transparent; cursor: pointer; }
.fx-obj-tag .ot-icon { font-size: 12px; line-height: 1; }
/* Real objective portrait (same art as the timeline marker) in the fight tag. */
.fx-obj-tag .ot-icon.has-img { width: 16px; height: 16px; border-radius: 4px; background-size: cover; background-position: center; color: transparent; flex: none; }
.fx-obj-tag .fot-name { color: var(--text); font-weight: 700; }
.fx-obj-tag .fot-sub { font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); }
.fx-obj-tag .fot-res { font-family: var(--font-mono); font-size: 9.5px; font-weight: 700; letter-spacing: .05em; }
.fx-obj-tag.secured { background: rgba(77,209,122,.1); border-color: rgba(77,209,122,.32); }
.fx-obj-tag.secured .ot-icon, .fx-obj-tag.secured .fot-res { color: var(--green); }
.fx-obj-tag.lost { background: rgba(255,93,93,.1); border-color: rgba(255,93,93,.32); }
.fx-obj-tag.lost .ot-icon, .fx-obj-tag.lost .fot-res { color: var(--red); }


/* ════════════════════════════════════════════════════════════════════
   GAME INFO
   ════════════════════════════════════════════════════════════════════ */
/* Three flex-none/flex-1/flex-none bands: objective+gold bar, the stage that
   holds the scoreboard and the map on top of each other, and the map toggle. */
.notes-tab-body[data-tab-body="info"] { overflow: hidden; }
.gi-flex { flex: 1; }
.gi-lbl { font-family: var(--font-mono); font-size: 8.5px; color: var(--text-faint); }
.gi-dot { color: var(--text-faint); font-size: 9px; }
.gi-sec-title { font-family: var(--font-mono); font-size: 10px; letter-spacing: .02em; color: var(--text-faint); }
.gi-sec-title::before { content: "// "; color: var(--accent); }

/* (The objective-timer + team-gold bar that led this tab moved to the
   timeline header on 2026-09-10 — see .timeline-header.) */

/* ── Stage: board + map share one box; the map slides up over the board ─── */
.gi-stage { flex: 1; min-height: 0; position: relative; overflow: hidden; }
/* giSetMapOpen toggles this; without it the panel only ever sat translated
   off the bottom (the rule was missing — "the map pull-up shows nothing"). */
.gi-stage.map-open .gi-map { transform: none; }
.gi-board, .gi-map { position: absolute; inset: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 8px; }
.gi-board { padding: 10px 10px 12px; }
.gi-map { padding: 10px; background: var(--surface); border-top: 1px solid var(--border); transform: translateY(101%); transition: transform .18s ease; }
.gi-sec-head { display: flex; align-items: center; gap: 8px; flex: none; }
/* The "// scoreboard" row gives way when the ten rows would not fit under it
   (a pool VOD's player card eats the height) — giFitHead() sets the class. */
.gi-board.gi-tight > .gi-sec-head { display: none; }
.gi-live { font-family: var(--font-mono); font-size: 9.5px; color: var(--text-faint); }
.gi-live b { color: var(--text); font-weight: 400; }

/* ── Per-team strip: drakes / grubs / an active baron-or-elder buff ─────── */
.gi-strip { flex: none; border: 1px solid var(--border); background: var(--surface-raised); padding: 4px 10px; }
.gi-strip.gi-blue { border-left: 2px solid var(--accent); }
.gi-strip.gi-red { border-left: 2px solid var(--red); }
.gi-strip-in { display: flex; align-items: center; gap: 6px; height: 18px; }
.gi-pips { display: flex; gap: 3px; }
.gi-pip { width: 7px; height: 7px; box-sizing: border-box; background: transparent; border: 1px solid var(--border-strong); }
.gi-blue .gi-pip.on { background: var(--accent); border-color: transparent; }
.gi-red .gi-pip.on { background: var(--red); border-color: transparent; }
.gi-num { font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; color: var(--text); }
.gi-buffv { font-family: var(--font-mono); font-size: 10.5px; font-weight: 700; }

/* ── Scoreboard rows ───────────────────────────────────────────────────── */
.gi-rows { flex: none; border: 1px solid var(--border); border-bottom: none; }
/* The gutters are grid columns of their own, so extra width spreads across the
   row instead of piling up in the middle: the edges grow to 16px and stop,
   the icon run keeps fixed gaps (7 · 7 · 10 · 10, divider centred in the
   last), and EVERYTHING else goes to the trinket→score gap. The edges fall
   back to 8px only when the pane is too narrow for 16. */
.gi-row {
  position: relative; display: grid; align-items: center; padding: 6px 0; border-bottom: 1px solid var(--border);
  grid-template-columns: minmax(8px,16px) 34px 7px 16px 7px 19px 10px auto 10px 23px minmax(7px,1fr) auto minmax(8px,16px);
}
.gi-row > .gi-portwrap { grid-column: 2; }
.gi-row > .gi-spells { grid-column: 4; }
.gi-row > .gi-runes { grid-column: 6; }
.gi-row > .gi-name { display: none; }
.gi-row > .gi-mid { grid-column: 8; }
.gi-row > .gi-idiv { grid-column: 9; justify-self: center; }
.gi-row > .gi-trinket { grid-column: 10; }
.gi-row > .gi-stat { grid-column: 12; }
/* Name + full rank between the runes and the inventory, once the board is wide
   enough to hold a 16-character Riot ID (104px column + its 10px gutter) on
   top of the compact row. The board is the query container. */
.gi-board { container-type: inline-size; }
.gi-name { flex-direction: column; justify-content: center; gap: 2px; min-width: 0; }
.gi-nm { font-family: var(--font-mono); font-size: 10.5px; font-weight: 600; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.gi-rk { font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
@container (min-width: 512px) {
  .gi-row { grid-template-columns: minmax(8px,16px) 34px 7px 16px 7px 19px 10px 104px 10px auto 10px 23px minmax(7px,1fr) auto minmax(8px,16px); }
  .gi-row > .gi-name { display: flex; grid-column: 8; }
  .gi-row > .gi-mid { grid-column: 10; }
  .gi-row > .gi-idiv { grid-column: 11; }
  .gi-row > .gi-trinket { grid-column: 12; }
  .gi-row > .gi-stat { grid-column: 14; }
}
/* YOUR row: the tinted fill (plus the accent portrait border below). */
.gi-row.you { background: var(--accent-soft); }
.gi-portwrap { position: relative; width: 34px; height: 34px; }
.gi-port { position: absolute; inset: 0; background-size: cover; box-sizing: border-box; border: 1px solid rgba(128,128,234,.45); padding: 0; cursor: pointer; }
.gi-red-rows .gi-port { border-color: rgba(255,93,93,.4); }
.gi-row.you .gi-port { border-color: var(--accent); }
.gi-port:hover { border-color: var(--text); }
.gi-lvl { position: absolute; right: -4px; bottom: -4px; min-width: 13px; height: 13px; padding: 0 2px; box-sizing: border-box; background: var(--bg); border: 1px solid var(--border-strong); font-family: var(--font-mono); font-size: 8.5px; font-weight: 600; line-height: 1; color: var(--text); display: flex; align-items: center; justify-content: center; }
.gi-col { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.gi-spell { width: 16px; height: 16px; box-sizing: border-box; background-color: var(--bg); background-size: cover; border: 1px solid var(--border); }
.gi-key { width: 19px; height: 19px; box-sizing: border-box; background-color: var(--bg); background-size: cover; border: 1px solid var(--border); }
.gi-tree { width: 12px; height: 12px; background-size: cover; opacity: .85; }
.gi-mid { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.gi-items { display: flex; align-items: center; gap: 2px; }
.gi-slot { width: 19px; height: 19px; flex: none; box-sizing: border-box; border: 1px solid var(--border-strong); background-color: var(--bg); background-size: cover; }
.gi-trinket { width: 23px; height: 23px; }
/* NOT `.empty` — styles.css has a global `.empty` empty-state block (60px of
   padding) that blew every vacant slot up into a tall black box. */
.gi-slot.gi-vacant { border-color: var(--border); opacity: .65; }
.gi-idiv { width: 1px; height: 13px; background: var(--border-strong); }

/* Time since the last shop trip, with the trip itself on hover. */
.gi-recall { position: relative; width: fit-content; display: flex; align-items: center; gap: 4px; font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); cursor: default; }
.gi-recall .r { color: var(--accent); }
.gi-buy { position: absolute; left: 0; bottom: calc(100% + 6px); z-index: 20; display: none; flex-direction: column; gap: 6px; padding: 8px 10px; background: var(--surface-raised); border: 1px solid var(--border-strong); box-shadow: 0 12px 32px rgba(8,9,14,.5); white-space: nowrap; }
.gi-recall:hover .gi-buy { display: flex; }
.gi-buy .l { font-family: var(--font-mono); font-size: 8.5px; color: var(--text-faint); }
.gi-buy .l b { color: var(--text); font-size: 10px; font-weight: 600; }
.gi-buy .gi-buyitems { display: flex; gap: 2px; }
.gi-buy .gi-buyitems span { width: 16px; height: 16px; flex: none; box-sizing: border-box; border: 1px solid var(--border-strong); background-color: var(--bg); background-size: cover; }

/* Stat column — kda pair or the damage readout, one visible at a time. */
.gi-stat { display: flex; flex-direction: column; justify-content: center; min-width: 70px; }
.gi-stat > * { display: none; }
.gi-board[data-stat="kda"] .gi-kda, .gi-board[data-stat="dmg"] .gi-dmg { display: flex; }
.gi-kda { flex-direction: column; align-items: flex-end; gap: 2px; }
.gi-kda .v { font-family: var(--font-mono); font-size: 13.5px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -.01em; white-space: nowrap; color: var(--text); }
.gi-kda .v .s { color: var(--text-faint); }
.gi-kda .v .d { color: var(--red); }
.gi-kda .v .a { color: var(--text-muted); }
.gi-kda .n { font-family: var(--font-mono); font-size: 9px; color: var(--text-muted); white-space: nowrap; }
.gi-dmg { flex-direction: column; align-items: flex-start; gap: 3px; width: 62px; flex: none; }
.gi-dmg .v { font-family: var(--font-mono); font-size: 13.5px; font-weight: 600; color: var(--text); white-space: nowrap; }
.gi-dmg .v i { font-style: normal; font-size: 9px; font-weight: 600; color: var(--text-faint); }
.gi-dmg .bar { width: 56px; height: 5px; background: var(--bg); border: 1px solid var(--border); box-sizing: border-box; display: flex; }
.gi-dmg .bar span { background: var(--red); }

/* ── Map panel ─────────────────────────────────────────────────────────── */
/* The map takes the largest square the panel offers — its inner width, or the
   height left beside the header, stepper and hint — and never less than 320px
   (giFitMap sets --gi-map-w). The header, stepper and hint share that width so
   the whole panel reads as one centered block. */
.gi-map > * { width: var(--gi-map-w, 320px); max-width: 100%; align-self: center; }
/* Baron / dragon pit anchors — the two landmarks a position is read against. */
.gi-pit { position: absolute; width: 12px; height: 12px; transform: translate(-50%,-50%); background: rgba(20,21,30,.55); border: 1px solid rgba(167,179,196,.5); }
.gi-trail { position: absolute; width: 3px; height: 3px; transform: translate(-50%,-50%); background: var(--accent); opacity: .8; }
/* Pins are small here on purpose: identity is one glance away on the board, so
   the map is read for SHAPE (who is where, together or split). You and your lane
   counterpart get the extra size + the front layer. */
.gi-snapnav { flex: none; display: flex; align-items: center; gap: 8px; }
.gi-snapnav .snapshot-link { flex: 1; text-align: center; font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); white-space: nowrap; }
.gi-snapnav .snapshot-link em { font-style: normal; color: var(--accent); }
.gi-snapnav .snapshot-link b { color: var(--text); font-weight: 600; }
.gi-snapnav .snap-nav { width: 26px; height: 22px; flex: none; padding: 0; display: flex; align-items: center; justify-content: center; background: var(--surface-raised); border: 1px solid var(--border); color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; cursor: pointer; }
.gi-snapnav .snap-nav:hover:not(:disabled) { border-color: var(--accent-border); color: var(--text); background: var(--surface-raised); }
.gi-snapnav .snap-nav:disabled { opacity: .35; cursor: default; }
.gi-maphint { flex: none; font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); }

/* ── Map toggle bar ────────────────────────────────────────────────────── */
.gi-minbar { flex: none; display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 12px; border-top: 1px solid var(--border); background: var(--surface-raised); cursor: pointer; user-select: none; }
.gi-minbar:hover { background: var(--surface-hover); }
.gi-min-meta { font-family: var(--font-mono); font-size: 9.5px; color: var(--text-faint); }
.gi-min-act { font-family: var(--font-mono); font-size: 10px; color: var(--accent); }

/* ════════════════════════════════════════════════════════════════════
   THEATER (fullscreen) — non-blocking, bottom-docked controls.
   A flex column: the video fills everything above a solid control bar docked
   at the bottom. Nothing overlays the footage: the old gradient chrome sat
   over the top + bottom of the screen and auto-hid on pointer distance, which
   meant a coach drawing over the minimap kept summoning the bar back on top
   of it. The summoned panels (phase navigator, note capture) still float above
   the control bar — they're transient and dismissable, so they can't trap you.
   ════════════════════════════════════════════════════════════════════ */
.theater { position: fixed; inset: 0; z-index: 40; background: #000; display: none; }
.theater.on { display: flex; flex-direction: column; background: var(--bg); }
/* inset/position here cancel styles.css's absolute-overlay base — the video
   host is in flow now, and the bar below it overlays nothing. */
.th-videohost { position: relative; inset: auto; flex: 1; min-height: 0; }
.th-videohost video { width: 100%; height: 100%; object-fit: contain; }
.theater.on .th-videohost { cursor: pointer; }
.th-btn { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; font-weight: 600; color: #fff; background: rgba(20,21,30,.6); border: 1px solid var(--border-strong); border-radius: 7px; padding: 8px 13px; }
.th-btn:hover { background: rgba(30,31,42,.8); }
/* Fixed height so --th-ctrl-h (and the panels docked off it) can't drift; the
   -3px margin lets the video's bottom edge tuck under the scrubber, which is
   what makes the scrubber read as part of the picture rather than a shelf. */
/* user-select: none — the control bar is for dragging (scrub) and clicking, not
   selecting. A stray text/element selection over the thin scrubber used to hijack
   pointer capture, leaving the bar "scrubbing" so a bare hover kept seeking. */
.th-controls { position: relative; z-index: 3; flex: none; box-sizing: border-box; height: 58px; padding: 0 20px 8px; margin-top: -3px; background: var(--surface); user-select: none; -webkit-user-select: none; }
.th-scrub { position: relative; z-index: 1; height: 6px; border-radius: 3px; background: rgba(255,255,255,.13); margin: 0 0 8px; cursor: pointer; touch-action: none; }
/* The visible bar is only 6px; extend the pointer target above/below (into the
   dead space over the bar and its bottom margin) so it's easy to land on. */
.th-scrub::before { content: ''; position: absolute; left: 0; right: 0; top: -10px; bottom: -7px; }
.th-fill { position: absolute; left: 0; top: 0; bottom: 0; background: var(--accent); border-radius: 3px; }
.th-knob { position: absolute; top: 50%; width: 13px; height: 13px; border-radius: 50%; background: #fff; transform: translate(-50%,-50%); }
/* Hover/scrub timestamp bubble — shows where a click would seek to (and confirms
   the cursor is on the bar). pointer-events:none so it never eats the seek. */
.th-scrub-tip { position: absolute; bottom: calc(100% + 9px); transform: translateX(-50%); padding: 2px 7px; border-radius: 5px; background: rgba(20,21,30,.96); border: 1px solid var(--border-strong); color: #fff; font-family: var(--font-mono); font-size: 12px; line-height: 1.4; white-space: nowrap; pointer-events: none; z-index: 5; }
.th-scrub-tip[hidden] { display: none; }
.th-mk { position: absolute; top: -3px; width: 3px; height: 12px; border-radius: 2px; transform: translateX(-50%); }
/* One 32px line, and it must STAY one line: the row's height is what the
   58px bar is budgeted around, so a wrap would overflow it silently. */
.th-row { display: flex; align-items: center; gap: 10px; height: 32px; flex-wrap: nowrap; overflow: hidden; }
.th-time { font-family: var(--font-mono); font-size: 13px; color: #fff; }
.th-hint { margin-left: auto; font-size: 11px; color: rgba(255,255,255,.55); }
.th-hint b { color: rgba(255,255,255,.85); font-weight: 600; }
.th-icon { width: 30px; height: 30px; border-radius: 7px; background: rgba(20,21,30,.55); border: 1px solid var(--border-strong); color: #fff; display: inline-flex; align-items: center; justify-content: center; font-size: 13px; padding: 0; line-height: 1; }
/* Normalize icon size regardless of the inline width/height each SVG carries,
   and center them (draw + record toggles were rendering small/off). */
.th-icon svg { width: 18px; height: 18px; display: block; }
.th-icon.on { color: var(--accent); border-color: var(--accent-border); background: var(--accent-soft); }
/* Fullscreen exit button, bottom-right of the theater controls. */
.th-exit-btn { margin-left: 4px; height: 30px; white-space: nowrap; }
/* capture palette — timestamp spinner + title/description fields */
.th-cmd { position: absolute; left: 50%; bottom: calc(var(--th-ctrl-h, 58px) + 14px); transform: translateX(-50%); width: 600px; max-width: 84vw; background: rgba(20,21,30,.95); border: 1px solid var(--border-strong); border-radius: 12px; box-shadow: 0 24px 60px -20px rgba(0,0,0,.8); z-index: 4; display: flex; align-items: flex-start; gap: 11px; padding: 13px 15px; }
.th-cmd[hidden] { display: none; }
.th-cmd-at { font-family: var(--font-mono); font-size: 13px; color: var(--accent); background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: 6px; padding: 4px 9px; white-space: nowrap; }
/* Timestamp is a tabbable field: title → description → time → title.
   When focused it shows ‹ › arrows (←/→ nudge ±1s, Space seeks to it). */
.th-cmd-time { position: relative; display: inline-flex; align-items: center; margin-top: 1px; padding: 3px 6px; border-radius: 5px; outline: none; cursor: default; }
.th-cmd-arrow { position: absolute; top: 50%; font-family: var(--font-sans); font-size: 16px; line-height: 1; color: var(--accent); opacity: 0; transform: translateY(-50%); transition: opacity .12s; pointer-events: none; }
.th-cmd-arrow-l { right: 100%; margin-right: 3px; }
.th-cmd-arrow-r { left: 100%; margin-left: 3px; }
.th-cmd-time:focus { background: rgba(255,255,255,.06); box-shadow: 0 0 0 1px var(--accent) inset; }
.th-cmd-time:focus .th-cmd-arrow { opacity: 1; }
.th-cmd-time.bump-l { animation: thTimeBumpL .16s ease; }
.th-cmd-time.bump-r { animation: thTimeBumpR .16s ease; }
.th-cmd-time.seek { animation: thTimeJump .2s cubic-bezier(.34,1.56,.64,1); }
@keyframes thTimeBumpL { 50% { transform: translateX(-3px); } }
@keyframes thTimeBumpR { 50% { transform: translateX(3px); } }
@keyframes thTimeJump { 40% { transform: translateY(-4px); } }
.th-cmd-fields { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8px; }
.th-cmd-title, .th-cmd-desc { background: transparent; border: none; outline: none; color: var(--text); font-family: var(--font-sans); caret-color: var(--accent); width: 100%; }
.th-cmd-title { font-size: 15px; font-weight: 600; }
.th-cmd-desc { font-size: 13.5px; line-height: 1.45; resize: none; border-top: 1px solid var(--border); padding-top: 8px; }
.th-cmd-title::placeholder, .th-cmd-desc::placeholder { color: var(--text-faint); }
.th-cmd-hint { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .02em; color: rgba(255,255,255,.45); }
.th-cmd-hint b { color: rgba(255,255,255,.8); font-weight: 600; }
/* quick switcher (hold Tab) */
.th-navi { position: absolute; left: 0; right: 0; bottom: 96px; padding: 13px 18px 16px; background: linear-gradient(transparent, rgba(0,0,0,.55)); z-index: 4; }
.th-navi[hidden] { display: none; }
/* In the theater the switcher docks onto the control bar's top edge (the bar
   overlaps the video by 3px, hence the subtraction). It keeps a
   transparent→surface gradient so the thumbnails read as floating over the
   footage and melt into the bar; the bar's own z-index 3 wins the 3px seam. */
.theater .th-navi { bottom: calc(var(--th-ctrl-h, 58px) - 3px); z-index: 2; background: linear-gradient(transparent, rgba(20,21,30,.55) 55%, var(--surface)); }
/* ── Overlay theater (pool VODs): simple fullscreen ────────────────────────
   Reviews keep the docked bar above so the whole frame stays visible; a
   public VOD's theater is a normal player instead — the video owns the
   entire screen and the bar rides its bottom edge on a gradient, sliding
   down off-screen unless the cursor is near it (.th-idle from review.js). */
.theater.th-overlay .th-videohost { position: absolute; inset: 0; }
/* A letterboxed video (aspect != screen) in the overlay theater gets a faintly
   painted shield above it. Nothing overlaps the video's content otherwise, so
   WebView2 promotes it to a hardware overlay plane — drawn above the whole
   composited page (the bar became clickable-but-invisible) and eating cursor
   movement. A 16:9 VOD on a 16:9 screen keeps the overlay (and its smoother
   presentation) because the bar itself overlaps the content there. */
.th-shield { display: none; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: rgba(0, 0, 0, .02); }
.theater.th-overlay.th-letterbox .th-shield { display: block; }
.theater.th-overlay .th-controls {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 3;
  height: auto; margin-top: 0; padding: 22px 20px 10px;
  background: linear-gradient(transparent, rgba(5, 6, 10, .78));
  transition: transform .22s ease;
}
/* The switcher rides the bar's top edge here too, but melts into the
   gradient instead of a solid surface. */
.theater.th-overlay .th-navi { background: linear-gradient(transparent, rgba(0, 0, 0, .62)); }
.theater.th-overlay.th-idle .th-controls { transform: translateY(100%); pointer-events: none; opacity: 0; }
.theater.th-overlay .th-controls { transition: transform .22s ease, opacity .22s ease; }
.th-navi-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.th-navi-head span { font-size: 12.5px; font-weight: 600; color: #fff; }
.th-navi-sub { font-family: var(--font-mono); font-size: 10.5px; font-weight: 400 !important; letter-spacing: .02em; color: rgba(255,255,255,.5) !important; }
.th-navi-close { margin-left: auto; font-size: 11px; color: rgba(255,255,255,.6); border: 1px solid var(--border-strong); border-radius: 6px; padding: 4px 9px; background: rgba(20,21,30,.5); }
/* No scrollbar — the inner row slides via transform (selection pins to the
   3rd slot) and the entries carry timestamps, so a scroll position isn't
   needed. position:relative makes the thumbs' offsetLeft measure from the
   strip, so translate targets land exactly. */
.th-navi-strip { display: flex; overflow: hidden; position: relative; padding-bottom: 4px; }
.th-navi-row { display: flex; gap: 9px; will-change: transform; }
/* No transition on the highlight: the row slides instantly when the selection
   pins to the 3rd slot, so a fading border would trail the old thumb into
   slot 2 for a frame (reads as the highlight flickering backwards). */
.th-thumb { flex: none; width: 152px; border: 1px solid var(--border-strong); border-radius: 7px; background: var(--surface-raised); padding: 0; overflow: hidden; cursor: pointer; }
/* Selected card: styles.css sets an 88%-transparent tint here, which left the
   card (and its text) hard to read over the video — especially inline. Give it
   a translucent-but-solid accent-tinted panel so the label always reads. */
.th-thumb.on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); background: rgba(46, 49, 96, .92); }
.th-thumb.on .tn { color: #fff; }
.th-thumb.on .th-thumb-meta { background: rgba(46, 49, 96, .55); }
/* Per-entry thumbnail: a low-res frame grab above the kind/name meta. */
.th-thumb-img { position: relative; width: 100%; aspect-ratio: 16 / 9; background: #000; border-bottom: 1px solid var(--border-strong); }
.th-thumb-img canvas, .th-thumb-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.th-thumb-t { position: absolute; right: 4px; bottom: 4px; font-family: var(--font-mono); font-size: 9.5px; color: #fff; background: rgba(0,0,0,.62); padding: 1px 4px; letter-spacing: .02em; }
.th-thumb-meta { padding: 7px 9px 9px; }
.th-thumb .tk { font-family: var(--font-mono); font-size: 8px; letter-spacing: .08em; margin-bottom: 5px; }
.th-thumb .tn { font-size: 11px; color: rgba(255,255,255,.85); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.th-thumb .tt { font-family: var(--font-mono); font-size: 10px; color: rgba(255,255,255,.55); margin-top: 3px; }
/* Phase-card event-kind summary dots — mirrors the Events-tab phase header
   (a dot per kind present, count when >1). Squared by the telemetry theme,
   exactly like .ph-tag. */
.th-thumb .tk { display: flex; align-items: center; }
.th-thumb .th-tags { display: inline-flex; align-items: center; gap: 4px; margin-left: 6px; }
.th-thumb .th-tag { display: inline-flex; align-items: center; gap: 2px; }
.th-thumb .th-tag .d { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.th-thumb .th-tag .n { font-family: var(--font-mono); font-size: 8px; font-weight: 600; color: rgba(255,255,255,.72); }
/* Wave phases that contained a recall: underline the name. */
.th-thumb.recall .tn { text-decoration: underline; text-decoration-color: var(--note-gold); text-underline-offset: 2px; }
/* Inline quick switcher — the same navigator docked to the bottom of the
   inline video stage instead of the fullscreen overlay. */
.in-navi { top: auto; bottom: 0; padding: 10px 12px 12px; background: linear-gradient(transparent, rgba(0,0,0,.72) 55%); z-index: 6; cursor: default; }
.in-navi .th-navi-head { margin-bottom: 8px; }
/* Summon / release / track-switch motion. */
@keyframes thNaviIn  { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
@keyframes thNaviOut { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateY(16px); } }
@keyframes thStripUp   { from { opacity: .25; transform: translateY(12px); } to { opacity: 1; transform: none; } }
@keyframes thStripDown { from { opacity: .25; transform: translateY(-12px); } to { opacity: 1; transform: none; } }
.th-navi:not([hidden]):not(.closing) { animation: thNaviIn .18s cubic-bezier(.16,.8,.29,1); }
.th-navi.closing { animation: thNaviOut .15s ease forwards; }
@media (prefers-reduced-motion: reduce) {
  .th-navi:not([hidden]):not(.closing), .th-navi.closing, .th-navi-strip,
  .th-cmd-time.bump-l, .th-cmd-time.bump-r, .th-cmd-time.seek { animation: none !important; }
}

/* ════════════════════════════════════════════════════════════════════
   MODAL / LIGHTBOX
   ════════════════════════════════════════════════════════════════════ */
/* Annotation lightbox: a large image viewer. The card shrinks to the image
   (a square map snapshot gets a square card, a 16:9 frame a wide one) — no
   letterbox bars; the image itself is capped by the viewport. */
.modal-card:has(.lightbox-img) { width: fit-content; min-width: 360px; max-width: min(94vw, 1280px); }
.modal-card:has(.lightbox-img) .modal-body { padding: 0; background: #000; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.lightbox-img { display: block; width: auto; height: auto; max-width: min(94vw, 1280px); max-height: 82vh; }

/* ════════════════════════════════════════════════════════════════════
   TELEMETRY — squared "instrument" overlay (the chosen design direction).
   Layered last so it re-squares the rounded chrome, paints the oscilloscope
   grid, and adds the flight-recorder marks: lowercase › labels, bracketed
   status, a HUD-framed video stage and a graph-paper timeline. (The
   targeting-reticle corners were removed — not a fit for DeepReview.)
   Semantic win/HP/role colours untouched.
   Review-scoped (review.css is linked only by review.html).
   ════════════════════════════════════════════════════════════════════ */
:root {
  --radius-xs: 0; --radius-sm: 0; --radius: 0; --radius-md: 0; --radius-lg: 0; --radius-pill: 0;
  --tele-grid: rgba(128, 128, 234, .045);
  --tele-line: rgba(128, 128, 234, .22);
}

/* De-round every panel/card/button/tag; genuinely circular affordances are
   restored just below. (Pseudo-element dots/ticks aren't matched by `*`, so
   they keep their own radii.) */
body * { border-radius: 0 !important; }
.save-dot, .nb-dot, .nb-chip .t .d,
.th-knob, .map-pin, .spinner, .sp-arc, .sp-conic,
/* Game Info: drake pips, the map's pit anchors and the route dots are all
   genuinely circular readouts, not panel chrome. */
.gi-pip, .gi-pit, .gi-trail { border-radius: 50% !important; }
/* Loading animations (creature loaders) are hand-drawn illustrations, NOT
   telemetry chrome — they are permanently EXEMPT from the squared reset above,
   which was flattening every round element (the scuttle puddle/ripples, turret
   shootout bullets, gromp spore puffs, …). Any element a loader marks round
   (inline border-radius) keeps its circle; sprite <img>s declare no radius, so
   they stay square. Every loader circle uses `50%`, so this covers all current
   AND future loaders automatically — never re-fix per-loader. */
.cl-loader [style*="border-radius"] { border-radius: 50% !important; }

/* ── Numeric voice: lowercase mono instrument micro-labels with a › tick ── */
.gt-row .rl, .pw-cs .l, .pw-barrow .bl, .nb-rchip .lab,
.wb-title { letter-spacing: .02em; }

/* ── Chrome → mono instrument labels (nav/toggles also go lowercase) ────── */
.tab, .back, .save, .mode-select, .view-btn, .vc-btn, .vc-time, .vc-select,
.btn, .btn-tiny, .btn-now, .act, .author, .pw-recall, .qr-input, .qr-time,
.nb-chip, .nb-tool, .th-btn, .th-icon, .th-hint, .dtool { font-family: var(--font-mono); }
.tab, .back, .save, .view-btn, .pw-recall { letter-spacing: .02em; }
.kda { white-space: nowrap; letter-spacing: -.01em; }

/* Bracketed status read-out — the "Saved" chip reads like an instrument tag */
.save { letter-spacing: .04em; }

/* Timeline view toggle → outlined instrument toggle (active = accent underline) */
.view-btn.active { background: var(--accent-soft) !important; color: var(--accent) !important; box-shadow: inset 0 -2px 0 var(--accent); }

/* (The targeting-reticle corner brackets that marked focal/active panels were
   removed everywhere — the motif didn't fit DeepReview. Active cards keep
   their tinted fill + accent border alone.) */

/* ── Data surfaces: graph-paper timeline ──────────────────────────────── */
/* Letterbox bars take the surface colour (set on the stage above + the video
   element below) instead of pitch black, and stay click-through to the video
   since the colour is just the element background.
   No inset bezel over the footage: an `inset box-shadow` paints a 1px ring
   INSIDE the stage, i.e. over the video's own edge when the footage fills the
   stage. Anything composited on top of the <video> quad stops WebView2/Chromium
   from promoting it to a hardware overlay, which is what keeps windowed 60fps
   playback smooth on a high-refresh monitor. Dropped for that reason. */

/* Terminal carets on text inputs. (The header's old hairline accent run is
   gone — it's a bordered window now, no full-bleed accent, no shadow.) */
.header { box-shadow: none; }
#noteBody, #noteTitle, #noteStart, #noteEnd, .qr-input, .nb-prose, .th-cmd-title, .th-cmd-desc { caret-color: var(--accent); }

/* ════════════════════════════════════════════════════════════════════
   COACHING (handoff 17) — coach POV chrome, replay recorder, fullscreen
   adjustments. Coach voice is gold; the user's own notes keep indigo.
   ════════════════════════════════════════════════════════════════════ */

/* ── Coach review, player POV (the chip's modal) ───────────────────────
   Their coach's overall write-up: learning objectives + summary, a tab per
   coach who sent work, and None to hide the track. Read-only throughout. */
.coach-modal .modal-card { max-width: 540px; width: 92vw; }
.coach-modal .modal-body { height: 500px; display: flex; flex-direction: column; gap: 14px; }
.cm-coaches { display: flex; gap: 7px; align-items: flex-start; flex: none; }
/* Coach tabs scroll horizontally when they overflow (4+); None lives outside
   the scroll so it stays in the same spot at all times. */
.cm-coach-scroll { flex: 1; min-width: 0; display: flex; gap: 7px; overflow-x: auto; padding-bottom: 2px; scrollbar-width: thin; }
.cm-coach-scroll::-webkit-scrollbar { height: 6px; }
.cm-coach-scroll::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 3px; }
/* Variable-length feedback scrolls inside a fixed frame, so switching coaches
   never resizes the modal. */
.cm-scroll { flex: 1; min-height: 0; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; padding-right: 12px; }
.cm-coach-tab { flex: 0 0 132px; min-width: 0; display: inline-flex; align-items: center; gap: 7px; padding: 6px 9px 6px 6px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-raised); cursor: pointer; }
.cm-coach-tab:hover { border-color: var(--border-strong); }
.cm-coach-tab.on { border-color: var(--border-strong); background: var(--surface); }
.cm-coach-tab .av { width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: rgba(240,194,68,.16); border: 1px solid rgba(240,194,68,.4); color: var(--gold); font-size: 10px; font-weight: 700; flex: none; overflow: hidden; }
.cm-coach-tab .av img, .cm-head .av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cm-coach-tab .nm { font-size: 12.5px; font-weight: 700; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cm-head { display: flex; align-items: center; gap: 12px; padding: 13px; border: 1px solid rgba(240,194,68,.3); background: rgba(240,194,68,.06); border-radius: 10px; }
.cm-head .av { width: 42px; height: 42px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  background: rgba(240,194,68,.16); border: 1px solid rgba(240,194,68,.45); color: var(--gold); font-size: 17px; font-weight: 700; flex: none; overflow: hidden; }
.cm-head .nm { font-size: 15px; font-weight: 700; color: var(--text); }
.cm-head .rl2 { font-size: 11.5px; color: var(--text-muted); }
.cm-head .when { margin-left: auto; font-family: var(--font-mono); font-size: 9.5px; color: var(--text-faint); text-align: right; line-height: 1.5; }
.cm-tk-title { font-family: var(--font-mono); font-size: 9px; letter-spacing: .1em; color: var(--text-faint); }
.cm-tk-title::before { content: "// "; color: var(--gold); }
.cm-takeaways { display: flex; flex-direction: column; gap: 10px; }
.cm-tk { display: flex; gap: 10px; font-size: 12.5px; line-height: 1.55; color: var(--text); }
.cm-tk .n { font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: var(--gold); flex: none; }
.cm-summary { font-size: 12.5px; line-height: 1.6; color: var(--text-muted); }
.cm-summary p { margin: 0 0 8px; }
.cm-summary p:last-child { margin-bottom: 0; }
.cm-empty { font-size: 12.5px; line-height: 1.6; color: var(--text-faint); margin: 0; }
.coach-modal .cm-btn { height: 34px; padding: 0 14px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface-raised);
  color: var(--text-muted); font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.coach-modal .cm-btn:hover { border-color: var(--border-strong); color: var(--text); }
.coach-modal .cm-btn.primary { flex: 1; background: var(--gold); border-color: var(--gold); color: #17150c; font-weight: 700; }
.coach-modal .cm-btn.primary:hover { filter: brightness(1.06); }

/* Header coaching pill. Dressed like the toolbar controls beside it (the back
   link, the icon buttons: border, raised surface, muted mono label); the gold
   that marks coaching is kept to the initial badge. The 10-03 version wore a
   full gold border, gold badge and gold name, and read as a warning next to
   the rest of the bar. Coach POV reads "Coaching X"; a coached review reads
   "Coached by X". */
.coach-chip { display: inline-flex; align-items: center; gap: 8px; height: 30px; padding: 0 11px 0 4px;
  border: 1px solid var(--border); background: var(--surface-raised); flex: none;
  font-family: var(--font-mono); font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.coach-chip:hover { border-color: var(--border-strong); }
.coach-chip:hover .cc-lab { color: var(--text); }
.coach-chip .cc-av { width: 20px; height: 20px; display: inline-flex; align-items: center; justify-content: center;
  background: rgba(240,194,68,.12); color: var(--gold); font-size: 11px; font-weight: 700; flex: none; overflow: hidden; }
/* The badge is the person's profile icon (never an initial letter, user rule
   2026-10-03); the "·" of the notes-hidden state is the only text it shows. */
.coach-chip .cc-av img { width: 100%; height: 100%; object-fit: cover; display: block; }
.coach-chip .cc-lab { color: var(--text-muted); letter-spacing: .02em; }
.coach-chip .cc-lab b { color: var(--text); font-weight: 600; }

/* Timeline: a coach's bars in the shared notes lane. Checkered gold is the
   coaching signature style (a player can pick gold too; the pattern is what
   tells them apart). */
.gt-bar.coach { fill-opacity: 1; stroke: #f0c244; }   /* fill = the gtCoachCheck pattern in the lane svg (review.js) */
/* Voice/replay note marker — a round waveform badge over the note bars. */
.gt-voice-mk { position: absolute; top: 50%; transform: translate(-50%, -50%); z-index: 4;
  width: 17px; height: 17px; border-radius: 50% !important; display: flex; align-items: center; justify-content: center;
  background: var(--vc, var(--accent)); color: #12151c; cursor: pointer;
  box-shadow: 0 0 0 2px rgba(12,15,22,.85), 0 1px 4px rgba(0,0,0,.5); }
.gt-voice-mk:hover { transform: translate(-50%, -50%) scale(1.15); }

/* Notes tab — the coach's author dropdown (player / you / both) sits in the
   filter bar as a cpick-style button; the menu itself is a .cm-menu. The
   "you" pick is gold, the coach's colour. Cards carry no author chip: the
   icon / colour (player) or the checkered gold + grad cap (coach) say who
   wrote it, and a "written by X" line appears only with two or more coaches. */
.cpick-author .cpick-current { font-family: var(--font-mono); font-size: 11px; font-weight: 600; letter-spacing: .02em; }
.cpick-author .cpick-current.coach { color: var(--gold); }
/* Coach cards: same spine system, gold voice (--note-color is COACH_HEX). */
.note.coach:hover { border-color: rgba(240,194,68,.45); }
/* Same rule in the coach's voice: the fill marks the SELECTED note, never
   the playing one. */
.note.coach.active { background: linear-gradient(rgba(240,194,68,.035), rgba(240,194,68,.035)), var(--surface-raised); border-color: rgba(240,194,68,.45); }
.note.coach.active .note-time, .note.coach .note-time:hover { color: var(--gold); }
.note.coach.active .note-foot { border-top-color: rgba(240,194,68,.45); }
.note-coach-badge { width: 17px; height: 17px; border-radius: 50% !important; background: var(--gold); color: #17150c; display: inline-flex; align-items: center; justify-content: center; flex: none; box-shadow: 0 0 0 2px rgba(240,194,68,.22); }

/* Coach reply on a player note (composer + shown + stale notice). */
.reply-edit { margin-top: 9px; display: flex; flex-direction: column; gap: 7px; border: 1px solid var(--border); border-left: 2px solid var(--gold); border-radius: 6px; background: var(--bg); padding: 8px 10px; }
.reply-ta { border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text); font-family: var(--font-sans); font-size: 12px; line-height: 1.45; padding: 7px 9px; resize: none; outline: none; }
.reply-ta:focus { border-color: var(--accent-border); }
.reply-edit-actions { display: flex; gap: 6px; }
.reply-edit-actions .cm-btn { height: 28px; font-size: 11px; }
.reply-edit-actions .cm-btn.primary { flex: none; padding: 0 12px; }
/* One 18px line box for all three parts: the "you" tag and the edit face sit
   on the reply's FIRST line (the row is top-aligned so a wrapped reply keeps
   them there), each centred in that line's height. */
.note-reply-shown { margin-top: 9px; display: flex; align-items: flex-start; gap: 8px; border: 1px solid var(--border); border-left: 2px solid var(--gold); border-radius: 6px; background: rgba(0,0,0,.16); padding: 7px 8px 7px 10px; }   /* a slight inset on the card (var(--bg) was too black); neutral box like the composer and the stale-reply row; the gold lives in the left rule and the tag (the gold wash + gold outline read as a warning) */
.note-reply-shown .nr-by { font-family: var(--font-mono); font-size: 9px; font-weight: 700; letter-spacing: .05em; color: var(--gold); flex: none; height: 18px; display: inline-flex; align-items: center; }
.note-reply-shown .nr-text { font-size: 12px; line-height: 18px; color: var(--text); flex: 1; min-width: 0; overflow-wrap: anywhere; }
.nr-edit { margin-left: auto; width: 18px; height: 18px; padding: 0; flex: none; display: inline-flex; align-items: center; justify-content: center; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--text-faint); cursor: pointer; }
.nr-edit:hover { color: var(--gold); background: rgba(240,194,68,.1); border-color: rgba(240,194,68,.3); }
.note-reply-stale { margin-top: 9px; display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 10.5px; color: var(--text-muted); background: var(--bg); border: 1px solid var(--border); border-left: 2px solid var(--gold); border-radius: 6px; padding: 7px 10px; cursor: pointer; }
.note-reply-stale:hover { border-color: var(--border-strong); color: var(--text); }
.note-reply-stale .rs-ic { color: var(--gold); flex: none; }
.note-reply-stale .rs-view { margin-left: auto; color: var(--accent); flex: none; }
.sm-lead { font-size: 12.5px; line-height: 1.55; color: var(--text-muted); margin: 0; }
.sm-lead b { color: var(--gold); }
.sm-orig { font-size: 12.5px; line-height: 1.6; color: var(--text); background: var(--surface-raised); border: 1px solid var(--border); border-radius: 8px; padding: 11px 13px; }
.sm-note { font-size: 11px; color: var(--text-faint); margin: 0; }

/* Overall-review editor (coach chip). */
.se-modal .modal-card { max-width: 620px; width: 94vw; }
.se-modal .modal-body { gap: 12px; max-height: 80vh; }
.se-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 4px; padding: 6px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-raised); }
.se-toolbar button { height: 28px; min-width: 30px; padding: 0 8px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--text-muted); font-family: var(--font-mono); font-size: 11px; cursor: pointer; }
.se-toolbar button:hover { background: var(--bg); color: var(--text); border-color: var(--border); }
.se-editor { min-height: 120px; max-height: 240px; overflow-y: auto; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); padding: 11px 13px; font-size: 13px; line-height: 1.6; color: var(--text); outline: none; }
.se-editor:focus { border-color: var(--accent-border); }
.se-tk-head { font-size: 12px; font-weight: 700; color: var(--text); display: flex; align-items: baseline; gap: 8px; }
.se-tk-hint { font-family: var(--font-mono); font-size: 9.5px; font-weight: 400; color: var(--text-faint); }
.se-tk-list { display: flex; flex-direction: column; gap: 6px; }
.se-tk-item { display: flex; align-items: flex-start; gap: 9px; }
.se-tk-n { font-family: var(--font-mono); font-size: 11px; font-weight: 700; color: var(--gold); width: 12px; flex: none; text-align: right; margin-top: 8px; }
.se-tk-inp { flex: 1; min-width: 0; min-height: 32px; border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); font-family: var(--font-sans); font-size: 12.5px; line-height: 1.45; padding: 6px 10px; resize: none; overflow: hidden; }
.se-tk-inp:focus { outline: none; border-color: var(--accent-border); }
.se-tk-del { width: 28px; height: 28px; flex: none; border: 1px solid var(--border); border-radius: 6px; background: var(--surface-raised); color: var(--text-muted); font-size: 18px; line-height: 1; cursor: pointer; margin-top: 3px; display: inline-flex; align-items: center; justify-content: center; padding: 0; }
.se-tk-del:hover { border-color: rgba(255,93,93,.5); color: var(--red); }
.se-tk-add { align-self: flex-start; height: 30px; padding: 0 12px; border: 1px dashed var(--border-strong); border-radius: 7px; background: transparent; color: var(--text-muted); font-size: 11.5px; font-weight: 600; cursor: pointer; }
.se-tk-add:hover { color: var(--text); border-color: var(--accent-border); }
.se-foot { display: flex; align-items: center; gap: 10px; padding-top: 12px; border-top: 1px solid var(--border); }
.se-status { flex: 1; display: inline-flex; align-items: center; gap: 7px; font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); }
.cm-btn { height: 34px; padding: 0 14px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface-raised); color: var(--text-muted); font-size: 12px; font-weight: 600; cursor: pointer; white-space: nowrap; }
.cm-btn:hover { border-color: var(--border-strong); color: var(--text); }
.cm-btn.primary { background: var(--gold); border-color: var(--gold); color: #17150c; font-weight: 700; }
.cm-btn.primary:hover { filter: brightness(1.06); }

/* Rec button in the player bar. */
.vc-btn.vc-rec svg { color: #ff5d5d; }
.th-icon.recording { background: var(--red) !important; color: #1a0d0d; }
/* The inline Rec button while rolling — lit, and clickable to stop. */
.vc-btn.recording { background: var(--red); border-color: var(--red); color: #1a0d0d; }
.vc-btn.recording:hover { background: var(--red); border-color: var(--red); }

/* Recording indicator — translucent strip in the frame's top-left corner. */
.rec-bar { position: absolute; left: 0; top: 0; z-index: 40;
  display: flex; align-items: center; gap: 8px; white-space: nowrap; padding: 5px 10px;
  background: rgba(16,18,24,.82); border: 1px solid rgba(255,255,255,.14);
  border-width: 0 1px 1px 0; border-radius: 0 0 8px 0; font-family: var(--font-sans); text-shadow: 0 1px 4px rgba(0,0,0,.9); }
/* No backdrop-filter here (perf item 32): the strip sits over a playing video,
   and a blur re-renders its backdrop on every video frame for the whole
   narration. A more opaque background reads the same. The dot blinks from the
   recording timer (review.js toggles .dim twice a second) instead of an
   infinite CSS pulse, which dirtied every display frame. */
.rec-bar[hidden] { display: none; }
.rec-bar .rb-dot { width: 8px; height: 8px; border-radius: 50%; background: #ff5d5d; flex: none; }
.rec-bar .rb-dot.dim { opacity: .35; transform: scale(.8); }
.rec-bar .rb-label { font-size: 11px; font-weight: 600; color: #fff; }
.rec-bar .rb-time { font-family: var(--font-mono); font-size: 11px; color: #ff8f8f; }
.rec-bar .rb-keys { display: inline-flex; align-items: center; gap: 6px; }
.rec-bar .rb-btn { font-family: var(--font-mono); font-size: 9.5px; color: rgba(255,255,255,.7); background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.18); border-radius: 5px; padding: 2px 7px; cursor: pointer; }
.rec-bar .rb-btn:hover { background: rgba(255,255,255,.18); color: #fff; }
.rec-bar .rb-btn b { color: #fff; font-weight: 600; }
#theater .rec-bar { gap: 10px; }
#theater .rec-bar .rb-dot { width: 9px; height: 9px; }
#theater .rec-bar .rb-label, #theater .rec-bar .rb-time { font-size: 12.5px; }

/* Post-recording review dialog — mirrors the fullscreen note menu. */
.rec-review { position: absolute; left: 50%; bottom: 14px; transform: translateX(-50%); z-index: 41;
  width: min(320px, 84%); display: flex; flex-direction: column; gap: 9px; padding: 12px 13px;
  background: rgba(16,18,24,.94); backdrop-filter: blur(8px); border: 1px solid var(--border-strong);
  border-radius: 12px; box-shadow: 0 24px 60px -20px rgba(0,0,0,.8);
  animation: thNaviIn .18s cubic-bezier(.16,.8,.29,1); }
.rec-review[hidden] { display: none; }
/* (No theater offset: the dialog rides inside .th-videohost, whose bottom edge
   already stops at the docked control bar. It used to need +108px to clear the
   old floating chrome that overlaid the footage.) */
.rec-review .rr-head { display: flex; align-items: center; gap: 8px; }
.rec-review .rr-badge { display: inline-flex; align-items: center; gap: 4px; color: var(--gold); font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em; border: 1px solid rgba(240,194,68,.4); border-radius: 999px; padding: 2px 8px; }
.rec-review .rr-dur { margin-left: auto; font-family: var(--font-mono); font-size: 11.5px; color: var(--text-muted); }
.rec-review .rr-title { width: 100%; background: transparent; border: none; outline: none; color: #fff; font-family: var(--font-sans); font-size: 15px; font-weight: 600; padding: 2px 0; caret-color: var(--accent); }
.rec-review .rr-title::placeholder { color: rgba(255,255,255,.4); }
.rec-review .rr-actions { display: flex; align-items: stretch; gap: 8px; margin-top: 2px; }
.rec-review .rr-opt { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 4px; padding: 8px 6px;
  border: 1px solid var(--border-strong); border-radius: 7px; background: rgba(255,255,255,.05); color: var(--text); cursor: pointer; }
.rec-review .rr-opt:hover { background: rgba(255,255,255,.12); }
.rec-review .rr-opt.primary { background: var(--accent); border-color: var(--accent); color: #06131f; }
.rec-review .rr-opt-label { font-size: 12.5px; font-weight: 600; }
.rec-review .rr-opt-key { font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .03em; opacity: .6; }
/* The "finishing up" line: when it goes, the window gets one row shorter but
   nothing above it moves. The window is anchored by its bottom edge, so
   without the line it sits higher by exactly the row it lost (14px line + the
   9px column gap) — the title and buttons stay where they were. */
.rec-review .rr-status { font-family: var(--font-mono); font-size: 10.5px; line-height: 14px; height: 14px; color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.rec-review .rr-status[hidden] { display: none; }
.rec-review:has(.rr-status[hidden]) { bottom: calc(14px + 14px + 9px); }

/* Replay note card player + badge (gold) and shared waveform pieces. */
.note-replay-tag { display: inline-flex; align-items: center; margin-left: 6px; color: var(--gold); vertical-align: middle; }
/* Cards are flex columns now (gap 7px) — the old stacking margins double up. */
.note-replay { display: flex; align-items: center; gap: 9px; margin: 0; }
.note .note-reply-shown, .note .note-reply-stale, .note .reply-edit { margin-top: 0; }
.rp-play { width: 28px; height: 28px; border-radius: 50% !important; border: 1px solid rgba(240,194,68,.5); background: rgba(240,194,68,.12); color: var(--gold); display: flex; align-items: center; justify-content: center; cursor: pointer; flex: none; padding: 0; }
.rp-play:hover { background: rgba(240,194,68,.22); }
.rp-wrap { position: relative; flex: 1; height: 28px; min-width: 60px; }
.rp-bars { position: absolute; inset: 0; display: flex; align-items: center; gap: 2px; }
.rp-bars.base { color: var(--text-muted); opacity: .4; }
.rp-bars.fill { color: var(--gold); clip-path: inset(0 100% 0 0); }
.wv-bar { flex: 1; min-width: 2px; background: currentColor; border-radius: 1px; }
.rp-dur { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); flex: none; }

/* "Now replaying" banner — covers the playback controls and IS the transport
   while a replay note plays: pause / resume, a scrubbable bar (the narration
   audio is the clock; seeking it rewinds the video and the drawings), the
   clock, stop. The keys route here too while a replay is on (see the key
   handler: play toggles pause, the seek keys move within the replay). */
.replay-banner { position: absolute; inset: 0; z-index: 9; display: flex; align-items: center; gap: 10px;
  background: rgba(18,20,26,.96); border: 1px solid rgba(240,194,68,.5); border-radius: 8px; padding: 0 12px; }
.replay-banner[hidden] { display: none; }
.replay-banner .rb-ic { color: var(--gold); display: inline-flex; }
.replay-banner .rb-label { font-size: 12px; color: #fff; font-weight: 600; white-space: nowrap; }
.replay-banner.paused .rb-label { color: var(--text-muted); }
.replay-banner .rb-track { flex: 1; height: 14px; display: flex; align-items: center; cursor: pointer; touch-action: none; }
.replay-banner .rb-track::before { content: ""; position: absolute; left: 0; right: 0; height: 4px; border-radius: 2px; background: rgba(255,255,255,.15); }
.replay-banner .rb-track { position: relative; }
.replay-banner .rb-fill { position: absolute; left: 0; height: 4px; width: 0; border-radius: 2px; background: var(--gold); pointer-events: none; }
.replay-banner .rb-track:hover::before { background: rgba(255,255,255,.22); }
.replay-banner .rb-time { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); white-space: nowrap; flex: none; min-width: 72px; text-align: right; }
.replay-banner .rb-stop, .replay-banner .rb-pp { width: 28px; height: 28px; flex: none; border-radius: 6px; border: 1px solid var(--border-strong); background: rgba(255,255,255,.08); color: #fff; cursor: pointer; display: flex; align-items: center; justify-content: center; padding: 0; }
.replay-banner .rb-stop svg, .replay-banner .rb-pp svg { width: 14px; height: 14px; display: block; }
.replay-banner .rb-stop:hover, .replay-banner .rb-pp:hover { background: rgba(255,255,255,.16); }
/* A replay note's Rift map: a square picture over the video, as tall as the
   stage, the rest of the frame dimmed. No input — clicks fall through to the
   stage, so the transport works as it does for the rest of the replay. */
.replay-map:not([hidden]) { display: block; }
.replay-map { position: absolute; inset: 0; margin: auto; height: calc(100% - 12px); width: auto; max-width: calc(100% - 12px); aspect-ratio: 1; z-index: 5; pointer-events: none;
  background: #05060a; box-sizing: border-box;
  /* The app's panel edge: one strong hairline with a darker line outside it
     (6px of the stage shows around it), over a heavy dim of the video. */
  border: 1px solid var(--border-strong);
  box-shadow: 0 0 0 1px #05060a, 0 0 0 100vmax rgba(5,6,10,.86); }
/* Replay-only display canvas (drawing playback; no input). */
.replay-canvas { position: absolute; inset: 0; z-index: 4; pointer-events: none; }

/* Coach mode: player-authored inputs are read-only. */
body[data-coach-mode] .qr-input, body[data-coach-mode] .ph-note textarea { opacity: .75; }

/* display rules on these classes would beat the UA [hidden] style — the rec
   button, theater rec toggle and coach chip must stay gone outside coach mode. */
.vc-btn[hidden], .th-icon[hidden], .coach-chip[hidden], .header .save[hidden] { display: none !important; }

/* Coach: local-cache download progress pill (next to the coach chip). */
.coach-cache-pill { display: inline-flex; align-items: center; height: 24px; padding: 0 9px; margin-left: 8px;
  border: 1px solid var(--accent-border); border-radius: 999px; background: var(--accent-soft);
  color: var(--accent); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .02em; white-space: nowrap; }

/* ── Clip export modal ──────────────────────────────────────────────────
   Shown once a cut is rendered: preview it, drag the file out, or file it in
   the library. It owns its backdrop rather than going through modal.js —
   this is a media surface (16:9 stage, transport, drag source), not a form,
   and it opens while ffmpeg is still running rather than on a finished fact. */

@keyframes lolClipDragPulse { 0%, 100% { opacity: .55 } 50% { opacity: 1 } }
@keyframes lolClipCheck { from { transform: scale(.6); opacity: 0 } to { transform: scale(1); opacity: 1 } }
@keyframes lolClipSpin { to { transform: rotate(360deg) } }

.lolclip-spin { border-radius: 50%; border: 3px solid var(--border); border-top-color: var(--accent);
  animation: lolClipSpin .8s linear infinite; }
@media (prefers-reduced-motion: reduce) {
  .lolclip-spin { animation-duration: 2s }
  .lolclip-drag-hint { animation: none }
}

/* The design system ships a global `* { box-sizing: border-box }` that this
   stylesheet doesn't have, and every measurement in the design was drawn under
   it — a 6px scrubber track means 6px INCLUDING its border. Scope the reset to
   this modal so those numbers mean what they say, without reflowing the rest
   of the review page. */
/* Every `display:` rule below outranks the UA's `[hidden] { display: none }`,
   so without this the chips, play button and transport stay on screen no
   matter what the JS hides — same trap as .vc-btn/.th-icon/.coach-chip above. */
.lolclip-backdrop [hidden] { display: none !important; }

.lolclip-backdrop { position: fixed; inset: 0; z-index: 80; display: flex; align-items: center; justify-content: center;
  background: var(--overlay); backdrop-filter: blur(2px); }
.lolclip-card { width: min(680px, 94vw); max-height: 92vh; display: flex; flex-direction: column;
  background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-modal);
  font-family: var(--font-sans); color: var(--text); }

/* Float in on open, drift + fade out on close — matches the app-wide modal
   motion. Keyframes live in styles.css, which this page also loads. The exit
   `.is-closing` class is added by floatOutModal (modal.js) via this modal's
   close(). */
.lolclip-card { animation: modalFloatIn .18s cubic-bezier(.16, .84, .44, 1) both; }
.lolclip-backdrop { animation: modalScrimIn .18s ease-out both; }
.lolclip-backdrop.is-closing { animation: modalScrimOut .16s ease-in forwards; }
.lolclip-backdrop.is-closing .lolclip-card { animation: modalFloatOut .16s ease-in forwards; }
@media (prefers-reduced-motion: reduce) {
  .lolclip-card, .lolclip-backdrop,
  .lolclip-backdrop.is-closing, .lolclip-backdrop.is-closing .lolclip-card { animation: none; }
}

/* ── top bar ── */
.lolclip-top { display: flex; align-items: center; gap: 9px; padding: 10px 10px 10px 16px; border-bottom: 1px solid var(--border); }
.lolclip-name { min-width: 0; font-family: var(--font-mono); font-size: 13px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lolclip-chip { flex: none; display: inline-flex; align-items: center; gap: 6px; padding: 2px 8px;
  font-family: var(--font-mono); font-size: 11px; }
.lolclip-chip.is-busy { gap: 5px; border: 1px solid var(--accent-border); color: var(--accent); }
.lolclip-chip.is-busy .lolclip-spin { width: 8px; height: 8px; border-width: 2px; }
.lolclip-chip.is-ready { border: 1px solid rgba(77, 209, 122, .4); color: var(--green); }
.lolclip-chip.is-ready::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.lolclip-chip.is-note { border: 1px solid var(--accent-border); color: var(--accent); }
.lolclip-chip.is-nonote { border: 1px solid var(--border); color: var(--text-muted); }
.lolclip-spacer { flex: 1; }
/* A square that flex-centres its glyph. The old `padding: 2px 7px` sized the
   box off the ×'s font metrics, whose ink doesn't sit centred in its em box —
   so the glyph rode high and left of the hover background. */
.lolclip-x { flex: none; width: 30px; height: 30px; padding: 0; background: none; border: none;
  color: var(--text-muted); font-size: 22px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; transition: var(--transition-colors); }
.lolclip-x:hover { color: var(--text); background: rgba(255, 255, 255, .05); }

/* ── body ── */
.lolclip-body { padding: 16px 18px; overflow-y: auto; display: flex; flex-direction: column; gap: 14px; }

/* 16:9 stage, shared by the rendering and ready states */
.lolclip-stage { position: relative; aspect-ratio: 16/9; background: #000; border: 1px solid var(--border); overflow: hidden; }
.lolclip-stage::before { content: ""; position: absolute; inset: 0; z-index: 0;
  background: radial-gradient(120% 90% at 50% 40%, #0a0c14 0%, #000 78%); }
.lolclip-stage.is-rendering::before { background: radial-gradient(120% 90% at 50% 42%, #0a0c14 0%, #000 78%); }
.lolclip-stage video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; z-index: 1; background: #000; }

/* rendering state */
.lolclip-rendering { position: absolute; inset: 0; z-index: 2; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 14px; }
.lolclip-rendering .lolclip-spin { width: 38px; height: 38px; }
.lolclip-cap { font-family: var(--font-mono); font-size: 11px; letter-spacing: .08em; color: var(--text-muted); }
.lolclip-pct { font-family: var(--font-mono); font-size: 40px; font-weight: 600; color: var(--slate-100);
  font-variant-numeric: tabular-nums; line-height: 1; }
.lolclip-renderbar { position: absolute; left: 0; right: 0; bottom: 0; z-index: 2; height: 4px; background: var(--surface-raised); }
.lolclip-renderbar > i { display: block; height: 100%; width: 0; background: var(--brand); transition: width .1s linear; }
.lolclip-status { display: flex; align-items: center; justify-content: space-between; font-family: var(--font-mono);
  font-size: 11.5px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.lolclip-statusleft { display: flex; align-items: center; gap: 14px; min-width: 0; }

/* comms toggle — prominent by design: it states what audio ships in the file */
.lolclip-commsbtn { display: inline-flex; align-items: center; gap: 7px; padding: 3px 8px; margin: -3px 0;
  background: none; border: none; cursor: pointer; font-family: var(--font-mono); font-size: 11.5px;
  letter-spacing: .01em; transition: var(--transition-colors); }
.lolclip-commsbtn.is-on { color: var(--accent); }
.lolclip-commsbtn.is-off { color: var(--text-muted); }
.lolclip-commsbtn:hover { color: var(--text); }
.lolclip-commsbtn svg { flex: none; }
.lolclip-commsrow { display: flex; align-items: center; }
/* Multi-POV clip: the POV switcher over the transport, and its picker. */
.lolclip-povs { display: flex; flex-wrap: wrap; gap: 6px; margin: 8px 0 2px; }
.lolclip-pov { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 9px 0 3px; cursor: pointer;
  background: var(--surface-raised); border: 1px solid var(--border); color: var(--text-muted);
  font-family: var(--font-mono); font-size: 11px; font-weight: 600; }
.lolclip-pov img { width: 20px; height: 20px; object-fit: cover; display: block; }
.lolclip-pov:hover { color: var(--text); border-color: var(--border-strong); }
.lolclip-pov.on { color: #fff; border-color: var(--brand); box-shadow: inset 0 -2px 0 var(--brand); }
/* One row per POV: check box, champion, player, all left-aligned on one line.
   The native box is hidden (the app-wide input rule stretches it to the row's
   full width) and .mpov-box draws the squared one. */
.mpov-list { display: flex; flex-direction: column; gap: 4px; }
.mpov-row { position: relative; display: flex; align-items: center; gap: 10px; height: 40px; padding: 0 12px 0 10px;
  cursor: pointer; background: var(--bg); border: 1px solid var(--border); white-space: nowrap; }
.mpov-row:hover { border-color: var(--border-strong); }
.mpov-row input { position: absolute; opacity: 0; width: 0; height: 0; margin: 0; pointer-events: none; }
.mpov-box { flex: none; width: 16px; height: 16px; border: 1px solid var(--border-strong); background: var(--surface);
  display: flex; align-items: center; justify-content: center; }
.mpov-row input:checked + .mpov-box { background: var(--brand); border-color: var(--brand); }
.mpov-row input:checked + .mpov-box::after { content: ''; width: 8px; height: 4px; margin-top: -2px;
  border-left: 2px solid #fff; border-bottom: 2px solid #fff; transform: rotate(-45deg); }
.mpov-row input:focus-visible + .mpov-box { outline: 1px solid var(--accent); outline-offset: 2px; }
.mpov-row:has(input:not(:checked)) > :not(.mpov-box) { opacity: .45; }
.mpov-row img, .mpov-row .mpov-ph { flex: none; width: 26px; height: 26px; object-fit: cover; display: block; background: var(--surface); }
.mpov-name { color: var(--text); font-size: 13px; font-weight: 600; }
.mpov-who { color: var(--text-muted); font-size: 12px; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
.mpov-cur { margin-left: auto; flex: none; font-family: var(--font-mono); font-size: 10px; letter-spacing: .04em;
  color: var(--text-muted); border: 1px solid var(--border); padding: 2px 6px; }
.mpov-comms { margin-top: 10px; }
.lolclip-commsrow .lolclip-commsbtn { padding-left: 0; }

/* scrubber + transport */

/* "// the file" divider */
.lolclip-div { display: flex; align-items: center; gap: 8px; font-family: var(--font-mono); font-size: 11px;
  color: var(--text-faint); letter-spacing: .02em; }
.lolclip-div::after { content: ""; flex: 1; height: 1px; background: var(--border); }

/* the draggable file card */
.lolclip-drag { display: flex; align-items: center; gap: 13px; padding: 11px 12px; background: var(--surface-raised);
  border: 1px solid var(--border); cursor: grab; transition: var(--transition-colors); user-select: none; }
.lolclip-drag:hover { border-color: var(--accent-border); background: var(--surface-hover); }
.lolclip-drag:active { cursor: grabbing; }
.lolclip-drag.is-ghost { opacity: .6; cursor: default; }
.lolclip-drag.is-ghost:hover { border-color: var(--border); background: var(--surface-raised); }
.lolclip-fico { flex: none; position: relative; width: 48px; height: 58px; background: #000;
  border: 1px solid var(--border-strong); display: flex; align-items: center; justify-content: center; }
/* folded corner */
.lolclip-fico::before { content: ""; position: absolute; top: 0; right: 0; width: 13px; height: 13px;
  background: var(--surface); border-left: 1px solid var(--border-strong); border-bottom: 1px solid var(--border-strong); }
.lolclip-tri { width: 0; height: 0; border-left: 11px solid var(--accent); border-top: 7px solid transparent;
  border-bottom: 7px solid transparent; margin-left: 2px; }
.lolclip-ext { position: absolute; bottom: 4px; left: 0; right: 0; text-align: center; font-family: var(--font-mono);
  font-size: 8px; letter-spacing: .08em; color: var(--text-muted); }
.lolclip-fmeta { min-width: 0; flex: 1; }
.lolclip-fname { font-family: var(--font-mono); font-size: 13px; color: var(--text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lolclip-fsub { margin-top: 3px; font-family: var(--font-mono); font-size: 11px; color: var(--text-muted);
  font-variant-numeric: tabular-nums; }
.lolclip-grip { flex: none; display: flex; align-items: center; gap: 8px; color: var(--text-muted); }
.lolclip-drag-hint { font-size: 11px; font-family: var(--font-mono);
  animation: lolClipDragPulse 1.8s var(--ease) infinite; }
.lolclip-dots { font-size: 17px; line-height: 1; letter-spacing: -2px; }

/* ── footer ── */
.lolclip-foot { display: flex; align-items: center; gap: 10px; padding: 12px 18px; border-top: 1px solid var(--border); }
.lolclip-msg { flex: 1; font-size: 12px; color: var(--text-muted); display: flex; align-items: center; gap: 6px; }
.lolclip-saved { display: inline-flex; align-items: center; gap: 6px; color: var(--green); font-weight: 600;
  animation: lolClipCheck .18s var(--ease); }
.lolclip-btn { height: 36px; padding: 0 16px; background: var(--surface-raised); border: 1px solid var(--border);
  color: var(--text); font-size: 14px; font-weight: 600; cursor: pointer; transition: var(--transition-colors); }
.lolclip-btn:hover { background: var(--surface-hover); }
.lolclip-btn.is-primary { background: var(--brand); border-color: var(--brand); color: #fff;
  display: inline-flex; align-items: center; gap: 7px; }
.lolclip-btn.is-primary:hover { background: var(--brand-hover); border-color: var(--brand-hover); }
.lolclip-btn.is-primary.is-done { background: var(--indigo-700); border-color: var(--indigo-700); }
.lolclip-btn:disabled { opacity: .45; cursor: not-allowed; }
.lolclip-btn.is-primary:disabled:hover { background: var(--brand); border-color: var(--brand); }

/* ── Compact events feed (2026-08-23 redesign) ─────────────────────────── */
.ef-feed { gap: 8px; padding: 8px; background: var(--bg); }
.ef-chip .n { color: var(--text-faint); font-variant-numeric: tabular-nums; margin-left: 1px; }
.ef-chip.on .n { color: var(--accent); }
.ef-hint { flex: none; display: flex; align-items: center; gap: 6px; height: 25px; padding: 0 10px; border-top: 1px solid var(--border); font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); }
.seq { background: var(--surface); border: 1px solid var(--border); }
.seq-head { display: flex; align-items: center; gap: 8px; height: 30px; padding: 0 10px; background: color-mix(in srgb, var(--surface-raised) 50%, var(--surface)); border-bottom: 1px solid var(--border); cursor: pointer; user-select: none; }
.seq-head:hover { background: var(--surface-hover); }
.seq-head .chev { font-size: 9px; color: var(--text-faint); }
.seq-head .lbl { font-family: var(--font-mono); font-size: 11.5px; font-weight: 600; letter-spacing: .02em; color: var(--text); white-space: nowrap; }
.seq-body { display: flex; flex-direction: column; }
.seq-div { display: flex; align-items: center; gap: 7px; height: 22px; padding: 0 10px; cursor: pointer; background: var(--surface-raised); border-top: 1px solid var(--border); font-family: var(--font-mono); font-size: 9.5px; letter-spacing: .05em; color: var(--text-muted); white-space: nowrap; }
.seq-div:hover { background: var(--surface-hover); }
.seq-body > :first-child { border-top: none; }
.seq .ph-ward, .seq-div .ph-ward { display: inline-flex; align-items: center; gap: 2px; height: 18px; padding: 0 5px; margin: 0; background: transparent; border: 1px solid transparent; color: var(--cyan); cursor: pointer; flex: none; }
.seq .ph-ward:hover, .seq-div .ph-ward:hover { background: rgba(0,212,212,.12); border-color: rgba(0,212,212,.34); }
.seq .ph-ward svg { width: 13px; height: 13px; display: block; }
.seq .ph-ward .n { font-family: var(--font-mono); font-size: 9px; font-weight: 700; }
.ev-row { display: flex; align-items: center; gap: 8px; height: 36px; padding: 0 10px; cursor: pointer; user-select: none; background: var(--surface); border-top: 1px solid var(--border); transition: background .1s; }
.ev-row:hover { background: var(--surface-hover); }
.ev-t { flex: none; width: 34px; text-align: right; font-family: var(--font-mono); font-size: 11.5px; font-variant-numeric: tabular-nums; color: var(--text-faint); }
.ev-bar { flex: none; width: 3px; height: 20px; }
.ev-txt { display: flex; align-items: baseline; gap: 5px; flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; }
.ev-lead { flex: none; font-size: 12.5px; line-height: 1.5; white-space: nowrap; }
.ev-sep { flex: none; font-size: 12.5px; line-height: 1.5; }
.ev-nm { font-size: 12.5px; font-weight: 700; white-space: nowrap; line-height: 1.5; color: var(--text); }
.ev-row.dim .ev-nm { color: var(--text-muted); }
.ev-tail { flex: none; font-family: var(--font-mono); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--text-faint); }
.ev-tail.win { color: var(--green); } .ev-tail.loss { color: var(--red); } .ev-tail.acc { color: var(--accent); } .ev-tail.mut { color: var(--text-muted); }
.ev-row.has-note .ev-lead, .ev-row.has-note:not(:has(.ev-lead)) .ev-nm { text-decoration: underline; text-underline-offset: 2px; }
.ev-hp { flex: none; display: flex; width: 36px; height: 5px; background: var(--bg); border: 1px solid var(--border); box-sizing: content-box; }
.ev-hp .rem { display: block; height: 100%; background: var(--green); }
.ev-hp .lost { display: block; height: 100%; background: var(--red); }
.ev-port { flex: none; width: 20px; height: 20px; border: 1px solid var(--border); background-color: var(--bg); background-size: cover; background-position: center; }
.ev-port.sm { width: 14px; height: 14px; }
.ev-note { flex: none; width: 8px; height: 8px; background: var(--note-purple); }
/* The current row is outlined by recoloring the hairlines it already shares
   with its neighbours — never a ring that would thicken the rule. */
.ev-row.kb-sel, .seq-div.kb-sel, .seq-head.kb-sel { outline: none; background: rgba(128,128,234,.05); box-shadow: inset 1px 0 0 var(--accent-border), inset -1px 0 0 var(--accent-border), inset 0 1px 0 var(--accent-border), inset 0 -1px 0 var(--accent-border); }
.ev-row.kb-sel .ev-t { color: var(--accent); }
.ef-feed.ef-following .kb-sel { outline-style: none; }
.seq-foot { background: color-mix(in srgb, var(--surface-raised) 50%, var(--surface)); border-top: 1px solid var(--border); padding-bottom: 3px; cursor: default; user-select: none; }
.rf-head, .rf-row { display: grid; align-items: center; padding: 0 10px; }
.rf-head.cs, .rf-row.cs { grid-template-columns: 1fr 44px 32px 32px 32px; }
.rf-head.nocs, .rf-row.nocs { grid-template-columns: 1fr 32px 32px 32px; }
.rf-head { height: 18px; font-family: var(--font-mono); font-size: 9px; letter-spacing: .03em; color: var(--text-faint); }
.rf-head > span { text-align: right; }
.rf-head .ttl { display: flex; align-items: center; gap: 5px; text-align: left; }
.rf-head .ttl .gl { color: var(--text-muted); }
.rf-head .ttl .tm { margin-left: 2px; }
.rf-row { height: 19px; }
.rf-row .who { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); white-space: nowrap; text-overflow: ellipsis; }
.rf-row.you .who { color: var(--text); }
.rf-row .num { display: flex; align-items: baseline; justify-content: flex-end; gap: 2px; font-family: var(--font-mono); font-size: 11px; font-variant-numeric: tabular-nums; color: var(--text); }
.rf-row:not(.you) .num { color: var(--text-muted); }
.rf-row .num .dl { font-size: 8.5px; font-weight: 700; }
.rf-row .num .dl.k { color: var(--green); } .rf-row .num .dl.d { color: var(--red); } .rf-row .num .dl.a { color: var(--gold); }
.ev-compose { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px 9px; background: var(--surface-raised); border-top: 1px solid var(--accent-border); }
.ev-compose textarea { width: 100%; min-height: 48px; background: var(--surface); border: 1px solid var(--border); color: var(--text); font-family: var(--font-sans); font-size: 12.5px; line-height: 1.45; padding: 7px 9px; outline: none; resize: none; caret-color: var(--accent); border-radius: 0; }
.ev-compose .ec-foot { display: flex; align-items: center; gap: 6px; }
.ev-compose .sw { width: 13px; height: 13px; flex: none; }
.ev-compose .at { font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); }
.ev-compose .sp { flex: 1; }
.ev-compose button { height: 24px; border-radius: 0; font-size: 11px; cursor: pointer; }
.ev-compose .ec-cancel { padding: 0 9px; background: var(--surface); border: 1px solid var(--border); color: var(--text-muted); font-family: var(--font-mono); }
.ev-compose .ec-cancel:hover { color: var(--text); background: var(--surface); }
.ev-compose .ec-save { padding: 0 11px; background: var(--brand); border: 1px solid var(--brand); color: #fff; font-family: var(--font-sans); font-size: 12px; font-weight: 600; }
.ev-compose .ec-save:hover { background: var(--brand-hover); }
.ef-head { height: 30px; padding: 0 10px; gap: 10px; border-bottom: 1px solid var(--border); }
.ef-head .ef-title { font-family: var(--font-mono); font-size: 10px; font-weight: 600; letter-spacing: .08em; color: var(--text-muted); }
.ef-head::after { content: ""; flex: 1; height: 1px; background: var(--border); order: 1; }
.ef-head .ef-busy { order: 2; }
.ef-head .ef-sub { order: 3; font-family: var(--font-mono); font-size: 9px; color: var(--text-faint); white-space: nowrap; }
.ef-filters { padding: 6px 10px; border-bottom: 1px solid var(--border); gap: 5px; }
.ef-chip { height: 23px; padding: 0 8px; font-size: 11px; font-weight: 500; box-sizing: border-box; }
.ef-chip.on { font-weight: 600; box-shadow: none; border-color: var(--accent); color: var(--accent); }

/* ---- Pool viewer (public Apex-pool VOD): header crumbs + profile card ---- */
.crumbs .pv-who { display: inline-flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 12px; color: var(--text); white-space: nowrap; }
.crumbs .pv-who.dim { color: var(--text-muted); }
.crumbs .pv-who i { font-style: normal; font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.crumbs .pv-who.dim i { color: var(--text-faint); }
.crumbs .pv-crest { height: 18px; }
.crumbs .pv-crest.dim { opacity: .8; }
.crumbs .pv-vs { font-family: var(--font-mono); font-size: 11px; color: var(--text-faint); margin: 0 2px; }
.crumbs .pv-patch { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.crumbs .pv-champ { width: 20px; height: 20px; object-fit: cover; background: var(--bg); border: 1px solid var(--border); flex: none; }
/* Patch · date: meta info, right-justified after the toolbar spacer. */
.header .pv-right { font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; flex: none; margin-right: 4px; }

/* Uploader card (Profile Card Banner design): splash banner header + bio
   (pre-wrap, 4-line clamp — newlines are content) + links/tally footer. */
.pool-profile:not([hidden]) { flex: none; display: flex; flex-direction: column; border-bottom: 1px solid var(--border); background: var(--surface); }
.ppb-hero { position: relative; height: 64px; overflow: hidden; border-bottom: 1px solid var(--border); background: var(--surface); }
.ppb-splash { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; object-position: 50% 20%; filter: saturate(0.45) brightness(0.55) contrast(0.9); }
.ppb-wash { position: absolute; inset: 0; background: rgba(12, 13, 19, 0.74); }
.ppb-hero::after { content: ''; position: absolute; left: 0; right: 0; bottom: 0; height: 1px; background: var(--tele-line, rgba(128, 128, 234, .22)); }
.ppb-hero-row { position: absolute; inset: 0; display: flex; align-items: center; gap: 10px; padding: 0 12px; z-index: 1; }
.ppb-av { width: 42px; height: 42px; flex: none; object-fit: cover; border: 1px solid var(--border); background: var(--bg); }
.ppb-av-letter { display: inline-flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 700; color: var(--text-muted); }
.ppb-id { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.ppb-name { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.ppb-name .n { font-size: 17px; font-weight: 600; letter-spacing: -.02em; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ppb-name .t { font-family: var(--font-mono); font-size: 10.5px; color: var(--text-muted); white-space: nowrap; }
.ppb-rankline { display: flex; align-items: center; gap: 7px; min-width: 0; overflow: hidden; }
.ppb-crest { width: 18px; height: 18px; flex: none; display: block; }
.ppb-lp { flex: none; font-family: var(--font-mono); font-size: 10.5px; white-space: nowrap; font-variant-numeric: tabular-nums; }
.ppb-sep { flex: none; width: 1px; height: 11px; background: var(--border-strong); }
.ppb-role { flex: none; display: flex; align-items: center; gap: 4px; }
.ppb-role img { width: 13px; height: 13px; flex: none; opacity: .8; display: block; }
.ppb-role i { font-style: normal; font-family: var(--font-mono); font-size: 10px; color: var(--text-muted); white-space: nowrap; }
.ppb-champs { flex: none; display: flex; gap: 3px; align-self: center; }
.ppb-champ { width: 24px; height: 24px; border: 1px solid var(--border); display: block; background: var(--bg); object-fit: cover; }
.ppb-bio {
  padding: 10px 12px 0; font-size: 12.5px; line-height: 1.45; color: var(--text-muted);
  white-space: pre-wrap; text-wrap: pretty; overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 4; -webkit-box-orient: vertical; overflow: hidden;
}
.ppb-foot { display: flex; align-items: center; gap: 12px; padding: 10px 12px 11px; min-width: 0; }
.ppb-links { flex: 1; min-width: 0; display: flex; align-items: center; gap: 12px; }
.ppb-link { position: relative; flex: 0 1 auto; min-width: 0; display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--text-muted); text-decoration: none; }
.ppb-link:hover { color: var(--accent); text-decoration: none; }
.ppb-fav { width: 13px; height: 13px; flex: none; display: block; }
.ppb-link-t { min-width: 0; max-width: 150px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ppb-tip {
  display: none; position: absolute; left: 0; bottom: calc(100% + 7px); z-index: 20;
  width: max-content; max-width: 300px; flex-direction: column; gap: 3px; padding: 7px 9px;
  background: var(--surface-raised); border: 1px solid var(--border);
  box-shadow: var(--shadow-menu, 0 12px 40px rgba(0, 0, 0, .5)); pointer-events: none;
}
.ppb-link:hover .ppb-tip { display: flex; }
.ppb-tip b { font-family: var(--font-sans); font-size: 12px; font-weight: 400; line-height: 1.3; color: var(--text); overflow-wrap: anywhere; }
.ppb-tip i { font-style: normal; font-family: var(--font-mono); font-size: 10px; line-height: 1.3; color: var(--text-faint); overflow-wrap: anywhere; }
.ppb-tally { flex: none; font-family: var(--font-mono); font-size: 10.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; white-space: nowrap; margin-left: auto; }

/* ── Buffering wheel (streamed VODs) ─────────────────────────────────────────
   Lives in whichever positioned host holds the <video> (.video-stage or the
   theater's .th-videohost — review.js re-appends it at show time). display
   scoped under :not([hidden]) (author display beats the hidden attribute);
   radius pinned !important against the squared/telemetry reset. */
.vid-buffer:not([hidden]) {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  pointer-events: none; z-index: 6;
}
.vb-arc {
  width: 34px; height: 34px; border: 3px solid rgba(255, 255, 255, .18);
  border-top-color: var(--accent, #4ea1ff); border-radius: 50% !important;
  animation: vb-spin .8s linear infinite;
  filter: drop-shadow(0 2px 8px rgba(0, 0, 0, .55));
}
@keyframes vb-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .vb-arc { animation-duration: 1.6s; } }

/* POV bar: the team divider between the ally and enemy groups */
.pov-bar .pov-vs { font-family: var(--font-mono); font-size: 10px; letter-spacing: .06em; color: var(--text-faint); margin: 0 5px; user-select: none; }

/* ── Team mode ─────────────────────────────────────────────────────────────
   Compose scope square (current POV's champion / fill icon), the note card's
   scope+author chip, the "included" dropdown and the header crumbs. */
.note-scope { width: 32px; height: 32px; flex: none; display: inline-flex; align-items: center; justify-content: center; padding: 0; background: var(--surface-raised); border: 1px solid var(--border); color: var(--text-muted); cursor: pointer; overflow: hidden; font-family: var(--font-mono); font-size: 12px; font-weight: 600; }
.note-scope:hover { border-color: var(--accent-border); color: var(--text); }
.note-scope img { width: 100%; height: 100%; object-fit: cover; display: block; }
.note-scope.global { color: var(--accent); border-color: var(--accent-border); background: var(--accent-soft); }
/* the fill role icon is a padded glyph, not a champion square: inset it */
.note-scope img.fill { width: 22px; height: 22px; object-fit: contain; }
/* A note about several players: the lead player's champion with "+x" for the
   rest, on the scope squares and beside a card's scope icon. */
.note-scope { position: relative; }
.note-scope .ns-more { position: absolute; right: 0; bottom: 0; padding: 0 2px; font: 700 9px/12px var(--font-mono); color: #fff; background: rgba(12,13,19,.85); }
.note-scope-more { margin: 0 7px 0 -4px; font: 700 9px var(--font-mono); color: var(--text-muted); vertical-align: 1px; }
.note-scope-ic.multi { margin-right: 6px; }
.note-scope-ic[data-scope-note] { cursor: context-menu; }
.cm-scope-ic { width: 16px; height: 16px; flex: none; object-fit: cover; display: block; border: 1px solid var(--border); background: var(--surface); }
.cm-scope-ic.fill { object-fit: contain; border-color: transparent; background: none; }
/* Replay save dialog: the scope square beside the title (team reviews). */
.rr-row { display: flex; align-items: stretch; gap: 6px; }
.rr-row .rr-title { flex: 1; min-width: 0; }
.rr-row .note-scope { height: auto; min-height: 32px; }
/* Note card: the scope icon follows the note icon at the head of the title
   (same 14px box, inline so a wrapped title flows under both); the author
   is a faint line of its own between the title and the body. */
.note-scope-ic { display: inline-block; width: 14px; height: 14px; margin-right: 7px; vertical-align: -2px; border: 1px solid var(--border); background: var(--surface); color: var(--text-muted); font-family: var(--font-mono); font-size: 9px; font-weight: 600; line-height: 12px; text-align: center; overflow: hidden; }
/* block children: the box then has no line box and its baseline is its
   bottom edge, exactly like the note icon's svg, so the two sit level */
.note-scope-ic img { width: 100%; height: 100%; object-fit: cover; display: block; }
.note .note-by { margin: -4px 0 0; font-family: var(--font-mono); font-size: 10px; color: var(--text-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The "included" picker wears the icon / colour filters' label style (the
   same .filter-count span); its own mono rule was the odd one out. */
/* Rows are full-width and read from the left edge (explicit: the app once
   showed them packed against the right edge). */
.cpick.open .cpick-pop-inc { display: flex; flex-direction: column; align-items: stretch; gap: 2px; width: max-content; min-width: 220px; padding: 6px; }
.nt-inc-row { display: flex; align-items: center; justify-content: flex-start; width: 100%; margin: 0; box-sizing: border-box; text-align: left; gap: 8px; padding: 5px 6px; font-family: var(--font-mono); font-size: 11px; color: var(--text); cursor: pointer; user-select: none; white-space: nowrap; }
.nt-inc-row:hover { background: var(--surface-hover); }
.nt-inc-row input { margin: 0; accent-color: var(--accent); }
.nt-inc-row.dim { opacity: .4; }
.nt-inc-row.cur { color: var(--accent); }
.nt-inc-ic { width: 16px; height: 16px; flex: none; object-fit: cover; border: 1px solid var(--border); display: inline-flex; align-items: center; justify-content: center; font-size: 10px; color: var(--text-muted); }
.nt-inc-ic.fill { object-fit: contain; padding: 1px; background: var(--accent-soft); border-color: var(--accent-border); }
.nt-inc-ic.cur { border: none; color: var(--accent); font-size: 14px; }
.nt-inc-cur { margin-top: 4px; padding-top: 4px; border-top: 1px solid var(--border); }
.team-mode .crumbs .tm-team { font-family: var(--font-mono); font-size: 10px; font-weight: 700; letter-spacing: .06em; color: var(--accent); padding: 2px 6px; border: 1px solid var(--accent-border); background: var(--accent-soft); }
.team-mode .crumbs .pv-res.win { color: var(--green); }
.team-mode .crumbs .pv-res.loss { color: var(--red); }

/* Right-click "Show in events tab": the row it landed on flashes once. */
.ev-row.ctx-flash { box-shadow: inset 0 0 0 1px var(--accent); transition: box-shadow .9s ease .6s; }

/* Vision rows: the eye at the row's end opens the placement modal; the row
   itself seeks like every other event. */
.ev-row .ev-eye { flex: none; margin-left: 6px; width: 18px; height: 18px; display: inline-flex; align-items: center; justify-content: center; color: var(--text-muted); border: 1px solid transparent; cursor: pointer; }
.ev-row .ev-eye:hover { color: var(--accent); border-color: var(--accent-border); }
.ev-row .ev-eye svg { width: 13px; height: 13px; }

/* ── Video-only review strip (review.js renderBareNotice) ──────────────────
   Sits between the toolbar and the layout grid; the grid's fixed height gives
   the strip its 34px so nothing scrolls. */
.rv-bare {
  display: flex; align-items: center; gap: 12px;
  height: 34px; padding: 0 14px;
  background: var(--surface-raised); border-bottom: 1px solid var(--border);
  color: var(--text-muted); font-size: 12.5px; line-height: 1.3;
}
.rv-bare b { color: var(--gold); font-weight: 600; }
.rv-bare-text { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rv-bare-btn {
  flex: none; height: 24px; padding: 0 10px;
  background: var(--surface); color: var(--text); border: 1px solid var(--border-strong);
  font: inherit; font-size: 12px; cursor: pointer;
}
.rv-bare-btn:hover:not(:disabled) { background: var(--surface-hover); }
.rv-bare-btn:disabled { opacity: .6; cursor: default; }
body.has-bare .review-layout { height: calc(100dvh - 84px - 34px); }
