/* ============================================================================
   MA3 Admin Panel -- Field Command theme
   Design language: massiveassault3.com / Tournament HQ
   - Green #99ff7b for links / interactive / success
   - Amber #ffd055 for accents / CTA / active state
   - Roboto + Roboto Condensed, vendored locally (latin + cyrillic)
   - Dark teal surfaces (#151d22)

   Loaded AFTER style.css. Source order lets these rules win for the shell,
   shared primitives, and the Players page; page-specific modules not yet
   migrated keep their existing styling until rollout.
   ============================================================================ */

/* --- Webfonts: Roboto + Roboto Condensed (vendored woff2) ----------------- */
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/roboto-latin-400.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap;
  src: url(/fonts/roboto-latin-500.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/roboto-latin-700.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/roboto-cyrillic-400.woff2) format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 500; font-display: swap;
  src: url(/fonts/roboto-cyrillic-500.woff2) format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }
@font-face { font-family: "Roboto"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/roboto-cyrillic-700.woff2) format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }
@font-face { font-family: "Roboto Condensed"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/roboto-condensed-latin-400.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Roboto Condensed"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/roboto-condensed-latin-700.woff2) format("woff2");
  unicode-range: U+0000-00FF,U+0131,U+0152-0153,U+02BB-02BC,U+02C6,U+02DA,U+02DC,U+0304,U+0308,U+0329,U+2000-206F,U+2074,U+20AC,U+2122,U+2191,U+2193,U+2212,U+2215,U+FEFF,U+FFFD; }
@font-face { font-family: "Roboto Condensed"; font-style: normal; font-weight: 400; font-display: swap;
  src: url(/fonts/roboto-condensed-cyrillic-400.woff2) format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }
@font-face { font-family: "Roboto Condensed"; font-style: normal; font-weight: 700; font-display: swap;
  src: url(/fonts/roboto-condensed-cyrillic-700.woff2) format("woff2");
  unicode-range: U+0301,U+0400-045F,U+0490-0491,U+04B0-04B1,U+2116; }

/* --- Tokens ----------------------------------------------------------------
   Field Command, unified (see docs/DESIGN_SYSTEM_1401.md).
   Three material tiers only: Canvas (--bg + hex) / Panel (--surface, flat,
   --line, --r-md) / Overlay (--bg-deep, --line-strong, --shadow-2).
   Amber is a scalpel: active state, one CTA per view, focus ring, hex marks. */
:root {
  /* Surface ladder (SOLID, teal-derived) */
  --bg-deep:   #0a1114;   /* deepest: sidebar base, overlays, well floors */
  --bg:        #151d22;   /* page canvas */
  --surface:   #1b262c;   /* THE panel/card surface */
  --surface-2: #212e35;   /* raised: hover, active fills, table stripe */
  --well:      #0e1519;   /* recessed input/field wells */

  /* Back-compat aliases (legacy consumers) -> solid ladder */
  --bg-panel:   var(--surface);
  --bg-panel-2: var(--surface-2);

  /* Accent washes -- accent chrome only (badges, focus ring, active pills) */
  --bg-soft-amber:   rgba(255, 184, 0, 0.10);
  --bg-amber-strong: rgba(255, 184, 0, 0.22);
  --bg-soft-green:   rgba(154, 255, 123, 0.10);

  /* Text */
  --text:       #dcf8ff;
  --text-warm:  #fffbed;
  --text-muted: #a2b2b8;
  --text-dim:   #7f939d;
  --text-faint: rgba(220, 248, 255, 0.35);

  /* Borders: ONE hairline system, two weights */
  --line:        rgba(255, 255, 255, 0.07);
  --line-strong: rgba(255, 255, 255, 0.14);
  --line-amber:  rgba(255, 184, 0, 0.45);
  --line-green:  rgba(154, 255, 123, 0.40);
  --line-info:   rgba(184, 232, 255, 0.45);

  /* Surface fills. These used to be literal rgba(0,0,0,x) / rgba(255,255,255,x) in
     43 rules -- the classic dark-theme cheat, and the one thing that cannot survive a
     light surface: black-at-alpha reads as a recess on dark and as dirt on light,
     white-at-alpha the other way round. Tokens now, so one line per theme decides. */
  --fill-hover:  rgba(255, 255, 255, 0.03);  /* table row under the cursor */
  --fill-idle:   rgba(255, 255, 255, 0.16);  /* an inert dot: offline, no status */
  --fill-head:   rgba(0, 0, 0, 0.22);        /* table headers, section strips */
  --fill-well:   rgba(0, 0, 0, 0.25);        /* recessed: chips, inputs, quiet buttons */
  --fill-sunken: rgba(0, 0, 0, 0.35);        /* deeper: paste areas, drop targets */

  /* Brand. Amber does two jobs that pull apart once a light theme exists: as INK it
     must read on the page surface, as a FILL it must stay a bright amber that dark ink
     reads on. So they are separate tokens, and --on-accent is that dark ink -- dark in
     BOTH themes, because it always sits on amber. */
  --accent:     #ffd055;
  --accent-hot: #ffb800;
  --accent-fill:     #ffd055;
  --accent-fill-hot: #ffb800;
  --on-accent:  #0a1114;
  --link:       #99ff7b;
  --link-soft:  #d3ffc6;

  /* Semantic */
  --win:       #99ff7b;
  --win-soft:  rgba(154, 255, 123, 0.12);
  --loss:      #ff8080;
  --loss-soft: rgba(255, 128, 128, 0.12);
  --draw:      #e8dba0;
  --info:      #b8e8ff;
  --info-soft: rgba(184, 232, 255, 0.12);
  --role-dev:  #c9b3ff;
  --role-dev-soft: rgba(201, 179, 255, 0.12);

  /* Type families */
  --f-display: "Roboto Condensed", "Arial Narrow", Arial, sans-serif;
  --f-body:    "Roboto", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
  --f-mono:    ui-monospace, "SFMono-Regular", Consolas, Monaco, monospace;

  /* Type size scale */
  --fs-2xs:  10px;  /* nav-section eyebrows */
  --fs-xs:   11px;  /* badges, kpi caption */
  --fs-sm:   12px;  /* card titles, buttons, table header */
  --fs-base: 14px;  /* body, inputs, table cells */
  --fs-md:   15px;  /* nav links */
  --fs-lg:   18px;  /* sub-section titles */
  --fs-h1:   22px;  /* page header */
  --fs-kpi:  28px;  /* hero stat number (mono) */

  /* Radii: one ladder (--r retired; alias kept for stragglers) */
  --r-xs:  4px;    /* popover items, nested bits */
  --r-sm:  6px;    /* inputs, buttons, chips, popovers */
  --r-md: 10px;    /* ALL panels + modals */
  --r:     var(--r-sm);
  --r-pill: 999px;

  /* Spacing scale */
  --sp-1: 4px;  --sp-2: 8px;  --sp-3: 12px;
  --sp-4: 16px; --sp-5: 24px; --sp-6: 32px;

  /* Shadows: panels are FLAT; overlays get --shadow-2; transient (toast) --shadow-1 */
  --shadow-1: 0 8px 24px rgba(0, 0, 0, 0.35);
  --shadow-2: 0 20px 60px rgba(0, 0, 0, 0.55);

  /* Motion */
  --ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --dur-fast: 120ms;
  --dur:      180ms;

  /* The navigation rail's own fill. A token because the rail is a surface like any
     other, and the light theme has to be able to answer it. */
  --nav-bg: linear-gradient(180deg, rgba(10, 17, 20, 0.85), rgba(10, 17, 20, 0.35));

  /* Layout + stacking */
  --sidebar-w: 220px;
  --z-dropdown: 100; --z-toast: 200; --z-modal: 300; --z-error: 400;

  /* Chart ink. The canvas cannot read a CSS variable, so dashboards.js resolves these
     at render time and re-renders on a theme flip -- but they live HERE, because a
     palette belongs where a designer can see both themes side by side.

     Two tiers, as in charts.py: FILL for bars and areas, LINE for 2px strokes. The
     fill tier is validated on BOTH surfaces and so is shared; the line tier is not --
     these are the panel's brand hues and on a light surface a 2px #99ff7b stroke has
     1.17:1 contrast, i.e. it is not there. The light row is its own validated set. */
  --chart-line-1: #b8e8ff;  /* blue   */
  --chart-line-2: #99ff7b;  /* green  */
  --chart-line-3: #ffd055;  /* amber  */
  --chart-line-4: #c9b3ff;  /* purple */
  --chart-line-5: #ff8080;  /* red    */
  /* Sequential ramp, one hue, monotone lightness. On dark it runs dim -> bright
     ("more" is brighter than the surface); on light it has to run the other way. */
  --ramp-1: #886a10;
  --ramp-2: #a68000;
  --ramp-3: #c29816;
  --ramp-4: #e2b000;
  --ramp-5: #fed25a;
  --chart-axis: #8ba0a8;
  --chart-grid: rgba(231, 237, 242, 0.06);
  --chart-tick: rgba(231, 237, 242, 0.10);
  --chart-label: #a2b2b8;                          /* value written above a bar */
  --chart-partial: rgba(231, 237, 242, 0.045);     /* the still-filling last bucket */
  --chart-partial-edge: rgba(231, 237, 242, 0.16);
}

/* --- Light theme -----------------------------------------------------------
   Dark is the default and light is an opt-in: the attribute is only ever present
   because the reader asked for it (an inline script in <head> reads the stored
   choice before the first paint, so navigating does not flash). There is
   deliberately NO prefers-color-scheme rule -- a dark-by-default panel that
   silently turns white because of an OS setting is not what was asked for.

   The navigation rail is themed too: it is a surface, and a dark strip down the side
   of a light page reads as an unfinished job. The logo needs help to survive that --
   see the filter below the token block. */
:root[data-theme="light"] {
  --bg-deep:   #dbe3e7;
  --bg:        #e8edf0;
  --surface:   #f7f9fa;
  --surface-2: #edf2f4;
  --well:      #e2e9ec;

  --bg-soft-amber:   rgba(180, 124, 0, 0.12);
  --bg-amber-strong: rgba(180, 124, 0, 0.24);
  --bg-soft-green:   rgba(31, 122, 69, 0.12);

  --text:       #16232a;
  --text-warm:  #0b1720;
  --text-muted: #4a5c66;
  --text-dim:   #5d6f79;
  --text-faint: rgba(22, 35, 42, 0.45);

  --line:        rgba(16, 32, 40, 0.12);
  --line-strong: rgba(16, 32, 40, 0.24);
  --line-amber:  rgba(138, 97, 0, 0.55);
  --line-green:  rgba(31, 122, 69, 0.50);
  --line-info:   rgba(11, 110, 168, 0.50);

  /* Ink darkens; the amber FILL stays a fill, one step deeper so dark ink still
     clears 4.5:1 on it. */
  --accent:     #8a6100;
  --accent-hot: #a87400;
  --accent-fill:     #f0b429;
  --accent-fill-hot: #dc9f14;
  --link:       #1f7a45;
  --link-soft:  #14602f;

  --win:       #1f7a45;
  --win-soft:  rgba(31, 122, 69, 0.14);
  --loss:      #c0392b;
  --loss-soft: rgba(192, 57, 43, 0.12);
  --draw:      #7a5900;
  --info:      #0b6ea8;
  --info-soft: rgba(11, 110, 168, 0.12);
  --role-dev:  #5b3fc4;
  --role-dev-soft: rgba(91, 63, 196, 0.12);

  /* The same five jobs, inverted: a recess on light is a light grey wash, not black
     at alpha, and the hover is a darkening rather than a lightening. */
  --fill-hover:  rgba(16, 32, 40, 0.045);
  --fill-idle:   rgba(16, 32, 40, 0.18);
  --fill-head:   rgba(16, 32, 40, 0.05);
  --fill-well:   rgba(16, 32, 40, 0.05);
  --fill-sunken: rgba(16, 32, 40, 0.08);

  /* Shadows on light are a soft cool grey, not black: black at 0.55 over a white
     panel reads as smudge rather than elevation. */
  --shadow-1: 0 8px 24px rgba(20, 40, 55, 0.14);
  --shadow-2: 0 20px 60px rgba(20, 40, 55, 0.22);

  /* Line tier, revalidated against the light panel (#f7f9fa): lightness band, chroma
     floor and 3:1 contrast all pass, worst adjacent CVD 4.7 and normal-vision 17.0 --
     both better than the dark row scores on its own surface. Same hue per slot, so a
     metric does not change identity when the theme does. */
  --chart-line-1: #0b6ea8;
  --chart-line-2: #1f7a45;
  --chart-line-3: #b06a00;
  --chart-line-4: #7a52d0;
  --chart-line-5: #c8433f;
  --ramp-1: #f2d489;
  --ramp-2: #e6b955;
  --ramp-3: #d19a12;
  --ramp-4: #a97c00;
  --ramp-5: #7a5900;
  --nav-bg: linear-gradient(180deg, #eef3f5, #e4ebee);

  --chart-axis: #5d6f79;
  --chart-grid: rgba(16, 32, 40, 0.10);
  --chart-tick: rgba(16, 32, 40, 0.16);
  --chart-label: #4a5c66;
  --chart-partial: rgba(16, 32, 40, 0.05);
  --chart-partial-edge: rgba(16, 32, 40, 0.20);
}

/* --- Base ----------------------------------------------------------------- */
html, body {
  font-family: var(--f-body);
  font-size: 15px;
  color: var(--text);
  background: var(--bg);
}
body {
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Subtle hex texture, blends into the background.

     A honeycomb does NOT tile as one hexagon in a rectangle: hex rows interlock,
     offset by half a width, so a rectangular tile of one hex leaves the rows
     disconnected with a stub hanging off every bottom vertex -- which is what the
     panel shipped, and it read as a half-drawn grid.

     This is the real fundamental tile. With side s = 32 the hex width is sqrt(3)*s
     = 55.4256 and the vertical period is 3s = 96 (two rows, at which point the
     half-width offset comes back round). The path is one hexagon plus the vertical
     edge below it; every second hexagon of the comb is formed by the neighbouring
     tiles' edges. Only the LEFT vertical is drawn -- the right one arrives as the
     next tile's left, so no line is painted twice at double opacity. background-size
     rounds the tile to whole pixels so no seam opens between repeats. */
  background-image:
    radial-gradient(1100px 480px at 20% -160px, rgba(255, 184, 0, 0.045), transparent 70%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='55.4256' height='96' viewBox='0 0 55.4256 96'><path d='M0 16L27.7128 0L55.4256 16M55.4256 48L27.7128 64L0 48M0 16L0 48M27.7128 64L27.7128 96' fill='none' stroke='%23dcf8ff' stroke-opacity='0.022' stroke-width='1'/></svg>");
  background-size: auto, 55px 96px;
  background-attachment: fixed, fixed;
}

* { scrollbar-width: thin; scrollbar-color: rgba(255, 184, 0, 0.3) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: rgba(255, 184, 0, 0.3); border-radius: 6px; border: 2px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: rgba(255, 184, 0, 0.5); background-clip: padding-box; }

/* The hex texture again for light: the same tile geometry, but a near-white stroke on
   a light canvas is nothing at all, so the ink flips to the page's own dark and the
   amber glow at the top drops to a quarter of its strength (on white it was a stain). */
:root[data-theme="light"] body {
  background-image:
    radial-gradient(1100px 480px at 20% -160px, rgba(180, 124, 0, 0.05), transparent 70%),
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='55.4256' height='96' viewBox='0 0 55.4256 96'><path d='M0 16L27.7128 0L55.4256 16M55.4256 48L27.7128 64L0 48M0 16L0 48M27.7128 64L27.7128 96' fill='none' stroke='%23102028' stroke-opacity='0.05' stroke-width='1'/></svg>");
}

/* The rail follows the theme like every other surface. The logo is the one thing that
   does not survive the flip on its own -- it is a light-on-transparent PNG, invisible on
   a light strip -- so it is inverted and hue-rotated back: white artwork becomes near
   black, and the blue "3" comes out blue again rather than orange. One asset, both
   themes, no second file to keep in step. */
:root[data-theme="light"] .sidebar-brand-logo,
:root[data-theme="light"] .mobile-bar-logo {
  filter: invert(1) hue-rotate(180deg);
}

/* Amber on a light page is a stain at 0.3 alpha; the thumb needs the ink weight the
   rest of the theme uses. */
:root[data-theme="light"] * { scrollbar-color: rgba(138, 97, 0, 0.45) transparent; }
:root[data-theme="light"] *::-webkit-scrollbar-thumb { background: rgba(138, 97, 0, 0.40); background-clip: padding-box; }
:root[data-theme="light"] *::-webkit-scrollbar-thumb:hover { background: rgba(138, 97, 0, 0.60); background-clip: padding-box; }
/* Native controls (date pickers, selects, the caret) follow the theme instead of
   staying dark-rendered on a light page. */
:root { color-scheme: dark; }
:root[data-theme="light"] { color-scheme: light; }

a { color: var(--link); transition: color var(--dur) var(--ease-out); }
a:hover { color: var(--link-soft); }
::selection { background: rgba(255, 208, 85, 0.45); color: var(--on-accent); }

/* --- Sidebar shell -------------------------------------------------------- */
/* FIXED, not sticky. Sticky only pins an element inside its containing block, and it
   held up here only for as long as the content column was its own scroll container --
   once the document became the scroller (see .content), a page taller than the viewport
   scrolled the rail away with it. Fixed cannot do that. The rail is a column: header,
   theme switch, the scrolling link list, then the footer pinned at the bottom, so its
   height is the viewport and never the page.

   ``100dvh`` after ``100vh``: on iOS 100vh is the viewport WITHOUT the browser bars, so
   a 100vh rail is taller than the screen and its footer hides under the toolbar. */
.sidebar {
  width: var(--sidebar-w);
  background: var(--nav-bg);
  border-right: 1px solid var(--line);
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: var(--z-dropdown);
  height: 100vh;
  height: 100dvh;
}
/* The rail is out of flow now, so the column has to be moved over by hand. */
.content { margin-left: var(--sidebar-w); }

.sidebar-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 18px 16px;
  border-bottom: 1px solid var(--line);
}
.sidebar-brand-logo { height: 30px; width: auto; display: block; filter: drop-shadow(0 1px 0 rgba(0, 0, 0, 0.5)); }
.sidebar-brand-tag {
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent);
  padding: 3px 9px;
  border: 1px solid var(--line-amber);
  border-radius: var(--r-pill);
  background: var(--bg-soft-amber);
}

