/* ─── base.css — shared foundation for every page ──────────────────
   Loaded FIRST by every entry point (app, setup wizard, login,
   public-share) before that page's own stylesheet. Owns the pieces that
   were previously copy-pasted (and had started to drift) across
   style.css / setup.css / login.css:

     - the CSS reset
     - the full theme-token system (dark / light / oled)
     - the theme-picker segmented row
     - the in-app Total Battle login bridge (inline + modal)

   Canonical source is the main app (style.css); where the standalone
   pages had drifted, their values were converged here. Page stylesheets
   load AFTER this file, so anything page-specific still overrides. */

/* ─── Reset ─── */
*,
*::before,
*::after {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}

/* ─── Reduced motion ──────────────────────────────────────────────
   One switch for the whole site rather than a rule per animation: a new
   pulse or slide added later is covered without anyone remembering to. The
   0.01ms (not 0 / none) keeps animationend-style bookkeeping firing, and one
   iteration means an infinite pulse settles on its end frame. Anything whose
   end frame is wrong as a resting state (the indeterminate upload bar) gets
   its own override next to its animation. JS motion — Chart.js entry
   animations, smooth scrolling — checks the same media query itself. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ─── Keyboard focus + screen-reader utilities ───────────────────
   A visible ring wherever focus came from the keyboard. Elements with a
   designed focus state of their own (.input) override it by specificity. */
:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}

/* Text that a screen reader announces but the eye doesn't need — the words
   behind a colour or an icon. */
