/* ============================================================
   41shots global styles
   ============================================================ */

/* --- Webfonts ---
   Manrope (body) and Oswald (display) are vendored under SIL Open Font
   License 1.1. Source files come from @fontsource-variable/{manrope,oswald}
   and live in /public/fonts. We keep latin + latin-ext only -- enough for
   every western and central-european player name; cyrillic/greek are
   skipped to keep first-paint cheap. Variable-axis files cover every
   weight in one request, so swapping between 400 and 700 needs no extra
   download. License files alongside the WOFF2s (manrope-OFL.txt,
   oswald-OFL.txt). */
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('/public/fonts/manrope-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Manrope';
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  src: url('/public/fonts/manrope-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Oswald';
  font-style: normal;
  font-display: swap;
  font-weight: 200 700;
  src: url('/public/fonts/oswald-latin-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Oswald';
  font-style: normal;
  font-display: swap;
  font-weight: 200 700;
  src: url('/public/fonts/oswald-latin-ext-wght-normal.woff2') format('woff2-variations');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* `[hidden]` should always remove the element from layout. Without
   `!important` any class with an explicit `display` (e.g. `display: flex`)
   wins on cascade order and the element stays visible while toggled
   "hidden" from JS. Standard CSS-reset entry, see HTML5 Boilerplate. */
/* stylelint-disable-next-line declaration-no-important */
[hidden] { display: none !important; }

/* --- Icon system --- */
.icon {
  display: inline-block;
  width: 1em;
  height: 1em;
  vertical-align: -0.15em;
  flex-shrink: 0;
}
.icon--sm  { width: 0.85em; height: 0.85em; }
.icon--lg  { width: 1.3em;  height: 1.3em;  }
.icon--xl  { width: 1.6em;  height: 1.6em;  }

/* Hides the inline SVG sprite definition at the top of <body>. Was
   `style="display:none"` until Tier-3 CSP hardening replaced it with a
   class -- strict `style-src 'self'` on /staff/* forbids inline styles. */
.icon-sprite { display: none; }

/* The :root token block is the single source of truth for all colours,
   surface tints, shadows, borders. Hex and rgba(255,255,255,*) /
   rgba(0,0,0,*) are intentionally allowed *here only*; the rest of the
   file must reach for these tokens via var(--...). */
/* stylelint-disable color-no-hex, declaration-property-value-disallowed-list */
:root {
  --bg:          #090909;
  --red:         #e03535;
  --text:        #efefef;
  --text-muted:  #a0a0a0;
  --text-dim:    #686868;

  /* Transparent text colours for content over tinted backgrounds
     (popups, tooltips, overlays). Unlike the solid --text / --text-muted
     tokens, these mix with whatever surface sits below, which is what
     gives leaflet popups and POI panels their lifted look. Migrated
     from a dozen 0.3-0.9 alpha micro-variants. */
  --text-overlay-dim:    rgba(255,255,255,0.35);
  --text-overlay-muted:  rgba(255,255,255,0.6);
  --text-overlay-strong: rgba(255,255,255,0.88);
  --font:        -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;

  /* Radius scale -- five tokens cover every shape we draw.
     sm   subtle box rounding (stat-bars, heatmap cells, mini-pills)
     md   mid box rounding (search-results, tooltips, list items)
     lg   card rounding (KPI cards, hero blocks, main panels)
     full perfect circle (dots, avatars, round markers)
     pill capsule with semicircle caps (badges, buttons, role-pills) */
  --radius-sm:    4px;
  --radius-md:    8px;
  --radius-lg:   16px;
  --radius-full: 50%;
  --radius-pill: 999px;

  /* Z-index scale -- eight named tiers replace nine raw values that
     drifted into similar but slightly different numbers (1000 vs 1001
     and so on). Each tier has a clear job; gaps between tiers leave
     room for future intermediate layers without renumbering.
     The nav sits above the map-control tier so descendants in the
     nav's stacking context (e.g. the user-menu dropdown) can render
     over the map sidebar -- otherwise the dropdown was clipped behind
     it on /map. */
  --z-base:         1;    /* hovered/focused element within local stack */
  --z-overlay:     10;    /* in-component floating control (lightbox nav) */
  --z-popover:     20;    /* small dropdown / search results overlay */
  --z-map-layer: 1000;    /* map sidebars + active markers */
  --z-map-control:1010;   /* map toolbars, legends, map-side controls */
  --z-nav:       1100;    /* sticky nav, tooltip flyouts */
  --z-float:     1500;    /* pinned floating CB-radio window (above nav, below modal) */
  --z-modal:     2000;    /* lightbox, full-page search results */

  /* Sizing scale for marker dots, avatars and icons. One scale covers
     all three because they share the same physical px steps in the
     design; the semantics live in the selector name (.foo__dot vs
     .foo-avatar vs .foo-icon), not in the token. Drift values like
     7px / 9px / 12px collapsed onto the nearest step. */
  --size-xs:     8px;     /* small dots (badge, live, list, chip) */
  --size-sm:    10px;     /* standard map markers, default dots */
  --size-md:    14px;     /* active markers, small avatars, sort icons */
  --size-lg:    16px;     /* selected markers, search-icon, follow */
  --size-xl:    18px;     /* close-icon, small-avatar (hall-of-fame links) */
  --size-2xl:   22px;     /* prominent nav-icon (lightbox) */
  --size-3xl:   28px;     /* small avatars */
  --size-4xl:   32px;     /* circular button (map-tb-chip, econ-sort-dir) */
  --size-5xl:   44px;     /* medium avatars */
  --size-6xl:   64px;     /* hero avatars */

  /* Transition durations -- four-step ladder replacing nine drifted
     values. Most hovers used 0.15s (base); the strays at 0.12 / 0.22 /
     0.25 / 0.3 / 0.4 were tiny inconsistencies that nobody picked on
     purpose. fast = press feedback, base = default hover, slow = larger
     element changes, animate = fade-in / longer movements. */
  --motion-fast:    0.1s;
  --motion-base:   0.15s;
  --motion-slow:    0.2s;
  --motion-animate: 0.35s;
  --nav-h:       58px;

  /* --- Type tokens -- Hybrid scale ---
     Body uses comfortable 15.2px for prose, headlines, cards. Data-dense
     surfaces (tables, sidebars, KPI sub-rows) read --fs-dense which stays
     pinned at 12.5px so list-heavy pages keep their information density.
     Pages migrate from hardcoded font-size values to these tokens one at
     a time. Until that migration completes, hardcodes win. */
  --fs-xs:    0.72rem;   /* 11.5px -- micro labels, tags */
  --fs-sm:    0.82rem;   /* 13.1px -- secondary text */
  --fs-base:  0.95rem;   /* 15.2px -- body, paragraphs, card content */
  --fs-md:    1.10rem;   /* 17.6px -- card titles, form input */
  --fs-lg:    1.35rem;   /* 21.6px -- KPI value, sub-heading */
  --fs-xl:    1.75rem;   /* 28px   -- detail page title */
  --fs-2xl:   2.40rem;   /* 38.4px -- hero / page title */
  --fs-dense: 0.78rem;   /* 12.5px -- tables, sidebars, dense data UIs */

  --fw-regular: 400;
  --fw-medium:  600;
  --fw-bold:    700;
  --fw-hero:    800;

  --lh-tight: 1.18;
  --lh-body:  1.58;

  /* --- Font tokens ---
     --font-body / --font-display split lets pages set headline-vs-body
     family in one place. */
  --font-body:    'Manrope', var(--font);
  --font-display: 'Oswald', 'Impact', 'Arial Narrow', sans-serif;

  /* Page-width tiers (see .page / .page--wide / .map-page below):
     content   = reading-focused pages (Rules, Support, Home, 41PD)
     dashboard = data-focused pages (Statistics, Economy, Housing, Newspaper, Gallery) */
  --max-w-content:   1160px;
  --max-w-dashboard: 1600px;

  /* Role colours -- single source of truth, used by:
       .role-pill, .resident-tile__role-dot, .map-player__role,
       and the residents-page filter buttons (data-filter="...").
     Transparent backgrounds/borders are derived via color-mix so a
     palette change here propagates everywhere automatically. */
  --role-admin:      #FFD23F;
  --role-police:     #1E5BFF;
  --role-enthusiast: #22E6C9;
  --role-none:       #4E9F3D;

  /* Map-marker colours for non-player buildings. The marker background
     stays constant; status info (e.g. housing expiry) is conveyed via
     text colour next to the marker, not through the marker itself. */
  --map-housing: #4E9F3D;
  --map-poi:     #deb204;

  /* Active wildfire markers on the map and the matching heat-slider /
     heat-mode controls in the sidebar. Distinct from --status-orange
     (#ff9800) which is an informational tone -- fire has more red so
     it reads as "danger / heat" rather than "warning / pending".
     --fire-extinguished is the cool blue used for already-put-out
     fire markers, so the two states read as opposites at a glance. */
  --fire:              #ff5500;
  --fire-extinguished: #4090ff;

  /* Player-track route visualisation (route-toggle button and matching
     map-legend swatch). Kept distinct from --role-police so an active
     route never looks like a police marker. Material Blue 500. */
  --route: #2196f3;

  /* Status colours for KPI labels and tabular state badges. Used by the
     housing page expiry buckets and the "available" highlight. Reusing
     --red for danger keeps the brand-red consistent everywhere.
     --status-muted is a 20% white overlay for inactive/zero states
     (e.g. a town with no payment bonus, a row whose status no longer
     applies). Lives in the status family because that is how callers
     reach for it -- data-status="muted" sits alongside red/orange/etc. */
  --status-red:    var(--red);
  --status-orange: #ff9800;
  --status-yellow: var(--map-poi);
  --status-green:  #4caf50;
  --status-muted:  rgba(255, 255, 255, 0.2);

  /* Motor Town skill class colours. Bewusste Duplikation mit
     src/public/skills.js (which exports them for JS-side colour math
     via dimHex()); keep both in sync on retune. CSP forbids inline
     style="--accent:..." so CSS-side consumers reach these via the
     [data-skill="CL_X"] selector family further down in this file.
     Police bleibt bewusst Blau aber NICHT identisch zu --role-police --
     the role pill tints differently on purpose, see
     project_skill_colors memory. */
  --skill-driver:  #A1A1AA;
  --skill-taxi:    #EAB308;
  --skill-bus:     #22C55E;
  --skill-truck:   #C2410C;
  --skill-racer:   #EF4444;
  --skill-wrecker: #D97706;
  --skill-police:  #3B82F6;

  /* Third-party brand colours, used by .badge--* on the home page. Kept
     separate from role/map tokens because they belong to external brands
     and must not drift when our own palette is retuned. */
  --brand-motortown: #deb204;
  --brand-discord:   #5865f2;
  --brand-steam:     #66c0f4;

  /* Movement-track heatmap palette: shared by the map track polyline
     (drawn from JS in map-app.js trackColor()) and the legend bar in
     the sidebar. JS reads these via getComputedStyle so any retune
     here propagates to both the line and the legend automatically. */
  --heat-cold: #3c50b4;
  --heat-cool: #00aad2;
  --heat-mid:  #3cc83c;
  --heat-warm: #f0c800;
  --heat-hot:  #ff5000;

  /* Surface tints and borders for cards, container blocks and overlays.
     Five tokens replace ~280 hardcoded rgba(255,255,255,X) variants that
     had drifted into 0.01-step micro-differences without visible intent.
     surface-1/2/3 climb in elevation (quiet base, raised, active);
     border-soft/strong pair with them. */
  --surface-1:     rgba(255,255,255,0.04);
  --surface-2:     rgba(255,255,255,0.06);
  --surface-3:     rgba(255,255,255,0.10);
  --border-hairline: rgba(255,255,255,0.06);
  --border-soft:     rgba(255,255,255,0.08);
  --border-strong:   rgba(255,255,255,0.14);
  --border-emphasis: rgba(255,255,255,0.25);

  /* Shadow tokens layered by how far the element sits from the page.
     The previous code had ~10 micro-variants (0.22 vs 0.25, 0.4 vs 0.45)
     which were drift, not intent. card sits on the page, popup floats
     above small dropdowns/markers, modal stacks over the whole page.
     highlight-top tiers are the 1px inner-top accent: base is the
     hairline used on quiet surfaces, mid is the standard for cards
     and buttons, strong is the brighter hover state. */
  --shadow-card:                 0 4px 24px rgba(0,0,0,0.22);
  --shadow-popup:                0 2px 10px rgba(0,0,0,0.45);
  --shadow-modal:                0 8px 28px rgba(0,0,0,0.55);
  --shadow-button:               0 1px 3px rgba(0,0,0,0.3);
  --shadow-highlight-top:        inset 0 1px 0 rgba(255,255,255,0.06);
  --shadow-highlight-top-mid:    inset 0 1px 0 rgba(255,255,255,0.12);
  --shadow-highlight-top-strong: inset 0 1px 0 rgba(255,255,255,0.18);

  /* Dark glass overlay used by popups, tooltips, sidebars, modal
     backdrops and any other floating surface that sits on top of the
     page or the map. One token covers all of them because on the
     normal --bg the difference between 0.85 and 0.96 is invisible;
     on the map both still feel like "frosted glass". The sticky-nav
     and map-search input use a lower-alpha override directly with a
     stylelint-disable next to them -- they need the underlying page
     to bleed through their backdrop-filter, which this token does not. */
  --surface-overlay: rgba(9,9,9,0.92);

  /* Map glow: same visual concept (a soft dark halo) applied via two
     different CSS properties so map markers (box-shadow) and labels
     /direction arrows (text-shadow) keep parity. Bumped from the old
     0.6 / 0.8 alphas because both were too faint on bright satellite
     tiles (beach, asphalt) where the markers used to wash out. */
  --map-glow-box:  0 0 6px rgba(0,0,0,0.85);
  --map-glow-text: 0 0 6px rgba(0,0,0,0.85);

  /* Backdrop scrim for modal overlays. Bewusst NICHT aus --surface-*
     gemischt: Backdrops haben eine eigene Bedeutung (sie verdunkeln
     den Inhalt darunter, statt selbst Inhalt zu zeigen) und sollen
     nicht die Surface-Skala beerben. Eigener Token statt zusaetzlicher
     stylelint-disable an jeder Modal-Stelle. */
  --scrim: rgba(0,0,0,0.6);

  /* Tow distress-call colour. One tone for the whole tow family --
     the specific reason is conveyed by the icon, not by a per-reason
     hue. The --tow-reason-* tokens are kept as aliases so existing
     consumers (REASON_META in garage-tow-app.js, the radio swatches
     in the modal) keep working without a sweep; flip the alias values
     here if you ever want per-reason tones back. */
  --tow-marker:             #E08A3A;
  --tow-reason-stuck:       var(--tow-marker);
  --tow-reason-flipped:     var(--tow-marker);
  --tow-reason-out-of-fuel: var(--tow-marker);
}
/* stylelint-enable color-no-hex, declaration-property-value-disallowed-list */

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
/* Anchors the rem unit; every --fs-* token resolves against this base.
   scrollbar-gutter reserves the vertical scrollbar's space even on
   pages that fit the viewport without scrolling -- without it, the
   nav and centred content jump sideways by ~8px when switching from
   a long page (scrollbar present) to a short page (scrollbar gone). */
/* stylelint-disable-next-line declaration-property-value-allowed-list */
html { font-size: 16px; scroll-behavior: smooth; scrollbar-gutter: stable; }
html:has(.map-page) { overflow: hidden; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-body, var(--font));
  line-height: 1.65;
  min-height: 100vh;
}
a { color: var(--red); text-decoration: none; }
a:hover { color: var(--text); }
img { display: block; max-width: 100%; }
ul { list-style: none; }

/* ============================================================
   Navigation
   ============================================================ */
.nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  height: var(--nav-h);
  /* Sticky nav lets the page below bleed through so scrolled content
     is faintly visible behind it -- backdrop-filter only works when
     the surface is partially transparent. --surface-overlay (0.92)
     would block too much; the dedicated low-alpha value lives here. */
  /* stylelint-disable-next-line declaration-property-value-disallowed-list */
  background: rgba(9,9,9,0.72);
  border-bottom: 1px solid var(--border-hairline);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
}
.nav__inner {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 0 1.5rem;
  height: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2rem;
}
.nav__logo {
  display: flex;
  align-items: center;
  flex-shrink: 0;
}
.nav__logo-img {
  height: 36px;
  width: auto;
  display: block;
  mix-blend-mode: screen;
}
.nav__links {
  display: flex;
  align-items: center;
  gap: 0.1rem;
}
.nav__link {
  padding: 0.36rem 0.8rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  border: 1px solid transparent;
  transition: color var(--motion-base), background var(--motion-base), border-color var(--motion-base);
}
.nav__link:hover {
  color: var(--text);
  background: var(--surface-2);
}
.nav__link.active {
  color: color-mix(in srgb, var(--red) 55%, white);
  background: color-mix(in srgb, var(--red) 10%, transparent);
  border-color: color-mix(in srgb, var(--red) 22%, transparent);
}

/* Auth slot in the top nav. Sits to the right of .nav__links and shows
   either the user chip (avatar + name + Log-out button) or the
   Sign-in-with-Steam CTA. Sign-in and Log-out share .btn .btn--compact
   so the brand-red CTA palette stays consistent with Gallery/Newspaper/
   Ko-fi. Steam branding lives in the icon, not the colour. */
.nav__auth {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
}
.nav__user {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  color: var(--text-muted);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  max-width: 14rem;
  text-decoration: none;
  transition: color var(--motion-base);
}
a.nav__user:hover { color: var(--text); }
.nav__user-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Avatar element shared between img, initial-bubble and logo-fallback.
   28px matches the .btn--compact height so the chip aligns with the
   Log-out button to its right. */
.nav-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-3xl);
  height: var(--size-3xl);
  border-radius: var(--radius-full);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  object-fit: cover;
  flex-shrink: 0;
  overflow: hidden;
}
/* stylelint-disable-next-line declaration-property-value-allowed-list */
.nav-avatar--bubble { font-size: 0.85rem; font-weight: var(--fw-bold); color: var(--text); }
.nav-avatar--logo   { color: var(--text-muted); }
.nav-avatar--logo .icon { width: 1rem; height: 1rem; }

/* User-chip dropdown. The chip itself is a <button> that toggles
   #nav-user-dropdown; nav-auth.js owns the open/close logic. Anchored
   to the right edge of the auth area so the menu does not push the
   logo / links around. z-popover keeps it above page content but
   below modals (~mirrors residents/staff modal layering). */
.nav__user-menu {
  position: relative;
  display: inline-flex;
}
.nav__user--trigger {
  background: transparent;
  border: 0;
  padding: 0.15rem 0.4rem;
  border-radius: var(--radius-md);
  cursor: pointer;
  color: var(--text-muted);
  font: inherit;
  transition: background var(--motion-base), color var(--motion-base);
}
.nav__user--trigger:hover,
.nav__user--trigger[aria-expanded="true"] {
  background: var(--surface-2);
  color: var(--text);
}
.nav__user-caret {
  width: 0.85rem;
  height: 0.85rem;
  color: var(--text-muted);
  transition: transform var(--motion-base);
}
.nav__user--trigger[aria-expanded="true"] .nav__user-caret {
  transform: rotate(180deg);
}
.nav__user-dropdown {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  min-width: 11rem;
  list-style: none;
  margin: 0;
  padding: 0.3rem;
  /* Opaque surface so the dropdown stays readable when the page
     content behind it is busy (notably the map). --surface-2 alone is
     a 6% white tint and reads transparent against a satellite tile;
     --surface-overlay (0.92) plus the backdrop blur mirror the nav's
     frosted-glass look. */
  background: var(--surface-overlay);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popup);
  z-index: var(--z-popover);
}
.nav__user-dropdown[hidden] { display: none; }
.nav__user-dropdown-item {
  display: block;
  width: 100%;
  padding: 0.5rem 0.7rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
  text-align: left;
  text-decoration: none;
  cursor: pointer;
  transition: background var(--motion-base);
}
.nav__user-dropdown-item:hover,
.nav__user-dropdown-item:focus-visible {
  background: var(--surface-3, var(--surface-1));
  outline: none;
}

/* ============================================================
   CB-radio: site-wide live-activity panel (nav trigger)
   ============================================================
   Mirrors the user-menu disclosure (.nav__user-menu + dropdown): a
   relative wrapper, a pill trigger, and an absolutely-positioned panel
   on the same frosted --surface-overlay + blur so it stays readable
   over the map (a plain --surface-* tint reads transparent on satellite
   tiles -- see the user-dropdown note above). Phase 2: trigger + empty
   scaffold; the feed list, filter tabs, unread badge and pin-to-float
   "CB radio" mode arrive in later phases. */
