/* ── RealRacer.ai — global app shell ─────────────────────────────────
 *
 * Single source of truth for the chrome shared by every logged-in
 * page (dashboard / laps / analysis / coaching / upload / settings /
 * my-setups / overlay).
 *
 * Used in tandem with /static/typography.css (design tokens) — typography
 * brings the spacing / type scale variables; this file uses them to
 * style the shell (sidebar, mobile drawer, panels, buttons, toasts).
 *
 * Page-specific styles continue to live in each page's <style> block;
 * but the chrome that used to be duplicated ~250 lines × ~8 pages now
 * lives here. See deferred memos for the full migration plan.
 *
 * Mobile contract
 * ---------------
 * The fixed 240px sidebar collapses to a slide-out drawer at
 * <980px width. A new top-bar appears with a hamburger that opens the
 * drawer (full-height, slide-from-left, dimmed backdrop). Closing
 * options: tap outside the drawer, tap a nav item, press Escape.
 * Crucial because the mobile real-track coaching product (per the
 * platform_vision_realracer_ecosystem memo) means people will be
 * using this on phones at race tracks.
 *
 * ─────────────────────────────────────────────────────────────────── */

/* ── Theme tokens ─────────────────────────────────────────────────── */
:root {
  /* Brand */
  --orange:       #FF8000;
  --orange-dark:  #cc6600;
  /* Surfaces */
  --bg:           #0a0a0f;
  --surface:      #06060c;
  --surface-2:    #0e0e16;
  --border:       #1e1e2e;
  /* Text */
  --text:         #f0f0f8;
  --muted:        #a0a0b8;
  --faint:        #606070;
  /* Accents */
  --blue:         #3b9eff;
  /* One green, one loss red — the brand values (tokens.css owns the v2
     copies; these keep pages that load app.css alone on the same hex). */
  --green:        #00C864;
  /* --red / --rose = loss/danger only. Brand action uses --orange /
     --accent (P0-3b). Do not re-alias --red to orange. */
  --red:          #ff5b5b;
  --red-dark:     #e04545;
  --rose:         #ff5b5b;
  --amber:        #fbbf24;
  --yellow:       #eab308;
}

/* ── Reset ─────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: 'Barlow', -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}
a { text-decoration: none; color: inherit; }
button { font-family: inherit; cursor: pointer; }
img, svg { display: block; max-width: 100%; }
.rr-inline-link {
  color: var(--accent, var(--orange));
  font-weight: 600;
  text-decoration: none;
  border-bottom: 1px solid rgba(var(--accent-rgb, 255, 128, 0), 0.35);
}
.rr-inline-link:hover {
  border-bottom-color: var(--accent, var(--orange));
}

/* ── App shell layout ─────────────────────────────────────────────── */
.layout {
  display: flex;
  min-height: 100vh;
  /* P2-5: clip accidental horizontal spill from dense tables / nowrap chips
     without killing intentional in-component scroll (those use overflow-x:auto). */
  overflow-x: clip;
}
.main {
  margin-left: 240px;
  flex: 1;
  min-width: 0;
  padding: var(--space-5, 20px) var(--space-6, 28px);
  max-width: 1200px; /* P2-5 — Improve content width (was 1600) */
  overflow-x: clip;
}
/* Older pages used <main> rather than .main; keep both selectors in sync */
main {
  margin-left: 240px;
  flex: 1;
  min-width: 0;
  padding: var(--space-5, 20px) var(--space-6, 28px);
  max-width: 1200px;
  overflow-x: clip;
}

/* ── Page-level tabs ──────────────────────────────────────────────
 * Used by destinations that collapsed multiple legacy sidebar entries
 * into one nav item. Sits directly under .page-header. DM Mono eyebrow
 * voice — small caps, tracked. Orange underline marks the active tab.
 *   <div class="page-tabs">
 *     <a class="page-tab active" href="/laps">All laps</a>
 *     <a class="page-tab"        href="/ghosts">Ghosts</a>
 *   </div>
 */
