/* ============================================================
   NW Sim Racing design system
   Motorsport timing screen crossed with a premium lounge.
   One accent: NW neon green (#bceb21), the shop's brand
   accent. The green is light: anything sitting ON an accent
   background must use dark ink (--accent-ink), never white.
   ============================================================ */

@font-face {
  font-family: "Rajdhani";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/Rajdhani-500-48a8a78d.woff2") format("woff2");
}
@font-face {
  font-family: "Rajdhani";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/Rajdhani-600-85fa0919.woff2") format("woff2");
}
@font-face {
  font-family: "Rajdhani";
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url("/assets/Rajdhani-700-adb790a1.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("/assets/Barlow-400-47c07709.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/assets/Barlow-500-078cf4a1.woff2") format("woff2");
}
@font-face {
  font-family: "Barlow";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("/assets/Barlow-600-81a96cd2.woff2") format("woff2");
}

:root {
  /* surfaces */
  --bg: #08090b;
  --surface: #0f1114;
  --surface-2: #14171c;
  --edge: #20242b;
  --edge-strong: #303743;

  /* chrome ink */
  --ink: #e8ebef;
  --ink-muted: #9aa3af;
  --ink-faint: #8b949e;   /* secondary text remains readable on all dark surfaces */

  /* the accent: NW neon green (fastest lap, brand) */
  --accent: #bceb21;
  --accent-hover: #cdf54a;
  --accent-deep: #8fb51a;
  --accent-soft: rgba(188, 235, 33, 0.12);
  --accent-edge: rgba(188, 235, 33, 0.4);
  --accent-ink: #1a1d0f;   /* dark ink for text on accent backgrounds */

  /* timing semantics */
  --pb: #34d399;          /* personal best / live / valid */
  --pb-soft: rgba(52, 211, 153, 0.1);
  --danger: #f87171;
  --danger-soft: rgba(248, 113, 113, 0.1);
  --wet: #38bdf8;
  --warm: #e5b567;        /* dry-conditions chips, earned stamps */
  --warm-soft: rgba(229, 181, 103, 0.1);
  --silver: #c8d0da;
  --bronze: #c08a52;

  --font-display: "Rajdhani", "Barlow", system-ui, sans-serif;
  --font-text: "Barlow", system-ui, -apple-system, "Segoe UI", sans-serif;

  --radius: 0.625rem;
}

/* ---------- base ---------- */

body {
  font-family: var(--font-text);
  background-color: var(--bg);
  background-image:
    radial-gradient(1100px 420px at 50% -180px, rgba(150, 165, 190, 0.07), transparent 70%);
  background-repeat: no-repeat;
  color: var(--ink);
}

h1, h2, h3, .display {
  font-family: var(--font-display);
  letter-spacing: 0.015em;
}

::selection { background: var(--accent); color: var(--accent-ink); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: 4px;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

/* ---------- wordmark ---------- */

/* The NW ribbon mark (nw-mark.svg), sized to sit where the old text
   wordmark did. Width follows the SVG's 2.52:1 aspect ratio. */
.wordmark-logo {
  display: block;
  height: 1.5rem;
  width: auto;
}

.wordmark {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1.35rem;
  line-height: 1;
  letter-spacing: 0.22em;
  background: linear-gradient(180deg, #f5f7f9 0%, #c9cfd8 42%, #848d99 55%, #dfe4ea 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}
.wordmark-sub {
  font-size: 0.6rem;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* ---------- structure ---------- */

.eyebrow {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.3em;
  color: var(--ink-faint);
}

/* Three sector segments, like the bars on a timing tower.
   .is-fastest paints all three green (an "overall fastest" lap). */
.sector-strip {
  display: inline-block;
  width: 66px;
  height: 3px;
  border-radius: 2px;
  background: repeating-linear-gradient(
    90deg,
    var(--edge-strong) 0 18px,
    transparent 18px 24px
  );
}
.sector-strip.is-fastest {
  background: repeating-linear-gradient(
    90deg,
    var(--accent-deep) 0 18px,
    transparent 18px 24px
  );
}

.nw-card {
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
.nw-card-hover { transition: border-color 120ms ease, transform 120ms ease; }
.nw-card-hover:hover { border-color: var(--edge-strong); transform: translateY(-1px); }

.nw-inset {
  background: rgba(5, 6, 8, 0.55);
  border: 1px solid var(--edge);
  border-radius: calc(var(--radius) - 3px);
}

/* ---------- type helpers ---------- */

.laptime {
  font-family: var(--font-display);
  font-weight: 600;
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: 0.03em;
  white-space: nowrap;
}
.t-fastest { color: var(--accent); }
.t-pb { color: var(--pb); }

/* ---------- race result strip ---------- */
/* Where you finished, on a session recap. The position is in display type,
   the podium in the accent, and the strip carries a faint accent wash so it
   reads before the chips around it. */
.result-strip {
  padding: 0.7rem 1rem;
  border: 1px solid var(--accent-edge);
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--accent-soft), rgba(188, 235, 33, 0.03) 55%, transparent);
}
.result-position {
  font-family: var(--font-display);
  font-size: 2.1rem;
  line-height: 1;
  font-weight: 700;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink);
}
.result-position.is-podium { color: var(--accent); }

/* The lap selector follows the parts of a race weekend. One URL scopes
   chart and table together, including without JavaScript and on Back. */
.recap-laps { min-width: 0; scroll-margin-top: 90px; }
.recap-segments {
  display: flex;
  overflow-x: auto;
  border-bottom: 1px solid var(--edge-strong);
  gap: 4px;
  padding: 2px 2px 0;
}
.recap-segment {
  display: grid;
  flex: 1 0 max-content;
  gap: 3px;
  padding: 10px 12px 12px;
  border-bottom: 3px solid transparent;
  color: var(--ink-muted);
  text-decoration: none;
}
.recap-segment:hover { color: var(--ink); background: var(--surface); }
.recap-segment[aria-current="page"] {
  color: var(--ink);
  background: var(--accent-soft);
  border-bottom-color: var(--accent);
}
.recap-segment:focus-visible { outline-offset: -2px; }
.recap-segment-name { font-weight: 600; font-size: 0.95rem; }
.recap-segment-count { font-size: 0.8rem; }
.recap-segment-best { font-size: 0.75rem; font-variant-numeric: tabular-nums; }
.recap-segment[aria-current="page"] .recap-segment-name { color: var(--accent); }
.recap-segment-chart + .recap-segment-chart { border-top: 1px solid var(--edge); margin-top: 16px; padding-top: 16px; }
.recap-chart-label { margin-top: 12px; color: var(--ink-muted); font-weight: 600; font-size: 0.85rem; }
.result-field { font-size: 0.95rem; color: var(--ink-muted); white-space: nowrap; }
.result-start { font-size: 0.85rem; color: var(--ink-muted); white-space: nowrap; }
.result-stat { font-size: 0.85rem; color: var(--ink-muted); white-space: nowrap; }
.result-stat strong {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums lining-nums;
  color: var(--ink);
}

/* ---------- chips ---------- */

.chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid var(--edge-strong);
  border-radius: 999px;
  padding: 0.1rem 0.55rem;
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--ink-muted);
  white-space: nowrap;
}
.chip-dry     { border-color: rgba(229, 181, 103, 0.35); background: var(--warm-soft);   color: var(--warm); }
.chip-wet     { border-color: rgba(56, 189, 248, 0.35);  background: rgba(56,189,248,.08); color: var(--wet); }
.chip-valid   { border-color: rgba(52, 211, 153, 0.35);  background: var(--pb-soft);     color: var(--pb); }
.chip-invalid { border-color: rgba(248, 113, 113, 0.35); background: var(--danger-soft); color: var(--danger); }
.chip-live    { border-color: rgba(52, 211, 153, 0.35);  background: var(--pb-soft);     color: var(--pb); }
.chip-sched   { border-color: rgba(56, 189, 248, 0.3);   background: rgba(56,189,248,.07); color: var(--wet); }
.chip-accent  { border-color: var(--accent-edge);        background: var(--accent-soft); color: var(--accent); }
.chip-quiet   { background: var(--surface-2); }

/* ---------- buttons & fields ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  font-family: var(--font-text);
  font-weight: 600;
  font-size: 0.875rem;
  border-radius: calc(var(--radius) - 2px);
  padding: 0.55rem 1rem;
  border: 1px solid transparent;
  cursor: pointer;
  transition: background-color 120ms ease, border-color 120ms ease;
  /* A button label never wraps ("Sign out" was breaking in two at squeezed
     header widths); the layout wraps around whole buttons instead. */
  white-space: nowrap;
}
.btn-sm { font-size: 0.875rem; min-height: 2.25rem; padding: 0.4rem 0.75rem; }
.btn-primary {
  background: var(--accent);
  color: var(--accent-ink);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.28);
}
.btn-primary:hover { background: var(--accent-hover); }
.btn-danger { background: rgba(190, 55, 55, 0.85); color: #fff; }
.btn-danger:hover { background: rgba(213, 70, 70, 0.95); }
.btn-ghost {
  background: var(--surface-2);
  border-color: var(--edge-strong);
  color: var(--ink);
}
.btn-ghost:hover { border-color: var(--ink-faint); }
.btn-quiet { background: transparent; color: var(--ink-muted); }
.btn-quiet:hover { color: var(--ink); }

/* "Continue with Google". Deliberately NOT themed to match the rest of the
   site: Google's branding guidelines fix the colours, the border, and the
   logo, and a customer recognising the button at a glance is the entire point
   of it on a walk-up sign-in page. */
.btn-google {
  width: 100%;
  background: #fff;
  color: #1f1f1f;
  border-color: #747775;
  font-size: 0.9375rem;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
}
.btn-google:hover { background: #f2f2f2; }
.btn-google svg { width: 20px; height: 20px; flex-shrink: 0; }

/* The "or" rule between the Google button and the email form. */
.or-divider {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  color: var(--ink-faint);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.or-divider::before,
.or-divider::after {
  content: "";
  flex: 1 1 auto;
  height: 1px;
  background: var(--edge);
}

.field {
  background: var(--surface-2);
  border: 1px solid var(--edge-strong);
  border-radius: calc(var(--radius) - 2px);
  color: var(--ink);
  padding: 0.5rem 0.7rem;
  font-size: 0.875rem;
  width: 100%;
}
.field::placeholder { color: var(--ink-faint); }
.field:focus { border-color: var(--accent-deep); }
.field:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* The hard cap (not 100%) matters: a percentage is circular when a flex
   ancestor derives its own width from the field's intrinsic size, which
   let long <option> text push phone layouts past the viewport. */
.field-sm { padding: 0.4rem 0.6rem; min-height: 2.25rem; font-size: 0.875rem; width: auto; max-width: min(100%, 20rem); }

/* ---------- timing tables ---------- */

.timing-table {
  /* position: relative keeps absolutely-positioned descendants (sr-only
     column headers) inside the scroll wrapper instead of widening the page. */
  position: relative;
  overflow-x: auto;
  background: var(--surface);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.04);
}
.timing-table table { width: 100%; font-size: 0.875rem; border-collapse: collapse; }
.timing-table thead th {
  text-align: left;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.8125rem;
  text-transform: none;
  letter-spacing: 0.04em;
  color: var(--ink-faint);
  background: rgba(5, 6, 8, 0.5);
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--edge);
}
.timing-table tbody td {
  padding: 0.7rem 1rem;
  border-bottom: 1px solid rgba(32, 36, 43, 0.6);
}
.timing-table tbody tr:last-child td { border-bottom: 0; }
.timing-table tbody tr { transition: background-color 100ms ease; }
.timing-table tbody tr:hover { background: rgba(255, 255, 255, 0.02); }

/* Where one sub-session of a weekend ends and the next begins: iRacing
   restarts the lap counter, so the table says which run lap 1 belongs to. */
.timing-table tbody tr.lap-segment th {
  padding: 0.5rem 1rem;
  background: rgba(5, 6, 8, 0.35);
  border-bottom: 1px solid var(--edge);
  color: var(--ink-muted);
}
.timing-table tbody tr.lap-segment:hover { background: transparent; }

/* P1: the green rail of the overall fastest lap */
.timing-table tr.row-p1 { background: var(--accent-soft); }
.timing-table tr.row-p1:hover { background: var(--accent-soft); }
.timing-table tr.row-p1 td:first-child { box-shadow: inset 3px 0 0 var(--accent-deep); }

/* Rows that open their own record (see row_link_controller.js). The whole
   row is the target, so it lifts a little harder than a plain hover, and the
   links inside it keep their own colour: the row and its links are two
   different destinations, not one. */
.row-link { cursor: pointer; }
.timing-table tbody tr.row-link:hover { background: rgba(255, 255, 255, 0.05); }
.timing-table tbody tr.row-p1.row-link:hover { background: var(--accent-soft); }
.row-link:focus-visible { outline: 2px solid var(--accent-deep); outline-offset: -2px; }
@media (prefers-reduced-motion: reduce) {
  .timing-table tbody tr { transition: none; }
}

/* Stacked variant: wide many-column tables (visit history) collapse into
   labeled cards on phones. Labels come from each td's data-th attribute. */
@media (max-width: 640px) {
  .timing-stack table, .timing-stack tbody, .timing-stack tr, .timing-stack td { display: block; width: 100%; }
  .timing-stack thead { display: none; }
  .timing-stack tbody tr { padding: 0.8rem 1rem; border-bottom: 1px solid var(--edge); }
  .timing-stack tbody tr:last-child { border-bottom: 0; }
  .timing-stack tbody td { border-bottom: 0; padding: 0.15rem 0; }
  .timing-stack .chip { max-width: 100%; white-space: normal; height: auto; }
  .timing-stack td[data-th]::before {
    content: attr(data-th);
    display: inline-block;
    min-width: 5.75rem;
    margin-right: 0.5rem;
    font-family: var(--font-display);
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-faint);
  }
}