.cb-radio {
  position: relative;
  display: inline-flex;
}
.cb-radio__trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.15rem 0.5rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  font: inherit;
  cursor: pointer;
  transition: background var(--motion-base), color var(--motion-base);
}
.cb-radio__trigger:hover,
.cb-radio__trigger[aria-expanded="true"] {
  background: var(--surface-2);
  color: var(--text);
}
.cb-radio__label {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
}
.cb-radio__panel {
  position: absolute;
  top: calc(100% + 0.4rem);
  right: 0;
  width: 21rem;
  max-width: calc(100vw - 2rem);
  background: var(--surface-overlay);
  backdrop-filter: blur(24px) saturate(180%);
  -webkit-backdrop-filter: blur(24px) saturate(180%);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-popup);
  z-index: var(--z-popover);
}
.cb-radio__panel[hidden] { display: none; }
.cb-radio__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.6rem 0.8rem;
  border-bottom: 1px solid var(--border-hairline);
}
.cb-radio__title {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin: 0;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text);
}
.cb-radio__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--motion-base), color var(--motion-base);
}
.cb-radio__close:hover {
  background: var(--surface-2);
  color: var(--text);
}
.cb-radio__body {
  max-height: 26rem;
  padding: 0.8rem;
  overflow-y: auto;
}
/* Unread-count badge on the trigger. Small red pill; hidden at zero (the
   `hidden` attribute) so it never shows an empty circle. */
.cb-radio__badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.1rem;
  height: 1.1rem;
  padding: 0 0.3rem;
  border-radius: var(--radius-pill);
  background: var(--red);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 1;
}
.cb-radio__badge[hidden] { display: none; }
/* Filter tabs sit between the header and the scrolling list; the group keeps
   its own bottom divider so the list reads as a separate region. */
.cb-radio__filters {
  margin: 0.6rem 0.8rem;
}
/* The feed reuses the garage .stream-event row. In the narrow 21rem panel the
   garage's 11ch fixed time column is too wide, so let the row wrap and show a
   compact time instead of crowding the body text. */
.cb-radio__body .stream-event {
  flex-wrap: wrap;
  padding: 0.5rem 0.2rem;
}
.cb-radio__body .stream-event__time {
  min-width: 0;
  flex-basis: 100%;
  order: 3;
  margin-left: 1.7rem;
}

/* --- Phase 4: pin -> floating window --------------------------------------
   The pin button detaches the panel from the nav into a free, fixed-position
   window that survives navigation; the dock buttons (non-drag alternative,
   WCAG 2.2) snap it to a screen corner; the header is a drag handle. JS sets
   left/top in px via the CSSOM (CSP-safe, like applyBarWidths). */
.cb-radio__tools {
  display: inline-flex;
  align-items: center;
  gap: 0.1rem;
}
.cb-radio__pin,
.cb-radio__home,
.cb-radio__popout,
.cb-radio__dock-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.2rem;
  background: transparent;
  border: 0;
  border-radius: var(--radius-md);
  color: var(--text-muted);
  cursor: pointer;
  transition: background var(--motion-base), color var(--motion-base);
}
.cb-radio__pin:hover,
.cb-radio__home:hover,
.cb-radio__popout:hover,
.cb-radio__dock-btn:hover {
  background: var(--surface-2);
  color: var(--text);
}

/* The home (reset-to-default) button exists only in float mode, like the dock. */
.cb-radio__home { display: none; }
.cb-radio__panel--float .cb-radio__home { display: inline-flex; }
/* Pressed pin = panel is floating; brighten it so the state reads at a glance. */
.cb-radio__pin[aria-pressed="true"] {
  background: var(--surface-2);
  color: var(--text);
}

/* Dock buttons exist only in float mode: a 2x2 grid of one corner glyph,
   rotated to point at each corner. */
.cb-radio__dock { display: none; }
.cb-radio__panel--float .cb-radio__dock {
  display: grid;
  grid-template-columns: repeat(2, auto);
  gap: 0.05rem;
  margin-right: 0.15rem;
}
.cb-radio__dock-btn { padding: 0.15rem; }
.cb-radio__dock-btn .icon { width: 0.85rem; height: 0.85rem; }
.cb-radio__dock-btn--tr .icon { transform: rotate(90deg); }
.cb-radio__dock-btn--br .icon { transform: rotate(180deg); }
.cb-radio__dock-btn--bl .icon { transform: rotate(270deg); }

/* Floating mode. position:fixed + a z-index above the nav; JS overrides
   left/top (the values here are only a pre-JS fallback). */
.cb-radio__panel--float {
  position: fixed;
  top: 4rem;
  left: 1rem;
  right: auto;
  bottom: auto;
  z-index: var(--z-float);
  width: 22rem;
}
.cb-radio__panel--float .cb-radio__head {
  cursor: grab;
  touch-action: none;   /* let the drag own the gesture instead of scrolling */
  user-select: none;
}
.cb-radio__panel--float.is-dragging { user-select: none; }
.cb-radio__panel--float.is-dragging .cb-radio__head { cursor: grabbing; }

/* Phone: a finger-dragged window is fiddly, so pinned mode becomes a fixed
   top overlay with no drag (memory: "Handy: Overlay ohne Drag"). JS clears its
   inline left/top and skips the drag handlers below this width. */
@media (max-width: 40rem) {
  .cb-radio__panel--float {
    top: 3.75rem;
    left: 0.6rem;
    right: 0.6rem;
    width: auto;
    max-width: none;
  }
  .cb-radio__panel--float .cb-radio__head {
    cursor: auto;
    touch-action: auto;
  }
}

/* Respect reduced-motion: no slide when the floating window is repositioned
   (it never animates position anyway, but kill any inherited transition). */
@media (prefers-reduced-motion: reduce) {
  .cb-radio__panel--float { transition: none; }
}

/* --- Phase 7: pop-out into its own window (Document Picture-in-Picture) -----
   When popped out, the panel IS the whole window: it fills it, drops the
   dropdown chrome (border/shadow/radius), and the body grows to scroll. The
   pin/dock/home/pop-out controls make no sense in a dedicated window, so they
   hide -- only the title + close (= bring it back) remain. */
.cb-radio__panel--popout {
  position: static;
  display: flex;
  flex-direction: column;
  width: 100%;
  height: 100vh;
  max-width: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
.cb-radio__panel--popout .cb-radio__body {
  max-height: none;
  flex: 1 1 auto;
}
.cb-radio__panel--popout .cb-radio__home,
.cb-radio__panel--popout .cb-radio__dock,
.cb-radio__panel--popout .cb-radio__pin,
.cb-radio__panel--popout .cb-radio__popout {
  display: none;
}
/* The pop-out window's <body> (class added by JS). style.css is copied into
   that window via its <link>, so these tokens resolve there too. Solid --bg
   because the window has nothing behind it for the panel's blur to pick up. */
.cb-radio-popout-body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
}

/* ============================================================
   Layout helpers
   ============================================================ */
.container {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 0 1.5rem;
}
.main { flex: 1; }

/* ============================================================
   Form basics
   ============================================================
   Global resets for native form elements so every new <input>,
   <select> or <textarea> picks up the dark-theme styling without
   needing a component-level class. Component classes win on
   specificity (0,1,0 > 0,0,1), so any existing styled control
   keeps its bespoke look. */

select,
textarea,
input[type="text"],
input[type="search"],
input[type="number"],
input[type="email"],
input[type="password"] {
  appearance: none;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  transition: border-color var(--motion-fast);
}
select { cursor: pointer; }
select:focus,
textarea:focus,
input[type="text"]:focus,
input[type="search"]:focus,
input[type="number"]:focus,
input[type="email"]:focus,
input[type="password"]:focus {
  outline: none;
  border-color: color-mix(in srgb, var(--red) 55%, transparent);
}
select option {
  background: var(--surface-overlay);
  color: var(--text);
}

/* ============================================================
   Breadcrumbs
   ============================================================
   Optional secondary navigation rendered above .main when the
   layout receives it.breadcrumbs from the route. */
.breadcrumbs {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0.6rem 1.5rem 0;
  font-size: var(--fs-sm);
}
.breadcrumbs__list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.breadcrumbs__item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}
.breadcrumbs__link {
  color: var(--text-muted);
  text-decoration: none;
  transition: color var(--motion-fast);
}
.breadcrumbs__link:hover { color: var(--text); }
.breadcrumbs__sep {
  color: var(--text-dim);
  flex-shrink: 0;
}
.breadcrumbs__current {
  color: var(--text);
  font-weight: var(--fw-medium);
}

/* ============================================================
   Hero
   ============================================================ */
.hero {
  min-height: 70vh;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  text-align: center;
  padding: 0 1.5rem 3.5rem;
  background:
    linear-gradient(to bottom,
      color-mix(in srgb, var(--bg) 10%, transparent) 0%,
      transparent 25%,
      color-mix(in srgb, var(--bg) 55%, transparent) 60%,
      var(--bg) 100%),
    url('/public/logo-hero.png') center 30% / cover no-repeat;
}
.hero__inner { max-width: 680px; }
.hero__subtitle {
  font-size: var(--fs-md);
  color: var(--text-muted);
  margin-bottom: 2.5rem;
  letter-spacing: 0.01em;
}
.hero__badges {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.65rem;
  flex-wrap: wrap;
}

/* --- Badge --- */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 1rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  color: var(--text-muted);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-highlight-top);
}
.badge--online {
  border-color: color-mix(in srgb, var(--status-green) 28%, transparent);
  background:   color-mix(in srgb, var(--status-green) 10%, transparent);
  color:        color-mix(in srgb, var(--status-green) 60%, white);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--status-green) 10%, transparent),
    0 0 18px       color-mix(in srgb, var(--status-green)  8%, transparent);
}
.badge--offline {
  border-color: color-mix(in srgb, var(--red) 28%, transparent);
  background:   color-mix(in srgb, var(--red) 10%, transparent);
  color:        color-mix(in srgb, var(--red) 60%, white);
  box-shadow:
    inset 0 1px 0 color-mix(in srgb, var(--red) 10%, transparent),
    0 0 18px       color-mix(in srgb, var(--red)  8%, transparent);
}
.badge--discord, .badge--motortown, .badge--steam {
  text-decoration: none;
  gap: 0.45rem;
  transition: background var(--motion-base), color var(--motion-base);
}
.badge--discord    { border-color: color-mix(in srgb, var(--brand-discord) 40%, transparent);  background: color-mix(in srgb, var(--brand-discord) 12%, transparent);  color: color-mix(in srgb, var(--brand-discord) 55%, white); }
.badge--motortown  { border-color: color-mix(in srgb, var(--map-poi)        35%, transparent);  background: color-mix(in srgb, var(--map-poi)         8%, transparent);  color: var(--brand-motortown); }
.badge--steam      { border-color: color-mix(in srgb, var(--brand-steam)    30%, transparent);  background: color-mix(in srgb, var(--brand-steam)     8%, transparent);  color: var(--brand-steam); }
.badge--discord:hover   { --accent: var(--brand-discord); background: color-mix(in srgb, var(--brand-discord) 22%, transparent); }
.badge--motortown:hover { background: color-mix(in srgb, var(--map-poi)       18%, transparent); color: color-mix(in srgb, var(--map-poi)       60%, white); }
.badge--steam:hover     { background: color-mix(in srgb, var(--brand-steam)   18%, transparent); color: color-mix(in srgb, var(--brand-steam)   55%, white); }
.badge__dot {
  width: var(--size-xs);
  height: var(--size-xs);
  border-radius: var(--radius-full);
  background: currentColor;
  animation: pulse 2s infinite;
}
@keyframes pulse {
  0%, 100% { opacity: 1; }
  50%       { opacity: 0.3; }
}

/* ============================================================
   Section cards (homepage)
   ============================================================ */
.section-cards {
  padding: 3rem 0 5rem;
}
.section-cards__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
  gap: 1rem;
}
.section-card {
  display: block;
  padding: 1.6rem 1.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(18px) saturate(160%);
  box-shadow: var(--shadow-card), var(--shadow-highlight-top-mid);
  transition: background var(--motion-slow), border-color var(--motion-slow), transform var(--motion-slow), box-shadow var(--motion-slow);
  color: var(--text);
}
.section-card:hover {
  background: var(--surface-3);
  border-color: var(--border-emphasis);
  color: var(--text);
  transform: translateY(-3px);
  box-shadow: var(--shadow-modal), var(--shadow-highlight-top-strong);
}
.section-card__label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--red);
  margin-bottom: 0.55rem;
  opacity: 0.8;
}
.section-card h2 {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  margin-bottom: 0.45rem;
  letter-spacing: 0.01em;
}
.section-card p {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  line-height: var(--lh-body);
}

/* ============================================================
   Community intro
   ============================================================ */
.community {
  padding: 3.5rem 0;
  max-width: 720px;
}
.community__heading {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  margin-bottom: 1.1rem;
  letter-spacing: 0.01em;
}
.community__text {
  color: var(--text-muted);
  font-size: var(--fs-base);
  line-height: 1.85;
  margin-bottom: 0.85rem;
}
.community__text:last-child { margin-bottom: 0; }

/* ============================================================
   Divider
   ============================================================ */
.divider {
  border: none;
  border-top: 1px solid var(--border-hairline);
  margin: 0;
}

/* ============================================================
   Footer
   ============================================================ */
.footer {
  padding: 1.5rem 0;
  border-top: 1px solid var(--border-hairline);
  color: var(--text-dim);
  font-size: var(--fs-sm);
}
.footer__inner {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 0 1.5rem;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}
.footer__logo-img {
  height: 28px;
  width: auto;
  opacity: 0.4;
  mix-blend-mode: screen;
}
.footer__legal {
  max-width: 62ch;
  margin: 0.8rem auto 0;
  padding: 0 1.5rem;
  text-align: center;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: var(--text-dim);
  opacity: 0.55;
}

/* ============================================================
   Page layout (Rules, Police, Support etc.)
   ============================================================ */
.page {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 3rem 1.5rem;
}
.page--wide { max-width: var(--max-w-dashboard); }
.page__header {
  margin-bottom: 2.5rem;
  padding-bottom: 1.5rem;
  border-bottom: 1px solid var(--border-hairline);
}
.page__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-2xl);
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
}
.page__title span { color: var(--red); }
.page__subtitle { color: var(--text-muted); margin-top: 0.45rem; font-size: var(--fs-base); }

/* ============================================================
   Buttons
   Single call-to-action style shared by Ko-fi, newspaper download
   and the gallery "load more" link. Visually echoes
   .nav__link.active so a click target reads as the same "tinted
   red pill" anywhere on the page; hover deepens the tint. No
   variants needed -- every .btn in the codebase wants the same
   "click me, something happens" affordance.
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.58rem 1.3rem;
  border-radius: var(--radius-pill);
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  letter-spacing: 0.01em;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  background:   color-mix(in srgb, var(--red) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--red) 22%, transparent);
  color:        color-mix(in srgb, var(--red) 55%, white);
  transition:
    background   var(--motion-base),
    border-color var(--motion-base),
    color        var(--motion-base),
    transform    var(--motion-fast);
}
.btn:hover {
  background:   color-mix(in srgb, var(--red) 20%, transparent);
  border-color: color-mix(in srgb, var(--red) 45%, transparent);
  color:        color-mix(in srgb, var(--red) 70%, white);
}
.btn:active { transform: scale(0.97); }
.btn[disabled] { opacity: 0.6; cursor: progress; }

/* Compact variant for nav-height contexts (Sign-in / Log-out chip).
   Same brand-red palette inherited from .btn; only padding and font-
   weight tighten so the button sits at the same height as the
   neighbouring .nav-avatar. */
.btn--compact {
  padding: 0.36rem 0.85rem;
  font-weight: var(--fw-medium);
}
/* ============================================================
   Newspaper page
   ============================================================ */
.reader {
  display: flex;
  flex-direction: column;
  height: calc(100vh - var(--nav-h));
}
.reader__bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--border-hairline);
  flex-shrink: 0;
}
.reader__back {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  white-space: nowrap;
}
.reader__back:hover { color: var(--text); }
.reader__title {
  flex: 1;
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
  text-align: center;
}
.reader__frame {
  flex: 1;
  width: 100%;
  border: none;
  background: var(--surface-1);
}

.issues__heading {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  margin-bottom: 1.25rem;
  letter-spacing: 0.01em;
}
.issues__empty {
  color: var(--text-muted);
  font-size: var(--fs-base);
}
.issue-card {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-card), var(--shadow-highlight-top);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform var(--motion-slow), background var(--motion-slow), border-color var(--motion-slow);
}
.issue-card__canvas,
.issue-card__cover-wrap img { transition: transform var(--motion-animate); }

.issues__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1.25rem;
}
.issues__grid > li:first-child {
  border-color: color-mix(in srgb, var(--red) 45%, transparent);
  background: color-mix(in srgb, var(--red) 7%, transparent);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--red) 20%, transparent), 0 4px 28px color-mix(in srgb, var(--red) 12%, transparent), var(--shadow-highlight-top);
}
.issues__grid > li:first-child .issue-card__edition { color: color-mix(in srgb, var(--red) 50%, white); }

.issues__latest-badge {
  position: absolute;
  top: 0.65rem;
  left: 0.65rem;
  padding: 0.2rem 0.65rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--red) 88%, transparent);
  backdrop-filter: blur(8px);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text);
  box-shadow: var(--shadow-popup);
}
.issue-card:hover {
  border-color: var(--border-emphasis);
  background: var(--surface-2);
  transform: translateY(-3px);
  position: relative;
  z-index: var(--z-base);
}
.issue-card:hover .issue-card__cover-wrap img,
.issue-card:hover .issue-card__canvas {
  transform: scale(1.03);
}
.issue-card__cover-wrap {
  position: relative;
  background: var(--surface-1);
  aspect-ratio: 1 / 1.414;
  overflow: hidden;
  cursor: pointer;
  display: block;
}
.issue-card__placeholder {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text-dim);
}
.issue-card__canvas {
  width: 100%;
  height: 100%;
  display: block;
}
.issue-card__canvas.hidden { display: none; }
.issue-card__info {
  padding: 0.75rem 1rem 0.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
}
.issue-card__edition {
  font-weight: var(--fw-bold);
  font-size: var(--fs-base);
}
.issue-card__date {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
/* ============================================================
   Support page
   ============================================================ */

.support-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 1.25rem;
}
.support-card {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-card), var(--shadow-highlight-top);
  padding: 1.75rem 2rem;
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.support-card__heading {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
}
.support-card__intro {
  color: var(--text-muted);
  font-size: var(--fs-base);
}
.support-list {
  list-style: disc;
  padding-left: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.support-list li {
  color: var(--text-muted);
  font-size: var(--fs-base);
}
.support-card__note {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  border-top: 1px solid var(--border-hairline);
  padding-top: 0.75rem;
}
.support-card__cta {
  align-self: flex-start;
  margin-top: 0.25rem;
}

/* ============================================================
   Police Department page
   ============================================================ */
.pd-sections {
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.pd-section {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-card), var(--shadow-highlight-top);
  padding: 1.75rem 2rem;
}
.pd-section__heading {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  margin-bottom: 1rem;
  letter-spacing: 0.01em;
}
.pd-list {
  list-style: disc;
  padding-left: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.pd-list li {
  color: var(--text-muted);
  font-size: var(--fs-base);
}
.pd-text {
  color: var(--text-muted);
  font-size: var(--fs-base);
  line-height: 1.8;
}

/* ============================================================
   Rules page
   ============================================================ */
.rules-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}
.rule {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-card), var(--shadow-highlight-top);
  padding: 1.75rem 2rem;
}
.rule__header {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  margin-bottom: 1rem;
}
.rule__number {
  font-size: var(--fs-xs);
  font-weight: var(--fw-hero);
  letter-spacing: 0.1em;
  color: var(--red);
  white-space: nowrap;
  opacity: 0.75;
}
.rule__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
}
.rule__body p {
  color: var(--text-muted);
  margin-bottom: 0.75rem;
  font-size: var(--fs-base);
}
.rule__body p:last-child { margin-bottom: 0; }
.rule__body strong { color: var(--text); }
.rule__body ul {
  list-style: disc;
  padding-left: 1.25rem;
  margin-bottom: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.rule__body ul li {
  color: var(--text-muted);
  font-size: var(--fs-base);
}
/* Higher selector specificity (.rule__body .rule__note instead of just
   .rule__note) so the note's smaller dimmer text wins against the
   sibling .rule__body p rule above, without needing !important. */
.rule__body .rule__note {
  font-size: var(--fs-sm);
  color: var(--text-dim);
  font-style: italic;
  border-top: 1px solid var(--border-hairline);
  padding-top: 0.75rem;
  margin-top: 0.75rem;
}
.rules-footer {
  margin-top: 2.5rem;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-base);
  padding-bottom: 1rem;
}