/* Theme switch, directly under the brand block. Sized like a nav row rather than like
   a form control: it belongs to the chrome, so it reads as part of the rail and not as
   a widget dropped on top of it. */
.theme-switch {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 18px;
  background: none;
  border: 0;
  border-bottom: 1px solid var(--line);
  color: var(--text-dim);
  cursor: pointer;
  font-family: var(--f-display);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  transition: color var(--dur) var(--ease-out);
}
.theme-switch:hover { color: var(--text-warm); }
.theme-switch-track {
  position: relative;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 46px;
  height: 22px;
  padding: 0 5px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--fill-well);
}
/* Both icons are always there and the thumb slides between them, so the control shows
   its two states at rest instead of asking the reader to guess what a click will do.
   The icons sit ABOVE the thumb: underneath it the active glyph -- the one that says
   which theme is on -- was hidden by the very thing pointing at it. The glyph on the
   thumb therefore wears the ink that reads on amber. */
.theme-switch-ico { position: relative; z-index: 2; transition: color var(--dur) var(--ease-out); }
.theme-switch-moon { color: var(--on-accent); }
.theme-switch-sun  { color: var(--text-faint); }
.theme-switch[aria-checked="true"] .theme-switch-moon { color: var(--text-faint); }
.theme-switch[aria-checked="true"] .theme-switch-sun  { color: var(--on-accent); }
.theme-switch-thumb {
  position: absolute;
  top: 2px;
  left: 2px;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  background: var(--accent-fill);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
  transition: transform var(--dur) var(--ease-out);
}
.theme-switch[aria-checked="true"] .theme-switch-thumb { transform: translateX(24px); }
.theme-switch:focus-visible { outline: none; }
.theme-switch:focus-visible .theme-switch-track {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--bg-soft-amber);
}
@media (prefers-reduced-motion: reduce) { .theme-switch-thumb { transition: none; } }

/* The link list is the only part of the rail that scrolls, and it has to: an admin gets
   5 section headings and 21 links, about 1090px of content, so on any window shorter
   than that the tail of the nav was painted OUTSIDE the 100vh sticky box and never came
   back -- at 1440x900 the list stopped mid-"Population" and OPERATIONS, Audit, Reports
   and the LOGOUT button were unreachable. ``min-height: 0`` is the load-bearing half: a
   flex item defaults to min-height:auto and refuses to shrink below its content, which
   is exactly how the overflow escaped. Header, theme switch and footer stay pinned. */
.nav-links { padding: 12px 0; flex: 1 1 auto; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
.nav-links li a {
  display: flex;
  align-items: center;
  padding: 10px 18px;
  color: var(--text-muted);
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 500;
  letter-spacing: 0.02em;
  border-left: 3px solid transparent;
  transition: color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.nav-links li a:hover { background: var(--bg-soft-green); color: var(--text-warm); }
.nav-links li a.active {
  background: var(--bg-soft-amber);
  color: var(--accent);
  border-left-color: var(--accent);
  padding-left: 18px;       /* override style.css 13px; border now carries the indent */
}

.sidebar-footer { padding: 14px 18px; border-top: 1px solid var(--line); }
.sidebar-user { color: var(--text-dim); font-size: 13px; }
.sidebar-logout {
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  font-family: var(--f-display);
  font-size: 12px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  padding: 7px 0;
}
.sidebar-logout:hover { background: var(--loss-soft); border-color: rgba(255, 128, 128, 0.45); color: var(--loss); }

/* --- Content -------------------------------------------------------------- */
/* ``overflow: visible`` undoes ``style.css:.content{overflow-y:auto}``, and it is not a
   cosmetic change. That one declaration made the content column its OWN scroll
   container, which did two harmful things at once. It gave the page two independent
   scrollers -- scrolling to the bottom of the document left the column's internal
   scroll untouched, with dead space below it -- and, because a container with
   overflow-y:auto computes overflow-x to auto as well, it silently swallowed every
   horizontal overflow: a 1434px games table simply ended at the screen edge with no
   scrollbar and no hint that anything was missing. The document is the scroller; the
   sidebar's sticky behaviour is unaffected, and wide content now scrolls inside the
   containers that were built for it. */
.content {
  background: transparent; padding: 28px clamp(20px, 3vw, 40px) 56px;
  overflow: visible; min-height: 0;
  /* ``min-width: 0`` stops a wide descendant from stretching the column. As a flex item
     the content column defaults to min-width:auto, so a 1434px table made the column
     1434px wide and every card inside it computed `width: 100%` of THAT -- which is why
     on a phone the filter card and the legend ran off the right edge while the table
     itself scrolled correctly. The table still scrolls in its own box; the column now
     stays the width of the screen. */
  min-width: 0;
}
#table-container, .table-scroll { min-width: 0; }

/* --- Page header ---------------------------------------------------------- */
.page-header { margin-bottom: 22px; }
.page-header h1 {
  font-family: var(--f-display);
  font-size: 30px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  color: var(--text);
}

/* --- Filters bar ---------------------------------------------------------- */
.filters {
  gap: 16px 24px;
  padding: 14px 18px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  margin-bottom: 22px;
}
.filter-label {
  font-family: var(--f-display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--text-dim);
}
.filter-input {
  background: var(--fill-sunken);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 14px;
  padding: 8px 12px;
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.filter-input::placeholder { color: var(--text-dim); }
.filter-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber); }

.radio-group { gap: 6px; flex-wrap: wrap; }
.radio-label {
  position: relative;
  color: var(--text-muted);
  font-family: var(--f-display);
  font-size: 13px;
  font-weight: 500;
  padding: 5px 11px;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  background: var(--fill-head);
  transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.radio-label:hover { color: var(--text); border-color: var(--line-amber); }
/* The native dot is hidden; the pill itself shows selection. */
.radio-label input[type="radio"] { position: absolute; opacity: 0; pointer-events: none; }
.radio-label:has(input[type="radio"]:checked) {
  color: var(--accent);
  border-color: var(--line-amber);
  background: var(--bg-soft-amber);
}
.radio-label:has(input[type="radio"]:focus-visible) { box-shadow: 0 0 0 3px var(--bg-soft-amber); }

/* --- Data table ----------------------------------------------------------- */
.data-table { font-family: var(--f-body); font-size: 14px; }
.data-table thead th {
  padding: 12px 14px;
  background: var(--fill-head);
  border-bottom: 1px solid var(--line-strong);
  color: var(--text-dim);
  font-family: var(--f-display);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.12em;
}
.data-table thead th.sortable:hover { color: var(--text-warm); background: var(--bg-soft-amber); }
.data-table thead th.sorted-asc,
.data-table thead th.sorted-desc { color: var(--accent); }
.data-table tbody tr { border-bottom: 1px solid var(--line); transition: background var(--dur) var(--ease-out); }
.data-table tbody tr:hover { background: var(--bg-soft-amber); }
.data-table td { padding: 11px 14px; color: var(--text); }

/* Players columns */
.col-id { color: var(--text-dim); font-family: var(--f-mono); font-weight: 500; }
td.col-id .action-icon { color: var(--text-faint); }
td.col-id .action-icon:hover { color: var(--accent); }
.col-email { color: var(--text-muted); font-size: 13px; }
.player-email { color: var(--text); }
.player-name { color: var(--text-warm); font-weight: 500; }
.col-datetime { color: var(--text-dim); font-family: var(--f-mono); font-size: 12px; }
/* Centered so the header ("Games / total / active") sits directly over the
   "N / N" numbers -- right-alignment left the caption drifting off the column. */
.col-counts { text-align: center; }
.col-counts-head { text-align: center; white-space: nowrap; }
/* Header legend that maps the "total / active" numbers below to their meaning,
   colour-matched so each word reads in the same hue as the count it names. */
.counts-legend { display: block; margin-top: 2px; font-size: 11px; font-weight: 400; font-family: var(--f-mono); }
.count-total { color: var(--text); font-family: var(--f-mono); }
.count-separator { color: var(--text-faint); }
.count-active { color: var(--link); font-family: var(--f-mono); }

/* Online status dot */
.online-dot { width: 9px; height: 9px; }
.online-dot.online { background: var(--win); box-shadow: 0 0 6px var(--win); }
.online-dot.offline { background: var(--fill-idle); }

/* --- Account type badges (semantic, branded pills) ------------------------ */
.account-type-badge {
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  background: var(--fill-well);
  color: var(--text-muted);
}
.account-type-human   { color: var(--win);      border-color: var(--line-green);             background: var(--win-soft); }
.account-type-ai      { color: var(--info);      border-color: rgba(184, 232, 255, 0.4);      background: var(--info-soft); }
.account-type-support { color: var(--accent);    border-color: var(--line-amber);             background: var(--bg-soft-amber); }
.account-type-dev     { color: var(--role-dev);  border-color: rgba(201, 179, 255, 0.4);      background: var(--role-dev-soft); }
.account-type-admin   { color: var(--loss);      border-color: rgba(255, 128, 128, 0.5);      background: var(--loss-soft); }
.account-type-unknown { color: var(--text-dim);  border-color: var(--line-strong);            background: var(--fill-well); }

/* --- Action icons --------------------------------------------------------- */
.action-icon {
  color: var(--text-faint);
  border-radius: var(--r-xs);
  transition: color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.action-icon:hover { color: var(--accent); background: var(--bg-soft-amber); }
.action-icon-active { color: var(--accent); background: var(--bg-soft-amber); }
.action-icon-danger { color: rgba(255, 128, 128, 0.5); }
.action-icon-danger:hover { color: var(--loss); background: var(--loss-soft); }

/* --- Popovers: account-type dropdown + identities ------------------------- */
.type-dropdown {
  background: var(--bg-deep);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-2);
  padding: 5px;
}
.type-option { border-radius: var(--r-xs); color: var(--text); font-family: var(--f-body); font-size: 13px; padding: 7px 12px; }
.type-option:hover { background: var(--bg-soft-amber); color: var(--accent); }
.type-option-current { color: var(--accent); font-weight: 700; }

.identities-list { gap: 4px; }
.identity-platform {
  border-radius: var(--r-xs);
  background: var(--info-soft);
  border: 1px solid rgba(184, 232, 255, 0.35);
  color: var(--info);
  font-family: var(--f-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.08em;
}
.identity-id { color: var(--text-muted); font-family: var(--f-mono); }
.identity-empty { color: var(--text-dim); }

/* --- Delete confirm row --------------------------------------------------- */
.row-confirm-delete td { background: var(--loss-soft); border-top: 1px solid rgba(255, 128, 128, 0.4); border-bottom: 1px solid rgba(255, 128, 128, 0.4); }
.confirm-delete-text { color: var(--text); font-size: 14px; }
.confirm-delete-text strong { color: var(--loss); }
.btn-confirm-delete {
  background: var(--loss-soft);
  border: 1px solid rgba(255, 128, 128, 0.5);
  border-radius: var(--r-sm);
  color: var(--loss);
  font-family: var(--f-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 6px 14px;
}
.btn-confirm-delete:hover { background: rgba(255, 128, 128, 0.22); border-color: var(--loss); color: #fff; }
.btn-cancel-delete {
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  font-family: var(--f-display);
  font-weight: 600;
  padding: 6px 14px;
}
.btn-cancel-delete:hover { background: var(--bg-panel-2); color: var(--text); }

/* --- Pagination ----------------------------------------------------------- */
.pagination { padding: 20px 0 4px; }
.pagination-info { color: var(--text-dim); font-family: var(--f-mono); font-size: 12px; }
.page-btn {
  /* inline-block on the base so <button>, <a> AND disabled <span> pagers all size
     identically (min-width + vertical padding); a plain inline <span> ignored both. */
  display: inline-block;
  text-align: center;
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text-muted);
  font-family: var(--f-display);
  font-weight: 600;
  min-width: 34px;
  padding: 6px 10px;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out);
}
.page-btn:hover:not(:disabled):not(.page-btn-disabled) { background: var(--bg-soft-amber); border-color: var(--line-amber); color: var(--text-warm); }
.page-btn.active { background: var(--bg-amber-strong); border-color: var(--accent); color: var(--accent); }
/* Anchor-based pager (full-page history nav): keep it looking like the button pager. */
a.page-btn { text-decoration: none; }
.page-btn-disabled { opacity: 0.4; pointer-events: none; }
.page-ellipsis { color: var(--text-dim); }

/* "Results per page" control, top-right above a table. */
.table-toolbar { display: flex; justify-content: flex-end; align-items: center; margin: 4px 0 6px; }
.per-page { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.per-page-label { color: var(--text-dim); font-family: var(--f-mono); font-size: 12px; }
/* .content-scoped so this compact sizing out-specifies the unified
   `.content select` rule (min-height:38px, 14px) further down the file. */
.content .per-page-select {
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-display);
  font-size: 12px;
  padding: 4px 8px;
  min-height: 0;
  cursor: pointer;
}
.content .per-page-select:hover { border-color: var(--line-amber); }
.content .per-page-select:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber); }

/* --- Empty state ---------------------------------------------------------- */
.no-results {
  padding: 56px 0;
  text-align: center;
  color: var(--text-dim);
  font-family: var(--f-display);
  font-size: 16px;
  letter-spacing: 0.04em;
}

/* --- Floating error panel (feedback) -------------------------------------- */
.error-panel {
  background: var(--bg-deep);
  border: 1px solid rgba(255, 128, 128, 0.5);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}
.error-panel-header {
  background: var(--loss-soft);
  border-bottom: 1px solid rgba(255, 128, 128, 0.4);
  border-radius: var(--r-md) var(--r-md) 0 0;
  color: var(--loss);
  font-family: var(--f-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.error-panel-close { color: var(--loss); }
.error-panel-close:hover { color: #fff; }
.error-panel-body { color: var(--text-muted); font-family: var(--f-mono); }

/* --- htmx top loading bar ------------------------------------------------- */
.nprogress { position: fixed; top: 0; left: 0; right: 0; height: 2px; z-index: 200; pointer-events: none; opacity: 0; transition: opacity var(--dur) var(--ease-out); }
body.is-loading .nprogress { opacity: 1; }
.nprogress__bar { height: 100%; width: 35%; background: var(--accent-fill); box-shadow: 0 0 10px var(--accent-fill); animation: nprogress-slide 1.1s var(--ease-out) infinite; }
@keyframes nprogress-slide { 0% { margin-left: -35%; } 100% { margin-left: 100%; } }

/* --- Login page ----------------------------------------------------------- */
.login-body { background: var(--bg); }
.login-card {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
  padding: 36px 30px;
}
.login-brand { display: flex; justify-content: center; margin-bottom: 18px; }
.login-brand img { height: 40px; width: auto; }
.login-header {
  font-family: var(--f-display);
  font-size: 13px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--text-dim);
}
.login-error { background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.45); border-left-width: 3px; border-radius: var(--r-sm); color: var(--loss); }
.login-notice { background: var(--info-soft); border: 1px solid rgba(184, 232, 255, 0.4); border-left: 3px solid var(--info); border-radius: var(--r-sm); color: var(--info); padding: 10px 12px; margin-bottom: 12px; font-size: 13px; line-height: 1.5; }
.login-alt { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 14px; }
.login-alt form { margin: 0; }
.login-linkbtn { background: none; border: none; padding: 0; color: var(--link); font-family: var(--f-body); font-size: 13px; cursor: pointer; text-decoration: none; }
.login-linkbtn:hover { color: var(--link-soft); text-decoration: underline; }
.login-field label { font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.14em; color: var(--text-dim); }
.login-input {
  background: var(--fill-sunken);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-body);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.login-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber); }