.page-tabs {
  display: flex;
  gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 20px;
  overflow-x: auto;
  scrollbar-width: none;
}
.page-tabs::-webkit-scrollbar { display: none; }
.page-tab {
  font-family: 'DM Mono', 'JetBrains Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  padding: 12px 16px;
  color: var(--muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color 0.15s, border-color 0.15s;
  margin-bottom: -1px;
}
.page-tab:hover { color: var(--text); }
.page-tab.active {
  color: var(--text);
  border-bottom-color: var(--accent, var(--orange));
}

/* ── Filter chips (pill) — destination tabs stay .page-tabs above.
 * Soft mono active state matches the Sessions/Laps coherence prototype.
 * Solid fill is `.chip-v2.is-solid` for catalogue filters that want weight.
 * Legacy page classes (.ss-chip / .lp-chip / .dr-chip / .chip) alias here
 * so JS can keep toggling `.active` without a rename pass.
 */
.chip-v2,
.ss-chip,
.lp-chip,
.dr-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'JetBrains Mono', 'DM Mono', monospace;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 7px 14px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.chip-v2:hover,
.ss-chip:hover,
.lp-chip:hover,
.dr-chip:hover {
  color: var(--text);
  border-color: rgba(255, 255, 255, 0.22);
}
.chip-v2.is-active,
.chip-v2.active,
.ss-chip.active,
.lp-chip.active,
.dr-chip.active {
  background: rgba(var(--accent-rgb, 255, 128, 0), 0.14);
  color: var(--accent, var(--orange));
  border-color: rgba(var(--accent-rgb, 255, 128, 0), 0.45);
}
/* Catalogue / garage filters that historically used a solid fill. */
.chip,
.chip-v2.is-solid {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: 'JetBrains Mono', 'DM Mono', monospace;
  font-size: 10.5px;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 7px 14px;
  border-radius: 100px;
  background: rgba(255, 255, 255, 0.04);
  border: 1px solid rgba(255, 255, 255, 0.12);
  color: var(--muted);
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.chip:hover,
.chip-v2.is-solid:hover {
  color: var(--text);
  border-color: rgba(255, 255, 255, 0.22);
}
.chip.active,
.chip-v2.is-solid.is-active,
.chip-v2.is-solid.active {
  background: var(--accent, var(--orange));
  border-color: var(--accent, var(--orange));
  color: #0a0a0f; /* dark-on-accent — never white-on-papaya */
}

/* ── Sidebar (desktop ≥980px) ─────────────────────────────────────── */
.sidebar {
  width: 240px;
  flex-shrink: 0;
  /* Pin the nav font so it's identical on every page regardless of what
     a page sets on <body>. The sidebar is a shared partial; previously it
     inherited the page's body font, so pages that set font-family:var(--body)
     (Inter) made the left-nav render in a different face than the Barlow
     majority. Pinning here makes that class of drift impossible. */
  font-family: 'Barlow', -apple-system, sans-serif;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: var(--sidebar-py, 16px) 0;
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  z-index: 30;
  transition: transform 0.2s ease;
}
.sidebar-logo {
  display: block;
  font-size: 20px;
  font-weight: 800;
  color: var(--text);
  text-decoration: none;
  padding: 0 var(--sidebar-px, 18px) var(--space-4, 14px);
  border-bottom: 1px solid var(--border);
  margin-bottom: 8px;
}
/* ── Brand lockup ────────────────────────────────────────────────────
 * Logo as: small italic RR mark (SVG) + wordmark rendered as HTML text
 * using the page's loaded Inter font. We do it this way instead of
 * embedding <text> inside the SVG because browsers don't honour custom
 * fonts inside SVGs loaded via <img>, so the SVG-text path silently
 * falls back to a generic sans-serif (no Arial Black weight) and the
 * wordmark looks thin / wrong. Inter is bundled into the page's font
 * stack, so HTML text renders at the intended weight everywhere.
 *
 * Sizing: font-size is set on the parent .rr-lockup (or inline). The
 * mark <img> sits at its own height (28/32/22 depending on slot).
 */
.rr-lockup {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  text-decoration: none;
  white-space: nowrap;
  line-height: 1;
}
.rr-lockup .rr-mark { display: block; flex-shrink: 0; }
/* Inline skewed-RR SVG mark (matches the v2 marketing nav). The papaya
   "R" + speed lines are .r1/.r1s; the white "R" is hardcoded #fff in the
   markup. Under the real-circuit theme they flip to green — same as the
   wordmark's .rr-orange rule below. (The legacy <img> theme-swap in
   app.js initLogoThemeSwap only touches img.rr-mark, so inline marks rely
   on these CSS rules instead.) */
.rr-lockup .rr-mark .r1 { fill: var(--orange); }
.rr-lockup .rr-mark .r1s { stroke: var(--orange); }
body.theme-circuit .rr-lockup .rr-mark .r1 { fill: var(--green); }
body.theme-circuit .rr-lockup .rr-mark .r1s { stroke: var(--green); }
.rr-lockup .rr-word {
  font-family: 'Inter', -apple-system, 'Helvetica Neue', Arial, sans-serif;
  font-weight: 900;
  letter-spacing: -0.5px;
  color: var(--text);
  line-height: 1;
}
.rr-lockup .rr-orange { color: var(--orange); transition: color 0.2s; }
/* Real-circuit theme — wordmark's "Racer" text + the brand mark SVG
   both flip from papaya orange to circuit green. The SVG swap is
   handled in app.js initLogoThemeSwap() (MutationObserver on body
   class). This rule covers the text half.
   Pages that previously hardcoded "stay orange under theme-circuit"
   (coaching.html prior to 2026-06-04) have been reverted so the
   theme is consistent across the brand. */
body.theme-circuit .rr-lockup .rr-orange { color: var(--green); }
.rr-lockup .rr-ai {
  font-family: 'JetBrains Mono', 'Courier New', monospace;
  font-size: 0.42em;
  font-weight: 400;
  color: var(--faint);
  margin-left: 2px;
  vertical-align: top;
  letter-spacing: 0;
}

/* Group-cluster labels (claude design review-journeys §2). Quiet
   mono uppercase markers between DRIVE / IMPROVE / GARAGE sections. */
.nav-group-label {
  font-family: 'JetBrains Mono', 'DM Mono', monospace;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--faint, #5e5e72);
  padding: 14px 22px 6px;
}
.nav-group-label:first-child { padding-top: 4px; }

.nav-item {
  display: flex;
  align-items: center;
  gap: var(--nav-item-gap, 10px);
  padding: var(--nav-item-py, 8px) var(--nav-item-px-x, 18px);
  font-size: 14px;
  font-weight: 500;
  color: var(--muted);
  cursor: pointer;
  border-left: 2px solid transparent;
  text-decoration: none;
  /* Touch target ≥40px on mobile so phone users aren't fat-fingering */
  min-height: 40px;
}
.nav-item:hover { color: var(--text); background: var(--surface-2); }
.nav-item.active {
  color: var(--text);
  border-left-color: var(--orange);
  background: rgba(255, 128, 0, 0.08);
}
.nav-icon {
  font-size: 16px;
  width: 20px;
  text-align: center;
  flex-shrink: 0;
}

/* Section heading inside the sidebar — non-clickable, groups related
 * nav items underneath it (e.g. "Laps" header over Search + Ghost).
 * Small, muted, sentence-case. */
.nav-section {
  font-size: 11px;
  font-weight: 600;
  color: var(--faint);
  padding: 14px var(--sidebar-px, 18px) 4px;
  letter-spacing: 0;
  /* Clickable to collapse — wired by sidebar.js. */
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  user-select: none;
}
.nav-section:hover { color: var(--muted); }
.nav-section-caret {
  font-size: 9px;
  color: var(--faint);
  transition: transform 0.15s ease;
  margin-right: 2px;
}
.nav-section.is-collapsed .nav-section-caret { transform: rotate(-90deg); }
.nav-section.is-collapsed { padding-bottom: 14px; }
/* Sub-items sit slightly indented under a .nav-section so the grouping
 * reads as a tree at a glance. */
.nav-item.nav-sub {
  padding-left: calc(var(--nav-item-px-x, 18px) + 14px);
  font-size: 13px;
}

.sidebar-bottom {
  margin-top: auto;
  padding: 14px 18px;
  border-top: 1px solid var(--border);
  /* Anchor for the absolutely-positioned #user-menu pop-up. */
  position: relative;
  /* Don't let the chip be squashed when the nav above it is the
     scrolling container — it has to stay a full-height row at the
     bottom of the sidebar. */
  flex-shrink: 0;
}

/* Sidebar user-chip — was only styled inline on dashboard.html; lift
   the rules into app.css so every page that mounts the sidebar partial
   gets the orange gradient avatar, ellipsis email, and hover state. */
.user-chip {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px;
  border-radius: 8px;
  cursor: pointer;
  transition: background 0.15s;
}
.user-chip:hover { background: var(--surface-2); }
.avatar {
  width: 32px;
  height: 32px;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--orange), var(--orange-dark, #cc5500));
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 13px;
  font-weight: 700;
  color: #fff;
  flex-shrink: 0;
}
.user-info { flex: 1; min-width: 0; }
.user-email {
  font-size: 12px;
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.user-menu-item:hover { background: rgba(255,255,255,0.04); }

/* Make the nav region between the logo and the user-chip the
   scrollable area. Without this, sidebars with many items push the
   .sidebar-bottom off the bottom of the viewport (the user chip with
   Settings / Log out then becomes unreachable). Targets both shapes
   pages use: the partial's <div class="sidebar-nav"> and the legacy
   <nav> wrapper still used by dashboard.html. */
.sidebar > nav,
.sidebar > .sidebar-nav {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Quietly thin the scrollbar on browsers that honour it. */
  scrollbar-width: thin;
  scrollbar-color: var(--border) transparent;
}
.sidebar > nav::-webkit-scrollbar,
.sidebar > .sidebar-nav::-webkit-scrollbar { width: 6px; }
.sidebar > nav::-webkit-scrollbar-thumb,
.sidebar > .sidebar-nav::-webkit-scrollbar-thumb {
  background: var(--border);
  border-radius: 3px;
}

/* User-menu pops UP from the bottom of the sidebar (it sits at the
   bottom of a fixed-height sidebar, so falling DOWN would push it off
   the viewport). Absolute-positioned with bottom: 100% so it floats
   directly above the user-chip. */
.sidebar-bottom > #user-menu {
  position: absolute !important;
  left: 12px;
  right: 12px;
  bottom: calc(100% - 14px);      /* above the user-chip, overlapping the top border */
  margin-top: 0 !important;        /* override the inline style from each page */
  margin-bottom: 8px;
  z-index: 32;
  box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
}

/* ── Mobile top-bar + drawer (<980px) ─────────────────────────────── */
/* Mobile-specific elements are hidden by default on desktop; the
   <980px media query below flips them on. */
/* Pin the mobile top-bar font too (same reason as .sidebar) so the
   nav surface is Barlow on every page regardless of the page's body. */
.mobile-topbar { display: none; font-family: 'Barlow', -apple-system, sans-serif; }
.mobile-drawer-backdrop { display: none; }

@media (max-width: 980px) {
  /* The flex layout default is row, which puts the sticky topbar
     BESIDE <main> instead of stacking above it — the logo then
     renders down the left edge of the page. Flip to column at
     mobile so the topbar is a true full-width banner and main
     reflows below it. */
  .layout {
    flex-direction: column;
  }

  /* Sidebar becomes a slide-out drawer. Higher specificity selectors
     ensure per-page <style> blocks (which load AFTER app.css and used
     to win the cascade) can't overwrite the drawer behaviour. */
  body .sidebar, body aside.sidebar {
    transform: translateX(-100%);
    box-shadow: 0 0 40px rgba(0, 0, 0, 0.6);
    /* Drawer width: comfortably wide on phones, never wider than the
       viewport with a 24px margin so the user can see it's overlay. */
    width: min(280px, calc(100vw - 24px)) !important;
    /* Force position:fixed so per-page sticky/relative rules don't
       reflow the sidebar into the document flow (which was making it
       "get bigger" — flex grow — instead of sliding off). */
    position: fixed !important;
    top: 0;
    left: 0;
    bottom: 0;
    z-index: 30;
  }
  body.drawer-open .sidebar, body.drawer-open aside.sidebar {
    transform: translateX(0) !important;
  }

  /* Backdrop dims the main content while drawer is open. v2: soft
     blur in addition to the alpha dim — gives the drawer a "lifted"
     feel against the warmer palette. Fall back to plain dim where
     backdrop-filter isn't supported. */
  .mobile-drawer-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.55);
    -webkit-backdrop-filter: blur(2px);
    backdrop-filter: blur(2px);
    z-index: 25;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.18s ease;
  }
  body.drawer-open .mobile-drawer-backdrop {
    opacity: 1;
    pointer-events: auto;
  }

  /* Top bar — appears at the top on mobile */
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: 10px 14px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
    min-height: 52px;
  }
  .mobile-topbar .menu-btn {
    background: transparent;
    border: 1px solid var(--border);
    color: var(--text);
    border-radius: 8px;
    padding: 0;
    width: 40px; height: 40px;
    display: inline-flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    transition: background .15s, border-color .15s;
  }
  .mobile-topbar .menu-btn:hover { background: var(--surface-2); border-color: var(--muted); }
  .mobile-topbar .topbar-title {
    /* v2 type — Inter Tight display so the page title above the
       drawer matches the headline of the page underneath. */
    font-family: var(--display, 'Inter Tight', system-ui, sans-serif);
    font-size: 15px;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--text);
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .mobile-topbar .topbar-logo {
    font-family: var(--display, 'Inter Tight', system-ui, sans-serif);
    font-size: 16px;
    font-weight: 800;
    letter-spacing: -0.02em;
    color: var(--text);
  }
  /* Main content reflows full-width on mobile, no left offset.
     `body main`, `body .main`, and `.layout main` raise specificity
     above the per-page `main { margin-left: 240px }` rules that load
     AFTER app.css (each HTML page has its own <style> block in <head>
     that beats us at single-tag specificity). User feedback 2026-05-16:
     "left nav gets bigger not smaller" — was caused by the per-page
     desktop margin-left:240px still applying on mobile while the
     sidebar tried to slide off, leaving a 240px gap pushing main
     content off-screen. */
  body main, body .main, .layout main, .layout .main {
    margin-left: 0 !important;
    padding: 16px !important;
    max-width: 100% !important;
  }
}