/* ============================================================
   Gallery
   ============================================================ */
.gallery__empty {
  color: var(--text-muted);
  font-size: var(--fs-base);
  padding: 2rem 0;
}

.gallery__grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 0.85rem;
}

.gallery__item {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: var(--radius-lg);
  cursor: pointer;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  box-shadow: var(--shadow-card);
  transition: transform var(--motion-slow), box-shadow var(--motion-slow), border-color var(--motion-slow);
}
.gallery__item:hover {
  transform: translateY(-3px) scale(1.01);
  border-color: var(--border-strong);
  box-shadow: var(--shadow-modal);
}
.gallery__item img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--motion-animate);
}
.gallery__item:hover img { transform: scale(1.04); }
.gallery__item--hidden   { display: none; }

.gallery__more {
  display: flex;
  justify-content: center;
  padding: 2.5rem 0 1rem;
}
.gallery__more-count {
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  color: var(--text-muted);
  margin-left: 0.25rem;
}

/* --- Lightbox --- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--surface-overlay);
  backdrop-filter: blur(20px) saturate(120%);
  -webkit-backdrop-filter: blur(20px) saturate(120%);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--motion-slow);
}
.lightbox--open {
  opacity: 1;
  pointer-events: all;
}

.lightbox__stage {
  max-width: calc(100vw - 140px);
  max-height: calc(100vh - 80px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.lightbox__img {
  max-width: 100%;
  max-height: calc(100vh - 80px);
  object-fit: contain;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  user-select: none;
  display: block;
}

.lightbox__nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  width: 52px;
  height: 52px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  backdrop-filter: blur(14px);
  color: var(--text);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-popup), var(--shadow-highlight-top-mid);
  transition: background var(--motion-base), border-color var(--motion-base), transform var(--motion-base);
  z-index: var(--z-overlay);
}
.lightbox__nav:hover {
  background: var(--surface-3);
  border-color: var(--border-emphasis);
  transform: translateY(-50%) scale(1.08);
}
.lightbox__nav--prev { left: 1.25rem; }
.lightbox__nav--next { right: 1.25rem; }
.lightbox__nav .icon   { width: var(--size-2xl); height: var(--size-2xl); }
.lightbox__close .icon { width: var(--size-xl); height: var(--size-xl); }

.lightbox__close {
  position: absolute;
  top: 1.1rem;
  right: 1.1rem;
  width: 42px;
  height: 42px;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-strong);
  background: var(--surface-2);
  backdrop-filter: blur(14px);
  color: var(--text-muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-popup), var(--shadow-highlight-top-mid);
  transition: background var(--motion-base), color var(--motion-base);
  z-index: var(--z-overlay);
}
.lightbox__close:hover { background: var(--surface-3); color: var(--text); }

.lightbox__counter {
  position: absolute;
  bottom: 1.4rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.38rem 1rem;
  border-radius: var(--radius-pill);
  background: var(--surface-overlay);
  border: 1px solid var(--border-soft);
  backdrop-filter: blur(14px);
  box-shadow: var(--shadow-popup), var(--shadow-highlight-top);
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  color: var(--text);
  pointer-events: none;
  white-space: nowrap;
}
.lightbox__sep { color: var(--text-muted); margin: 0 0.1rem; }

/* ============================================================
   Map page
   ============================================================ */
.map-page {
  --map-sidebar-w:           260px;
  --map-sidebar-collapsed-w: 26px;

  position: relative;
  height: calc(100vh - var(--nav-h));
  overflow: hidden;
}

/* --- Toolbar --- */
.map-toolbar {
  position: absolute;
  top: 0; left: 0; right: 0;
  height: 54px;
  z-index: var(--z-map-control);
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0.75rem;
  pointer-events: none;
}
.map-toolbar > * { pointer-events: auto; }


/* --- Town bonus panel --- */
.map-town-bonuses {
  position: absolute;
  top: 12px; right: calc(var(--map-sidebar-w) + 12px);
  z-index: var(--z-map-control);
  transition: right var(--motion-slow) ease;
  display: flex; flex-direction: column; gap: 4px;
  pointer-events: none;
}
.map-tb-chip {
  position: relative; overflow: hidden;
  display: flex; align-items: center; gap: 0.5rem;
  height: 26px;
  padding: 0 0.6rem 0 0.55rem;
  border-radius: var(--radius-pill);
  background: var(--surface-overlay);
  border: 1px solid var(--border-soft);
  backdrop-filter: blur(16px);
  box-shadow: var(--shadow-popup);
  min-width: 150px;
}
.map-tb-chip__fill {
  position: absolute; top: 0; left: 0; bottom: 0;
  transition: width var(--motion-animate) ease;
  opacity: 0.18;
  border-radius: var(--radius-pill);
  background: var(--accent);
  width: var(--fill, 0%);
}
.map-tb-chip__name {
  position: relative; z-index: var(--z-base);
  font-size: var(--fs-xs); font-weight: var(--fw-medium);
  color: var(--text-overlay-muted);
  white-space: nowrap; flex: 1;
}
.map-tb-chip__val {
  position: relative; z-index: var(--z-base);
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--accent);
}

/* --- Layers bar --- */
.map-layers-bar { display: flex; gap: 0.35rem; flex-shrink: 0; }

/* Group trigger pill -- one per category (POI / Live / Housing).
   Opens a dropdown with the layer checkboxes for that group. */
.map-layers-group { position: relative; }
.map-layers-group__trigger {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  padding: 0.32rem 0.9rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--map-poi) 8%, var(--surface-overlay));
  backdrop-filter: blur(20px) saturate(180%);
  box-shadow: var(--shadow-popup);
  color: var(--text-overlay-muted);
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  cursor: pointer; white-space: nowrap;
  transition: background var(--motion-base), color var(--motion-base), border-color var(--motion-base);
}
.map-layers-group__trigger:hover {
  background: color-mix(in srgb, var(--map-poi) 14%, var(--surface-overlay));
}
.map-layers-group__trigger--on {
  background: color-mix(in srgb, var(--map-poi) 18%, var(--surface-overlay));
  border-color: color-mix(in srgb, var(--map-poi) 40%, transparent);
  color: var(--map-poi);
}
.map-layers-group__caret {
  display: inline-flex;
  align-items: center;
  color: var(--text-overlay-muted);
  transition: transform var(--motion-base);
}
.map-layers-group__trigger--on .map-layers-group__caret {
  color: var(--map-poi);
}
.map-layers-group__trigger--open .map-layers-group__caret {
  transform: rotate(180deg);
}

/* Dropdown panel: column of checkbox items. Positioned absolutely
   under the trigger; high z so it floats above the map. */
.map-layers-dropdown {
  position: absolute;
  top: calc(100% + 0.4rem);
  left: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.4rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  background: var(--surface-overlay);
  backdrop-filter: blur(20px) saturate(180%);
  box-shadow: var(--shadow-popup);
  z-index: var(--z-popover);
  min-width: 11rem;
}

/* Items inside the dropdown: checkbox-style. The item stays the same
   visual shape it had on the flat bar -- only the layout context
   (column inside a panel) changes. */
.map-layers-item {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.32rem 0.7rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-sm);
  background: transparent;
  color: var(--text-overlay-muted);
  font: inherit; font-size: var(--fs-sm); font-weight: var(--fw-medium);
  cursor: pointer; white-space: nowrap;
  transition: background var(--motion-base), color var(--motion-base), border-color var(--motion-base);
  text-align: left;
}
.map-layers-item:hover {
  background: color-mix(in srgb, var(--map-poi) 14%, transparent);
  color: var(--text-overlay-muted);
}
.map-layers-item--on  {
  background: color-mix(in srgb, var(--map-poi) 18%, transparent);
  border-color: color-mix(in srgb, var(--map-poi) 40%, transparent);
  color: var(--map-poi);
}
.map-layers-label { flex: 1; }
.map-layers-check { font-size: var(--fs-xs); opacity: 0; transition: opacity var(--motion-base); }
.map-layers-item--on .map-layers-check { opacity: 1; color: var(--map-poi); }

/* --- Search --- */
.map-search {
  position: relative;
  flex-shrink: 0;
}
.map-search__wrap {
  position: relative;
  display: flex;
  align-items: center;
}
.map-search__icon {
  position: absolute;
  left: 0.6rem;
  color: var(--text-muted);
  pointer-events: none;
}
input.map-search__input {
  width: 200px;
  /* Sits over the map tiles, so --surface-1 (a 4% white wash) would
     still let the tiles bleed through. --bg is the only token that
     resolves to a solid colour in the palette. */
  background: var(--bg);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  color: var(--text);
  font: inherit;
  font-size: var(--fs-sm);
  padding: 0.32rem 0.75rem 0.32rem 2rem;
  outline: none;
  box-shadow: var(--shadow-popup), var(--shadow-highlight-top);
  transition: border-color var(--motion-base), width var(--motion-slow);
}
input.map-search__input:focus {
  border-color: color-mix(in srgb, var(--red) 50%, transparent);
  width: 240px;
}
input.map-search__input::placeholder { color: var(--text-muted); }

.map-search__results {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 280px;
  background: var(--surface-overlay);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  overflow: hidden;
  z-index: var(--z-modal);
  max-height: 220px;
  overflow-y: auto;
  backdrop-filter: blur(20px);
  box-shadow: var(--shadow-modal), var(--shadow-highlight-top);
}
.map-search__item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.85rem;
  cursor: pointer;
  font-size: var(--fs-sm);
  transition: background var(--motion-fast);
}
.map-search__item:hover { background: var(--surface-2); }
.map-search__type {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-muted);
  width: 48px;
  flex-shrink: 0;
}
.map-search__type--online  { color: var(--status-green); }
.map-search__type--offline { color: var(--text-muted); }
.map-search__name  { color: var(--text); flex: 1; }
.map-search__empty { padding: 0.7rem 0.85rem; font-size: var(--fs-sm); color: var(--text-muted); }

/* --- Map canvas --- */
.map {
  position: absolute;
  top: 0; left: 0; right: var(--map-sidebar-w); bottom: 0;
  background: var(--bg);
  transition: right var(--motion-slow) ease;
}
.map-page:has(.map-sidebar--collapsed) .map             { right: var(--map-sidebar-collapsed-w); }
.map-page:has(.map-sidebar--collapsed) .map-town-bonuses { right: calc(var(--map-sidebar-collapsed-w) + 12px); }

/* --- Player sidebar --- */
.map-sidebar {
  position: absolute;
  top: 0; right: 0; bottom: 0;
  width: var(--map-sidebar-w);
  background: var(--surface-overlay);
  border-left: 1px solid var(--border-hairline);
  backdrop-filter: blur(12px);
  display: flex; flex-direction: row;
  z-index: var(--z-map-layer);
  transition: transform var(--motion-slow) ease;
}
.map-sidebar--collapsed {
  transform: translateX(calc(100% - var(--map-sidebar-collapsed-w)));
}
.map-sidebar__toggle {
  width: 26px;
  flex-shrink: 0;
  background: none;
  border: none;
  border-right: 1px solid var(--border-hairline);
  color: var(--text-overlay-muted);
  font-size: var(--fs-sm);
  cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.map-sidebar__toggle:hover {
  color: var(--text);
  background: var(--surface-1);
}
.map-sidebar__inner {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.map-sidebar__panel {
  flex: 1;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.map-sidebar__list {
  flex: 1; overflow-y: auto;
  padding: 0.35rem 0;
  scrollbar-width: thin;
  scrollbar-color: var(--surface-3) transparent;
}
.map-sidebar__empty {
  padding: 1rem 0.9rem;
  font-size: var(--fs-sm); color: var(--text-dim);
  font-style: italic;
}

/* Sidebar sections: collapsible groups (Tow / Police / Players).
   Header is a button that flips the --collapsed modifier; the body
   hides via display:none when collapsed. State persists via
   localStorage so the user's preference survives reloads. */
.map-sidebar__section + .map-sidebar__section {
  margin-top: 0.5rem;
}
.map-sidebar__section-head {
  display: flex; align-items: center; gap: 0.4rem;
  width: 100%;
  padding: 0.4rem 0.75rem 0.25rem;
  background: transparent;
  border: 0;
  font: inherit;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--text-muted);
  cursor: pointer;
  text-align: left;
}
.map-sidebar__section-head:hover { color: var(--text); }
.map-sidebar__section-caret {
  flex-shrink: 0;
  color: var(--text-muted);
  transition: transform var(--motion-base);
}
.map-sidebar__section--collapsed .map-sidebar__section-caret {
  transform: rotate(-90deg);
}
.map-sidebar__section-title { flex: 1; }
.map-sidebar__section-count {
  background: var(--surface-2);
  border-radius: var(--radius-pill);
  padding: 0 0.4rem;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  color: var(--text);
}
.map-sidebar__section--collapsed .map-sidebar__section-body { display: none; }

/* Activity rows: shared shape for Tow + Police entries. Two-line
   layout matches the player rows below so the whole list reads as one
   visual rhythm. */
.map-activity {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.45rem 0.75rem;
  border-bottom: 1px solid var(--border-hairline);
  transition: background var(--motion-fast);
}
.map-activity--locatable { cursor: pointer; }
.map-activity--locatable:hover { background: var(--surface-1); }
.map-activity__icon {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0;
  width: 1.5rem; height: 1.5rem;
  border-radius: var(--radius-full);
  color: var(--text);
}
.map-activity--tow    .map-activity__icon { background: var(--tow-marker); }
.map-activity--police .map-activity__icon { background: var(--role-police); }
.map-activity__info { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.map-activity__name {
  font-size: var(--fs-dense); font-weight: var(--fw-medium);
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-activity__detail {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* --- Map bottom controls (heatmap + track bars) --- */
.map-bottom-controls {
  position: absolute;
  bottom: 2.5rem;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.4rem;
  z-index: var(--z-map-layer);
  pointer-events: none;
}

/* --- Map legend --- */
/* Floating button at the bottom-right corner of the map area (just
   inside the sidebar). Click toggles the legend popover above it.
   Same offset (12px) as the town-bonuses panel keeps the right edge
   visually aligned. */
.map-legend-btn {
  position: absolute;
  right: calc(var(--map-sidebar-w) + 12px);
  bottom: 12px;
  z-index: var(--z-map-control);
  width: var(--size-4xl); height: var(--size-4xl);
  border-radius: var(--radius-full);
  border: 1px solid var(--border-emphasis);
  background: var(--surface-overlay);
  backdrop-filter: blur(12px);
  color: var(--text);
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  cursor: pointer;
  transition: right var(--motion-slow) ease, background var(--motion-base), border-color var(--motion-base);
}
.map-legend-btn:hover { background: color-mix(in srgb, var(--surface-3) 50%, var(--surface-overlay)); border-color: var(--border-emphasis); }
.map-legend-btn--open { background: color-mix(in srgb, var(--route) 22%, transparent); border-color: var(--route); color: var(--text); }
.map-page:has(.map-sidebar--collapsed) .map-legend-btn {
  right: calc(var(--map-sidebar-collapsed-w) + 12px);
}

.map-legend {
  position: absolute;
  right: calc(var(--map-sidebar-w) + 12px);
  bottom: 52px;   /* above the legend button (32px + 12px + 8px gap) */
  z-index: var(--z-map-control);
  width: 220px;
  padding: 0.85rem 1rem;
  background: var(--surface-overlay);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  backdrop-filter: blur(12px);
  font-size: var(--fs-xs);
  color: var(--text);
  transition: right var(--motion-slow) ease;
}
.map-page:has(.map-sidebar--collapsed) .map-legend {
  right: calc(var(--map-sidebar-collapsed-w) + 12px);
}
.map-legend__title {
  font-size: var(--fs-sm);
  font-weight: var(--fw-bold);
  margin: 0 0 0.5rem 0;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}
.map-legend__section + .map-legend__section { margin-top: 0.7rem; }
.map-legend__heading {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  color: var(--text-dim);
  margin-bottom: 0.3rem;
}
.map-legend__row {
  display: flex; align-items: center;
  gap: 0.5rem;
  padding: 0.15rem 0;
  color: var(--text);
}
.map-legend__swatch {
  display: inline-block;
  width: var(--size-sm); height: var(--size-sm);
  border-radius: var(--radius-full);
  border: 1.5px solid var(--text);
  flex-shrink: 0;
}
.map-legend__swatch--player        { background: var(--red); }
.map-legend__swatch--player-follow { background: var(--red); border-color: color-mix(in srgb, var(--red) 60%, var(--bg)); }
.map-legend__swatch--fire          { background: var(--fire); }
/* Building swatches mirror the marker shape: square instead of round. */
.map-legend__swatch--poi,
.map-legend__swatch--housing {
  border-radius: var(--radius-sm);
}
.map-legend__swatch--poi     { background: var(--map-poi); }
.map-legend__swatch--housing { background: var(--map-housing); }

/* Composite swatch: player dot with a small badge stuck to its
   bottom-right, matching the live marker rendering. */
.map-legend__composite {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-sm); height: var(--size-sm);
  flex-shrink: 0;
}
.map-legend__composite .map-legend__swatch { display: block; }
.map-legend__badge {
  position: absolute;
  top: 55%; left: 55%;
  display: inline-flex; align-items: center; justify-content: center;
  width: 12px; height: 12px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--text);
  color: var(--text);
}
.map-legend__badge .icon { width: 8px; height: 8px; }
.map-legend__badge--police { background: var(--role-police); }
.map-legend__badge--tow    { background: var(--tow-marker); }
.map-legend__badge--helper { background: var(--status-green); }
.map-legend__heat-bar {
  height: 8px;
  border-radius: var(--radius-sm);
  background: linear-gradient(
    to right,
    var(--heat-cold) 0%,
    var(--heat-cool) 35%,
    var(--heat-mid)  65%,
    var(--heat-warm) 85%,
    var(--heat-hot)  100%
  );
  margin-bottom: 0.2rem;
}
.map-legend__heat-scale {
  display: flex; justify-content: space-between;
  font-size: var(--fs-xs);
  color: var(--text-dim);
}
.heat-slider-bar,
.track-bar {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--surface-overlay);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-md);
  padding: 0.45rem 1rem;
  pointer-events: all;
  white-space: nowrap;
}
.heat-slider-bar__title {
  font-size: var(--fs-xs); color: var(--text-dim);
  text-transform: uppercase; letter-spacing: 0.04em;
}
.heat-slider-bar__range,
.track-bar__range { width: 140px; cursor: pointer; }
.heat-slider-bar__range { accent-color: var(--fire); }

.heat-slider-bar__player,
.track-bar__player {
  font-size: var(--fs-xs);
  color: var(--status-orange);
  max-width: 120px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.heat-mode-group {
  display: flex;
  gap: 2px;
}
.heat-mode-btn {
  padding: 2px 8px;
  font-size: var(--fs-xs);
  background: var(--surface-2);
  border: 1px solid var(--border-emphasis);
  border-radius: var(--radius-sm);
  color: var(--text-overlay-muted);
  cursor: pointer;
  white-space: nowrap;
}
.heat-mode-btn--on {
  background: color-mix(in srgb, var(--fire) 30%, transparent);
  border-color: var(--fire);
  color: var(--text);
}
.heat-tip-wrap {
  position: relative;
  display: flex;
  align-items: center;
}
.heat-tip-wrap::after {
  content: attr(data-tip);
  position: absolute;
  bottom: calc(100% + 10px);
  left: 50%;
  transform: translateX(-50%);
  width: 220px;
  padding: 7px 10px;
  background: var(--surface-overlay);
  border: 1px solid var(--border-emphasis);
  border-radius: var(--radius-sm);
  color: var(--text-overlay-strong);
  font-size: var(--fs-xs);
  line-height: 1.45;
  pointer-events: none;
  white-space: normal;
  opacity: 0;
  transition: opacity var(--motion-base);
  z-index: var(--z-nav);
}
.heat-tip-wrap:hover::after {
  opacity: 1;
}
.route-toggle-btn {
  padding: 3px 10px;
  font-size: var(--fs-xs);
  background: var(--surface-2);
  border: 1px solid var(--border-emphasis);
  border-radius: var(--radius-sm);
  color: var(--text-overlay-muted);
  cursor: pointer;
  white-space: nowrap;
}
.route-toggle-btn:hover {
  background: var(--surface-3);
  color: var(--text);
}
.route-toggle-btn--active {
  background: color-mix(in srgb, var(--route) 20%, transparent);
  border-color: var(--route);
  color: var(--route);
}
.route-toggle-btn--active:hover {
  background: color-mix(in srgb, var(--route) 32%, transparent);
}
.resume-follow-btn {
  padding: 3px 10px;
  font-size: var(--fs-xs);
  border-radius: var(--radius-sm);
  cursor: pointer;
  white-space: nowrap;
  background: color-mix(in srgb, var(--status-orange) 20%, transparent);
  border: 1px solid var(--status-orange);
  color: var(--status-orange);
}
.resume-follow-btn:hover {
  background: color-mix(in srgb, var(--status-orange) 35%, transparent);
}
.resume-follow-btn--paused {
  background: color-mix(in srgb, var(--status-green) 20%, transparent);
  border-color: var(--status-green);
  color: var(--status-green);
}
.resume-follow-btn--paused:hover {
  background: color-mix(in srgb, var(--status-green) 35%, transparent);
}
.heat-slider-bar__label,
.track-bar__label {
  font-size: var(--fs-sm);
  color: var(--text);
  min-width: 4rem;
  text-align: right;
}
.track-bar__date {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  white-space: nowrap;
}
.track-bar__range { accent-color: var(--heat-hot); }
.track-arrow {
  /* Leaflet's L.divIcon paints its own background and border on the
     wrapper; the only way to render a transparent SVG glyph is to
     null those out, which requires !important to beat Leaflet's
     bundled CSS specificity. */
  /* stylelint-disable-next-line declaration-no-important */
  background: none !important;
  /* stylelint-disable-next-line declaration-no-important */
  border: none !important;
  /* Per-arrow rotation. Angle is set via DOM-API (setProperty('--angle',
     ...)) after Leaflet creates the marker element -- inline style="" on
     the SVG would need CSP 'unsafe-inline'. */
  transform: rotate(var(--angle, 0deg));
  /* Glyph for a Leaflet marker; sized in px to stay independent of
     the document type scale (markers must look the same regardless
     of which page they appear on). */
  /* stylelint-disable-next-line declaration-property-value-allowed-list */
  font-size: 16px;
  line-height: 1;
  text-shadow: var(--map-glow-text);
  pointer-events: none;
}
.map-player {
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.45rem 0.9rem;
  cursor: pointer;
  border-left: 2px solid transparent;   /* placeholder so the layout doesn't shift on selection */
  transition: background var(--motion-base);
}
.map-player:hover { background: var(--surface-1); }
/* Selection follows the project's "active control" pattern (see
   .heat-mode-btn--on and .route-toggle-btn--active): saturated tint on
   the background plus a coloured border in the same hue. The hue
   matches the red player marker on the map. */
.map-player--follow {
  background: color-mix(in srgb, var(--red) 22%, transparent);
  border-left-color: var(--red);
}
.map-player--follow:hover { background: color-mix(in srgb, var(--red) 30%, transparent); }
/* Fixed-width column on the left so player names line up across rows
   regardless of how many roles each player carries. Roles align right
   within the column so the gap between names and roles stays constant
   whether a player has 0 or 3 roles. Width fits three 8px dots (incl.
   their white border) with 3px gaps. The white border echoes the
   player markers on the map so the dots read as "this is a player". */
.map-player__roles {
  display: inline-flex; align-items: center; justify-content: flex-end;
  gap: 3px;
  width: 30px;
  flex-shrink: 0;
}
.map-player__role {
  width: var(--size-xs); height: var(--size-xs);
  border-radius: var(--radius-full);
  border: 1.5px solid var(--text);
  box-sizing: content-box;
  flex-shrink: 0;
}
/* Role colours come from the global tokens (see :root). */
.map-player__role--police     { background: var(--role-police); }
.map-player__role--admin      { background: var(--role-admin); }
.map-player__role--enthusiast { background: var(--role-enthusiast); }
.map-player__role--none       { background: var(--role-none); }
/* On-duty pulse: a Police-role player who is currently driving a police
   vehicle. The expanding ring draws the eye without dominating the row.
   The police hue comes from the role token via color-mix so it stays in
   sync with --role-police. */
.map-player__role--on-duty {
  animation: police-on-duty 1.6s ease-in-out infinite;
}
@keyframes police-on-duty {
  0%, 100% { box-shadow: 0 0 0 0   color-mix(in srgb, var(--role-police) 70%, transparent); }
  50%      { box-shadow: 0 0 0 4px color-mix(in srgb, var(--role-police) 0%,  transparent); }
}
.map-player__info { display: flex; flex-direction: column; gap: 0.05rem; min-width: 0; }
.map-player__name {
  font-size: var(--fs-dense); font-weight: var(--fw-medium); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-player__vehicle {
  font-size: var(--fs-xs); color: var(--text-dim);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.map-player__vehicle--idle {
  color: var(--text-muted);
  font-style: italic;
}
.map-player__speed {
  margin-left: auto; flex-shrink: 0;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}

/* --- Markers --- */
.player-marker,
.poi-marker      { background: transparent; border: none; }

.player-marker__dot {
  display: block;
  width: var(--size-sm);
  height: var(--size-sm);
  border-radius: var(--radius-full);
  background: var(--red);
  border: 2px solid var(--text);
  box-shadow: var(--map-glow-box);
}

.player-marker--follow .player-marker__dot {
  width: var(--size-lg);
  height: var(--size-lg);
  /* Selected marker keeps the regular red fill so the only colour on
     the player layer is red -- the pulse + larger size are what signal
     selection, not a different hue. */
  border-color: color-mix(in srgb, var(--red) 60%, var(--bg));
  animation: follow-pulse 1.6s ease-in-out infinite;
}

@keyframes follow-pulse {
  0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--red) 60%, transparent), var(--map-glow-box); }
  50%      { box-shadow: 0 0 0 8px color-mix(in srgb, var(--red) 0%,  transparent), var(--map-glow-box); }
}

/* Badge layer: small icons stuck to the bottom-right of the player
   dot. Renders inline inside the divIcon HTML so badges move with
   the player on every map tick. */
.player-marker__badges {
  position: absolute;
  top: 60%;
  left: 60%;
  display: flex;
  gap: 2px;
  pointer-events: auto;
}
.player-marker__badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--text);
  box-shadow: var(--map-glow-box);
  color: var(--text);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  line-height: 1;
}
.player-marker__badge .icon { width: 10px; height: 10px; }
.player-marker__badge--police { background: var(--role-police); }
.player-marker__badge--tow    { background: var(--tow-marker); }
.player-marker__badge--helper { background: var(--status-green); }
.player-marker__badge--more   {
  background: var(--surface-2);
  color: var(--text);
}