/* One-time code: a single big, centred, wide-spaced field so the six digits
   read like a code rather than cramped text. text-indent offsets the trailing
   letter-spacing gap so the value stays optically centred. */
.login-otp-input {
  text-align: center;
  font-family: var(--f-mono);
  font-size: 30px;
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: 0.55em;
  text-indent: 0.55em;
  padding: 14px 10px;
  caret-color: var(--accent);
}
.login-otp-input::placeholder { letter-spacing: 0.35em; font-size: 20px; color: var(--text-faint); opacity: 1; }
.login-btn {
  background: var(--accent-fill);
  border: none;
  border-radius: var(--r-sm);
  color: var(--on-accent);
  font-family: var(--f-display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  transition: background var(--dur) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.login-btn:hover { background: var(--accent-fill-hot); border: none; }
.login-btn:active { transform: translateY(1px); }

/* ==========================================================================
   ROLLOUT -- remaining pages mapped onto brand tokens.
   These override the legacy purple modules in style.css via source order.
   ========================================================================== */

/* --- Shared: form controls across all pages ------------------------------- */
.field-row input, .field-row textarea, .field-row select,
.roster-add input[type="text"], .roster-bulk textarea,
.manual-match input, .round-scores input[type="number"],
.round-add-form input, .round-add-form select,
.filters-row input[type="text"],
.bulk-email-textarea,
.create-arena-map-select, .player-search-input,
.cg-tags-input, .cg-search-input,
.cg-paste-panel textarea {
  background: var(--fill-sunken);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-body);
  transition: border-color var(--dur) var(--ease-out), box-shadow var(--dur) var(--ease-out);
}
.field-row input:focus, .field-row textarea:focus, .field-row select:focus,
.roster-add input[type="text"]:focus, .roster-bulk textarea:focus,
.manual-match input:focus, .round-scores input[type="number"]:focus,
.round-add-form input:focus, .round-add-form select:focus,
.filters-row input[type="text"]:focus,
.bulk-email-textarea:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--bg-soft-amber);
  outline: none;
}

/* --- Shared: panel surfaces ----------------------------------------------- */
.filters-row, .round-add-form, .create-arena-fields, .create-arena-preview,
.bulk-create-fields {
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}

/* --- Shared: field / section labels --------------------------------------- */
/* Match the canonical .filter-label eyebrow (700 / 0.14em), so overview + round
   form labels read identically to the rest of the panel. */
.field-row label, .round-add-form label {
  color: var(--text-dim);
  font-family: var(--f-display);
  font-weight: 700;
  letter-spacing: 0.14em;
}

/* --- Shared: section headings --------------------------------------------- */
.tournament-header h1, .round-header h3, .round-scores h4, .manual-match h4,
.proposed-pairings h4, .roster-add h3, .roster-bulk h3, .roster-candidates h3,
.roster-participants h3, .create-arena-preview h3, .bulk-create-result h2,
.bulk-create-error h2, .cg-section-title {
  font-family: var(--f-display);
  color: var(--text);
  letter-spacing: 0.01em;
}

/* --- Shared: buttons ------------------------------------------------------ */
.button,
#tab-content button, .tournament-header button, .round-chips button,
.search-result button, .roster-candidates button,
.cg-btn, .cg-icon-btn,
.create-arena-submit, .bulk-create-submit {
  background: var(--bg-soft-amber);
  border: 1px solid var(--line-amber);
  border-radius: var(--r-sm);
  color: var(--accent);
  font-family: var(--f-display);
  font-weight: 700;
  letter-spacing: 0.04em;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.button:hover,
#tab-content button:hover, .tournament-header button:hover, .round-chips button:hover,
.search-result button:hover, .roster-candidates button:hover,
.cg-btn:hover, .cg-icon-btn:hover,
.create-arena-submit:hover, .bulk-create-submit:hover {
  background: rgba(255, 208, 85, 0.18);
  border-color: var(--accent);
  color: var(--text-warm);
}
/* Primary / "go" actions -> green */
.primary, .button.primary, .cg-btn-primary {
  background: var(--bg-soft-green);
  border: 1px solid var(--line-green);
  color: var(--link-soft);
}
.primary:hover, .button.primary:hover, .cg-btn-primary:hover {
  background: rgba(154, 255, 123, 0.18);
  color: #fff;
}
/* Destructive actions -> red */
.cg-btn-danger {
  background: var(--loss-soft);
  border: 1px solid rgba(255, 128, 128, 0.5);
  color: var(--loss);
}
.cg-btn-danger:hover { background: rgba(255, 128, 128, 0.22); color: #fff; }
.create-arena-submit:disabled, .create-arena-submit:disabled:hover {
  background: var(--fill-well); border-color: var(--line); color: var(--text-dim);
}

/* --- Badges: generic + tournament status / candidate ---------------------- */
.badge, .game-type-badge, .game-status-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  background: var(--fill-well);
  color: var(--text-muted);
}
.status-completed, .cand-accepted, .game-type-local, .game-status-completed { color: var(--win); border-color: var(--line-green); background: var(--win-soft); }
.status-registration, .status-in_progress, .cand-invited, .game-type-network, .game-status-in_progress { color: var(--info); border-color: rgba(184, 232, 255, 0.4); background: var(--info-soft); }
.status-draft, .status-challenge, .cand-applied, .game-type-tutorial, .game-status-challenge { color: var(--accent); border-color: var(--line-amber); background: var(--bg-soft-amber); }
.status-cancelled, .status-expired, .status-deleted, .cand-rejected { color: var(--loss); border-color: rgba(255, 128, 128, 0.5); background: var(--loss-soft); }
.status-scheduled, .cand-declined { color: var(--role-dev); border-color: rgba(201, 179, 255, 0.4); background: var(--role-dev-soft); }
.badge-email-failed { color: var(--accent); border-color: var(--line-amber); background: var(--bg-soft-amber); border-radius: var(--r-pill); }

/* --- Games page: tags, opponents, timestamps ------------------------------ */
.game-tag {
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-family: var(--f-mono);
}
.game-tag:hover { border-color: var(--line-amber); color: var(--text); }
.game-tag-active { background: var(--bg-soft-amber); border-color: var(--accent); color: var(--accent); }
.col-map { color: var(--link); font-family: var(--f-mono); }
.col-uuid { color: var(--text-dim); font-family: var(--f-mono); }
.col-turns { color: var(--text-muted); font-family: var(--f-mono); }
/* Turns cell: the count plus a toggle that expands the per-turn submission log. */
.turns-row { display: inline-flex; align-items: center; gap: 6px; }
.turns-count { font-family: var(--f-mono); color: var(--text-muted); }
/* --- Opponents column palette (one consistent scheme) ---------------------
   Soft light-blue = a normal, clickable player.
   Yellow = the player we are WAITING ON: their turn (in progress) OR to accept
            a challenge (awaiting).
   Green  = the winner of a finished game.
   Scoped + slightly over-specified so it beats the generic green .row-open link. */
.col-opponents .player-name,
.col-opponents a.player-name.row-open { color: var(--info); font-weight: 500; }
.col-opponents .opponent-active .player-name,
.col-opponents .opponent-active a.player-name.row-open,
.col-opponents .opponent-awaiting .player-name,
.col-opponents .opponent-awaiting a.player-name.row-open { color: var(--accent); font-weight: 700; }
.col-opponents .opponent-winner .player-name,
.col-opponents .opponent-winner a.player-name.row-open { color: var(--win); font-weight: 700; }
/* Same palette on the game-detail Participants table. */
.dash-card td.player-name { color: var(--info); font-weight: 500; }
tr.opponent-active td.player-name,
tr.opponent-awaiting td.player-name { color: var(--accent); font-weight: 700; }
tr.opponent-winner td.player-name { color: var(--win); font-weight: 700; }
/* Inline state flags next to a player name (yellow turn arrow, green win star). */
.opp-flag { font-family: var(--f-mono); font-size: 11px; margin-left: 3px; white-space: nowrap; }
.opp-flag-turn { color: var(--accent); }
.opp-flag-win { color: var(--win); }
.player-type { color: var(--text-dim); font-family: var(--f-mono); }
.alliance-id { color: var(--text-faint); font-family: var(--f-mono); }
/* Per-move log: "Turn N  <mover>  <timestamp>". */
.turn-timestamps-list { background: var(--bg-deep); border: 1px solid var(--line); border-radius: var(--r-sm); text-align: left; }
.turn-timestamp-entry { display: flex; align-items: baseline; gap: 8px; color: var(--text-dim); font-family: var(--f-mono); }
.turn-number { color: var(--accent); min-width: 52px; }
.turn-mover { color: var(--info); flex: 1 1 auto; }
.turn-ts { color: var(--text-dim); white-space: nowrap; }
.turn-timestamps-empty { color: var(--text-dim); }
/* Export G/M/R as visible amber pills instead of dim icons -- one-click downloads. */
.col-export { white-space: nowrap; }
.export-links { display: inline-flex; gap: 5px; }
.export-link {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: var(--r-xs);
  border: 1px solid var(--line-amber); background: var(--bg-soft-amber);
  color: var(--accent); font-family: var(--f-display); font-weight: 700; font-size: 12px;
  text-decoration: none; transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.export-link:hover { background: var(--bg-amber-strong); color: var(--text-warm); }

/* --- Games filter panel: two tiers (pills, then search fields) + legend ---- */
.games-filters { align-items: flex-start; }
/* Forces a clean line break between the pill row and the search-field row. */
.filters-break { flex-basis: 100%; height: 0; margin: 0; padding: 0; }
/* Search/chip filters flow 2-4 per row instead of each taking a full width.
   (No min-width: `.filters .filter-group { min-width:0 }` wins anyway, and a hard
   floor would fight the intended wrapping.) */
.games-filters .cg-tags-field { flex: 1 1 260px; }
/* The field is a COLUMN flex item (see .filters .filter-group below), so its
   input-wrap is only stretched to the field width with align-self:stretch --
   without this the wrap shrink-wraps to the input's intrinsic ~175px and clips
   longer placeholders like "Player name, Enter to add". */
.games-filters .cg-tags-field .cg-tags-input-wrap { align-self: stretch; }
/* Legend for the coloured player names in the Opponents column -- the sample
   word "Player" is shown in each state's exact colour. */
.filter-legend { display: flex; flex-wrap: wrap; gap: 8px 22px; margin: -10px 0 20px; font-size: 12px; color: var(--text-muted); }
.legend-item { display: inline-flex; align-items: baseline; gap: 7px; }
.lg { font-weight: 700; }
.lg-wait { color: var(--accent); }
.lg-click { color: var(--info); }
.lg-win { color: var(--win); }

/* --- Popovers: export menu ------------------------------------------------ */
.export-menu { background: var(--bg-deep); border: 1px solid var(--line-strong); border-radius: var(--r-sm); box-shadow: var(--shadow-2); }
.export-option { color: var(--text); }
.export-option:hover { background: var(--bg-soft-amber); color: var(--accent); }

/* --- Tournaments: header, tabs, chips ------------------------------------- */
.tournament-header { border-bottom: 1px solid var(--line); }
.tournament-header .meta { color: var(--text-dim); }
.tabs { border-bottom: 1px solid var(--line); }
.tabs .tab { color: var(--text-muted); font-family: var(--f-display); font-weight: 600; }
.tabs .tab:hover { color: var(--text-warm); }
.tabs .tab.active { color: var(--accent); border-bottom-color: var(--accent); }
.chip {
  background: var(--fill-well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-pill);
  color: var(--text-muted);
  font-family: var(--f-display);
}
.chip:hover { background: var(--bg-soft-amber); border-color: var(--line-amber); color: var(--text); }
.chip.active { background: var(--bg-amber-strong); border-color: var(--accent); color: var(--accent); }
.chip small { color: var(--text-dim); }
.chip-live small { color: var(--info); }
.chip-done small { color: var(--win); }
.chip-planned small { color: var(--accent); }

/* --- Tournaments: rounds / roster / misc ---------------------------------- */
.round-chips, .round-header, .manual-match, .status-actions { border-color: var(--line); }
.round-header h3 { color: var(--text); }
.round-notice { border-color: var(--line-amber); background: rgba(255, 176, 0, 0.10); color: var(--text); }
.round-meta, .round-locked-note, .manual-match p, .round-scores p, .empty,
.search-no-results, .bulk-result, .pagination-footer { color: var(--text-dim); }
.search-result { background: var(--bg-panel); border-color: var(--line); }
.search-result:first-child { border-color: var(--line); }
.pagination-footer a { color: var(--link); }
#tab-content .data-table td a,
#tournaments-table-target .data-table td a { color: var(--link); }
#tab-content .data-table td a:hover,
#tournaments-table-target .data-table td a:hover { color: var(--link-soft); }
.logo-thumb { border-radius: var(--r-xs); }

/* --- Tournaments: inline alert (htmx-swapped) ----------------------------- */
.alert-error { background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.45); border-left-width: 3px; border-radius: var(--r-sm); color: var(--loss); }
.alert-error button { color: var(--loss); }

/* --- Create Game (cg-*) board --------------------------------------------- */
.cg-section { background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md); }
.cg-queue, .cg-tags-input-wrap, .cg-slot-target { background: var(--fill-sunken); border: 1px solid var(--line-strong); border-radius: var(--r-sm); }
.cg-tag-chip { background: var(--bg-soft-amber); color: var(--accent); border-radius: var(--r-pill); }
.cg-chip { background: var(--info-soft); border: 1px solid rgba(184, 232, 255, 0.3); border-radius: var(--r-sm); color: var(--text); }
.cg-slot-target.sortable-drag-over { border-color: var(--line-green); background: var(--bg-soft-green); }
.cg-matches { border: 1px solid var(--line); border-radius: var(--r-sm); }
.cg-match-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-sm); }
.cg-match-card-placeholder { background: var(--fill-head); }
.cg-submit-bar { background: var(--bg-deep); border-top: 1px solid var(--line); }
.cg-paste-panel, .cg-confirm-box { background: var(--bg-deep); border: 1px solid var(--line-strong); border-radius: var(--r-md); }
.cg-paste-panel { border-radius: 0; border-top: none; border-bottom: none; border-right: none; }
.cg-toast { background: var(--bg-deep); border: 1px solid var(--line-amber); border-radius: var(--r-sm); box-shadow: var(--shadow-1); color: var(--text); }
.cg-result-green { border-left: 4px solid var(--win); }
.cg-result-amber { border-left: 4px solid var(--accent); }
.cg-result-table tr.cg-result-ok td { color: var(--win); }
.cg-result-table tr.cg-result-err td { color: var(--loss); }
/* Outcome unknown (connection dropped mid-chunk): amber, not red -- may have succeeded. */
.cg-result-table tr.cg-result-unknown td { color: var(--accent); }
.create-arena-json { background: var(--bg-deep); border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text-muted); font-family: var(--f-mono); }

/* --- Bulk Create Accounts ------------------------------------------------- */
.bulk-create-help { color: var(--text-muted); }
.bulk-email-textarea { font-family: var(--f-mono); }
.bulk-file-input::file-selector-button {
  background: var(--bg-soft-amber); border: 1px solid var(--line-amber); border-radius: var(--r-sm);
  color: var(--accent); font-family: var(--f-display); font-weight: 700;
}
.bulk-file-input::file-selector-button:hover { background: rgba(255, 208, 85, 0.18); }
.bulk-create-table thead th { background: var(--fill-head); border-bottom: 1px solid var(--line-strong); color: var(--text-dim); font-family: var(--f-display); letter-spacing: 0.12em; }
.bulk-create-table td { border-bottom: 1px solid var(--line); color: var(--text); }
.bulk-create-table tr.row-created { background: var(--win-soft); }
.bulk-create-table tr.row-created td { color: var(--win); }
.bulk-create-table tr.row-invalid_format, .bulk-create-table tr.row-error { background: var(--loss-soft); }
.bulk-create-table tr.row-invalid_format td, .bulk-create-table tr.row-error td { color: var(--loss); }
.bulk-create-error { background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.45); border-radius: var(--r-md); color: var(--loss); }
.bulk-create-error pre { background: var(--bg-deep); border: 1px solid var(--line); border-radius: var(--r-sm); color: var(--text-muted); }

/* --- Audit ---------------------------------------------------------------- */
.col-game-id { color: var(--text-muted); font-family: var(--f-mono); }
.audit-details-count { color: var(--text); }
.audit-details-toggle, .audit-details-key { color: var(--text-dim); }
.audit-details-list { color: var(--text); }
.audit-details-value { font-family: var(--f-mono); }