.rank {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: 1rem;
  font-variant-numeric: tabular-nums;
  color: var(--ink-muted);
}
.rank-1 { color: var(--accent); }
.rank-2 { color: var(--silver); }
.rank-3 { color: var(--bronze); }

/* ---------- status ---------- */

.dot { width: 8px; height: 8px; border-radius: 999px; display: inline-block; }
.dot-on { background: var(--pb); box-shadow: 0 0 6px rgba(52, 211, 153, 0.6); }
.dot-off { background: var(--edge-strong); }

@keyframes nw-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.45; }
}
.dot-live { animation: nw-pulse 2.4s ease-in-out infinite; }

/* ---------- flashes ---------- */

.flash-notice {
  border: 1px solid rgba(52, 211, 153, 0.35);
  background: var(--pb-soft);
  color: var(--pb);
  border-radius: var(--radius);
  padding: 0.55rem 1rem;
  font-size: 0.875rem;
}
.flash-alert {
  border: 1px solid rgba(248, 113, 113, 0.35);
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: var(--radius);
  padding: 0.55rem 1rem;
  font-size: 0.875rem;
}

/* Flash messages leave on their own (flash_controller). Room is made for the
   close button, and the fade out is short enough not to be in the way. */
.flash-toast {
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  transition: opacity 180ms ease, transform 180ms ease;
}
.flash-toast > span { flex: 1 1 auto; }
.flash-toast.is-leaving {
  opacity: 0;
  transform: translateY(-4px);
}
.flash-close {
  flex: 0 0 auto;
  margin: -0.15rem -0.35rem 0 0;
  padding: 0 0.35rem;
  border: 0;
  background: transparent;
  color: inherit;
  font-size: 1.1rem;
  line-height: 1.2;
  opacity: 0.6;
  cursor: pointer;
}
.flash-close:hover { opacity: 1; }
.flash-close:focus-visible { outline: 2px solid currentColor; outline-offset: 2px; }