/* Smaller phones — tighter padding, allow more horizontal room */
@media (max-width: 480px) {
  body main, body .main, .layout main, .layout .main {
    padding: 12px !important;
  }
}

/* ── Panels + cards ───────────────────────────────────────────────── */
.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card, var(--card-radius, 12px));
  padding: var(--space-5, 20px) var(--space-6, 28px);
  margin-bottom: var(--space-5, 20px);
}
.panel h2 {
  font-size: 14px;
  font-weight: 700;
  margin-bottom: var(--space-2, 6px);
}
.panel h3 {
  font-size: 13px;
  font-weight: 700;
  color: var(--text);
  letter-spacing: 0;
  margin-bottom: var(--space-2, 6px);
}
.panel p.intro {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.55;
  margin-bottom: var(--space-4, 14px);
}
@media (max-width: 480px) {
  .panel {
    padding: var(--space-4, 14px) var(--space-5, 20px);
    border-radius: var(--radius-card, var(--card-radius, 12px));
  }
}

/* ── Buttons ──────────────────────────────────────────────────────── */
.btn {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text);
  font-family: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 9px 16px;
  border-radius: 8px;
  cursor: pointer;
  transition: all 0.15s;
  /* 40px min height = touch-friendly */
  min-height: 40px;
}
.btn:hover { background: var(--border); }
/* Dark-on-accent primary lives with .btn-v2.is-primary below (P0-5 / P1-9).
   .btn-coach keeps its secondary / icon-only extras. */
