/*
 * Slate Operations Theme — GameBuddyTracker
 *
 * Overrides Bootstrap 5 defaults with the Slate Operations palette.
 * Uses Outfit (sans-serif) + JetBrains Mono (monospace).
 * All colors defined as CSS variables for easy tweaking.
 */

/* ==========================================================================
   Design Tokens — Axis-Driven Theme System
   ==========================================================================
   Four orthogonal axes drive the appearance, set as data attributes on
   <body> by base.html (resolved in core/appearance.py, see there):
     data-mode    = dark | light | black   (brightness baseline; the stored
                    "system" is resolved to light or dark by base.html's
                    inline script before paint — never write CSS for it)
     data-accent  = slate | storm | forest | sunrise | pride
     data-bg      = flat | aurora | festival
     data-motion  = full | reduced | system   ("system" = Match device, the
                    default: the reduced rules apply when the OS asks for
                    reduced motion — see Motion preference)
   <html> carries data-bs-theme="light" | "dark" from the same mode, never
   <body> (see the bridge below): it switches Bootstrap's own dark/light
   component variants — color-scheme for native widgets, the select chevron,
   the close-button X, the switch knob.

   Legacy stable token names (--bg-deep, --bg-card, --accent, --text-primary,
   etc.) are preserved so existing rules and templates need no edits.
   :root holds the dark + slate + aurora baseline. Mode and accent overrides
   live in dedicated blocks below the rules section.

   Contrast (audit TH-4): every text token meets WCAG AA (4.5:1) on the card
   and the page in every mode × accent, and so do the button text/fill pairs;
   core/tests/test_theme_tokens.py parses this file and checks it. Change a
   colour here and run that test.

   Only literal values belong in :root. A var() in :root is resolved on
   <html>, where no data-mode / data-accent applies, so it would be frozen at
   dark + slate on every page. Tokens built from other tokens go in the
   ":root, body" block below.
   ========================================================================== */

:root {
  /* Mode tokens (dark default — overridden under [data-mode="light"]) */
  --bg-deep: #0c0f14;
  --bg-surface: #12161e;
  --bg-card: rgba(24, 29, 40, 0.85);
  --bg-card-hover: rgba(30, 37, 53, 0.88);
  --bg-elevated: rgba(36, 44, 61, 0.9);
  --text-primary: #e2e8f0;
  --text-secondary: #94a3b8;
  --text-muted: #7c8aa0;
  --border: #1e293b;
  /* Text field and select edges (.form-control, .form-select, Ready Up's
     panel selects): >= 3:1 on the card, the page and the field's own
     --bg-surface fill (WCAG 1.4.11). --border (about 1.2:1) stays for
     dividers, where the line is decoration, not the control's edge. */
  --input-border: #5f6f86;
  --border-green: rgba(34, 197, 94, 0.25);
  --navbar-bg: rgba(12, 15, 20, 0.8);

  /* Semantic colors (per-mode shading allowed; values here are dark-mode) */
  --green: #22c55e;
  --green-dim: #16a34a;
  --green-on: #052e16;       /* text on a --green / --green-dim fill (.btn-success) */
  --orange: #f59e0b;
  --red: #f36666;            /* #ef4444 was 3.95:1 on the outline buttons' pressed tint */
  --sky: #38bdf8;
  --amber: #fbbf24;
  --emerald: #34d399;
  --purple: #b06af8;
  /* Text on a solid status fill (--red, --green, --amber, --sky, --orange,
     --emerald, --purple, --text-muted): an inline badge with
     `background: var(--x)` must set `color: var(--fill-on)` — Bootstrap's
     .badge text is #fff, which was 1.7-3.5:1 on these dark-mode fills
     (the unread-count pill, Cancelled / Past / Going / Maybe, feedback
     status). Light mode's fills are dark, so it is white there. */
  --fill-on: #0c0f14;

  /* Accent tokens (slate default — overridden under [data-accent="..."]) */
  --accent: #5b5ef0;
  --accent-light: #818cf8;   /* the accent as text: links, .text-primary, outline buttons */
  --accent-dim: #4338ca;
  --accent-rgb: 91, 94, 240;
  --accent-on: #ffffff;      /* text on an --accent / --accent-dim fill (.btn-primary) */
}

/* ---- Derived tokens + Bootstrap variable bridge (audit TH-1) ----
   Declared on BOTH :root and body. data-mode / data-accent live on <body>,
   and a custom property that uses var() is resolved on the element that
   declares it — its children inherit the result. Declared on :root only,
   every value below was computed on <html> from the dark + slate tokens and
   inherited unchanged by everything in <body>: in light mode Bootstrap's
   cards kept #e2e8f0 text on white, checkboxes drew #0c0f14 blocks.
   Re-declared on body, the var()s resolve against body's mode and accent
   tokens. (:root keeps them for anything outside <body>.)

   Never put data-bs-theme on <body>: Bootstrap's [data-bs-theme] block (0,1,0)
   would outrank `body` (0,0,1) and replace this bridge. It goes on <html>,
   where this rule's `:root` (0,1,0, later in the cascade) still wins for
   every variable bridged here; the ones not bridged (--bs-emphasis-color-rgb,
   --bs-*-bg-subtle, …) take Bootstrap's light or dark values, matching the
   mode. */
:root,
body {
  --border-accent: rgba(var(--accent-rgb), 0.2);
  /* Link hover moves away from the background: lighter in dark modes,
     darker (--accent-dim) in light mode. */
  --link-hover: color-mix(in srgb, var(--accent-light) 65%, #ffffff);
  /* One keyboard focus ring everywhere (audit TH-8): the text-safe accent. */
  --focus-ring: var(--accent-light);

  /* Bootstrap components (.card, .dropdown-menu, .form-*, .input-group,
     .table, .badge.bg-*, .border-*, .link-*) read these. Buttons are themed
     through their own --bs-btn-* variables in the Buttons section. */
  --bs-primary: var(--accent);
  --bs-primary-rgb: var(--accent-rgb);
  --bs-link-color: var(--accent-light);
  --bs-link-color-rgb: var(--accent-rgb);
  --bs-link-hover-color: var(--link-hover);
  --bs-body-bg: var(--bg-deep);
  --bs-body-color: var(--text-primary);
  --bs-emphasis-color: var(--text-primary);
  --bs-secondary-color: var(--text-secondary);
  --bs-tertiary-color: var(--text-muted);
  --bs-border-color: var(--border);
  --bs-secondary-bg: var(--bg-elevated);
  --bs-tertiary-bg: var(--bg-surface);
  --bs-success: var(--green);
  --bs-success-rgb: 34, 197, 94;
  --bs-danger: var(--red);
  --bs-danger-rgb: 239, 68, 68;
  --bs-warning: var(--orange);
  --bs-warning-rgb: 245, 158, 11;
  --bs-info: var(--sky);
  --bs-info-rgb: 56, 189, 248;
}


/* ==========================================================================
   Base / Body
   ========================================================================== */

body {
  font-family: 'Outfit', sans-serif;
  /* Only a colour. The ambient glow (aurora, festival) is painted by the
     fixed .bg-effects layer behind the content — see "Ambient backdrop"
     (audit TH-7 / TH-12). Keep body free of transform, filter, opacity,
     isolation and z-index, and html free of a background: either would stop
     that layer from sitting behind the page. */
  background-color: var(--bg-deep);
  color: var(--text-primary);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  font-size: 15px;
  line-height: 1.5;
}

a { color: var(--accent-light); }
a:hover { color: var(--link-hover); }


/* ==========================================================================
   Navbar
   ========================================================================== */

/* Background from --navbar-bg (per mode). No backdrop blur: the bar is
   position: relative, not sticky, so it only ever blurred the static top of
   the page (audit TH-12). */
.navbar {
  background: var(--navbar-bg) !important;
  border-bottom: 1px solid var(--border);
  padding-top: 0.4rem;
  padding-bottom: 0.4rem;
  position: relative;
  z-index: 1050;
}

.navbar-brand {
  font-weight: 800;
  font-size: 1.05rem;
  letter-spacing: -0.5px;
  color: var(--text-primary) !important;
  display: flex;
  align-items: center;
  gap: 10px;
}

.navbar-brand .brand-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  background: linear-gradient(135deg, var(--accent), var(--accent-dim));
  border-radius: 8px;
  color: white;
  font-size: 0.9rem;
}

/* ── Red-E-Up Logo Images (audit TH-9) ──
   static/img/logo.jpg is a 1280×1280 square of black with the "RED"
   wordmark across the middle ~22% of its height. object-fit: cover in a
   box about 2.9:1 crops it to the wordmark band, so the letters are ~23 px
   tall in the navbar instead of ~9 px (the box's scale is its width / 1280).
   In light mode the black would be a black tile on a white bar: the
   #logo-on-light SVG filter (inline in base.html) turns the black into
   transparency, the white letters into the light-mode text colour and
   keeps the red E red. */
.brand-logo-clip {
  line-height: 0;
}

.brand-logo-img,
.brand-logo-img-footer,
.brand-logo-img-auth {
  object-fit: cover;
  border-radius: 6px;
}

.brand-logo-img {
  height: 36px;
  width: 104px;
}

.brand-logo-img-footer {
  height: 28px;
  width: 81px;
  opacity: 0.6;
}

.brand-logo-img-auth {
  height: 56px;
  width: 162px;
}

[data-mode="light"] .brand-logo-img,
[data-mode="light"] .brand-logo-img-footer,
[data-mode="light"] .brand-logo-img-auth {
  filter: url(#logo-on-light);
}

.footer-tagline {
  font-size: 0.7rem;
  color: var(--text-muted, rgba(255,255,255,0.3));
  margin-top: 4px;
  font-style: italic;
  letter-spacing: 0.3px;
}

.navbar-nav .nav-link {
  color: var(--text-secondary) !important;
  font-weight: 500;
  font-size: 0.875rem;
  padding: 0.4rem 0.85rem;
  border-radius: 6px;
  transition: all 0.15s;
}

.navbar-nav .nav-link:hover {
  color: var(--text-primary) !important;
  background: var(--bg-card);
}

.navbar-nav .nav-link.active,
.navbar-nav .nav-link[aria-current="page"] {
  color: var(--accent-light) !important;
  background: rgba(var(--accent-rgb), 0.1);
}

.nav-user-text {
  color: var(--text-secondary) !important;
  font-size: 0.875rem;
  font-weight: 500;
}

.btn-nav-logout {
  color: var(--text-muted);
  background: none;
  border: 1px solid var(--border);
  padding: 4px 12px;
  border-radius: 6px;
  font-size: 0.8rem;
  font-weight: 500;
  transition: all 0.15s;
}

.btn-nav-logout:hover {
  color: var(--text-secondary);
  border-color: var(--text-muted);
  background: var(--bg-card);
}

/* Mobile menu button: the mode's text colour (it used .text-light, which is
   remapped to --text-primary anyway). Keyboard focus gets the global ring,
   not Bootstrap's currentColor box-shadow. */
.navbar-toggler {
  color: var(--text-primary);
}
.navbar-toggler:focus {
  box-shadow: none;
}


/* ---------- Dropdowns ---------- */

/* User menu and notifications: Bootstrap hard-codes the pressed item as
   #0d6efd with white text (audit TH-8). Must stay before .crew-dropdown,
   which sets its own values at the same specificity. */
.dropdown-menu {
  --bs-dropdown-color: var(--text-primary);
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-link-color: var(--text-primary);
  --bs-dropdown-link-hover-color: var(--text-primary);
  --bs-dropdown-link-hover-bg: var(--bg-elevated);
  --bs-dropdown-link-active-color: var(--text-primary);
  --bs-dropdown-link-active-bg: rgba(var(--accent-rgb), 0.15);
  --bs-dropdown-divider-bg: var(--border);
}


/* ---------- Crew Switcher ---------- */

.crew-switcher-toggle,
.crew-switcher-label {
  color: var(--amber) !important;
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.02em;
}

.crew-switcher-toggle:hover {
  color: var(--sky) !important;
}

/* Override Bootstrap's dropdown CSS variables at the component level
   so .active states, hover, and focus all use our theme colours. */
.crew-dropdown {
  z-index: 1050;
  --bs-dropdown-bg: var(--bg-card);
  --bs-dropdown-border-color: var(--border);
  --bs-dropdown-link-color: var(--text-secondary);
  --bs-dropdown-link-hover-color: var(--text-primary);
  --bs-dropdown-link-hover-bg: var(--bg-elevated);
  --bs-dropdown-link-active-color: var(--amber);
  --bs-dropdown-link-active-bg: rgba(251, 191, 36, 0.06);
  --bs-dropdown-divider-bg: var(--border);
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 6px 0;
  min-width: 220px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}

.crew-dropdown .dropdown-item {
  font-size: 0.85rem;
  font-weight: 500;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: all 0.15s;
}

.crew-dropdown .dropdown-item.active,
.crew-dropdown .dropdown-item.active:hover,
.crew-dropdown .dropdown-item.active:focus {
  color: var(--amber) !important;
  background-color: rgba(251, 191, 36, 0.06) !important;
}

.crew-dropdown .dropdown-item.active i {
  color: var(--emerald);
}

.crew-dropdown .crew-action {
  color: var(--text-muted);
}

.crew-dropdown .crew-action:hover {
  color: var(--text-primary);
  background: var(--bg-elevated);
}

.crew-dropdown form {
  margin: 0;
  padding: 0;
}

.crew-dropdown form button.dropdown-item {
  border: none;
  background: none;
  width: 100%;
  text-align: left;
  cursor: pointer;
}

.crew-dropdown .dropdown-divider {
  margin: 4px 0;
  opacity: 0.3;
}


/* ==========================================================================
   Cards (override Bootstrap .card)
   ========================================================================== */

/* No backdrop-filter (audit TH-12): the card is 85-92% opaque over smooth
   gradients, so a blur was almost invisible, yet every card paid for its
   own compositing layer and backdrop pass on every scroll frame (My List
   renders hundreds). The transition names what .card-hover changes. */
.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 10px;
  transition: background-color 0.15s, border-color 0.15s, transform 0.15s, box-shadow 0.15s;
}