/* ---------- nav ---------- */

.nav-link {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--ink-muted);
  padding: 0.25rem 0.1rem;
  border-bottom: 2px solid transparent;
  transition: color 120ms ease;
}
.nav-link:hover { color: var(--ink); }
.nav-link.is-active { color: var(--ink); border-bottom-color: var(--accent-deep); }

/* Second-level tabs: the pages inside one nav group. Quieter than the bar
   above them, and they scroll sideways rather than wrapping into a block, so
   a nine-tab group still reads as one line on a counter monitor. */
.nav-tabs {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  overflow-x: auto;
  scrollbar-width: none;
  margin-inline: -0.35rem;
  padding-inline: 0.35rem;
}
.nav-tabs::-webkit-scrollbar { display: none; }

.nav-tab {
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid transparent;
  padding: 0.25rem 0.7rem;
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--ink-muted);
  transition: color 120ms ease, border-color 120ms ease, background-color 120ms ease;
}
.nav-tab:hover { color: var(--ink); border-color: var(--edge-strong); }
.nav-tab.is-active {
  color: var(--accent);
  border-color: var(--accent-edge);
  background: var(--accent-soft);
}

/* The hamburger panel stacks instead: a group's tabs are indented under it,
   against a hairline, so the level is obvious without a second strip. */