/* --- Public tournament page ----------------------------------------------- */
.public-header { border-bottom: 1px solid var(--line); }
.public-header h1 { font-family: var(--f-display); color: var(--text); text-transform: uppercase; }
.public-header .description { color: var(--text-muted); }
.public-standings h2, .public-rounds h2 { font-family: var(--f-display); color: var(--text); }
.public-standings th, .public-rounds th { background: var(--fill-head); color: var(--text-dim); font-family: var(--f-display); letter-spacing: 0.1em; border-bottom: 1px solid var(--line-strong); }
.public-standings td, .public-rounds td { border-bottom: 1px solid var(--line); color: var(--text); }

/* --- Map Stats page -------------------------------------------------------- */
/* Filter console: one tactical panel holding every control, above the board. */
.ms-console {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 16px 30px;
  padding: 18px 22px;
  margin: 0 0 20px;
  background: linear-gradient(180deg, var(--bg-panel-2), var(--bg-panel));
  border: 1px solid var(--line);
  border-left: 3px solid var(--accent-hot);
  border-radius: var(--r);
}
.ms-filter { display: flex; flex-direction: column; gap: 8px; min-width: 0; }
.ms-filter-map { flex: 1 1 240px; max-width: 320px; }
.ms-filter-players { flex: 1 1 180px; max-width: 260px; }
.ms-filter-range { flex: 0 0 auto; }
.ms-filter-gametype { flex: 0 0 auto; }

/* Game-type segmented toggle (PVP | PVE); active button is amber. */
.ms-gt-toggle { display: inline-flex; border: 1px solid var(--line-strong); border-radius: var(--r-sm); overflow: hidden; }
.ms-gt-btn {
  padding: 9px 20px;
  min-height: 40px;
  background: var(--fill-head);
  border: none;
  color: var(--text-dim);
  font-family: var(--f-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.1em;
  cursor: pointer;
  transition: background var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.ms-gt-btn + .ms-gt-btn { border-left: 1px solid var(--line-strong); }
.ms-gt-btn:hover { color: var(--text); }
.ms-gt-btn.active { background: var(--accent-fill); color: var(--on-accent); }
.ms-filter-label {
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-dim);
}
.ms-console .ms-combo { width: 100%; }
.ms-console .ms-map-search,
.ms-console .ms-player-search { width: 100%; }

/* Combobox shared by the map picker and the player search */
.ms-combo { position: relative; }
.ms-map-search { min-width: 220px; }
.ms-player-search { min-width: 200px; }
.ms-combo-list {
  position: absolute;
  z-index: 40;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  max-height: 280px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--bg-deep);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.5);
}
.ms-combo-item {
  padding: 7px 10px;
  border-radius: var(--r-sm);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
}
.ms-combo-item:hover,
.ms-combo-item.ms-combo-active { background: var(--bg-soft-amber); color: var(--accent); }

/* Player autocomplete suggestions */
.ms-suggest { min-width: 240px; padding: 4px; }
.ms-suggest-item {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  width: 100%;
  padding: 7px 10px;
  border: none;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--text);
  font-family: var(--f-body);
  font-size: 13px;
  text-align: left;
  cursor: pointer;
}
.ms-suggest-item:hover { background: var(--bg-soft-amber); color: var(--accent); }
.ms-suggest-name { font-weight: 500; }
.ms-suggest-count { color: var(--text-dim); font-family: var(--f-mono); font-size: 11px; }
.ms-suggest-empty { padding: 8px 10px; color: var(--text-dim); font-size: 12px; }

/* Selected-player chips (below the search box) */
.ms-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.ms-chips:empty { display: none; }
.ms-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 4px 3px 10px;
  border: 1px solid var(--line-amber);
  border-radius: var(--r-pill);
  background: var(--bg-soft-amber);
  color: var(--accent);
  font-size: 12px;
}
.ms-chip-label { font-weight: 500; }
.ms-chip-remove {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 16px;
  height: 16px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: var(--fill-well);
  color: var(--text-muted);
  font-size: 13px;
  line-height: 1;
  cursor: pointer;
}
.ms-chip-remove:hover { background: var(--loss-soft); color: var(--loss); }

/* From/to range filters (finished games, win rate) */
.ms-range { display: flex; align-items: center; gap: 6px; }
.ms-range-input {
  width: 64px;
  padding: 5px 8px;
  background: var(--fill-head);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-mono);
  font-size: 13px;
}
.ms-range-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber); outline: none; }
.ms-range-sep { color: var(--text-dim); }
.ms-range-unit { color: var(--text-dim); font-family: var(--f-mono); font-size: 12px; }

/* Leaderboard table */
.ms-table { width: 100%; }
.ms-table thead th { vertical-align: bottom; font-size: 12.5px; padding-top: 14px; padding-bottom: 14px; }
.ms-table thead th.sortable { white-space: nowrap; }
.sort-caret { margin-left: 6px; font-size: 10px; color: var(--accent); }
.ms-summary-row td { padding: 10px 14px 12px; border-bottom: 1px solid var(--line-strong); }
.ms-summary-map {
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 12px;
}
.ms-summary-counts { color: var(--text-muted); font-size: 14px; }

/* Row hover: neutralise the blanket .data-table amber (same specificity, later
   in source, so it wins) and re-apply it to real player rows only. This also
   stops the amber TR from bleeding through the detail panel's cells. */
.ms-table tbody tr:hover { background: transparent; }
.ms-col-rank { width: 66px; }
.ms-player-row { cursor: pointer; }
/* Roomy, readable rows (well above the base data-table row height). */
.ms-table .ms-player-row > td { padding-top: 20px; padding-bottom: 20px; font-size: 16px; }
.ms-table .ms-player-row:hover > td { background: var(--bg-soft-amber); }
.ms-table .ms-player-row-open > td { background: var(--bg-soft-amber); }
.ms-player-row-open .ms-expand-toggle { color: var(--accent); transform: rotate(180deg); }

/* Veteran badge: a liquid-glass hexagon (an accent, not a medal) for the map's
   top players by battles. Uniform and frosted -- the number carries the standing.
   A hexagon clip-path cannot take a border, so the rim + lift come from layered
   drop-shadows (which follow the clipped shape) and an inner specular sheen. */
.ms-vet-hex {
  --vet-rgb: 255, 208, 85; /* fallback (gold) if the per-rank tint is absent */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 33px;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  /* Metallic tint (gold->bronze by rank) over a frosted-white base -- a tinted
     glass, not a solid medal. */
  background:
    linear-gradient(150deg, rgba(var(--vet-rgb), 0.72), rgba(var(--vet-rgb), 0.22) 55%, rgba(var(--vet-rgb), 0.05)),
    linear-gradient(153deg, rgba(255, 255, 255, 0.16), rgba(255, 255, 255, 0.03) 55%, transparent);
  backdrop-filter: blur(5px);
  -webkit-backdrop-filter: blur(5px);
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.5)) drop-shadow(0 0 0.6px rgba(var(--vet-rgb), 0.8));
}
.ms-vet-hex::before {
  content: "";
  position: absolute;
  inset: 0;
  background: radial-gradient(62% 45% at 32% 16%, rgba(255, 255, 255, 0.45), transparent 72%);
}
.ms-vet-num {
  position: relative;
  z-index: 1;
  font-family: var(--f-body);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.01em;
  color: var(--text-warm);
  text-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
}
/* Non-veteran position: a quiet ordinal, never a grid marker (>=AA contrast). */
.ms-rank-num { font-family: var(--f-mono); font-size: 13px; color: var(--text-dim); }

.ms-col-games .count-total { font-size: 18px; font-weight: 700; }
.ms-col-wins { color: var(--win); font-family: var(--f-mono); }
.ms-col-losses { color: var(--loss); font-family: var(--f-mono); }
.ms-col-winrate { white-space: nowrap; }
.ms-winrate-value { display: inline-block; min-width: 40px; font-family: var(--f-mono); color: var(--text); }
.ms-winrate-bar {
  display: inline-block;
  vertical-align: middle;
  width: 88px;
  height: 4px;
  margin-left: 10px;
  background: var(--fill-sunken);
  border-radius: 2px;
  overflow: hidden;
}
.ms-winrate-fill { display: block; height: 100%; background: var(--win); border-radius: 2px; }
.ms-col-expand { width: 40px; text-align: right; }
.ms-expand-toggle { transition: transform var(--dur) var(--ease-out); }
.ms-pager-row td { padding-top: 12px; }
.ms-pager-row:hover td, .ms-summary-row:hover td, .ms-empty-row:hover td { background: transparent; }

/* Per-player games detail panel. The panel cell keeps its recessed background on
   hover (the blanket .ms-table row-hover reset above stops any amber bleed-through
   that previously tinted the whole block yellow). */
.ms-detail-row > td {
  padding: 0 14px 16px;
  background: var(--bg-deep);
  border-bottom: 1px solid var(--line-strong);
  border-left: 3px solid var(--accent-hot);
}
/* Constant size (one page of taller games rows) so expanding never resizes the layout. */
.ms-detail-fixed { min-height: 640px; display: flex; flex-direction: column; }
.ms-detail-head { margin: 14px 0 4px; }
.ms-detail-title {
  color: var(--text-muted);
  font-family: var(--f-display);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.ms-games-table { font-size: 14px; margin-top: 8px; }
.ms-games-table thead th { font-size: 12px; }
.ms-games-table td { padding: 12px 14px; }
.ms-games-table tbody tr:hover { background: var(--fill-head); }
.ms-result-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: var(--r-pill);
  border: 1px solid var(--line-strong);
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.08em;
  background: var(--fill-well);
  color: var(--text-muted);
}
.ms-result-won { color: var(--win); border-color: var(--line-green); background: var(--win-soft); }
.ms-result-lost { color: var(--loss); border-color: rgba(255, 128, 128, 0.5); background: var(--loss-soft); }
.ms-result-draw { color: var(--accent); border-color: var(--line-amber); background: var(--bg-soft-amber); }
.ms-winner-name { color: var(--text-warm); font-weight: 500; }
.ms-winner-none { color: var(--text-dim); }
.ms-col-opponents { color: var(--text); }
.ms-teammates { color: var(--text-dim); font-size: 12px; }
.ms-col-turns { font-family: var(--f-mono); }
.ms-end-status { color: var(--text-dim); font-family: var(--f-mono); font-size: 12px; }
.ms-detail-pagination { display: flex; align-items: center; gap: 12px; padding: 10px 0 2px; margin-top: auto; }
.ms-detail-empty { margin: 8px 0; }

/* Download-replay action (one button, replaces the old G/M links) */
.col-replay { white-space: nowrap; }
.ms-replay-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border: 1px solid var(--line-green);
  border-radius: var(--r-pill);
  background: var(--bg-soft-green);
  color: var(--link);
  font-family: var(--f-display);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  text-decoration: none;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.ms-replay-btn:hover { background: rgba(154, 255, 123, 0.18); border-color: var(--link); color: var(--link-soft); }
.ms-replay-icon { font-size: 14px; line-height: 1; }

/* --- Standalone Map Stats site (MAP_STATS_ONLY) --------------------------- */
/* No sidebar: a full-width header, the content, and a pinned footer, stacked. */
body.layout-site { flex-direction: column; min-height: 100vh; }
body.layout-site .content { min-height: 0; flex: 1 0 auto; width: 100%; margin-left: 0; }

.sitehead {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  padding: 15px 32px;
  background: linear-gradient(180deg, rgba(10, 17, 20, 0.94), rgba(10, 17, 20, 0.55));
  border-bottom: 1px solid var(--line-amber);
}
.sitehead-brand { display: flex; align-items: center; gap: 18px; }
.sitehead-logo { height: 40px; width: auto; display: block; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6)); }
/* The logo already carries the wordmark, so the header names only the section. */
.sitehead-section {
  font-family: var(--f-display);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--accent);
  padding-left: 18px;
  border-left: 1px solid var(--line-strong);
}
.sitehead-user { display: flex; align-items: center; gap: 14px; }
.sitehead-username { color: var(--text-dim); font-size: 13px; }
.sitehead .sidebar-logout { padding: 7px 16px; }

.sitefoot {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
  flex-shrink: 0;
  padding: 16px 32px;
  border-top: 1px solid var(--line);
  font-family: var(--f-display);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.sitefoot-mark { color: var(--text-muted); font-weight: 600; }
.sitefoot-sep { color: var(--accent); }
.sitefoot-year { color: var(--text-dim); font-family: var(--f-mono); letter-spacing: normal; }

.ms-denied { max-width: 640px; line-height: 1.6; color: var(--text-muted); }
.ms-denied p { margin-bottom: 10px; }
.ms-denied strong { color: var(--text-warm); }

/* --- Map Stats: shared scroll guard + sort control ------------------------ */
/* Wide tables scroll inside their own box so the page body never scrolls
   horizontally on mid-width screens; on phones they become cards (below). */
.ms-board-scroll, .ms-games-scroll { overflow-x: auto; }
.ms-mobile-only { display: none; }
.ms-sort-control { display: flex; gap: 8px; align-items: stretch; }
.ms-sort-control select {
  flex: 1;
  background: var(--fill-head);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--text);
  font-family: var(--f-body);
  font-size: 14px;
  padding: 8px 10px;
}
.ms-sort-dir {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  padding: 0 12px;
  background: var(--fill-head);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  color: var(--accent);
  font-size: 12px;
  cursor: pointer;
}
.ms-sort-dir:hover { border-color: var(--accent); }

/* --- Map Stats: phone layout (cards) -------------------------------------- */
@media (max-width: 720px) {
  /* Header: slim, drop the username to save width, keep brand + logout. */
  .sitehead { padding: 12px 16px; gap: 12px; }
  .sitehead-logo { height: 30px; }
  .sitehead-brand { gap: 12px; }
  .sitehead-section { font-size: 11px; letter-spacing: 0.16em; padding-left: 12px; }
  .sitehead-username { display: none; }
  .content { padding: 16px 12px; }
  .sitefoot { padding: 16px; font-size: 11px; }

  /* Filter console: single column, full-width, touch-sized (16px inputs avoid
     the iOS focus-zoom), and reveal the mobile Sort control. */
  .ms-console { flex-direction: column; align-items: stretch; gap: 14px; padding: 14px; }
  .ms-console .ms-filter { flex: 1 1 auto; max-width: none; width: 100%; }
  .ms-mobile-only { display: flex; }
  .ms-gt-toggle { width: 100%; }
  .ms-gt-btn { flex: 1; min-height: 44px; }
  .ms-console .filter-input { min-height: 44px; font-size: 16px; min-width: 0; }
  .ms-range { gap: 10px; min-width: 0; }
  /* min-width:0 lets the number inputs shrink below their intrinsic width so the
     from/to pair never overflows the card (default flex min-width is auto). */
  .ms-range-input { flex: 1 1 0; min-width: 0; width: auto; min-height: 44px; font-size: 16px; }
  .ms-sort-control select { min-height: 44px; font-size: 16px; }

  /* Leaderboard table -> one card per player. */
  .ms-board-scroll, .ms-table, .ms-table > tbody { display: block; overflow: visible; }
  .ms-table thead { display: none; }
  /* :not([hidden]) so this block-display does not override the UA [hidden] rule
     that togglePlayerGames uses to collapse the per-player games row. */
  .ms-table tr:not([hidden]) { display: block; }
  .ms-summary-row td { display: block; padding: 2px 2px 12px; border: none; }
  .ms-empty-row td { display: block; padding: 12px 2px; }

  .ms-table .ms-player-row {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 12px;
    row-gap: 9px;
    padding: 15px 44px 15px 62px;
    margin-bottom: 10px;
    background: var(--bg-panel);
    border: 1px solid var(--line);
    border-radius: var(--r);
  }
  .ms-table .ms-player-row.ms-player-row-open { background: var(--bg-soft-amber); border-color: var(--line-amber); }
  .ms-table .ms-player-row > td { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: none; font-size: 14px; }
  .ms-table .ms-player-row > td.ms-col-rank { position: absolute; left: 15px; top: 16px; display: block; width: auto; }
  .ms-table .ms-player-row > td.col-player { order: 1; flex: 1 1 100%; display: block; font-size: 18px; font-weight: 600; color: var(--text-warm); white-space: normal; overflow-wrap: anywhere; }
  .ms-table .ms-player-row > td.ms-col-expand { position: absolute; right: 8px; top: 12px; width: auto; text-align: right; }
  .ms-table .ms-player-row > td.ms-col-games { order: 2; }
  .ms-table .ms-player-row > td.ms-col-wins { order: 3; }
  .ms-table .ms-player-row > td.ms-col-losses { order: 4; }
  .ms-table .ms-player-row > td.ms-col-winrate { order: 5; }
  .ms-table .ms-player-row > td.col-datetime { order: 6; }
  .ms-table .ms-player-row > td[data-label]::before {
    content: attr(data-label);
    color: var(--text-dim);
    font-family: var(--f-display);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .ms-player-row .ms-col-games .count-total { font-size: 16px; }
  .ms-rank-num { font-size: 15px; }

  /* Per-player games panel -> one card per game, full-width replay button.
     Gate on :not([hidden]) so this author display rule does not override the
     `hidden` attribute that togglePlayerGames uses to collapse the panel. */
  .ms-detail-row:not([hidden]) { display: block; }
  .ms-detail-row > td { display: block; padding: 0 0 12px; background: transparent; border: none; }
  .ms-detail-fixed { min-height: 0; }
  .ms-detail-head { margin: 6px 2px 8px; }
  .ms-games-scroll, .ms-games-table, .ms-games-table > tbody { display: block; overflow: visible; }
  .ms-games-table thead { display: none; }
  .ms-games-table tbody { display: block; }
  .ms-games-table tr {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 12px;
    row-gap: 8px;
    padding: 14px 16px;
    margin-bottom: 8px;
    background: var(--bg-panel);
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
  }
  .ms-games-table td { display: inline-flex; align-items: center; gap: 6px; padding: 0; border: none; font-size: 14px; }
  .ms-games-table td[data-label]::before {
    content: attr(data-label);
    color: var(--text-dim);
    font-family: var(--f-display);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
  }
  .ms-games-table td.ms-game-id { order: 1; font-weight: 700; font-size: 15px; }
  .ms-games-table td.ms-game-result { order: 2; }
  .ms-games-table td.ms-col-ended { order: 3; }
  .ms-games-table td.ms-col-winner { order: 4; }
  .ms-games-table td.ms-col-opponents { order: 5; }
  .ms-games-table td.ms-col-turns { order: 6; }
  .ms-games-table td.ms-col-started { order: 7; }
  .ms-games-table td.ms-col-finished { order: 8; }
  .ms-games-table td.col-replay { order: 9; flex: 1 1 100%; }
  .ms-games-table td.col-replay .ms-replay-btn { width: 100%; justify-content: center; padding: 12px; font-size: 12px; }

  /* Pagination: wrap and give buttons a real tap target. */
  .ms-pager-row td { display: block; padding: 10px 0 2px; }
  .pagination { flex-direction: column; align-items: stretch; gap: 12px; }
  .pagination-controls { flex-wrap: wrap; justify-content: center; gap: 6px; }
  .ms-detail-pagination { flex-wrap: wrap; gap: 8px; }
  .page-btn { min-height: 44px; }
}

/* ==========================================================================
   Dashboards (Players flux / Game production / Server health) + nav groups.
   Native inline-SVG charts; built on the Field Command tokens above.
   ========================================================================== */

/* Sidebar group headers */
.nav-links .nav-sec {
  padding: 14px 18px 5px;
  font-family: var(--f-display);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-faint);
}
.nav-links .nav-sec:first-child { padding-top: 6px; }