.card.card-hover:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-accent);
  transform: translateY(-2px);
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

.card-body { padding: 1.25rem; }
.card-title { font-weight: 600; }

/* Card section headers (uppercase label style) */
.card-section-title {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 1rem;
}

.card-section-title i { color: var(--accent-light); font-size: 0.9rem; }


/* ==========================================================================
   Stat Cards
   ========================================================================== */

.stat-card {
  display: flex;
  align-items: center;
  gap: 14px;
  cursor: pointer;
}

.stat-card:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-accent);
  transform: translateY(-1px);
}

.stat-icon {
  width: 44px;
  height: 44px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.1rem;
  flex-shrink: 0;
}

.stat-icon-accent {
  background: rgba(var(--accent-rgb), 0.1);
  color: var(--accent-light);
  border: 1px solid rgba(var(--accent-rgb), 0.15);
}

.stat-icon-sky {
  background: rgba(56, 189, 248, 0.1);
  color: var(--sky);
  border: 1px solid rgba(56, 189, 248, 0.15);
}

.stat-icon-green {
  background: rgba(34, 197, 94, 0.1);
  color: var(--green);
  border: 1px solid rgba(34, 197, 94, 0.15);
}

.stat-icon-amber {
  background: rgba(251, 191, 36, 0.1);
  color: var(--amber);
  border: 1px solid rgba(251, 191, 36, 0.15);
}

.stat-number {
  font-family: 'JetBrains Mono', monospace;
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  margin-bottom: 2px;
  color: var(--text-primary);
}

.stat-label {
  color: var(--text-secondary);
  font-size: 0.78rem;
  font-weight: 500;
}


/* ==========================================================================
   Availability Banner
   ========================================================================== */

.avail-banner {
  border-color: var(--border);
}

.avail-banner.available {
  border-color: var(--border-green);
  background: linear-gradient(135deg, var(--bg-card), rgba(34, 197, 94, 0.03));
}

.avail-indicator {
  width: 36px;
  height: 36px;
  border-radius: 10px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
}

.avail-indicator-on {
  background: rgba(34, 197, 94, 0.1);
  color: var(--green);
  border: 1px solid rgba(34, 197, 94, 0.2);
}

.avail-indicator-off {
  background: rgba(71, 85, 105, 0.2);
  color: var(--text-muted);
  border: 1px solid var(--border);
}

/* "You don't own any games on your list yet — import your Steam library…"
   below the banner's row
   (availability_section.html, audit PT-11): a line of its own, so the
   check-in row doesn't get cramped at phone width. */
.avail-empty-note {
  padding: 0 1rem 0.75rem;
  font-size: 0.8rem;
  color: var(--text-secondary);
}


/* ==========================================================================
   Player Avatars
   ========================================================================== */

.player-avatar {
  width: 28px;
  height: 28px;
  border-radius: 8px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.65rem;
  font-weight: 700;
  color: white;
  flex-shrink: 0;
}