.nav-substack {
  display: flex;
  flex-direction: column;
  margin-left: 0.2rem;
  padding-left: 0.85rem;
  border-left: 1px solid var(--edge);
}
.nav-tab-stacked {
  border-radius: calc(var(--radius) - 3px);
  padding-inline: 0.55rem;
}

/* Thumb-sized rows in the phone menus: 44px is the smallest target a finger
   hits reliably, and every menu row here is a whole line to itself. */
.nav-link-row,
.nav-tab-stacked {
  display: flex;
  align-items: center;
  min-height: 44px;
  width: 100%;
  border-bottom: 0;
}
.nav-link-row.is-active { color: var(--accent); }

.nav-menu-heading {
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  color: var(--ink-faint);
  padding: 0.6rem 0 0.2rem;
}

/* ---------- account menu ---------- */

.account-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border-radius: 999px;
  border: 1px solid transparent;
  padding: 0.2rem 0.6rem 0.2rem 0.2rem;
  font-size: 0.875rem;
  color: var(--ink-muted);
  cursor: pointer;
  transition: color 120ms ease, border-color 120ms ease;
}
.account-trigger:hover { color: var(--ink); border-color: var(--edge-strong); }
.account-trigger[aria-expanded="true"] { color: var(--ink); border-color: var(--edge-strong); }