.visually-hidden {
  position: absolute !important;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* ─── Theme tokens ──────────────────────────────────────────────
   Colors are declared once on :root (= dark, the original palette)
   and overridden in the [data-theme="light"] / [data-theme="oled"]
   blocks below. Everything else reads var(--token) so a single
   data-theme flip on <html> retones the entire site. */
:root {
  color-scheme: dark;

  /* surface */
  --bg-primary: #0b0e14;
  --bg-card: #141920;
  --bg-card-hover: #1a2030;
  --bg-surface: #0f1319;
  --bg-input: #0c1017;
  --bg-elevated: #1e2736;
  --bg-modal-backdrop: rgba(0, 0, 0, 0.65);
  /* Deeper than the modal backdrop — a screenshot lightbox wants the page gone,
   * not just dimmed, so nothing competes with the image being read. */
  --bg-lightbox-backdrop: rgba(0, 0, 0, 0.88);
  --bg-mobile-overlay: rgba(0, 0, 0, 0.5);
  --bg-header-grad: linear-gradient(135deg, #131829 0%, #1e1433 50%, #191225 100%);

  /* border */
  --border: #1e2736;
  --border-light: #2a3545;
  --border-strong: #3a4a5e;
  --row-divider: rgba(30, 39, 54, 0.6);
  --row-divider-mobile: rgba(30, 39, 54, 0.4);

  /* text
     Every text/background pair is held to WCAG AA (4.5:1) by
     tests/config/theme-contrast.test.ts, run on every build. --text-muted sits
     barely below --text-secondary as a result: it has to clear 4.5:1 even on
     the goal-washed leaderboard rows, so muted text leans on size, not on
     being faint. */
  --text-primary: #e6edf6;
  --text-secondary: #8b95a5;
  --text-muted: #84909f;
  --text-on-accent: #ffffff;
  /* Text on a solid --accent-gold fill: white on bright gold is 1.7:1. */
  --text-on-gold: #141920;

  /* Keyboard focus outline. Non-text, so it needs 3:1 against the surface. */
  --focus-ring: #60a5fa;

  /* overlays — light tints used over any surface for hover/zebra */
  --hover-overlay: rgba(255, 255, 255, 0.05);
  --hover-overlay-strong: rgba(255, 255, 255, 0.06);
  --row-hover-overlay: rgba(255, 255, 255, 0.02);
  --row-hover-overlay-strong: rgba(255, 255, 255, 0.03);
  --subtle-bg: rgba(255, 255, 255, 0.04);
  --subtle-bg-strong: rgba(255, 255, 255, 0.06);
  --code-bg: rgba(255, 255, 255, 0.05);
  --zebra-bg: rgba(255, 255, 255, 0.025);
  --details-bg: rgba(0, 0, 0, 0.25);

  /* Leaderboard goal status, washed across a whole row. Deliberately its own
     token set and not --accent-*-bg: those are tuned to sit behind a single
     cell, and at that strength a full-width band on every row of a 25-row
     table is louder than the numbers it is meant to qualify. Most rows are
     below goal on any given day, so `low` is the one that has to stay
     comfortable to read past. */
  --goal-row-ok-bg: rgba(52, 211, 153, 0.10);
  --goal-row-warn-bg: rgba(250, 204, 21, 0.09);
  --goal-row-low-bg: rgba(248, 113, 113, 0.09);

  /* The Points cell sits ON TOP of the row wash, and both are translucent, so
     these stack rather than replace: the cell reads as one clear step up, not
     as a second unrelated colour. Kept as tokens for the same reason as the
     row — so OLED can quieten both halves together, which reusing
     --accent-*-bg-strong could not do. */
  --goal-cell-ok-bg: rgba(52, 211, 153, 0.15);
  --goal-cell-warn-bg: rgba(250, 204, 21, 0.14);
  --goal-cell-low-bg: rgba(248, 113, 113, 0.14);

  /* accent */
  --accent-gold: #f0c040;
  --accent-gold-dim: #6d5a0f;
  --accent-gold-bg: rgba(240, 192, 64, 0.08);
  --accent-gold-bg-strong: rgba(240, 192, 64, 0.18);
  --accent-gold-border: rgba(240, 192, 64, 0.3);
  --accent-gold-active-bg: rgba(240, 192, 64, 0.06);
  --accent-blue: #3b82f6;
  --accent-blue-hover: #60a5fa;
  /* Filled buttons carry white text, and #3b82f6 only manages 3.7:1 under it
     (its lighter hover, 2.5:1). --accent-blue stays the bright one for links
     and borders on dark surfaces; fills use these. Same for red below. */
  --accent-blue-solid: #2563eb;
  --accent-blue-solid-hover: #1d4ed8;
  --accent-red-solid: #dc2626;
  --accent-red-solid-hover: #b91c1c;
  --accent-blue-bg: rgba(59, 130, 246, 0.15);
  --accent-blue-soft: rgba(88, 166, 255, 0.06);
  --accent-blue-soft-strong: rgba(100, 150, 255, 0.12);
  --accent-blue-soft-hover: rgba(100, 150, 255, 0.06);
  --accent-green: #34d399;
  --accent-green-bg: rgba(52, 211, 153, 0.12);
  --accent-green-bg-strong: rgba(74, 222, 128, 0.12);
  --accent-green-soft-text: #bbf7d0;
  --accent-red: #f87171;
  --accent-red-bg: rgba(248, 113, 113, 0.08);
  --accent-red-bg-strong: rgba(248, 113, 113, 0.18);
  --accent-red-border: rgba(248, 113, 113, 0.4);
  --accent-red-border-soft: rgba(248, 113, 113, 0.3);
  --accent-red-soft-text: #fecaca;
  --accent-purple: #c084fc;
  --accent-amber: #f59e0b;
  --accent-amber-bg: rgba(230, 180, 30, 0.18);
  --accent-amber-border: rgba(230, 180, 30, 0.35);
  --accent-amber-text: #f1c044;
  --accent-warn-bg: rgba(250, 204, 21, 0.12);
  --accent-warn-text: #fde68a;

  /* status / errors */
  --status-error-bg: #1a0f0f;
  --status-error-text: #ff6c6c;
  --status-error-row-bg: rgba(230, 74, 74, 0.18);
  --status-error-row-border: rgba(230, 74, 74, 0.35);
  --status-success-border: #2f6f4d;
  --status-success-text: #7ee2a8;
  /* General success/error surface borders (used by setup message cards). */
  --status-error-border: #7d3b3b;
  --status-error-row-border2: #7a3a3a;
  --status-error-row-text: #ff9d9d;
  --transfer-progress-track: #1b2430;
  --transfer-card-danger-border: #5d2d2d;
  --transfer-card-danger-bg: linear-gradient(180deg, #14171f 0%, #131821 100%);

  /* chest type rarities */
  --chest-common-bg: #21262d;
  --chest-common-text: #8b949e;
  --chest-uncommon-bg: #1b3a2a;
  --chest-uncommon-text: #3fb950;
  --chest-rare-bg: #1a2744;
  --chest-rare-text: #58a6ff;
  --chest-epic-bg: #2d1b4e;
  --chest-epic-text: #bc8cff;
  --chest-legendary-bg: #3d2b0f;
  --chest-legendary-text: #f0c040;
  --chest-arena-bg: #3b1d1d;
  --chest-arena-text: #f85149;
  --chest-event-bg: #2d3a1b;
  --chest-event-text: #d29922;
  --chest-unknown-bg: #21262d;
  --chest-unknown-text: #858d98;

  /* type-row dot/bar fills */
  --type-common: #7f8c9b;
  --type-uncommon: #46c27a;
  --type-rare: #58a6ff;
  --type-epic: #d68cff;
  --type-legendary: #ffbe3b;
  --type-arena: #ff6b6b;
  --type-event: #f08c2e;
  --type-unknown: #5f6b7a;
  --type-row-bar-bg: #1c232d;

  /* podium */
  --rank-1: #ffd700;
  --rank-2: #c0c0c0;
  --rank-3: #cd7f32;
  --rank-1-glow: rgba(255, 215, 0, 0.15);
  --rank-2-glow: rgba(192, 192, 192, 0.15);
  --rank-3-glow: rgba(205, 127, 50, 0.15);

  /* chart */
  --chart-grid: rgba(255, 255, 255, 0.04);
  --chart-tick: #8b949e;
  --chart-tooltip-bg: #1e2736;
  --chart-tooltip-text: #e6edf6;
  --chart-tooltip-border: #2a3545;
  --chart-bar-blue: rgba(59, 130, 246, 0.7);
  --chart-bar-blue-hover: rgba(96, 165, 250, 0.9);
  --chart-bar-gold: rgba(240, 192, 64, 0.7);
  --chart-bar-gold-hover: rgba(240, 192, 64, 0.9);
  --chart-bar-green: rgba(52, 211, 153, 0.7);
  --chart-bar-green-hover: rgba(52, 211, 153, 0.9);
  --chart-bar-purple: rgba(192, 132, 252, 0.7);
  --chart-bar-purple-hover: rgba(192, 132, 252, 0.9);
  --chart-bar-source: rgba(188, 140, 255, 0.7);
  --chart-bar-source-hover: rgba(188, 140, 255, 0.9);
  --chart-value-label: #e6edf6;

  /* radii / shadows / motion (theme-invariant) */
  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 14px;
  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.3), 0 4px 12px rgba(0, 0, 0, 0.15);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.4), 0 8px 32px rgba(0, 0, 0, 0.2);
  --shadow-mobile-nav: -4px 0 24px rgba(0, 0, 0, 0.4);
  --shadow-error-panel: 0 6px 24px rgba(0, 0, 0, 0.5);
  --shadow-login-card: 0 4px 24px rgba(0, 0, 0, 0.3), 0 12px 48px rgba(0, 0, 0, 0.15);
  --shadow-setup-card: 0 4px 24px rgba(0, 0, 0, 0.3);
  --shadow-crop-preview: 0 8px 24px rgba(0, 0, 0, 0.5);
  --transition: 0.2s ease;
}

/* ─── Light theme ─────────────────────────────────────────────── */
[data-theme="light"] {
  color-scheme: light;

  --bg-primary: #f6f8fb;
  --bg-card: #ffffff;
  --bg-card-hover: #eef1f5;
  --bg-surface: #f1f3f6;
  --bg-input: #ffffff;
  --bg-elevated: #ffffff;
  --bg-modal-backdrop: rgba(20, 25, 35, 0.45);
  --bg-lightbox-backdrop: rgba(20, 25, 35, 0.78);
  --bg-mobile-overlay: rgba(20, 25, 35, 0.35);
  --bg-header-grad: linear-gradient(135deg, #f0e9ff 0%, #ffeed6 50%, #f7e9ff 100%);

  --border: #e1e5ec;
  --border-light: #ced4dc;
  --border-strong: #b6bec9;
  --row-divider: rgba(30, 39, 54, 0.08);
  --row-divider-mobile: rgba(30, 39, 54, 0.06);

  --text-primary: #1a1f2c;
  --text-secondary: #5a6373;
  --text-muted: #646e7e;
  --text-on-accent: #ffffff;
  --text-on-gold: #ffffff;
  --focus-ring: #2563eb;

  --hover-overlay: rgba(0, 0, 0, 0.05);
  --hover-overlay-strong: rgba(0, 0, 0, 0.06);
  --row-hover-overlay: rgba(0, 0, 0, 0.025);
  --row-hover-overlay-strong: rgba(0, 0, 0, 0.04);
  --subtle-bg: rgba(0, 0, 0, 0.04);
  --subtle-bg-strong: rgba(0, 0, 0, 0.06);
  --code-bg: rgba(0, 0, 0, 0.05);
  --zebra-bg: rgba(0, 0, 0, 0.025);
  --details-bg: rgba(0, 0, 0, 0.04);

  /* Light theme uses the darker accents (see --accent-green / --accent-red
     below), so the same alpha reads stronger here than on dark. */
  --goal-row-ok-bg: rgba(21, 128, 61, 0.10);
  --goal-row-warn-bg: rgba(202, 138, 4, 0.12);
  --goal-row-low-bg: rgba(185, 28, 28, 0.08);

  --goal-cell-ok-bg: rgba(21, 128, 61, 0.16);
  --goal-cell-warn-bg: rgba(202, 138, 4, 0.18);
  --goal-cell-low-bg: rgba(185, 28, 28, 0.14);

  /* Darker than the brand gold it stands in for: #b88a00 read 3.2:1 on white
     and 2.6:1 on its own tinted pills. */
  --accent-gold: #7f5e00;
  --accent-gold-dim: #fff3c4;
  --accent-gold-bg: rgba(184, 138, 0, 0.1);
  --accent-gold-bg-strong: rgba(184, 138, 0, 0.18);
  --accent-gold-border: rgba(184, 138, 0, 0.4);
  --accent-gold-active-bg: rgba(184, 138, 0, 0.08);
  --accent-blue: #2563eb;
  --accent-blue-hover: #1d4ed8;
  --accent-blue-solid: #2563eb;
  --accent-blue-solid-hover: #1d4ed8;
  --accent-red-solid: #b91c1c;
  --accent-red-solid-hover: #991b1b;
  --accent-blue-bg: rgba(37, 99, 235, 0.12);
  --accent-blue-soft: rgba(37, 99, 235, 0.07);
  --accent-blue-soft-strong: rgba(37, 99, 235, 0.14);
  --accent-blue-soft-hover: rgba(37, 99, 235, 0.08);
  --accent-green: #15803d;
  --accent-green-bg: rgba(21, 128, 61, 0.12);
  --accent-green-bg-strong: rgba(21, 128, 61, 0.16);
  --accent-green-soft-text: #14532d;
  --accent-red: #b91c1c;
  --accent-red-bg: rgba(185, 28, 28, 0.08);
  --accent-red-bg-strong: rgba(185, 28, 28, 0.16);
  --accent-red-border: rgba(185, 28, 28, 0.4);
  --accent-red-border-soft: rgba(185, 28, 28, 0.3);
  --accent-red-soft-text: #7f1d1d;
  --accent-purple: #7c3aed;
  --accent-amber: #b45309;
  --accent-amber-bg: rgba(180, 83, 9, 0.16);
  --accent-amber-border: rgba(180, 83, 9, 0.4);
  --accent-amber-text: #92400e;
  --accent-warn-bg: rgba(202, 138, 4, 0.16);
  --accent-warn-text: #92400e;

  --status-error-bg: #fee2e2;
  --status-error-text: #b91c1c;
  --status-error-row-bg: rgba(185, 28, 28, 0.14);
  --status-error-row-border: rgba(185, 28, 28, 0.32);
  --status-success-border: #86efac;
  --status-success-text: #15803d;
  --status-error-border: #fecaca;
  --status-error-row-border2: #fecaca;
  --status-error-row-text: #b91c1c;
  --transfer-progress-track: #e1e5ec;
  --transfer-card-danger-border: #fecaca;
  --transfer-card-danger-bg: linear-gradient(180deg, #fff5f5 0%, #fee2e2 100%);

  /* chest rarities — keep semantic hue, swap to softer pastel surfaces with darker text */
  --chest-common-bg: #e5e7eb;
  --chest-common-text: #4b5563;
  --chest-uncommon-bg: #d1fae5;
  --chest-uncommon-text: #065f46;
  --chest-rare-bg: #dbeafe;
  --chest-rare-text: #1d4ed8;
  --chest-epic-bg: #ede9fe;
  --chest-epic-text: #6d28d9;
  --chest-legendary-bg: #fef3c7;
  --chest-legendary-text: #92400e;
  --chest-arena-bg: #fee2e2;
  --chest-arena-text: #b91c1c;
  --chest-event-bg: #fef3c7;
  --chest-event-text: #92400e;
  --chest-unknown-bg: #e5e7eb;
  --chest-unknown-text: #4b5563;

  --type-common: #6b7280;
  --type-uncommon: #16a34a;
  --type-rare: #2563eb;
  --type-epic: #7c3aed;
  --type-legendary: #b45309;
  --type-arena: #dc2626;
  --type-event: #ea580c;
  --type-unknown: #6b7280;
  --type-row-bar-bg: #e1e5ec;

  --rank-1: #ca8a04;
  --rank-2: #6b7280;
  --rank-3: #b45309;
  --rank-1-glow: rgba(202, 138, 4, 0.18);
  --rank-2-glow: rgba(107, 114, 128, 0.18);
  --rank-3-glow: rgba(180, 83, 9, 0.18);

  --chart-grid: rgba(0, 0, 0, 0.06);
  --chart-tick: #5a6373;
  --chart-tooltip-bg: #ffffff;
  --chart-tooltip-text: #1a1f2c;
  --chart-tooltip-border: #ced4dc;
  --chart-bar-blue: rgba(37, 99, 235, 0.7);
  --chart-bar-blue-hover: rgba(37, 99, 235, 0.9);
  --chart-bar-gold: rgba(184, 138, 0, 0.75);
  --chart-bar-gold-hover: rgba(184, 138, 0, 0.95);
  --chart-bar-green: rgba(21, 128, 61, 0.75);
  --chart-bar-green-hover: rgba(21, 128, 61, 0.95);
  --chart-bar-purple: rgba(124, 58, 237, 0.7);
  --chart-bar-purple-hover: rgba(124, 58, 237, 0.9);
  --chart-bar-source: rgba(124, 58, 237, 0.75);
  --chart-bar-source-hover: rgba(124, 58, 237, 0.95);
  --chart-value-label: #1a1f2c;

  --shadow-card: 0 1px 2px rgba(20, 25, 35, 0.06), 0 2px 8px rgba(20, 25, 35, 0.06);
  --shadow-elevated: 0 4px 12px rgba(20, 25, 35, 0.12), 0 8px 24px rgba(20, 25, 35, 0.1);
  --shadow-mobile-nav: -4px 0 24px rgba(20, 25, 35, 0.18);
  --shadow-error-panel: 0 6px 24px rgba(20, 25, 35, 0.18);
  --shadow-login-card: 0 4px 24px rgba(20, 25, 35, 0.1), 0 12px 48px rgba(20, 25, 35, 0.08);
  --shadow-setup-card: 0 4px 24px rgba(20, 25, 35, 0.08);
  --shadow-crop-preview: 0 8px 24px rgba(20, 25, 35, 0.18);
}

/* ─── OLED theme (pure black for AMOLED) ───────────────────────── */
[data-theme="oled"] {
  color-scheme: dark;

  --bg-primary: #000000;
  --bg-card: #000000;
  --bg-card-hover: #0c0c0e;
  --bg-surface: #050505;
  --bg-input: #050505;
  --bg-elevated: #0c0c0e;
  --bg-modal-backdrop: rgba(0, 0, 0, 0.85);
  --bg-lightbox-backdrop: rgba(0, 0, 0, 0.95);
  --bg-mobile-overlay: rgba(0, 0, 0, 0.7);
  --bg-header-grad: linear-gradient(135deg, #000000 0%, #060508 50%, #000000 100%);

  --border: #1a1a1c;
  --border-light: #28282c;
  --border-strong: #3a3a3e;
  --row-divider: rgba(255, 255, 255, 0.06);
  --row-divider-mobile: rgba(255, 255, 255, 0.04);

  --text-primary: #e6edf6;
  --text-secondary: #8b95a5;
  --text-muted: #6f7b8a;
  --text-on-accent: #ffffff;

  --hover-overlay: rgba(255, 255, 255, 0.06);
  --hover-overlay-strong: rgba(255, 255, 255, 0.08);
  --row-hover-overlay: rgba(255, 255, 255, 0.03);
  --row-hover-overlay-strong: rgba(255, 255, 255, 0.05);
  --subtle-bg: rgba(255, 255, 255, 0.04);
  --subtle-bg-strong: rgba(255, 255, 255, 0.07);
  --code-bg: rgba(255, 255, 255, 0.06);
  --zebra-bg: rgba(255, 255, 255, 0.03);
  --details-bg: rgba(0, 0, 0, 0.6);

  /* Roughly half the dark-theme alpha. A tint that reads as "subtle" over
     #0d1117 reads as a lit panel over pure black — OLED has no ambient
     surface glow for it to blend into, so the same value that whispers on
     dark shouts here. */
  --goal-row-ok-bg: rgba(52, 211, 153, 0.055);
  --goal-row-warn-bg: rgba(250, 204, 21, 0.05);
  --goal-row-low-bg: rgba(248, 113, 113, 0.05);

  --goal-cell-ok-bg: rgba(52, 211, 153, 0.085);
  --goal-cell-warn-bg: rgba(250, 204, 21, 0.075);
  --goal-cell-low-bg: rgba(248, 113, 113, 0.075);

  --status-error-bg: #100707;
  --transfer-progress-track: #0c0c0e;
  --transfer-card-danger-bg: linear-gradient(180deg, #0a0506 0%, #060304 100%);

  --type-row-bar-bg: #0c0c0e;

  --chart-grid: rgba(255, 255, 255, 0.06);
  --chart-tooltip-bg: #050505;
  --chart-tooltip-border: #28282c;

  --shadow-card: 0 1px 3px rgba(0, 0, 0, 0.6), 0 4px 12px rgba(0, 0, 0, 0.4);
  --shadow-elevated: 0 4px 16px rgba(0, 0, 0, 0.8), 0 8px 32px rgba(0, 0, 0, 0.5);
  --shadow-mobile-nav: -4px 0 24px rgba(0, 0, 0, 0.7);
  --shadow-error-panel: 0 6px 24px rgba(0, 0, 0, 0.85);
  --shadow-login-card: 0 4px 24px rgba(0, 0, 0, 0.7), 0 12px 48px rgba(0, 0, 0, 0.5);
  --shadow-setup-card: 0 4px 24px rgba(0, 0, 0, 0.7);
  --shadow-crop-preview: 0 8px 24px rgba(0, 0, 0, 0.85);
}

/* ─── Theme picker (segmented row) ──────────────────────────────
   iOS-style single track: borderless buttons in a recessed container;
   the active one lifts off as a soft pill. Used by the app's user menu
   and the setup wizard. The surrounding .user-menu-* chrome is app-only
   and stays in style.css. */
.theme-segmented {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 2px;
  padding: 3px;
  background: var(--bg-input);
  border-radius: 8px;
}
.theme-segmented-btn {
  background: transparent;
  border: 0;
  color: var(--text-secondary);
  padding: 6px 4px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 600;
  font-family: inherit;
  letter-spacing: 0.01em;
  transition: background var(--transition), color var(--transition), box-shadow var(--transition);
}
.theme-segmented-btn:hover {
  color: var(--text-primary);
}
.theme-segmented-btn.is-active {
  background: var(--bg-card);
  color: var(--text-primary);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.25), 0 0 0 1px var(--border);
}
[data-theme="light"] .theme-segmented-btn.is-active {
  box-shadow: 0 1px 2px rgba(20, 25, 35, 0.08), 0 0 0 1px var(--border);
}
[data-theme="oled"] .theme-segmented-btn.is-active {
  background: var(--bg-card-hover);
  box-shadow: 0 0 0 1px var(--border-light);
}

/* ─── Login bridge ──────────────────────────────────────────────
   Streams a remote Chromium frame into a <canvas>. Rendered by
   lib/login-bridge.js and used by both the Clans admin page and the
   setup wizard. Hidden via .is-hidden; .login-session-modal upgrades it
   to a centered overlay (see the modal block further down). */
.login-session-panel {
  margin: 0 0 24px;
}
.login-session-statusrow {
  gap: 8px;
  align-items: center;
  flex-wrap: wrap;
}
.login-session-url {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12px;
  word-break: break-all;
}
.login-session-viewport {
  width: 100%;
  max-width: 1280px;
}
/* The frame itself, at the stream's native 1280:800. A separate box from the
   viewport so the modal can let the viewport take all the room it has and
   fit the frame inside it (see the modal block). */
.login-session-screen {
  position: relative;
  width: 100%;
  aspect-ratio: 1280 / 800;
  background: var(--bg-input);
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.login-session-canvas {
  width: 100%;
  height: 100%;
  display: block;
  cursor: crosshair;
  outline: none;
}
/* Inset: the viewport clips anything drawn outside the canvas. */
.login-session-canvas:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: -2px;
}
.login-session-overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-secondary);
  font-size: 14px;
  pointer-events: none;
}
.login-session-overlay.is-hidden {
  display: none;
}
.login-session-side {
  display: flex;
  flex-direction: column;
  gap: 12px;
}
.login-session-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.login-session-hint {
  margin: 0;
  font-size: 12px;
}

/* Bonus links: preset ?present= buttons plus a paste box (Clans only). */
.login-session-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}
.login-session-links-label {
  font-size: 12px;
  font-weight: 600;
  color: var(--text-secondary);
}
.login-session-linkform {
  display: flex;
  gap: 8px;
  flex: 1 1 280px;
  min-width: 0;
}
.login-session-linkform .input {
  flex: 1 1 auto;
  max-width: none;
}
.login-session-linkform .input[aria-invalid="true"] {
  border-color: var(--accent-red-solid);
}

/* ─── Login bridge, modal presentation ──────────────────────────
   When the shared panel is tagged .login-session-modal it doubles as a
   centered overlay: .login-session-panel becomes the dimmed, blurred
   backdrop and .login-session-card becomes the modal itself. Mirrors the
   app's .modal-overlay / .modal-card system (same variables, same 0.18s
   fade + slide). .is-hidden (display:none) still wins to fully remove the
   overlay between sessions. */
.login-session-panel.login-session-modal {
  position: fixed;
  inset: 0;
  margin: 0;
  z-index: 1000;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 8px;
  background: var(--bg-modal-backdrop);
  backdrop-filter: blur(4px);
  opacity: 0;
  transition: opacity 0.18s ease;
}
.login-session-panel.login-session-modal.is-visible {
  opacity: 1;
}

/* Layout: the game frame on the left, everything else in a sidebar on the
   right. The stream is 16:10 and nearly every desktop window is wider than
   that, so the frame is bound by HEIGHT and the spare room is at the sides —
   a sidebar spends that, where a row of controls under the frame would cost
   frame height. .login-session-card-body is display:contents so its two
   children (viewport, sidebar) sit on the card's grid next to the header. */
.login-session-modal .login-session-card {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 248px;
  grid-template-rows: auto minmax(0, 1fr);
  grid-template-areas:
    "view head"
    "view side";
  gap: 10px 14px;
  width: 100%;
  /* Ultrawide: no wider than a full-height frame plus the sidebar, so the
     frame stays beside its controls instead of floating in a wide gap.
     38px = panel padding + card padding + border; 284px = sidebar + gap +
     card padding + border. */
  max-width: calc((100vh - 38px) * 1.6 + 284px);
  height: 100%;
  overflow: hidden;
  padding: 10px;
  background: var(--bg-card);
  border: 1px solid var(--border-light);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-elevated);
  transform: translateY(8px);
  transition: transform 0.18s ease;
}
.login-session-panel.login-session-modal.is-visible .login-session-card {
  transform: translateY(0);
}
.login-session-modal .login-session-card-body {
  display: contents;
}