.btn-coach {
  background: var(--accent, var(--orange));
  border: 1px solid var(--accent, var(--orange));
  color: #0a0a0f;
  font-weight: 600;
  padding: 9px 18px;
  border-radius: 8px;
  font-size: 13px;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  min-height: 40px;
}
.btn-coach:hover {
  background: var(--accent-dk, var(--orange-dark));
  border-color: var(--accent-dk, var(--orange-dark));
}
.btn-coach.secondary {
  background: transparent;
  color: var(--accent, var(--orange));
}
.btn-coach.secondary:hover {
  background: rgba(var(--accent-rgb, 255, 128, 0), 0.08);
}
.btn-coach.icon-only {
  padding: 9px 12px;
  min-width: 40px;
}
.btn-danger {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
}
.btn-danger:hover {
  border-color: var(--rose);
  color: var(--rose);
  background: rgba(255, 91, 91, 0.06);
}

/* Canonical v2 buttons (promoted from form.css so review pages that
 * don't load form.css still share one control system). Prefer these
 * for new markup; .btn / .btn-primary / .btn-coach remain as aliases. */
.btn-v2 {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 10px 16px;
  border-radius: 8px;
  border: 1px solid transparent;
  cursor: pointer;
  transition: filter 0.15s, background 0.15s, border-color 0.15s, color 0.15s;
  min-height: 40px;
  text-decoration: none;
  color: var(--text);
  background: var(--surface-2);
}
.btn-v2:disabled {
  opacity: 0.55;
  cursor: wait;
}
.btn-v2.is-primary,
.btn-primary {
  background: var(--accent, var(--orange));
  border-color: var(--accent, var(--orange));
  color: #0a0a0f;
  font-weight: 600;
}
.btn-v2.is-primary:hover:not(:disabled),
.btn-primary:hover {
  background: var(--accent-dk, var(--orange-dark));
  border-color: var(--accent-dk, var(--orange-dark));
  filter: none;
}
.btn-v2.is-secondary,
.btn-outline {
  background: transparent;
  color: var(--text);
  border-color: var(--border);
}
.btn-v2.is-secondary:hover:not(:disabled),
.btn-outline:hover {
  border-color: var(--muted);
  background: var(--surface-2);
}
.btn-v2.is-ghost,
.btn-ghost {
  background: var(--surface-2);
  color: var(--muted);
  border-color: var(--border);
}
.btn-v2.is-ghost:hover:not(:disabled),
.btn-ghost:hover {
  color: var(--text);
  border-color: var(--muted);
}
.btn-v2.is-danger {
  background: transparent;
  color: var(--red, var(--rose));
  border-color: var(--red, var(--rose));
}
.btn-v2.is-danger:hover:not(:disabled) {
  background: rgba(255, 91, 91, 0.08);
}

