/*
 * AutoGrind — telemetry console
 *
 * A from-scratch presentation layer. The previous iterations were boxed
 * "card grid" dashboards; this one is an open, flat operations surface in the
 * spirit of Linear / Vercel / Raycast: hairline dividers instead of card
 * borders, a single teal accent, display-size numerals, and dense data tables
 * with no chrome around them.
 */

:root {
  --font-sans: "IBM Plex Sans", ui-sans-serif, system-ui, sans-serif;
  --font-display: "Space Grotesk", var(--font-sans);
  --font-mono: "JetBrains Mono", ui-monospace, monospace;
  /* The rail is what the layout reserves; --sidebar-w is how wide the panel
   * grows to when it opens. The panel overlays the page rather than widening
   * its column, so opening it never reflows the content beside it. */
  --rail-w: 64px;
  --sidebar-w: 236px;
  --topbar-h: 60px;
  --content-max: 1280px;
  --gutter: 36px;
  --radius: 10px;
  --radius-sm: 7px;
  --ease: cubic-bezier(.22, 1, .36, 1);
  /* Sidebar reveal. Slightly softer than --ease so the panel settles rather
   * than snapping, and slow enough to read as a reveal at 172px of travel. */
  --sidebar-ease: cubic-bezier(.32, .72, 0, 1);
  --sidebar-dur: 260ms;
  --dur: 160ms;
  --dur-slow: 320ms;

  /* Dark (default) */
  --bg: #0a0b0d;
  --bg-soft: #0e0f12;
  --surface: #131417;
  --surface-2: #181a1e;
  --hairline: #22242a;
  --hairline-soft: #1a1c21;
  --text: #eceef1;
  --text-2: #9ba1ac;
  /* Same story as light's --text-3: 3.14:1 on --surface-2 for 10px uppercase
   * table headers. Lifted to clear 4.5:1 on every surface. */
  --text-3: #7b838f;
  /* Monochrome accent: the UI carries no brand hue at all, so colour means
   * status and nothing else. Primary actions and the active nav item read as
   * light-on-dark rather than as a colour. */
  --accent: #f0f2f5;
  --accent-strong: #ffffff;
  --on-accent: #0a0b0d;
  --accent-soft: rgba(240, 242, 245, .10);
  --success: #3ddc97;
  --warning: #f5b64a;
  --danger: #fb7185;
  --info: #63a8f5;
  --success-soft: rgba(61, 220, 151, .13);
  --warning-soft: rgba(245, 182, 74, .13);
  --danger-soft: rgba(251, 113, 133, .13);
  --info-soft: rgba(99, 168, 245, .13);
  --row-hover: rgba(255, 255, 255, .035);
  --overlay: rgba(3, 4, 6, .6);
  --focus-ring: 0 0 0 3px rgba(240, 242, 245, .22);
  /* Off-state switch track. Derived from --text-3 rather than a surface token
   * so it stays visible on every background a switch can sit on — the page,
   * a card (--surface) and a dialog (--surface-2) alike. */
  --track-off: color-mix(in srgb, var(--text-3) 30%, transparent);
  --track-off-border: color-mix(in srgb, var(--text-3) 42%, transparent);
  /* Switch on-state. The track used to be --accent, i.e. the same near-white as
   * a primary button fill, and the knob --on-accent, which is the *exact* page
   * background: the knob read as a hole punched in a small white pill rather
   * than a knob sitting on a track, so an on switch did not read as a switch.
   * Colour now carries the state (the house rule for colour is status, and a
   * switch is status) while the knob stays one constant bright object that
   * simply moves — the cue that makes it legible as a switch at all. */
  --switch-on: var(--success);
  --switch-on-border: var(--success);
  --switch-knob: #ffffff;
  --switch-knob-edge: rgba(6, 8, 11, .38);

  /* Compatibility aliases used by page scripts */
  --background: var(--bg);
  --foreground: var(--text);
  --card: var(--surface);
  --card-foreground: var(--text);
  --popover: var(--surface-2);
  --popover-foreground: var(--text);
  --primary: var(--accent);
  --primary-foreground: var(--on-accent);
  --secondary: var(--surface-2);
  --secondary-foreground: var(--text-2);
  --muted: var(--surface);
  --muted-foreground: var(--text-3);
  --border: var(--hairline);
  --border-subtle: var(--hairline-soft);
  --input: var(--hairline);
  --ring: var(--accent);
  /* NOTE: do NOT re-declare --surface / --surface-2 / --row-hover here as
   * `var(--surface)` etc. A custom property that references itself is invalid
   * at computed-value time, which makes it resolve to nothing — every
   * `background: var(--surface-2)` then falls back to `transparent`. That is
   * what made the account popover, buttons, inputs and modals see-through.
   * These three are already defined in the palette block above. */
  --rail: var(--bg-soft);
  --fg-2: var(--text-2);
  --fg-3: var(--text-3);
  --border-soft: var(--hairline-soft);
  --border-strong: var(--hairline);
  --primary-soft: var(--accent-soft);
  --accent-hi: var(--accent);
  --accent-lo: var(--accent-soft);
  --accent-on: var(--on-accent);
  --accent-color: var(--accent);
  --border-color: var(--hairline);
  --text-muted: var(--text-3);
  --color-success: var(--success);
  --color-danger: var(--danger);
  --line: var(--hairline);
  --panel: var(--surface);
  --card-bg: var(--surface);
  --rule: 1px solid var(--hairline);
  --rule-soft: 1px solid var(--hairline-soft);
  --ctl-h: 36px;
  --ctl-h-sm: 32px;
  --r-inner: 7px;
  --r-ctl: 8px;
  --label: 11px;
  --track: .06em;

  /* shadcn contract completion — these are the only names src/tailwind.css's
   * @theme mapping needs that aren't already aliased above. Defined here so
   * every token has exactly one source of truth (see src/tailwind.css). */
  --accent-foreground: var(--on-accent);
  --destructive: var(--danger);
  --destructive-foreground: #ffffff;
  --sidebar: var(--bg-soft);
  --sidebar-foreground: var(--text);
  --sidebar-primary: var(--accent);
  --sidebar-primary-foreground: var(--on-accent);
  --sidebar-accent: var(--surface);
  --sidebar-accent-foreground: var(--text);
  --sidebar-border: var(--hairline);
  --sidebar-ring: var(--accent);
}

html[data-theme="light"] {
  --bg: #fbfbfc;
  --bg-soft: #f4f5f7;
  --surface: #ffffff;
  --surface-2: #f4f5f7;
  --hairline: #e4e6eb;
  --hairline-soft: #eceef2;
  --text: #16181d;
  --text-2: #565c67;
  /* Was #8a919c: 3.07:1 on the page and 2.91:1 on a card, i.e. under even the
   * 3:1 large-text floor — and this is the token behind every 10-12.5px label,
   * table header and hint on the page, where the size demands *more* contrast,
   * not less. Now clears 4.5:1 on every surface a muted string can sit on. */
  --text-3: #6a717d;
  --accent: #16181d;
  --accent-strong: #000000;
  --on-accent: #ffffff;
  --accent-soft: rgba(22, 24, 29, .07);
  --success: #0b8059;
  --warning: #9f620d;
  --danger: #da1c46;
  --info: #2b6cda;
  --row-hover: rgba(20, 24, 30, .04);
  --overlay: rgba(20, 24, 30, .4);
  --focus-ring: 0 0 0 3px rgba(22, 24, 29, .18);
  /* Same knob in both themes; the ring is what keeps a white knob visible on
   * light theme's pale off-track. */
  --switch-on: var(--success);
  --switch-on-border: var(--success);
  --switch-knob: #ffffff;
  --switch-knob-edge: rgba(20, 24, 30, .30);
}

*, *::before, *::after { box-sizing: border-box; }

/* Any component rule that sets `display` (grid/flex) outranks the `hidden`
 * attribute's UA `display:none`, which silently expands collapsed cards and
 * panels. Keep this ahead of the component layer. */
[hidden] { display: none !important; }

html { min-width: 320px; background: var(--bg); color-scheme: dark; }
html[data-theme="light"] { color-scheme: light; }
body {
  min-width: 320px;
  min-height: 100vh;
  margin: 0;
  color: var(--text);
  background: var(--bg);
  font-family: var(--font-sans);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}
a { color: inherit; text-decoration: none; }
button { font: inherit; border: 0; background: none; }
svg { display: block; }
::selection { background: rgba(240, 242, 245, .22); }
:focus-visible { outline: none; box-shadow: var(--focus-ring); }

.skip-link {
  position: fixed; z-index: 400; top: 10px; left: 10px;
  padding: 9px 14px; color: var(--on-accent); background: var(--accent);
  border-radius: var(--radius-sm); transform: translateY(-200%);
  transition: transform var(--dur) var(--ease);
}
.skip-link:focus { transform: none; }

/* Layout ------------------------------------------------------------------ */
.app { display: grid; grid-template-columns: var(--rail-w) minmax(0, 1fr); min-height: 100vh; }
/* The rail is position:fixed, so it is out of flow and does not claim column 1.
 * Without an explicit placement .app-main auto-flows into that first (rail-width)
 * column and the entire page collapses to ~64px wide. Column 1 stays an empty
 * gutter that the fixed rail sits over. */
.app-main { grid-column: 2; min-width: 0; }

/* Fixed, not sticky: the grid column above holds the rail's width open, and the
 * panel expands over the page on top of it. Widening the column instead would
 * relayout every table and card on the page each time the pointer brushed past
 * the rail. */
aside.sidebar {
  position: fixed; inset: 0 auto 0 0; z-index: 40;
  display: flex; flex-direction: column;
  width: var(--rail-w); height: 100vh;
  overflow: hidden auto;
  scrollbar-width: none;
  background: var(--bg-soft);
  border-right: 1px solid var(--hairline);
  transition:
    width var(--sidebar-dur) var(--sidebar-ease),
    box-shadow var(--sidebar-dur) var(--sidebar-ease);
}
aside.sidebar::-webkit-scrollbar { width: 0; }
html[data-sidebar="expanded"] aside.sidebar {
  width: var(--sidebar-w);
  box-shadow: 10px 0 38px rgba(0, 0, 0, .30);
}
/* 17px = (64 - 30) / 2, so the brand mark is optically centred in the rail and
 * then stays exactly where it is as the panel opens. Nothing slides; only the
 * label arrives. Animating justify-content instead would make every icon jump. */
.sidebar-brand { display: flex; align-items: center; gap: 10px; height: var(--topbar-h); padding: 0 17px; border-bottom: 1px solid var(--hairline); }
.sidebar-brand-mark { display: grid; flex: 0 0 30px; place-items: center; width: 30px; height: 30px; color: var(--on-accent); background: var(--accent); border-radius: 8px; }
.sidebar-brand-mark svg { width: 17px; height: 17px; }
.sidebar-brand-text { font-family: var(--font-display); font-size: 14px; font-weight: 600; letter-spacing: -.02em; }

/* Labels are always in the DOM — clipped and faded, never display:none — so they
 * can animate, and so a screen reader still reads a rail link by its name rather
 * than announcing a bare icon. */
.sidebar-brand-text, .rail-link-text {
  opacity: 0; transform: translateX(-6px);
  transition:
    opacity 90ms ease,
    transform 140ms var(--sidebar-ease);
}
html[data-sidebar="expanded"] .sidebar-brand-text,
html[data-sidebar="expanded"] .rail-link-text {
  opacity: 1; transform: none;
  /* Held back so the text arrives into space the panel has already opened,
   * instead of racing the width out and being clipped mid-fade. */
  transition:
    opacity 150ms ease 90ms,
    transform 220ms var(--sidebar-ease) 90ms;
}