/* Rotating avatar colors */
.player-avatar-1 { background: linear-gradient(135deg, #6366f1, #4338ca); }
.player-avatar-2 { background: linear-gradient(135deg, #22c55e, #16a34a); }
.player-avatar-3 { background: linear-gradient(135deg, #f59e0b, #d97706); }
.player-avatar-4 { background: linear-gradient(135deg, #38bdf8, #0284c7); }
.player-avatar-5 { background: linear-gradient(135deg, #ef4444, #b91c1c); }
.player-avatar-6 { background: linear-gradient(135deg, #a855f7, #7c3aed); }

.player-status-dot {
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--green);
  display: inline-block;
}

.player-status-dot--idle {
  background: var(--orange);
}

.player-status-label--idle {
  color: var(--orange);
}

/* Ready Up's "Expected: sam, ana (RSVP'd Going)" under the Checked In
   chips (audit PT-19): RSVPs who haven't checked in, muted and dashed so
   they don't read as players. */
.expected-players {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-size: 0.8rem;
  color: var(--text-muted);
}
.expected-chip {
  padding: 2px 10px;
  border: 1px dashed var(--text-muted);
  border-radius: 8px;
  color: var(--text-secondary);
}


/* ==========================================================================
   Buttons
   ==========================================================================
   Themed through Bootstrap's own --bs-btn-* variables (audit TH-8), so every
   state Bootstrap draws — :hover, :focus-visible, :active / .active,
   :disabled — uses theme colours. Setting background / border-color on the
   base and :hover only (as before) left Bootstrap's higher-specificity state
   rules (.btn:first-child:active is 0,3,0) painting its hard-coded blues
   (#0b5ed7, #0a58ca, #0d6efd) whatever the accent.
   Text on fills uses the *-on tokens (--accent-on, --green-on), which meet
   WCAG AA on the resting and the hover/active fill in every mode × accent
   (audit TH-4; core/tests/test_theme_tokens.py checks every pair). Outline
   buttons' hover/active text is --link-hover, which moves away from the
   background (lighter in dark modes, darker in light mode).
   ========================================================================== */

.btn-primary {
  --bs-btn-color: var(--accent-on);
  --bs-btn-bg: var(--accent);
  --bs-btn-border-color: var(--accent);
  --bs-btn-hover-color: var(--accent-on);
  --bs-btn-hover-bg: var(--accent-dim);
  --bs-btn-hover-border-color: var(--accent-dim);
  --bs-btn-focus-shadow-rgb: var(--accent-rgb);
  --bs-btn-active-color: var(--accent-on);
  --bs-btn-active-bg: var(--accent-dim);
  --bs-btn-active-border-color: var(--accent-dim);
  --bs-btn-disabled-color: var(--accent-on);
  --bs-btn-disabled-bg: var(--accent);
  --bs-btn-disabled-border-color: var(--accent);
  font-weight: 600;
}

.btn-success {
  --bs-btn-color: var(--green-on);
  --bs-btn-bg: var(--green);
  --bs-btn-border-color: var(--green);
  --bs-btn-hover-color: var(--green-on);
  --bs-btn-hover-bg: var(--green-dim);
  --bs-btn-hover-border-color: var(--green-dim);
  --bs-btn-focus-shadow-rgb: 34, 197, 94;
  --bs-btn-active-color: var(--green-on);
  --bs-btn-active-bg: var(--green-dim);
  --bs-btn-active-border-color: var(--green-dim);
  --bs-btn-disabled-color: var(--green-on);
  --bs-btn-disabled-bg: var(--green);
  --bs-btn-disabled-border-color: var(--green);
  font-weight: 600;
}

.btn-outline-primary {
  --bs-btn-color: var(--accent-light);
  --bs-btn-border-color: var(--border-accent);
  --bs-btn-hover-color: var(--link-hover);
  --bs-btn-hover-bg: rgba(var(--accent-rgb), 0.1);
  --bs-btn-hover-border-color: var(--accent-light);
  --bs-btn-focus-shadow-rgb: var(--accent-rgb);
  --bs-btn-active-color: var(--link-hover);
  --bs-btn-active-bg: rgba(var(--accent-rgb), 0.15);
  --bs-btn-active-border-color: var(--accent-light);
  --bs-btn-disabled-color: var(--accent-light);
  --bs-btn-disabled-border-color: var(--border-accent);
}

/* 43 uses (filters, Cancel, Back): Bootstrap's #6c757d was 3.68:1 on the
   dark card (audit TH-4). */
.btn-outline-secondary {
  --bs-btn-color: var(--text-secondary);
  --bs-btn-border-color: var(--border);
  --bs-btn-hover-color: var(--text-primary);
  --bs-btn-hover-bg: var(--bg-elevated);
  --bs-btn-hover-border-color: var(--text-muted);
  --bs-btn-focus-shadow-rgb: 148, 163, 184;
  --bs-btn-active-color: var(--text-primary);
  --bs-btn-active-bg: var(--bg-elevated);
  --bs-btn-active-border-color: var(--text-secondary);
  --bs-btn-disabled-color: var(--text-muted);
  --bs-btn-disabled-border-color: var(--border);
}

.btn-outline-danger {
  --bs-btn-color: var(--red);
  --bs-btn-border-color: rgba(239, 68, 68, 0.3);
  --bs-btn-hover-color: var(--red);
  --bs-btn-hover-bg: rgba(239, 68, 68, 0.1);
  --bs-btn-hover-border-color: var(--red);
  --bs-btn-focus-shadow-rgb: 239, 68, 68;
  --bs-btn-active-color: var(--red);
  --bs-btn-active-bg: rgba(239, 68, 68, 0.15);
  --bs-btn-active-border-color: var(--red);
  --bs-btn-disabled-color: var(--red);
  --bs-btn-disabled-border-color: rgba(239, 68, 68, 0.3);
}

.btn-outline-success {
  --bs-btn-color: var(--green);
  --bs-btn-border-color: rgba(34, 197, 94, 0.3);
  --bs-btn-hover-color: var(--green);
  --bs-btn-hover-bg: rgba(34, 197, 94, 0.1);
  --bs-btn-hover-border-color: var(--green);
  --bs-btn-focus-shadow-rgb: 34, 197, 94;
  --bs-btn-active-color: var(--green);
  --bs-btn-active-bg: rgba(34, 197, 94, 0.15);
  --bs-btn-active-border-color: var(--green);
  --bs-btn-disabled-color: var(--green);
  --bs-btn-disabled-border-color: rgba(34, 197, 94, 0.3);
}

.btn-outline-info {
  --bs-btn-color: var(--sky);
  --bs-btn-border-color: rgba(56, 189, 248, 0.3);
  --bs-btn-hover-color: var(--sky);
  --bs-btn-hover-bg: rgba(56, 189, 248, 0.1);
  --bs-btn-hover-border-color: var(--sky);
  --bs-btn-focus-shadow-rgb: 56, 189, 248;
  --bs-btn-active-color: var(--sky);
  --bs-btn-active-bg: rgba(56, 189, 248, 0.15);
  --bs-btn-active-border-color: var(--sky);
  --bs-btn-disabled-color: var(--sky);
  --bs-btn-disabled-border-color: rgba(56, 189, 248, 0.3);
}

/* Ready Up's End Session / "We didn't play", Wrap up, the review prompts:
   Bootstrap's #ffc107 was 1.62:1 on a light card (audit TH-1). --orange is
   AA in both modes (#9a4508 in light). */
.btn-outline-warning {
  --bs-btn-color: var(--orange);
  --bs-btn-border-color: rgba(245, 158, 11, 0.4);
  --bs-btn-hover-color: var(--orange);
  --bs-btn-hover-bg: rgba(245, 158, 11, 0.1);
  --bs-btn-hover-border-color: var(--orange);
  --bs-btn-focus-shadow-rgb: 245, 158, 11;
  --bs-btn-active-color: var(--orange);
  --bs-btn-active-bg: rgba(245, 158, 11, 0.15);
  --bs-btn-active-border-color: var(--orange);
  --bs-btn-disabled-color: var(--orange);
  --bs-btn-disabled-border-color: rgba(245, 158, 11, 0.4);
}

.btn-outline-light {
  --bs-btn-color: var(--text-secondary);
  --bs-btn-border-color: var(--border);
  --bs-btn-hover-color: var(--text-primary);
  --bs-btn-hover-bg: var(--bg-card-hover);
  --bs-btn-hover-border-color: var(--text-muted);
  --bs-btn-focus-shadow-rgb: 148, 163, 184;
  --bs-btn-active-color: var(--text-primary);
  --bs-btn-active-bg: var(--bg-elevated);
  --bs-btn-active-border-color: var(--text-muted);
  --bs-btn-disabled-color: var(--text-muted);
  --bs-btn-disabled-border-color: var(--border);
}

/* ── Keyboard focus: one ring for everything you can tab to (audit TH-8) ──
   Bootstrap sets outline: 0 on .btn / .nav-link / .btn-close :focus-visible
   and draws a box-shadow instead — from --bs-btn-focus-shadow-rgb, which a
   plain .btn (Ready Up's Steam / Deal / Info / Lock In, the navbar check-in
   toggles) never defines, so those showed no focus at all. The ring is
   --focus-ring (the text-safe accent, >= 3:1 on every surface). */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
.btn:focus-visible,
.nav-link:focus-visible,
.btn-close:focus-visible,
.navbar-toggler:focus-visible,
.dropdown-item:focus-visible,
.form-check-input:focus-visible,
[role="button"]:focus-visible,
[tabindex]:not([tabindex="-1"]):focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
}
/* Bootstrap's blue rings on these, replaced by the outline above
   (.btn-link keeps Bootstrap's --bs-btn-focus-shadow-rgb 49,132,253) */
.nav-link:focus-visible,
.btn-close:focus-visible,
.btn-link:focus-visible {
  box-shadow: none;
}
/* Menu items fill the menu, and the notification list scrolls inside an
   overflow box that clips anything outside an item: draw the ring inside. */
.dropdown-item:focus-visible {
  outline-offset: -2px;
}

/* Quick action buttons (dashboard) */
.action-btn {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 10px 14px;
  margin-bottom: 6px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text-primary);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  transition: all 0.15s;
}

.action-btn i {
  color: var(--text-muted);
  width: 18px;
  font-size: 0.9rem;
  transition: color 0.15s;
}

.action-btn:hover {
  background: var(--bg-card-hover);
  border-color: var(--border-accent);
  color: var(--accent-light);
}

.action-btn:hover i { color: var(--accent-light); }


/* ==========================================================================
   Badges
   ========================================================================== */

.badge-players {
  background: rgba(var(--accent-rgb), 0.1) !important;
  color: var(--accent-light) !important;
  border: 1px solid rgba(var(--accent-rgb), 0.15);
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.65rem;
  font-weight: 500;
  padding: 3px 7px;
  border-radius: 5px;
}

.badge-vr-optional {
  background: rgba(245, 158, 11, 0.1) !important;
  color: var(--orange) !important;
  border: 1px solid rgba(245, 158, 11, 0.15);
}

.badge-vr-required {
  background: rgba(245, 158, 11, 0.15) !important;
  color: var(--orange) !important;
  border: 1px solid rgba(245, 158, 11, 0.25);
}

.badge-no-vr {
  background: rgba(71, 85, 105, 0.15) !important;
  color: var(--text-secondary) !important;
  border: 1px solid rgba(71, 85, 105, 0.2);
}

.badge-platform {
  background: rgba(148, 163, 184, 0.08) !important;
  color: var(--text-secondary) !important;
  border: 1px solid rgba(148, 163, 184, 0.12);
}

.badge-ideal {
  background: rgba(34, 197, 94, 0.1) !important;
  color: var(--green) !important;
  border: 1px solid rgba(34, 197, 94, 0.15);
}

.badge-free {
  background: rgba(56, 189, 248, 0.1) !important;
  color: var(--sky) !important;
  border: 1px solid rgba(56, 189, 248, 0.2);
}

.badge-deal {
  background: rgba(34, 197, 94, 0.15) !important;
  color: var(--green) !important;
  border: 1px solid rgba(34, 197, 94, 0.25);
  font-weight: 700;
  font-size: 0.85rem;
  padding: 4px 10px;
}

.badge-deal-sm {
  background: rgba(34, 197, 94, 0.15) !important;
  color: var(--green) !important;
  border: 1px solid rgba(34, 197, 94, 0.25);
  font-weight: 700;
  font-size: 0.7rem;
  padding: 2px 6px;
}

.badge-price {
  background: rgba(34, 197, 94, 0.08) !important;
  color: var(--green) !important;
  border: 1px solid rgba(34, 197, 94, 0.15);
  font-weight: 600;
  font-size: 0.7rem;
}

.badge-hist-low {
  background: rgba(245, 158, 11, 0.15) !important;
  color: var(--amber) !important;
  border: 1px solid rgba(245, 158, 11, 0.25);
  font-weight: 600;
  font-size: 0.7rem;
}


/* Wishlist buttons and My List's Wishlist tab (.active-purple = the open
   tab). Through --bs-btn-* like the other buttons, so the pressed state
   isn't Bootstrap's. White on the lavender tint is fine on a dark card but
   1.5:1 on a light one: light mode uses a deep purple (audit TH-1). */
.btn-outline-purple {
  --bs-btn-color: var(--purple);
  --bs-btn-border-color: rgba(168, 85, 247, 0.4);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: rgba(168, 85, 247, 0.2);
  --bs-btn-hover-border-color: var(--purple);
  --bs-btn-focus-shadow-rgb: 168, 85, 247;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: rgba(168, 85, 247, 0.25);
  --bs-btn-active-border-color: var(--purple);
  --bs-btn-disabled-color: var(--purple);
  --bs-btn-disabled-border-color: rgba(168, 85, 247, 0.4);
}
.btn-outline-purple.active-purple {
  --bs-btn-color: #fff;
  --bs-btn-bg: rgba(168, 85, 247, 0.25);
  --bs-btn-border-color: var(--purple);
}
[data-mode="light"] .btn-outline-purple {
  --bs-btn-hover-color: #5b21b6;
  --bs-btn-active-color: #5b21b6;
}
[data-mode="light"] .btn-outline-purple.active-purple {
  --bs-btn-color: #5b21b6;
}

/* Override Bootstrap badge defaults for our context */

/* My List's All / Owned / Wishlist counts, the platform badges on Games,
   Crew Details' labels: Bootstrap's .badge keeps #fff text, and .bg-dark is
   remapped to the card colour — white on white in light mode (audit TH-1). */
.badge.bg-dark {
  color: var(--text-primary) !important;
  background: var(--bg-elevated) !important;
}

.badge.bg-secondary {
  background: var(--bg-elevated) !important;
  color: var(--text-secondary) !important;
}

.badge.bg-success {
  background: rgba(34, 197, 94, 0.15) !important;
  color: var(--green) !important;
}

.badge.bg-info {
  background: rgba(56, 189, 248, 0.1) !important;
  color: var(--sky) !important;
}

.badge.bg-warning {
  background: rgba(245, 158, 11, 0.15) !important;
  color: var(--orange) !important;
}

/* Bootstrap's .bg-danger is the bridged rgb (#ef4444) under .badge's #fff
   text: 3.76:1 (Duplicate Finder's count) */
.badge.bg-danger {
  background: rgba(239, 68, 68, 0.15) !important;
  color: var(--red) !important;
}


/* ==========================================================================
   Forms
   ========================================================================== */

/* background-color, never the `background` shorthand: the shorthand also
   resets background-image, which on .form-select is Bootstrap's chevron
   (var(--bs-form-select-bg-img); .form-select has appearance: none), so
   every select lost its arrow (audit TH-3). The chevron's colour follows
   <html data-bs-theme> (light #343a40 / dark #dee2e6). */
.form-control,
.form-select {
  background-color: var(--bg-surface);
  border: 1px solid var(--input-border);
  color: var(--text-primary);
  border-radius: 8px;
  font-size: 0.875rem;
  padding: 0.5rem 0.75rem;
}

/* Room for the chevron (the shared padding above took Bootstrap's away) */
.form-select {
  padding-right: 2.25rem;
}

.form-control:focus,
.form-select:focus {
  background-color: var(--bg-card);
  border-color: var(--accent);
  color: var(--text-primary);
  box-shadow: 0 0 0 3px rgba(var(--accent-rgb), 0.15);
}

.form-control::placeholder { color: var(--text-muted); }

/* ── Checkboxes, radios, switches (audit TH-3) ──
   36e48f6 deleted the old rules, leaving Bootstrap's: unchecked =
   --bs-body-bg with a --border edge (about 1.1:1 on a card, so "Free to
   Play", "Show hidden" and the profile switches looked blank), checked =
   hard-coded #0d6efd. The muted edge is >= 3:1 on every card. */
.form-check-input {
  background-color: var(--bg-elevated);
  border-color: var(--text-muted);
  cursor: pointer;
}
.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
  background-color: var(--accent);
  border-color: var(--accent);
}
.form-check-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 0.25rem rgba(var(--accent-rgb), 0.25);
}
/* The tick, the indeterminate dash, the radio dot and the "on" switch knob
   are Bootstrap's white SVGs. On the bright dark-mode forest / sunrise /
   pride fills white is 2.2-2.8:1, so those draw them in the accent's
   --accent-on colour (a data: URI can't read a variable). Slate and storm
   (white --accent-on) and every light accent keep Bootstrap's white.
   (0,5,0) beats Bootstrap's .form-check-input:checked[type=checkbox] and
   .form-switch .form-check-input:checked (0,3,0). */
[data-accent="forest"]:not([data-mode="light"]) .form-check-input:checked[type="checkbox"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23062416' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
[data-accent="forest"]:not([data-mode="light"]) .form-check-input[type="checkbox"]:indeterminate {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23062416' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
[data-accent="forest"]:not([data-mode="light"]) .form-check-input:checked[type="radio"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23062416'/%3e%3c/svg%3e");
}
[data-accent="forest"]:not([data-mode="light"]) .form-switch .form-check-input:checked {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23062416'/%3e%3c/svg%3e");
}
[data-accent="sunrise"]:not([data-mode="light"]) .form-check-input:checked[type="checkbox"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%232a1408' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
[data-accent="sunrise"]:not([data-mode="light"]) .form-check-input[type="checkbox"]:indeterminate {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%232a1408' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
[data-accent="sunrise"]:not([data-mode="light"]) .form-check-input:checked[type="radio"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%232a1408'/%3e%3c/svg%3e");
}
[data-accent="sunrise"]:not([data-mode="light"]) .form-switch .form-check-input:checked {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%232a1408'/%3e%3c/svg%3e");
}
[data-accent="pride"]:not([data-mode="light"]) .form-check-input:checked[type="checkbox"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%232a0a2e' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}
[data-accent="pride"]:not([data-mode="light"]) .form-check-input[type="checkbox"]:indeterminate {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%232a0a2e' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}
[data-accent="pride"]:not([data-mode="light"]) .form-check-input:checked[type="radio"] {
  --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%232a0a2e'/%3e%3c/svg%3e");
}
[data-accent="pride"]:not([data-mode="light"]) .form-switch .form-check-input:checked {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%232a0a2e'/%3e%3c/svg%3e");
}
/* Switch knob when off: Bootstrap draws it rgba(0,0,0,.25) / under
   data-bs-theme=dark rgba(255,255,255,.25) — near-invisible on our cards —
   and #86b7fe while focused. Muted text colour instead (a data: URI can't
   read a variable, so one per mode). Specificity 0,5,0 to beat Bootstrap's
   [data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus). */
[data-mode] .form-switch .form-check-input:not(:checked):not(:focus),
[data-mode] .form-switch .form-check-input:not(:checked):focus {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%237c8aa0'/%3e%3c/svg%3e");
}
[data-mode="light"] .form-switch .form-check-input:not(:checked):not(:focus),
[data-mode="light"] .form-switch .form-check-input:not(:checked):focus {
  --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%235c6b82'/%3e%3c/svg%3e");
}

/* Range sliders: Bootstrap's thumb is #0d6efd. One rule per vendor
   pseudo-element — a selector list mixing ::-webkit- and ::-moz- is dropped
   whole by every browser (each rejects the other's pseudo-element). */
.form-range::-webkit-slider-thumb { background-color: var(--accent); }
.form-range::-webkit-slider-thumb:active { background-color: var(--accent-light); }
.form-range::-moz-range-thumb { background-color: var(--accent); }
.form-range::-moz-range-thumb:active { background-color: var(--accent-light); }
.form-range:focus::-webkit-slider-thumb {
  box-shadow: 0 0 0 1px var(--bg-deep), 0 0 0 0.25rem rgba(var(--accent-rgb), 0.25);
}
.form-range:focus::-moz-range-thumb {
  box-shadow: 0 0 0 1px var(--bg-deep), 0 0 0 0.25rem rgba(var(--accent-rgb), 0.25);
}

.form-label {
  color: var(--text-secondary);
  font-size: 0.85rem;
  font-weight: 500;
}

.form-text {
  color: var(--text-muted);
  font-size: 0.8rem;
}

.invalid-feedback { color: var(--red); }


/* ==========================================================================
   Alerts
   ========================================================================== */

/* Each variant's 8% tint sits on an opaque --bg-surface base: a bare tint
   let the Festival / aurora glow through under flash messages (dark
   Festival's red text fell to about 3.1:1). */
.alert {
  border-radius: 8px;
  border: 1px solid;
  font-size: 0.875rem;
}

.alert-success {
  background: linear-gradient(rgba(34, 197, 94, 0.08), rgba(34, 197, 94, 0.08)), var(--bg-surface);
  border-color: rgba(34, 197, 94, 0.2);
  color: var(--green);
}

/* .alert-error: Django's messages.error() tag (no MESSAGE_TAGS mapping), which
   Bootstrap doesn't style — those flashes rendered as plain text. */
.alert-danger,
.alert-error {
  background: linear-gradient(rgba(239, 68, 68, 0.08), rgba(239, 68, 68, 0.08)), var(--bg-surface);
  border-color: rgba(239, 68, 68, 0.2);
  color: var(--red);
}

.alert-info {
  background: linear-gradient(rgba(56, 189, 248, 0.08), rgba(56, 189, 248, 0.08)), var(--bg-surface);
  border-color: rgba(56, 189, 248, 0.2);
  color: var(--sky);
}

.alert-warning {
  background: linear-gradient(rgba(245, 158, 11, 0.08), rgba(245, 158, 11, 0.08)), var(--bg-surface);
  border-color: rgba(245, 158, 11, 0.2);
  color: var(--orange);
}

/* Close X: Bootstrap draws it black, and white only under
   <html data-bs-theme="dark"> (base.html sets it from the mode, audit TH-3).
   Its mouse-focus glow is the accent, not #0d6efd. */
.btn-close {
  --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(var(--accent-rgb), 0.25);
}


/* ==========================================================================
   Tables (admin: review queue, cover art, duplicates)
   ==========================================================================
   Transparent on the card, mode text colours. They used .table-dark, which
   stayed dark in light mode while .text-white became dark text (1.05:1,
   audit TH-1); templates no longer use .table-dark. */

.table {
  --bs-table-bg: transparent;
  --bs-table-color: var(--text-primary);
  --bs-table-border-color: var(--border);
  --bs-table-striped-color: var(--text-primary);
  --bs-table-striped-bg: rgba(var(--accent-rgb), 0.03);
  --bs-table-hover-color: var(--text-primary);
  --bs-table-hover-bg: rgba(var(--accent-rgb), 0.06);
  --bs-table-active-color: var(--text-primary);
  --bs-table-active-bg: rgba(var(--accent-rgb), 0.1);
}


/* ==========================================================================
   Game Cards (catalog, my list, coordination)
   ========================================================================== */

.game-card { cursor: pointer; }

.game-card .card-img-top {
  height: 160px;
  object-fit: cover;
}

.game-card .card-img-placeholder {
  height: 160px;
  background: linear-gradient(135deg, var(--bg-surface), var(--bg-elevated));
  display: flex;
  align-items: center;
  justify-content: center;
  position: relative;
}

.game-card .card-img-placeholder i {
  color: var(--text-muted);
  font-size: 2.5rem;
}

/* Fade gradient at bottom of art */
.game-card .card-img-placeholder::after,
.game-card .img-wrapper::after {
  content: '';
  position: absolute;
  bottom: 0; left: 0; right: 0;
  height: 40px;
  background: linear-gradient(transparent, var(--bg-card));
}

.game-card .img-wrapper {
  position: relative;
  overflow: hidden;
}

.game-card .card-title a {
  color: var(--text-primary);
  text-decoration: none;
  font-weight: 600;
  letter-spacing: -0.2px;
}

.game-card .card-title a:hover { color: var(--accent-light); }


/* ==========================================================================
   Game Detail Page
   ========================================================================== */

.detail-player-num {
  font-family: 'JetBrains Mono', monospace;
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
}

.detail-player-num-primary { color: var(--accent-light); }
.detail-player-num-green { color: var(--green); }
.detail-player-num-sky { color: var(--sky); }


/* ==========================================================================
   Breadcrumbs
   ========================================================================== */

.breadcrumb {
  background: none;
  padding: 0;
  font-size: 0.85rem;
}

.breadcrumb-item a { color: var(--text-secondary); text-decoration: none; }
.breadcrumb-item a:hover { color: var(--accent-light); }
.breadcrumb-item.active { color: var(--text-muted); }
.breadcrumb-item + .breadcrumb-item::before { color: var(--text-muted); }


/* ==========================================================================
   Section Dividers
   ========================================================================== */

.section-divider {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-secondary);
  text-transform: uppercase;
  letter-spacing: 0.5px;
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 2rem;
  margin-bottom: 1rem;
}

.section-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}


/* ==========================================================================
   Footer
   ========================================================================== */

footer {
  background: transparent !important;
  border-top: 1px solid var(--border);
  color: var(--text-muted) !important;
}

footer small {
  color: var(--text-muted) !important;
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.5px;
}


/* ==========================================================================
   Auth Pages (login, register)
   ========================================================================== */

.auth-card {
  max-width: 420px;
  margin: 2rem auto;
}

.auth-card .card-body { padding: 2rem; }

.auth-card h2 {
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.5px;
}

.auth-card hr {
  border-color: var(--border);
  opacity: 1;
}


/* ==========================================================================
   Empty States
   ========================================================================== */

.empty-state {
  text-align: center;
  padding: 3rem 1rem;
  color: var(--text-muted);
}

.empty-state i {
  font-size: 3rem;
  display: block;
  margin-bottom: 1rem;
}

.empty-state p { margin-top: 0.5rem; }


/* ==========================================================================
   Utility Overrides
   ========================================================================== */

.text-muted { color: var(--text-muted) !important; }
.text-primary { color: var(--accent-light) !important; }
.text-success { color: var(--green) !important; }
.text-info { color: var(--sky) !important; }
.text-warning { color: var(--orange) !important; }
.text-danger { color: var(--red) !important; }
.text-light { color: var(--text-primary) !important; }
.text-white { color: var(--text-primary) !important; }
.text-secondary { color: var(--text-secondary) !important; }

.border-secondary { border-color: var(--border) !important; }
.border-success { border-color: var(--border-green) !important; }
/* Bootstrap's is the bridged dark amber (#f59e0b): 2.1:1 on a light card */
.border-warning { border-color: var(--orange) !important; }

.bg-dark { background: var(--bg-card) !important; }
.bg-secondary { background: var(--bg-elevated) !important; }

h1, h2, h3, h4, h5, h6 { letter-spacing: -0.3px; }
h1 { font-weight: 700; font-size: 1.5rem; }
h5 { font-weight: 600; }


/* ==========================================================================
   Scrollbar
   ========================================================================== */

::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--bg-deep); }
::-webkit-scrollbar-thumb {
  background: var(--bg-elevated);
  border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }


/* ==========================================================================
   Mobile Responsive
   ========================================================================== */

@media (max-width: 768px) {
  .stat-card { gap: 10px; }
  .stat-icon { width: 36px; height: 36px; font-size: 0.9rem; }
  .stat-number { font-size: 1.4rem; }
  h1 { font-size: 1.3rem; }
}


/* ==========================================================================
   Notification Toasts
   ========================================================================== */

.notification-toast {
  background: var(--bg-elevated);
  border: 1px solid var(--border-accent);
  border-radius: 10px;
  padding: 12px 16px;
  min-width: 280px;
  max-width: 360px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(var(--accent-rgb), 0.08);
  backdrop-filter: blur(12px);
  opacity: 0;
  transform: translateX(40px);
  transition: opacity 0.3s ease, transform 0.3s ease;
}

.notification-toast.show {
  opacity: 1;
  transform: translateX(0);
}

.notification-toast.hide {
  opacity: 0;
  transform: translateX(40px);
}


/* ==========================================================================
   Notification Bell
   ========================================================================== */

#notification-bell .notification-bell-trigger {
  position: relative;
  padding: 4px 8px;
}

#notification-bell .notification-bell-trigger:hover i {
  color: var(--amber) !important;
}

.notification-dropdown .dropdown-item:hover {
  background: var(--bg-card-hover);
}

.notification-dropdown .dropdown-item:active {
  background: var(--bg-elevated);
}

/* The history page's rows (/notifications/, partials/notification_row.html) */
.notification-row {
  color: var(--text-primary);
  text-decoration: none;
  font-size: 0.88rem;
  border-bottom: 1px solid var(--border);
}

.notification-row:last-child {
  border-bottom: 0;
}

.notification-row.unread {
  background: rgba(var(--accent-rgb), 0.04);
}

a.notification-row:hover {
  color: var(--text-primary);
  background: var(--bg-card-hover);
  /* The meta line (type · age) is muted text: on the hover background in
     dark / black it fell to 4.4:1, so it reads as secondary text here. */
  --text-muted: var(--text-secondary);
}

a.notification-row:focus-visible {
  outline-offset: -2px;
}


/* ==========================================================================
   Crew Dropdown Check-In Toggles
   ========================================================================== */

.crew-checkin-toggle {
  transition: all 0.15s ease;
}

.crew-checkin-toggle:hover {
  transform: scale(1.15);
  filter: brightness(1.3);
}


/* ==========================================================================
   Steam Quick-Add Badges
   ========================================================================== */

#steam-badge-wrapper .badge:hover,
[id^="steam-badge-"] button:hover {
  background: rgba(34,197,94,0.1) !important;
  color: var(--green) !important;
  border-color: rgba(34,197,94,0.3) !important;
}


/* ==========================================================================
   Appearance Axes — Mode / Accent / Background / Motion
   ==========================================================================
   Body carries data-mode, data-accent, data-bg, and data-motion. The :root
   block above is the dark + slate + aurora baseline; the rules below override
   tokens for the other axis values. Token names (--bg-deep, --accent, etc.)
   stay the same so existing rules and templates require no edits.

   Legacy theme → axes mapping (core/appearance.py LEGACY_THEME_AXES):
     theme=dark     → mode=dark  + accent=slate  + bg=aurora
     theme=light    → mode=light + accent=slate  + bg=aurora
     theme=midnight → mode=black + accent=storm  + bg=flat
     theme=pride    → mode=dark  + accent=pride  + bg=aurora
   ========================================================================== */

/* ---------- Mode: light ---------- */
[data-mode="light"] {
  --bg-deep: #f0f2f5;
  --bg-surface: #ffffff;
  --bg-card: rgba(255, 255, 255, 0.92);
  --bg-card-hover: rgba(245, 247, 250, 0.95);
  --bg-elevated: rgba(235, 238, 244, 0.9);
  --text-primary: #1e293b;
  --text-secondary: #475569;
  /* #5c6b82, not the audit's #64748b: that is 4.72 on a card but 4.24 on
     the #f0f2f5 page, where page subtitles sit. */
  --text-muted: #5c6b82;
  --border: #e2e8f0;
  --input-border: #7d889b;
  --border-green: rgba(22, 163, 74, 0.2);
  --navbar-bg: rgba(255, 255, 255, 0.85);
  --link-hover: var(--accent-dim);

  /* Semantic colors darkened for legibility on a light surface: each is
     >= 4.5:1 as text on the card AND the #f0f2f5 page, including under the
     outline buttons' hover / pressed tints (audit TH-4; the audit's #15803d /
     #b45309 were 4.47 / 4.48 on the page) */
  --green: #137034;
  --green-dim: #166534;
  --green-on: #ffffff;
  --orange: #9a4508;
  --red: #b91c1c;
  --sky: #0369a1;
  --amber: #9a4508;
  /* --emerald / --purple also sit on their own .1-.15 tints on the page
     (the Owned / Wishlisted badges on a game page): #047857 / #7c3aed were
     4.46 / 4.42 there */
  --emerald: #04704f;
  --purple: #7130e0;
  --fill-on: #ffffff;
  /* <code> (Discord commands, What's New): Bootstrap's light #d63384 is
     4.46:1 on the card. Dark modes keep Bootstrap's #e685b5 (html has
     data-bs-theme="dark"). */
  --bs-code-color: #a3175f;
}

/* ---------- Mode: black (audit TH-6) ----------
   Pure black with blue-tinted text: the Midnight look from before 36e48f6
   (legacy theme=midnight and the Midnight preset map here). The accent
   blocks below are dark-mode values ([data-accent="x"], no mode), so they
   serve black too; html gets data-bs-theme="dark". --text-muted #6b82a8,
   not the old #3b5580 (about 2.8:1 on the card). The semantic colours stay
   the dark-mode ones. */
[data-mode="black"] {
  --bg-deep: #000000;
  --bg-surface: #0a0e18;
  --bg-card: rgba(14, 19, 32, 0.9);
  --bg-card-hover: rgba(20, 27, 46, 0.92);
  --bg-elevated: rgba(26, 35, 58, 0.9);
  --text-primary: #dbeafe;
  --text-secondary: #7c9ed6;
  --text-muted: #6b82a8;
  --border: #152040;
  --input-border: #4f6696;
  --border-green: rgba(34, 197, 94, 0.2);
  --navbar-bg: rgba(0, 0, 0, 0.9);
}


/* ---------- Accents ----------
   Dark values ([data-accent="x"]) also serve any non-light mode; light mode
   has its own block for each accent. In light mode --accent-light is the
   accent AS TEXT (links, .text-primary, outline buttons, ~35 inline
   var(--accent-light) uses in templates), so it is the darker, text-safe
   shade there, not a lighter one (audit TH-4). Change --accent-rgb with
   --accent. */

/* ---------- Accent: slate (default — dark values in :root) ---------- */
[data-mode="light"][data-accent="slate"] {
  --accent: #4f46e5;
  --accent-light: #4338ca;
  --accent-dim: #3730a3;
  --accent-rgb: 79, 70, 229;
  --accent-on: #ffffff;
  --border-accent: rgba(79, 70, 229, 0.2);
}

/* ---------- Accent: storm (deep blue — Midnight's accent) ----------
   #2563eb, not #3b82f6: white on #3b82f6 was 3.68:1. */
[data-accent="storm"] {
  --accent: #2563eb;
  --accent-light: #60a5fa;
  --accent-dim: #1d4ed8;
  --accent-rgb: 37, 99, 235;
  --accent-on: #ffffff;
  --border-accent: rgba(37, 99, 235, 0.2);
}
[data-mode="light"][data-accent="storm"] {
  --accent: #2563eb;
  --accent-light: #1d4ed8;
  --accent-dim: #1e40af;
  --accent-rgb: 37, 99, 235;
  --border-accent: rgba(37, 99, 235, 0.2);
}

/* ---------- Accent: forest (calm green) ---------- */
[data-accent="forest"] {
  --accent: #22c97c;
  --accent-light: #4cd99a;
  --accent-dim: #16a763;
  --accent-rgb: 34, 201, 124;
  --accent-on: #062416;
  --border-accent: rgba(34, 201, 124, 0.25);
}
/* Light --accent-light is darker than the fill: the "time until" badges and
   .crew-badge draw it on rgba(var(--accent-rgb), .15), where #0f7a47 was
   4.33:1. --accent-dim stays darker still, so link hover darkens. */
[data-mode="light"][data-accent="forest"] {
  --accent: #0f7a47;
  --accent-light: #0c6b3f;
  --accent-dim: #095c36;
  --accent-rgb: 15, 122, 71;
  --accent-on: #ffffff;
  --border-accent: rgba(15, 122, 71, 0.2);
}

/* ---------- Accent: sunrise (warm coral) ----------
   Dark --accent-dim #e85d23, not #d94d18: the dark button text on the hover
   fill was 4.18:1. */
[data-accent="sunrise"] {
  --accent: #ff6f3c;
  --accent-light: #ff9a55;
  --accent-dim: #e85d23;
  --accent-rgb: 255, 111, 60;
  --accent-on: #2a1408;
  --border-accent: rgba(255, 111, 60, 0.25);
}
/* Darker text shade for the same reason as forest (#b8460f was 4.27:1 on
   its own tint) */
[data-mode="light"][data-accent="sunrise"] {
  --accent: #b8460f;
  --accent-light: #a33c0c;
  --accent-dim: #8f340b;
  --accent-rgb: 184, 70, 15;
  --accent-on: #ffffff;
  --border-accent: rgba(184, 70, 15, 0.2);
}

/* ---------- Accent: pride (pink/magenta — Pride migration target) ----------
   Dark text on the pride fill: white on #e879f9 was 2.46:1. */
[data-accent="pride"] {
  --accent: #e879f9;
  --accent-light: #f0abfc;
  --accent-dim: #d946ef;
  --accent-rgb: 232, 121, 249;
  --accent-on: #2a0a2e;
  --border-accent: rgba(232, 121, 249, 0.25);
}
[data-mode="light"][data-accent="pride"] {
  --accent: #c739a4;
  --accent-light: #a3268a;
  --accent-dim: #86197a;
  --accent-rgb: 199, 57, 164;
  --accent-on: #ffffff;
  --border-accent: rgba(199, 57, 164, 0.2);
}


/* ==========================================================================
   Mode + Accent component overrides (navbar / dropdown / nav-link active)
   ========================================================================== */

/* The navbar's background is --navbar-bg (set per mode in the token blocks)
   and its border is --border, so light mode needs no navbar rule of its own. */
[data-mode="light"] .navbar-brand,
[data-mode="light"] .nav-link,
[data-mode="light"] .nav-user-text {
  color: var(--text-primary) !important;
}
[data-mode="light"] .btn-nav-logout {
  color: var(--text-secondary);
  border-color: var(--border);
}
[data-mode="light"] .btn-nav-logout:hover {
  color: var(--text-primary);
  background: var(--bg-elevated);
}
[data-mode="light"] .btn-close { filter: none; }
[data-mode="light"] footer { color: var(--text-muted); }
/* The page's own scrollbar is styled from body itself (no [data-mode]
   ancestor), hence the compound selectors next to the descendant ones */
[data-mode="light"]::-webkit-scrollbar-track,
[data-mode="light"] ::-webkit-scrollbar-track { background: var(--bg-deep); }
[data-mode="light"]::-webkit-scrollbar-thumb,
[data-mode="light"] ::-webkit-scrollbar-thumb { background: #cbd5e1; }
[data-mode="light"]::-webkit-scrollbar-thumb:hover,
[data-mode="light"] ::-webkit-scrollbar-thumb:hover { background: #94a3b8; }

/* Pride keeps the rainbow border under the navbar */
[data-accent="pride"] .navbar {
  border-bottom: 2px solid transparent;
  border-image: linear-gradient(90deg,
    #ef4444 0%, #f59e0b 17%, #22c55e 33%,
    #38bdf8 50%, #6366f1 67%, #a855f7 83%, #ec4899 100%) 1;
}

/* Active nav-link uses the current accent */
.navbar-nav .nav-link.active,
.navbar-nav .nav-link[aria-current="page"] {
  background: rgba(var(--accent-rgb), 0.1);
}


/* ==========================================================================
   Motion preference (audit TH-5)
   ==========================================================================
   data-motion="reduced" (the Studio's "Reduced"), and "system" (Match
   device, the default) while the OS asks for reduced motion:
   - every animation runs once, at once. animation-iteration-count: 1
     matters: a 0.001s *infinite* animation cycles ~1000 times a second and
     jitters instead of stopping;
   - transitions are near-instant;
   - the Festival drift, the Pride·Festival navbar shimmer and the confetti
     stop (the confetti is hidden; it is still built, so it is there when
     motion is turned back on);
   - loading spinners keep turning, slowly (Bootstrap's own reduced-motion
     rule does the same): a frozen spinner reads as a hung page;
   - no smooth scrolling (Bootstrap turns it on for :root; Ready Up's
     "Surprise Me" scroll asks window.redeupCalmMotion(), base.html).
   The @media block repeats the "reduced" rules for "system" — keep the two
   in step (core/tests/test_theme_tokens.py compares them).
   ========================================================================== */

body[data-motion="reduced"] *,
body[data-motion="reduced"] *::before,
body[data-motion="reduced"] *::after {
  animation-duration: 0.001s !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.05s !important;
}
body[data-motion="reduced"] .bg-effects,
body[data-motion="reduced"] .navbar::before {
  animation: none !important;
}
body[data-motion="reduced"] .festival-confetti span {
  display: none;
}
body[data-motion="reduced"] .spinner-border,
body[data-motion="reduced"] .spinner-grow {
  animation-duration: 1.5s !important;
  animation-iteration-count: infinite !important;
}
:root:has(body[data-motion="reduced"]) {
  scroll-behavior: auto;
}

@media (prefers-reduced-motion: reduce) {
  body[data-motion="system"] *,
  body[data-motion="system"] *::before,
  body[data-motion="system"] *::after {
    animation-duration: 0.001s !important;
    animation-delay: 0s !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.05s !important;
  }
  body[data-motion="system"] .bg-effects,
  body[data-motion="system"] .navbar::before {
    animation: none !important;
  }
  body[data-motion="system"] .festival-confetti span {
    display: none;
  }
  body[data-motion="system"] .spinner-border,
  body[data-motion="system"] .spinner-grow {
    animation-duration: 1.5s !important;
    animation-iteration-count: infinite !important;
  }
  :root:has(body[data-motion="system"]) {
    scroll-behavior: auto;
  }
}



/* ==========================================================================
   Preferences: Toggle Styles
   ========================================================================== */

.pref-toggle {
  cursor: pointer;
  padding: 10px 14px;
  border-radius: 8px;
  transition: background 0.15s;
  color: var(--text-primary);
}

.pref-toggle .fw-medium {
  color: var(--text-primary);
}

.pref-toggle:hover {
  background: var(--bg-card-hover);
}


/* ==========================================================================
   Dual-Range Slider (Coordination Page)
   ========================================================================== */

.range-slider-container {
  position: relative;
  height: 36px;
  display: flex;
  align-items: center;
}

.range-slider-track {
  position: absolute;
  top: 50%;
  left: 0;
  right: 0;
  height: 6px;
  transform: translateY(-50%);
  background: var(--bg-elevated);
  border-radius: 3px;
  border: 1px solid var(--border);
  pointer-events: none;
}

.range-slider-fill {
  position: absolute;
  top: 0;
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  opacity: 0.6;
  transition: left 0.05s, width 0.05s;
}

/* Diamond marker showing actual checked-in count */
.range-slider-marker {
  position: absolute;
  top: 50%;
  width: 8px;
  height: 8px;
  background: var(--amber);
  transform: translate(-50%, -50%) rotate(45deg);
  border-radius: 1px;
  pointer-events: none;
  z-index: 3;
  box-shadow: 0 0 6px rgba(251, 191, 36, 0.5);
}

/* Range input thumbs — overlaid on the same track */
.range-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: transparent;
  pointer-events: none;
  margin: 0;
  padding: 0;
  z-index: 4;
}

.range-slider-thumb::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent-light);
  border: 2px solid var(--bg-deep);
  cursor: pointer;
  pointer-events: auto;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
  transition: transform 0.1s, box-shadow 0.1s;
}

.range-slider-thumb::-webkit-slider-thumb:hover {
  transform: scale(1.15);
  box-shadow: 0 2px 10px rgba(var(--accent-rgb), 0.5);
}

.range-slider-thumb::-webkit-slider-thumb:active {
  transform: scale(1.2);
}

.range-slider-thumb::-moz-range-thumb {
  width: 20px;
  height: 20px;
  border-radius: 50%;
  background: var(--accent-light);
  border: 2px solid var(--bg-deep);
  cursor: pointer;
  pointer-events: auto;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.4);
}

/* Ensure the max slider's thumb is on top when at the same position */
.range-slider-max {
  z-index: 5;
}


/* ==========================================================================
   Hidden Game Cards
   ========================================================================== */

/* A card hidden from Ready Up, faded (was an inline style, so a swapped card
   kept the old look; audit CAT-10) */
.hidden-game-card {
  opacity: 0.55;
  transition: opacity 0.2s ease;
}

.hidden-game-card:hover,
.hidden-game-card:focus-within {
  opacity: 0.85;
}

/* List actions (partials/list_button.html): what Hide from Ready Up does,
   in words a touch screen shows too */
.list-action-help {
  font-size: 0.75rem;
  color: var(--text-secondary);
  margin-top: 0.4rem;
}

/* Add Game: the game a duplicate URL or name already is (audit CAT-8) */
.existing-game-thumb {
  width: 96px;
  height: 45px;
  object-fit: cover;
  border-radius: 4px;
  flex-shrink: 0;
}

.existing-game-title {
  font-weight: 600;
}

.existing-game-text {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* Game page: how many own it on the site (audit CAT-11) */
.owners-site-line {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* My List: "Showing X of Y …" over the cards */
.my-list-summary {
  color: var(--text-secondary);
}

.my-list-left-out {
  margin-left: 0.25rem;
}


/* ==========================================================================
   No-Crew Onboarding Card
   ========================================================================== */

.onboarding-card {
  max-width: 540px;
  margin: 3rem auto;
  text-align: center;
  padding: 2.5rem 2rem;
}

.onboarding-card .onboarding-icon {
  font-size: 3rem;
  color: var(--accent-light);
  margin-bottom: 1rem;
}

.onboarding-card h2 {
  font-size: 1.3rem;
  font-weight: 700;
  margin-bottom: 0.75rem;
}

.onboarding-card p {
  color: var(--text-secondary);
  margin-bottom: 1.5rem;
}

.onboarding-actions {
  display: flex;
  gap: 12px;
  justify-content: center;
  flex-wrap: wrap;
}

/* Dashboard: "Import your Steam library" (partials/steam_first_run_card.html,
   audit PT-21). Under the no-crew welcome card it takes that card's width;
   on the crew dashboard it spans the page above the check-in banner. */
.first-run-card {
  border-left: 3px solid var(--accent-light);
  margin-bottom: 1.5rem;
}

.first-run-card-solo {
  max-width: 540px;
  margin: -1.5rem auto 1.5rem;
}

.first-run-head {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
}

.first-run-icon {
  font-size: 1.6rem;
  line-height: 1;
  color: var(--accent-light);
}

.first-run-title {
  font-size: 1.05rem;
  font-weight: 700;
  margin-bottom: 0.25rem;
}

.first-run-text {
  font-size: 0.875rem;
  color: var(--text-secondary);
  margin-bottom: 0.75rem;
}

.first-run-label {
  font-size: 0.8rem;
  color: var(--text-secondary);
  margin-bottom: 0.25rem;
}

.first-run-row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
}

.first-run-row .form-control {
  flex: 1 1 16rem;
  min-width: 0;
}

.first-run-help {
  font-size: 0.75rem;
}

.first-run-links,
.first-run-actions {
  display: flex;
  gap: 0.5rem 1rem;
  flex-wrap: wrap;
  align-items: center;
}

.first-run-links {
  margin-top: 0.75rem;
  font-size: 0.8rem;
}

.first-run-actions {
  gap: 0.5rem;
}

.first-run-dismiss {
  font-size: 0.8rem;
  padding: 0;
  color: var(--text-secondary);
}

/* ── Ready Up session panel (#session-panel.session-sidebar, audit FE-9) ── */
/* One copy at every width. Desktop: the sticky right column, but never
   taller than the window: once the "Tonight's session" card (players,
   settings, Surprise Me, Wrap up) outgrows a short viewport, the panel
   scrolls on its own.
   Without the cap, a sticky element taller than the window keeps its bottom
   out of reach until the whole page has been scrolled to the end. Below
   992px it is an ordinary collapsible block above the results: no sticky,
   no cap, no inner scrollbar. */
@media (min-width: 992px) {
  .session-sidebar {
    position: sticky;
    top: 80px;
    max-height: calc(100vh - 96px);  /* 80px top offset + 16px breathing room */
    overflow-y: auto;
    overscroll-behavior: contain;    /* reaching the end doesn't scroll the page */
    scrollbar-width: thin;
  }
}

/* "Session · N picks · Mode: …" — opens the panel below 992px */
.session-panel-toggle {
  margin-bottom: 0.5rem;
}
.session-panel-chevron {
  display: inline-block;
  transition: transform 0.2s ease;
}
.session-panel-toggle:not(.collapsed) .session-panel-chevron {
  transform: rotate(180deg);
}

/* ── Coordination mobile sticky end-session bar ── */
.coordination-mobile-end {
  position: fixed;
  bottom: 0;
  left: 0;
  right: 0;
  z-index: 1040;
  padding: 12px 16px;
  background: var(--bg-card);
  border-top: 1px solid var(--border);
  box-shadow: 0 -4px 20px rgba(0, 0, 0, 0.4);
}
/* Room below the results for that bar (rendered below 992px only, d-lg-none)
   so it never covers the last cards: vote budget + Surprise Me + Wrap up is
   about 150px tall. */
.coordination-mobile-end-spacer {
  height: 170px;
}

/* ── Tonight's picks: the Ready Up shortlist (audit PT-18) ── */
.tonights-picks {
  border-color: rgba(34, 197, 94, 0.25);
}
.tonights-pick {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 8px 0;
}
.tonights-pick + .tonights-pick {
  border-top: 1px solid var(--border);
}
.tonights-pick-thumb {
  width: 64px;
  height: 30px;
  flex-shrink: 0;
  object-fit: cover;
  border-radius: 4px;
  background: var(--bg-surface);
}
.tonights-pick-thumb-empty {
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--text-muted);
  font-size: 0.9rem;
}
.tonights-pick-body {
  flex: 1;
  min-width: 0;
}
.tonights-pick-name {
  font-weight: 600;
  font-size: 0.9rem;
  color: var(--text-primary);
}
.tonights-pick-tally {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.75rem;
  color: var(--green);
}
.tonights-pick-voters {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}
.tonights-pick-voter {
  font-size: 0.68rem;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(34, 197, 94, 0.1);
  color: var(--emerald);
  border: 1px solid rgba(34, 197, 94, 0.2);
}
.tonights-pick-voter.is-idle {
  background: rgba(245, 158, 11, 0.1);
  color: var(--orange);
  border-color: rgba(245, 158, 11, 0.25);
}
/* Picked earlier by someone who has since checked out: listed, not counted */
.tonights-pick-voter.is-earlier {
  background: rgba(100, 116, 139, 0.1);
  color: var(--text-secondary);
  border-color: rgba(100, 116, 139, 0.2);
  opacity: 0.65;
}
.tonights-pick-earlier .tonights-pick-name,
.tonights-pick-earlier .tonights-pick-thumb {
  opacity: 0.6;
}
.tonights-pick-earlier .tonights-pick-tally {
  color: var(--text-muted);
}
.tonights-pick-remove {
  flex-shrink: 0;
  padding: 2px 8px;
  font-size: 0.75rem;
  color: var(--text-secondary);
  background: var(--bg-deep);
  border: 1px solid var(--border);
}
.tonights-pick-remove:hover {
  color: var(--red, #ef4444);
  border-color: rgba(239, 68, 68, 0.4);
}

/* ── Ready Up match cards, near-miss cards and session panel controls ── */
/* Moved verbatim from partials/coordination_results.html, which re-sent
   them inside every 30 s poll (audit FE-4). Cards are no longer click
   targets — only their labelled buttons act (audit PT-8) — so none of them
   has cursor: pointer or the image zoom any more. */
/* ---------- Base match card ---------- */
.coord-card {
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
  border: 1px solid var(--border);
}
.coord-card:hover {
  box-shadow: 0 0 16px rgba(34, 197, 94, 0.2);
  border-color: rgba(34, 197, 94, 0.4);
}

/* ---------- Selected / nominated card ---------- */
.coord-card-selected {
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
  border: 2px solid rgba(34, 197, 94, 0.6);
  box-shadow: 0 0 20px rgba(34, 197, 94, 0.25);
  background: rgba(34, 197, 94, 0.03);
}
.coord-card-selected:hover {
  box-shadow: 0 0 24px rgba(34, 197, 94, 0.35);
  border-color: rgba(34, 197, 94, 0.8);
}

/* ---------- Vetoed card ---------- */
.coord-card-vetoed {
  transition: box-shadow 0.2s ease, border-color 0.2s ease;
  border: 1px solid rgba(239, 68, 68, 0.3);
  opacity: 0.5;
}
.coord-card-vetoed:hover {
  opacity: 0.7;
  box-shadow: 0 0 12px rgba(239, 68, 68, 0.15);
}
.coord-card-vetoed .coord-img-wrapper img {
  filter: grayscale(0.7);
}

/* ---------- Vote limit reached: the cards you haven't voted for (PT-6) ---------- */
.coord-card-capped {
  opacity: 0.6;
}
.coord-card-capped:hover {
  opacity: 0.8;
}
.coord-limit-notice {
  font-size: 0.75rem;
  padding: 4px 8px;
  border-radius: 6px;
  color: var(--red, #ef4444);
  background: rgba(239, 68, 68, 0.08);
  border: 1px solid rgba(239, 68, 68, 0.25);
}

/* ---------- Locked-in card ---------- */
.coord-card-locked {
  border: 2px solid rgba(251, 191, 36, 0.6);
  box-shadow: 0 0 24px rgba(251, 191, 36, 0.3);
  background: rgba(251, 191, 36, 0.05);
}

.coord-locked-banner {
  background: linear-gradient(135deg, rgba(251, 191, 36, 0.9), rgba(245, 158, 11, 0.9));
  color: #1a1a2e;
  text-align: center;
  font-weight: 700;
  font-size: 0.85rem;
  padding: 6px 0;
  letter-spacing: 0.5px;
}

/* ---------- Overlay icons ---------- */
.coord-selected-overlay {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  background: rgba(34, 197, 94, 0.9);
  color: white;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

.coord-veto-overlay {
  position: absolute;
  top: 8px;
  right: 8px;
  z-index: 2;
  background: rgba(239, 68, 68, 0.9);
  color: white;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1rem;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3);
}

/* ---------- Image wrappers ---------- */
.coord-card .coord-img-wrapper,
.coord-card-selected .coord-img-wrapper,
.coord-card-vetoed .coord-img-wrapper,
.coord-card-locked .coord-img-wrapper {
  position: relative;
  overflow: hidden;
}
.coord-card .coord-img-wrapper img,
.coord-card-selected .coord-img-wrapper img,
.coord-card-locked .coord-img-wrapper img {
  height: 150px;
  object-fit: cover;
  width: 100%;
}
.coord-card-vetoed .coord-img-wrapper img {
  height: 150px;
  object-fit: cover;
  width: 100%;
}
.coord-card .card-img-placeholder,
.coord-card-selected .card-img-placeholder,
.coord-card-vetoed .card-img-placeholder,
.coord-card-locked .card-img-placeholder {
  height: 150px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg-surface);
  color: var(--text-muted);
  font-size: 2rem;
}

/* ---------- Near-miss card ---------- */
/* Nothing to pick on it (not everyone owns it), so no pointer, glow or
   zoom on hover (audit FE-11); "I own it now" is its one action. */
.coord-card-near {
  cursor: default;
  border: 1px solid rgba(168, 85, 247, 0.25);
}
.coord-card-near .coord-img-wrapper {
  position: relative;
  overflow: hidden;
}
.coord-card-near .coord-img-wrapper img {
  height: 150px;
  object-fit: cover;
  width: 100%;
}
.coord-btn-row .btn.coord-btn-own {
  color: var(--purple, #a855f7);
  border-color: rgba(168, 85, 247, 0.4);
}

/* ---------- Rating badges ---------- */
.rating-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 7px;
  border-radius: 4px;
  font-size: 0.7rem;
  font-weight: 600;
}
.rating-badge-high {
  background: rgba(34, 197, 94, 0.12);
  color: var(--green);
  border: 1px solid rgba(34, 197, 94, 0.25);
}
.rating-badge-mid {
  background: rgba(251, 191, 36, 0.12);
  color: var(--amber);
  border: 1px solid rgba(251, 191, 36, 0.25);
}
.rating-badge-low {
  background: rgba(239, 68, 68, 0.12);
  color: var(--red, #ef4444);
  border: 1px solid rgba(239, 68, 68, 0.25);
}

/* "Played recently" (PT-13). The text is --purple moved a fifth of the way
   to the text colour (lighter in the dark modes, darker in light): plain
   --purple on this tint was 4.1:1 on tonight's pick, whose amber wash
   lightens the card in dark (browser round, B-LOOKS-2). */
.badge-played-recently {
  background: rgba(168, 85, 247, 0.12);
  color: color-mix(in srgb, var(--purple) 80%, var(--text-primary));
  border: 1px solid rgba(168, 85, 247, 0.25);
  font-size: 0.7rem;
}

/* ---------- Grid notes (audit PT-10 / PT-11) ---------- */
/* "ana doesn't own any games on their list yet, so these matches are for
   the other 2 of you" and the cause-specific empty states' call to action. */
.coord-notice {
  padding: 10px 12px;
  margin-bottom: 1rem;
  border-radius: 8px;
  font-size: 0.85rem;
  color: var(--text-secondary);
  background: rgba(251, 191, 36, 0.06);
  border: 1px solid rgba(251, 191, 36, 0.25);
}
.coord-notice-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  margin-top: 10px;
}
.coord-notice .coord-notice-actions {
  justify-content: flex-start;
}
/* "Showing 24 of 151 · Show all 151 games" / "Show fewer" below a capped list */
.coord-show-all {
  margin-top: 1rem;
  text-align: center;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* ---------- Button row ---------- */
.coord-btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  align-items: center;
}
.coord-btn-row .btn {
  font-size: 0.72rem;
  padding: 2px 7px;
  border: 1px solid var(--border);
  color: var(--text-secondary);
  background: var(--bg-deep);
}
.coord-btn-row .btn:hover {
  color: var(--text-primary);
  border-color: var(--text-muted);
}
.coord-btn-lock {
  color: var(--amber) !important;
  border-color: rgba(251, 191, 36, 0.4) !important;
}
.coord-btn-lock:hover {
  background: rgba(251, 191, 36, 0.1) !important;
  border-color: rgba(251, 191, 36, 0.6) !important;
}
/* The card's pick button: Nominate / Vote / Veto (audit PT-8). Filled
   (.is-on, aria-pressed="true") once it's yours. */
.coord-btn-row .btn.coord-btn-pick {
  font-weight: 600;
  color: var(--green);
  border-color: rgba(34, 197, 94, 0.45);
}
/* Filled like .btn-success (white on the translucent green was 2.98:1,
   audit TH-4 / Slice B) */
.coord-btn-row .btn.coord-btn-pick.is-on {
  color: var(--green-on);
  background: var(--green);
  border-color: var(--green);
}
.coord-btn-row .btn.coord-btn-veto {
  color: var(--red, #ef4444);
  border-color: rgba(239, 68, 68, 0.45);
}
.coord-btn-row .btn.coord-btn-veto.is-on {
  color: #fff;
  background: rgba(239, 68, 68, 0.85);
  border-color: rgba(239, 68, 68, 0.9);
}
/* The translucent red over a light card is 3.19:1 with white text */
[data-mode="light"] .coord-btn-row .btn.coord-btn-veto.is-on {
  background: var(--red);
  border-color: var(--red);
}
.coord-btn-row .btn.coord-btn-pick:disabled {
  color: var(--text-muted);
  border-color: var(--border);
  opacity: 1;
}

/* ---------- Mode selector ---------- */
.mode-selector select {
  background: var(--bg-deep);
  color: var(--text-primary);
  border: 1px solid var(--input-border);
  border-radius: 6px;
  padding: 4px 8px;
  font-size: 0.8rem;
  width: 100%;
}
.mode-selector select:focus {
  border-color: var(--accent-light);
  outline: none;
}

/* ---------- Vote budget ---------- */
.vote-budget {
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.85rem;
  padding: 6px 10px;
  border-radius: 6px;
  text-align: center;
}

/* ---------- Surprise Me pulse animation ---------- */
.coord-card-surprise {
  animation: surprise-pulse 1.5s ease-in-out;
}
@keyframes surprise-pulse {
  0%   { box-shadow: 0 0 0 rgba(251, 191, 36, 0); transform: scale(1); }
  30%  { box-shadow: 0 0 30px rgba(251, 191, 36, 0.6); transform: scale(1.03); }
  60%  { box-shadow: 0 0 15px rgba(251, 191, 36, 0.3); transform: scale(1.01); }
  100% { box-shadow: 0 0 0 rgba(251, 191, 36, 0); transform: scale(1); }
}


/* ==========================================================================
   Ambient backdrop — the .bg-effects layer + Festival confetti
   ==========================================================================
   base.html renders two empty layers as the first things in <body>:
   .bg-effects (the glow, painted per data-bg) and .festival-confetti (spans
   base.html's script builds when bg = festival).

   Behind the content (audit TH-7): both are position: fixed with
   z-index: -1. In the root stacking context that paints them above the page
   background (body's colour, which propagates to the canvas) and below
   everything else — page titles, text outside cards, the footer, cards,
   the navbar. With z-index: 0 they painted OVER every unpositioned element
   (main and footer are not positioned), tinting titles and the footer.
   This holds only while body is no stacking context and html has no
   background of its own: never give body a transform, filter, opacity < 1,
   isolation, will-change, contain or z-index, nor html a background
   (core/tests/test_theme_tokens.py checks). Lifting main / footer to
   z-index: 1 instead would trap their modals and dropdowns under the navbar.

   The layer owns the whole glow (audit TH-12): body has only a colour, so
   nothing is painted twice and nothing needs background-attachment: fixed.
   Aurora is the default; flat hides the layer.
   ========================================================================== */

/* Aurora: accent glows at the top corners and the bottom, a sky glow at the
   bottom right (first listed = on top) */
.bg-effects {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  background:
    radial-gradient(900px 500px at 12% -10%, rgba(var(--accent-rgb), 0.18), transparent 60%),
    radial-gradient(800px 600px at 95%   8%, rgba(var(--accent-rgb), 0.14), transparent 60%),
    radial-gradient(700px 500px at 50% 110%, rgba(var(--accent-rgb), 0.12), transparent 60%),
    radial-gradient(ellipse at 80% 80%, rgba(56, 189, 248, 0.09), transparent 55%);
}
body[data-bg="flat"] .bg-effects { display: none; }

/* Pride's aurora: a rainbow wash along the top under the accent glows */
body[data-accent="pride"][data-bg="aurora"] .bg-effects {
  background:
    radial-gradient(900px 500px at 12% -10%, rgba(var(--accent-rgb), 0.18), transparent 60%),
    radial-gradient(800px 600px at 95%   8%, rgba(var(--accent-rgb), 0.14), transparent 60%),
    radial-gradient(700px 500px at 50% 110%, rgba(var(--accent-rgb), 0.12), transparent 60%),
    radial-gradient(ellipse at 10% 15%, rgba(239, 68, 68, 0.10), transparent 35%),
    radial-gradient(ellipse at 30% 10%, rgba(245, 158, 11, 0.07), transparent 35%),
    radial-gradient(ellipse at 50% 8%, rgba(34, 197, 94, 0.07), transparent 35%),
    radial-gradient(ellipse at 70% 12%, rgba(59, 130, 246, 0.08), transparent 35%),
    radial-gradient(ellipse at 90% 15%, rgba(168, 85, 247, 0.10), transparent 35%);
}

/* Light mode: the same glow at half strength on the light page */
body[data-mode="light"] .bg-effects { opacity: 0.5; }

/* Festival: soft rainbow blobs, drifting. No filter: blur() on the layer
   (audit TH-12) — on a viewport-sized layer it was re-rasterised every
   frame of the drift; the gradients are a little wider instead, and the
   drift is a transform the compositor animates on its own. */
body[data-bg="festival"] .bg-effects {
  background:
    radial-gradient(680px 460px at 14% 18%, rgba(255, 75, 75, 0.30), transparent 60%),
    radial-gradient(620px 420px at 78% 12%, rgba(255, 209, 102, 0.26), transparent 60%),
    radial-gradient(700px 500px at 88% 62%, rgba(76, 199, 255, 0.30), transparent 60%),
    radial-gradient(760px 520px at 22% 78%, rgba(139, 107, 255, 0.30), transparent 60%),
    radial-gradient(600px 420px at 50% 50%, rgba(255, 121, 198, 0.22), transparent 65%),
    radial-gradient(560px 400px at  6% 50%, rgba(92,  220, 138, 0.22), transparent 65%);
  animation: festivalDrift 22s ease-in-out infinite alternate;
}
body[data-mode="light"][data-bg="festival"] .bg-effects { opacity: 0.55; }

@keyframes festivalDrift {
  0%   { transform: translate3d(0, 0, 0) scale(1); }
  50%  { transform: translate3d(-2%, 1.5%, 0) scale(1.05); }
  100% { transform: translate3d(2%, -1%, 0) scale(1.03); }
}

/* Festival makes cards translucent so the wash bleeds through. No backdrop
   blur (audit TH-12): the drift and the confetti change the backdrop every
   frame, so every card would be re-blurred at 60 fps. */
body[data-bg="festival"] .card {
  background: color-mix(in oklab, var(--bg-card) 78%, transparent);
}

/* Festival's blobs are on-screen by design, under page text and under the
   translucent cards: muted text there fell to about 2.9:1 (dark, at the
   yellow blob) on the page and 4.1:1 on a card. Under Festival, muted text
   uses the secondary shade (each mode's own: (0,1,1) beats the mode blocks'
   (0,1,0), and the var() resolves on body). */
body[data-bg="festival"] {
  --text-muted: var(--text-secondary);
}

/* Festival + Pride: a highlight sweeps along the navbar. It is a ::before
   behind the bar's content (z-index: -1 inside the navbar's own stacking
   context), not part of the navbar's background: that background is
   !important, and an !important declaration beats an animation, so the old
   background-position shimmer never moved (audit TH-5). */
body[data-bg="festival"][data-accent="pride"] .navbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(var(--accent-rgb), 0.10) 50%, transparent) 0 0 / 200% 100%;
  animation: festivalShimmer 4.5s linear infinite;
}
@keyframes festivalShimmer {
  0%   { background-position: -100% 0; }
  100% { background-position:  200% 0; }
}


/* Confetti — shown only when bg = festival; base.html builds the spans */
.festival-confetti {
  display: none;
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  overflow: hidden;
}
body[data-bg="festival"] .festival-confetti { display: block; }

.festival-confetti span {
  position: absolute;
  top: 0;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  opacity: 0.55;
  will-change: transform;
  animation: confettiFall linear infinite;
}
.festival-confetti span:nth-child(odd)  { width: 6px;  height: 6px;  border-radius: 50%; }
.festival-confetti span:nth-child(3n)   { width: 14px; height: 4px;  border-radius: 2px; }
body[data-mode="light"] .festival-confetti span { opacity: 0.4; }

@keyframes confettiFall {
  0%   { transform: translate3d(0, -10vh, 0) rotate(0deg); opacity: 0; }
  8%   { opacity: 0.55; }
  100% { transform: translate3d(var(--dx, 20px), 110vh, 0) rotate(540deg); opacity: 0.55; }
}


/* ==========================================================================
   Appearance Studio — segmented control / swatches / bg cards / presets
   ==========================================================================
   Lives on the profile page. Every pick re-skins the page (the four data
   attributes on <body>, data-bs-theme on <html>) and is saved straight
   away (profile.html posts action=save_appearance; audit TH-2). Buttons
   carry aria-pressed; .on marks the current pick and the matching preset.
   ========================================================================== */

.appearance-row {
  display: flex;
  flex-wrap: wrap;
  gap: 18px;
  margin-bottom: 18px;
}
.appearance-field { min-width: 0; }
.appearance-field-grow { flex: 1 1 220px; }
.appearance-field-motion { flex: 1 1 280px; }
.appearance-field-head {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-primary);
  margin-bottom: 8px;
  letter-spacing: 0.02em;
}
.appearance-hint {
  font-weight: 400;
  color: var(--text-muted);
  font-size: 0.78rem;
}

/* Segmented controls — Mode (four modes) and Motion (three values): they
   wrap rather than overflow on a phone-width card */
.seg-mode {
  display: inline-flex;
  flex-wrap: wrap;
  padding: 3px;
  border-radius: 10px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  width: 100%;
}
.seg-mode .seg-btn {
  flex: 1;
  border: none;
  background: transparent;
  padding: 7px 10px;
  border-radius: 7px;
  font-size: 0.8rem;
  font-weight: 500;
  color: var(--text-secondary);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  white-space: nowrap;
  cursor: pointer;
  transition: background 0.15s, color 0.15s;
}
.seg-mode .seg-btn:hover { color: var(--text-primary); }
/* The pressed segment's ring is the text-safe accent: the fill alone is
   only 1.1-1.3:1 against the track, and a state needs 3:1 (WCAG 1.4.11) */
.seg-mode .seg-btn.on {
  background: var(--bg-elevated);
  color: var(--text-primary);
  box-shadow: inset 0 0 0 1px var(--accent-light);
}
.seg-mode .seg-btn i { font-size: 0.85rem; }

/* Accent swatches. The names sit under the swatches and are always shown
   (they were hover-only, so touch users saw unlabelled colours): the row-gap
   and bottom padding leave room for them and for the focus ring, which sits
   outside the selection ring (see .swatch:focus-visible). */
.accents {
  display: flex;
  flex-wrap: wrap;
  gap: 34px 14px;
  padding-bottom: 24px;
}
.swatch {
  width: 38px;
  height: 38px;
  border-radius: 10px;
  border: 1px solid var(--border);
  cursor: pointer;
  position: relative;
  background: var(--swatch-bg, #888);
  transition: transform 0.1s, box-shadow 0.15s, border-color 0.15s;
  padding: 0;
}
.swatch:hover { transform: translateY(-1px); border-color: var(--text-muted); }
.swatch.on { box-shadow: 0 0 0 2px var(--bg-deep), 0 0 0 4px var(--accent); }
/* The selection ring is the accent 2-4px outside the swatch; the global
   focus ring (2px at offset 2px) would land on exactly that band, in the
   same colour in light forest / sunrise. Keyboard focus is drawn further
   out (6-8px) in the text colour instead. */
.swatch:focus-visible {
  outline-color: var(--text-primary);
  outline-offset: 6px;
}
.swatch .swatch-name {
  position: absolute;
  bottom: -24px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 10.5px;
  color: var(--text-muted);
  white-space: nowrap;
  pointer-events: none;
}
.swatch.on .swatch-name { color: var(--text-primary); }

.sw-slate   { --swatch-bg: linear-gradient(135deg, #7c8cff, #5d6cff); }
.sw-storm   { --swatch-bg: linear-gradient(135deg, #4f9cff, #2d80f0); }
.sw-forest  { --swatch-bg: linear-gradient(135deg, #67e3a8, #22c97c); }
.sw-sunrise { --swatch-bg: linear-gradient(135deg, #ffba6e, #ff6f3c); }
.sw-pride   { --swatch-bg: linear-gradient(90deg,  #ff4b4b, #ff8c42, #ffd166, #5cdc8a, #4cc7ff, #8b6bff, #ff79c6); }
/* Light forest / sunrise are much deeper than the dark ones */
[data-mode="light"] .sw-forest  { --swatch-bg: linear-gradient(135deg, #16a763, #0f7a47); }
[data-mode="light"] .sw-sunrise { --swatch-bg: linear-gradient(135deg, #e85d23, #b8460f); }

/* Background option cards */
.bg-options {
  display: flex;
  gap: 10px;
}
.bg-opt {
  flex: 1;
  height: 56px;
  border-radius: 10px;
  border: 1px solid var(--border);
  cursor: pointer;
  position: relative;
  overflow: hidden;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  background: var(--bg-surface);
  transition: border-color 0.15s, transform 0.1s;
  padding: 0;
}
.bg-opt:hover { transform: translateY(-1px); }
.bg-opt.on { border-color: var(--accent); box-shadow: 0 0 0 1px var(--accent); }
.bg-opt-name {
  font-size: 0.7rem;
  font-weight: 500;
  color: var(--text-primary);
  position: relative;
  z-index: 2;
  background: var(--bg-card);
  padding: 1px 7px 2px;
  border-radius: 4px 4px 0 0;
  border: 1px solid var(--border);
  border-bottom: none;
  margin-bottom: 0;
}
.bg-opt-preview {
  position: absolute;
  inset: 0;
  pointer-events: none;
}
.bg-flat .bg-opt-preview { background: transparent; }
.bg-aurora .bg-opt-preview {
  background:
    radial-gradient(80px 50px at 20% 20%, rgba(var(--accent-rgb), 0.35), transparent 60%),
    radial-gradient(80px 60px at 80% 80%, rgba(var(--accent-rgb), 0.30), transparent 60%);
}
.bg-festival .bg-opt-preview {
  background:
    radial-gradient(60px 50px at 18% 25%, rgba(255, 75, 75, 0.6), transparent 60%),
    radial-gradient(60px 50px at 78% 22%, rgba(255, 209, 102, 0.6), transparent 60%),
    radial-gradient(60px 50px at 28% 78%, rgba(76, 199, 255, 0.6), transparent 60%),
    radial-gradient(60px 50px at 80% 80%, rgba(139, 107, 255, 0.6), transparent 60%);
  filter: blur(2px);
}

/* Quick presets */
.appearance-presets-wrap { margin-top: 6px; }
.presets {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 6px;
}
.preset-chip {
  padding: 4px 10px;
  border-radius: 999px;
  background: var(--bg-surface);
  border: 1px solid var(--border);
  color: var(--text-secondary);
  font-size: 0.78rem;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s;
}
.preset-chip:hover { color: var(--text-primary); border-color: var(--text-muted); }
/* The preset the current mode + accent + background match */
.preset-chip.on {
  color: var(--text-primary);
  border-color: var(--accent);
  box-shadow: 0 0 0 1px var(--accent);
}
.preset-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  display: inline-block;
}
.preset-dot-slate   { background: #5d6cff; }
.preset-dot-storm   { background: #2d80f0; }
.preset-dot-forest  { background: #16a763; }
.preset-dot-sunrise { background: #ff6f3c; }
.preset-dot-pride   { background: linear-gradient(90deg, #ff4b4b, #ffd166, #5cdc8a, #4cc7ff, #8b6bff); }

/* Save status next to the summary badge: "Saving…" / "Saved ✓" /
   "Couldn't save — try again" (profile.html showStatus) */
/* Never display: none, even while empty: a role=status region has to be in
   the accessibility tree before its text changes, or the first "Saving…"
   isn't announced. Empty, it is zero-width. */
.appearance-save-status {
  margin-left: auto;
  font-size: 0.72rem;
  font-weight: 500;
  letter-spacing: normal;
  text-transform: none;
  white-space: nowrap;
  color: var(--text-muted);
}
.appearance-save-status.is-saved { color: var(--green); }
.appearance-save-status.is-error { color: var(--red); }

/* The Appearance title wraps at phone width instead of pushing the page
   sideways ("Forest · Match device · Aurora" plus a status is wider than a
   390px card) */
.appearance-title {
  flex-wrap: wrap;
  row-gap: 4px;
}

/* Card-title trailing summary badge (pushed right by the status before it,
   or by itself while the status is empty) */
.appearance-save-status:empty + #appearance-summary { margin-left: auto; }
#appearance-summary {
  max-width: 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  font-size: 0.68rem;
  font-weight: 500;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--text-secondary);
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  padding: 2px 8px;
  border-radius: 999px;
}


/* ==========================================================================
   Profile sidebar — Your Crews widget
   ==========================================================================
   Per-crew row with shield icon, name, role badge, member count, and chevron.
   Shield color is set inline via --cs (derived from crew.pk for stability). */

.crew-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 8px;
  border-radius: 8px;
  text-decoration: none;
  color: inherit;
  transition: background 0.15s;
}
.crew-row:hover {
  background: var(--bg-card-hover);
  color: inherit;
}
.crew-row + .crew-row {
  border-top: 1px solid var(--border);
  border-radius: 0;
}
.crew-row + .crew-row:hover { border-radius: 8px; }

.crew-shield {
  width: 36px;
  height: 36px;
  border-radius: 8px;
  background: color-mix(in oklab, var(--cs, var(--accent)) 18%, transparent);
  color: var(--cs, var(--accent));
  display: grid;
  place-items: center;
  flex-shrink: 0;
  border: 1px solid color-mix(in oklab, var(--cs, var(--accent)) 35%, transparent);
}
.crew-shield i { font-size: 1.05rem; }

.crew-meta {
  flex: 1;
  min-width: 0;
}
.crew-name {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--text-primary);
  display: flex;
  align-items: center;
  gap: 6px;
}
.crew-badge {
  font-size: 0.62rem;
  padding: 1px 7px;
  border-radius: 999px;
  background: rgba(var(--accent-rgb), 0.12);  /* .15 was 4.50:1 for dark slate on the row's hover */
  color: var(--accent-light);  /* the text-safe accent (--accent was ~3.3:1 here) */
  border: 1px solid rgba(var(--accent-rgb), 0.3);
  font-weight: 500;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.crew-sub {
  font-size: 0.75rem;
  color: var(--text-secondary);
  margin-top: 2px;
}
.crew-arrow {
  color: var(--text-muted);
  font-size: 0.85rem;
  flex-shrink: 0;
}

/* =============================================================================
   HTMX pending state (audit FE-8)
   htmx puts .htmx-request on the element that sent a request until the
   response is handled. Only controls get the pending look: a polled
   container (#coordination-results, #availability-section) also carries the
   class during every poll and must not dim, and neither does a field being
   typed in (the select-game page's search sends a request on every pause):
   no input gets it (the session panel's settings are selects).
   hx-disabled-elt="this" on the buttons blocks a second click meanwhile.
   ============================================================================= */
.btn.htmx-request,
button.htmx-request,
select.htmx-request {
  opacity: 0.6;
  cursor: progress;
}


/* ==========================================================================
   Error pages (audit TH-10)
   templates/error_base.html (400 / 403 / 404, inside base.html) and the
   standalone 500.html share these, so all four follow the mode and accent.
   ========================================================================== */
.error-page {
  max-width: 480px;
  margin: 3rem auto;
  text-align: center;
}
.error-code {
  font-family: 'JetBrains Mono', monospace;
  font-size: 6rem;
  font-weight: 700;
  line-height: 1;
  margin-bottom: 0.5rem;
  color: var(--accent-light);
}
.error-code-warning { color: var(--amber); }
.error-code-danger { color: var(--red); }
.error-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--text-primary);
  margin-bottom: 0.75rem;
}
.error-message {
  color: var(--text-secondary);
  font-size: 0.95rem;
  line-height: 1.6;
  margin-bottom: 2rem;
}
.error-actions {
  display: flex;
  gap: 0.75rem;
  justify-content: center;
  flex-wrap: wrap;
}