/* POI and housing markers share the same square shape so all "buildings"
   on the map read as the same kind of object; only colour distinguishes
   delivery sites from residential plots. */
.poi-marker__dot {
  display: block;
  width: var(--size-sm);
  height: var(--size-sm);
  border-radius: var(--radius-sm);
  background: var(--map-poi);
  border: 2px solid var(--text);
  box-shadow: var(--map-glow-box);
}

.housing-marker { background: transparent; border: none; }
.housing-marker__dot {
  display: block;
  width: var(--size-sm);
  height: var(--size-sm);
  border-radius: var(--radius-sm);
  background: var(--accent, var(--map-housing));
  border: 2px solid var(--text);
  box-shadow: var(--map-glow-box);
}

/* Leaflet popup widgets keep px font sizes -- they live inside Leaflet's
   own sizing context (popup width, anchor offsets, tip arrows are all px)
   and should not scale with the document type scale. */
/* stylelint-disable declaration-property-value-allowed-list */
.housing-popup { font-size: 12px; min-width: 160px; }
.housing-popup__name { font-weight: 600; margin-bottom: 4px; color: var(--text); }
.housing-popup__row { display: flex; justify-content: space-between; gap: 8px; margin-top: 2px; }
.housing-popup__label { color: var(--text-overlay-muted); }
.housing-popup__value { color: var(--accent, var(--text-overlay-strong)); }
/* stylelint-enable declaration-property-value-allowed-list */

.fire-marker { background: transparent; border: none; }
.fire-marker__dot {
  display: block;
  width: var(--size-md);
  height: var(--size-md);
  border-radius: var(--radius-full);
  background: radial-gradient(circle,
    color-mix(in srgb, var(--map-poi) 55%, transparent) 10%,
    color-mix(in srgb, var(--fire)    45%, transparent) 65%);
  border: 2px solid color-mix(in srgb, var(--fire) 50%, transparent);
  box-shadow: 0 0 6px color-mix(in srgb, var(--fire) 40%, transparent);
}
.fire-marker__dot--ext {
  background: radial-gradient(circle,
    color-mix(in srgb, var(--fire-extinguished) 50%, white)       10%,
    color-mix(in srgb, var(--fire-extinguished) 40%, transparent) 65%);
  border-color: color-mix(in srgb, var(--fire-extinguished) 50%, transparent);
  box-shadow: 0 0 6px color-mix(in srgb, var(--fire-extinguished) 40%, transparent);
}

/* Tow-fallback marker: stays at the crash position while the
   requester is offline (no player marker to anchor the badge to).
   Police shifts use no fallback -- if the officer disconnects, the
   background job ends the shift after the grace window. */
.tow-layer-marker { background: transparent; border: none; }
.tow-layer-marker__disc {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  border-radius: var(--radius-full);
  border: 2px solid var(--text);
  box-shadow: var(--map-glow-box);
  color: var(--text);
  background: var(--tow-marker);
}

.tow-layer-popup {
  display: grid;
  gap: 0.2rem;
  font-size: var(--fs-sm);
  color: var(--text-overlay-strong);
}
.tow-layer-popup__title {
  font-weight: var(--fw-medium);
  color: var(--text-overlay-strong);
}
.tow-layer-popup__row {
  display: flex;
  gap: 0.4rem;
}
.tow-layer-popup__label { color: var(--text-overlay-muted); min-width: 5rem; }
.tow-layer-popup__note  { color: var(--text-overlay-muted); font-style: italic; }

/* Dark Leaflet popups + tooltips.
   Leaflet ships its own CSS in leaflet.css with high-specificity rules
   that win against our class selectors. The only reliable way to skin
   .leaflet-popup-close-button, .leaflet-tooltip and friends is with
   !important; without it the upstream values bleed through. The whole
   block is wrapped so each !important does not need its own disable. */
/* stylelint-disable declaration-no-important */

/* Leaflet's default container background is #ddd, which flashes light-
   grey around / beneath the tiles (during pan, while tiles load, or
   if the viewport is briefly larger than the tiled bounds). Override
   with our --bg so the gap blends into the rest of the page. */
.leaflet-container { background: var(--bg); }

.leaflet-popup-content-wrapper {
  background: var(--surface-overlay);
  color: var(--text);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  backdrop-filter: blur(12px);
  box-shadow: var(--shadow-modal);
}
.leaflet-popup-tip          { background: var(--surface-overlay); }
.leaflet-popup-close-button { color: var(--text-muted) !important; top: 6px !important; right: 8px !important; }
.leaflet-popup-close-button:hover { color: var(--text) !important; background: transparent !important; }
.leaflet-popup-content      { margin: 0.75rem 1rem; }

.leaflet-tooltip {
  background: var(--surface-overlay) !important;
  color: var(--text) !important;
  border: 1px solid var(--border-strong) !important;
  border-radius: var(--radius-md) !important;
  backdrop-filter: blur(10px);
  box-shadow: var(--shadow-modal) !important;
  padding: 0.5rem 0.75rem !important;
}
.leaflet-tooltip::before { display: none !important; }
/* stylelint-enable declaration-no-important */

/* Leaflet popup -- see note above .housing-popup for why these stay literal. */
/* stylelint-disable declaration-property-value-allowed-list */
.fire-popup__title  { font-weight: 700; color: color-mix(in srgb, var(--status-orange) 55%, white); margin-bottom: 0.25rem; font-size: 0.85rem; }
.fire-popup__row    { font-size: 0.78rem; color: var(--text-muted); line-height: 1.6; }
.fire-popup__status { font-size: 0.78rem; color: var(--text); margin-top: 0.25rem; }
/* stylelint-enable declaration-property-value-allowed-list */

/* ============================================================
   Responsive
   ============================================================ */
@media (max-width: 860px) {
  .nav__links .nav__link:not(.active) { display: none; }
  .nav__links .nav__link:nth-last-child(-n+3) { display: flex; }
}
@media (max-width: 680px) {
  .nav__links { display: none; }
  .section-cards__grid { grid-template-columns: 1fr 1fr; }
}

/* ============================================================
   Statistics page
   ============================================================ */

.stats-page { max-width: var(--max-w-dashboard); margin: 0 auto; padding: 2.5rem 2rem 4rem; display: flex; flex-direction: column; gap: 1.5rem; }
/* Narrower variant for table-only pages without charts (e.g. /staff/admins,
   /staff/police-roster). Same vertical rhythm as .stats-page, just a tighter
   content column so 4-column tables stay readable instead of stretched. */
.stats-page--narrow { max-width: var(--max-w-content); }

/* Header */
.stats-header        { display: flex; align-items: flex-end; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
.stats-header__title {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 4vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
  margin: 0;
}
.stats-header__sub   { margin: 0.25rem 0 0; color: var(--text-muted); font-size: var(--fs-base); }
.stats-header__meta  { display: flex; align-items: center; gap: 0.6rem; font-size: var(--fs-sm); color: var(--text-muted); }

.stats-live-dot {
  display: inline-block; width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full);
  background: var(--status-green);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 60%, transparent);
  animation: livePulse 2s ease-out infinite;
}
@keyframes livePulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 60%, transparent); }
  70%  { box-shadow: 0 0 0 8px color-mix(in srgb, var(--status-green) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 0%, transparent); }
}
.stats-last-update { color: var(--text); }
.stats-countdown   { color: var(--text-dim); font-size: var(--fs-xs); }
/* Housing KPI strip (card grid).
   auto-fit/minmax scales from 2 to N columns depending on viewport
   and number of cards, so adding or removing a card needs no CSS
   change. Each .kpi-card reads --accent from its inline style (set per
   card in the view); the optional .kpi-card--accented modifier tints
   the card background to match. Cards without --accent stay neutral.
   NOTE: named .housing-kpis, NOT .kpi-strip -- the garage KPI partial
   (views/partials/kpi-strip.eta) owns .kpi-strip as a flex column with a
   .kpi-strip__grid child. Sharing the bare name made the later garage
   rule's `display:flex` win the cascade and collapse this grid to a
   single column (the 2026-05-30 housing-layout bug). */
.housing-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 1rem;
}
.kpi-card {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-top: 2px solid var(--accent, transparent);
  border-radius: var(--radius-lg);
  backdrop-filter: blur(10px);
  padding: 1.4rem 1.6rem;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
.kpi-card--accented {
  background: color-mix(in srgb, var(--accent) 6%, var(--surface-1));
}
/* Global status accent. Set via data-status="red|orange|yellow|green|muted"
   on anything that should tint itself with the status colour (KPI cards,
   housing dots/markers, map popups, town-bonus chips). "muted" is for
   inactive/zero states (e.g. a town with no payment bonus). */
[data-status="muted"]  { --accent: var(--status-muted); }
[data-status="red"]    { --accent: var(--status-red); }
[data-status="orange"] { --accent: var(--status-orange); }
[data-status="yellow"] { --accent: var(--status-yellow); }
[data-status="green"]  { --accent: var(--status-green); }
.kpi-card__label { font-size: var(--fs-xs); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: 0.09em; color: var(--text-muted); }
.kpi-card__label--red    { color: var(--status-red); }
.kpi-card__label--orange { color: var(--status-orange); }
.kpi-card__label--yellow { color: var(--status-yellow); }
.kpi-card__label--green  { color: var(--status-green); }
.kpi-card__value {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 3.5vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  color: var(--accent, var(--text));
  line-height: var(--lh-tight);
  font-variant-numeric: tabular-nums;
}
.kpi-card__sub   { font-size: var(--fs-xs); color: var(--text-muted); }

/* Card chrome shared across data-focused pages */
.econ-card { background: var(--surface-1); border: 1px solid var(--border-soft); border-radius: var(--radius-lg); backdrop-filter: blur(10px); overflow: hidden; }
.econ-card__header    { display: flex; align-items: center; gap: 0.75rem; padding: 1rem 1.25rem; border-bottom: 1px solid var(--border-hairline); }
.econ-card__title     { font-size: var(--fs-xs); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: 0.1em; color: var(--text); opacity: 0.7; white-space: nowrap; flex-shrink: 0; }
.econ-card__meta      { flex: 1; display: flex; align-items: center; gap: 0.4rem; font-size: var(--fs-sm); color: var(--text-muted); overflow: hidden; white-space: nowrap; }
.econ-card__body-inner { padding: 1.25rem; display: flex; flex-direction: column; gap: 1rem; }

/* Bar primitive used by htmlBar() in utils.js. Width is set per-fill via
   --fill (DOM-API setProperty in applyBarWidths()); we cannot inline it
   because the value is unbounded continuous and CSP would block it. The
   data-pct="X.X" attribute on the fill marks elements for the JS pass. */
.stat-bar       { width: 100%; background: var(--surface-2); border-radius: var(--radius-sm); overflow: hidden; height: 4px; }
.stat-bar--sm   { height: 3px; }
/* Stat-bar fill uses a deliberately slow 0.6s so the bar visibly grows
   on first render, drawing the eye. Twice --motion-animate; longer
   than any other transition in the file. */
.stat-bar__fill { height: 100%; border-radius: var(--radius-sm); transition: width 0.6s ease; background: var(--accent, var(--red)); width: var(--fill, 0%); }

/* Sortable table headers used by initSortableTable().
   --fs-dense applies to the whole table so cell content stays compact in
   the Hybrid scale; cells can opt out by setting their own size. */
.sortable-table { font-size: var(--fs-dense, var(--fs-sm)); }
.sortable-table th[data-key] { cursor: pointer; user-select: none; white-space: nowrap; }
.sortable-table th[data-key]:hover { color: var(--text); }
.sortable-table th.th-sort-active  { color: var(--text); }
.sortable-table th .sort-indicator { display: inline-block; width: 0.85em; height: 0.85em; vertical-align: -0.15em; margin-left: 0.3em; opacity: 0.3; }
.sortable-table th.th-sort-active .sort-indicator { opacity: 1; color: var(--red); }
.sortable-table thead th {
  text-align: left; padding: 0.45rem 0.6rem;
  font-size: var(--fs-xs); font-weight: var(--fw-bold);
  text-transform: uppercase; letter-spacing: 0.07em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-soft);
}

/* --- Housing page --- */
.housing-page .housing-kpis {
  grid-template-columns: repeat(6, 1fr);
}
.housing-page .kpi-card {
  padding: 1rem 1.2rem;
}
.housing-page .kpi-card__value {
  font-size: clamp(var(--fs-lg), 2.5vw, var(--fs-xl));
}
@media (max-width: 1100px) {
  .housing-page .housing-kpis { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 600px) {
  .housing-page .housing-kpis { grid-template-columns: repeat(2, 1fr); }
}

/* --- Housing table --- */
.housing-table { width: 100%; }
.housing-name__plot  { font-weight: var(--fw-medium); }
.housing-name__depot {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 0.15rem;
}
.housing-owner__link {
  color: var(--text);
  text-decoration: none;
  font-weight: var(--fw-medium);
  border-bottom: 1px solid transparent;
  transition: color var(--motion-base), border-color var(--motion-base);
}
.housing-owner__link:hover {
  color: var(--red);
  border-bottom-color: var(--red);
}
.housing-owner__anon {
  color: var(--text-dim);
  font-style: italic;
}
.housing-owner__available {
  display: inline-block;
  padding: 0.15rem 0.55rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--status-green) 15%, transparent);
  color: var(--status-green);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.housing-rent   { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text); font-weight: var(--fw-medium); }
.housing-size   { font-variant-numeric: tabular-nums; white-space: nowrap; color: var(--text-muted); }
.housing-expiry { white-space: nowrap; }
.cell--empty         { color: var(--text-dim); font-weight: normal; }
.cell--empty-italic  { font-style: italic; }

/* --- Housing split-view (table + map) --- */
.housing-split {
  display: grid;
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 1rem;
  margin-top: 1rem;
  align-items: start;
}
.housing-split__list { min-width: 0; }
.housing-split__map-wrap {
  position: sticky;
  top: calc(var(--nav-h) + 1rem);
  height: calc(100vh - var(--nav-h) - 2rem);
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--bg);
  border: 1px solid var(--border-hairline);
  box-shadow: var(--shadow-card);
}
.housing-map { width: 100%; height: 100%; }
.housing-split__hint {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  background: var(--surface-overlay);
  color: var(--text-overlay-muted);
  font-size: var(--fs-xs);
  padding: 0.35rem 0.75rem;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border-soft);
  pointer-events: none;
  backdrop-filter: blur(6px);
  transition: opacity var(--motion-animate);
}
.housing-split__hint--hidden { opacity: 0; }