.auth-slot { padding: 10px 11px; border-bottom: 1px solid var(--hairline); }
.auth-rail { position: relative; }
.auth-rail::before { content: "Workspace"; display: block; padding: 2px 8px 6px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.auth-rail-trigger { display: flex; align-items: center; width: 100%; gap: 9px; padding: 6px 8px; color: var(--text); border-radius: 8px; cursor: pointer; transition: background var(--dur) ease; }
.auth-rail-trigger:hover, .auth-rail.is-open .auth-rail-trigger { background: var(--surface-2); }
.auth-rail-trigger::after { content: ""; width: 7px; height: 7px; margin-left: auto; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor; transform: rotate(45deg) translateY(-2px); opacity: .5; }
.auth-rail-initial { display: grid; flex: 0 0 26px; place-items: center; width: 26px; height: 26px; color: var(--on-accent); background: var(--accent); border-radius: 7px; font-size: 11px; font-weight: 600; }
.auth-rail-popover { position: absolute; z-index: 100; top: 46px; left: 2px; width: 226px; max-height: calc(100vh - var(--topbar-h) - 24px); overflow: hidden auto; overscroll-behavior: contain; padding: 5px; color: var(--text); background: var(--surface-2); border: 1px solid var(--hairline); border-radius: 10px; box-shadow: 0 16px 48px rgba(0,0,0,.35); animation: rise var(--dur) var(--ease); }
.auth-rail-popover[hidden] { display: none; }
.auth-rail-popover-head { display: flex; align-items: center; justify-content: space-between; padding: 9px 11px; border-bottom: 1px solid var(--hairline-soft); }
.auth-rail-popover-head strong { font-size: 13px; }
.auth-rail-popover-head span, .auth-rail-popover-note { color: var(--text-3); font-size: 11px; }
.auth-rail-popover-note { padding: 8px 11px; }
.auth-rail-section { padding: 6px 0; border-bottom: 1px solid var(--hairline-soft); }
.auth-rail-section-label { padding: 2px 11px 5px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.auth-rail-user-list { display: grid; gap: 1px; }
.auth-rail-user-button, .auth-rail-link { display: flex; align-items: center; width: 100%; gap: 8px; padding: 8px 11px; color: var(--text); border-radius: 7px; cursor: pointer; text-align: left; }
.auth-rail-user-button:hover, .auth-rail-link:hover { background: var(--surface); }
.auth-rail-user-button.is-current { color: var(--accent); }
.auth-rail-user-name { flex: 1; font-size: 12px; }
.auth-rail-user-meta { color: var(--text-3); font-size: 10px; }
.auth-rail-popover-actions { display: grid; gap: 1px; padding-top: 4px; }
.auth-rail-link--logout { color: var(--danger); }

.sidebar-nav { display: flex; flex-direction: column; padding: 10px; }
/* Groups are separated by a hairline instead of an uppercase heading. :empty
 * matters because role gating can leave a group with no links (a peasant has
 * no Admin group), and an empty group would otherwise draw a stray rule. */
.sidebar-group { display: flex; flex-direction: column; gap: 1px; padding: 9px 0; }
.sidebar-group:empty { display: none; }
.sidebar-group + .sidebar-group { border-top: 1px solid var(--hairline); }
.sidebar-group:first-child { padding-top: 0; }
.rail-link { position: relative; display: flex; align-items: center; gap: 11px; min-height: 34px; padding: 0 14px; color: var(--text-2); border-radius: 7px; transition: color var(--dur) ease, background var(--dur) ease; }
.rail-link svg { flex: 0 0 16px; width: 16px; height: 16px; }
.rail-link:hover { color: var(--text); background: var(--surface); }
.rail-link.active { color: var(--text); background: var(--surface); }
.rail-link.active::before { content: ""; position: absolute; left: 0; top: 8px; bottom: 8px; width: 2px; background: var(--accent); border-radius: 2px; }
.rail-link.active svg { color: var(--accent); }
.rail-link-text { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.rail-spacer { flex: 1; }

/* Top bar ----------------------------------------------------------------- */
.topbar {
  position: sticky; top: 0; z-index: 20;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 18px;
  min-height: var(--topbar-h);
  /* The bar itself spans the full width (its background and bottom rule should),
   * but its CONTENT lines up with the centred --content-max column that .shell
   * uses below it. Without this the title and the action buttons drifted further
   * from the page content the wider the window got — 566px out of line each side
   * on a 2476px display, since .shell stops growing at 1280px and the topbar
   * never did. Collapses to a plain gutter once the window is narrower than the
   * column. */
  padding-block: 9px;
  padding-inline: max(var(--gutter), calc(50% - var(--content-max) / 2 + var(--gutter)));
  background: color-mix(in srgb, var(--bg) 92%, transparent);
  border-bottom: 1px solid var(--hairline);
  backdrop-filter: blur(10px);
}
.topbar-block { display: flex; align-items: center; min-width: 0; }
.topbar-block:first-child { display: block; }
.topbar h1 { margin: 1px 0 0; font-family: var(--font-display); font-size: 17px; font-weight: 600; letter-spacing: -.03em; line-height: 1.15; }
.eyebrow { margin: 0 0 3px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .12em; line-height: 1.2; text-transform: uppercase; }
.topbar-status { gap: 9px; }
.topbar-status-copy { display: grid; gap: 1px; min-width: 0; }
.topbar-status-copy strong { overflow: hidden; font-size: 12px; text-overflow: ellipsis; white-space: nowrap; }
.topbar-status-copy span { overflow: hidden; color: var(--text-3); font-size: 11px; text-overflow: ellipsis; white-space: nowrap; }
.topbar-actions { flex-wrap: wrap; gap: 8px; margin-left: auto; justify-content: flex-end; }
/* The system group pushes itself to the right edge. It used to rely on a
 * preceding page-actions group to do that for it, so on the pages that have no
 * topbar buttons of their own (Account Modifier, SPC Generator, SPC Refresh)
 * the avatar and theme toggle sat jammed against the page title. */
.topbar-actions--system { flex: 0 0 auto; margin-left: auto; }
/* When the page does supply its own buttons, the two groups stay together as
 * one cluster on the right rather than splitting the free space between them.
 *
 * `~`, not `+`: the sidebar menu button sits between the two groups in source
 * order (it is display:none on desktop and order:-1 when it shows). With the
 * adjacent combinator that button broke the match, so both groups kept
 * margin-left:auto, split the free space evenly, and stranded the page's own
 * button in the middle of the topbar. */
.topbar-actions:not(.topbar-actions--system) ~ .topbar-actions--system { margin-left: 0; }
/* Sizing intentionally left to .icon-btn (--ctl-h). This rule used to pin 34px,
 * but .icon-btn is declared later at the same specificity and always won, so
 * the toggle has in fact rendered at 36px. Kept as a hook for the icon swap. */
.theme-toggle-icon { display: none; }
.theme-toggle-button[data-theme="dark"] .theme-toggle-icon-moon, .theme-toggle-button:not([data-theme="light"]) .theme-toggle-icon-moon { display: block; }
.theme-toggle-button[data-theme="light"] .theme-toggle-icon-sun { display: block; }

/* Buttons ----------------------------------------------------------------- */
.button {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--ctl-h); gap: 7px; padding: 0 13px;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  font-size: 13px; font-weight: 500; line-height: 1; white-space: nowrap; cursor: pointer;
  transition: background var(--dur) ease, border-color var(--dur) ease, color var(--dur) ease, transform 90ms var(--ease);
}
.button:hover { background: var(--hairline); border-color: var(--hairline); }
.button:active { transform: translateY(1px); }
.button:disabled { opacity: .45; cursor: not-allowed; transform: none; }
.button svg { width: 15px; height: 15px; }
.button-sm { min-height: var(--ctl-h-sm); padding: 0 10px; border-radius: var(--r-inner); font-size: 12px; }
/* .button-lg (42px) removed: its only two uses were primary submits dropped
 * into rows of 36px and 32px buttons, so the one control meant to look
 * deliberate was just the one that did not line up. Two sizes now: 36px for
 * page-level actions, .button-sm 32px for toolbars and table rows. */
.button-solid { color: var(--on-accent); background: var(--accent); border-color: var(--accent); }
.button-solid:hover { background: var(--accent-strong); border-color: var(--accent-strong); }
.button-ghost { color: var(--text-2); background: transparent; border-color: transparent; }
.button-ghost:hover { color: var(--text); background: var(--surface-2); }
.button-max { color: var(--accent); background: var(--accent-soft); border-color: transparent; }
.action-danger-outline { color: var(--danger); background: transparent; border-color: color-mix(in srgb, var(--danger) 45%, var(--hairline)); }
.action-danger-outline:hover { background: var(--danger-soft); border-color: var(--danger); }
.icon-btn { display: inline-grid; place-items: center; width: var(--ctl-h); height: var(--ctl-h); padding: 0; color: var(--text-2); background: var(--surface-2); border: 1px solid var(--hairline); border-radius: 8px; cursor: pointer; transition: color var(--dur) ease, background var(--dur) ease, border-color var(--dur) ease; }
.icon-btn:hover { color: var(--text); background: var(--hairline); }
.icon-btn svg { width: 16px; height: 16px; }
.icon-btn--primary { color: var(--accent); }
.icon-btn--danger { color: var(--danger); }
.icon-btn--tracked { color: var(--success); }

input, select, textarea, .input-inline {
  width: 100%; min-height: var(--ctl-h); padding: 0 11px;
  color: var(--text); background: var(--surface);
  border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  outline: none; transition: border-color var(--dur) ease, box-shadow var(--dur) ease, background var(--dur) ease;
}
textarea { min-height: 76px; padding-top: 9px; padding-bottom: 9px; resize: vertical; }
input::placeholder, textarea::placeholder { color: var(--text-3); }
input:hover, select:hover, textarea:hover { border-color: var(--hairline); }
input:focus, select:focus, textarea:focus { border-color: var(--accent); box-shadow: var(--focus-ring); background: var(--surface); }
/* Number spinners are drawn by the platform, not by this stylesheet: a pair of chevrons in a
 * bordered box that matches nothing around it, rendering light on a dark surface. They also
 * occupy the right edge of the field, which is where a unit label ("%", "/hr") belongs -- so
 * the two overlapped. Removed; typing is the primary interaction here and the arrow keys still
 * step the value. */
/* Search inputs get a platform decoration too: a native clear button in Chrome, and in
 * Safari a pill shape that ignores the shared control chrome entirely. */
input[type="search"] { appearance: none; -webkit-appearance: none; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-results-button { -webkit-appearance: none; }

input[type="number"] { appearance: textfield; -moz-appearance: textfield; }
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Native date/time pickers hand back the same problem: an indicator glyph in the platform's
 * own colours. Tint it toward the page rather than leaving a light mark on a dark control. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  filter: invert(62%) sepia(6%) saturate(430%) hue-rotate(185deg) brightness(95%);
  cursor: pointer; opacity: .85;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
html[data-theme="light"] input[type="date"]::-webkit-calendar-picker-indicator,
html[data-theme="light"] input[type="datetime-local"]::-webkit-calendar-picker-indicator,
html[data-theme="light"] input[type="time"]::-webkit-calendar-picker-indicator { filter: none; }

/* Selects are styled rather than left to the platform. The chevron is inlined as a data
 * URI so it needs no asset and inherits the control's colour via stroke. */
select {
  appearance: none;
  padding-right: 32px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%239ba1ac' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 11px center;
  cursor: pointer;
  text-overflow: ellipsis;
}
select:hover { background-color: var(--surface-2); }
select:disabled { cursor: not-allowed; opacity: .55; }
/* The popup list itself is the one part the platform still draws, so give it colours that
 * belong to the page instead of a white sheet on a dark UI. */
select option { background: var(--surface); color: var(--text); }
/* The chevron is a data URI, so its colour cannot come from a token -- it is restated for
 * light, where the dark stroke would sit at roughly 2:1 against the control. */
html[data-theme="light"] select {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12' fill='none' stroke='%23565c67' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 4.5 6 7.5 9 4.5'/%3E%3C/svg%3E");
}
/* Native date/time controls have an intrinsic min-width wider than the grid
 * tracks they sit in, so they overhung the column beside them. */
input[type="date"], input[type="datetime-local"], input[type="time"], input[type="month"], input[type="week"] { min-width: 0; }
/* The range track ignores the shared input chrome; without this it drew a
 * 36px-tall bordered box around a 4px slider. */
input[type="range"] { min-height: 0; height: 20px; padding: 0; background: none; border: 0; accent-color: var(--accent); }
/* Radios were never given a size of their own, so the shared `input` rule above
 * stretched them to width:100% / min-height:36px, and Chrome painted them in
 * its own blue instead of the palette accent. */
input[type="radio"] {
  appearance: none; -webkit-appearance: none;
  flex: 0 0 15px; width: 15px; min-height: 15px; height: 15px; padding: 0; margin: 0;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: 50%;
  cursor: pointer; transition: background var(--dur) ease, border-color var(--dur) ease;
}
input[type="radio"]:hover:not(:disabled) { border-color: var(--text-3); }
input[type="radio"]:checked { background: var(--accent); border-color: var(--accent); }
input[type="radio"]:checked::after {
  content: ""; position: relative; top: 50%; left: 50%;
  display: block; width: 5px; height: 5px; margin: -2.5px 0 0 -2.5px;
  background: var(--on-accent); border-radius: 50%;
}
input[type="radio"]:focus-visible { box-shadow: var(--focus-ring); }
input[type="radio"]:disabled { opacity: .45; cursor: not-allowed; }

/* Drawn rather than native. With appearance:auto the unchecked box painted as
 * a filled square — mid-grey in dark, near-black in light — so "off" read as
 * "on", and :disabled was indistinguishable from :checked. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  flex: 0 0 15px; width: 15px; min-height: 15px; height: 15px; padding: 0; margin: 0;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: 4px;
  cursor: pointer; transition: background var(--dur) ease, border-color var(--dur) ease;
}
input[type="checkbox"]:hover:not(:disabled) { border-color: var(--text-3); }
input[type="checkbox"]:checked { background: var(--accent); border-color: var(--accent); }
/* The tick is a clip-path over a solid fill rather than a glyph, so it needs no
 * font and stays crisp at any zoom. */
input[type="checkbox"]:checked::after {
  content: ""; display: block; width: 100%; height: 100%;
  background: var(--on-accent);
  clip-path: polygon(19% 47%, 9% 58%, 39% 86%, 91% 27%, 80% 17%, 38% 65%);
}
input[type="checkbox"]:focus-visible { box-shadow: var(--focus-ring); }
input[type="checkbox"]:disabled { opacity: .45; cursor: not-allowed; }
input[type="checkbox"]:indeterminate { background: var(--accent); border-color: var(--accent); }
input[type="checkbox"]:indeterminate::after {
  content: ""; display: block; width: 100%; height: 100%;
  background: var(--on-accent);
  clip-path: polygon(20% 42%, 80% 42%, 80% 58%, 20% 58%);
}
label { color: var(--text-2); font-size: 12px; font-weight: 500; }
/* align-content:start for the same reason .field has it: without it a
 * .form-group stretches to the tallest cell in its grid row and the extra
 * height lands on the input, so fields sitting side by side had inputs of
 * different heights sitting at different baselines. */
.form-group { display: grid; align-content: start; gap: 7px; }
.form-group-inline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.form-actions, .action-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

.uid-field { display: flex; align-items: center; gap: 5px; padding: 3px; background: var(--surface); border: 1px solid var(--hairline); border-radius: 9px; }
.uid-field input { width: 200px; min-height: 30px; padding: 0 9px; background: transparent; border-color: transparent; box-shadow: none; font-family: var(--font-mono); font-size: 11px; }
.uid-field input:focus { background: var(--surface); border-color: var(--accent); box-shadow: var(--focus-ring); }
.uid-field .button { min-height: 30px; }

/* Badges, chips, tabs ------------------------------------------------------ */
/* Status is a caption, not an object: no capsule, no border, no fill — just a
 * coloured dot and its label. `.chip` is excluded because it is an interactive
 * filter control and still needs a hit target. */
.status-badge, .player-chip, .read-only-pill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 0; color: var(--text-2); background: none; border: 0; border-radius: 0;
  font-size: 11px; font-weight: 500; white-space: nowrap;
}
.status-badge::before, .player-chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.status-badge.ok, .status-badge.is-live, .player-chip.is-active { color: var(--success); }
.status-badge.warn { color: var(--warning); }
.status-badge.err, .status-badge.is-err, .player-chip.is-danger { color: var(--danger); }
.status-badge.is-syncing::before { animation: pulse 1.1s ease-in-out infinite; }
.read-only-pill { color: var(--text-3); font-size: 9px; letter-spacing: .07em; text-transform: uppercase; }
.read-only-pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }

/* Filter chips stay clickable objects, but squared off rather than capsules. */
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 28px; padding: 0 11px;
  color: var(--text-2); background: var(--surface-2);
  border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  font-size: 11px; font-weight: 500; white-space: nowrap; cursor: pointer;
}
.chip:hover { color: var(--text); }
.chip.active { color: var(--text); background: var(--accent-soft); border-color: var(--text-3); }

.segmented, .window-segments, .drawer-tabs, .my-team-tabs, .ag-events-subtabs, .ag-lb-window-tabs {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 3px; background: var(--surface); border: 1px solid var(--hairline); border-radius: 9px;
}
.segment, .drawer-tab, .ag-events-subtab, .ag-lb-window-tab {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 29px; padding: 0 11px; color: var(--text-2);
  border: 1px solid transparent; border-radius: 6px; font-size: 12px; cursor: pointer;
  transition: color var(--dur) ease, background var(--dur) ease;
}
.segment:hover, .drawer-tab:hover, .ag-events-subtab:hover, .ag-lb-window-tab:hover { color: var(--text); }
.segment.active, .drawer-tab.active, .ag-events-subtab.is-active, .ag-lb-window-tab.active, .ag-lb-window-tab.is-active { color: var(--text); background: var(--surface-2); border-color: var(--hairline); }

/* Content ----------------------------------------------------------------- */
/* Entrance is on .shell, which mounts once per page load — deliberately NOT on
 * .ag-card or table rows, which re-render on every poll and would strobe. */
.shell { width: min(100%, var(--content-max)); margin: 0 auto; padding: 32px var(--gutter) 96px; animation: content-in 260ms var(--ease); }
.muted { color: var(--text-3); }
.panel-message { margin: 0; color: var(--text-3); font-size: 12.5px; }

/* Flat section primitive — replaces the old boxed "panel". Every page that
 * used .panel now renders as an open, hairline-separated section. */
.panel, .drawer-card, .ov-player-panel, .my-team-card {
  min-width: 0;
  background: transparent;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.panel + .panel { border-top: 1px solid var(--hairline); }
/* Two-column grid rather than a flex row: the description sits under its own
 * heading (row 2, column 1) instead of being right-aligned opposite it, where
 * anything longer than ~46 characters wrapped mid-phrase against a ragged
 * right edge. Actions still take column 2. */
.panel-head {
  display: grid; grid-template-columns: minmax(0, 1fr) auto;
  /* center, not start. Column 2 holds a control — a button, a toolbar, a status
   * pill — and aligning it to the *top* of column 1 levelled it with the 10px
   * uppercase eyebrow, leaving it floating above the heading that gives the row
   * its visual weight. .login-card .panel-head had already overridden this to
   * center, which is the look the rest of the app should have had too. */
  align-items: center; gap: 4px 18px; padding: 26px 0 14px;
}
.panel-head > div { min-width: 0; }
.panel-head > :first-child { grid-area: 1 / 1; }
.panel-head > .panel-sub { grid-area: 2 / 1; }
.panel-head > :not(:first-child):not(.panel-sub) { grid-area: 1 / 2; justify-self: end; }
.panel-head h2, .panel-head h3 { margin: 0; font-family: var(--font-display); font-size: 16px; font-weight: 600; letter-spacing: -.02em; }
.panel-head .panel-sub { max-width: 78ch; margin-top: 2px; color: var(--text-3); font-size: 12px; text-align: left; }
.panel > .panel-message, .panel > p.muted { padding: 0 0 14px; }
.panel-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 0 0; border-top: 1px solid var(--hairline-soft); }

/* Stat band — open numbers, no boxes -------------------------------------- */
.summary-row, .my-team-summary-row {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  border-top: 1px solid var(--hairline); border-bottom: 1px solid var(--hairline);
}
.metric { min-width: 0; padding: 26px 28px; border-right: 1px solid var(--hairline-soft); }
.metric:last-child { border-right: 0; }
.metric-label { display: block; margin-bottom: 12px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .11em; text-transform: uppercase; }
.metric-value, .metric strong { display: block; color: var(--text); font-family: var(--font-display); font-size: clamp(26px, 3vw, 40px); font-weight: 600; letter-spacing: -.045em; line-height: 1; font-variant-numeric: tabular-nums; }
.metric-accent { color: var(--accent) !important; }
.metric-sub { display: block; margin-top: 10px; color: var(--text-3); font-size: 11.5px; }
.metric-value.tick, .metric strong.tick { animation: metric-pop 700ms var(--ease); }