.login-session-modal .login-session-card-header {
  grid-area: head;
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}
.login-session-modal .login-session-card-header h2 {
  margin: 0;
  font-size: 15px;
  font-weight: 700;
  letter-spacing: -0.2px;
  color: var(--text-primary);
}
.login-session-modal .login-session-statusrow {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.login-session-modal .login-session-url {
  min-width: 0;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
  word-break: normal;
}

/* The game frame fills its whole grid cell at 16:10: the viewport is a size
   container and the frame is as wide as fits both ways (160cqh = 1.6 × the
   cell's height). Nothing reserves a guessed number of pixels for chrome. */
.login-session-modal .login-session-viewport {
  grid-area: view;
  min-width: 0;
  min-height: 0;
  max-width: none;
  display: flex;
  align-items: center;
  justify-content: center;
  container-type: size;
}
.login-session-modal .login-session-screen {
  width: min(100cqw, 160cqh);
}

.login-session-modal .login-session-side {
  grid-area: side;
  min-height: 0;
  overflow-y: auto;
}
.login-session-modal .login-session-actions,
.login-session-modal .login-session-links {
  flex-direction: column;
  flex-wrap: nowrap;
  align-items: stretch;
}
.login-session-modal .login-session-actions .btn {
  padding: 8px 12px;
}
.login-session-modal .login-session-links {
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.login-session-modal .login-session-linkform {
  flex: none;
}
.login-session-modal .login-session-hint {
  margin-top: auto;
}

/* Portrait, or too narrow for a sidebar beside a usable frame: stack it —
   header, frame, then the controls in wrapping rows — and size the card to
   its content, since the width binds and a full-height card would only add
   empty space under the frame. */
@media (max-aspect-ratio: 1/1), (max-width: 760px) {
  .login-session-modal .login-session-card {
    grid-template-columns: minmax(0, 1fr);
    grid-template-rows: auto auto auto;
    grid-template-areas:
      "head"
      "view"
      "side";
    max-width: none;
    height: auto;
    max-height: 100%;
    overflow-y: auto;
  }
  .login-session-modal .login-session-card-header {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 16px;
  }
  .login-session-modal .login-session-viewport {
    container-type: normal;
  }
  .login-session-modal .login-session-screen {
    width: 100%;
  }
  .login-session-modal .login-session-side {
    overflow: visible;
  }
  .login-session-modal .login-session-actions,
  .login-session-modal .login-session-links {
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
  }
  .login-session-modal .login-session-actions .btn {
    flex: 1 1 auto;
  }
  .login-session-modal .login-session-linkform {
    flex: 1 1 260px;
  }
}