/* Page header with a right-aligned window picker */
.dash-header { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; flex-wrap: wrap; }
.window-picker { display: inline-flex; gap: 4px; }
.win-btn {
  font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
  color: var(--text-muted); background: var(--fill-well);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  padding: 5px 11px; text-decoration: none; transition: color var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out);
}
.win-btn:hover { color: var(--text-warm); border-color: var(--line-amber); }
.win-btn.active { color: var(--accent); border-color: var(--accent); background: var(--bg-soft-amber); }

/* KPI tiles */
.dash-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 12px; margin-bottom: 16px; }
.kpi {
  position: relative; padding: 14px 16px 15px;
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
}
.kpi::after { content: ""; position: absolute; top: 10px; right: 10px; width: 6px; height: 6px; border-radius: 50%; background: var(--fill-idle); }
.kpi-good::after { background: var(--win); box-shadow: 0 0 7px var(--win); }
.kpi-info::after { background: var(--info); box-shadow: 0 0 7px var(--info); }
.kpi-warn::after { background: var(--accent-fill-hot); box-shadow: 0 0 7px var(--accent-fill-hot); }
.kpi-bad::after  { background: var(--loss);  box-shadow: 0 0 8px var(--loss); }
.kpi-cap { font-family: var(--f-display); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.kpi-val { font-family: var(--f-mono); font-size: 30px; font-weight: 600; line-height: 1.1; margin-top: 6px; color: var(--text); font-variant-numeric: tabular-nums; }
.kpi-sub { font-family: var(--f-mono); font-size: 12px; color: var(--text-dim); margin-top: 5px; }
.kpi-sub a { color: var(--link); }
.kpi-delta.up { color: var(--win); } .kpi-delta.down { color: var(--loss); }

/* Chart cards */
.dash-card { background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md); padding: 12px 14px 8px; margin-bottom: 14px; }
.dash-card-head { display: flex; align-items: baseline; gap: 12px; margin-bottom: 6px; }
.dash-card-title { font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted); }
.dash-note { margin-left: auto; font-family: var(--f-mono); font-size: 11px; color: var(--text-faint); }
.dash-legend { margin-left: auto; display: flex; gap: 14px; font-family: var(--f-mono); font-size: 11px; color: var(--text-muted); }
.dash-legend .lg { display: inline-flex; align-items: center; gap: 6px; }
.dash-legend .lg i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
.dash-grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
.chart-wrap { overflow-x: auto; }
.chart-svg { display: block; }
.dash-foot { color: var(--text-dim); font-size: 13px; line-height: 1.55; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--line); }
.dash-foot a { color: var(--link); }

/* Checkbox pills (e.g. the Games "frozen" toggle) share the radio-pill look. */
.radio-label:has(input[type="checkbox"]:checked) {
  color: var(--accent);
  border-color: var(--line-amber);
  background: var(--bg-soft-amber);
}
.radio-label input[type="checkbox"] { position: absolute; opacity: 0; pointer-events: none; }

/* Detail pages (Account / Game) */
.detail-kv { width: 100%; border-collapse: collapse; font-size: 13px; }
.detail-kv td { padding: 7px 0; border-bottom: 1px solid var(--line); color: var(--text); vertical-align: top; }
.detail-kv td:first-child { color: var(--text-dim); font-family: var(--f-display); letter-spacing: 0.06em; width: 40%; text-transform: uppercase; font-size: 11px; }
.detail-kv tr:last-child td { border-bottom: none; }
.detail-kv a { color: var(--link); }
.identities-list .identity-row { padding: 5px 0; }
.detail-actions { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 20px; }
.detail-actions .button { text-decoration: none; padding: 8px 14px; border-radius: var(--r-sm); }
/* Clickable open-links inside the data tables */
.data-table a.row-open { color: var(--link); }
.data-table a.row-open:hover { color: var(--link-soft); }

/* ==========================================================================
   Confirmation modal (shown for any hx-confirm action). Data-changing ops
   never fire without an explicit OK.
   ========================================================================== */
.modal-overlay {
  position: fixed; inset: 0; z-index: 300;
  display: flex; align-items: center; justify-content: center;
  background: rgba(6, 10, 12, 0.66); backdrop-filter: blur(2px);
  animation: modal-fade var(--dur) var(--ease-out);
}
.modal-overlay.hidden { display: none; }
@keyframes modal-fade { from { opacity: 0; } to { opacity: 1; } }
.modal-box {
  width: min(440px, calc(100vw - 40px));
  background: var(--bg-deep); border: 1px solid var(--line-strong);
  border-radius: var(--r-md); box-shadow: var(--shadow-2); overflow: hidden;
}
.modal-title {
  padding: 14px 18px; border-bottom: 1px solid var(--line);
  font-family: var(--f-display); font-size: 12px; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
  background: var(--bg-soft-amber);
}
.modal-body { padding: 18px; color: var(--text); font-size: 14px; line-height: 1.55; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; padding: 0 18px 18px; }
.modal-btn {
  font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; padding: 9px 18px; border-radius: var(--r-sm); cursor: pointer;
  transition: background var(--dur) var(--ease-out), border-color var(--dur) var(--ease-out), color var(--dur) var(--ease-out);
}
.modal-cancel { background: var(--fill-well); border: 1px solid var(--line-strong); color: var(--text-muted); }
.modal-cancel:hover { background: var(--bg-panel-2); color: var(--text); }
.modal-ok { background: var(--bg-amber-strong); border: 1px solid var(--accent); color: var(--accent); }
.modal-ok:hover { background: var(--accent-fill); color: var(--on-accent); }

/* Clickable KPI tiles (dashboard drill-down) */
a.kpi, .kpi-link { display: block; text-decoration: none; color: inherit; cursor: pointer;
  transition: border-color var(--dur) var(--ease-out), background var(--dur) var(--ease-out); }
a.kpi:hover, .kpi-link:hover { border-color: var(--line-amber); background: var(--bg-panel-2); }
a.kpi .kpi-sub, .kpi-link .kpi-sub { color: var(--link); }

/* Active-filter banner (drill-down landing) */
.filter-banner { display: flex; align-items: center; gap: 12px; padding: 10px 14px; margin-bottom: 12px;
  background: var(--bg-soft-amber); border: 1px solid var(--line-amber); border-radius: var(--r-sm);
  color: var(--accent); font-size: 13px; font-family: var(--f-display); letter-spacing: 0.04em; }
.filter-banner strong { color: var(--text-warm); }
.filter-banner-clear { margin-left: auto; color: var(--link); font-family: var(--f-body); }

/* uPlot dark theme overrides (the vendored CSS is light by default) */
.chart-box { width: 100%; min-height: 150px; }
.chart-fallback { color: var(--text-dim); font-family: var(--f-mono); padding: 24px; text-align: center; }
.uplot, .u-legend { font-family: var(--f-mono); color: var(--text-muted); }
.u-title { color: var(--text); font-family: var(--f-display); }
.u-legend th, .u-legend td, .u-legend .u-value { color: var(--text-muted); }
.u-legend .u-marker { border-radius: 2px; }
.u-legend .u-inactive { opacity: 0.4; }
.u-select { background: rgba(255, 208, 85, 0.10); }

/* ==========================================================================
   Simple Create Game form (sg-*).
   ========================================================================== */
.sg-form { display: flex; flex-direction: column; gap: 18px; max-width: 900px; }
.sg-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; }
.sg-players { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.sg-field { display: flex; flex-direction: column; gap: 7px; }
.sg-field .filter-input, .sg-field select { width: 100%; }
.sg-advanced { border: 1px solid var(--line); border-radius: var(--r-md); padding: 4px 14px; background: var(--bg-panel); }
.sg-advanced summary { cursor: pointer; padding: 10px 0; font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.sg-advanced summary:hover { color: var(--text-warm); }
.sg-advanced[open] { padding-bottom: 14px; }
.sg-submit-bar { display: flex; align-items: center; gap: 14px; padding-top: 4px; }
.sg-submit-bar .cg-btn { text-decoration: none; padding: 10px 20px; }
.sg-hint { color: var(--text-dim); font-size: 13px; max-width: 900px; line-height: 1.5; }
#create-game-result { max-width: 900px; margin-top: 18px; }

/* ==========================================================================
   Autocomplete dropdown (reusable, for any text search field).
   ========================================================================== */
.ac-box {
  position: fixed; z-index: 250; max-height: 260px; overflow-y: auto;
  padding: 4px; margin: 0; list-style: none;
  background: var(--bg-deep); border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); box-shadow: var(--shadow-2);
}
.ac-item {
  padding: 7px 11px; border-radius: var(--r-xs); color: var(--text);
  font-family: var(--f-body); font-size: 13px; cursor: pointer; white-space: nowrap;
}
.ac-item:hover, .ac-item.ac-active { background: var(--bg-soft-amber); color: var(--accent); }

/* ==========================================================================
   Create Game -- merged page: Quick (2 players) + Multiple Games.
   ========================================================================== */
.qg-section, .mg-section { margin-bottom: 28px; }
.mg-section { margin-top: 34px; padding-top: 22px; border-top: 1px solid var(--line-strong); }
.qg-head { margin-bottom: 16px; }
.qg-title { font-family: var(--f-display); font-size: 18px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase; color: var(--text); }
.qg-sub { display: block; margin-top: 4px; color: var(--text-dim); font-size: 13px; }

/* Batch "group vs group" builder -- mirrors the Quick two-player matchup panel. */
.bg-matchup {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 20px; align-items: start;
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 20px; margin-bottom: 18px;
}
.bg-group-col { display: flex; flex-direction: column; gap: 12px; min-width: 0; }
/* The opener marker sits RIGHT NEXT TO the side label (flex-start, not
   space-between) so "SIDE A 6  > moves first" reads as one phrase -- it must be
   obvious which side opens. */
.bg-group-head { display: flex; justify-content: flex-start; align-items: center; gap: 12px; padding-bottom: 8px; border-bottom: 2px solid var(--line-info); }
.bg-group-b .bg-group-head { border-bottom-color: var(--line-amber); }
/* Faction colours: the game plays BLUE vs YELLOW, so Side A = blue, Side B = yellow. */
.bg-group-label { font-family: var(--f-display); font-size: 13px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--info); }
.bg-group-b .bg-group-label { color: var(--accent); }
.bg-group-label .cg-counter { margin-left: 6px; }
/* Opener marker, tinted to the faction that opens; neutral "both directions"
   when each pairing is created as two games (no single opener). */