/* Tables ------------------------------------------------------------------ */
.table-wrap, .ov-table-wrap { max-width: 100%; overflow: auto; scrollbar-width: thin; scrollbar-color: var(--hairline) transparent; }
.table-wrap::-webkit-scrollbar, .ov-table-wrap::-webkit-scrollbar { width: 8px; height: 8px; }
.table-wrap::-webkit-scrollbar-thumb, .ov-table-wrap::-webkit-scrollbar-thumb { background: var(--hairline); border: 2px solid transparent; border-radius: 999px; background-clip: padding-box; }
.data-table, .ov-table { width: 100%; min-width: 640px; border-collapse: collapse; font-size: 13px; }
.data-table thead th, .ov-table thead th { position: sticky; top: 0; z-index: 2; height: 38px; padding: 0 16px; color: var(--text-3); background: var(--bg); border-bottom: 1px solid var(--hairline); font-size: 10px; font-weight: 600; letter-spacing: .09em; text-align: left; text-transform: uppercase; white-space: nowrap; }
.data-table tbody td, .ov-table tbody td { height: 50px; padding: 8px 16px; color: var(--text); border-bottom: 1px solid var(--hairline-soft); vertical-align: middle; }
.data-table tbody tr { transition: background var(--dur) ease; }
.data-table tbody tr:hover td, .ov-table tbody tr:hover td { background: var(--row-hover); }
.data-table tbody tr:last-child td, .ov-table tbody tr:last-child td { border-bottom: 0; }
.data-table .num, .ov-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.data-table thead th.num, .ov-table thead th.num { text-align: right; }
.data-table tbody td:first-child, .data-table thead th:first-child, .ov-table tbody td:first-child, .ov-table thead th:first-child { padding-left: 2px; }
.data-table tbody td:last-child, .data-table thead th:last-child, .ov-table tbody td:last-child, .ov-table thead th:last-child { padding-right: 2px; }
.data-table .empty, .ov-table .empty { height: 96px; color: var(--text-3); text-align: center; }
/* Was a parallel re-implementation of .button at 28px, which is why row actions
 * were 28px in one table and 32px (.button-sm) in another. Every call site now
 * carries .button.button-sm, so this is a colour variant only — size and states
 * come from the button system like every other control. */
.table-action, .list-row-action { color: var(--text-2); }
.table-action:hover, .list-row-action:hover { color: var(--text); }

/* Detail lists and rows ---------------------------------------------------- */
.detail-list { display: grid; margin: 0; }
.detail-list > div { display: flex; align-items: baseline; justify-content: space-between; gap: 18px; min-height: 37px; padding: 9px 0; border-bottom: 1px solid var(--hairline-soft); }
.detail-list > div:last-child { border-bottom: 0; }
.detail-list dt { color: var(--text-3); font-size: 12px; }
.detail-list dd { max-width: 64%; margin: 0; color: var(--text); font-size: 12.5px; font-weight: 500; text-align: right; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }

.discovery-toolbar, .ov-search-row, .filter-chip-row { display: flex; align-items: center; gap: 8px; padding: 0 0 14px; }
.discovery-toolbar input, .ov-search-row input { flex: 1; min-width: 0; }
.filter-chip-row { flex-wrap: wrap; }
.stack-list { display: flex; flex-direction: column; }
.list-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; min-height: 50px; padding: 9px 0; border-bottom: 1px solid var(--hairline-soft); }
.list-row:last-child { border-bottom: 0; }
.list-row--loading { justify-content: flex-start; color: var(--text-3); }

/* Dashboard composition ---------------------------------------------------- */
.dashboard-shell { display: grid; gap: 8px; }
.dashboard-shell > .summary-row { order: 1; margin: 8px 0 24px; }
.dashboard-shell > #leaderboard { order: 2; }
.dashboard-shell > #multi-select-panel { order: 5; }
.dashboard-shell > .split-grid:not(.split-grid-bottom) { order: 3; }
.dashboard-shell > .split-grid-bottom { order: 4; }
.dashboard-shell > section:not(.summary-row):not(#leaderboard):not(#multi-select-panel):not(.split-grid) { order: 6; }
.split-grid, .split-grid-bottom, .drawer-grid, .ov-player-modal-grid, .player-board-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; min-width: 0; }
/* .split-grid-bottom deliberately shares the 40px gap above. It used to set its
 * own 48px, which made its columns 542px against the 546px of the .split-grid
 * stacked directly on top of it — so on the dashboard "Open Teams" sat 4px right
 * of "Player Detail" and the two sections visibly failed to line up. */
.focus-block { padding: 0 0 12px; }
.focus-block h3 { margin: 0 0 5px; font-family: var(--font-display); font-size: 22px; letter-spacing: -.03em; }
/* Mono suits an id; it made the prose notes ("Select a team from the table…")
 * read like code, so only the id keeps it. */
.focus-block > .muted { margin: 0 0 18px; color: var(--text-3); font-size: 12px; }
.focus-block > #player-id { margin: 0 0 18px; font-family: var(--font-mono); font-size: 11px; }
/* Placeholder headings shouldn't shout at display size before anything is picked. */
.focus-block h3.is-placeholder { color: var(--text-3); font-size: 16px; }
#leaderboard .panel-head { padding-top: 0; }
.multi-select-panel { border-top: 1px solid var(--hairline); padding-top: 4px; }

/* Tracker (overview) ------------------------------------------------------- */
.player-list { display: flex; flex-direction: column; min-width: 0; padding: 0 0 8px; }
.player-item { display: flex; flex-direction: column; gap: 10px; padding: 15px 0; border-bottom: 1px solid var(--hairline-soft); }
.player-item:last-child { border-bottom: 0; }
.player-item-head, .player-item-foot { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; min-width: 0; }
.player-item-head > div:first-child { display: grid; gap: 3px; min-width: 0; }
.player-item-head strong { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player-item-head small { max-width: 100%; color: var(--text-3); font-family: var(--font-mono); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.player-item-head-right, .player-item-actions, .ov-table-actions { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; flex-wrap: wrap; }
.player-item-stats { display: flex; align-items: center; gap: 0; flex-wrap: wrap; min-width: 0; }
.player-item-stats span { color: var(--text-3); font-size: 11.5px; white-space: nowrap; }
.player-item-stats span + span::before { content: "·"; margin: 0 7px; color: var(--hairline); }
.player-item-actions .icon-btn, .ov-table-actions .icon-btn { width: 28px; height: 28px; background: transparent; border-color: transparent; }
.player-item-actions .icon-btn:hover, .ov-table-actions .icon-btn:hover { background: var(--surface-2); }
.player-item-lg { gap: 16px; }
.pid { color: var(--text-3); font-family: var(--font-mono); }
.watchlist-section-title { margin: 0; padding: 22px 0 6px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
.player-board-grid { gap: 0; }
.player-board-grid > div { min-width: 0; }
.player-board-grid > div + div { border-left: 1px solid var(--hairline); padding-left: 40px; }
.ov-hidden-players-wrap { padding: 13px 0; border-top: 1px solid var(--hairline-soft); border-bottom: 1px solid var(--hairline-soft); }
.ov-hidden-players-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.ov-hidden-players-head-copy { display: grid; gap: 2px; }
.ov-hidden-players-head-copy strong { font-size: 12px; }
.ov-hidden-players-head-copy span { font-size: 11px; }
.ov-hidden-player-chip { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid var(--hairline-soft); }
.ov-hidden-player-chip:last-child { border-bottom: 0; }
.ov-hidden-player-copy { display: grid; gap: 2px; min-width: 0; }
.ov-hidden-player-copy strong { font-size: 12px; }
.ov-hidden-player-copy small { color: var(--text-3); font-size: 10px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ov-growth-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 8px; }
.ov-growth-card { display: grid; gap: 3px; padding: 11px; background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: 8px; }
.ov-growth-card small { color: var(--text-3); font-size: 10px; }
.ov-growth-card strong { font-family: var(--font-display); font-size: 14px; }

/* Workspace (My Team) ------------------------------------------------------- */
.team-console { display: grid; gap: 0; }
.connect-strip { display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 28px; padding: 26px 0; border-bottom: 1px solid var(--hairline); }
.connect-strip-main { display: grid; gap: 6px; min-width: 0; }
.connect-strip-main h2 { margin: 0; font-family: var(--font-display); font-size: 19px; font-weight: 600; letter-spacing: -.03em; }
.connect-strip-main p { margin: 0; color: var(--text-3); font-size: 12.5px; max-width: 58ch; }
.connect-strip-side { display: flex; align-items: flex-start; gap: 8px; }
/* flex-start + an auto margin on the trailing button: `space-between` spread
 * the four children evenly, stranding the empty-state sentence mid-row. */
.accounts-strip { display: flex; align-items: center; gap: 18px; padding: 16px 0; border-bottom: 1px solid var(--hairline); }
.accounts-strip > .button:last-child { margin-left: auto; }
.accounts-strip-copy { display: grid; gap: 3px; min-width: 0; }
.accounts-strip-copy strong { font-size: 13px; }
.accounts-strip-copy span { color: var(--text-3); font-size: 12px; }
.accounts-strip #my-team-account-list { display: flex; flex-wrap: wrap; gap: 6px; }
.accounts-strip #my-team-account-empty, .accounts-strip #my-team-account-summary { margin: 0; color: var(--text-3); font-size: 12px; }
.account-chip { display: inline-flex; align-items: center; gap: 8px; min-height: 30px; padding: 0 10px; color: var(--text-2); background: var(--surface); border: 1px solid var(--hairline); border-radius: 8px; font-size: 12px; cursor: pointer; }
.account-chip:hover { color: var(--text); border-color: var(--hairline); background: var(--surface-2); }
.account-chip.is-active { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }

.workspace { border-top: 1px solid var(--hairline); }
.workspace-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; padding: 26px 0 18px; }
.workspace-head-copy { display: grid; gap: 4px; min-width: 0; }
.workspace-head-copy h2 { margin: 0; font-family: var(--font-display); font-size: 24px; font-weight: 600; letter-spacing: -.04em; }
.workspace-head-copy p { margin: 0; color: var(--text-3); font-size: 12.5px; }
.workspace-meta { display: flex; gap: 10px; flex-wrap: wrap; }
.workspace-meta-card { display: grid; gap: 2px; min-width: 108px; padding: 10px 14px; background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: 9px; }
.workspace-meta-card span { color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; }
.workspace-meta-card strong { font-size: 13px; font-weight: 600; }
.workspace-meta-card small { color: var(--text-3); font-size: 11px; }

/* Saved leader accounts */
/* One chip = one account. The whole tile switches to that account; removal is a
 * compact ✕ on the end.
 *
 * It used to pair the name with a full-width ghost "Remove" button, which put a
 * destructive action in plain grey text at the same weight as everything else
 * and repeated the word once per saved account. The active tile also carried two
 * competing treatments — an outer border AND a filled box behind the name — so
 * the inner fill is gone and the whole tile now shows selection. */
.my-team-account-item {
  display: inline-flex; align-items: stretch; overflow: hidden;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: 9px;
  transition: border-color var(--dur) ease, background var(--dur) ease;
}
.my-team-account-item:hover { border-color: var(--text-3); }
.my-team-account-item.is-active { border-color: var(--accent); background: var(--accent-soft); }
.my-team-account-switch {
  display: grid; gap: 1px; padding: 6px 11px;
  color: var(--text); background: none; border: 0;
  text-align: left; cursor: pointer;
}
.my-team-account-switch strong { font-size: 12.5px; font-weight: 600; }
.my-team-account-switch span { color: var(--text-3); font-family: var(--font-mono); font-size: 10px; }
.my-team-account-switch:focus-visible { box-shadow: var(--focus-ring); border-radius: 8px; }
.my-team-account-remove {
  display: grid; place-items: center; flex: 0 0 28px; width: 28px;
  color: var(--text-3); background: none;
  border: 0; border-left: 1px solid var(--hairline-soft);
  cursor: pointer; transition: color var(--dur) ease, background var(--dur) ease;
}
.my-team-account-remove svg { width: 13px; height: 13px; }
.my-team-account-remove:hover { color: var(--danger); background: var(--danger-soft); }
.my-team-account-remove:focus-visible { box-shadow: var(--focus-ring); }
.my-team-account-item.is-active .my-team-account-remove { border-left-color: color-mix(in srgb, var(--accent) 35%, transparent); }

/* Workspace card headers (reused across tab panels) */
.my-team-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
.my-team-mini-note { color: var(--text-3); font-size: 11px; white-space: nowrap; }

/* Tabs for the workspace — underline style */
.drawer-tabs.my-team-tabs, .drawer-tabs { display: flex; gap: 2px; padding: 0; background: transparent; border: 0; border-bottom: 1px solid var(--hairline); border-radius: 0; }
.drawer-tabs .drawer-tab { min-height: 40px; padding: 0 14px; border: 0; border-radius: 0; color: var(--text-3); background: transparent; position: relative; }
.drawer-tabs .drawer-tab:hover { color: var(--text); }
.drawer-tabs .drawer-tab.active { color: var(--text); background: transparent; }
.drawer-tabs .drawer-tab.active::after { content: ""; position: absolute; left: 12px; right: 12px; bottom: -1px; height: 2px; background: var(--accent); border-radius: 2px; }
.drawer-tab-panel { padding: 24px 0 40px; animation: content-in var(--dur-slow) var(--ease); }
/* On My Team these live inside .shell, which supplies the gutter. In the fixed
 * slide-over there is no gutter, so the tab strip and the panel body ran flush
 * to the window edge while .drawer-head above them was inset 30px. */
.drawer .drawer-tabs, .drawer .drawer-tab-panel { padding-left: 30px; padding-right: 30px; }
.drawer-tab-panel[hidden] { display: none; }

.drawer-grid, .my-team-overview-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; }
.drawer-card { padding: 0; }
.drawer-card-wide { grid-column: 1 / -1; }
.drawer-card h3 { margin: 0 0 14px; font-family: var(--font-display); font-size: 14px; }
.drawer-copy { margin: 0; color: var(--text-2); white-space: pre-wrap; }
.drawer-card-table { padding: 0; }
.read-only-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 16px; }
.read-only-raw { margin-top: 16px; color: var(--text-3); font-size: 12px; }
.read-only-raw summary { cursor: pointer; }
.read-only-pre, .member-modal-raw { display: none; max-height: 300px; overflow: auto; padding: 14px; color: var(--text-2); background: var(--bg-soft); border: 1px solid var(--hairline-soft); border-radius: 8px; font: 11px/1.55 var(--font-mono); white-space: pre-wrap; }
.read-only-raw[open] .read-only-pre, .member-modal-raw.visible { display: block; }

.chat-window { min-height: 160px; max-height: 440px; overflow: auto; padding: 14px; background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: 9px; }
.chat-input-row { display: flex; gap: 8px; margin-top: 12px; }
.chat-actions { margin-top: 12px; }
.settings-group { display: grid; gap: 8px; padding: 16px 0; border-top: 1px solid var(--hairline-soft); }
.settings-group:first-child { border-top: 0; }
.settings-group-label { margin: 0; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }

/* Auto-kick / switches ------------------------------------------------------ */
.team-autokick-panel { padding: 4px 0 8px; }
.team-autokick-hero { display: flex; align-items: flex-start; justify-content: space-between; gap: 20px; }
.team-autokick-copy { max-width: 62ch; }
.team-autokick-title { margin: 3px 0 6px; font-family: var(--font-display); font-size: 15px; }
.team-autokick-lead { margin: 0; font-size: 12.5px; }
.team-autokick-head-actions { display: flex; align-items: center; gap: 8px; }
.team-autokick-status-pill { display: inline-flex; align-items: center; gap: 6px; min-height: 24px; padding: 0 9px; color: var(--text-2); background: var(--surface-2); border: 1px solid var(--hairline); border-radius: 999px; font-size: 11px; }
.team-autokick-body { display: grid; gap: 14px; margin-top: 16px; }
.team-autokick-switches { display: grid; gap: 2px; }
/* Same switch as .toggle-slider — geometry, colours and states are shared so
 * the two never drift. It used to draw a solid white knob on a bare track with
 * no border: off read as on in dark, and in light the white knob on an #e4e6eb
 * track made the whole control disappear. */
.team-switch { display: flex; align-items: flex-start; gap: 12px; padding: 10px 0; cursor: pointer; }
/* Visually hidden, not display:none — display:none takes the input out of the
 * tab order, so these switches could not be reached or toggled by keyboard. */
.team-switch input {
  position: absolute; width: 1px; height: 1px; min-height: 0; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}