.account-avatar {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  height: 1.75rem;
  width: 1.75rem;
  flex: 0 0 auto;
  border-radius: 999px;
  border: 1px solid var(--edge-strong);
  background: var(--surface-2);
  font-family: var(--font-display);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
}

/* The panel sets its own display, which beats the layered Tailwind
   "hidden" utility, so closed is the hidden attribute, and it must win here. */
.account-panel[hidden] {
  display: none;
}

.account-panel {
  position: absolute;
  right: 0;
  top: calc(100% + 0.5rem);
  z-index: 40;
  min-width: 12rem;
  display: flex;
  flex-direction: column;
  padding: 0.35rem;
  border: 1px solid var(--edge-strong);
  border-radius: var(--radius);
  background: var(--surface);
  box-shadow: 0 18px 40px rgba(0, 0, 0, 0.45);
}

.account-item {
  display: block;
  width: 100%;
  border-radius: calc(var(--radius) - 4px);
  padding: 0.45rem 0.65rem;
  font-size: 0.875rem;
  text-align: left;
  color: var(--ink-muted);
  cursor: pointer;
}
.account-item:hover { background: var(--surface-2); color: var(--ink); }
.account-item.is-active { color: var(--accent); }
.account-item-button {
  border: 0;
  border-top: 1px solid var(--edge);
  margin-top: 0.35rem;
  border-radius: 0;
  background: transparent;
  font: inherit;
}

/* ---------- passport stamps ---------- */