.bg-first-marker { display: none; font-family: var(--f-mono); font-size: 11px; white-space: nowrap; }
.bg-first-marker.is-on { display: inline; }
.bg-group-a .bg-first-marker.is-on { color: var(--info); }
.bg-group-b .bg-first-marker.is-on { color: var(--accent); }
.bg-first-marker.is-both { display: inline; color: var(--text-dim); }
.bg-add-row { display: flex; gap: 8px; }
.bg-search { flex: 1; min-width: 0; }
.bg-add-row .cg-btn { white-space: nowrap; }
.bg-paste-panel:not([hidden]) { display: block; }
.bg-paste-area { width: 100%; min-height: 84px; box-sizing: border-box; background: var(--fill-sunken); border: 1px solid var(--line-strong); border-radius: var(--r-sm); color: var(--text); font-family: var(--f-mono); font-size: 12px; padding: 8px; resize: vertical; }
.bg-paste-actions { display: flex; gap: 8px; margin-top: 8px; }
/* Import feedback: keep what didn't resolve visible in the panel, not in a fleeting toast. */
.bg-import-feedback { margin-top: 10px; display: flex; flex-direction: column; gap: 4px; }
.bg-import-warn { color: var(--accent); font-size: 12px; }
.bg-import-amb { color: var(--text-muted); font-family: var(--f-mono); font-size: 12px; padding-left: 8px; }
.bg-chips { display: flex; flex-wrap: wrap; align-content: flex-start; gap: 8px; min-height: 96px; padding: 12px; background: var(--fill-head); border: 1px solid var(--line-strong); border-radius: var(--r-sm); }
/* Faint faction tint on each side's drop-zone, so the whole column reads blue / yellow. */
.bg-group-a .bg-chips { border-color: rgba(184, 232, 255, 0.28); }
.bg-group-b .bg-chips { border-color: rgba(255, 184, 0, 0.30); }
.bg-chips:empty { align-items: center; justify-content: center; }
.bg-chips:empty::before { content: attr(data-empty); color: var(--text-dim); font-size: 12px; font-style: italic; text-align: center; max-width: 240px; }
.bg-chip { display: inline-flex; align-items: center; gap: 7px; height: 28px; padding: 0 4px 0 10px; background: var(--info-soft); border: 1px solid rgba(184, 232, 255, 0.28); border-radius: 14px; color: var(--text); }
.bg-chip-name { font-size: 13px; }
.bg-chip-rating { font-family: var(--f-mono); font-size: 11px; color: var(--text-dim); }
.bg-chip-remove { border: none; background: transparent; color: var(--text-dim); font-size: 16px; line-height: 1; cursor: pointer; width: 20px; height: 20px; border-radius: 50%; display: inline-flex; align-items: center; justify-content: center; }
.bg-chip-remove:hover { color: #fff; background: rgba(255, 128, 128, 0.24); }

/* Center divider: swap button + "vs", vertically centred against the two sides. */
.bg-center { display: flex; flex-direction: column; align-items: center; gap: 8px; align-self: center; min-width: 60px; }
.bg-center-label { font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase; color: var(--text-dim); }
.bg-swap-btn {
  width: 44px; height: 44px; border-radius: 50%; border: 1px solid var(--line-strong);
  background: var(--fill-well); color: var(--text-dim); font-size: 20px; line-height: 1; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; transition: color .12s, border-color .12s, background .12s;
}
.bg-swap-btn:hover { color: var(--accent); border-color: var(--accent); background: var(--bg-soft-amber); }

/* First-move: four self-describing option cards, then an inline "each pair plays
   N games" stepper (the words are the label -- no separate description needed). */
.bg-controls { display: flex; flex-direction: column; gap: 18px; margin-bottom: 18px; }
.bg-firstmove-block { min-width: 0; }
.bg-block-label { display: block; font-family: var(--f-display); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 8px; }
.bg-fm-options { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.bg-repeat { display: flex; align-items: center; gap: 12px; }
.bg-repeat-text { font-family: var(--f-display); font-size: 16px; font-weight: 600; letter-spacing: 0.01em; color: var(--text); }
.bg-stepper { display: inline-flex; align-items: center; border: 1px solid var(--line-strong); border-radius: var(--r-sm); overflow: hidden; background: var(--fill-well); }
.bg-step { border: none; background: transparent; color: var(--text-dim); font-size: 20px; width: 40px; height: 42px; line-height: 1; cursor: pointer; display: inline-flex; align-items: center; justify-content: center; transition: color .12s, background .12s; }
.bg-step:hover { color: var(--accent); background: var(--bg-soft-amber); }
.bg-step-val { width: 54px; height: 42px; border: none; border-left: 1px solid var(--line-strong); border-right: 1px solid var(--line-strong); background: transparent; color: var(--text-warm); text-align: center; font-size: 17px; font-family: var(--f-mono); -moz-appearance: textfield; appearance: textfield; }
.bg-step-val:focus { outline: none; box-shadow: inset 0 0 0 2px var(--bg-soft-amber); }
.bg-step-val::-webkit-outer-spin-button, .bg-step-val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.bg-fm-opt { display: flex; flex-direction: column; gap: 4px; text-align: left; padding: 12px 14px; background: var(--fill-head); border: 1px solid var(--line-strong); border-radius: var(--r-sm); cursor: pointer; transition: border-color .12s, background .12s; }
.bg-fm-opt:hover { border-color: var(--line-amber); }
.bg-fm-a:hover { border-color: var(--line-info); }
.bg-fm-title { font-family: var(--f-display); font-weight: 700; font-size: 14px; letter-spacing: 0.02em; color: var(--text); }
.bg-fm-desc { font-size: 11px; line-height: 1.35; color: var(--text-dim); }
.bg-fm-opt.active { border-color: var(--accent); background: var(--bg-soft-amber); }
.bg-fm-opt.active .bg-fm-title { color: var(--text-warm); }
/* "Side A opens" -> blue, "Side B opens" -> yellow, matching the faction sides above. */
.bg-fm-a.active { border-color: var(--info); background: var(--info-soft); }
.bg-fm-b.active { border-color: var(--accent); background: var(--bg-soft-amber); }

.bg-file-label { display: inline-flex; align-items: center; cursor: pointer; }

.bg-submit-bar { display: flex; align-items: center; justify-content: space-between; gap: 16px; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--line); flex-wrap: wrap; }
.bg-preview { color: var(--text-dim); font-size: 13px; }
.cg-page-intro { color: var(--text-dim); font-size: 14px; line-height: 1.5; margin: -6px 0 22px; max-width: 920px; }
.cg-page-intro strong { color: var(--text); font-weight: 600; }
/* Match-settings card heading, aligned with the uppercase section titles above it. */
#cg-shared .cg-section-title { font-size: 13px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); margin-bottom: 4px; }

/* ---- Preview / confirm modal ---- */
.cg-confirm-modal { backdrop-filter: blur(3px); }
.bg-modal {
  display: flex; flex-direction: column; width: min(640px, calc(100vw - 32px)); max-height: 86vh;
  /* Same surface as the on-page panels: base bg + the panel overlay, composited to a
     solid so it reads identically to a card (matches the theme instead of a new hue). */
  background-color: var(--bg);
  background-image: linear-gradient(var(--bg-panel-2), var(--bg-panel-2));
  border: 1px solid var(--line-strong); border-radius: var(--r-md);
  box-shadow: 0 24px 60px rgba(0, 0, 0, 0.55); overflow: hidden; animation: bg-modal-in .14s ease-out;
}
@keyframes bg-modal-in { from { opacity: 0; transform: translateY(8px) scale(.985); } to { opacity: 1; transform: none; } }
.bg-modal-head { padding: 20px 22px 16px; border-bottom: 1px solid var(--line); }
.bg-modal-title { font-family: var(--f-display); font-size: 22px; font-weight: 700; letter-spacing: 0.02em; color: var(--text-warm); }
.bg-modal-count { color: var(--accent); }
.bg-modal-sub { margin-top: 4px; font-family: var(--f-mono); font-size: 12px; color: var(--text-dim); }
.bg-modal-body { padding: 18px 22px; overflow-y: auto; display: flex; flex-direction: column; gap: 18px; }
.bg-modal-settings { display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 12px 18px; }
.bg-set { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.bg-set-wide { grid-column: 1 / -1; }
.bg-set-k { font-family: var(--f-display); font-size: 10px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.bg-set-v { font-size: 14px; color: var(--text); word-break: break-word; }
.bg-set-empty { color: var(--text-dim); font-style: italic; }
.bg-modal-matches { display: flex; flex-direction: column; gap: 6px; }
.bg-matches-head { display: flex; align-items: baseline; gap: 8px; }
.bg-matches-title { font-family: var(--f-display); font-size: 11px; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim); }
.bg-matches-count { color: var(--accent); }
/* Legend sits on the LEFT, padded to line up with the opener marker in each row. */
.bg-matches-legend { font-family: var(--f-mono); font-size: 11px; color: var(--text-dim); padding-left: 15px; }
.bg-open { color: var(--accent); }
.bg-matches-list { max-height: 300px; overflow-y: auto; border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: var(--fill-head); }
/* Row: ▶ opener ... vs ... opponent, all reading left-to-right (opener first). */
.bg-match { display: flex; align-items: center; gap: 8px; padding: 7px 14px; border-bottom: 1px solid var(--line); }
.bg-match:last-child { border-bottom: none; }
.bg-match .bg-open { font-size: 10px; flex: none; }
.bg-match-a { color: var(--text); }
.bg-match-vs { font-family: var(--f-display); font-size: 10px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-faint); margin: 0 2px; }
.bg-match-b { color: var(--text-dim); }
.bg-match-more { padding: 8px 14px; font-size: 12px; font-style: italic; color: var(--text-dim); }
.bg-modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding: 14px 22px; border-top: 1px solid var(--line); background: var(--fill-head); }
@media (prefers-reduced-motion: reduce) { .bg-modal { animation: none; } }

@media (max-width: 1100px) {
  .bg-fm-options { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 900px) {
  .bg-matchup { grid-template-columns: 1fr; }
  .bg-center { flex-direction: row; align-self: auto; }
  .bg-swap-btn { transform: rotate(90deg); }
}

/* Two-player matchup: Player 1 (left) -- center -- Player 2 (right) */
.qg-matchup {
  display: grid; grid-template-columns: 1fr auto 1fr; gap: 18px; align-items: start;
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 18px; margin-bottom: 16px;
}
.qg-side { display: flex; flex-direction: column; gap: 10px; min-width: 0; }
.qg-side-2 { text-align: right; }
.qg-side-label {
  font-family: var(--f-display); font-size: 13px; font-weight: 700; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--accent);
  padding-bottom: 6px; border-bottom: 2px solid var(--line-amber);
}
.qg-side-2 .qg-side-label { border-bottom-color: var(--line-green); color: var(--link); }
.qg-side .player-slot, .qg-side .player-search-input { width: 100%; }
/* "moves first" marker -- shown on the side chosen by the first_mover radios */
.qg-first { display: none; font-family: var(--f-mono); font-size: 12px; color: var(--win); }
.qg-matchup:has(input[name="first_mover"][value="player_1"]:checked) .qg-side-1 .qg-first { display: block; }
.qg-matchup:has(input[name="first_mover"][value="player_2"]:checked) .qg-side-2 .qg-first { display: block; }

.qg-center { display: flex; flex-direction: column; align-items: center; gap: 12px; padding-top: 22px; min-width: 150px; }
.qg-vs { font-family: var(--f-display); font-size: 16px; font-weight: 700; letter-spacing: 0.18em; color: var(--text-dim); }
.qg-firstmove { display: flex; flex-direction: column; align-items: center; gap: 8px; }
.qg-firstmove-label { font-family: var(--f-display); font-size: 10px; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase; color: var(--text-dim); }
.qg-firstmove .radio-group { justify-content: center; }
.qg-random-note { display: none; font-family: var(--f-mono); font-size: 11px; color: var(--info); }
.qg-matchup:has(input[name="first_mover"][value="random"]:checked) .qg-random-note { display: block; }

.qg-config { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 14px; margin-bottom: 14px; }
.qg-submit-bar { display: flex; align-items: center; gap: 14px; }
#sg-result { margin-top: 16px; }

@media (max-width: 760px) {
  .qg-matchup { grid-template-columns: 1fr; }
  .qg-side-2, .qg-side-2 .qg-side-label { text-align: left; }
  .qg-center { padding-top: 0; }
}

/* Anchor styled as a button (e.g. "+ New tournament") */
a.button { display: inline-block; padding: 8px 16px; border-radius: var(--r-sm); text-decoration: none; cursor: pointer; }

/* ==========================================================================
   Create Accounts (email-only) -- redesigned input.
   ========================================================================== */
.ca-explain {
  max-width: 820px; margin-bottom: 20px; padding: 12px 16px;
  background: var(--info-soft); border: 1px solid rgba(184, 232, 255, 0.4);
  border-left: 3px solid var(--info); border-radius: var(--r-sm);
  color: var(--text-muted); font-size: 13px; line-height: 1.55;
}
.ca-explain strong { color: var(--text-warm); }
.ca-block { max-width: 820px; margin-bottom: 16px; }
.ca-block-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 10px; }
.ca-rows { display: flex; flex-direction: column; gap: 8px; }
.ca-row { display: flex; align-items: center; gap: 8px; }
.ca-row .ca-email { flex: 1; }
.ca-row-x { flex: none; font-size: 13px; }
.ca-bulk-block { border: 1px solid var(--line); border-radius: var(--r-md); background: var(--bg-panel); padding: 4px 14px; }
.ca-bulk-block summary { cursor: pointer; padding: 10px 0; font-family: var(--f-display); font-size: 12px; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.ca-bulk-block summary:hover { color: var(--text-warm); }
.ca-bulk-block[open] { padding-bottom: 14px; }
.ca-bulk-fields { display: flex; flex-direction: column; gap: 14px; }
.ca-submit-bar { display: flex; align-items: center; gap: 14px; max-width: 820px; }
.ca-count { font-family: var(--f-mono); font-size: 12px; color: var(--text-dim); }

/* Announcements "New announcement" form: a normal stacked form (constrained
   width, full-width fields) instead of the filter-bar layout that squeezed the
   title/message into fixed 180px boxes. */
.ann-form { display: flex; flex-direction: column; gap: 14px; max-width: 720px; }
.ann-form textarea.filter-input { resize: vertical; min-height: 84px; }

/* ==========================================================================
   Spacing / alignment cleanup (site-wide).
   ========================================================================== */
/* Wide tables scroll inside their own box instead of stretching the page. */
.table-scroll { overflow-x: auto; max-width: 100%; }

/* Filter bars: stack each control under its label and top-align every group,
   so mixed controls (radio pills, inputs, selects) line up instead of a ragged
   center-aligned row. */
.filters { align-items: flex-start; gap: 14px 22px; }
.filters .filter-group { flex-direction: column; align-items: flex-start; gap: 6px; min-width: 0; }
.filters .radio-group { gap: 6px; }

/* Consistent control height across text inputs and selects. */
.filter-input { min-height: 38px; }
select.filter-input { padding-top: 6px; padding-bottom: 6px; }

/* Games "Tags" column */
.col-tags { min-width: 120px; }
.col-tags .game-info-tags { display: flex; flex-wrap: wrap; gap: 4px; }
.col-tags-empty { color: var(--text-faint); font-family: var(--f-mono); }

/* Opponent nickname link keeps the highlighted-active colour when linked. */
.opponent-active a.player-name { color: var(--accent); }

/* Create Game -- submit bars.
   Multiple Games submit was position:sticky (floated over the whole page,
   confusing since it belongs to the bottom block). Make it a normal bar at the
   end of the Multiple Games section. */
.cg-submit-bar {
  position: static;
  margin-top: 16px;
  padding: 12px 16px;
  background: var(--bg-panel);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
}
/* Quick submit: a clear action bar separated from the Advanced panel above it,
   not a button cramped right under it. */
.qg-submit-bar {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.qg-submit-bar .cg-btn { padding: 11px 26px; }

/* ==========================================================================
   FIELD COMMAND — UNIFIED LAYER (docs/DESIGN_SYSTEM_1401.md).
   Appended last so one recipe wins over every earlier divergence.
   Three materials only: Canvas / Panel (flat, --surface) / Overlay (--bg-deep
   + --shadow-2). One button geometry, one input, one pill, one table, one
   popover. Amber = active, one CTA, focus ring, and the hex signature marks.
   ========================================================================== */

/* --- §2 THE panel: every box is this ------------------------------------- */
.cg-section, .dash-card, .kpi, .filters, .filters-row,
.bg-matchup, .qg-matchup, .create-arena-preview, .placeholder,
.cg-shared, .bulk-create-error {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: none;
  padding: var(--sp-4);
}
.kpi { padding: var(--sp-4); }
.dash-kpis { gap: var(--sp-3); }
.dash-grid-2 { gap: var(--sp-4); }

/* One card title recipe (both existing title classes collapse onto it) */
.dash-card-title, .cg-section-title {
  font-family: var(--f-display); font-size: var(--fs-sm); font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--text-muted);
}
.dash-card-head { margin-bottom: var(--sp-3); }

/* Recessed wells inside panels (chip boxes, paste areas, match lists) */
.bg-chips, .bg-paste-area, .cg-tags-input-wrap, .bg-matches-list, .cg-queue {
  background: var(--well);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
}

/* --- §3a ONE button geometry, four skins ---------------------------------- */
.button, .cg-btn, .cg-icon-btn, .create-arena-submit, .bulk-create-submit,
.win-btn, .page-btn, .sidebar-logout, .modal-box button {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-1);
  min-height: 34px; padding: 0 var(--sp-3);
  font-family: var(--f-display); font-size: var(--fs-sm); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.06em; line-height: 1;
  border-radius: var(--r-sm); cursor: pointer; white-space: nowrap;
  transition: background var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              color var(--dur-fast) var(--ease-out);
}
:is(.button, .cg-btn, .cg-icon-btn, .create-arena-submit, .win-btn, .page-btn):focus-visible {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber);
}
:is(.button, .cg-btn, .create-arena-submit):active { transform: translateY(1px); }
/* PRIMARY: solid amber -- the one strong CTA per view */
.primary, .button.primary, a.button.primary {
  background: var(--accent-fill); border: 1px solid var(--accent-fill); color: var(--on-accent);
}
.primary:hover, .button.primary:hover, a.button.primary:hover {
  background: var(--accent-fill-hot); border-color: var(--accent-fill-hot); color: var(--on-accent);
}
/* GREEN GO: create/apply confirm actions */
.cg-btn-primary {
  background: var(--bg-soft-green); border: 1px solid var(--line-green); color: var(--link);
}
.cg-btn-primary:hover { background: rgba(154, 255, 123, 0.2); border-color: var(--link); color: var(--link-soft); }
/* DANGER: red ghost, destructive only */
.cg-btn-danger { background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.5); color: var(--loss); }
.cg-btn-danger:hover { background: rgba(255, 128, 128, 0.22); border-color: var(--loss); color: #fff; }

/* --- §3b ONE input pattern ------------------------------------------------ */
.filter-input, .cg-tags-input-wrap, .bg-paste-area,
.content input[type="text"], .content input[type="email"],
.content input[type="number"], .content input[type="search"],
.content input[type="datetime-local"],
.content textarea, .content select {
  font-family: var(--f-body); font-size: var(--fs-base); color: var(--text);
  background: var(--well); border: 1px solid var(--line-strong);
  border-radius: var(--r-sm); padding: 8px 12px; min-height: 38px;
  transition: border-color var(--dur-fast) var(--ease-out),
              box-shadow var(--dur-fast) var(--ease-out);
}
:is(.filter-input, .content input[type="text"], .content input[type="email"],
    .content input[type="number"], .content input[type="search"],
    .content input[type="datetime-local"],
    .content textarea, .content select):focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber);
}
.content ::placeholder { color: var(--text-dim); }
.content select, select.filter-input {
  appearance: none; -webkit-appearance: none; padding-right: 30px;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='10' height='6'><path d='M1 1l4 4 4-4' fill='none' stroke='%23ffd055' stroke-width='1.5'/></svg>");
  background-repeat: no-repeat; background-position: right 12px center;
}
/* chip-input: the WRAP is the field; the inner input is bare */
.cg-tags-input-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.cg-tags-input-wrap .cg-tags-input, .cg-tags-input-wrap input {
  background: transparent; border: none; box-shadow: none; min-height: 24px; padding: 2px 4px; flex: 1;
}
.cg-tags-input-wrap:focus-within { border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber); }
.bg-step-val:focus { box-shadow: 0 0 0 3px var(--bg-soft-amber); }