.team-switch-control {
  position: relative; display: inline-block;
  flex: 0 0 34px; width: 34px; height: 20px; margin-top: 1px;
  background: var(--track-off); border: 1px solid var(--track-off-border); border-radius: 999px;
  transition: background var(--dur) ease, border-color var(--dur) ease;
}
.team-switch-control::after {
  content: ""; position: absolute; top: 1px; left: 1px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--switch-knob);
  /* The ring is not decoration: it is what stops a white knob from vanishing
   * into light theme's pale off-track. The drop shadow lifts it off the track
   * so it reads as a moving part rather than a painted-on dot. */
  box-shadow: 0 1px 3px rgba(0, 0, 0, .32), 0 0 0 1px var(--switch-knob-edge);
  transition: transform var(--dur) var(--ease);
}
.team-switch input:checked + .team-switch-control { background: var(--switch-on); border-color: var(--switch-on-border); }
.team-switch input:checked + .team-switch-control::after { transform: translateX(14px); }
.team-switch input:focus-visible + .team-switch-control { box-shadow: var(--focus-ring); }
.team-switch input:disabled + .team-switch-control { opacity: .45; }
.team-switch:has(input:disabled) { cursor: not-allowed; color: var(--text-3); }
.team-switch-copy { display: grid; gap: 1px; }
.team-switch-copy strong { font-size: 13px; font-weight: 500; }
.team-switch-copy small { color: var(--text-3); font-size: 12px; }
.team-autokick-target-card { padding: 12px 14px; background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: 9px; }
.team-autokick-target { margin: 5px 0 0; font-family: var(--font-mono); font-size: 12px; }
.team-autokick-config-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 26px; }
.team-autokick-block { min-width: 0; }
.team-autokick-block-wide { grid-column: 1 / -1; }
.team-autokick-block-head { margin-bottom: 12px; }
.team-autokick-block-copy { margin: 3px 0 0; font-size: 12px; }
/* auto-fit, not a fixed 4. These grids sit inside .team-autokick-config-grid's
 * two columns, so a hardcoded 4 gave each field an eighth of the panel: the
 * Activity-rules switch got a ~90px cell and its one-line description wrapped
 * down four lines beside a number input. Columns now follow the space the block
 * actually has, which no viewport media query can see. */
.team-autokick-rule-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
/* A switch is a control plus a sentence, not a labelled field — it gets the row. */
.team-autokick-rule-grid > .team-switch { grid-column: 1 / -1; padding: 2px 0 6px; }
.team-autokick-rule-field { display: grid; gap: 6px; }
.team-autokick-rule-field span { color: var(--text-3); font-size: 11px; }
.team-autokick-input-row { margin-top: 10px; }
.team-autokick-chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.team-autokick-help { margin: 8px 0 0; font-size: 12px; }

/* Chat auto-clean / responder ---------------------------------------------- */
.chat-autoclean { margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--hairline); }
.chat-autoclean-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.chat-autoclean-head strong { font-size: 13px; }
.chat-autoclean-option { display: flex; align-items: flex-start; gap: 10px; padding: 5px 0; color: var(--text-2); font-size: 12.5px; cursor: pointer; }
.chat-autoclean-option input { margin-top: 2px; }
.chat-autoclean-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 20px; margin-top: 8px; }

/* Utility pages ------------------------------------------------------------- */
.create-shell, .my-team-shell, .ag-app-panel, .ag-events-standalone-block { display: grid; gap: 0; }
.create-head, .ag-events-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; padding: 24px 0; border-bottom: 1px solid var(--hairline); }
.create-head h1, .ag-events-head h1 { margin: 0; font-family: var(--font-display); font-size: 24px; letter-spacing: -.04em; }
.field-grid, .ag-settings-grid, .ag-events-board-config-grid, .chat-autoclean-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 16px; }
.field-grid--create { display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: 18px 16px; }
/* align-content:start stops the last child (usually the input) from being
 * stretched to the height of the tallest field in the row. */
.field, .field-grid > .field, .form-row { display: grid; align-content: start; gap: 7px; }
.create-section { padding: 26px 0; border-bottom: 1px solid var(--hairline); }
.create-section-title { margin: 0 0 16px; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .1em; text-transform: uppercase; }
/* .mod-status is an inline caption ("No save loaded.", the "—" beside Save),
 * not a panel: boxing it made a bare sentence look like an empty input and the
 * status glyph look like a button. */
.create-status, .action-result { padding: 11px 14px; color: var(--text-2); background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: 8px; }
.ag-accounts-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.ag-account-card, .ag-group-card, .player-follow-link-row { min-width: 0; padding: 16px; background: var(--surface); border: 1px solid var(--hairline); border-radius: 10px; transition: border-color var(--dur) ease; }
.ag-account-card:hover, .ag-group-card:hover { border-color: var(--accent); }
.ag-settings-actions, .ag-events-head-actions, .ag-events-history-toolbar, .ag-lb-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ag-events-table, .ag-lb-table { width: 100%; min-width: 640px; border-collapse: collapse; }
.ag-events-lb-scroll, .ag-events-history-pane, .ag-events-live-pane { overflow: auto; }
.ag-events-table th, .ag-lb-table th { height: 38px; padding: 0 16px; color: var(--text-3); background: var(--bg); border-bottom: 1px solid var(--hairline); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-align: left; text-transform: uppercase; }
.ag-events-table td, .ag-lb-table td { height: 48px; padding: 8px 16px; border-bottom: 1px solid var(--hairline-soft); }

/* Bare button/field rows -----------------------------------------------------
 * These four containers were used in markup but never had a rule, so they fell
 * back to plain block/inline-block: no gap between the controls and no vertical
 * rhythm around the row, which left the button row sitting flush against the
 * control above it and the status line below. */
.spc-refresh-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; padding: 0 0 14px; }
.spc-refresh-textarea { display: block; margin-bottom: 12px; font-family: var(--font-mono); font-size: 12.5px; }
.batch-code-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 6px 0; }
/* A table cell, so it stays a cell — flexing it would drop the row's baseline
 * alignment. Spacing comes from the buttons instead. */
.users-actions { white-space: nowrap; }
.users-actions .button + .button { margin-left: 6px; }

/* Sheets, dialogs ------------------------------------------------------------ */
.modal-overlay { position: fixed; inset: 0; z-index: 80; background: var(--overlay); opacity: 0; pointer-events: none; transition: opacity var(--dur-slow) ease; backdrop-filter: blur(3px); }
.modal-overlay.active { opacity: 1; pointer-events: auto; }
.modal { position: fixed; z-index: 90; inset: 50% auto auto 50%; width: min(560px, calc(100vw - 32px)); max-height: calc(100vh - 32px); overflow: auto; background: var(--surface-2); border: 1px solid var(--hairline); border-radius: 14px; box-shadow: 0 24px 64px rgba(0,0,0,.4); opacity: 0; pointer-events: none; transform: translate(-50%, -46%) scale(.97); transition: opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease); }
.modal.active { opacity: 1; pointer-events: auto; transform: translate(-50%, -50%) scale(1); }
/* A confirm/prompt dialog is almost always raised FROM another modal ("Reset all
 * event badges" inside Adjust event points, delete-group inside the group
 * editor). At the shared .modal z-index of 90 the winner was whichever sat later
 * in the document, and these dialogs are declared earlier — so the modal that
 * spawned them painted over them and swallowed the clicks. */
.ag-dialog-modal, .confirm-modal { z-index: 95; }
.modal-content { position: relative; padding: 26px; }
.modal-content h2 { margin: 0 0 18px; font-family: var(--font-display); font-size: 18px; letter-spacing: -.03em; }
.modal-close, .drawer-close { display: grid; place-items: center; width: 30px; height: 30px; color: var(--text-3); border-radius: 7px; font-size: 22px; line-height: 1; cursor: pointer; }
.modal-close { position: absolute; top: 16px; right: 16px; }
.modal-close:hover, .drawer-close:hover { color: var(--text); background: var(--surface); }
.confirm-modal { width: min(420px, calc(100vw - 32px)); }
.confirm-modal-hint { margin: 10px 0 0; color: var(--text-3); font-size: 11.5px; }
.confirm-modal-hint kbd {
  padding: 1px 5px; color: var(--text-2); background: var(--surface);
  border: 1px solid var(--hairline); border-radius: 5px;
  font-family: var(--font-mono); font-size: 10.5px;
}
/* No pointer, no modifier key: the tip is desktop-only advice. */
@media (pointer: coarse) { .confirm-modal-hint { display: none; } }
.confirm-modal-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 22px; }
/* Section footer action rows -------------------------------------------------
 * Every row that closes a form or a section sits bottom-right: dialogs
 * (.modal-content .form-actions, .confirm-modal-actions), page sections
 * (.ag-settings-actions, .ag-events-board-config-actions), and the standalone
 * page forms below. Previously these disagreed — "Save settings" and the SPC
 * Refresh toolbar left-packed while the board-config footer right-aligned, so
 * two adjacent sections put their primary buttons at opposite edges.
 *
 * .action-row is deliberately NOT included: it is an inline control row inside a
 * .settings-group, where the buttons belong under their own label ("Danger Zone
 * -> Leave Team"), not pushed to the far side of the panel. */
.modal-content .form-actions { justify-content: flex-end; margin-top: 22px; }
.form-actions { justify-content: flex-end; }
.member-modal-head, .team-autokick-history-head { padding-right: 30px; }
.member-modal-head h2, .team-autokick-history-head h2 { margin-bottom: 3px; }
.member-modal-body { display: grid; gap: 18px; }
.member-modal-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* Slide-over drawer (dashboard team inspector) ------------------------------ */
/* Above .modal-overlay (80), not below it. Opening the drawer activates that
 * same shared overlay (see updateOverlayState in app.js), so at z-index 70 the
 * drawer was dimmed by the scrim it had just switched on. Still below .modal
 * (90) so a dialog opened from inside the drawer lands on top. */
.drawer { position: fixed; inset: 0 0 0 auto; z-index: 85; width: min(760px, 100vw); overflow: hidden; background: var(--bg); border-left: 1px solid var(--hairline); box-shadow: -20px 0 60px rgba(0,0,0,.3); transform: translateX(102%); transition: transform var(--dur-slow) var(--ease); }
.drawer.active { transform: translateX(0); }
.drawer-inner { display: flex; flex-direction: column; height: 100%; overflow: auto; }
.drawer-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 18px; padding: 26px 30px 20px; border-bottom: 1px solid var(--hairline); }
.drawer-head h2 { margin: 0; font-family: var(--font-display); font-size: 22px; letter-spacing: -.04em; }
.drawer-head .panel-message { margin-top: 4px; }

/* Loading and motion --------------------------------------------------------- */
.load-line, .load-inline { display: inline-flex; align-items: center; gap: 7px; }
.load-line__text { color: var(--text-3); }
.loading-dots { display: inline-flex; color: var(--text-3); letter-spacing: .08em; }
.loading-dot { animation: dot-bounce 1.05s var(--ease) infinite; }
.loading-dot:nth-child(2) { animation-delay: .14s; }
.loading-dot:nth-child(3) { animation-delay: .28s; }
.spinner { display: inline-block; flex: 0 0 auto; width: 14px; height: 14px; border: 2px solid var(--accent-soft); border-top-color: var(--accent); border-radius: 50%; animation: spin .6s linear infinite; }

/* Generic busy affordance for controls a page drives by hand. Scoped away from
 * .btn-fb-busy so a button driven by ui-feedback.js does not end up wearing this
 * spinner *and* the one that module injects. The ring used to be hardcoded
 * rgba(255,255,255,.25), which is invisible against the light theme. */
.is-loading, [aria-busy="true"] { position: relative; pointer-events: none; }
.is-loading::after, [aria-busy="true"]:not(.btn-fb-busy)::after {
  content: ""; width: 13px; height: 13px; margin-left: 5px;
  border: 2px solid color-mix(in srgb, currentColor 25%, transparent);
  border-top-color: currentColor; border-radius: 50%;
  animation: spin .6s linear infinite;
}

/* Button action feedback (ui-feedback.js) -------------------------------------
 * That module drives every fetch-backed control through busy -> ok / err and
 * injects .btn-fb-icon / .btn-fb-spin, but none of it had a rule: the spinner
 * was an empty span and the result icons had no box, so a request in flight
 * just left the button dim and dead. */
.btn-fb-icon { display: inline-flex; flex: 0 0 auto; align-items: center; justify-content: center; width: 14px; height: 14px; }
.button .btn-fb-icon svg { width: 14px; height: 14px; }
.btn-fb-spin { border: 2px solid color-mix(in srgb, currentColor 25%, transparent); border-top-color: currentColor; border-radius: 50%; animation: spin .6s linear infinite; }
.btn-fb-busy { cursor: progress; }
/* .button:disabled dims to .45, which would half-hide the very feedback these
 * states exist to show. */
.btn-fb-busy:disabled, .btn-fb-ok:disabled { opacity: 1; }
.btn-fb-ok { color: var(--success); border-color: color-mix(in srgb, var(--success) 40%, var(--hairline)); }
.btn-fb-err { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--hairline)); }
.button-solid.btn-fb-ok { color: var(--on-accent); background: var(--success); border-color: var(--success); }
.button-solid.btn-fb-err { color: var(--on-accent); background: var(--danger); border-color: var(--danger); }

/* Skeletons — for table and list bodies, where a blinking ellipsis reads as a
 * stalled page rather than one that is filling in. */
.skeleton { position: relative; display: block; overflow: hidden; height: 12px; border-radius: 6px; background: var(--surface-2); }
.skeleton::after {
  content: ""; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text) 9%, transparent), transparent);
  animation: skeleton-sweep 1.5s var(--ease) infinite;
}
.skeleton-stack { display: grid; gap: 11px; width: 100%; }
.skeleton-stack .skeleton:nth-child(2) { width: 84%; }
.skeleton-stack .skeleton:nth-child(3) { width: 62%; }
.skeleton-stack .skeleton:nth-child(4) { width: 73%; }
/* Beats `.data-table .empty`'s centred 96px block, which is sized for a one-line
 * "No results" message rather than a stack of placeholder rows. */
.data-table .load-cell, .ov-table .load-cell { height: auto; padding: 18px 2px; text-align: left; }

@keyframes spin { to { transform: rotate(360deg); } }
@keyframes skeleton-sweep { to { transform: translateX(100%); } }
@keyframes pulse { 50% { opacity: .4; transform: scale(.75); } }
@keyframes dot-bounce { 0%, 60%, 100% { opacity: .25; transform: translateY(0); } 30% { opacity: 1; transform: translateY(-2px); } }
@keyframes metric-pop { 0% { transform: translateY(3px) scale(.97); color: var(--accent); } 45% { transform: translateY(-2px) scale(1.05); } 100% { transform: none; color: var(--text); } }
@keyframes content-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@keyframes rise { from { opacity: 0; transform: translateY(-5px) scale(.98); } to { opacity: 1; transform: none; } }

/* Login ---------------------------------------------------------------------- */
.login-shell { display: grid; min-height: 100vh; place-items: center; padding: 24px; background: radial-gradient(760px 480px at 50% -10%, var(--accent-soft), transparent 70%), var(--bg); }
.login-theme-toggle { position: fixed; top: 20px; right: 20px; }
.login-layout { width: min(100%, 420px); }
.login-card { background: var(--surface); border: 1px solid var(--hairline); border-radius: 16px; overflow: hidden; }
.login-card .panel-head { padding: 26px 26px 20px; border-bottom: 1px solid var(--hairline); }
.login-card .panel-head h1 { margin: 0; font-family: var(--font-display); font-size: 20px; letter-spacing: -.03em; }
.login-brand { display: grid; flex: 0 0 36px; place-items: center; width: 36px; height: 36px; color: var(--on-accent); background: var(--accent); border-radius: 10px; font-family: var(--font-display); font-size: 17px; font-weight: 600; }
.login-copy { margin: 0; padding: 18px 26px 0; color: var(--text-3); font-size: 13px; }
.login-form { display: grid; gap: 15px; padding: 20px 26px 26px; }
.login-field { display: grid; gap: 7px; }
.login-field span { color: var(--text-2); font-size: 12px; font-weight: 500; }
.login-submit { width: 100%; min-height: 42px; margin-top: 4px; }
.login-error { margin: 0 26px 24px; padding: 10px 12px; color: var(--danger); background: var(--danger-soft); border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent); border-radius: 8px; font-size: 12px; }