.stamp-earned {
  border-color: rgba(229, 181, 103, 0.45) !important;
  background:
    radial-gradient(120px 60px at 85% -10%, rgba(229, 181, 103, 0.12), transparent 70%),
    var(--surface) !important;
}
.stamp-icon-unearned { filter: grayscale(1); opacity: 0.5; }

/* ---------- links ---------- */

.link-accent { color: var(--accent); }
.link-accent:hover { color: var(--accent-hover); }
.link-quiet { color: var(--ink-muted); }
.link-quiet:hover { color: var(--ink); }

/* ---------- class tier chips (Bronze / Silver / Gold) ---------- */

.chip-bronze { border-color: rgba(192, 138, 82, 0.45); background: rgba(192, 138, 82, 0.12); color: var(--bronze); }
.chip-silver { border-color: rgba(200, 208, 218, 0.40); background: rgba(200, 208, 218, 0.09); color: var(--silver); }
.chip-gold   { border-color: rgba(240, 197, 122, 0.55); background: rgba(229, 181, 103, 0.16); color: #f0c57a; }

/* ---------- catalog artwork (cars and tracks) ---------- */

/* A framed photo: the img fills it (CatalogHelper :fill), a gradient darkens
   the foot so chips and titles laid over it stay readable. */
.catalog-photo {
  position: relative;
  overflow: hidden;
  background: var(--surface-2);
  border: 1px solid var(--edge);
  border-radius: var(--radius);
}
.catalog-photo > img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.catalog-photo-shade {
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(8, 9, 11, 0.05) 0%, rgba(8, 9, 11, 0.15) 45%, rgba(8, 9, 11, 0.85) 100%);
}
.catalog-photo-chips {
  position: absolute;
  left: 0.75rem;
  right: 0.75rem;
  bottom: 0.7rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem;
}
.catalog-photo-chips .chip {
  border-color: rgba(255, 255, 255, 0.16);
  background: rgba(8, 9, 11, 0.62);
  color: var(--ink);
  backdrop-filter: blur(4px);
}
/* A strip is a short photo across the top of a card (competitions, combo
   records): same frame, square bottom corners, no border of its own. */
.catalog-photo-strip {
  aspect-ratio: 16 / 5;
  border: 0;
  border-bottom: 1px solid var(--edge);
  border-radius: var(--radius) var(--radius) 0 0;
}
/* The 16:9 top of a browse card: same frame, square bottom corners. */
.catalog-photo-top {
  aspect-ratio: 16 / 9;
  border: 0;
  border-bottom: 1px solid var(--edge);
  border-radius: var(--radius) var(--radius) 0 0;
}
/* Text laid over a strip's shaded foot. */
.catalog-photo-caption {
  position: absolute;
  left: 1rem;
  right: 1rem;
  bottom: 0.7rem;
  color: var(--ink);
  text-shadow: 0 1px 2px rgba(8, 9, 11, 0.8);
}

/* The combo header: photo beside the names from md up, stacked on phones. */
.combo-header { display: grid; gap: 1.1rem 1.5rem; align-items: start; }
.combo-header-photo { aspect-ratio: 16 / 9; }
@media (min-width: 768px) {
  .combo-header { grid-template-columns: minmax(0, 20rem) minmax(0, 1fr); }
}
.combo-header-car { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.9rem; }

/* ---------- ranked tables on phones ---------- */

/* .timing-board on a .timing-table wrapper: above 640px the table is a
   table. Under it every row becomes a stacked card laid out by the cells'
   data-cell slot: rank and member (lead, head) on the first line, time and
   gap on the second, then the chips (meta) with the actions right-aligned.
   The row's ::before and ::after pseudo-elements are the two line breaks.
   Every ranked board and record table shares this one arrangement. */