/* ── Empty state ──────────────────────────────────────────────────── */
.empty-state {
  padding: 32px;
  text-align: center;
  color: var(--faint);
  font-size: 13px;
}
.empty-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-card, var(--card-radius, 12px));
  padding: var(--space-6, 28px) var(--space-5, 20px);
  text-align: center;
}
.empty-card h2 { font-size: 16px; font-weight: 700; margin-bottom: 10px; }
.empty-card p {
  color: var(--muted);
  font-size: 14px;
  line-height: 1.6;
  margin-bottom: 18px;
  max-width: 520px;
  margin-left: auto;
  margin-right: auto;
}
.empty-card .cta {
  display: inline-block;
  padding: 9px 22px;
  border-radius: 8px;
  background: var(--accent, var(--orange));
  color: #0a0a0f;      /* same dark-on-accent contrast as .btn-primary */
  font-weight: 600;
  font-size: 14px;
}
.empty-card .cta:hover { background: var(--orange-dark); }

/* ── Toast ────────────────────────────────────────────────────────── */
.toast {
  position: fixed;
  bottom: 20px;
  right: 20px;
  background: var(--surface);
  border: 1px solid var(--orange);
  color: var(--text);
  padding: 10px 16px;
  border-radius: 8px;
  font-size: 13px;
  z-index: 1000;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.4);
}
@media (max-width: 480px) {
  .toast {
    left: 12px; right: 12px; bottom: 12px;
    /* Cap to one phone-line of content */
    font-size: 13px;
  }
}