/* --- §3c ONE pill --------------------------------------------------------- */
/* Status/type badges: uppercase display micro-type */
.badge, .game-type-badge, .game-status-badge, .account-type-badge, .sai-key-badge {
  display: inline-flex; align-items: center; gap: var(--sp-1);
  padding: 3px 10px; border-radius: var(--r-pill);
  font-family: var(--f-display); font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.08em;
}
/* Entity chips (players, tags): body type, pill geometry */
.bg-chip, .cg-chip, .cg-tag-chip, .player-chip, .ms-chip, .game-tag {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 3px 10px; border-radius: var(--r-pill);
  background: var(--fill-well); border: 1px solid var(--line-strong);
  color: var(--text); font-family: var(--f-body); font-size: 13px;
}
.bg-chip { padding-right: 4px; }

/* --- §4a ONE table --------------------------------------------------------- */
.data-table thead th {
  padding: var(--sp-3) var(--sp-4);
  background: var(--fill-head);
  border-bottom: 1px solid var(--line-strong);
  color: var(--text-dim);
  font-family: var(--f-display); font-size: var(--fs-sm); font-weight: 700;
  text-align: left; text-transform: uppercase; letter-spacing: 0.12em;
}
.data-table tbody td { padding: 11px var(--sp-4); border-bottom: 1px solid var(--line); vertical-align: middle; }
.data-table tbody tr:hover { background: var(--fill-hover); }
.table-scroll { overflow-x: auto; max-width: 100%; }
/* Ten rows tall, the rest reachable by scrolling -- the height is stated in ROWS so
   it stays ten rows after any type change. Only applied when there is more than that
   to show, so a short table keeps its natural height and grows no scrollbar. The
   header sticks, or scrolling past row ten leaves the columns unnamed; its fill has
   to be opaque (the surface colour, not the translucent wash) for the rows not to
   read through it. */
.table-scroll-y { --rows: 10; --row-h: 41px; max-height: calc(var(--row-h) * var(--rows) + 40px); overflow-y: auto; }
.table-scroll-y .data-table thead th { position: sticky; top: 0; z-index: 2; background: var(--surface); }
.table-caption { color: var(--text-dim); font-size: 12px; margin: 0 0 8px; }
/* bulk-create result table joins the family */
.bulk-create-table { width: 100%; border-collapse: collapse; font-family: var(--f-body); font-size: var(--fs-base); }
.bulk-create-table thead th {
  padding: var(--sp-3) var(--sp-4); background: var(--fill-head);
  border-bottom: 1px solid var(--line-strong); color: var(--text-dim);
  font-family: var(--f-display); font-size: var(--fs-sm); font-weight: 700;
  text-align: left; text-transform: uppercase; letter-spacing: 0.12em;
}
.bulk-create-table td { padding: 11px var(--sp-4); border-bottom: 1px solid var(--line); color: var(--text); }

/* --- §4b ONE overlay -------------------------------------------------------- */
/* Popovers: dropdowns, autocomplete, suggests, search lists */
.type-dropdown, .export-menu, .ac-box, .ms-combo-list, .ms-suggest, .player-search-list {
  background: var(--bg-deep);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-sm);
  box-shadow: var(--shadow-2);
  padding: var(--sp-1);
  z-index: var(--z-dropdown);
}
.ac-item, .ms-suggest-item, .type-dropdown button, .export-option {
  padding: 7px 11px; border-radius: var(--r-xs); color: var(--text); cursor: pointer;
}
.ac-item:hover, .ac-item.ac-active, .ms-suggest-item:hover,
.type-dropdown button:hover, .export-option:hover {
  background: var(--bg-soft-amber); color: var(--accent);
}
/* Modals: one sheet recipe for both systems */
.modal-overlay, .cg-confirm-modal {
  background: rgba(6, 10, 12, 0.66);
  -webkit-backdrop-filter: blur(2px); backdrop-filter: blur(2px);
  z-index: var(--z-modal);
}
.modal-box, .cg-confirm-box, .bg-modal {
  background: var(--bg-deep);
  background-image: none;
  -webkit-backdrop-filter: none; backdrop-filter: none;
  border: 1px solid var(--line-strong);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-2);
}
.bg-modal { animation: bg-modal-in 0.2s var(--ease-out); }
.bg-modal-head, .bg-modal-foot { background: transparent; }
.cg-toast { z-index: var(--z-toast); box-shadow: var(--shadow-1); }
.error-panel { z-index: var(--z-error); background: var(--bg-deep); border: 1px solid var(--line-strong); box-shadow: var(--shadow-2); border-radius: var(--r-md); }

/* --- §5.2 Repaint the legacy purple islands ------------------------------- */
.placeholder h1 { color: var(--text); font-family: var(--f-display); }
.placeholder-text { color: var(--text-dim); }
.player-chip { background: var(--fill-well); border-color: var(--line-strong); color: var(--text); }
.sai-owner-results, .sai-owner-chosen, .sai-key-reveal {
  background: var(--well); border: 1px solid var(--line-strong); border-radius: var(--r-sm); color: var(--text);
}
.sai-owner-clear { color: var(--text-dim); }
.sai-owner-clear:hover { color: var(--loss); }
.sai-key-active { color: var(--win); background: var(--win-soft); border: 1px solid var(--line-green); }
.sai-key-revoked { color: var(--loss); background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.5); }

/* --- §6 The signature: the amber hex mark ---------------------------------- */
/* One tiny hex (same geometry as the canvas texture) marks "command group" +
   "you are here" + "status". Nothing else in the system glows or decorates. */
.hex-mark, .nav-links .nav-sec::before, .nav-links li a.active::before {
  content: ""; display: inline-block; width: 7px; height: 8px; flex: 0 0 auto;
  background: var(--accent-fill-hot);
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
}
.nav-links .nav-sec { display: flex; align-items: center; gap: 7px; }
.nav-links .nav-sec::before { background: var(--text-faint); width: 6px; height: 7px; }
.nav-links li a { display: flex; align-items: center; gap: 8px; }
.nav-links li a::before {
  content: ""; display: inline-block; width: 7px; height: 8px; flex: 0 0 auto;
  background: transparent;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  transition: background var(--dur-fast) var(--ease-out);
}
.nav-links li a.active::before { background: var(--accent-fill-hot); }
/* KPI status dot -> hex */
.kpi::after {
  border-radius: 0;
  clip-path: polygon(50% 0, 100% 25%, 100% 75%, 50% 100%, 0 75%, 0 25%);
  width: 8px; height: 9px;
}

@media (prefers-reduced-motion: reduce) {
  .bg-modal { animation: none; }
}

/* ==========================================================================
   TOURNAMENTS -- structural cohesion pass (task 1401).
   The rollout layer above color-mapped the bespoke .tournament-*/.roster-*/.round-*
   modules, but their STRUCTURE still read as a different app than Players/Games/
   Create. This block maps them onto the site vocabulary: uppercase page-header,
   uppercase eyebrow section labels, bordered control panels, themed file input,
   and the amber/green/red button semantics. Source order wins over style.css and
   the earlier rollout rules.
   ========================================================================== */

/* Detail header reads like a real page header (uppercase display title, status
   badge inline, meta on its own line, actions pinned right). */
.tournament-header {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  margin-bottom: 22px; padding-bottom: 0;
  /* No divider here -- the .tabs bar below is the single separator, exactly like
     .page-header (which has no bottom border). Overrides the rollout rule above. */
  border-bottom: none;
}
.tournament-header h1 {
  font-family: var(--f-display); font-size: 30px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.01em; color: var(--text); margin: 0;
}
.tournament-header .meta { flex-basis: 100%; color: var(--text-dim); font-size: 13px; margin: 0; }
.tournament-header .actions { margin-left: auto; display: flex; gap: 8px; }

/* Every tournament section heading -> uppercase eyebrow (same treatment as
   .cg-section-title / .filter-label). The site never uses plain bold white headings. */
.roster-add h3, .roster-bulk h3, .roster-candidates h3, .roster-participants h3,
.round-header h3, .round-scores h4, .manual-match h4, .proposed-pairings h4 {
  font-family: var(--f-display); font-size: 13px; font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.14em; color: var(--text-dim);
  margin: 0 0 10px;
}
.round-header h3 { font-size: 15px; letter-spacing: 0.1em; color: var(--text); }

/* Group the roster add + import controls into bordered panels, like .filters/.cg-section. */
.roster-add, .roster-bulk {
  background: var(--bg-panel); border: 1px solid var(--line); border-radius: var(--r-md);
  padding: 16px 18px; margin-bottom: 16px;
}
.roster-bulk form { display: flex; flex-direction: column; gap: 10px; align-items: flex-start; }
.roster-add input[type="text"], .roster-bulk textarea { width: 100%; box-sizing: border-box; padding: 8px 12px; }
.roster-candidates, .roster-participants { margin-bottom: 22px; }

/* Theme the native file input to match the bulk-create file button. */
.roster-bulk input[type="file"] { color: var(--text-muted); font-size: 13px; }
.roster-bulk input[type="file"]::file-selector-button {
  background: var(--bg-soft-amber); border: 1px solid var(--line-amber); border-radius: var(--r-sm);
  color: var(--accent); font-family: var(--f-display); font-weight: 700; letter-spacing: 0.04em;
  padding: 7px 12px; margin-right: 10px; cursor: pointer;
  transition: background var(--dur) var(--ease-out);
}
.roster-bulk input[type="file"]::file-selector-button:hover { background: rgba(255, 208, 85, 0.18); }

/* Button semantics inside the tabbed detail. #tab-content button (id+element)
   out-specifies a bare .cg-btn-danger/.cg-btn-primary, so scope the variants by id. */
#tab-content button.cg-btn-primary,
.tournament-header .actions a.cg-btn-primary {
  background: var(--bg-soft-green); border: 1px solid var(--line-green); color: var(--link-soft);
}
#tab-content button.cg-btn-primary:hover { background: rgba(154, 255, 123, 0.18); color: #fff; }
#tab-content button.cg-btn-danger {
  background: var(--loss-soft); border: 1px solid rgba(255, 128, 128, 0.5); color: var(--loss);
}
#tab-content button.cg-btn-danger:hover { background: rgba(255, 128, 128, 0.22); color: #fff; }

/* Roster/round tables get the standard scroll guard so wide rows never bleed the page. */
#tab-content .data-table { width: 100%; }

/* ==========================================================================
   DASHBOARDS -- overhaul pass (task 1401 follow-up).
   Appended last so these rules win over the earlier dashboard block. Tokens
   only. Roboto Condensed uppercase for every label, mono for every number,
   amber kept for active state / the one CTA / focus / the hex marks and the
   two data marks the spec reserves it for (share bars, tile accents).
   ========================================================================== */

/* --- Page head: freshness + window picker + export, right of the title ----- */
.dash-header-tools {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-3); margin-left: auto;
}
.dash-fresh {
  display: block; margin-bottom: var(--sp-2);
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-faint);
}
.dash-header-tools .dash-fresh { margin-bottom: 0; }
.dash-fresh-mark { color: var(--text-dim); }
.dash-fresh-warn { color: var(--loss); }

/* --- Analytics pages: mirror stamp, lede, server-rendered figures ---------- */
/* The analytics pages read the tournaments-hq mirror, so their age is a property
   of the data and belongs in the header next to the render time. Bordered rather
   than coloured while healthy: it is a standing fact, not an alert, until it goes
   stale -- at which point the border and the ink both carry the warning. */
.dash-mirror {
  padding: 2px var(--sp-2);
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-dim);
  border: 1px solid var(--line-strong); border-radius: var(--r-sm);
}
.dash-mirror-stale { color: var(--loss); border-color: var(--loss); }
/* The one paragraph that states a page's population before any number appears.
   Measure-capped: it is prose and has to read like prose. */
.dash-lede {
  max-width: 96ch; margin: 0 0 var(--sp-4);
  color: var(--text-dim); font-size: var(--fs-base); line-height: 1.6;
}
.dash-lede strong { color: var(--text); }
/* Figures built as inline SVG in charts.py (forest, funnel, scatter, ranked
   bars). They carry their own viewBox, so the box only has to let a wide figure
   scroll instead of squeezing the plot area. */
.dash-figure { overflow-x: auto; }
.dash-figure .dash-svg {
  display: block; width: 100%; height: auto;
  min-width: 520px;
  /* The figure's type is authored at 11px against a 720-wide viewBox. Letting it
     stretch into a full-width card would scale every label up with it, so the
     figure stops at its natural width and only ever scales down. */
  max-width: 720px;
}
/* The honest line when a source is absent -- body type, because it is a sentence. */
.dash-note-bad { margin: 0; max-width: 78ch; color: var(--loss); font-size: var(--fs-base); line-height: 1.55; }

/* Custom range inside the picker. Date inputs are outside the unified input
   selector list, so they are dressed here to match .win-btn's geometry. */
/* The two controls read as two controls: the period is a row of plain buttons, the
   bucket size a bordered segmented toggle. Captions name each one, but the shapes
   are what carry the distinction -- the previous version relied on captions alone
   and put the same label in both groups. */
.win-cap {
  font-family: var(--f-mono); font-size: var(--fs-2xs); letter-spacing: .04em;
  color: var(--text-faint); text-transform: uppercase;
}
.win-toggle {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); overflow: hidden;
}
.win-seg {
  display: inline-flex; align-items: center; min-height: 34px; padding: 0 var(--sp-3);
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-dim);
  text-decoration: none; background: transparent;
}
.win-seg + .win-seg { border-left: 1px solid var(--line-strong); }
.win-seg:hover:not(.disabled) { background: var(--surface-2); color: var(--text); }
/* Same active treatment as a period button, so "selected" reads identically
   across the two controls. */
.win-seg.active { background: var(--bg-soft-amber); color: var(--accent); }
/* --- On-demand detail (the hint bubble) ----------------------------------- */
/* The marker is small enough to sit inside a heading and big enough to aim at. The
   affordance has to be VISIBLE: a tooltip nobody knows about is deleted text. */
.hint {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; margin-left: 6px; vertical-align: middle;
  font-family: var(--f-mono); font-size: 9px; font-style: italic; font-weight: 400;
  line-height: 1; letter-spacing: 0; text-transform: none;
  color: var(--text-faint); background: transparent;
  border: 1px solid var(--line-strong); border-radius: 50%;
  cursor: help; flex: none;
}
.hint:hover, .hint:focus-visible { color: var(--accent); border-color: var(--accent); outline: none; }

/* The bubble is ONE element on <body>, not a pseudo-element on the marker. A KPI
   tile sets overflow:hidden (it clips its sparkline to the rounded corner), so an
   absolutely-positioned bubble inside one is cut off at the tile edge -- and it
   inherits the tile's uppercase display font on the way. Living on <body> it is
   clipped by nothing, inherits nothing, and can be clamped to the viewport. */
.hint-bubble {
  position: fixed; top: 0; left: 0; z-index: 90;
  max-width: 46ch; padding: var(--sp-2) var(--sp-3);
  font-family: var(--f-body); font-size: var(--fs-sm); font-style: normal; font-weight: 400;
  line-height: 1.5; letter-spacing: 0; text-transform: none; text-align: left; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.45);
  opacity: 0; visibility: hidden; transition: opacity .12s ease;
  pointer-events: none;
}
.hint-bubble.on { opacity: 1; visibility: visible; }
/* The footer's reference to the marker, so "hover the i" points at something the
   reader can recognise. */
.hint-inline {
  display: inline-flex; align-items: center; justify-content: center;
  width: 13px; height: 13px; vertical-align: middle;
  font-family: var(--f-mono); font-size: 9px; font-style: italic;
  color: var(--text-faint); border: 1px solid var(--line-strong); border-radius: 50%;
}
/* The one line that states a page's scope, with the reasoning on hover. */
.dash-scope {
  margin: 0 0 var(--sp-4); color: var(--text-dim);
  font-size: var(--fs-base); line-height: 1.5;
}
.dash-scope strong { color: var(--text); }

/* --- The shared modal: question, optional field, optional preview --------- */
.modal-question { color: var(--text); font-size: var(--fs-base); line-height: 1.5; }
.modal-field {
  display: block; width: 100%; margin-top: var(--sp-3); min-height: 34px; padding: 0 var(--sp-2);
  font-family: var(--f-mono); font-size: var(--fs-sm); color: var(--text);
  background: var(--well); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
}
.modal-field:focus { border-color: var(--accent); outline: none; }
/* The preview of what is about to happen. Scrolls, because a selection can be long
   and the operator still has to be able to reach the buttons. */
