/* ── 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;

  /* Height of the phone's status bar (clock / signal / battery).
     Non-zero ONLY when the page sets `viewport-fit=cover`, which makes
     web content render UNDERNEATH that bar instead of below it — most
     app pages do, plus `apple-mobile-web-app-status-bar-style:
     black-translucent`. Everywhere else it resolves to 0px, so rules
     built on it are inert rather than conditional.

     Held in a variable rather than calling env() inline so a headless
     browser can override it and actually exercise the inset layout —
     env() always reports 0 off-device, which is why this class of bug
     reaches a real phone unseen. See tests/smoke/playwright. */
  --rr-safe-top: env(safe-area-inset-top, 0px);
}

/* ── 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: 0 0 auto;
  width: 2.8em;
  height: 1.5em;
  background: url(/static/logo.svg?v=brand-v3-fidelity) center/contain no-repeat;
}
body.theme-circuit .rr-lockup .rr-mark {
  background-image: url(/static/logo-circuit.svg?v=brand-v3-fidelity);
}
body.lane-purple .rr-lockup .rr-mark,
body.mode-drift .rr-lockup .rr-mark {
  background-image: url(/static/logo-drift.svg?v=brand-v3-fidelity);
}
body.lane-blue .rr-lockup .rr-mark,
body.theme-coach .rr-lockup .rr-mark {
  background-image: url(/static/logo-coach.svg?v=brand-v3-fidelity);
}
.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; }
/* Discipline state retints the mark and the "Real" word together. */
body.theme-circuit .rr-lockup .rr-orange { color: var(--green); }
body.lane-purple .rr-lockup .rr-orange,
body.mode-drift .rr-lockup .rr-orange { color: var(--purple); }
body.lane-blue .rr-lockup .rr-orange,
body.theme-coach .rr-lockup .rr-orange {
  color: #5EC8FF;
}
.rr-lockup .rr-ai {
  display: inline-block;
  font-family: 'JetBrains Mono', 'Courier New', monospace;
  font-size: .34em;
  font-weight: 600;
  line-height: 1;
  color: var(--faint);
  margin-left: .22em;
  vertical-align: .16em;
  letter-spacing: .02em;
}

/* 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;
    /* Same status-bar inset as .mobile-topbar — the drawer is pinned to
       top:0, so on a viewport-fit=cover page its own logo rendered at
       y=16..72 underneath a 47px status bar. Added to the base 16px
       rather than replacing it so the drawer keeps its normal breathing
       room where there's no inset (variable resolves to 0px). */
    padding-top: calc(16px + var(--rr-safe-top, 0px));
  }
  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.

     Padded down by the status-bar inset so the hamburger sits BELOW the
     phone's clock/signal/battery instead of under it. Without this the
     40px button rendered at y=10..50 against a 47px status bar on an
     iPhone 14/15 Pro — 37px of it physically unreachable, which is what
     "can't access the menus on /live" was (reported 2026-08-14).

     `min-height` has to grow by the same amount because the global reset
     is `box-sizing: border-box`: min-height bounds the BORDER box, so
     padding eats into it, and a bare padding bump would have squashed the
     40px button into ~5px of content instead of moving it down.

     Both terms collapse to their originals off-device (--rr-safe-top is
     0px), so this is a no-op on desktop and on any page without
     viewport-fit=cover. */
  .mobile-topbar {
    display: flex;
    align-items: center;
    gap: 12px;
    padding: calc(10px + var(--rr-safe-top, 0px)) 14px 10px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    position: sticky;
    top: 0;
    z-index: 20;
    min-height: calc(52px + var(--rr-safe-top, 0px));
  }
  .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); }

/* ── In-app tier lock badge (B52) ─────────────────────────────────
 *
 * A small pill on a control the viewer's plan does not include: the sibling
 * of the rr-upgrade card, at the scale of a nav row. Same inviting tone —
 * --orange, never --red. A lock is a price, not an error.
 *
 * The host control is NEVER disabled (see tier-badge.js rule 1), so it must
 * still look and behave like itself; this adds a label, nothing more. No
 * opacity change on the parent for the same reason — a dimmed row reads as
 * unavailable, and the whole point is that it is tappable.
 *
 * Drawn by /static/tier-badge.js from [data-rr-lock="<capability key>"].
 * ────────────────────────────────────────────────────────────────── */
.rr-tier-badge {
  display: inline-flex;
  align-items: center;
  margin-left: auto;
  padding: 1px 6px;
  border: 1px solid var(--orange);
  border-radius: 999px;
  background: transparent;
  color: var(--orange);
  font-size: 10px;
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  flex: 0 0 auto;
  white-space: nowrap;
}

/* ── 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;
  }
}