/* ── Tiny utility set (Tailwind-ish, deliberately small) ──────────── */
/* These are the handful that actually show up repeatedly across pages.
   Resist the urge to grow this into a full utility framework — the
   Option A memo argues that's where Tailwind's true value would kick
   in, and at that point we should adopt it properly with a build. */
.flex   { display: flex; }
.flex-1 { flex: 1; min-width: 0; }
.flex-col { flex-direction: column; }
.items-center { align-items: center; }
.justify-between { justify-content: space-between; }
.gap-1 { gap: var(--space-1, 4px); }
.gap-2 { gap: var(--space-2, 6px); }
.gap-3 { gap: var(--space-3, 10px); }
.gap-4 { gap: var(--space-4, 14px); }
.text-muted { color: var(--muted); }
.text-faint { color: var(--faint); }
.text-orange { color: var(--orange); }
.text-rose  { color: var(--rose); }
.text-green { color: var(--green); }
.font-mono { font-family: 'JetBrains Mono', Menlo, monospace; font-feature-settings: 'tnum'; }
.font-bold { font-weight: 700; }
.text-xs { font-size: 11px; }
.text-sm { font-size: 13px; }
.hidden  { display: none; }
.mt-2 { margin-top: var(--space-2, 6px); }
.mt-4 { margin-top: var(--space-4, 14px); }
.mb-2 { margin-bottom: var(--space-2, 6px); }
.mb-4 { margin-bottom: var(--space-4, 14px); }