/* Responsive ------------------------------------------------------------------ */
@media (max-width: 1180px) {
  :root { --gutter: 24px; }
  .topbar { gap: 10px 14px; }
  /* Keep the title and the theme toggle together on the first line and give the
   * page's own button group its own full-width row, instead of letting the
   * toggle strand itself on a third line below the wrapped buttons. */
  .topbar > .topbar-block:first-child { order: 1; flex: 1 1 auto; }
  .topbar-actions--system { order: 2; margin-left: auto; }
  .topbar-actions:not(.topbar-actions--system) { order: 3; flex: 1 1 100%; margin-left: 0; justify-content: flex-start; }
  .topbar-status-copy { display: none; }
  .summary-row { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .metric:nth-child(2) { border-right: 0; }
  .metric:nth-child(-n + 2) { border-bottom: 1px solid var(--hairline-soft); }
  .field-grid, .ag-settings-grid, .ag-events-board-config-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .field-grid--create { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .connect-strip { grid-template-columns: 1fr; gap: 16px; }
}
@media (max-width: 900px) {
  /* Nothing to force here any more: the rail always reserves 64px and the panel
   * opens over the page, so a narrow viewport needs no special layout. */
  .rail-link svg { width: 18px; height: 18px; }
  .topbar { padding: 0 16px; }
  .topbar-actions .uid-field { display: none; }
  .split-grid, .split-grid-bottom, .drawer-grid, .my-team-overview-grid, .player-board-grid { grid-template-columns: 1fr; }
  .player-board-grid > div + div { border-top: 1px solid var(--hairline); border-left: 0; padding-left: 0; padding-top: 32px; }
  .team-autokick-config-grid { grid-template-columns: 1fr; }
  .chat-autoclean-grid { grid-template-columns: 1fr; }
  .workspace-head { flex-direction: column; }
  .workspace-meta { width: 100%; }
}
@media (max-width: 620px) {
  :root { --gutter: 14px; }
  .shell { padding-top: 20px; }
  .topbar { min-height: 56px; }
  .topbar h1 { font-size: 16px; }
  .summary-row, .my-team-summary-row { grid-template-columns: 1fr 1fr; }
  /* At 390px these were still laying out 2, 3 and 4 columns of form fields —
   * .field-grid--create was giving each input about 85px. */
  .field-grid, .ag-settings-grid, .ag-events-board-config-grid,
  .chat-autoclean-grid, .field-grid--create { grid-template-columns: 1fr; }
  .team-autokick-rule-grid { grid-template-columns: 1fr; }
  .metric { padding: 18px 16px; }
  .metric-value, .metric strong { font-size: 26px; }
  .panel-head { padding: 20px 0 12px; }
  .panel-head .panel-sub { display: none; }
  .data-table tbody td:first-child, .data-table thead th:first-child, .ov-table tbody td:first-child, .ov-table thead th:first-child { padding-left: 0; }
  .data-table tbody td:last-child, .data-table thead th:last-child, .ov-table tbody td:last-child, .ov-table thead th:last-child { padding-right: 0; }
  .drawer-head, .drawer .drawer-tabs, .drawer .drawer-tab-panel { padding-left: 16px; padding-right: 16px; }
  .drawer-tabs { max-width: calc(100vw - 28px); overflow: auto; }
  .drawer-tab { white-space: nowrap; }
  .accounts-strip { align-items: flex-start; flex-direction: column; }
  .connect-strip-side { flex-wrap: wrap; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .001ms !important; animation-iteration-count: 1 !important; scroll-behavior: auto !important; transition-duration: .001ms !important; }
}

/* Sidebar scrim — only ever visible in drawer mode (below). Sits under the
 * panel (40) and over the page. */
.sidebar-scrim {
  position: fixed; inset: 0; z-index: 35;
  background: var(--overlay);
  opacity: 0; pointer-events: none;
  transition: opacity var(--sidebar-dur) ease;
}
/* Hidden outright above the drawer breakpoint so it can never intercept a click
 * on a desktop where the panel merely overlays. */
.sidebar-menu-btn { display: none; }

/* Phones: a 64px rail cannot earn its keep on a 390px screen, and with no hover
 * there is nothing to reveal it with anyway — it just ate a sixth of the width
 * and showed unlabelled icons. Below this width the sidebar stops reserving any
 * layout at all and becomes an off-canvas drawer opened from the topbar. */
/* Notched phones: in landscape the cutout sits over the left edge, which is
 * exactly where the drawer and the page gutter live. */
@supports (padding: env(safe-area-inset-left)) {
  .shell { padding-left: max(var(--gutter), env(safe-area-inset-left)); padding-right: max(var(--gutter), env(safe-area-inset-right)); }
  /* Keeps the content-column alignment from the base .topbar rule while still
   * clearing a notch: whichever of the three is largest wins. Dropping the
   * column term here is what previously let this block silently undo it. */
  .topbar {
    padding-left: max(var(--gutter), env(safe-area-inset-left), calc(50% - var(--content-max) / 2 + var(--gutter)));
    padding-right: max(var(--gutter), env(safe-area-inset-right), calc(50% - var(--content-max) / 2 + var(--gutter)));
  }
  aside.sidebar { padding-left: env(safe-area-inset-left); }
}

@media (max-width: 700px) {
  .app { grid-template-columns: minmax(0, 1fr); }
  /* There is only one column in drawer mode, and `grid-column: 2` from the base
   * rule would otherwise create an implicit second one off the right edge. */
  .app-main { grid-column: 1; }
  .sidebar-menu-btn { display: grid; flex: 0 0 auto; order: -1; }
  aside.sidebar {
    width: min(284px, 84vw);
    transform: translateX(-101%);
    box-shadow: none;
    transition:
      transform var(--sidebar-dur) var(--sidebar-ease),
      box-shadow var(--sidebar-dur) var(--sidebar-ease);
  }
  html[data-sidebar="expanded"] aside.sidebar {
    width: min(284px, 84vw);
    transform: none;
    box-shadow: 10px 0 38px rgba(0, 0, 0, .34);
  }
  /* Nothing is being revealed progressively here — the drawer is either present
   * or it is not, so labels are simply always on. */
  .sidebar-brand-text, .rail-link-text,
  html[data-sidebar="expanded"] .sidebar-brand-text,
  html[data-sidebar="expanded"] .rail-link-text {
    opacity: 1; transform: none; transition: none;
  }
  html[data-sidebar="expanded"] .sidebar-scrim { opacity: 1; pointer-events: auto; }
  /* Stops the page behind the drawer from scrolling under a dragging thumb. */
  html[data-sidebar="expanded"] body { overflow: hidden; }
}

/* Touch and pen: 32-36px controls clear WCAG 2.5.8's 24px floor but are still a
 * poor target for a thumb. 2.5.5 asks for 44px, and every control here is sized
 * from these two tokens, so raising them lifts buttons, icon buttons and form
 * fields together. */
@media (pointer: coarse) {
  :root { --ctl-h: 44px; --ctl-h-sm: 38px; }
  .rail-link { min-height: 44px; }
  .modal-close, .drawer-close { width: 40px; height: 40px; }
}

/* Sidebar reveal state (shell.js) --------------------------------------------
 * There is no collapse button any more. The rail opens when the pointer comes
 * near it and closes when it leaves; shell.js owns the intent timing (an open
 * delay so brushing past does not trigger it, a close delay so a clipped corner
 * does not slam it shut) and only ever sets data-sidebar here. */
/* Touch tablets are above the drawer breakpoint but still have no hover to
 * reveal the panel with, so they get the topbar button too. It drives the same
 * overlay expand a pointer would — previously this block pinned the rail shut,
 * which left those devices with unlabelled icons and no way to open anything. */
@media (hover: none) {
  .sidebar-menu-btn { display: grid; flex: 0 0 auto; order: -1; }
}

/* =============================================================================
   Auto-Grind console — account cards, switches, inline field pairs
   -----------------------------------------------------------------------------
   These components had no styles at all: the shell (sidebar/topbar/buttons/
   tables) was ported into this file but the page-level pieces never were, so
   `.ag-card` and friends rendered as raw HTML flow. Everything below is new.
   ========================================================================== */

/* Metric band: `#ag-refresh-status` injects `Idle<br><small>Last tick …</small>`
 * into the display-size <strong>, so the caption inherited a ~32px face. */
.metric-value small, .metric strong small {
  display: block; margin-top: 9px;
  color: var(--text-3);
  font-family: var(--font-sans); font-size: 11.5px; font-weight: 400;
  letter-spacing: 0; line-height: 1.45;
}
.metric-value br, .metric strong br { display: none; }
.metric > .button { margin-top: 12px; }

/* Inline field pairs — `50 – 70`, `8 : 0`. Inputs are width:100% by default,
 * so without this the pair and its separator stacked into three rows. */
.ag-settings-inline-split,
.ag-target-inline,
.input-action-cluster,
.range-field {
  display: flex; align-items: center; gap: 8px; min-width: 0;
}
.ag-settings-inline-split > input, .range-field > input { flex: 1 1 0; min-width: 0; }
.ag-settings-inline-sep { flex: 0 0 auto; color: var(--text-3); font-size: 12px; }
/* Save row reads as a form footer rather than a grid cell, so the button keeps
 * its natural width instead of stretching across a whole column.
 *
 * Section footers sit bottom-right — see "Section footer action rows" below. */
.ag-settings-actions {
  grid-column: 1 / -1;
  display: flex; justify-content: flex-end;
  margin-top: 4px; padding-top: 16px;
  border-top: 1px solid var(--hairline-soft);
}

/* Switch — replaces the raw checkbox that shipped for "Worker enabled" and
 * every per-account toggle. */
.toggle-label {
  display: inline-flex; align-items: center; gap: 9px;
  color: var(--text); font-size: 12.5px; font-weight: 500;
  cursor: pointer; user-select: none;
}
input[type="checkbox"].toggle-input {
  position: absolute; width: 1px; height: 1px; min-height: 0; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap;
}
.toggle-slider {
  /* inline-block, not the default inline: width/height do not apply to an
   * inline box, so anywhere the slider is not a flex item (e.g. the "Camp if
   * full" card) the track collapsed and only the round knob was painted. */
  position: relative; display: inline-block;
  flex: 0 0 34px; width: 34px; height: 20px;
  background: var(--track-off); border: 1px solid var(--track-off-border);
  border-radius: 999px;
  transition: background var(--dur) ease, border-color var(--dur) ease;
}
.toggle-slider::after {
  content: ""; position: absolute; top: 1px; left: 1px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--switch-knob);
  /* The ring is not decoration: it is what stops a white knob from vanishing
   * into light theme's pale off-track. The drop shadow lifts it off the track
   * so it reads as a moving part rather than a painted-on dot. */
  box-shadow: 0 1px 3px rgba(0, 0, 0, .32), 0 0 0 1px var(--switch-knob-edge);
  transition: transform var(--dur) var(--ease);
}
.toggle-input:checked + .toggle-slider { background: var(--switch-on); border-color: var(--switch-on-border); }
.toggle-input:checked + .toggle-slider::after { transform: translateX(14px); }
.toggle-input:focus-visible + .toggle-slider { box-shadow: var(--focus-ring); }
.toggle-input:disabled + .toggle-slider { opacity: .45; }
.toggle-label:has(.toggle-input:disabled) { cursor: not-allowed; color: var(--text-3); }
.toggle-text { min-width: 0; }

/* Account grid ------------------------------------------------------------ */
.ag-accounts-grid {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(340px, 1fr));
  padding-bottom: 8px;
}
.ag-accounts-grid > .empty {
  padding: 56px 0; color: var(--text-3); text-align: center;
}

.ag-card {
  display: flex; flex-direction: column; min-width: 0;
  padding: 15px 16px 16px;
  background: var(--surface);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  transition: border-color var(--dur) ease, background var(--dur) ease;
}
.ag-card:hover { border-color: var(--text-3); }

/* Identity and actions each get their own line: four compact buttons plus a
 * name never fit on one row at the card's min width, and letting them wrap
 * inside the row produced a ragged 2x2 button block beside the title. */
.ag-card-head { display: flex; flex-direction: column; gap: 9px; }
.ag-card-identity { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; min-width: 0; }
.ag-card-identity h3 {
  display: flex; align-items: baseline; gap: 7px; min-width: 0; margin: 0;
  font-family: var(--font-display); font-size: 14px; font-weight: 600;
  letter-spacing: -.01em;
}
.ag-card-identity h3 > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ag-card-tag { flex: 0 0 auto; color: var(--text-3); font-family: var(--font-mono); font-size: 10.5px; font-weight: 400; }
.ag-card-actions-top { display: flex; justify-content: flex-end; gap: 5px; }
.ag-card-actions-top .button { min-height: 26px; padding: 0 8px; font-size: 11.5px; }

/* Status pill */
.ag-status {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0; border: 0; background: none;
  font-size: 10.5px; font-weight: 600; letter-spacing: .02em; white-space: nowrap;
  color: var(--text-2);
}
.ag-status::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.ag-status-running { color: var(--success); }
.ag-status-paused  { color: var(--warning); }
.ag-status-error   { color: var(--danger); }
.ag-status-idle    { color: var(--text-3); }
.ag-status-war     { color: var(--info); }

/* Label / value rows */
.ag-card-meta {
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  margin: 13px 0 0;
}
.ag-card-meta dt,
.ag-card-meta dd {
  padding: 7px 0; border-top: 1px solid var(--hairline-soft);
}
.ag-card-meta dt { color: var(--text-3); font-size: 11.5px; }
.ag-card-meta dd {
  margin: 0; min-width: 0; overflow: hidden;
  color: var(--text); font-size: 12px; font-weight: 500;
  text-align: right; text-overflow: ellipsis; white-space: nowrap;
}
.ag-card-dim { color: var(--text-3); font-weight: 400; }
.ag-card-time { font-variant-numeric: tabular-nums; }
.ag-card-spc { color: var(--text-2); font-family: var(--font-mono); font-size: 11px; }

/* Collapsed summary */
.ag-card-summary { display: grid; gap: 1px; margin-top: 13px; }
.ag-card-summary-item {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 6px 0; border-top: 1px solid var(--hairline-soft);
}
.ag-card-summary-label { color: var(--text-3); font-size: 11.5px; }
.ag-card-summary-item strong { font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Expanded body */
.ag-card-body { display: flex; flex-direction: column; gap: 14px; margin-top: 14px; }
.ag-card-stats {
  display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
}
.ag-card-stats--compact { gap: 10px; }
.ag-stat { display: grid; align-content: start; gap: 3px; min-width: 0; }
.ag-stat-label {
  color: var(--text-3); font-size: 10px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
}
.ag-stat strong {
  font-family: var(--font-display); font-size: 14px; font-weight: 600;
  line-height: 1.35; letter-spacing: -.02em; font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}
.ag-stat small { color: var(--text-3); font-size: 11px; line-height: 1.4; }
.ag-stat .ok-tag { color: var(--success); }

.ag-progress {
  height: 4px; margin-top: 5px; overflow: hidden;
  background: var(--surface-2); border-radius: 999px;
}
.ag-progress > span {
  display: block; height: 100%;
  background: var(--accent); border-radius: inherit;
  transition: width var(--dur-slow) var(--ease);
}

/* Per-account switch rows */
.ag-card-toggles {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  padding-top: 12px; border-top: 1px solid var(--hairline-soft);
}
.ag-card-toggles > .muted { color: var(--text-3); font-size: 11px; }
.ag-badge-pct-row { padding-top: 12px; border-top: 1px solid var(--hairline-soft); }

.input-sm, .input-inline.input-sm {
  min-height: var(--ctl-h-sm); padding: 0 9px; font-size: 12px;
}

.ag-card-footer {
  display: flex; flex-wrap: wrap; gap: 6px;
  margin-top: 14px; padding-top: 13px; border-top: 1px solid var(--hairline-soft);
}
.ag-card-footer .button { min-height: 28px; padding: 0 9px; font-size: 11.5px; }

.ag-pause-at {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--hairline-soft);
}
.ag-pause-at > label { display: inline-flex; align-items: center; gap: 7px; color: var(--text-2); font-size: 12px; }
/* Inline target editor: inputs inherit width:100%, so inside a flex row they
 * all shrank to slivers. Pin each control to a width that fits its content. */
.ag-target-inline { flex-wrap: wrap; gap: 6px; }
.ag-target-inline > input { flex: 0 0 78px; width: 78px; }
.ag-target-inline > select { flex: 0 1 auto; width: auto; min-width: 116px; }
/* No `margin-left: auto` here either -- same defect as .mod-save-primary, and in
 * the account grid it fires constantly: this row holds three number inputs, two
 * selects and a button inside a card, so it wraps at almost every card width, and
 * the auto margin then pushed "Update account" off the left edge of whichever
 * line it landed on. */
.ag-target-inline > .button { flex: 0 0 auto; }