@media (max-width: 640px) {
  .timing-board table, .timing-board tbody { display: block; width: 100%; }
  .timing-board thead { display: none; }
  .timing-board tbody tr {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    column-gap: 0.6rem;
    row-gap: 0.4rem;
    padding: 0.8rem 1rem;
    border-bottom: 1px solid var(--edge);
  }
  .timing-board tbody tr:last-child { border-bottom: 0; }
  .timing-board tbody td { display: block; padding: 0; border-bottom: 0; min-width: 0; }
  .timing-board tbody tr::before, .timing-board tbody tr::after { content: ""; display: block; flex-basis: 100%; height: 0; }
  .timing-board td[data-cell="lead"]    { order: 10; }
  .timing-board td[data-cell="head"]    { order: 20; flex: 1 1 0; }
  .timing-board tbody tr::before        { order: 25; }
  .timing-board td[data-cell="time"]    { order: 30; flex: 1 1 auto; }
  .timing-board td[data-cell="gap"]     { order: 40; }
  .timing-board tbody tr::after         { order: 45; }
  .timing-board td[data-cell="meta"]    { order: 50; }
  .timing-board td[data-cell="meta"]:empty { display: none; }
  .timing-board td[data-cell="actions"] { order: 60; margin-left: auto; text-align: right; }
  .timing-board td[data-cell="meta"] .chip { max-width: 100%; white-space: normal; height: auto; }
  .timing-board td[data-th]::before {
    content: attr(data-th);
    display: inline-block;
    margin-right: 0.35rem;
    font-family: var(--font-display);
    font-size: 0.65rem;
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ink-faint);
  }
  /* The P1 rail moves from the first cell to the whole card. */
  .timing-board tr.row-p1 { box-shadow: inset 3px 0 0 var(--accent-deep); }
  .timing-board tr.row-p1 td:first-child { box-shadow: none; }
  /* Row actions (Telemetry, Compare, Card) become 32px tap targets. */
  .timing-board td[data-cell="actions"] a,
  .timing-board td[data-cell="actions"] > span {
    display: inline-flex;
    align-items: center;
    min-height: 32px;
    padding: 0 0.6rem;
    margin-left: 0.25rem;
    border: 1px solid var(--edge-strong);
    border-radius: calc(var(--radius) - 3px);
    background: var(--surface-2);
  }
}

/* ---------- chart hover readouts ---------- */

/* The two profile/passport charts (shared/_improvement_chart,
   shared/_activity_chart) are server-rendered SVG; chart_hover_controller.js
   turns each point and bar into a hoverable, tappable, tabbable mark and
   positions .chart-tip inside .chart-plot. The tooltip is HTML, not an SVG
   <title>: it appears instantly, in the theme's own type, for pointer,
   touch, and keyboard alike.

   Nothing here animates beyond a 90ms highlight, and the global
   prefers-reduced-motion rule above already flattens that. */

.chart-plot { position: relative; }

.chart-tip {
  position: absolute;
  z-index: 20;
  pointer-events: none;
  white-space: nowrap;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--edge-strong);
  border-radius: calc(var(--radius) - 3px);
  background: var(--surface-2);
  box-shadow: 0 8px 22px rgba(0, 0, 0, 0.5);
  line-height: 1.35;
}
.chart-tip[hidden] { display: none; }