.housing-table tbody tr { cursor: pointer; transition: background var(--motion-base); }
.housing-table tbody tr:hover { background: var(--surface-1); }
.housing-table tbody tr.housing-tr--active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  box-shadow: inset 3px 0 0 var(--red);
}
.housing-table tbody tr.housing-tr--available {
  box-shadow: inset 3px 0 0 var(--status-green);
}
.housing-table tbody tr.housing-tr--available.housing-tr--active {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  box-shadow: inset 3px 0 0 var(--red), inset 6px 0 0 var(--status-green);
}

/* Leaflet assigns z-index to markers dynamically by latitude (markers
   lower on the screen render above markers higher up). To make the
   active housing marker always sit above the rest, !important is the
   only reliable way to pin it. */
/* stylelint-disable-next-line declaration-no-important */
.housing-marker--active { z-index: var(--z-map-layer) !important; }
.housing-marker--active .housing-marker__dot {
  width: var(--size-md); height: var(--size-md);
  border-color: var(--text);
  box-shadow: 0 0 0 4px color-mix(in srgb, var(--red) 45%, transparent), 0 0 8px color-mix(in srgb, var(--red) 80%, transparent);
}

@media (max-width: 900px) {
  .housing-split { grid-template-columns: 1fr; }
  .housing-split__map-wrap {
    position: relative;
    top: auto;
    height: 380px;
  }
}
.housing-date   { color: var(--text-muted); font-size: var(--fs-dense); white-space: nowrap; }
.housing-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  margin-right: 0.45em;
  vertical-align: middle;
  flex-shrink: 0;
  background: var(--accent);
}

/* Responsive */
@media (max-width: 900px) {
  .housing-kpis  { grid-template-columns: repeat(2, 1fr); }
  .econ-grid  { grid-template-columns: 1fr; }
  .econ-sub-grid { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .housing-kpis { grid-template-columns: 1fr 1fr; }
}

/* --- Statistics page --- */
.stats-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.75rem;
}
.stats-card {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.1rem 1.25rem;
  text-align: center;
}
.stats-card__val {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-xl);
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  color: var(--text);
  line-height: 1;
  font-variant-numeric: tabular-nums;
  margin-bottom: 0.35rem;
}
.stats-card__lbl {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
}

.stats-section {
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.5rem 1.5rem;
}
.stats-section--half { flex: 1 1 0; min-width: 0; }
.stats-section__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.stats-section__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  margin: 0;
  flex-shrink: 0;
}
.stats-section__sub { font-size: var(--fs-sm); color: var(--text-muted); font-weight: var(--fw-regular); }

.stats-row { display: flex; gap: 1rem; flex-wrap: wrap; }

.stats-chart-wrap          { position: relative; height: 280px; }
.stats-chart-wrap--sm      { height: 200px; }
.stats-chart-wrap--md      { height: 360px; }

/* Segmented control (action filter group). The shared container with its
   own border distinguishes interactive filters from passive status pills,
   which stay fully rounded. Each segment carries an accent via --accent so the
   active slot can light up in its own colour (police blue, admin red,
   skill-class colour, etc.). */
.stats-toggle-group {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.15rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: 0.2rem;
}
.stats-toggle {
  font-size: var(--fs-xs);
  padding: 0.3rem 0.75rem;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  font-weight: var(--fw-medium);
  transition: background var(--motion-base), color var(--motion-base);
}
.stats-toggle:hover { color: var(--text); background: var(--surface-1); }
.stats-toggle--on {
  background: color-mix(in srgb, var(--accent, var(--text-muted)) 22%, transparent);
  color: var(--accent, var(--text));
  font-weight: var(--fw-bold);
}
.stats-toggle--on:hover {
  background: color-mix(in srgb, var(--accent, var(--text-muted)) 28%, transparent);
  color: var(--accent, var(--text));
}
/* Residents-page role filter buttons. "all" maps to status-green by
   UI choice (not a real role token). Other --accent users on .stats-toggle
   (statistics.eta dataset toggles) set --accent via DOM-API, see
   statistics-app.js. */
.stats-toggle[data-filter="all"]        { --accent: var(--status-green); }
.stats-toggle[data-filter="police"]     { --accent: var(--role-police); }
.stats-toggle[data-filter="admin"]      { --accent: var(--role-admin); }
.stats-toggle[data-filter="enthusiast"] { --accent: var(--role-enthusiast); }

.stats-section__controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 0.75rem;
  margin-left: auto;
  align-items: center;
}
.stats-range-group {
  display: inline-flex;
  gap: 0.15rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-sm);
  padding: 0.15rem;
}
.stats-range {
  font-size: var(--fs-xs);
  padding: 0.2rem 0.55rem;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  transition: background var(--motion-base), color var(--motion-base);
}
.stats-range:hover { color: var(--text); background: var(--surface-1); }
.stats-range--on {
  background: color-mix(in srgb, var(--red) 18%, transparent);
  color: var(--text);
  font-weight: var(--fw-medium);
}

/* Activity heatmap */
.heatmap-wrap {
  min-height: 120px;
  display: flex;
  flex-direction: column;
}
.heatmap-grid {
  display: grid;
  grid-template-columns: 2rem repeat(24, 1fr);
  gap: 2px;
  margin-top: 0.5rem;
}
.hm-label {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  display: flex;
  align-items: center;
  justify-content: center;
}
.hm-label--wd { justify-content: flex-end; padding-right: 4px; }
/* Smaller than --fs-xs so 24 hour labels fit a narrow heatmap row. */
/* stylelint-disable-next-line declaration-property-value-allowed-list */
.hm-label--hour { font-size: 0.55rem; }
.hm-cell {
  aspect-ratio: 1;
  border-radius: var(--radius-sm);
  cursor: default;
}
.chart-spinner {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100%;
  gap: 0.6rem;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.chart-spinner::before {
  content: '';
  width: var(--size-xl);
  height: var(--size-xl);
  border: 2px solid var(--border-soft);
  border-top-color: var(--text-overlay-muted);
  border-radius: var(--radius-full);
  animation: spin 0.7s linear infinite;
  flex-shrink: 0;
}
@keyframes spin { to { transform: rotate(360deg); } }
.stats-player-search {
  position: relative;
  margin-bottom: 0.75rem;
}
.stats-search-input {
  width: 100%;
  box-sizing: border-box;
  padding: 0.4rem 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  color: var(--text);
  font-size: var(--fs-sm);
  outline: none;
}
.stats-search-input:focus {
  border-color: var(--border-emphasis);
}
.stats-search-results {
  position: absolute;
  bottom: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: var(--z-popover);
  margin: 0;
  padding: 0;
  list-style: none;
  background: var(--surface-overlay);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  max-height: 260px;
  overflow-y: auto;
}
.stats-search-results--down {
  top: calc(100% + 4px);
  bottom: auto;
}
.stats-search-result {
  padding: 0.45rem 0.75rem;
  font-size: var(--fs-sm);
  cursor: pointer;
  color: var(--text-overlay-strong);
}
.stats-search-result a {
  color: inherit;
  text-decoration: none;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}
.stats-search-result:hover { background: var(--surface-2); }
.stats-search-result__meta {
  color: var(--text-muted);
  font-size: var(--fs-xs);
  white-space: nowrap;
}
.stats-search-result--empty {
  color: var(--text-muted);
  font-style: italic;
  cursor: default;
}
.stats-search-result--added {
  opacity: 0.4;
  cursor: default;
}
.stats-search-highlight {
  background: none;
  color: var(--text);
  font-weight: var(--fw-medium);
}
/* --- POI delivery popup --- */
.poi-info-popup .leaflet-popup-content-wrapper {
  background: var(--surface-overlay);
  color: var(--text-overlay-strong);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-modal);
  padding: 0;
}
.poi-info-popup .leaflet-popup-tip-container { display: none; }
.poi-info-popup .leaflet-popup-content       { margin: 0; line-height: 1.5; }

/* Leaflet popup -- see note above .housing-popup for why these stay literal. */
/* stylelint-disable declaration-property-value-allowed-list */
.poi-popup          { min-width: 220px; font-size: 12px; display: flex; flex-direction: column; max-height: 320px; }
.poi-popup__name    { padding: 7px 12px 5px; font-size: 13px; font-weight: 700; color: var(--text); border-bottom: 1px solid var(--border-soft); white-space: nowrap; }
.poi-popup__section { padding: 5px 12px; border-bottom: 1px solid var(--border-hairline); }
.poi-popup__section:last-child { border-bottom: none; }
.poi-popup__head    { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.08em; color: var(--text-overlay-dim); margin-bottom: 3px; }
.poi-popup__inv-row,
.poi-popup__job-row { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; padding: 1px 0; }
.poi-popup__inv-name,
.poi-popup__job-dest { color: var(--text-overlay-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.poi-popup__inv-stock       { color: var(--map-poi); font-weight: 600; font-variant-numeric: tabular-nums; white-space: nowrap; }
.poi-popup__inv-stock--empty { color: var(--red); }
.poi-popup__job-meta { color: var(--map-poi); font-variant-numeric: tabular-nums; white-space: nowrap; }
.poi-popup__more    { color: var(--text-overlay-dim); font-size: 11px; padding: 1px 0 2px; }
.poi-popup__empty   { color: var(--text-overlay-dim); font-style: italic; }
.poi-popup__sources { padding: 1px 0 4px 8px; display: flex; flex-direction: column; gap: 1px; }
.poi-popup__source  { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; font-size: 11px; }
.poi-popup__source-name  { color: var(--text-overlay-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
.poi-popup__source-stock { color: var(--map-poi); font-variant-numeric: tabular-nums; white-space: nowrap; font-weight: 600; }

.poi-popup__body { flex: 1; min-height: 0; overflow: hidden; }
.poi-popup--pinned .poi-popup__body { overflow-y: auto; scrollbar-width: thin; scrollbar-color: var(--border-emphasis) transparent; }
.poi-popup__hint { flex-shrink: 0; padding: 5px 12px 6px; font-size: 11px; color: var(--text-overlay-dim); text-align: center; border-top: 1px solid var(--border-soft); cursor: pointer; user-select: none; }
/* stylelint-enable declaration-property-value-allowed-list */
.poi-popup__hint:hover { color: var(--text-overlay-muted); background: var(--surface-1); }

/* Residents overview + detail
 *
 * Shared building blocks live at the top, page-specific styles below.
 * Avatar sizes (sm/md) and role pills are reused by both pages. */

/* --- Shared: avatars and role pills --- */

.resident-avatar {
  border-radius: var(--radius-full);
  object-fit: cover;
  background: var(--surface-2);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: var(--fw-bold);
  color: var(--text-muted);
  flex-shrink: 0;
}
/* Avatar font-size is paired to its px width/height (initials sit
   centred inside the circle); not part of the document type scale. */
/* stylelint-disable declaration-property-value-allowed-list */
.resident-avatar--sm  { width: var(--size-3xl); height: var(--size-3xl); font-size: 0.85rem; }
.resident-avatar--md  { width: var(--size-5xl); height: var(--size-5xl); font-size: 1.05rem; }
/* stylelint-enable declaration-property-value-allowed-list */
.resident-avatar--placeholder { border: 1px solid var(--border-soft); }

.role-pill {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.65rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* Pills inherit the role-token palette; transparent background and
   border are derived in-place via color-mix so a token change at :root
   updates pills, dots, and animations together. */
.role-pill--police {
  background: color-mix(in srgb, var(--role-police) 22%, transparent);
  color:      var(--role-police);
  border:     1px solid color-mix(in srgb, var(--role-police) 45%, transparent);
}
.role-pill--admin {
  background: color-mix(in srgb, var(--role-admin) 22%, transparent);
  color:      var(--role-admin);
  border:     1px solid color-mix(in srgb, var(--role-admin) 50%, transparent);
}
.role-pill--enthusiast {
  background: color-mix(in srgb, var(--role-enthusiast) 22%, transparent);
  color:      var(--role-enthusiast);
  border:     1px solid color-mix(in srgb, var(--role-enthusiast) 50%, transparent);
}

/* ============================================================
   Residents (overview + detail)
   ============================================================ */

.residents-live-pill {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.25rem 0.7rem 0.25rem 0.55rem;
  border-radius: var(--radius-pill);
  background: color-mix(in srgb, var(--status-green) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--status-green) 40%, transparent);
  color: color-mix(in srgb, var(--status-green) 50%, white);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  letter-spacing: 0.04em;
  white-space: nowrap;
}
.residents-live-pill--offline {
  background: color-mix(in srgb, var(--status-orange) 14%, transparent);
  border-color: color-mix(in srgb, var(--status-orange) 45%, transparent);
  color: color-mix(in srgb, var(--status-orange) 55%, white);
}
.residents-live-pill--offline .stats-live-dot {
  background: var(--status-orange);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-orange) 55%, transparent);
  animation: none;
}

/* --- Residents overview --- */

.residents-page { display: flex; flex-direction: column; gap: 1.75rem; }

.residents-hero {
  display: grid;
  grid-template-columns: 1fr minmax(260px, 420px);
  gap: 1.25rem;
  align-items: end;
}
.residents-hero__title {
  margin: 0;
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 3vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
}
.residents-hero__sub   { margin: 0.35rem 0 0; color: var(--text-muted); font-size: var(--fs-base); }
.residents-hero__search { position: relative; margin-bottom: 0; }
@media (max-width: 720px) {
  .residents-hero { grid-template-columns: 1fr; }
}

.residents-block {
  display: flex;
  flex-direction: column;
  gap: 0.9rem;
}
/* Each section header carries a "tone" color via data-accent, used by the
   leading accent bar, the underline accent and the hint pill. Tone is
   set once at the header, inherited via the --accent custom property. */
.residents-block__header {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  flex-wrap: wrap;
  padding-bottom: 0.55rem;
  border-bottom: 1px solid var(--border-hairline);
  position: relative;
}
.residents-block__header::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -1px;
  height: 1px;
  width: 90px;
  background: linear-gradient(90deg, var(--accent), transparent);
}
.residents-block__header[data-accent="live"]      { --accent: var(--status-green);                                  --accent-soft: color-mix(in srgb, var(--status-green)   22%, transparent); }
.residents-block__header[data-accent="champions"] { --accent: var(--brand-motortown);                                --accent-soft: color-mix(in srgb, var(--brand-motortown) 22%, transparent); }
.residents-block__header[data-accent="fame"]      { --accent: color-mix(in srgb, var(--brand-motortown) 75%, white); --accent-soft: color-mix(in srgb, var(--brand-motortown) 24%, transparent); }

.residents-block__title {
  margin: 0;
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-lg), 2.1vw, var(--fs-xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  text-transform: none;
  color: var(--text);
  line-height: var(--lh-tight);
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
}
.residents-block__title::before {
  content: '';
  display: inline-block;
  width: 4px;
  height: 1.05em;
  border-radius: var(--radius-sm);
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 35%, transparent));
  box-shadow: 0 0 14px var(--accent-soft);
  flex: 0 0 auto;
}
.residents-block__header[data-accent="live"] .residents-block__title::before {
  animation: residents-block-marker-pulse 2.6s ease-in-out infinite;
}
@keyframes residents-block-marker-pulse {
  0%, 100% { box-shadow: 0 0 12px var(--accent-soft); }
  50%      { box-shadow: 0 0 22px var(--accent), 0 0 4px var(--accent); }
}

.residents-block__hint {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  letter-spacing: 0.02em;
  padding: 0.2rem 0.6rem;
  border-radius: var(--radius-pill);
  background: var(--accent-soft);
  border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
  color: color-mix(in srgb, var(--accent) 35%, var(--text));
}
.residents-block__empty { color: var(--text-muted); font-size: var(--fs-base); font-style: italic; }
.residents-online-filter { margin-left: auto; }

/* "Right now" tile strip */
.residents-online-strip {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(96px, 1fr));
  gap: 0.5rem;
}
.residents-online-strip__empty {
  grid-column: 1 / -1;
  text-align: center;
  color: var(--text-muted);
  font-style: italic;
  padding: 1.5rem 1rem;
  background: var(--surface-1);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-lg);
}
.resident-tile {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.15rem;
  padding: 0.55rem 0.45rem 0.5rem;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--text);
  transition: border-color var(--motion-base), background var(--motion-base), transform var(--motion-base);
  min-width: 0;
}
.resident-tile:hover { border-color: var(--border-emphasis); background: var(--surface-2); transform: translateY(-1px); }
.resident-tile__avatar { position: relative; margin-bottom: 0.2rem; }
.resident-tile__roles {
  position: absolute;
  top: -3px;
  right: -3px;
  display: inline-flex;
  flex-direction: column;
  gap: 2px;
}
.resident-tile__role-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--bg);
  animation: resident-role-pulse 2.4s ease-in-out infinite;
}
/* `color` is consumed as the pulse-halo via `currentColor` in the
   @keyframes below; deriving it from the token keeps the halo in step. */
.resident-tile__role-dot--police {
  background: var(--role-police);
  color:      color-mix(in srgb, var(--role-police) 60%, transparent);
}
.resident-tile__role-dot--admin {
  background: var(--role-admin);
  color:      color-mix(in srgb, var(--role-admin) 60%, transparent);
}
.resident-tile__role-dot--enthusiast {
  background: var(--role-enthusiast);
  color:      color-mix(in srgb, var(--role-enthusiast) 60%, transparent);
}
@keyframes resident-role-pulse {
  0%   { box-shadow: 0 0 0 0   currentColor; }
  70%  { box-shadow: 0 0 0 5px transparent; }
  100% { box-shadow: 0 0 0 0   transparent; }
}
.resident-tile__name {
  font-weight: var(--fw-bold);
  color: var(--text);
  font-size: var(--fs-dense);
  line-height: var(--lh-tight);
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.resident-tile__meta {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  max-width: 100%;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.resident-tile__since {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
  margin-top: 0.1rem;
}
/* Skill accent slot. Set via data-skill="CL_X" on any element that needs to
   tint itself in the skill's colour (champion cards, char tabs, invalid
   pills). Values come from the --skill-* token family in :root; the JS
   side (src/public/skills.js) keeps its own copies of the hex literals
   for dimHex() colour math -- keep both in sync on retune. */
[data-skill="CL_Driver"]  { --accent: var(--skill-driver); }
[data-skill="CL_Taxi"]    { --accent: var(--skill-taxi); }
[data-skill="CL_Bus"]     { --accent: var(--skill-bus); }
[data-skill="CL_Truck"]   { --accent: var(--skill-truck); }
[data-skill="CL_Racer"]   { --accent: var(--skill-racer); }
[data-skill="CL_Wrecker"] { --accent: var(--skill-wrecker); }
[data-skill="CL_Police"]  { --accent: var(--skill-police); }

/* Skill champions: 7 colored cards, one per skill class */
.residents-champions {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 0.75rem;
}
.resident-champion {
  display: flex;
  flex-direction: column;
  gap: 0.55rem;
  padding: 0.9rem 1rem;
  border-radius: var(--radius-lg);
  text-decoration: none;
  color: var(--text);
  background:
    linear-gradient(140deg,
      color-mix(in srgb, var(--accent) 28%, transparent),
      color-mix(in srgb, var(--accent) 8%, transparent) 70%);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, transparent);
  transition: transform var(--motion-base), border-color var(--motion-base);
}
.resident-champion:hover { transform: translateY(-2px); border-color: color-mix(in srgb, var(--accent) 75%, transparent); }
.resident-champion__skill {
  font-size: var(--fs-xs);
  font-weight: var(--fw-hero);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: color-mix(in srgb, var(--accent) 90%, white 25%);
}
.resident-champion__level {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-xl);
  font-weight: var(--fw-hero);
  color: var(--text);
  line-height: 1;
  letter-spacing: 0.01em;
  font-variant-numeric: tabular-nums;
}
.resident-champion__player {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  margin-top: auto;
}
.resident-champion__name { font-weight: var(--fw-bold); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs-sm); }

/* Hall of fame: six small leaderboards in a single row on desktop, with
   stepwise fallbacks to keep the rows readable as the viewport shrinks.
   Step boundaries are picked so each column never gets narrower than ~150px
   for the avatar+rank+name+value layout to stay legible. */