/* Event grind monitor ----------------------------------------------------- */
.ag-event-monitor { padding-top: 12px; border-top: 1px solid var(--hairline-soft); }
.ag-event-monitor-head {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin-bottom: 9px;
}
.ag-event-monitor-head .muted { color: var(--text-3); font-size: 11px; }
.ag-event-monitor-empty { color: var(--text-3); font-size: 11.5px; line-height: 1.5; }
.ag-event-monitor-row {
  display: grid; gap: 4px; padding: 8px 0;
  border-top: 1px solid var(--hairline-soft);
}
.ag-event-monitor-row:first-of-type { border-top: 0; padding-top: 0; }
.ag-event-monitor-title {
  display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 4px 10px;
}
.ag-event-monitor-title strong { font-size: 12.5px; font-weight: 600; }
.ag-event-monitor-title .muted { color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
.ag-event-monitor-meta {
  display: flex; flex-wrap: wrap; gap: 4px 16px;
  color: var(--text-2); font-size: 11px; font-variant-numeric: tabular-nums;
}
.ag-event-monitor-meta strong {
  margin-right: 4px; color: var(--text-3); font-size: 10px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
}

/* =============================================================================
   Toasts, Events page, and screen-reader-only text
   ========================================================================== */

/* Toast host — fixed, bottom-right, and takes no space when empty. */
/* Hosts are listed by id as well as class: tests/shell-integrity.test.js pins
 * every `id="…toast…"` a template renders to a `position: fixed` rule, because
 * a host left in page flow renders its toasts off-screen with no error. */
#ag-toast, #ov-toast-host, #war-toast-host, .ag-toast, .war-toast-host, .toast-host {
  position: fixed; z-index: 300; right: 20px; bottom: 20px;
  display: flex; flex-direction: column; align-items: flex-end; gap: 8px;
  pointer-events: none;
}
.ag-toast-item, .ov-toast, .war-toast, .toast-item {
  --toast-accent: var(--text-3);
  --toast-icon: none;
  /* Block flow with an absolutely-placed icon, NOT grid/flex: toast bodies are
   * raw HTML assembled by several modules ("... as <b>tooEarly</b> by the live
   * server"), and a grid container turns each inline run into its own grid item,
   * so the markup collided with itself. */
  display: block;
  width: max-content; max-width: min(440px, calc(100vw - 40px));
  padding: 11px 14px 11px 42px;
  color: var(--text);
  background: var(--surface-2);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .28), 0 1px 3px rgba(0, 0, 0, .2);
  font-size: 12.5px; line-height: 1.5;
  cursor: pointer; pointer-events: auto;
  animation: toast-in var(--dur-slow) var(--ease);
  transition: border-color var(--dur) ease, transform var(--dur) ease;
}
.ag-toast-item, .ov-toast, .war-toast, .toast-item { position: relative; }

/* Severity reads as a tinted icon chip rather than a saturated bar down the
 * edge: the bar drew the eye before the words did, and at four different hues
 * it made a stack of toasts look like a warning panel. */
.ag-toast-item::before, .ov-toast::before, .war-toast::before, .toast-item::before {
  content: ""; position: absolute; top: 11px; left: 13px;
  width: 20px; height: 20px; border-radius: 6px;
  /* Tint only. The glyph is drawn by ::after as a mask so it takes the accent
   * colour; painting the raw SVG here as well would stack a white copy under it. */
  background: color-mix(in srgb, var(--toast-accent) 16%, transparent);
}
/* The mask carries the glyph; the chip behind it carries the tint. */
.ag-toast-item::after, .ov-toast::after, .war-toast::after, .toast-item::after {
  content: ""; position: absolute; top: 11px; left: 13px;
  width: 20px; height: 20px;
  background: var(--toast-accent);
  mask: var(--toast-icon) center / 13px 13px no-repeat;
  -webkit-mask: var(--toast-icon) center / 13px 13px no-repeat;
}

/* Every alias the JS actually emits is covered: war.js and modifier.js write
 * "ok", overview.js writes "error"/"info", autogrind.js writes ag-toast-<kind>,
 * app.js writes toast-<kind>. "ok" was previously unmapped, so success toasts
 * rendered with the neutral grey treatment and no icon. */
.ag-toast-success, .ag-toast-ok, .toast-success, .toast-ok,
.ov-toast.is-success, .ov-toast.is-ok, .war-toast.is-success, .war-toast.is-ok {
  --toast-accent: var(--success);
  --toast-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}
.ag-toast-error, .ag-toast-err, .toast-error, .toast-err,
.ov-toast.is-error, .ov-toast.is-err, .war-toast.is-error, .war-toast.is-err {
  --toast-accent: var(--danger);
  --toast-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 6 12 12M18 6 6 18'/%3E%3C/svg%3E");
}
.ag-toast-warn, .ag-toast-warning, .toast-warn, .toast-warning,
.ov-toast.is-warn, .ov-toast.is-warning, .war-toast.is-warn, .war-toast.is-warning {
  --toast-accent: var(--warning);
  --toast-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 7.5v5.5M12 17h.01'/%3E%3C/svg%3E");
}
.ag-toast-info, .toast-info, .ov-toast.is-info, .war-toast.is-info {
  --toast-accent: var(--info);
  --toast-icon: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23fff' stroke-width='2.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M12 16.5v-6M12 7h.01'/%3E%3C/svg%3E");
}
.ag-toast-item:hover, .ov-toast:hover, .war-toast:hover, .toast-item:hover {
  border-color: var(--text-3);
  transform: translateY(-1px);
}
@media (prefers-reduced-motion: reduce) {
  .ag-toast-item:hover, .ov-toast:hover, .war-toast:hover, .toast-item:hover { transform: none; }
}
.ag-toast-out { animation: toast-out 280ms var(--ease) forwards; }