.modal-preview {
  margin-top: var(--sp-3); max-height: 46vh; overflow-y: auto;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.modal-preview table { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); }
.modal-preview th, .modal-preview td {
  padding: 6px var(--sp-2); text-align: left; border-bottom: 1px solid var(--line);
}
.modal-preview th {
  position: sticky; top: 0; background: var(--surface-2);
  font-family: var(--f-mono); font-size: var(--fs-2xs); letter-spacing: .04em;
  text-transform: uppercase; color: var(--text-faint);
}
.modal-preview .win { color: var(--win); }
.modal-preview .lose { color: var(--loss); }
.modal-preview .blocked { color: var(--text-faint); }
.modal-preview .why { color: var(--text-dim); }
.modal-ok-danger { background: rgba(255, 128, 128, 0.18); border-color: var(--loss); color: var(--loss); }
.modal-ok-danger:hover { background: rgba(255, 128, 128, 0.28); color: #fff; }

/* --- Bulk selection on the games list ------------------------------------- */
/* The narrow first column is a hit target, so the box gets the whole cell. */
.col-pick-head, .col-pick { width: 34px; text-align: center; }
.col-pick input, .col-pick-head input { cursor: pointer; }
/* The action bar only exists while something is selected, and it sits directly above
   the table so the count and the rows it refers to are in one glance. */
/* An author display rule beats the browser's [hidden] { display: none }, so the
   attribute has to be honoured explicitly or the bar shows up empty. */
.bulk-bar[hidden], .bulk-result[hidden] { display: none; }
.bulk-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2) var(--sp-3);
  margin-bottom: var(--sp-3); padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
}
.bulk-count { font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-dim); }
.bulk-count strong { color: var(--accent); font-size: var(--fs-base); }
.bulk-group { display: inline-flex; align-items: center; gap: var(--sp-1); }
.bulk-clear {
  margin-left: auto; padding: 0 var(--sp-2); min-height: 30px;
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-faint);
  background: transparent; border: 1px solid transparent; border-radius: var(--r-sm); cursor: pointer;
}
.bulk-clear:hover { color: var(--text); border-color: var(--line-strong); }
/* The outcome of the last bulk action. It stays on screen after the table refreshes,
   because it is the only record of what just happened -- especially the part that
   did NOT apply. */
.bulk-result {
  margin-bottom: var(--sp-3); padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-base); color: var(--text-dim);
  background: var(--bg-soft-amber); border-left: 2px solid var(--accent); border-radius: var(--r-sm);
}
.bulk-result strong { color: var(--text); }
.bulk-result-bad { background: transparent; border-left-color: var(--loss); color: var(--loss); }

/* A control that belongs to one chart rather than to the page sits on the card,
   above the plot. The metric chips wrap: eleven of them will not fit one line, and
   truncating a catalogue is how a metric becomes invisible. */
.chart-tools { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-bottom: var(--sp-3); }
.metric-picker { display: flex; flex-wrap: wrap; gap: var(--sp-1) var(--sp-2); }
.metric-chip {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 28px; padding: 0 var(--sp-2);
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text-dim);
  text-decoration: none;
  border: 1px solid var(--line); border-radius: var(--r-sm);
}
.metric-chip:hover { color: var(--text); border-color: var(--line-strong); }
/* Selected carries the series colour as a dot, not as the chip's own ink: the dot is
   the same hue the line is drawn in, which is what ties chip to curve. */
.metric-chip.on { color: var(--text); border-color: var(--line-strong); background: var(--surface-2); }
.metric-chip.locked { cursor: default; }
.metric-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.metric-chip:not(.on) .metric-dot { opacity: .35; }
.window-custom { display: inline-flex; align-items: center; gap: var(--sp-1); }
.window-custom .win-date {
  min-height: 34px; padding: 0 var(--sp-2);
  font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--text);
  background: var(--well); border: 1px solid var(--line-strong); border-radius: var(--r-sm);
}
.window-custom .win-date:focus {
  outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--bg-soft-amber);
}
.window-custom .win-date::-webkit-calendar-picker-indicator { filter: invert(0.75); cursor: pointer; }
/* "asked for 20 days, an hourly axis carries 14" -- said next to the boxes that made
   the request, since that is where the disagreement would otherwise sit unexplained. */
.win-clamped { font-family: var(--f-mono); font-size: var(--fs-xs); color: var(--warn); }

/* --- Tiles ---------------------------------------------------------------- */
.kpi-note {
  margin-top: var(--sp-1);
  font-family: var(--f-mono); font-size: var(--fs-xs); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-faint);
}
.kpi-delta.flat { color: var(--text-dim); }
/* The class charts.sparkline_svg has always emitted, finally styled. */
.sparkline { display: block; width: 100%; margin-top: var(--sp-2); opacity: 0.9; }

/* --- Charts --------------------------------------------------------------- */
/* dashboards.js positions both of these absolutely: the empty-data overlay against
   the box, the partial-bucket note against uPlot's own .u-over. */
.chart-box { position: relative; }
.chart-empty {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center; text-align: center;
  font-family: var(--f-mono); font-size: var(--fs-sm); letter-spacing: 0.04em; color: var(--text-dim);
}
.chart-partial-note {
  position: absolute; top: var(--sp-1); right: var(--sp-2); pointer-events: none;
  font-family: var(--f-mono); font-size: var(--fs-2xs); letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--text-faint);
}

/* --- Health zones --------------------------------------------------------- */
.dash-zone { margin-bottom: var(--sp-5); }
.dash-zone-head {
  display: flex; align-items: baseline; gap: var(--sp-3);
  padding-bottom: var(--sp-2); margin-bottom: var(--sp-3); border-bottom: 1px solid var(--line);
  font-family: var(--f-display); font-size: var(--fs-sm); font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-dim);
}
/* Right-pushed diagnostic link in a zone head (the metrics-backend probe). */
.dash-zone-link { margin-left: auto; font-family: var(--f-mono); font-size: var(--fs-xs);
  letter-spacing: 0; text-transform: none; color: var(--link); }

/* --- Numeric + share columns in dashboard tables -------------------------- */
.data-table td.col-num {
  font-family: var(--f-mono); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.data-table td.col-share { width: 30%; min-width: 90px; }
/* Inline proportion bar. The soft amber wash alone is too faint on --surface, so
   it carries the matching hairline; the width is the only inline style. */
.share-bar {
  display: block; height: 8px; min-width: 2px;
  background: var(--bg-soft-amber); border: 1px solid var(--line-amber); border-radius: var(--r-xs);
}

@media (prefers-reduced-motion: reduce) {
}

/* ==========================================================================
   §9 Narrow screens: phone and iPad
   --------------------------------------------------------------------------
   Until now the shell had no responsive rules at all -- the only media queries in
   the sheet were for the standalone Map Stats site and the Create Game builder's
   grids. On a 390px phone that meant a 220px rail eating 56% of the viewport with
   the content column starting off-screen.

   The layout answers in two steps, and the numbers are the devices, not round ones:
     - 900px and below   -- the rail becomes a drawer. This covers iPad mini (768)
                            and iPad Air (834) in PORTRAIT as well as every phone;
                            an iPad in landscape (1024, 1194) keeps the desktop rail,
                            which is the right call: there is room for it.
     - 620px and below   -- phone. One column everywhere, controls at touch size.

   Tables are deliberately NOT restacked into cards. A games row is a comparison --
   twelve columns read against each other -- and a card per row destroys exactly
   that. They scroll sideways inside their own container instead, with the id column
   pinned so a horizontally-scrolled row still says which game it is.
   ========================================================================== */

/* Chrome that only exists on narrow screens. Hidden here rather than shown there, so
   a desktop pays nothing for it. */
.mobile-bar, .nav-toggle, .nav-scrim { display: none; }

@media (max-width: 900px) {
  /* The bar is fixed, so the page needs room for it, and iOS needs the notch inset
     added rather than assumed. */
  body { padding-top: calc(52px + env(safe-area-inset-top, 0px)); }
  .mobile-bar {
    position: fixed;
    top: 0; left: 0; right: 0;
    z-index: var(--z-dropdown);
    display: flex;
    align-items: center;
    gap: 12px;
    height: calc(52px + env(safe-area-inset-top, 0px));
    padding: env(safe-area-inset-top, 0px) 14px 0;
    background: var(--nav-bg);
    border-bottom: 1px solid var(--line);
  }
  .mobile-bar-logo { height: 22px; width: auto; display: block; }
  .mobile-bar-section {
    font-family: var(--f-display); font-size: var(--fs-xs); font-weight: 700;
    letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  /* 44px square: the smallest target a thumb hits reliably. */
  .nav-toggle {
    display: inline-flex; align-items: center; justify-content: center;
    flex: none; width: 44px; height: 44px; margin-left: -10px;
    background: none; border: 0; cursor: pointer; color: var(--text);
  }
  .nav-toggle-bars, .nav-toggle-bars::before, .nav-toggle-bars::after {
    content: ""; display: block; width: 20px; height: 2px; border-radius: 2px;
    background: currentColor; transition: transform var(--dur) var(--ease-out);
  }
  .nav-toggle-bars { position: relative; }
  .nav-toggle-bars::before { position: absolute; top: -6px; }
  .nav-toggle-bars::after  { position: absolute; top: 6px; }
  /* Open state folds the bars into a cross, so the button says what it will do now. */
  body.nav-open .nav-toggle-bars { background: transparent; }
  body.nav-open .nav-toggle-bars::before { transform: translateY(6px) rotate(45deg); }
  body.nav-open .nav-toggle-bars::after  { transform: translateY(-6px) rotate(-45deg); }

  /* The drawer. dvh, not vh: on iOS 100vh is the viewport WITHOUT the browser bars, so
     a 100vh drawer is taller than the screen and its footer sits under the toolbar. */
  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    z-index: var(--z-modal);
    width: min(280px, 84vw);
    height: 100vh;
    height: 100dvh;
    transform: translateX(-100%);
    transition: transform var(--dur) var(--ease-out);
    box-shadow: var(--shadow-2);
  }
  body.nav-open .sidebar { transform: none; }
  /* Opaque while it is an overlay: the desktop rail's translucent gradient sits over
     the page canvas by design, but a drawer over live content has to hide it. */
  .sidebar { background: var(--nav-bg), var(--bg); }
  .nav-scrim {
    display: block;
    position: fixed; inset: 0;
    z-index: calc(var(--z-modal) - 1);
    /* Dimmed hard and blurred: at 0.55 with no blur the page behind stayed legible and
       competed with the drawer for attention, which is the one thing an overlay must
       not allow. The blur is progressive-enhancement -- without support the dim alone
       still does the job, which is why it is 0.72 rather than something the blur has to
       rescue. */
    background: rgba(6, 10, 12, 0.72);
    backdrop-filter: blur(8px) saturate(0.9);
    -webkit-backdrop-filter: blur(8px) saturate(0.9);
  }
  .nav-scrim[hidden] { display: none; }
  /* Momentum scrolling, or the drawer's link list feels broken next to every other
     scrollable thing on the device. */
  .nav-links { -webkit-overflow-scrolling: touch; }

  .content { padding: 18px 14px 40px; min-height: 0; margin-left: 0; }
  /* A wide table or chart scrolls inside its own box; the PAGE never scrolls sideways. */
  body { overflow-x: hidden; }
  .table-scroll { -webkit-overflow-scrolling: touch; }

  /* The window picker is an inline-flex row with an intrinsic width of 608px (six
     presets, two date boxes, Apply). Below ~868px viewport its Apply button was pushed
     off-canvas -- and, until the overflow fix above, off-canvas behind an invisible
     scroll. Both iPad portraits land in that band, so it wraps. */
  .window-picker { flex-wrap: wrap; row-gap: 8px; }
  .window-custom { flex-wrap: wrap; }

  /* 16px is Safari's zoom threshold: below it, focusing any field zooms the whole
     layout viewport and the reader has to pinch back out. Both iPad portraits and every
     phone are affected, so the floor starts here rather than at the phone breakpoint. */
  .content input, .content select, .content textarea, .filter-input { font-size: 16px; }

  /* Dashboards: one column. Two 320px cards do not fit 768px minus the padding, and
     auto-fit would otherwise give two cards too narrow to read an axis in. */
  .dash-grid-2 { grid-template-columns: 1fr; }
  .dash-kpis { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

@media (max-width: 620px) {
  .page-header { flex-wrap: wrap; gap: 10px; }
  .page-header h1 { font-size: 18px; }
  /* The tools row (window picker, export, freshness) wraps instead of squeezing its
     children to unusable widths. */
  .dash-header-tools { flex-wrap: wrap; width: 100%; row-gap: 8px; }

  /* Filter console: one control per row, each full width. Side by side, four token
     inputs on a 390px screen are 80px each, which is not an input. */
  .filters, .filters-row { flex-direction: column; align-items: stretch; }
  .filters .filter-group, .filters-row .filter-group { width: 100%; min-width: 0; }
  .filter-input, .cg-tags-input-wrap { width: 100%; box-sizing: border-box; }
  /* The token fields carry `flex: 1 1 260px`, which is a WIDTH basis while the row is
     a row -- and becomes a 260px HEIGHT the moment the container turns into a column.
     That is what made MAPS and PLAYERS render as empty 260px boxes running off the
     right edge. Basis off, width on. */
  .games-filters .cg-tags-field { flex: 0 0 auto; width: 100%; }
  .games-filters .cg-tags-field .cg-tags-input-wrap { align-self: auto; }
  /* Cards must not push the page sideways; anything genuinely wide scrolls in its own
     box (.table-scroll), not by dragging the whole layout. */
  .filters, .filters-row, .dash-card, .cg-section { max-width: 100%; box-sizing: border-box; }

  /* Touch sizes for the pill rows (status, type, player type, window presets). */
  .radio-label, .win-btn, .metric-chip, .page-btn { min-height: 36px; }
  .cg-btn, .button, .modal-box button { min-height: 40px; }

  /* The id column stays put while the rest of the row scrolls, so a row that has been
     dragged sideways still says which game or player it is. */
  .table-scroll .data-table td.col-id,
  .table-scroll .data-table th.col-id {
    position: sticky; left: 0; z-index: 1; background: var(--surface);
  }
  .table-scroll-y .data-table thead th.col-id { z-index: 3; }

  .dash-kpis { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); gap: 10px; }
  .kpi { padding: 12px; }
  .kpi-val { font-size: 22px; }

  /* A modal on a phone is a sheet: full width, and its buttons stay reachable with
     the keyboard open. */
  .modal-box, .bg-modal { width: calc(100vw - 24px); max-width: none; max-height: 88dvh; overflow-y: auto; }
  .modal-actions, .bg-modal-foot { flex-wrap: wrap; }

  /* The bulk action bar wraps rather than scrolling out of reach. */
  .bulk-bar { flex-wrap: wrap; row-gap: 8px; }

  .dash-card { padding: 10px 10px 6px; }
  .chart-box { min-height: 150px; }
}

/* A phone in landscape is ~390px tall: the drawer must scroll as a whole, not just its
   link list, or the footer with LOGOUT is unreachable again. */
@media (max-width: 900px) and (max-height: 480px) {
  .sidebar { overflow-y: auto; }
  .nav-links { overflow-y: visible; }
}

/* --------------------------------------------------------------------------
   Touch pointers, at any width. Keyed on the POINTER, not on the viewport: an
   iPad in landscape is 1024px wide and still has no cursor, so a width-based rule
   would leave exactly that device with 13px checkboxes and hover-only affordances.
   -------------------------------------------------------------------------- */
@media (hover: none) and (pointer: coarse) {
  /* A 13x13 checkbox next to a link is a coin flip on which one the finger gets.
     Bigger box, and a padded cell so the target extends past the ink. */
  .pick-one, .pick-all, .content input[type="checkbox"] { width: 20px; height: 20px; }
  .data-table td.col-pick, .data-table th.col-pick { padding-left: 14px; padding-right: 14px; }

  /* Row actions (delete, download, reveal) were 14-18px of glyph. */
  .row-action, .cg-icon-btn, .export-btn, .ms-replay-btn { min-width: 36px; min-height: 36px; }

  /* The hint marker is the size it looks, but its TARGET is not: 14px of glyph asks
     for precision a finger does not have. The bubble opens on tap (see base.html),
     so the target has to be worth aiming at. */
  .hint { position: relative; }
  .hint::after {
    content: ""; position: absolute; top: 50%; left: 50%;
    width: 40px; height: 40px; transform: translate(-50%, -50%);
  }

  /* Row hover is a cursor idiom; on touch it fires on tap and lingers, which reads as
     a stuck selection. */
  .data-table tbody tr:hover { background: none; }
}