.residents-hall {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  gap: 0.75rem;
}
@media (max-width: 1180px) { .residents-hall { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1rem; } }
@media (max-width:  720px) { .residents-hall { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width:  480px) { .residents-hall { grid-template-columns: 1fr; } }

.residents-hall__col {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 0.7rem 0.75rem;
  min-width: 0;
}
.residents-hall__title {
  margin: 0 0 0.55rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-hero);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text);
  opacity: 0.85;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.25rem;
}
.residents-hall__hint { color: var(--text-dim); font-style: italic; font-weight: var(--fw-regular); letter-spacing: 0; text-transform: none; font-size: var(--fs-xs); }
.residents-hall__list { list-style: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 0.4rem; }
.residents-hall__row {
  display: grid;
  grid-template-columns: 1rem 1fr auto;
  align-items: center;
  gap: 0.35rem;
  padding: 0.22rem 0;
  border-bottom: 1px dashed var(--border-hairline);
  font-size: var(--fs-dense);
  min-width: 0;
}
.residents-hall__row:last-child { border-bottom: none; }
.residents-hall__rank { color: var(--text-dim); font-weight: var(--fw-bold); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; }
.residents-hall__link { display: inline-flex; align-items: center; gap: 0.4rem; color: var(--text); text-decoration: none; min-width: 0; }
.residents-hall__link:hover .residents-hall__name { color: var(--red); }
.residents-hall__name { font-weight: var(--fw-medium); color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; font-size: var(--fs-dense); min-width: 0; }
.residents-hall__value { font-weight: var(--fw-bold); color: var(--text); font-variant-numeric: tabular-nums; font-size: var(--fs-dense); text-align: right; justify-self: end; }
/* Smaller avatars so 10 rows fit compactly into a narrow 6-up column.
   font-size paired to 18px width -- see avatar note above. */
/* stylelint-disable-next-line declaration-property-value-allowed-list */
.residents-hall__link .resident-avatar--sm { width: var(--size-xl); height: var(--size-xl); font-size: 0.6rem; }

/* --- Resident detail page --- */

.resident-page { display: flex; flex-direction: column; gap: 1.5rem; }

.resident-toolbar { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.resident-toolbar__search { flex: 1; min-width: 240px; max-width: 480px; margin-bottom: 0; }
.resident-back-link {
  color: var(--text-muted);
  text-decoration: none;
  font-size: var(--fs-base);
  padding: 0.4rem 0.7rem;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-soft);
  white-space: nowrap;
  transition: color var(--motion-base), border-color var(--motion-base);
}
.resident-back-link:hover { color: var(--text); border-color: var(--border-emphasis); }

/* Hero */
.resident-hero {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  padding: 1.1rem 1.25rem 1.2rem;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--red) 8%, var(--surface-overlay)),
    var(--surface-overlay));
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
}
.resident-hero__top {
  display: grid;
  grid-template-columns: auto 1fr auto;
  gap: 1rem;
  align-items: center;
}
.resident-hero__avatar { width: var(--size-6xl); height: var(--size-6xl); flex-shrink: 0; }
.resident-avatar-img,
.resident-avatar-fallback {
  width: var(--size-6xl);
  height: var(--size-6xl);
  border-radius: var(--radius-full);
  object-fit: cover;
  display: block;
  border: 2px solid var(--border-strong);
}
.resident-avatar-fallback {
  background: var(--surface-2);
  display: flex;
  align-items: center;
  justify-content: center;
  /* Paired to 64px avatar -- see avatar note above. */
  /* stylelint-disable-next-line declaration-property-value-allowed-list */
  font-size: 1.6rem;
  font-weight: var(--fw-hero);
  color: var(--text-muted);
}
.resident-hero__ident { min-width: 0; }
.resident-hero__name {
  margin: 0;
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-lg), 2.4vw, var(--fs-xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
}
.resident-hero__status-row { display: flex; align-items: baseline; gap: 0.5rem; margin-top: 0.35rem; font-size: var(--fs-base); }
.resident-hero__playing-as { margin-top: 0.2rem; font-size: var(--fs-sm); color: var(--text-muted); }
.resident-hero__pills { display: flex; flex-direction: column; align-items: flex-end; gap: 0.3rem; }

.resident-status--online  { color: var(--status-green); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: 0.08em; font-size: var(--fs-xs); }
.resident-status--offline { color: var(--text-muted); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: 0.08em; font-size: var(--fs-xs); }
.resident-vehicle { font-size: var(--fs-sm); color: var(--text); opacity: 0.85; }

/* Account-wide KPI strip */
.resident-kpis {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 0.5rem;
}
.resident-kpis li {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.6rem 0.8rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
}
.resident-kpi__value {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-md), 1.8vw, var(--fs-lg));
  font-weight: var(--fw-hero);
  color: var(--text);
  font-variant-numeric: tabular-nums;
  line-height: var(--lh-tight);
  letter-spacing: 0.01em;
}
.resident-kpi__label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}
.resident-kpi__hint { font-weight: var(--fw-regular); font-style: italic; color: var(--text-dim); letter-spacing: 0; text-transform: none; }

/* Character tabs + properties row */
.resident-hero__bottom {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
}
.resident-char-tabs {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}
.resident-char-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  padding: 0.35rem 0.7rem;
  border-radius: var(--radius-pill);
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  text-decoration: none;
  color: var(--text);
  transition: border-color var(--motion-base), background var(--motion-base);
}
.resident-char-tab:hover { border-color: var(--border-emphasis); background: var(--surface-2); }
/* Doubled selector wins specificity over `:hover`, so the active red stays
   visible while the cursor is still over the tab after a click. Without
   the explicit `:hover` partner, `.foo:hover` (0,2,0) beats `.foo--active`
   (0,1,0) and the active state only shows after the cursor moves away. */
.resident-char-tab--active,
.resident-char-tab--active:hover {
  background: color-mix(in srgb, var(--red) 18%, transparent);
  border-color: color-mix(in srgb, var(--red) 55%, transparent);
  cursor: default;
}
.resident-char-tab__name { font-weight: var(--fw-bold); font-size: var(--fs-sm); color: var(--text); }
.resident-char-tab__tag {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 22%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--accent, var(--text));
}
.resident-char-tab__tag--invalid {
  background: var(--surface-1);
  border: 1px dashed var(--border-emphasis);
  color: var(--text-dim);
}
.resident-properties {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
  font-size: var(--fs-sm);
}
.resident-properties__label {
  color: var(--text-muted);
  font-weight: var(--fw-bold);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  align-self: center;
  margin-right: 0.2rem;
}
.resident-properties li:not(.resident-properties__label) {
  padding: 0.2rem 0.55rem;
  border-radius: var(--radius-pill);
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  color: var(--text);
}

/* Cards (shared) */
.resident-card {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.25rem 1.35rem;
}
.resident-card__title {
  margin: 0 0 0.85rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-hero);
  text-transform: uppercase;
  letter-spacing: 0.12em;
  color: var(--text);
  opacity: 0.85;
}
.resident-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.85rem;
  flex-wrap: wrap;
}
.resident-card__header .resident-card__title { margin: 0; }
.resident-card__sub {
  margin: 1rem 0 0.45rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}
.resident-card__sub-note { font-weight: var(--fw-regular); color: var(--text-dim); text-transform: none; letter-spacing: 0; font-style: italic; margin-left: 0.4rem; }

.resident-section-title {
  margin: 0.75rem 0 0;
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  color: var(--text);
  letter-spacing: 0.04em;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
}
.resident-section-title span:first-of-type { color: var(--red); }

.resident-profile-tag {
  display: inline-flex;
  align-items: center;
  padding: 0.25rem 0.65rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  white-space: nowrap;
}
.resident-profile-tag--specialist  { background: color-mix(in srgb, var(--red)           22%, transparent); color: color-mix(in srgb, var(--red)           50%, white); border: 1px solid color-mix(in srgb, var(--red)           50%, transparent); }
.resident-profile-tag--hybrid      { background: color-mix(in srgb, var(--status-orange) 18%, transparent); color: color-mix(in srgb, var(--status-orange) 55%, white); border: 1px solid color-mix(in srgb, var(--status-orange) 45%, transparent); }
.resident-profile-tag--allrounder  { background: color-mix(in srgb, var(--status-green)  18%, transparent); color: color-mix(in srgb, var(--status-green)  50%, white); border: 1px solid color-mix(in srgb, var(--status-green)  40%, transparent); }

.resident-aka {
  margin-left: 0.6rem;
  font-size: var(--fs-sm);
  font-weight: var(--fw-regular);
  font-style: italic;
  color: var(--text-muted);
  opacity: 0.75;
  letter-spacing: 0;
}

/* Habits card subsections (heatmap + driving + most used vehicles).
   The "When they play" sub-title shares a row with the heatmap legend. */
.resident-habits-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin: 0.85rem 0 0.45rem;
}
.resident-habits-row__title { margin: 0; }

/* Activity heatmap (7 weekdays x 24 hours) */
.resident-heatmap {
  display: grid;
  grid-template-columns: 2rem repeat(24, minmax(8px, 14px));
  gap: 2px;
  width: max-content;
  max-width: 100%;
}
.resident-heatmap__corner { /* empty cell */ }
.resident-heatmap__hour {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-align: center;
  font-variant-numeric: tabular-nums;
  padding-bottom: 4px;
}
.resident-heatmap__day {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  align-self: center;
}
.resident-heatmap__cell {
  aspect-ratio: 1 / 1;
  border-radius: var(--radius-sm);
  background: var(--surface-1);
  transition: transform var(--motion-fast);
  cursor: default;
}
.resident-heatmap__cell:hover { transform: scale(1.15); outline: 1px solid var(--text-muted); }
.resident-heatmap__cell[data-l="0"] { background: var(--surface-1); }
.resident-heatmap__cell[data-l="1"] { background: color-mix(in srgb, var(--red) 20%, transparent); }
.resident-heatmap__cell[data-l="2"] { background: color-mix(in srgb, var(--red) 40%, transparent); }
.resident-heatmap__cell[data-l="3"] { background: color-mix(in srgb, var(--red) 65%, transparent); }
.resident-heatmap__cell[data-l="4"] { background: color-mix(in srgb, var(--red) 95%, transparent); }
.resident-heatmap__empty {
  color: var(--text-muted);
  font-style: italic;
  text-align: center;
  margin: 1rem 0 0;
}
.resident-heatmap__legend { display: inline-flex; align-items: center; gap: 0.4rem; font-size: var(--fs-xs); color: var(--text-dim); }
.resident-heatmap__legend-scale { display: inline-flex; gap: 2px; }
.resident-heatmap__legend-cell {
  width: var(--size-md);
  height: var(--size-md);
  border-radius: var(--radius-sm);
}
.resident-heatmap__legend-cell[data-l="0"] { background: var(--surface-1); }
.resident-heatmap__legend-cell[data-l="1"] { background: color-mix(in srgb, var(--red) 20%, transparent); }
.resident-heatmap__legend-cell[data-l="2"] { background: color-mix(in srgb, var(--red) 40%, transparent); }
.resident-heatmap__legend-cell[data-l="3"] { background: color-mix(in srgb, var(--red) 65%, transparent); }
.resident-heatmap__legend-cell[data-l="4"] { background: color-mix(in srgb, var(--red) 95%, transparent); }

/* Career grid: skills (treemap) + driving (stats + bars) side by side */
.resident-grid--career {
  display: grid;
  grid-template-columns: minmax(280px, 1.3fr) minmax(280px, 1fr);
  gap: 1.25rem;
}
@media (max-width: 820px) {
  .resident-grid--career { grid-template-columns: 1fr; }
}
.resident-treemap {
  position: relative;
  width: 100%;
}
/* Chart.js paints the treemap canvas with inline width/height styles,
   which beat any non-!important rule in the cascade. We need the
   canvas to fill the responsive container, so override here. */
/* stylelint-disable-next-line declaration-no-important */
.resident-treemap canvas { display: block; width: 100% !important; height: auto !important; }
.resident-radar__note { color: var(--text-muted); font-style: italic; text-align: center; margin: 1rem 0; }

.resident-skills-invalid {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0.85rem 0 0;
  padding: 0.55rem 0.7rem;
  background: var(--surface-1);
  border: 1px dashed var(--border-emphasis);
  border-radius: var(--radius-md);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.resident-skills-invalid__label {
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--fs-xs);
  color: var(--text);
  opacity: 0.75;
}
.resident-skills-invalid__list { display: inline-flex; flex-wrap: wrap; gap: 0.3rem; }
.resident-skills-invalid__pill {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border-radius: var(--radius-sm);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  border: 1px dashed color-mix(in srgb, var(--accent) 55%, transparent);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: color-mix(in srgb, var(--accent) 80%, white 25%);
}
.resident-skills-invalid__hint { font-style: italic; color: var(--text-dim); margin-left: auto; font-size: var(--fs-xs); }

.resident-driving-stats {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.resident-driving-stats > div {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.6rem 0.8rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
}
.resident-stat__label { font-size: var(--fs-xs); color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; font-weight: var(--fw-bold); }
.resident-stat__value {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-hero);
  font-variant-numeric: tabular-nums;
  color: var(--text);
  letter-spacing: 0.01em;
}

.resident-skill-list { list-style: none; padding: 0; margin: 0.5rem 0 0; display: flex; flex-direction: column; gap: 0.5rem; }
.resident-skill {
  display: grid;
  grid-template-columns: 6.5rem 1fr auto;
  align-items: center;
  gap: 0.7rem;
  font-size: var(--fs-sm);
}
.resident-skill__name  { color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.resident-skill__bar   { display: block; }
.resident-skill__value { font-weight: var(--fw-bold); font-variant-numeric: tabular-nums; min-width: 3rem; text-align: right; color: var(--text); }
.resident-skill .stat-bar       { height: 8px; border-radius: var(--radius-sm); background: var(--surface-1); }
.resident-skill .stat-bar__fill { background: linear-gradient(90deg, color-mix(in srgb, var(--red) 55%, white), var(--red)); border-radius: var(--radius-sm); }

/* Recent moves timeline */
.resident-card--timeline { padding-bottom: 1.25rem; }
.resident-timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.resident-timeline__row {
  display: grid;
  grid-template-columns: 1.75rem 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.55rem 0;
  border-bottom: 1px dashed var(--border-hairline);
  font-size: var(--fs-base);
}
.resident-timeline__row:last-child { border-bottom: none; }
/* Timeline-row accent per event kind. Skill-level rows also carry a
   data-skill attribute that overrides --accent via the global selector
   higher up -- so a level-up row tints with the skill colour, not a
   generic level-event colour. Buy/sell are money-flow signals, so they
   reuse the status palette (green = money in, yellow = money out)
   rather than carry their own hex. */
.resident-timeline__row[data-kind="login"] { --accent: var(--text-muted); }
.resident-timeline__row[data-kind="buy"]   { --accent: var(--status-green); }
.resident-timeline__row[data-kind="sell"]  { --accent: var(--status-yellow); }
.resident-timeline__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: var(--radius-full);
  background: color-mix(in srgb, var(--accent) 15%, transparent);
  color: var(--accent);
}
.resident-timeline__icon svg { width: 0.95rem; height: 0.95rem; }
.resident-timeline__text { color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.resident-timeline__text strong { color: var(--text); font-weight: var(--fw-bold); }
.resident-timeline__when { color: var(--text-muted); font-size: var(--fs-xs); font-variant-numeric: tabular-nums; white-space: nowrap; }
.resident-timeline__empty { color: var(--text-muted); font-style: italic; margin: 0.5rem 0 0; }

/* Character-switch animation: each block scoped to the current character
   gets a brief fade-up when the user clicks a different char-tab. The JS
   removes + re-adds the class to restart the animation per switch. */
@keyframes resident-character-switch {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: translateY(0); }
}
.resident-character-block--switching {
  animation: resident-character-switch var(--motion-slow) ease;
}

/* Tow activity card (account-scoped, between hero and Career-as-X). The
   grid shape mirrors .resident-footer-kpis -- same tile look, only the
   container class differs because tow lives in a real resident-card, not
   in the character-scoped footer row. The .resident-stat__label/value
   pair from .resident-driving-stats above is reused so the typography
   is consistent across all three "small metric tile" surfaces. */
.resident-tow-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 0.5rem;
}
.resident-tow-grid > div {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.55rem 0.8rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
}

/* Footer KPIs row (character-scoped detail metrics) */
.resident-footer-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 0.5rem;
}
.resident-footer-kpis > div {
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding: 0.55rem 0.8rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
}
.resident-footer-kpis .resident-kpi__label { font-size: var(--fs-xs); }
.resident-footer-kpis .resident-kpi__value {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
}

/* --- Economy page ---------------------------------------------------- */

.econ-page {
  max-width: var(--max-w-dashboard);
  margin: 0 auto;
  padding: 2.5rem 2rem 4rem;
  display: flex;
  flex-direction: column;
  gap: 1.25rem;
}

.econ-header        { display: flex; align-items: flex-end; justify-content: space-between; flex-wrap: wrap; gap: 1rem; }
.econ-header__title {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 4vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
  margin: 0;
}
.econ-header__sub   { margin: 0.25rem 0 0; color: var(--text-muted); font-size: var(--fs-base); }
.econ-header__meta  { display: flex; align-items: center; gap: 0.6rem; font-size: var(--fs-sm); color: var(--text-muted); }

.econ-live-dot {
  width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full);
  background: var(--status-green);
  box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 50%, transparent);
  animation: econ-pulse 2s infinite;
}
.econ-last-update { color: var(--text); }
.econ-countdown   { color: var(--text-dim); font-size: var(--fs-xs); }

@keyframes econ-pulse {
  0%   { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 50%, transparent); }
  70%  { box-shadow: 0 0 0 8px color-mix(in srgb, var(--status-green) 0%, transparent); }
  100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--status-green) 0%, transparent); }
}

.econ-toolbar {
  display: flex; align-items: center; gap: 1rem; flex-wrap: wrap;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 0.6rem 0.75rem;
}

.econ-tabs {
  display: inline-flex;
  gap: 0.15rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: 0.2rem;
}
.econ-tab {
  font-size: var(--fs-sm);
  padding: 0.35rem 0.95rem;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--text-muted);
  cursor: pointer;
  font-family: inherit;
  font-weight: var(--fw-medium);
  transition: background var(--motion-base), color var(--motion-base);
}
.econ-tab:hover { color: var(--text); background: var(--surface-1); }
.econ-tab.is-active {
  background: color-mix(in srgb, var(--red) 22%, transparent);
  color: var(--red);
  font-weight: var(--fw-bold);
}