@keyframes toast-in  { from { opacity: 0; transform: translateY(10px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes toast-out { to { opacity: 0; transform: translateY(6px) scale(.98); } }

/* Visually hidden but still announced. Without this the events header's
 * caption rendered as a stray sentence above the toolbar. */
.ag-sr-only, .sr-only {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Events page ------------------------------------------------------------- */
.ag-events-head--standalone { align-items: center; justify-content: space-between; }
.ag-events-head-actions, .ag-events-head-actions--standalone {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
}
.ag-events-subtabs { display: inline-flex; padding: 3px; background: var(--surface); border: 1px solid var(--hairline); border-radius: 9px; gap: 2px; }
.ag-events-subtab { min-height: 28px; padding: 0 12px; background: transparent; border: 0; color: var(--text-2); font-size: 12px; }
.ag-events-subtab:hover { color: var(--text); background: var(--surface-2); }
.ag-events-subtab.is-active { color: var(--text); background: var(--surface-2); border: 1px solid var(--hairline); }

.ag-events-pane { display: flex; flex-direction: column; gap: 16px; }
/* Per-account share of the paced rate. One number, labelled by what it is a share of in
   whichever mode is in force -- it used to be a min-max pair described as a "% of top-5
   average gain per cycle", which was the old projection's vocabulary. */
.ag-badge-pct-row { margin: 12px 0 0; }
.ag-badge-pct-label { margin-bottom: 7px; color: var(--text-2); font-size: 12px; }
.ag-badge-pct-controls { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.ag-badge-pct-field { flex: 0 0 108px; }
.ag-badge-pct-controls > .muted { color: var(--text-3); font-size: 11.5px; }

/* -----------------------------------------------------------------------------
   Grind mode (admin)
   -----------------------------------------------------------------------------
   Dense by design. This is a settings strip, not a landing page: one bar for the
   mode, one row per event type, one line of tuning. The earlier version spent a
   full screen on four description cards and four bordered panels to carry what is
   really twelve values.
   -------------------------------------------------------------------------- */
.ag-grind-mode {
  display: grid; gap: 12px;
  margin-bottom: 16px; padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
}
.ag-grind-bar { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; }
.ag-grind-bar h3 {
  margin: 0; font-family: var(--font-display); font-size: 13.5px;
  letter-spacing: -.01em; color: var(--text); white-space: nowrap;
}
.ag-grind-mode-meta { margin-left: auto; color: var(--text-3); font-size: 10.5px; font-variant-numeric: tabular-nums; }
.ag-grind-mode-lede { margin: 0; max-width: 84ch; color: var(--text-3); font-size: 11.5px; line-height: 1.5; }

/* Segmented control: four modes on one line instead of four cards. */
.ag-grind-seg { display: inline-flex; padding: 2px; gap: 2px; background: var(--bg-soft); border: 1px solid var(--hairline); border-radius: 8px; }
.ag-grind-seg-btn {
  min-height: 26px; padding: 0 11px; border: 1px solid transparent; border-radius: 6px;
  background: transparent; color: var(--text-3); font-size: 11.5px; cursor: pointer; white-space: nowrap;
  transition: color var(--dur) ease, background var(--dur) ease;
}
.ag-grind-seg-btn:hover { color: var(--text); }
.ag-grind-seg-btn.is-active { color: var(--text); background: var(--surface-2); border-color: var(--hairline); }
.ag-grind-seg-btn:focus-visible { outline: none; box-shadow: var(--focus-ring); }

/* One row per event type, on a shared column grid so the four read as a list. */
.ag-grind-mode-body { display: grid; gap: 1px; background: var(--hairline-soft); border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm); overflow: hidden; }
.ag-grind-row {
  display: grid; grid-template-columns: 20px minmax(0, 1fr) 122px 148px 56px;
  gap: 10px; align-items: center;
  padding: 7px 11px; background: var(--bg-soft);
}
.ag-grind-row.is-off { opacity: .5; }
.ag-grind-row-on { display: flex; align-items: center; }
.ag-grind-row-id { display: flex; align-items: baseline; gap: 9px; min-width: 0; }
.ag-grind-row-name { color: var(--text); font-size: 12px; font-weight: 600; white-space: nowrap; }
.ag-grind-row-live {
  color: var(--text-3); font-family: var(--font-mono); font-size: 10.5px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ag-grind-row-idle { color: var(--text-3); opacity: .7; }
.ag-grind-row-mode, .ag-grind-row-field > * { min-width: 0; }
.ag-grind-row-mode { min-height: var(--ctl-h-sm); font-size: 11.5px; padding-left: 8px; }
.ag-grind-row-field { min-width: 0; }
.ag-grind-row-unit { color: var(--text-3); font-size: 10.5px; white-space: nowrap; }
.ag-grind-type-auto { color: var(--text-3); font-size: 11px; }
.ag-grind-rival { display: grid; grid-template-columns: minmax(0, 1fr) 44px 62px; gap: 4px; align-items: center; }
.ag-grind-rival > input { min-height: var(--ctl-h-sm); font-size: 11.5px; }

/* A unit that belongs to the field rather than floating beside it. The wrapper carries the
   control chrome and the input inside it is stripped bare, so any unit width fits without a
   fixed padding -- and the label can never end up rendering underneath the field. */
.ag-grind-suffix {
  display: flex; align-items: center;
  min-height: var(--ctl-h-sm); padding-right: 9px;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius-sm);
  transition: border-color var(--dur) ease, box-shadow var(--dur) ease;
}
.ag-grind-suffix:hover { border-color: var(--text-3); }
/* A target the clamps cannot reach. Warning rather than error: the value is saved and used,
   it just will not arrive, and the label says how far it actually gets. */
.ag-grind-suffix.is-warn { border-color: var(--warning); background: var(--warning-soft); }
.ag-grind-suffix.is-warn > span { color: var(--warning); }
.ag-grind-suffix:focus-within { border-color: var(--accent); box-shadow: var(--focus-ring); }
.ag-grind-suffix > input {
  flex: 1 1 auto; min-width: 0; min-height: 0; height: auto;
  padding: 0 6px 0 9px; font-size: 11.5px;
  background: transparent; border: 0; border-radius: 0;
}
.ag-grind-suffix > input:hover,
.ag-grind-suffix > input:focus { background: transparent; border: 0; box-shadow: none; }
.ag-grind-suffix > span { flex: 0 0 auto; color: var(--text-3); font-size: 10.5px; white-space: nowrap; }

/* Tuning and save on one line. */
.ag-grind-tune { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; padding-top: 11px; border-top: 1px solid var(--hairline-soft); }
.ag-grind-tune-fields { display: contents; }
.ag-grind-tune-fields[hidden] { display: none; }
.ag-grind-tune-field { display: flex; align-items: center; gap: 7px; color: var(--text-3); font-size: 11px; white-space: nowrap; }
.ag-grind-tune-field > .ag-grind-suffix { width: 74px; }
.ag-grind-tune-field > input { width: 62px; min-height: var(--ctl-h-sm); font-size: 11.5px; }
.ag-grind-tune-note { color: var(--text-3); font-size: 10.5px; }

/* What an account currently holds on a live board — the figure you scan the card for. */
.ag-event-monitor-held {
  color: var(--text); font-family: var(--font-mono); font-size: 13px;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.ag-event-monitor-unit { margin-left: 3px; color: var(--text-3); font-size: 10px; }
.ag-event-monitor-title { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.ag-grind-mode-status { margin-left: auto; color: var(--text-3); font-size: 11px; }
.ag-grind-mode-status.is-ok { color: var(--success); }
.ag-grind-tune > .button { flex: 0 0 auto; }
.ag-grind-tune-fields:not([hidden]) ~ .ag-grind-tune-note { color: var(--text-3); font-size: 10.5px; }

/* What an account currently holds on a live board — the figure you scan the card for. */
.ag-event-monitor-held {
  color: var(--text); font-family: var(--font-mono); font-size: 13px;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.ag-event-monitor-unit { margin-left: 3px; color: var(--text-3); font-size: 10px; }
.ag-event-monitor-title { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.ag-grind-mode-status { margin-left: auto; }

@media (max-width: 900px) {
  .ag-grind-row { grid-template-columns: 20px minmax(0, 1fr) 110px; row-gap: 7px; }
  .ag-grind-row-field { grid-column: 2 / -1; }
  .ag-grind-row-unit { display: none; }
  .ag-grind-bar h3 { width: 100%; }
}

/* -----------------------------------------------------------------------------
   Board detail dialog
   -------------------------------------------------------------------------- */
/* The detected event is a fact, not a field. It used to wear input chrome, which
   invited people to try to edit the one thing on the page they cannot change. */
.ag-board-detected {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 13px;
  background: var(--bg-soft); border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm);
}
.ag-board-detected-label {
  color: var(--text-3); font-size: 10px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
}
.ag-board-detected-value {
  color: var(--text); font-family: var(--font-mono); font-size: 12px;
}
.ag-board-detected-note { margin-left: auto; color: var(--text-3); font-size: 11px; }

/* Destructive and secondary actions sit quietly so the primary one reads first. */
.ag-quiet-action { color: var(--text-3); background: transparent; border-color: transparent; }
.ag-quiet-action:hover { color: var(--text); background: var(--surface-2); border-color: var(--hairline); }
#ag-events-board-hide:hover { color: var(--danger); }

.ag-events-lb-toolbar { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.ag-lb-section-title {
  margin: 0; font-family: var(--font-display); font-size: 13px;
  letter-spacing: -.01em; color: var(--text);
}
.ag-lb-window-group { display: flex; align-items: center; gap: 9px; }
.ag-lb-window-label {
  color: var(--text-3); font-size: 10px; font-weight: 600;
  letter-spacing: .08em; text-transform: uppercase;
}

.ag-events-manage-toolbar {
  margin-bottom: 18px; padding: 14px 16px;
  background: var(--bg-soft); border: 1px solid var(--hairline-soft); border-radius: var(--radius);
}
.ag-events-manage-slot { display: grid; gap: 8px; }
.ag-events-manage-label {
  color: var(--text-3); font-size: 10px; font-weight: 600;
  letter-spacing: .09em; text-transform: uppercase;
}
/* select is width:100% by default, which pushed the button onto its own row */
.ag-events-manage-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.ag-events-manage-row > select { flex: 0 1 340px; width: auto; min-width: 0; }
.ag-events-manage-row > .button { flex: 0 0 auto; }
.ag-events-manage-slot > .muted { color: var(--text-3); font-size: 11.5px; }

.ag-events-history-toolbar { display: flex; flex-wrap: wrap; gap: 8px; }
.ag-events-lb-toolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding-bottom: 12px; }
/* -----------------------------------------------------------------------------
   Events table
   -----------------------------------------------------------------------------
   Six columns, each with a declared width, because the table previously stretched
   to the full content width and let the browser distribute the slack -- which put
   ~200px of nothing between "Active" and the player count while the config column
   wrapped. Widths are percentages so the layout holds at any container size.
   -------------------------------------------------------------------------- */
.ag-events-table { table-layout: fixed; }
.ag-ev-col-name { width: 34%; }
.ag-ev-col-status { width: 10%; }
.ag-ev-col-num { width: 9%; text-align: right; }
.ag-ev-col-sched { width: 15%; }
.ag-ev-col-pace { width: 14%; text-align: right; }
.ag-ev-col-rate { width: 18%; text-align: right; }

.ag-events-table td { vertical-align: middle; }
.ag-events-row { cursor: pointer; }
/* An event with no leaderboard has nothing to open, so the row must not offer itself as a
   control: no pointer, no hover lift. It is a status line, not a button. */
.ag-events-row.is-noboard { cursor: default; }
/* Row hover is already handled by .data-table. Focus has to be painted on the cells: with
 * border-collapse the cell backgrounds paint over anything set on the <tr>, so a ring or a
 * background there is simply invisible. */
.ag-events-row:focus-visible { outline: none; }
.ag-events-row:focus-visible td { background: var(--row-hover); }
.ag-events-row:focus-visible td:first-child { box-shadow: inset 2px 0 0 var(--accent); }

/* A two-line cell: the value that matters, then its supporting detail. */
.ag-ev-stack { display: flex; flex-direction: column; gap: 2px; line-height: 1.3; }

.ag-ev-cell-name { min-width: 0; }
.ag-ev-title {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  font-size: 13px; font-weight: 600; letter-spacing: -.01em; color: var(--text);
}
/* When a board carries a custom name, the game's own name for the event still shows, so a
 * rename that has gone stale is visible instead of silently standing in for the real one. */
.ag-ev-detected {
  padding: 1px 6px; border: 1px solid var(--hairline); border-radius: 999px;
  color: var(--text-3); font-size: 10px; font-weight: 500; letter-spacing: 0;
  white-space: nowrap;
}
.ag-ev-sub {
  display: block; margin-top: 3px;
  color: var(--text-3); font-family: var(--font-mono); font-size: 10.5px; letter-spacing: -.01em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.ag-ev-cell-status { white-space: nowrap; }
.ag-ev-pill {
  display: inline-flex; align-items: center; gap: 6px;
  color: var(--text-2); font-size: 11.5px; font-weight: 500; white-space: nowrap;
}
.ag-ev-pill::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: 0 0 auto; }
.ag-ev-pill.is-active { color: var(--success); }
.ag-ev-pill.is-active::before { box-shadow: 0 0 0 3px var(--success-soft); }
.ag-ev-pill.is-upcoming { color: var(--info); }
.ag-ev-pill.is-ended { color: var(--text-3); }

.ag-ev-cell-num {
  text-align: right;
  font-family: var(--font-mono); font-size: 12.5px; font-variant-numeric: tabular-nums;
  color: var(--text-2);
}

.ag-ev-cell-schedule { font-variant-numeric: tabular-nums; }
.ag-ev-sched-lead { color: var(--text); font-size: 12px; font-weight: 500; }
.ag-ev-substat { color: var(--text-3); font-size: 10.5px; font-variant-numeric: tabular-nums; }

/* Board pace: the measured number the grind copies. Display-weight because it is the one
   figure that says whether a board's config is plausible. */
.ag-ev-cell-pace { text-align: right; }
.ag-ev-cell-pace .ag-ev-stack { align-items: flex-end; }
.ag-ev-num {
  color: var(--text); font-family: var(--font-mono); font-size: 13px;
  font-variant-numeric: tabular-nums; letter-spacing: -.02em;
}
.ag-ev-unit { margin-left: 1px; color: var(--text-3); font-size: 10.5px; }
.ag-ev-dim { color: var(--text-3); font-size: 11.5px; }

/* Grind config: readout then action, identically on every row. */
.ag-ev-cell-rate { text-align: right; }
.ag-events-rate-summary {
  display: flex; align-items: center; justify-content: flex-end; gap: 10px; flex-wrap: nowrap;
}
.ag-ev-chips { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; justify-content: flex-end; }
.ag-ev-chip {
  padding: 2px 7px; border: 1px solid var(--hairline); border-radius: 999px;
  color: var(--text-3); font-family: var(--font-mono); font-size: 10.5px;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ag-ev-chip.is-strong { color: var(--text); background: var(--surface-2); border-color: var(--hairline); }
.ag-ev-config-btn { white-space: nowrap; flex: 0 0 auto; }

/* Below the widest breakpoint the fixed columns stop paying for themselves: the table
   scrolls in its own container rather than crushing six columns into a phone. */
@media (max-width: 1100px) {
  .ag-events-table { table-layout: auto; min-width: 860px; }
}

/* Event leaderboard table ------------------------------------------------- */
.ag-lb-rank { width: 3rem; color: var(--text-3); font-variant-numeric: tabular-nums; }
.ag-lb-num { text-align: right; font-variant-numeric: tabular-nums; }
.ag-lb-th-rank { width: 3rem; }
.ag-lb-th-pts, .ag-lb-th-rate { text-align: right; }
.ag-lb-th-status { width: 7rem; }
.ag-lb-player { min-width: 0; }
.ag-lb-player-name { overflow: hidden; font-weight: 500; text-overflow: ellipsis; white-space: nowrap; }
.ag-lb-pts { font-weight: 600; }
.ag-lb-status-cell { width: 7rem; }
.ag-lb-status { display: inline-flex; align-items: center; gap: 6px; font-size: 11.5px; }
.ag-lb-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
.ag-lb-status-online { color: var(--success); }
.ag-lb-status-offline { color: var(--text-3); }
.ag-lb-status-unknown { color: var(--text-3); }
.ag-lb-rate-cell { line-height: 1.35; }
.ag-lb-rate-gain { color: var(--text); font-weight: 600; }
.ag-lb-rate-proj { color: var(--text-3); font-size: 10.5px; }
.ag-lb-rate-cell.is-projected .ag-lb-rate-gain { color: var(--accent); }

/* "This player is one of yours" marker in leaderboards */
.autogrind-player-name-wrap { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.autogrind-player-mark {
  display: inline-grid; flex: 0 0 15px; place-items: center;
  width: 15px; height: 15px;
  color: var(--accent); background: var(--accent-soft);
  border-radius: 999px;
}
.autogrind-player-mark svg { width: 10px; height: 10px; }

/* =============================================================================
   SPC Generator
   ========================================================================== */
.field-create-span-2 { grid-column: span 2; }
.field-create-span-3 { grid-column: span 3; }
.field-create-span-4 { grid-column: span 4; }
.field-create-span-9 { grid-column: 1 / -1; }

@media (max-width: 1180px) {
  .field-create-span-2, .field-create-span-3, .field-create-span-4 { grid-column: span 2; }
}
@media (max-width: 760px) {
  .field-create-span-2, .field-create-span-3, .field-create-span-4 { grid-column: 1 / -1; }
}

.create-form { display: grid; }
.panel-create { min-width: 0; }
.create-head .muted { max-width: 74ch; margin-top: 4px; font-size: 12.5px; }
.last-backup { text-align: right; }
.last-backup-code {
  display: inline-block; margin-top: 4px;
  color: var(--text-2); font-family: var(--font-mono); font-size: 12px;
}

.field-hint { color: var(--text-3); font-size: 11.5px; line-height: 1.5; }
.field-hint-tight { margin-top: -2px; }

/* Input + trailing action ("Randomize", "Purple (100)", …) */
.input-with-action { display: flex; align-items: center; gap: 8px; min-width: 0; }
.input-with-action > input, .input-with-action > select { flex: 1 1 auto; min-width: 0; }
.input-with-action > .button { flex: 0 0 auto; }
.input-with-action--split > input { flex: 1 1 60%; }
.input-action-cluster { flex-wrap: wrap; gap: 6px; }
.input-action-cluster > .button { flex: 0 0 auto; }

/* Slider + numeric box */
.range-field > input[type="range"] { flex: 1 1 auto; min-width: 0; padding: 0; accent-color: var(--accent); }
.range-field > input[type="text"], .range-field > input[type="number"] { flex: 0 0 140px; }

.chip-row, .chip-row-events { display: flex; flex-wrap: wrap; gap: 6px; }

/* Generated code panel */
.result-import {
  margin: 22px 0 0; padding: 18px;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius);
}
.result-import-inner { display: grid; gap: 12px; }
.result-import-head { display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px; }
.result-import-hint { max-width: 62ch; margin-top: 3px; color: var(--text-3); font-size: 11.5px; }
.result-copy-primary { flex: 0 0 auto; }
.result-import-code, .result-advanced-code {
  display: block; padding: 12px 14px; overflow-wrap: anywhere;
  color: var(--text); background: var(--bg-soft);
  border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 12px; line-height: 1.6;
}
.batch-codes-wrap { display: grid; gap: 10px; }
.batch-code-list { display: grid; gap: 6px; max-height: 260px; overflow: auto; }
.batch-code-list > * {
  padding: 8px 10px; color: var(--text-2);
  background: var(--bg-soft); border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 11.5px; overflow-wrap: anywhere;
}
.create-status:empty { display: none; }
.create-form .form-actions { padding: 24px 0 4px; }

/* Paired with a field, a borderless ghost button reads as a link rather than a
 * control, so give it the same affordance as the input it sits beside. */
.input-with-action > .button-ghost, .input-action-cluster > .button-ghost {
  color: var(--text-2); background: var(--surface-2); border-color: var(--hairline);
}
.input-with-action > .button-ghost:hover, .input-action-cluster > .button-ghost:hover { color: var(--text); background: var(--hairline); }

/* =============================================================================
   War Room
   ========================================================================== */
.war-modal { width: min(680px, calc(100vw - 32px)); }
.war-command-modal { width: min(760px, calc(100vw - 32px)); }
.war-modal-content, .war-command-content { display: grid; gap: 16px; }
.war-modal-head { display: grid; gap: 4px; padding-right: 34px; }
.war-modal-head h2 { margin: 0; font-family: var(--font-display); font-size: 18px; letter-spacing: -.03em; }
.war-modal-head .muted { color: var(--text-3); font-size: 12px; }
.war-modal-results, .war-modal-list { display: grid; gap: 8px; max-height: 52vh; overflow: auto; }
.war-modal-empty { padding: 28px 0; color: var(--text-3); text-align: center; font-size: 12.5px; }

.war-modal-item {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur) ease, background var(--dur) ease;
}
.war-modal-item:hover { border-color: var(--text-3); }
.war-modal-item.is-active { border-color: var(--accent); background: var(--accent-soft); }
.war-modal-item-main {
  display: flex; align-items: center; gap: 12px; flex: 1 1 auto; min-width: 0;
  padding: 0; color: inherit; text-align: left; cursor: pointer;
}
.war-modal-item-rank { flex: 0 0 auto; color: var(--text-3); font-family: var(--font-mono); font-size: 11.5px; }
.war-modal-item-copy { display: grid; gap: 2px; min-width: 0; }
.war-modal-item-name { overflow: hidden; font-size: 13px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.war-modal-item-meta { color: var(--text-3); font-size: 11px; }
.war-modal-item-actions { flex: 0 0 auto; }

/* Measure checkboxes read as selectable cards */
.war-measure-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 8px; }
.war-measure-option {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 12px; color: var(--text);
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius-sm); cursor: pointer;
  transition: border-color var(--dur) ease, background var(--dur) ease;
}
.war-measure-option:hover { border-color: var(--text-3); }
.war-measure-option:has(input:checked) { border-color: var(--accent); background: var(--accent-soft); }
.war-measure-option input { margin-top: 1px; }
.war-measure-option strong { font-size: 12.5px; font-weight: 600; }
.war-field-label > span { display: block; color: var(--text-2); font-size: 12px; font-weight: 500; }
.war-command-summary { display: grid; gap: 6px; color: var(--text-2); font-size: 12.5px; }
.war-command-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding-top: 4px; }

/* Ongoing campaign cards */
.war-ongoing-list { display: grid; gap: 10px; }
.war-ongoing-card {
  display: flex; flex-wrap: wrap; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 14px 16px;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius);
  transition: border-color var(--dur) ease;
}
.war-ongoing-card:hover { border-color: var(--text-3); }
.war-ongoing-card.is-current { border-color: var(--accent); }
.war-ongoing-main { display: grid; gap: 4px; flex: 1 1 260px; min-width: 0; }
.war-ongoing-name-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.war-ongoing-name-row strong { font-family: var(--font-display); font-size: 14px; font-weight: 600; }
.war-ongoing-meta { color: var(--text-3); font-size: 11.5px; }
.war-ongoing-actions { flex: 0 0 auto; }

/* Pills */
/* .war-pill is a status caption (dot + text); .war-measure-pill stays a
 * bordered object because some of them are clickable. */
.war-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 0; border: 0; background: none;
  color: var(--text-2); font-size: 10.5px; font-weight: 600; white-space: nowrap;
}
.war-pill::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.war-pill.is-open { color: var(--success); }
.war-pill.is-closed { color: var(--warning); }
.war-measure-pill {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 2px 8px; border-radius: var(--radius-sm);
  color: var(--text-2); background: var(--surface-2); border: 1px solid var(--hairline);
  font-size: 10.5px; font-weight: 600; white-space: nowrap;
}
.war-measure-pills { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 5px; }
.war-measure-pill.is-empty { color: var(--text-3); }
button.war-measure-pill { cursor: pointer; transition: background var(--dur) ease, border-color var(--dur) ease; }
button.war-measure-pill:hover { color: var(--text); border-color: var(--accent); }
.war-eligible-op-btn { margin-top: 6px; }

.war-search-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.war-search-row > input { flex: 1 1 260px; min-width: 0; }
.war-search-summary { padding: 4px 0 10px; color: var(--text-3); font-size: 12px; }

/* =============================================================================
   Team workspace — roster, chat, auto-responder rules, auto-kick history
   ========================================================================== */

/* Roster */
.member-name { display: grid; gap: 2px; min-width: 0; }
.member-name strong { font-size: 12.5px; font-weight: 600; }
.member-sub { color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
.online-dot {
  display: inline-block; width: 6px; height: 6px; margin-right: 7px;
  background: var(--text-3); border-radius: 50%; vertical-align: middle;
}
.online-dot.is-online, .online-dot.online { background: var(--success); }
.online-dot.is-offline, .online-dot.offline { background: var(--text-3); }
.lb-ch, .lb-select-col { width: 40px; }
.lb-ch input, .lb-select-col input { margin: 0; }

/* Open-team list rows */
.list-row-main { display: grid; gap: 3px; min-width: 0; }
.list-row-main strong { font-size: 12.5px; font-weight: 600; }
.list-meta { display: flex; flex-wrap: wrap; gap: 4px 12px; color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; }
.list-row--static { cursor: default; }

/* Chat */
.chat-body-wrap, #chat-messages { display: grid; gap: 8px; max-height: 420px; overflow: auto; }
.chat-message {
  position: relative;
  padding: 10px 34px 10px 12px;
  background: var(--surface); border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
}
.chat-meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px; margin-bottom: 4px; }
.chat-meta strong { font-size: 12px; font-weight: 600; }
.chat-meta span { color: var(--text-3); font-size: 10.5px; font-variant-numeric: tabular-nums; }
.chat-author-reportable { cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px; text-decoration-color: var(--text-3); }
.chat-author-reportable:hover, .chat-author-reportable:focus-visible { color: var(--danger, #e5484d); text-decoration-color: currentColor; }
.chat-body { color: var(--text-2); font-size: 12.5px; line-height: 1.5; overflow-wrap: anywhere; }
.chat-load-msg { padding: 20px 0; color: var(--text-3); text-align: center; font-size: 12px; }
.delete-chat-btn {
  position: absolute; top: 7px; right: 7px;
  display: grid; place-items: center; width: 22px; height: 22px;
  color: var(--text-3); border-radius: 6px;
  font-size: 16px; line-height: 1; cursor: pointer;
  opacity: 0; transition: opacity var(--dur) ease, color var(--dur) ease, background var(--dur) ease;
}
.chat-message:hover .delete-chat-btn, .delete-chat-btn:focus-visible { opacity: 1; }
.delete-chat-btn:hover { color: var(--danger); background: var(--danger-soft); }

/* Auto-responder rules */
.rule-row, .chat-autoresponder-rule { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; padding: 6px 0; }
.rule-match-type, .rule-keyword, .rule-response { min-height: var(--ctl-h-sm); font-size: 12px; }
.remove-rule-btn { flex: 0 0 auto; min-width: 32px; padding: 0 8px; }

/* Auto-kick history */
/* List and row styling live with the rest of the history block further down. */
.team-autokick-history-list { display: grid; gap: 0; overflow: auto; }
.team-autokick-button { flex: 0 0 auto; }
.team-autokick-input-row-compact { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.team-switch-compact { display: inline-flex; align-items: center; gap: 8px; }

/* Confirm dialog */
.confirm-modal-inner { position: relative; display: grid; gap: 16px; }
.confirm-modal-message { color: var(--text-2); font-size: 13px; line-height: 1.55; }
/* Was `margin-left:auto` on a bare glyph, which left it floating in the grid
 * flow above the title and pushed the heading down. Same affordance as every
 * other dialog's close button now. */
.confirm-modal-inner h2 { padding-right: 34px; }
.confirm-modal-dismiss {
  position: absolute; top: -6px; right: -6px;
  display: grid; place-items: center; width: 30px; height: 30px;
  color: var(--text-3); border-radius: 7px; font-size: 22px; line-height: 1; cursor: pointer;
}
.confirm-modal-dismiss:hover { color: var(--text); background: var(--surface); }
.member-modal-content, .team-autokick-history-modal-content { display: grid; gap: 16px; }
.member-modal-head { display: grid; gap: 3px; padding-right: 34px; }
.member-modal-head h2 { margin: 0; }
.member-modal-raw {
  max-height: 320px; margin: 0; padding: 12px 14px; overflow: auto;
  color: var(--text-2); background: var(--bg-soft);
  border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 11px; line-height: 1.55;
  white-space: pre-wrap; overflow-wrap: anywhere;
}

/* =============================================================================
   Player tracker — follow links, compact rows, player drawer
   ========================================================================== */
.player-list-lg .player-item { padding: 18px 0; }
.player-item-compact { gap: 8px; }
.player-item-primary { font-weight: 600; }
.player-item-tracked { border-left: 2px solid var(--accent); padding-left: 12px; }

.player-follow-box {
  margin-top: 10px; padding: 14px;
  background: var(--surface); border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
}
.player-follow-link-row + .player-follow-link-row { margin-top: 10px; }
.player-follow-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; }
.player-follow-summary { color: var(--text-3); font-size: 11.5px; }
.player-follow-links:not(:empty) { margin-top: 10px; }
.follow-field { display: grid; align-content: start; gap: 5px; min-width: 0; }
.follow-field > span { color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }
.follow-field-static {
  min-height: var(--ctl-h-sm); padding: 7px 10px;
  color: var(--text); background: var(--bg-soft);
  border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm);
  font-size: 12px; font-variant-numeric: tabular-nums;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.follow-field input { min-height: var(--ctl-h-sm); font-size: 12px; }

/* Follow links: identity on top, the one editable field and its actions below.
   The margin field is width-capped so a lone number input does not stretch to
   the full card and read as the row's primary content. */
.follow-link-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; min-width: 0; }
.follow-link-ident { display: grid; gap: 3px; min-width: 0; }
.follow-link-ident strong { font-size: 12.5px; font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.follow-link-stats { display: flex; flex-wrap: wrap; align-items: center; gap: 0; }
.follow-link-stats span { color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; }
.follow-link-stats span + span::before { content: "·"; margin: 0 7px; color: var(--hairline); }
.follow-link-controls { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.follow-link-actions { display: flex; align-items: center; gap: 6px; margin-left: auto; }
.follow-field-margin { flex: 0 1 190px; }

/* Add row: the select, its margin, and the button that submits both. */
.follow-add-row { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 10px; margin-top: 12px; }
.follow-add-row > .follow-field:first-child { flex: 0 1 320px; }
.follow-add-row select { min-height: var(--ctl-h-sm); font-size: 12px; width: 100%; }
.follow-add-btn { flex: 0 0 auto; }
.follow-empty { margin: 0; color: var(--text-3); font-size: 11.5px; }
.player-follow-links:has(.follow-empty) + .follow-add-row { margin-top: 10px; }

/* Card footer: browse actions left, the single destructive action pushed right. */
.tracked-card-actions { margin-top: 12px; }
.tracked-card-actions-gap { flex: 1 1 auto; }
.action-danger-quiet { color: var(--danger); }
.action-danger-quiet:hover { color: var(--danger); background: var(--danger-soft); }
.player-inline-status { margin: 8px 0 0; color: var(--text-3); font-size: 11.5px; }
.player-inline-status.is-error { color: var(--danger); }
.player-inline-status.is-success { color: var(--success); }

/* Hidden-from-leaderboard tray */
.ov-hidden-players-wrap { margin-bottom: 14px; padding: 12px 14px; background: var(--surface); border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm); }
.ov-hidden-players-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 12px; }
.ov-hidden-players-head-copy { display: grid; gap: 2px; }
.ov-hidden-players-head-copy strong { font-size: 12.5px; }
.ov-hidden-players-head-copy .muted { color: var(--text-3); font-size: 11px; }
.ov-hidden-players { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.ov-hidden-players:empty { display: none; }

/* Player drawer */
/* width belongs on the .modal box, not its inner content wrapper */
#ov-player-modal { width: min(860px, calc(100vw - 32px)); }
.ov-player-modal-content { display: grid; gap: 16px; }
.ov-player-modal-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 16px; }
.ov-player-panel { display: grid; align-content: start; gap: 10px; min-width: 0; }
.ov-player-panel-wide { grid-column: 1 / -1; }
.ov-player-panel h3 { margin: 0; color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .09em; text-transform: uppercase; }
.ov-profile-summary { display: flex; flex-wrap: wrap; gap: 8px; }
.ov-growth-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(110px, 1fr)); gap: 10px; }
.detail-list-drawer dt, .detail-list-drawer dd { font-size: 12px; }

/* =============================================================================
   Account Modifier
   ========================================================================== */
.two-col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 40px; min-width: 0; }
.mod-panel-head-stacked { padding-top: 26px; }

.mod-load-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.mod-load-row > input { flex: 1 1 320px; min-width: 0; font-family: var(--font-mono); font-size: 12px; }
.mod-load-row > .button { flex: 0 0 auto; }
.mod-status { color: var(--text-3); font-size: 12px; }
.mod-status:empty { display: none; }

.mod-quick-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.mod-quick-fields > .muted { grid-column: 1 / -1; color: var(--text-3); font-size: 12.5px; }
.mod-field { display: grid; align-content: start; gap: 6px; min-width: 0; }
.mod-field label { color: var(--text-2); font-size: 11.5px; font-weight: 500; }
.mod-field input, .mod-field select { min-height: var(--ctl-h-sm); font-size: 12px; }

.mod-json {
  min-height: 300px; resize: vertical;
  font-family: var(--font-mono); font-size: 11.5px; line-height: 1.6;
  white-space: pre; overflow-wrap: normal;
}
.mod-json-actions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; margin-top: 12px; }
/* .mod-save-primary deliberately has no `margin-left: auto`.
 *
 * An auto margin re-applies on *every* line of a wrapping flex row, so once this
 * toolbar was too narrow to hold all five controls the primary button wrapped to
 * a second line and then shoved itself away from the left edge with whatever
 * slack that line had -- landing mid-row, aligned to nothing above or below it.
 * Alignment is handled by justify-content on the row itself, which applies to
 * every wrapped line consistently, rather than by an auto margin on one child.
 * The solid fill is what marks this button as primary, not its position. */