/* ── Upgrade CTA component (B42) ──────────────────────────────────
 *
 * rr-upgrade-*  classes form an inline upsell card for users whose
 * tier is below a feature's gate.  The tone is inviting, not punitive:
 * --surface-2 background + --orange left accent + lock icon, never
 * --red.  Distinct from error states.
 *
 * JS API:  window.RealRacer.renderUpgradeCTA(container, opts)
 *          lives in /static/upgrade-cta.js.
 * ────────────────────────────────────────────────────────────────── */
.rr-upgrade-cta {
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-left: 3px solid var(--orange);
  border-radius: var(--radius-card, var(--radius-lg, 12px));
  padding: var(--space-4, 14px) var(--space-4, 14px);
  display: flex;
  flex-direction: column;
  gap: var(--space-3, 10px);
  max-width: 620px;
}

/* Compact variant — just the top-left accent, no left-strip */
.rr-upgrade-cta.rr-upgrade-compact {
  border-left: 1px solid var(--border);
  border-top: 3px solid var(--orange);
}

/* Headline row: lock icon + feature name */
.rr-upgrade-headline {
  display: flex;
  align-items: center;
  gap: 8px;
  color: var(--orange);
}
.rr-upgrade-headline-text {
  font-weight: 600;
  font-size: 14px;
  color: var(--text);
  line-height: 1.3;
  margin: 0;
}

/* Subline: "Unlocks with <tier>" — tier name in orange bold */
.rr-upgrade-subline {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.5;
}
.rr-upgrade-subline strong {
  color: var(--orange);
  font-weight: 600;
}

/* CTA button — orange bg, dark text, deep-link */
.rr-upgrade-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 7px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  background: var(--orange);
  color: #0a0a0f;
  border: 1px solid var(--orange);
  transition: filter 0.15s;
  text-decoration: none;
  width: fit-content;
}
.rr-upgrade-btn:hover {
  filter: brightness(1.1);
}

/* Mobile — natural reflow, button goes full-width */
@media (max-width: 720px) {
  .rr-upgrade-cta {
    max-width: none;
  }
  .rr-upgrade-btn {
    width: 100%;
    justify-content: center;
  }
}

/* ── Shared empty / error states (P1-2) ────────────────────────────
 * .rr-empty mirrors data-table.css's .rr-table__empty so a page can dual-
 * class them; .rr-error adds a "Try again" affordance for failed loads. */
.rr-empty,
.rr-error {
  text-align: center;
  padding: 60px 20px;
  font-family: var(--body, 'Barlow', sans-serif);
  color: var(--muted);
}
.rr-empty h3,
.rr-error h3 {
  font-family: var(--hero, 'Bebas Neue', sans-serif);
  font-size: 32px;
  color: var(--text);
  margin: 0 0 10px;
  letter-spacing: 0.02em;
}
.rr-error button {
  margin-top: 16px;
  font-family: 'JetBrains Mono', 'DM Mono', monospace;
  font-size: 11px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  padding: 9px 18px;
  border-radius: 9px;
  background: transparent;
  color: var(--orange);
  border: 1px solid rgba(255, 128, 0, 0.40);
  cursor: pointer;
}
.rr-error button:hover {
  background: rgba(255, 128, 0, 0.08);
}

/* ── Accessibility (P1-6) ──────────────────────────────────────────
 * A visible focus ring for keyboard users on every interactive control.
 * :focus-visible only paints on keyboard focus, so mouse clicks stay
 * ring-free. Uses the accent so it reads on both lanes. */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
.chip-v2:focus-visible,
.ss-chip:focus-visible,
.dr-chip:focus-visible,
.page-tab:focus-visible,
.btn:focus-visible,
.btn-v2:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--accent, #ff8000);
  outline-offset: 2px;
  border-radius: 4px;
}

/* Respect reduced-motion: kill the "live"/"fresh" pulse animations so a
 * driver who opted out of motion doesn't get a throbbing dot. Covers the
 * shared dots + the page-local pulse keyframes on sessions/laps. */
@media (prefers-reduced-motion: reduce) {
  .ss-fresh-dot,
  .lp-live-dot,
  .rr-live-dot,
  .live-dot {
    animation: none !important;
  }
}