.econ-search {
  flex: 1;
  min-width: 220px;
  display: flex; align-items: center; gap: 0.55rem;
  padding: 0.45rem 1rem;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  transition: background var(--motion-base), border-color var(--motion-base);
}
.econ-search:focus-within {
  background: var(--surface-3);
  border-color: var(--border-emphasis);
}
.econ-search__icon  { width: var(--size-lg); height: var(--size-lg); color: var(--text-dim); flex-shrink: 0; }
.econ-search__input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  color: var(--text);
  font-size: var(--fs-base);
}
.econ-search__input::placeholder { color: var(--text-dim); }
.econ-search__clear {
  background: transparent; border: none;
  color: var(--text-muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  padding: 0.2rem;
  transition: color var(--motion-base), transform var(--motion-fast);
}
.econ-search__clear:hover { color: var(--text); }
.econ-search__clear:active { transform: scale(0.92); }

.econ-layout {
  display: grid;
  grid-template-columns: minmax(280px, 360px) minmax(0, 1fr);
  gap: 1rem;
  align-items: start;
}
@media (max-width: 880px) {
  .econ-layout { grid-template-columns: minmax(0, 1fr); }
}

.econ-list-pane {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 0.85rem;
  display: flex; flex-direction: column; gap: 0.6rem;
  max-height: calc(100vh - 220px);
  overflow: hidden;
  min-width: 0;
}
.econ-list-pane__count {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-dim);
  padding: 0 0.25rem;
}
.econ-list {
  display: flex; flex-direction: column; gap: 0.3rem;
  overflow-y: auto;
  padding-right: 0.25rem;
}
.econ-list__empty {
  padding: 1.5rem 0.5rem;
  text-align: center;
  color: var(--text-dim);
  font-size: var(--fs-base);
}
.econ-list__item {
  display: grid;
  grid-template-columns: 10px 1fr;
  align-items: center;
  gap: 0.6rem;
  padding: 0.55rem 0.7rem;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  background: var(--surface-1);
  color: var(--text);
  cursor: pointer;
  text-align: left;
  font-size: var(--fs-sm);
}
.econ-list__item:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}
.econ-list__item.is-selected {
  background: color-mix(in srgb, var(--red) 12%, transparent);
  border-color: color-mix(in srgb, var(--red) 55%, transparent);
}
.econ-list__dot {
  width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full);
  background: var(--accent, var(--text-dim));
}
.econ-list__main {
  display: flex; flex-direction: column; gap: 0.1rem;
  min-width: 0;
}
.econ-list__title {
  font-weight: var(--fw-medium); color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.econ-list__sub {
  font-size: var(--fs-xs); color: var(--text-dim);
}
.econ-list__metrics {
  grid-column: 1 / -1;
  display: flex; gap: 0.75rem;
  margin-top: 0.25rem;
  padding-top: 0.4rem;
  border-top: 1px dashed var(--border-hairline);
  font-size: var(--fs-xs);
}
.econ-list__metric        { display: flex; align-items: baseline; gap: 0.3rem; }
.econ-list__metric-label  { color: var(--text-dim); text-transform: uppercase; letter-spacing: 0.05em; font-size: var(--fs-xs); }
.econ-list__metric-value  { color: var(--text-muted); font-variant-numeric: tabular-nums; }

.econ-detail-pane {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  display: flex; flex-direction: column; gap: 1.25rem;
  min-height: 240px;
  min-width: 0;
}
.econ-detail-pane__placeholder {
  margin: auto; color: var(--text-dim); font-size: var(--fs-base);
  text-align: center;
}


.econ-detail__header {
  display: flex; gap: 0.85rem; align-items: flex-start;
}
.econ-detail__type-dot {
  width: var(--size-md); height: var(--size-md); border-radius: var(--radius-full);
  flex-shrink: 0; margin-top: 0.4rem;
  background: var(--accent, var(--text-dim));
}
.econ-detail__type-dot--site {
  background: var(--surface-3);
  border: 2px solid var(--border-emphasis);
}
.econ-detail__title {
  margin: 0;
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
}
.econ-detail__sub   { margin: 0.25rem 0 0; color: var(--text-muted); font-size: var(--fs-sm);
                      display: flex; flex-wrap: wrap; gap: 0.4rem; }
.econ-detail__sep   { color: var(--text-dim); }

.econ-detail__metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 0.5rem;
}
.econ-metric {
  display: flex; flex-direction: column; gap: 0.15rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  padding: 0.55rem 0.75rem;
}
.econ-metric__label { font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: 0.07em; color: var(--text-dim); }
.econ-metric__value {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-md);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  color: var(--text);
  font-variant-numeric: tabular-nums;
}

.econ-detail__cols {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 1rem;
}
@media (max-width: 720px) {
  .econ-detail__cols { grid-template-columns: minmax(0, 1fr); }
}

.econ-col {
  display: flex; flex-direction: column; gap: 0.5rem;
  min-width: 0;
}
.econ-col__title {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  display: flex; gap: 0.4rem; align-items: baseline;
}
.econ-col__count {
  color: var(--text-dim); font-weight: var(--fw-medium);
}

.econ-ref-list {
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: 0.2rem;
  max-height: 320px;
  overflow-y: auto;
}
.econ-ref-list__item {
  display: flex; align-items: center; gap: 0.5rem;
  padding: 0.4rem 0.5rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
}
.econ-ref-list__dot { width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full); flex-shrink: 0; background: var(--accent, var(--text-dim)); }
.econ-ref-list__btn {
  background: transparent; border: none; padding: 0;
  color: var(--text); flex: 1; text-align: left;
  cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.econ-ref-list__btn:hover { color: var(--red); text-decoration: underline; }
.econ-ref-list__amount {
  display: inline-flex; align-items: baseline; gap: 0.3rem;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
  font-size: var(--fs-xs);
  flex-shrink: 0;
}
.econ-ref-list__amount-label {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.econ-jobs {
  display: flex; flex-direction: column; gap: 0.4rem;
}
.econ-jobs__wrap { overflow-x: auto; }
.econ-jobs__table {
  width: 100%; border-collapse: collapse; font-size: var(--fs-dense, var(--fs-sm));
}
.econ-jobs__table th {
  text-align: left;
  padding: 0.45rem 0.6rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-dim);
  border-bottom: 1px solid var(--border-soft);
}
.econ-jobs__table td {
  padding: 0.5rem 0.6rem;
  border-bottom: 1px solid var(--border-hairline);
  color: var(--text);
  vertical-align: middle;
}
.econ-jobs__table tr:last-child td { border-bottom: none; }
.econ-jobs__table tr:hover td      { background: var(--surface-1); }
.econ-jobs__dot {
  display: inline-block;
  width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full);
  margin-right: 0.4rem;
  vertical-align: middle;
  background: var(--accent, var(--text-dim));
}
.econ-jobs__qty    { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.econ-jobs__payout { font-weight: var(--fw-bold); color: var(--red); white-space: nowrap; text-align: right; }
.econ-jobs__table th.econ-jobs__th--num { text-align: right; }
.econ-jobs__timed  { font-size: var(--fs-xs); opacity: 0.7; padding: 0 0.3rem; border: 1px solid currentColor; border-radius: var(--radius-sm); color: var(--text-dim); }
.econ-jobs__ambig  { color: var(--text-muted); font-style: italic; cursor: help; border-bottom: 1px dotted var(--text-dim); }

.econ-link {
  background: transparent; border: none; padding: 0;
  color: var(--text); cursor: pointer;
  text-align: left; font: inherit;
}
.econ-link:hover { color: var(--red); text-decoration: underline; }

.econ-hint {
  margin: 0;
  padding: 0.6rem 0.85rem;
  background: var(--surface-1);
  border-left: 3px solid color-mix(in srgb, var(--red) 55%, transparent);
  border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.econ-hint strong { color: var(--text); }

.econ-empty {
  margin: 0;
  padding: 0.8rem 0.5rem;
  font-size: var(--fs-sm);
  color: var(--text-dim);
  font-style: italic;
}

.econ-filters {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  padding: 0.75rem 0.85rem;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-lg);
}
.econ-filters__row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
}
.econ-filters__row--sort { justify-content: flex-end; }
.econ-filters__label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--text-dim);
}
.econ-filters__field {
  display: flex; align-items: center; gap: 0.4rem;
  font-size: var(--fs-sm); color: var(--text-muted);
}
.econ-filters__clear {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.4rem 0.95rem;
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  color: var(--text-muted);
  border-radius: var(--radius-pill);
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  box-shadow: var(--shadow-button), var(--shadow-highlight-top);
  transition: background var(--motion-base), border-color var(--motion-base), color var(--motion-base), box-shadow var(--motion-base), transform var(--motion-fast);
  margin-left: auto;
}
.econ-filters__clear:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text);
}
.econ-filters__clear:active { transform: scale(0.97); }

.econ-chips {
  display: flex; flex-wrap: wrap; gap: 0.3rem;
  flex: 1;
}
.econ-chip {
  display: inline-flex; align-items: center; gap: 0.4rem;
  padding: 0.3rem 0.8rem;
  font: inherit;
  font-size: var(--fs-sm);
  font-weight: var(--fw-medium);
  cursor: pointer;
  background: var(--surface-1);
  border: 1px solid var(--border-hairline);
  color: var(--text-muted);
  border-radius: var(--radius-pill);
  transition: background var(--motion-base), color var(--motion-base), border-color var(--motion-base);
}
.econ-chip:hover {
  background: var(--surface-2);
  color: var(--text);
}
.econ-chip.is-active {
  background: color-mix(in srgb, var(--accent, var(--red)) 22%, transparent);
  border-color: color-mix(in srgb, var(--accent, var(--red)) 45%, transparent);
  color: var(--accent, var(--text));
  font-weight: var(--fw-bold);
}
.econ-chip.is-active:hover {
  background: color-mix(in srgb, var(--accent, var(--red)) 28%, transparent);
}
.econ-chip__dot {
  width: var(--size-xs); height: var(--size-xs); border-radius: var(--radius-full);
  flex-shrink: 0;
  background: var(--accent, var(--text-dim));
}

.econ-toggle {
  display: inline-flex; align-items: center; gap: 0.4rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.econ-toggle input[type="checkbox"] {
  appearance: none;
  width: var(--size-4xl); height: var(--size-xl);
  background: var(--surface-3);
  border: 1px solid var(--border-emphasis);
  border-radius: var(--radius-pill);
  position: relative;
  cursor: pointer;
  transition: background var(--motion-base), border-color var(--motion-base);
}
.econ-toggle input[type="checkbox"]::after {
  content: '';
  position: absolute;
  top: 1px; left: 1px;
  width: var(--size-md); height: var(--size-md);
  background: var(--text-muted);
  border-radius: var(--radius-full);
  transition: transform var(--motion-base), background var(--motion-base);
}
.econ-toggle input[type="checkbox"]:checked {
  background: color-mix(in srgb, var(--red) 50%, transparent);
  border-color: color-mix(in srgb, var(--red) 70%, transparent);
}
.econ-toggle input[type="checkbox"]:checked::after {
  transform: translateX(14px);
  background: var(--text);
}

.econ-sort-dir {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--size-4xl); height: var(--size-4xl);
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  color: var(--text-muted);
  border-radius: var(--radius-pill);
  cursor: pointer;
  backdrop-filter: blur(14px) saturate(180%);
  -webkit-backdrop-filter: blur(14px) saturate(180%);
  box-shadow: var(--shadow-button), var(--shadow-highlight-top);
  transition: background var(--motion-base), border-color var(--motion-base), color var(--motion-base), box-shadow var(--motion-base), transform var(--motion-fast);
}
.econ-sort-dir:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--text);
}
.econ-sort-dir:active { transform: scale(0.92); }
.econ-sort-dir .icon { width: var(--size-md); height: var(--size-md); }

/* ============================================================
   Garage
   ============================================================ */

.garage-page {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 2.5rem 2rem 4rem;
}
.garage-page__header { margin-bottom: 2rem; }
.garage-page__title {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 4vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
  margin: 0;
}
.garage-page__sub { margin: 0.5rem 0 0; color: var(--text-muted); font-size: var(--fs-base); }
.garage-section { margin-top: 2.5rem; }
.garage-section:first-of-type { margin-top: 0; }
.garage-section__header { margin-bottom: 1rem; }
.garage-section__title {
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  color: var(--text);
  margin: 0;
}
.garage-section__sub {
  margin: 0.25rem 0 0;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.garage-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
}
.garage-card {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1.4rem 1.6rem;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-lg);
  color: inherit;
  transition: background var(--motion-base), border-color var(--motion-base);
}
.garage-card:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
}
.garage-card__title { font-size: var(--fs-md); font-weight: var(--fw-bold); color: var(--text); }
.garage-card__desc  { font-size: var(--fs-sm); color: var(--text-muted); }

/* --- Garage / Leaderboard page --- */

.garage-leaderboard {
  max-width: var(--max-w-content);
  margin: 0 auto;
  padding: 2.5rem 2rem 4rem;
}
.garage-leaderboard__header { margin-bottom: 1.5rem; }
.garage-leaderboard__title {
  font-family: var(--font-display, var(--font));
  font-size: clamp(var(--fs-xl), 4vw, var(--fs-2xl));
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  line-height: var(--lh-tight);
  margin: 0;
}
.garage-leaderboard__sub  { margin: 0.5rem 0 0; color: var(--text-muted); font-size: var(--fs-base); }
.garage-leaderboard__error {
  --accent: var(--red);
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  font-size: var(--fs-sm);
  margin-bottom: 1rem;
}
.garage-leaderboard__empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface-1);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
}

/* Sub-classed table -- shares the thead typography idiom with .sortable-
   table (capitalised, muted, narrow) but doesn't carry the click-to-sort
   interaction. Standalone class keeps the leaderboard simple to evolve
   without touching the dense statistics tables elsewhere. */
.leaderboard-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.leaderboard-table thead th {
  text-align: left;
  padding: 0.5rem 0.8rem;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
  border-bottom: 1px solid var(--border-soft);
}
.leaderboard-table tbody td {
  padding: 0.55rem 0.8rem;
  border-bottom: 1px solid var(--border-hairline);
  color: var(--text);
}
.leaderboard-table tbody tr:last-child td { border-bottom: none; }
.leaderboard-table__rank { width: 3rem; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.leaderboard-table__num  { text-align: right; font-variant-numeric: tabular-nums; }
.leaderboard-name {
  color: var(--text);
  font-weight: var(--fw-medium);
}
.leaderboard-name:hover { color: var(--red); }
.leaderboard-name--unlinked { color: var(--text-muted); font-weight: var(--fw-regular); }

/* --- Garage / Tow page --- */

.garage-tow {
  max-width: 720px;
  margin: 0 auto;
  padding: 1.5rem 2rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.garage-tow__panel {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  min-width: 0;
}
.garage-tow__header { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 0.5rem; }
.garage-tow__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-xl);
  font-weight: var(--fw-hero);
  letter-spacing: 0.01em;
  margin: 0;
}
.garage-tow__count {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-muted);
}
.garage-tow__banner {
  --accent: var(--status-orange);
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-md);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  font-size: var(--fs-sm);
}
.garage-tow__login-hint {
  padding: 0.6rem 0.9rem;
  border-radius: var(--radius-md);
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.garage-tow__login-hint a { color: color-mix(in srgb, var(--brand-steam) 60%, white); }

.garage-tow__list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: 70vh;
  overflow-y: auto;
  /* Reserve scrollbar gutter on the inner scroll container so the list
     doesn't reflow when items appear or disappear. */
  scrollbar-gutter: stable;
}
.garage-tow__empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--text-muted);
  background: var(--surface-1);
  border: 1px dashed var(--border-soft);
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
}

.tow-card {
  display: flex;
  flex-direction: column;
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-left: 3px solid var(--accent, var(--text-dim));
  border-radius: var(--radius-md);
  overflow: hidden;
  transition: background var(--motion-base);
}
.tow-card--mine { background: color-mix(in srgb, var(--accent, var(--text-dim)) 4%, var(--surface-1)); }
.tow-card--accepted { border-left-color: color-mix(in srgb, var(--accent, var(--text-dim)) 60%, var(--text)); }
.tow-card--disconnected { opacity: 0.7; }

/* Header is the clickable area that expands the drawer. Styled like
   the rest of the card (no chrome) so the click affordance comes from
   the caret + hover background, not button visuals. */
.tow-card__header {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.75rem;
  padding: 0.8rem 1rem;
  background: transparent;
  border: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
  transition: background var(--motion-base);
}
.tow-card__header:hover { background: var(--surface-2); }
.tow-card__caret {
  flex-shrink: 0;
  color: var(--text-muted);
  transition: transform var(--motion-base);
}
.tow-card--expanded .tow-card__caret { transform: rotate(180deg); }

.tow-card__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: var(--size-4xl);
  height: var(--size-4xl);
  border-radius: var(--radius-full);
  background: var(--accent);
  color: var(--text);
  flex-shrink: 0;
}
.tow-card__icon .icon { width: var(--size-lg); height: var(--size-lg); }
.tow-card__body { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.tow-card__head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
  font-size: var(--fs-sm);
}
.tow-card__name { color: var(--text); font-weight: var(--fw-bold); }
.tow-card__vehicle { color: var(--text-muted); }
.tow-card__age {
  margin-left: auto;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.tow-card__status {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--accent);
}
.tow-card__note {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* Drawer: the expanded detail panel below the header. Renders the
   full note, timestamps, helper info, and the action buttons.
   Accordion -- only one drawer open at a time, controlled by JS. */
.tow-card__drawer {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
  padding: 0.2rem 1rem 0.9rem;
  border-top: 1px solid var(--border-hairline);
}
.tow-card__drawer-meta {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.tow-card__drawer-row {
  display: flex;
  gap: 0.6rem;
  font-size: var(--fs-sm);
  color: var(--text);
}
.tow-card__drawer-label {
  min-width: 4.5rem;
  color: var(--text-muted);
  font-weight: var(--fw-medium);
}
.tow-card__drawer-value { color: var(--text); }
.tow-card__drawer-row--note {
  display: block;
  color: var(--text-muted);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.tow-card__drawer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: stretch;
}
.tow-card__btn {
  padding: 0.45rem 0.8rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  border: 1px solid var(--border-soft);
  background: var(--surface-2);
  color: var(--text);
  cursor: pointer;
  white-space: nowrap;
  min-height: 32px;
  transition: background var(--motion-base), border-color var(--motion-base);
}
.tow-card__btn:hover { background: var(--surface-3); border-color: var(--border-strong); }
.tow-card__btn[disabled] { opacity: 0.5; cursor: not-allowed; filter: grayscale(0.4); }
.tow-card__btn--accept {
  --accent: var(--status-green);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.tow-card__btn--done {
  --accent: var(--status-green);
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.tow-card__btn--cancel {
  --accent: var(--red);
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
}
.tow-card__error {
  --accent: var(--red);
  padding: 0.4rem 1rem 0.7rem;
  font-size: var(--fs-xs);
  color: var(--accent);
}

/* "Show on map" button is an anchor; layout same as the action
   buttons but with an inline icon. */
.tow-card__btn--map {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  text-decoration: none;
}

/* --- Modal (native <dialog>) --- */

/* Inline create-panel that opens directly under the "Send distress
   call" CTA. Replaces the previous centred dialog overlay. Same
   internal layout as the modal had -- the surface chrome is the only
   thing that changed (no backdrop, no centring, integrates with the
   page flow). */
.tow-create-panel {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 1rem 1.2rem;
}
.tow-create-panel__form {
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.tow-create-panel__reasons {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
}
.tow-reason-radio {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius-md);
  border: 1px solid var(--border-soft);
  background: var(--surface-1);
  cursor: pointer;
  min-height: 44px;
  transition: background var(--motion-base), border-color var(--motion-base);
}
/* Per-reason accent. Set via data-reason="stuck|flipped|out_of_fuel" on
   anything that should tint with the tow-reason colour (radio buttons in
   the create panel, tow-card list items). All three currently resolve to
   var(--tow-marker) (see :root --tow-reason-* aliases); the indirection
   stays so a future diversification is a token-only change. */
[data-reason="stuck"]       { --accent: var(--tow-reason-stuck); }
[data-reason="flipped"]     { --accent: var(--tow-reason-flipped); }
[data-reason="out_of_fuel"] { --accent: var(--tow-reason-out-of-fuel); }
.tow-reason-radio:hover { background: var(--surface-2); border-color: var(--border-strong); }
.tow-reason-radio input { accent-color: var(--accent); }
.tow-reason-radio--checked {
  background: color-mix(in srgb, var(--accent) 12%, var(--surface-1));
  border-color: color-mix(in srgb, var(--accent) 45%, transparent);
}
.tow-reason-radio__swatch {
  display: inline-block;
  width: var(--size-sm);
  height: var(--size-sm);
  border-radius: var(--radius-full);
  background: var(--accent);
  flex-shrink: 0;
}
.tow-reason-radio__label { font-size: var(--fs-sm); color: var(--text); }
.tow-create-panel__field { display: flex; flex-direction: column; gap: 0.35rem; }
.tow-create-panel__label { font-size: var(--fs-xs); font-weight: var(--fw-bold); text-transform: uppercase; letter-spacing: 0.09em; color: var(--text-muted); }
.tow-create-panel__textarea {
  width: 100%;
  min-height: 80px;
  resize: vertical;
}
.tow-create-panel__counter {
  align-self: flex-end;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  font-variant-numeric: tabular-nums;
}
.tow-create-panel__counter--warn { --accent: var(--status-orange); }
.tow-create-panel__error {
  --accent: var(--red);
  font-size: var(--fs-sm);
  background: color-mix(in srgb, var(--accent) 10%, transparent);
  border: 1px solid color-mix(in srgb, var(--accent) 25%, transparent);
  border-radius: var(--radius-md);
  padding: 0.5rem 0.7rem;
}
.tow-create-panel__buttons { display: flex; justify-content: flex-end; gap: 0.6rem; margin-top: 0.5rem; }

/* ----- KPI strip + history stream (shared by /garage/tow + /garage/police) -----
   Markup lives in views/partials/kpi-strip.eta + history-stream.eta; the
   classes here are shared so the two garage surfaces never drift. The KPI
   strip is a three-card grid driven by ONE range pill group, reusing the
   statistics-page .stats-card + .stats-range-group tokens. */
.kpi-strip {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.kpi-strip__head {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem 0.75rem;
}
.kpi-strip__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  margin: 0;
}
.kpi-strip__sub {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-weight: var(--fw-regular);
}
.kpi-strip__head .stats-range-group { margin-left: auto; }
/* Override the statistics-page card auto-fit minmax so three KPIs stay
   on one row at the garage content width (720 px max). Auto-fit with a
   150-px minmax would let a fourth empty column hint appear. */
.kpi-strip__grid { grid-template-columns: repeat(3, 1fr); }

/* ----- History stream -----
   Event log shown below the active list, with a compact row design
   optimized for chronological scanning rather than action. */
.history-stream {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  margin-top: 1.5rem;
}
.history-stream__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.history-stream__title {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-bold);
  letter-spacing: 0.01em;
  margin: 0;
}
.history-stream__count {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-muted);
}
.history-stream__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--border-hairline);
  border-radius: var(--radius-md);
  background: var(--surface-1);
  overflow: hidden;
}
.history-stream__loading,
.history-stream__empty {
  padding: 2rem 1rem;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
.stream-event {
  display: flex;
  gap: 0.75rem;
  padding: 0.55rem 0.9rem;
  border-bottom: 1px solid var(--border-hairline);
  font-size: var(--fs-sm);
  align-items: baseline;
}
.stream-event:last-child { border-bottom: none; }
.stream-event__time {
  flex: 0 0 auto;
  font-variant-numeric: tabular-nums;
  color: var(--text-muted);
  font-size: var(--fs-xs);
  min-width: 11ch;
}
.stream-event__body {
  flex: 1 1 auto;
  color: var(--text);
  min-width: 0;
}
.stream-event__actor,
.stream-event__admin {
  font-weight: var(--fw-bold);
  color: var(--text);
}
/* When the actor/admin name resolves to a publicId, it becomes a link.
   Use the same subdued underline as other inline residents-links. */
a.stream-event__actor:hover,
a.stream-event__admin:hover {
  color: color-mix(in srgb, var(--accent, var(--brand-steam)) 70%, white);
  text-decoration: underline;
}
.stream-event--system {
  color: var(--text-muted);
  font-style: italic;
}
.stream-event--system .stream-event__body { color: var(--text-muted); }

/* Category icon (left column). Colour comes from --accent, set per row
   via [data-tone] -- direction at a glance. The tone set is the union of
   both surfaces (tow: good/bad/alert/system; police: good/bad/police/
   neutral); each surface uses only its subset. Same mechanism as the
   resident-timeline icon (currentColor <- --accent). */
.stream-event__icon {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  align-self: center;
  color: var(--accent, var(--text-muted));
}
.stream-event__icon svg { width: 1.05rem; height: 1.05rem; }
.stream-event__icon[data-tone="good"]    { --accent: var(--status-green); }
.stream-event__icon[data-tone="bad"]     { --accent: var(--status-red); }
.stream-event__icon[data-tone="alert"]   { --accent: var(--status-yellow); }
.stream-event__icon[data-tone="system"]  { --accent: var(--text-muted); }
.stream-event__icon[data-tone="police"]  { --accent: var(--role-police); }
.stream-event__icon[data-tone="neutral"] { --accent: var(--text-muted); }

.history-stream__filters { align-self: flex-start; }

.history-stream__pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.history-stream__page-info {
  font-size: var(--fs-sm);
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
/* Caret rotation for prev/next buttons. The shared #icon-caret-down
   asset points down; rotation makes it left/right. CSS classes (not
   inline style) so the strict CSP holds (no style-src 'unsafe-inline'). */
.history-stream__caret--prev { transform: rotate(90deg); }
.history-stream__caret--next { transform: rotate(-90deg); }

/* --- Tinted-text utility ---
   Single source of truth for the "text on tinted surface" formula
   (35% mix of the source tone with white, for legible foreground over a
   coloured-12-to-18% background). Consumers set --accent on their own class;
   the formula stays here so the colour math never drifts again.
   New components with the same idiom get added to this selector list,
   they do NOT re-declare the color rule. Pattern grew from one badge
   variant pre-step-6 to a family of seven status surfaces -- past the
   pattern-check 3+ threshold it earns one canonical declaration. */
.badge--discord:hover,
.garage-tow__banner,
.tow-card__btn--accept,
.tow-card__btn--done,
.tow-card__btn--cancel,
.tow-card__error,
.tow-create-panel__counter--warn,
.tow-create-panel__error {
  color: color-mix(in srgb, var(--accent) 35%, white);
}


/* ============================================================
   Shared modal chrome.
   Two modals (staff edit, off-duty confirm) share the same dark
   surface + scrim backdrop layout. The tow and police-shift create
   flows used to be in this group too, but have moved to inline panels;
   the Discord seed wizard was removed with the roster sync redesign.
   ============================================================ */

.staff-roster-modal,
.police-offduty-modal {
  border: none;
  padding: 0;
  background: transparent;
  color: var(--text);
  max-width: 480px;
  width: calc(100% - 2rem);
}
.staff-roster-modal::backdrop,
.police-offduty-modal::backdrop {
  background: var(--scrim);
  backdrop-filter: blur(4px);
}
.staff-roster-modal__form,
.police-offduty-modal__form {
  background: var(--surface-overlay);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-lg);
  padding: 1.5rem 1.6rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
  box-shadow: var(--shadow-modal);
}
.staff-roster-modal__field {
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.staff-roster-modal__title,
.police-offduty-modal__form h2 {
  font-family: var(--font-display, var(--font));
  font-size: var(--fs-lg);
  font-weight: var(--fw-hero);
  margin: 0;
}
.staff-roster-modal__error,
.garage-police__own-shift-error {
  color: var(--red);
  font-size: var(--fs-sm);
}
.staff-roster-modal__buttons,
.police-offduty-modal__buttons {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 0.5rem;
}

/* Inline start-shift panel: opens under the "Start a shift" button
   when the officer clicks it. Same chrome as the tow create-panel
   (consistency across the two CTAs); the form layout mirrors what
   the dialog used to render. */
.police-start-panel {
  background: var(--surface-1);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  padding: 1rem 1.2rem;
  margin-top: 0.6rem;
}
.police-start-panel__form {
  display: flex;
  flex-direction: column;
  gap: 0.8rem;
}
.police-start-panel__field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.police-start-panel__label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-muted);
}
.police-start-panel__error {
  color: var(--red);
  font-size: var(--fs-sm);
}
.police-start-panel__buttons {
  display: flex;
  justify-content: flex-end;
  gap: 0.6rem;
  margin-top: 0.2rem;
}