.mod-json-status-inline { flex: 0 0 auto; }

/* Result */
.form-title { margin: 0 0 8px; font-family: var(--font-display); font-size: 13px; font-weight: 600; }
.mod-result {
  padding: 16px;
  background: var(--surface); border: 1px solid var(--hairline); border-radius: var(--radius);
}
.mod-result-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.mod-result-row > code {
  flex: 1 1 260px; min-width: 0; padding: 10px 12px; overflow-wrap: anywhere;
  color: var(--text); background: var(--bg-soft);
  border: 1px solid var(--hairline-soft); border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: 12px;
}
.mod-result-placeholder { color: var(--text-3); font-size: 12.5px; }

/* Auto-Grind apply block */
.mod-autogrind-apply { margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--hairline); }
.mod-autogrind-head { display: grid; gap: 3px; margin-bottom: 12px; }
.mod-autogrind-head .small, .small { color: var(--text-3); font-size: 11.5px; }
.mod-check-row { display: flex; align-items: center; gap: 9px; color: var(--text-2); font-size: 12.5px; cursor: pointer; }
.mod-autogrind-controls { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 14px; margin-top: 14px; }
.mod-inline-field { display: grid; align-content: start; gap: 6px; min-width: 0; }
.mod-inline-field > span { color: var(--text-3); font-size: 10px; font-weight: 600; letter-spacing: .08em; text-transform: uppercase; }

@media (max-width: 1080px) {
  .two-col { grid-template-columns: 1fr; gap: 0; }
}

/* =============================================================================
   Users (admin)
   ========================================================================== */
/* Fields were full-bleed: a username box 1140px wide reads as a page-width
 * banner rather than an input. */
.users-form {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  align-items: end; gap: 14px; max-width: 860px;
}
.users-field { display: grid; align-content: start; gap: 6px; min-width: 0; }
.users-field > span { color: var(--text-2); font-size: 12px; font-weight: 500; }
.users-form > .button { justify-self: start; }
.users-form > .muted, .users-form > p { grid-column: 1 / -1; color: var(--text-3); font-size: 11.5px; }

/* =============================================================================
   Auto-Grind dialogs — confirm / prompt / account form / camp / board config
   ========================================================================== */
.ag-dialog-content { display: grid; gap: 14px; }
.ag-dialog-content h2 { margin: 0; padding-right: 34px; }
.ag-dialog-message { color: var(--text-2); font-size: 13px; line-height: 1.55; }
.ag-dialog-actions { justify-content: flex-end; padding-top: 4px; }
.ag-events-lb-modal, #ag-events-lb-modal { width: min(920px, calc(100vw - 32px)); }
.ag-events-lb-content { display: grid; gap: 14px; }
.ag-events-lb-table { max-height: 56vh; overflow: auto; }

.ag-form { display: grid; gap: 14px; }
.ag-form .form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; }
.ag-form-row-account-meta { align-items: start; }
.ag-form-group-name-color select { min-width: 0; }
.form-error {
  margin: 0; padding: 10px 12px;
  color: var(--danger); background: var(--danger-soft);
  border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
  border-radius: var(--radius-sm);
  font-size: 12.5px; line-height: 1.5;
}

/* Radio / checkbox option rows inside dialogs */
.ag-delete-group-option {
  padding: 10px 12px; color: var(--text-2); font-size: 12.5px; line-height: 1.5;
  background: var(--surface); border: 1px solid var(--hairline-soft);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur) ease, background var(--dur) ease;
}
.ag-delete-group-option:hover { border-color: var(--text-3); }
.ag-delete-group-option:has(input:checked) { color: var(--text); border-color: var(--accent); background: var(--accent-soft); }

/* "Camp if full" switch card */
.ag-team-camp-group { gap: 0; }
.ag-team-camp-card {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 14px;
  padding: 13px 14px; cursor: pointer;
  background: var(--surface); border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  transition: border-color var(--dur) ease;
}
.ag-team-camp-card:hover { border-color: var(--text-3); }
.ag-team-camp-card:has(input:checked) { border-color: var(--accent); }
.ag-team-camp-copy { display: grid; gap: 4px; min-width: 0; }
.ag-team-camp-title { color: var(--text); font-size: 12.5px; font-weight: 600; }
.ag-team-camp-help { color: var(--text-3); font-size: 11px; line-height: 1.5; }
.ag-team-camp-switch { display: inline-flex; align-items: center; flex: 0 0 auto; padding-top: 2px; }

/* Board config */
.ag-events-board-config { display: grid; gap: 20px; }
.ag-events-board-config-grid--manual { align-items: start; }
.ag-events-board-config-grid--manual { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.ag-events-board-check { display: flex; align-items: center; gap: 9px; color: var(--text-2); font-size: 12.5px; cursor: pointer; }
.ag-events-board-note-group { grid-column: 1 / -1; }
.ag-events-board-config-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; padding-top: 4px; border-top: 1px solid var(--hairline-soft); }
.ag-spc-batch-group-panel { display: grid; gap: 10px; }
.ag-events-rate-summary { color: var(--text-3); font-size: 11.5px; }
.ag-row-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 5px; }

/* =============================================================================
   Account switcher in the topbar
   -----------------------------------------------------------------------------
   The workspace block used to sit at the top of the sidebar under a "WORKSPACE"
   label. It is a compact avatar button in the topbar now, so the sidebar is
   nothing but navigation.
   ========================================================================== */
.topbar .auth-slot { padding: 0; border: 0; }
.topbar .auth-rail::before { content: none; }
/* The button itself carries the chrome, at exactly the .icon-btn size. It used
 * to be a bare 34px hit area wrapping a 26px bordered chip, so the avatar drew
 * visibly smaller than the theme toggle sitting right next to it. */
.topbar .auth-rail-trigger {
  /* Same box as the .icon-btn theme toggle standing next to it. */
  width: var(--ctl-h); height: var(--ctl-h); min-height: var(--ctl-h);
  padding: 0; gap: 0; justify-content: center; border-radius: 8px;
  /* Not accent-filled: in the topbar it sits beside the primary button and a
   * second solid light block reads as a second primary action. */
  color: var(--text-2); background: var(--surface-2);
  border: 1px solid var(--hairline);
  transition: color var(--dur) ease, background var(--dur) ease, border-color var(--dur) ease;
}
.topbar .auth-rail-trigger::after { content: none; }
.topbar .auth-rail-trigger:hover,
.topbar .auth-rail.is-open .auth-rail-trigger { color: var(--text); background: var(--hairline); }
/* Just the letter now — the chip it used to sit in is the button. */
.topbar .auth-rail-initial {
  flex: none; width: auto; height: auto;
  color: inherit; background: none; border: 0; border-radius: 0;
  font-size: 13px; font-weight: 600; line-height: 1;
}
/* Anchor the popover to the button's right edge so it cannot run off-screen. */
.topbar .auth-rail-popover { top: 42px; right: 0; left: auto; }

/* =============================================================================
   Topbar status
   -----------------------------------------------------------------------------
   State reads as a coloured dot on the status label. The old standalone chip
   only ever repeated the sentence next to it ("Waiting" beside "Waiting for
   team sync"), so it was noise with a border around it.
   ========================================================================== */
.topbar-status-copy strong { display: inline-flex; align-items: center; gap: 7px; }
.topbar-status-copy strong::before {
  content: ""; flex: 0 0 6px; width: 6px; height: 6px;
  background: var(--text-3); border-radius: 50%;
}
.topbar-status.is-ok      .topbar-status-copy strong::before { background: var(--success); }
.topbar-status.is-warn    .topbar-status-copy strong::before { background: var(--warning); }
.topbar-status.is-err     .topbar-status-copy strong::before { background: var(--danger); }
.topbar-status.is-syncing .topbar-status-copy strong::before { background: var(--accent); animation: pulse 1.1s ease-in-out infinite; }

/* Auto-kick history: a scrolling list of kicks, each opening a stacked detail
 * dialog. The list owns the only scrollbar; the head, search and footer stay put. */
.team-autokick-history-controls { display: grid; }
.team-autokick-history-list {
    max-height: min(52vh, 460px);
    border-top: 1px solid var(--hairline-soft);
    border-bottom: 1px solid var(--hairline-soft);
}
.team-autokick-history-item {
    display: flex; align-items: baseline; justify-content: space-between; gap: 18px;
    width: 100%; padding: 10px 2px; text-align: left; background: none;
    border: 0; border-bottom: 1px solid var(--hairline-soft); border-radius: 0; cursor: pointer;
}
.team-autokick-history-item:last-child { border-bottom: 0; }
.team-autokick-history-item:hover .team-autokick-history-name,
.team-autokick-history-item:focus-visible .team-autokick-history-name {
    color: var(--accent-strong); text-decoration-color: currentColor;
}
.team-autokick-history-main { display: grid; gap: 2px; min-width: 0; }
/* Underlined so it reads as a link: the row opens the account, not just selects it. */
.team-autokick-history-name {
    color: var(--text); font-size: 12.5px; font-weight: 600;
    text-decoration: underline; text-underline-offset: 2px;
    text-decoration-color: var(--text-3); overflow-wrap: anywhere;
}
.team-autokick-history-reason { color: var(--text-3); font-size: 11px; }
.team-autokick-history-time {
    flex: 0 0 auto; color: var(--text-3); font-size: 11px; font-variant-numeric: tabular-nums;
}
.team-autokick-history-foot {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
}
.team-autokick-history-foot .muted { font-size: 11px; font-variant-numeric: tabular-nums; }
/* Sits above the history modal it is opened from, per the stacking note above. */
.team-autokick-player-modal { z-index: 95; }
.team-autokick-player-modal .detail-list dd { overflow-wrap: anywhere; }