.chart-tip-title {
  display: block;
  font-family: var(--font-display);
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.chart-tip-value { display: block; font-size: 0.95rem; color: var(--ink); }
.chart-tip-note  { display: block; font-size: 0.72rem; color: var(--ink-muted); }
.chart-tip-note[hidden] { display: none; }

/* A mark is an <a> (or a <g> where there is nothing to link to) wrapped
   round the visible dot or bar, and nothing else: the hit targets are gone.
   chart_hover_controller.js lights whichever mark the pointer is NEAREST, so
   a 6px dot and a 1px empty week are no harder to reach than a tall bar, and
   the cursor says so anywhere over the plot. */
.chart-mark { cursor: pointer; }
.chart-plot svg { cursor: pointer; }

.chart-dot, .chart-bar { transition: stroke-width 90ms ease, fill 90ms ease; }

/* NO native focus ring on a mark. The plots are drawn with
   preserveAspectRatio="none", so the browser's outline gets stretched with
   them and a focused bar came out as a tall white pill down the whole card.
   Focus is shown the way hover is: chart_hover_controller.js adds
   .chart-mark-on, which brightens the mark and raises its tooltip. */
.chart-mark:focus, .chart-mark:focus-visible { outline: none; }

/* Highlight keeps the mark's own colour: the fastest session stays green.
   Hovered, focused, and "this is the week the table is filtered to" all read
   the same, which is the point: it is one selection. */
.chart-mark-on .chart-dot, .chart-mark-picked .chart-dot { stroke-width: 10; }
.chart-mark-on .chart-bar, .chart-mark-picked .chart-bar { fill: var(--accent); }
.chart-mark-on .chart-bar-empty, .chart-mark-picked .chart-bar-empty { fill: var(--edge-strong); }

/* ---------- chart axes ---------- */

/* Gridlines live in the SVG, every label outside it. Text inside a plot that
   stretches to the container width would smear; these sit in their own column
   and row and stay legible at any size. */
.chart-grid { stroke: var(--edge); stroke-width: 1; }
.chart-baseline { stroke: var(--edge-strong); stroke-width: 1; }
.chart-best { stroke: var(--pb); stroke-width: 1; opacity: 0.55; }

.chart-axis-tick,
.chart-x-tick {
  position: absolute;
  font-variant-numeric: tabular-nums;
  font-size: 0.65rem;
  line-height: 1;
  color: var(--ink-faint);
  white-space: nowrap;
}
.chart-axis-tick { right: 0; transform: translateY(-50%); }
.chart-x-tick { top: 0; transform: translateX(-50%); }
.chart-x-tick-first { left: 0 !important; transform: none; }

.chart-x-axis {
  font-variant-numeric: tabular-nums;
  font-size: 0.65rem;
  color: var(--ink-faint);
}

/* The time to beat, flagged where its dashed line crosses the plot. The card
   background behind it is what keeps the dashed line from striking the word
   through when the best lap sits right on a gridline. */
.chart-best-flag {
  position: absolute;
  left: 0;
  z-index: 1;
  padding: 0 0.25rem 0 0;
  background: var(--surface);
  transform: translateY(-50%);
  font-family: var(--font-display);
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--pb);
  pointer-events: none;
}

/* ---------- the one date filter ---------- */
/* shared/_date_range_filter.html.erb. One row: two native calendar inputs,
   Apply, and the quick chips. It lives inside the page's own filter form, so
   it wraps with everything else on that row rather than claiming a line. */

.date-range {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 0.5rem;
}
.date-range-label {
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--ink-faint);
}
.date-range-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
}

/* A chip is a link (it works with JavaScript off and it is shareable) that
   reads as a toggle. Sized for a thumb on the counter iPad, not for a mouse:
   two rem of touch target on a control staff use one-handed all evening. */
.date-chip {
  display: inline-flex;
  align-items: center;
  min-height: 2rem;
  padding: 0.25rem 0.6rem;
  border: 1px solid var(--edge-strong);
  border-radius: 999px;
  background: var(--surface-2);
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.4;
  white-space: nowrap;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 120ms ease, color 120ms ease, background-color 120ms ease;
}
.date-chip:hover { border-color: var(--ink-faint); color: var(--ink); }
.date-chip.is-on {
  border-color: var(--accent-edge);
  background: var(--accent-soft);
  color: var(--accent);
}

/* The calendar glyph Safari and Chrome paint inside type="date" is black on
   a black field otherwise, which reads as an empty box on the shop's dark
   theme until you tap it. */
.date-range input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(0.72);
  cursor: pointer;
}

/* Useful actions need room to tap; dense timing figures stay compact. */
a.chip, button.chip { min-height: 2rem; }
.timing-table td[data-cell="actions"] > a:not(.chip) {
  display: inline-flex;
  align-items: center;
  min-height: 2.25rem;
  font-size: .875rem;
}
@media (pointer: coarse) {
  .btn, .btn-sm, .field, .field-sm, a.chip, button.chip,
  .timing-table td[data-cell="actions"] > a:not(.chip) { min-height: 2.75rem; }
  .field, .field-sm { font-size: 1rem; }
}