/* /staff/police-roster page layout. */
.staff-roster {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding: 1rem 0;
}
.staff-roster__header {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
}
.staff-roster__title {
  margin: 0;
  font-size: var(--fs-xl);
  font-weight: var(--fw-hero);
}
.staff-roster__section-title,
.staff-roster__queue-title {
  font-size: var(--fs-lg);
  margin: 0 0 0.5rem;
}
.staff-roster__hint {
  color: var(--text-muted);
  font-size: var(--fs-sm);
  margin: 0 0 0.6rem;
}
.staff-roster__intake-list,
.staff-roster__rank-list,
.staff-roster__audit-list,
.staff-roster__queue-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
}
.staff-roster__intake-tile,
.staff-roster__row,
.staff-roster__queue-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.5rem 0.75rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
}
.staff-roster__intake-tile:hover,
.staff-roster__row:hover {
  background: var(--surface-3);
}
.staff-roster__badge {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  min-width: 3ch;
}
.staff-roster__name {
  flex: 1;
  font-weight: var(--fw-bold);
  text-decoration: none;
  color: var(--text);
}
.staff-roster__name:hover { color: var(--role-police); }
.staff-roster__rank {
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}
.staff-roster__date {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-family: var(--font-mono);
}
.staff-roster__online-dot {
  font-size: var(--fs-xs);
  color: var(--status-green);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
/* /staff/admins and /staff/police-roster tables. Builds on
   .sortable-table (font-size baseline + sort-indicator chrome) and
   the existing .resident-avatar pattern for the identity cell. */
.staff-table { width: 100%; border-collapse: collapse; }
.staff-table th,
.staff-table td {
  padding: 0.55rem 0.75rem;
  vertical-align: top;
  border-bottom: 1px solid var(--border-hairline);
}
.staff-table tbody tr:last-child td { border-bottom: none; }
.staff-table thead th {
  text-align: left;
  font-size: var(--fs-xs);
  font-weight: var(--fw-bold);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--text-muted);
}
.staff-table__actions-col   { width: 1px; white-space: nowrap; }
.staff-table__actions-cell  { white-space: nowrap; text-align: right; }
/* Identity cell: avatar floats left, nickname + steamid stack right.
   The flex layout lives on the inner wrapper (.staff-table__identity),
   NOT on the <td> -- setting display:flex directly on a <td> opts the
   cell out of table-row height sync, which leaves the row's
   border-bottom hairline a sub-pixel out of step with its siblings and
   shows up as a broken/offset divider. Other tables in the codebase
   (housing, economy) follow the same wrapper-div pattern.
   The --no-avatar variant drops the avatar gap (used on
   /staff/police-roster where character-name + steamid is shown
   without an avatar -- player identity is intentionally character-
   centric there). */
.staff-table__identity {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  min-width: 0;
}
.staff-table__identity--no-avatar { gap: 0; }
/* Rank pill: small token next to the rank column on the main roster
   table. Reuses --radius-pill + muted surface like other section pills. */
.staff-table__rank-pill {
  display: inline-block;
  padding: 0.1rem 0.55rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--text-muted);
}
.staff-table__badge-cell {
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  white-space: nowrap;
}
.staff-table__identity-text {
  display: flex;
  flex-direction: column;
  min-width: 0;
  line-height: 1.2;
}
.staff-table__name {
  font-weight: var(--fw-bold);
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.staff-table__name--unknown { font-weight: var(--fw-regular); font-style: italic; color: var(--text-muted); }
.staff-table__steamid {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-family: var(--font-mono);
}
.staff-table__added-date  { color: var(--text); }
.staff-table__added-by    { font-size: var(--fs-xs); color: var(--text-muted); }
.staff-table__note-cell   { color: var(--text-muted); }
.staff-table__when-cell   { font-family: var(--font-mono); font-size: var(--fs-xs); color: var(--text-muted); white-space: nowrap; }
/* Two-line action cell on /staff/police-roster audit log: a readable
   label on top, an optional plain-English description ("Officer -> PPO;
   Badge: 042 -> 007") below in muted small text. Built from the
   action + before/after JSON in staff-police-roster-app.js. */
.staff-table__action-cell        { font-size: var(--fs-sm); color: var(--text); }
.staff-roster__audit-label       { font-weight: var(--fw-medium); }
.staff-roster__audit-desc {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  margin-top: 0.15rem;
}
.staff-table__sentinel {
  display: inline-block;
  padding: 0.1rem 0.45rem;
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  font-family: var(--font-mono);
}

/* Add-form panel -- inline section inside the Active section's head,
   appears in flow above the table when toggled. */
.staff-admins-add-panel { margin-bottom: 1rem; padding: 1rem 1.25rem; background: var(--surface-2); border-radius: var(--radius-md); border: 1px solid var(--border-hairline); }
.staff-admins-add-form { display: flex; flex-direction: column; gap: 0.75rem; }
.staff-admins-add-form__field        { display: flex; flex-direction: column; gap: 0.25rem; font-size: var(--fs-sm); color: var(--text-muted); }
.staff-admins-add-form__preview      { padding: 0.5rem 0.75rem; background: var(--surface-1); border-radius: var(--radius-md); font-size: var(--fs-sm); }
.staff-admins-add-form__error        { padding: 0.5rem 0.75rem; background: var(--surface-1); border-left: 3px solid var(--status-red); color: var(--text); font-size: var(--fs-sm); }
.staff-admins-add-form__actions      { display: flex; gap: 0.5rem; justify-content: flex-end; }

.staff-roster__rank-block { margin-bottom: 1rem; }
.staff-roster__rank-title {
  font-size: var(--fs-base);
  margin: 0 0 0.4rem;
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.staff-roster__audit-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.5rem;
  padding: 0.35rem 0.6rem;
  font-size: var(--fs-sm);
  border-bottom: 1px solid var(--border-soft);
}
.staff-roster__actor {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  text-decoration: none;
  color: var(--text);
}
.staff-roster__actor--admin:hover { color: var(--role-police); }
.staff-roster__actor--system {
  color: var(--text-muted);
  font-style: italic;
}
.staff-roster__audit-action,
.staff-roster__audit-ts {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}

/* Section-header icon: sits left of the section title, muted-police
   tint so it reads as decorative chrome rather than an action. The
   title wrapper is the .stats-section__title h2 (inline-flex via the
   icon's own alignment -- the .icon base class is vertical-aligned). */
.staff-roster__section-icon {
  width: 1.05em;
  height: 1.05em;
  color: var(--role-police);
  margin-right: 0.35rem;
  vertical-align: -0.18em;
}

/* Inline "lost the police role" chip, shown next to the name in the
   identity cell. Two states: un-acked (red, needs attention) and
   acked (muted, operator already saw it). */
.staff-roster__flag {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  border-radius: var(--radius-pill);
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  white-space: nowrap;
  vertical-align: middle;
}
.staff-roster__flag .icon { width: 0.9em; height: 0.9em; }
.staff-roster__flag--lost {
  color: color-mix(in srgb, var(--red) 75%, white);
  background: color-mix(in srgb, var(--red) 14%, transparent);
}
.staff-roster__flag--acked {
  color: var(--text-muted);
  background: color-mix(in srgb, var(--text-muted) 14%, transparent);
}

/* Flagged row: a faint red left border + tint so the whole row reads
   as "needs attention" even before the eye reaches the chip. */
.staff-table__row--flagged > td:first-child {
  box-shadow: inset 3px 0 0 0 color-mix(in srgb, var(--red) 60%, transparent);
}
.staff-table__row--flagged {
  background: color-mix(in srgb, var(--red) 5%, transparent);
}

/* Rank filter bar above the active-officers table. Reuses the project-
   wide .stats-toggle-group / .stats-toggle pattern (residents-page,
   statistics datasets) so all filter pills look the same across the
   app. Only the per-pill --accent token is set here -- everything else
   (layout, hover, on-state) inherits from the shared base. "All" maps
   to status-green by UI choice (mirrors the residents filter). */
.staff-roster-active-filter { margin-bottom: 0.75rem; }
.staff-roster-active-filter .stats-toggle[data-rank-filter="*"] {
  --accent: var(--status-green);
}
.staff-roster-active-filter .stats-toggle[data-rank-filter="chief"],
.staff-roster-active-filter .stats-toggle[data-rank-filter="asst_chief"],
.staff-roster-active-filter .stats-toggle[data-rank-filter="instructor"],
.staff-roster-active-filter .stats-toggle[data-rank-filter="sergeant"],
.staff-roster-active-filter .stats-toggle[data-rank-filter="officer"] {
  --accent: var(--role-police);
}

/* Both roster tables share these column widths (via <colgroup>) so
   Rank / Player / Badge / Actions line up vertically across the
   Active and Candidates sections. Without fixed widths the browser's
   auto-layout would size each table independently and the columns
   would drift apart. */
.staff-roster-table__col--rank    { width: 9rem;  }
.staff-roster-table__col--player  { width: auto;  }   /* flex */
.staff-roster-table__col--badge   { width: 6rem;  }
.staff-roster-table__col--actions { width: 16rem; }

/* Candidates is a native <details>: the <summary> carries the section
   heading. We swap the default disclosure triangle for a chevron that
   rotates on open, and align the summary content like other section
   heads. The whole strip is clickable. */
.staff-roster-collapsible > summary {
  list-style: none;       /* drop the default marker (Firefox/Chromium) */
  cursor: pointer;
}
.staff-roster-collapsible > summary::-webkit-details-marker {
  display: none;          /* drop the Safari/Chromium native marker */
}
.staff-roster-collapsible__summary {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.staff-roster-collapsible__summary::after {
  content: '';
  width: 0.65em;
  height: 0.65em;
  margin-left: auto;
  border-right: 2px solid var(--text-muted);
  border-bottom: 2px solid var(--text-muted);
  transform: rotate(-45deg);   /* points right when collapsed */
  transition: transform var(--motion-base);
}
.staff-roster-collapsible[open] > .staff-roster-collapsible__summary::after {
  transform: rotate(45deg);    /* points down when open */
}

/* Inline edit panel: an expandable row beneath a roster entry, opened
   by the row's "Edit" button. Replaces the old centred <dialog> for the
   staff roster (user-requested). The .staff-roster-edit__row is the
   <tr> wrapper; .staff-roster-edit is the flex strip inside its single
   full-width <td>. */
.staff-roster-edit__row > td {
  padding: 0;
  border-bottom: 1px solid var(--border-soft);
}
.staff-roster-edit {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 0.9rem;
  padding: 0.8rem 1rem;
  background: var(--surface-1);
}
.staff-roster-edit__field {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.staff-roster-edit__label {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.staff-roster-edit__rank,
.staff-roster-edit__badge {
  /* inherits the global dark form-element reset; keep widths sane */
  min-width: 8rem;
}
.staff-roster-edit__badge { min-width: 6rem; }
.staff-roster-edit__badge-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.staff-roster-edit__actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;   /* push Save/Cancel/Revoke to the right edge */
}
/* Revoke sits visually apart (destructive) + has an "armed" confirm
   state after the first click. */
.staff-roster-edit__revoke {
  margin-left: 0.6rem;
  color: color-mix(in srgb, var(--red) 60%, white);
  border-color: color-mix(in srgb, var(--red) 30%, transparent);
}
.staff-roster-edit__revoke--armed {
  background:    color-mix(in srgb, var(--red) 28%, transparent);
  border-color: color-mix(in srgb, var(--red) 60%, transparent);
  color:        color-mix(in srgb, var(--red) 85%, white);
}
.staff-roster-edit__error {
  flex: 1 0 100%;
  color: color-mix(in srgb, var(--red) 75%, white);
  font-size: var(--fs-sm);
}

/* /garage/police page layout. */
.garage-police {
  max-width: 720px;
  margin: 0 auto;
  padding: 1.5rem 2rem 3rem;
  display: flex;
  flex-direction: column;
  gap: 1rem;
}
.garage-police__panel {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
}
.garage-police__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
}
.garage-police__title {
  margin: 0;
  font-size: var(--fs-xl);
}
.garage-police__count {
  font-size: var(--fs-xs);
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.garage-police__banner {
  background: color-mix(in srgb, var(--status-orange) 22%, transparent);
  color: var(--status-orange);
  padding: 0.4rem 0.6rem;
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
}
.garage-police__own-shift {
  background: var(--surface-2);
  border: 1px solid color-mix(in srgb, var(--role-police) 30%, var(--border-soft));
  border-radius: var(--radius-md);
  padding: 0.75rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
/* No-shift wrapper: no surface chrome, only stacks the CTA + the inline
   start panel that opens underneath. Mirrors the tow create surface
   where the CTA sits bare in the section. */
.garage-police__no-shift {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.garage-police__own-shift-title {
  font-size: var(--fs-base);
  margin: 0;
}
.garage-police__own-shift-meta {
  display: flex;
  gap: 0.75rem;
  font-size: var(--fs-sm);
  color: var(--text-muted);
}
.garage-police__own-shift-controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
  font-size: var(--fs-sm);
}
.garage-police__own-shift-controls label {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}
.garage-police__own-shift-events { font-size: var(--fs-sm); }
.garage-police__own-events-list {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  max-height: 200px;
  overflow-y: auto;
}
.garage-police__own-events-list li {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}
.garage-police__own-events-list time {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.garage-police__list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
.garage-police__rows {
  list-style: none;
  padding: 0;
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
}
.garage-police__row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.6rem;
  background: var(--surface-2);
  border: 1px solid var(--border-soft);
  border-radius: var(--radius-md);
  font-size: var(--fs-sm);
}
.garage-police__row:hover {
  background: var(--surface-3);
  border-color: color-mix(in srgb, var(--role-police) 40%, var(--border-soft));
}
.garage-police__badge {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  color: var(--text-muted);
  min-width: 3ch;
}
.garage-police__name {
  flex: 1;
  font-weight: var(--fw-bold);
  text-decoration: none;
  color: var(--text);
}
.garage-police__name:hover { color: var(--role-police); }
.garage-police__rank,
.garage-police__code,
.garage-police__region,
.garage-police__since {
  font-size: var(--fs-xs);
  color: var(--text-muted);
}
.garage-police__since { font-family: var(--font-mono); }
.garage-police__empty {
  padding: 1rem;
  text-align: center;
  color: var(--text-muted);
  font-size: var(--fs-sm);
}
/* "View on map" deep-link in the own-shift meta row. Inline pill,
   subtle until hovered. */
.garage-police__own-shift-map-link {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  color: var(--role-police);
  text-decoration: none;
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  transition: color var(--motion-fast);
}
.garage-police__own-shift-map-link:hover {
  color: color-mix(in srgb, var(--role-police) 70%, white);
}

