/* ═══════════════════════════════════════════════════════════
   EntryLane Admin — option7 design system
   Fonts: Archivo Black · Archivo · JetBrains Mono
   Themes: dark (default) / light — toggled via html[data-theme]
═══════════════════════════════════════════════════════════ */

/* ── FONT TOKENS ── single source for the kinetic type stack, theme-independent.
   Names mirror the public kinetic.css so the vocabulary is shared cross-surface. */
:root {
  --font-label:    'Archivo', sans-serif;                    /* labels + headings */
  /* One small-uppercase label voice for the whole admin (DESIGN.md §3, Label
     tier: Archivo 700, 0.84rem, 0.06em, UPPERCASE). Field labels, tile headers
     and settings row titles had each drifted to their own size and tracking
     — 0.62rem/0.12em, 0.66rem/0.18em, 0.95rem/0.05em — so the same kind of
     label read three different ways depending on the screen, and a 9.92px
     "FIRST NAME" sat over a 44px input looking unrelated to it. */
  --label-size:     0.84rem;
  --label-tracking: 0.06em;
  --font-body:     'Archivo', sans-serif;            /* body copy */
  --font-mono:     'JetBrains Mono', ui-monospace, monospace; /* IDs, data, numerals */
  --font-wordmark: 'Bebas Neue', 'Archivo Black', 'Archivo', sans-serif;      /* the EntryLane wordmark */
  --font-display:  'Archivo Black', 'Archivo', sans-serif;      /* page + section headings */
}

/* ── THEME TOKENS ── */
html[data-theme="dark"] {
  --primary:         #58A0D8;   /* alias kinetic's accent name → admin's --accent */
  --bg-base:         #0D0D12;
  --bg-card:         #13131A;
  --bg-card-alt:     #1A1A24;
  --bg-inset:        rgba(255,255,255,0.03);
  --bg-nav:          #09090E;
  --border:          rgba(88, 160, 216,0.14);
  --border-dim:      rgba(255,255,255,0.05);
  --border-mid:      rgba(88, 160, 216,0.28);
  --border-strong:   rgba(88, 160, 216,0.55);
  --accent:          #58A0D8;
  --accent-hover:    #3F86C2;
  --accent-dim:      rgba(88, 160, 216,0.09);
  --accent-glow:     rgba(88, 160, 216,0.25);  /* DESIGN.md §4; matches lib/accentTokens.js */
  --on-accent:       #0D0D12;   /* text on accent fills; recomputed per picked accent */
  --text-primary:    #F0F0F7;
  --text-secondary:  #8080A0;
  --text-muted:      #85859E;
  --error:           #ef4444;
  --error-bg:        rgba(239,68,68,0.10);
  --error-border:    rgba(239,68,68,0.32);
  --success:         #10B981;
  --success-dim:     rgba(16,185,129,0.10);
  --success-border:  rgba(16,185,129,0.22);
  /* Text/icon-safe success on carbon (mirrors admin-kinetic.css, defined here
     too so var(--success-ink) never falls back to "invalid → inherited"). */
  --success-ink:     #34D399;
  --warning:         #58A0D8;
  --shadow:          0 2px 12px rgba(0,0,0,0.5);
  --shadow-lg:       0 8px 32px rgba(0,0,0,0.6);
  --modal-bg:        #0E0E1A;
  --input-bg:        rgba(0,0,0,0.4);
  --ticker-bg:       #09090E;
  --ticker-div:      rgba(88, 160, 216,0.1);
  --row-alt:         rgba(88, 160, 216,0.025);
  --row-hover:       rgba(88, 160, 216,0.04);
  --toggle-off:      rgba(255,255,255,0.08);
  --toggle-border:   rgba(88, 160, 216,0.2);
}

html[data-theme="light"] {
  --primary:         #58A0D8;   /* alias kinetic's accent name → admin's --accent */
  --bg-base:         #EDEDF2;
  --bg-card:         #FAFAFA;
  --bg-card-alt:     #F2F2F7;
  --bg-inset:        rgba(0,0,0,0.025);
  --bg-nav:          #0D1117;
  --border:          #DDDDE8;
  --border-dim:      #EEEEF4;
  --border-mid:      #C8C8D8;
  --border-strong:   rgba(88, 160, 216,0.55);
  --accent:          #58A0D8;
  --accent-hover:    #3F86C2;
  --accent-dim:      rgba(88, 160, 216,0.07);
  --accent-glow:     rgba(88, 160, 216,0.25);  /* DESIGN.md §4; matches lib/accentTokens.js */
  --on-accent:       #0D0D12;   /* text on accent fills; recomputed per picked accent */
  --text-primary:    #0F0F1A;
  --text-secondary:  #60607A;
  --text-muted:      #67677A;
  --error:           #ba1a1a;
  --error-bg:        rgba(186,26,26,0.08);
  --error-border:    rgba(186,26,26,0.3);
  --success:         #10B981;
  --success-dim:     rgba(16,185,129,0.08);
  --success-border:  rgba(16,185,129,0.22);
  /* Text/icon-safe success. #10B981 is ~2.5:1 on white; this clears ~5.5:1. */
  --success-ink:     #047857;
  --warning:         #3F86C2;
  --shadow:          0 1px 4px rgba(0,0,0,0.07), 0 2px 12px rgba(0,0,0,0.04);
  --shadow-lg:       0 4px 24px rgba(0,0,0,0.12);
  --modal-bg:        #FFFFFF;
  --input-bg:        #FFFFFF;
  --ticker-bg:       #FAFAFA;
  --ticker-div:      #DDDDE8;
  --row-alt:         rgba(0,0,0,0.018);
  --row-hover:       rgba(88, 160, 216,0.04);
  --toggle-off:      #DDDDE8;
  --toggle-border:   #C8C8D8;
}

/* ── RESET ── */
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

/* Pin headings to Archivo Black so h1/h2/h3 don't inherit the
   browser's bold default. Mirrors public-kinetic.css's rule so the
   apex picker, admin dashboard, judge, survey, and results pages all
   read with the same headline weight. Component-level rules can still
   override (.foo-title { font-weight: 700 } wins on specificity). */
h1, h2 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: 0.01em;
}
h3, h4 {
  font-family: var(--font-label);
  font-weight: 700;
}

body {
  font-family: var(--font-body);
  background: var(--bg-base);
  color: var(--text-primary);
  min-height: 100vh;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  transition: background 0.2s, color 0.2s;
  /* Flex column lets the footer hug the bottom of the viewport even
     when the dashboard content is shorter than the screen — kills the
     white gap that used to show below the footer on sparse pages. */
  display: flex;
  flex-direction: column;
}
body > .kfooter { margin-top: auto; }

.hidden { display: none !important; }
/* Tier gating (applyTierGate) — its own class so it can never collide with
   .hidden's UI-state uses (tab panels, modals). */
.tier-hidden { display: none !important; }

/* Destructive-but-secondary actions (Reset voter sessions): red outline,
   transparent fill — clearly dangerous, never competing with the primary. */
.btn-danger-quiet {
  background: transparent;
  color: var(--error);
  border: 1px solid var(--error-border);
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.btn-danger-quiet:hover { background: var(--error-bg); border-color: var(--error); }
/* Clear-all-votes at the foot of a dashboard tile (People's Choice, Best of
   Show votes) — quieter than the settings-row size so it reads as tile
   housekeeping, not a primary action. */
.tile-clear-votes { margin-top: 0.75rem; font-size: 0.8rem; padding: 0.45rem 0.85rem; }
/* Delete All at the foot of the Manage Classes lists — same quiet voice. */
.classes-delete-all { margin-top: 0.75rem; font-size: 0.8rem; padding: 0.45rem 0.85rem; align-self: flex-start; }

/* Import lives in the setup/classes header now — align it with the exit. */
.modal-header .setup-header-action { margin-left: auto; margin-right: 0.6rem; }
/* Classes modal header: Export / Import Classes + Import Registrations sit as
   one group pushed against the close glyph; they wrap under the title on
   narrow screens instead of squeezing it. */
.modal-header .setup-header-actions {
  display: flex; flex-wrap: wrap; gap: 0.4rem; margin-left: auto; margin-right: 0.6rem;
}
/* Once the header can't fit the title + all three buttons on one line, the
   ragged mid-title wrap reads as broken. Below 900px the action group takes
   its own full-width row under the title, buttons stretching to equal fill. */
@media (max-width: 900px) {
  #classes-modal .modal-header { flex-wrap: wrap; }
  #classes-modal .modal-header .setup-header-actions {
    flex-basis: 100%;
    order: 5; /* after the title and the close/Back control */
    margin-left: 0;
    margin-right: 0;
  }
  #classes-modal .setup-header-actions .btn-add { flex: 1 1 auto; min-height: 44px; }
  /* /admin/setup on a phone: the section content is the destination — the
     masthead, the four import/export buttons, and the tab strip compress so
     a tapped section's header sits at the top of the screen with minimal
     dead air (setClassesModalTab also scrolls the section up under the nav). */
  body.setup-page #classes-modal .modal-header { margin-bottom: 0.5rem; gap: 0.45rem 0.6rem; }
  body.setup-page #classes-modal .setup-header-actions { gap: 0.35rem; }
  body.setup-page #classes-modal .setup-header-actions .btn-add {
    min-height: 40px; padding: 0.35rem 0.5rem; font-size: 0.82rem;
  }
  body.setup-page #classes-modal .modal-tabs { margin: 0.3rem 0 0.75rem; }
  body.setup-page .settings-section-head { margin-top: 1.1rem; padding-top: 0.8rem; }
  body.setup-page .settings-section-head--flush { margin-top: 0; padding-top: 0; }
}
/* Import Registrations: opt-in to keep the file's entry numbers. */
.import-option {
  display: flex; align-items: flex-start; gap: 0.55rem;
  margin: 0.4rem 0 0.9rem; font-size: 0.88rem; line-height: 1.45;
  color: var(--text-secondary); cursor: pointer;
}
.import-option input { margin-top: 0.2em; flex-shrink: 0; }

/* /admin/setup: the modal close glyph becomes a labeled exit. */
body.setup-page .setup-back-btn {
  width: auto;
  height: auto;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  line-height: 1.2;
  padding: 0.5rem 0.95rem;
  white-space: nowrap;
}

/* Wordmark logo (ENTRY + LANE spans) keeps Archivo Black — the rest of
   the app uses readable sans (Archivo) for legibility. */
.kw-entry, .kw-lane { font-family: var(--font-wordmark); }
.kw-lane { color: #58A0D8 !important; } /* brand blue, never the show accent */
.kw-lane::after { content: "."; }   /* ENTRYLANE. — blue period, inherits the LANE color */

/* ═══════════════════════════════════════════════════════════
   LOGIN
═══════════════════════════════════════════════════════════ */
.login-screen {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(rgba(88, 160, 216,0.025) 1px, transparent 1px),
    linear-gradient(90deg, rgba(88, 160, 216,0.025) 1px, transparent 1px),
    var(--bg-base);
  background-size: 48px 48px, 48px 48px, auto;
}

.login-card {
  background: var(--bg-card);
  border: 1px solid var(--border-mid);
  border-top: 3px solid var(--accent);
  padding: 2.5rem 2rem;
  width: 100%;
  max-width: 380px;
  box-shadow: var(--shadow-lg);
}

.login-header { text-align: center; margin-bottom: 2rem; }

.login-title {
  font-family: var(--font-label);
  font-size: 2rem;
  letter-spacing: 0.2em;
  color: var(--accent-ink, var(--accent));
  line-height: 1;
}

.login-sub {
  font-family: var(--font-label);
  font-size: 0.65rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 0.4rem;
}

.login-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  border-top: 2px solid var(--error);
  padding: 0.7rem 0.9rem;
  color: var(--error);
  font-family: var(--font-body);
  font-size: 0.85rem;
  margin-bottom: 1rem;
}

.login-field { margin-bottom: 1rem; }

.login-field label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin-bottom: 0.4rem;
}

.login-field input {
  width: 100%;
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  padding: 0.75rem 1rem;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.95rem;
  transition: border-color 0.15s;
}

.login-field input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
}

.login-actions {
  display: flex;
  gap: 0.75rem;
  margin-top: 0.5rem;
}

.btn-login {
  flex: 1 1 60%;
  /* Platform button scheme: Archivo sentence case, not label caps. */
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--accent);
  color: var(--on-accent, #0D0D12);
  border: none;
  padding: 0.9rem;
  cursor: pointer;
  transition: background 0.15s;
}
.btn-login:hover { background: var(--accent-hover); }
.btn-login:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-login-secondary {
  flex: 1 1 40%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: transparent;
  color: var(--accent-ink, var(--accent));
  border: 1px solid var(--border-mid);
  padding: 0.9rem 0.5rem;
  cursor: pointer;
  text-decoration: none;
  transition: border-color 0.15s, background 0.15s;
}
.btn-login-secondary:hover { border-color: var(--accent); background: var(--accent-dim); }

@media (max-width: 480px) {
  .login-actions { flex-direction: column-reverse; }
  .btn-login, .btn-login-secondary { flex: 1 1 auto; width: 100%; }
}

/* ═══════════════════════════════════════════════════════════
   NAV
═══════════════════════════════════════════════════════════ */
.admin-nav {
  position: sticky;
  top: 0;
  z-index: var(--z-nav);
  background: var(--bg-nav);
  border-bottom: 1px solid rgba(88, 160, 216,0.18);
  /* min-height (not fixed height) — the show name lives on a second line
     under the wordmark, and the bar has to grow with browser text scaling
     instead of letting content spill out of it. */
  min-height: 64px;
  padding: 0.6rem clamp(1.1rem, 4vw, 2.5rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
}

/* Wordmark sizing locked to the marketing hero-bar (1.75rem, drops to
   1.5rem on narrow viewports) so ENTRYLANE renders at the same size on
   every page across the app. */
.admin-nav-title {
  font-family: var(--font-label);
  font-weight: 400;
  font-size: 1.75rem;
  letter-spacing: 0.04em;
  line-height: 1;
  color: #fff;
  /* Let the title column shrink so a long show name wraps at word
     boundaries instead of forcing the nav buttons off-screen. */
  flex: 1 1 auto;
  min-width: 0;
}
/* When the nav title is a LINK (participant pages link it home), it is a tap
   target and meets the field-flow floor. flex-wrap + full-basis show name
   (below) drops the show name onto its own line under the wordmark instead
   of sitting inline beside it. */
a.admin-nav-title { display: inline-flex; flex-wrap: wrap; align-items: baseline; min-height: 44px; }
@media (max-width: 480px) {
  .admin-nav-title { font-size: 1.5rem; }
}
.admin-nav-title em { color: var(--accent); font-style: normal; } /* dark nav band: bright accent passes there */

.admin-nav-right {
  display: flex;
  align-items: center;
  gap: 0.65rem;
}

/* Switch Show — admin-only quick-jump dropdown. Anchored to its
   trigger button via .switch-show-wrap (position: relative). Each
   menu item is a full-width anchor that links to the destination
   show's admin via the platform_token SSO query param. */
.switch-show-wrap { position: relative; display: inline-block; }
.switch-show-wrap .reports-menu { right: 0; left: auto; min-width: 240px; }
/* On narrow viewports the trigger button sits mid-header (left of Settings/
   Logout), so a right-anchored menu grows leftward off the screen edge and
   gets clipped. Anchor to the button's LEFT edge instead — it grows rightward
   where there's room — and cap the width so it can't spill past either side. */
@media (max-width: 640px) {
  /* The trigger can land anywhere in the wrapped right-hand cluster, so the
     menu anchors to the BAR (the sticky nav is the containing block) and
     hangs from its bottom-right edge: never off-screen on either side. */
  .switch-show-wrap { position: static; }
  .switch-show-wrap .reports-menu {
    left: auto; right: clamp(1.1rem, 4vw, 2.5rem);
    min-width: 200px;
    max-width: calc(100vw - 2rem);
  }
  .switch-show-item { white-space: normal; }
}
.switch-show-item {
  display: block;
  padding: 0.55rem 0.85rem;
  color: var(--text-primary);
  text-decoration: none;
  font-family: var(--font-label);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--border);
}
.switch-show-item:last-child { border-bottom: 0; }
.switch-show-item:hover { background: var(--accent-dim); color: var(--accent-ink, var(--accent)); }
.switch-show-date { color: var(--text-muted); font-size: 0.7rem; }

/* Global footer (admin / public registration / judge UI). Thin row
   with brand on the left and Contact / Terms / Privacy on the right.
   Matches the kinetic design system rather than the marketing site's
   richer kfooter — that one's tied to the marketing layout. */
/* Shared kinetic-style footer used across admin, judge, participant
   registration, and admin-login. Hardcoded values (not tokens) so the
   footer renders identically across every CSS context. */
.kfooter {
  background: #15151a;
  color: #fff;
  padding: 1.6rem 1.5rem;
  border-top: 1px solid rgba(255,255,255,0.08);
  display: flex; flex-direction: column; gap: 1rem;
}
@media (min-width: 761px) {
  .kfooter { flex-direction: row; justify-content: space-between; align-items: center; }
}
.kfooter-logo {
  font-family: var(--font-label); font-weight: 400;
  font-size: 1.4rem; letter-spacing: 0.04em;
  color: #fff; text-transform: uppercase;
}
.kfooter-logo .kw-entry { color: #fff; }
.kfooter-logo .kw-lane  { color: #58A0D8; margin-left: 0.05em; }
.kfooter-tagline {
  font-family: var(--font-label);
  font-size: 0.72rem; letter-spacing: 0.12em;
  color: rgba(255,255,255,0.55);
  margin: 0.4rem 0 0;
  text-transform: uppercase;
}
.kfooter a { color: rgba(255,255,255,0.85); text-decoration: none; display: inline-flex; align-items: center; min-height: 44px; }
.kfooter a:hover { color: #58A0D8; }
.kfooter-nav {
  display: flex; flex-wrap: wrap; gap: 1.6rem; align-items: center;
}
.kfooter-nav a {
  font-family: var(--font-label); font-weight: 500;
  font-size: 0.74rem; letter-spacing: 0.14em;
  text-transform: uppercase;
}

.admin-nav-user {
  font-family: var(--font-body);
  font-size: 0.8rem;
  font-weight: 500;
  letter-spacing: 0.01em;
  color: rgba(255,255,255,0.7);
  white-space: nowrap;
}

/* Demo-show badge (applyDemoGate) — mono stamp next to the wordmark so a
   prospect watching a demo always knows they're on a demo show. */
.admin-demo-badge {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  color: var(--accent, #58A0D8);
  border: 1px solid currentColor;
  padding: 0.15rem 0.4rem;
  margin-left: 0.6rem;
  align-self: center;
  white-space: nowrap;
}

/* Theme toggle */
.btn-theme {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--font-label);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255,255,255,0.5);
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.1);
  padding: 0.3rem 0.7rem;
  cursor: pointer;
  transition: all 0.15s;
}
.btn-theme:hover { background: rgba(255,255,255,0.11); color: rgba(255,255,255,0.85); }

.btn-logout, .btn-settings {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px; /* tap target — these render in the judge field nav (PRODUCT.md ≥48px) */
  font-family: var(--font-body);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.82); /* was 0.55 — too low contrast on the dark nav */
  padding: 0.32rem 0.9rem;
  cursor: pointer;
  transition: all 0.15s;
}
.btn-logout:hover { color: var(--error); border-color: rgba(255,77,109,0.4); }
.btn-settings:hover { color: var(--accent); border-color: rgba(88, 160, 216,0.4); } /* dark nav band */
.btn-theme-toggle {
  font-family: var(--font-label);
  font-size: 0.67rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.1);
  color: rgba(255,255,255,0.45);
  padding: 0.38rem 0.85rem;
  cursor: pointer;
  transition: all 0.15s;
  line-height: 1;
}
.btn-theme-toggle:hover { color: rgba(255,255,255,0.85); background: rgba(255,255,255,0.1); }

/* ═══════════════════════════════════════════════════════════
   MAIN LAYOUT
═══════════════════════════════════════════════════════════ */
.admin-main {
  max-width: var(--shell-console, 1240px);
  margin: 0 auto;
  padding: 0 var(--sp-24) var(--sp-48);
}

/* ═══════════════════════════════════════════════════════════
   STATS — ticker bar style
═══════════════════════════════════════════════════════════ */
.stats-row {
  display: grid;
  /* Equal-width cells for however many stats a tier shows (2 or 3), so the
     band always fills the row instead of hugging the left. */
  grid-template-columns: repeat(auto-fit, minmax(0, 1fr));
  gap: 0;
  background: var(--ticker-bg);
  border-bottom: 1px solid var(--border);
  margin: 0 calc(-1 * var(--sp-24)) var(--sp-32);
}

@media (max-width: 640px) {
  .stat-card { padding: 0.7rem 1rem; }
  .stat-num { font-size: 1.55rem; }
  .stat-label { font-size: 0.6rem; letter-spacing: 0.12em; }
  .stat-sub { font-size: 0.6rem; }
}

.stat-card {
  padding: 1.1rem 1.5rem;
  border-right: 1px solid var(--ticker-div);
  transition: background 0.2s;
}
.stat-card:last-child { border-right: none; }

.stat-label {
  font-family: var(--font-label);
  /* Sunlight bar: 0.58rem read as haze on a venue phone in glare;
     0.72rem is the platform's smallest label (kinetic .label-sm). */
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.2rem;
}

/* Secondary live metric under a stat (e.g. "avg 3m 12s / car" on Judged). */
.stat-sub {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  color: var(--accent-ink, var(--accent));
  margin-top: 0.25rem;
}
.stat-sub.hidden { display: none; }

/* The Judged stat doubles as the Unjudged-list button; this line is the
   visible affordance (critique P1: "secret door"). Label voice, accent ink,
   underlined so it reads as an action even in sunlight. */
.stat-tap-hint {
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
  text-decoration: underline;
  text-underline-offset: 3px;
  margin-top: 0.35rem;
}
.stat-card--btn:hover .stat-tap-hint { color: var(--accent-hover); }

.stat-num {
  font-family: var(--font-label);
  font-size: 2.4rem;
  letter-spacing: 0.04em;
  color: var(--text-primary);
  line-height: 1;
}
.stat-num.accent { color: var(--accent-ink, var(--accent)); }

/* Percentage judged, appended to the "Judged" label. Mono so it reads as a
   live figure, not a heading; accent ink ties it to the fill bar below. */
.stat-pct {
  font-family: var(--font-mono);
  font-size: 0.8em;
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--accent-ink, var(--accent));
}
.stat-pct.hidden { display: none; }

/* ═══════════════════════════════════════════════════════════
   SHOW-DAY MODE — sticky toolbar
═══════════════════════════════════════════════════════════ */

/* Show day: the toolbar sticks so search + Add stay at hand deep in the
   roster. Shadow only while actually stuck (scroll listener sets .is-stuck). */
body.show-day .toolbar {
  position: sticky;
  /* Stops BELOW the nav, which is itself sticky at top:0. Both parked at 0
     with the same z-index meant the toolbar scrolled up and sat on the
     wordmark. The nav's height isn't fixed (the show name wraps to a second
     line, and text scaling grows it), so bootShowDayMode publishes it as
     --admin-nav-h; 64px is the min-height fallback. */
  top: var(--admin-nav-h, 64px);
  /* Under the nav, still above table content and below overlays/menus. */
  z-index: calc(var(--z-nav) - 1);
  background: var(--bg-base);
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
  margin-bottom: 0.5rem;
}
body.show-day .toolbar.is-stuck {
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
}

/* ═══════════════════════════════════════════════════════════
   AWARDS GRID (judge cards)
═══════════════════════════════════════════════════════════ */
.judge-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 1rem;
  margin-bottom: 1.5rem;
}
@media (max-width: 960px) { .judge-grid { grid-template-columns: repeat(2,1fr); } }
@media (max-width: 760px)  { .judge-grid { grid-template-columns: 1fr; } }

.judge-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 0;
  box-shadow: var(--shadow);
  overflow: hidden;
  transition: background 0.2s, border-color 0.2s;
}

.heatmap-map-wrap {
  width: 100%;
  height: 320px;
  border: 1px solid var(--accent);
  background: var(--bg-card);
}
.heatmap-card {
  grid-column: span 2;
}
@media (max-width: 960px) {
  .heatmap-card { grid-column: span 1; }
}

.judge-card-label {
  font-family: var(--font-label);
  font-size: 1.2rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border);
}

.judge-card-body {
  padding: 1rem;
  max-height: 280px;
  overflow-y: auto;
}

.judge-empty {
  color: var(--text-muted);
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-style: italic;
  padding: 1rem 0;
  text-align: center;
}

/* ── Breakdown-tile view toggle (list / pie / bar) ──
   Lives in the card header row; the active view wears the accent. */
.judge-card-label--views {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
}
.judge-card-label--views > span:first-child {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-view-toggle {
  display: inline-flex;
  flex-shrink: 0;
  border: 1px solid var(--border-mid);
}
.tile-view-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 24px;
  padding: 0;
  background: transparent;
  border: none;
  border-right: 1px solid var(--border-mid);
  color: var(--text-muted);
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.tile-view-btn:last-child { border-right: none; }
.tile-view-btn svg { width: 13px; height: 13px; display: block; }
.tile-view-btn:hover { color: var(--accent-ink, var(--accent)); background: var(--accent-dim); }
.tile-view-btn--on {
  background: var(--accent);
  color: var(--on-accent, #0D0D12);
}
.tile-view-btn--on:hover { background: var(--accent); color: var(--on-accent, #0D0D12); }
.tile-view-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Breakdown tile: donut view ──
   Slices step the accent's opacity big → small; identity lives in the
   legend rows (which are also the click/keyboard path to the filter). */
.tile-pie { display: flex; flex-direction: column; gap: 0.85rem; }
.tile-pie-chart {
  position: relative;
  width: 152px;
  margin: 0.25rem auto 0;
}
.tile-pie-chart svg { display: block; width: 100%; height: auto; }
.tile-pie-slice { cursor: pointer; transition: filter 0.12s; }
.tile-pie-slice:hover { filter: brightness(1.15); }
.tile-pie-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.tile-pie-total {
  font-family: var(--font-mono);
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-primary);
}
.tile-pie-total-label {
  font-family: var(--font-label);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 0.2rem;
}
.tile-pie-legend { display: flex; flex-direction: column; }
.tile-pie-key {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.4rem 0.35rem;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s;
}
.tile-pie-key:last-child { border-bottom: none; }
.tile-pie-key:hover:not(:disabled) { background: var(--accent-dim); }
.tile-pie-key:disabled { cursor: default; color: var(--text-muted); }
.tile-pie-key:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tile-pie-swatch {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
  background: var(--accent);
}
.tile-pie-swatch--other { background: var(--text-muted); opacity: 0.35; }
.tile-pie-key-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-pie-key-count {
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text-secondary);
  white-space: nowrap;
}
.tile-pie-key-count em {
  font-style: normal;
  color: var(--text-muted);
  margin-left: 0.45rem;
  font-size: 0.72rem;
}

/* ── Breakdown tile: bar view ──
   One hue (one series — length is the data), value at the bar tip. */
.tile-bars { display: flex; flex-direction: column; }
.tile-bar-row {
  display: flex;
  align-items: center;
  gap: 0.55rem;
  width: 100%;
  padding: 0.42rem 0.35rem;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border);
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-primary);
  text-align: left;
  cursor: pointer;
  transition: background 0.12s;
}
.tile-bar-row:last-child { border-bottom: none; }
.tile-bar-row:hover { background: var(--accent-dim); }
.tile-bar-row:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tile-bar-name {
  flex: 0 0 5.5rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.tile-bar-track {
  flex: 1;
  height: 12px;
  background: var(--bg-inset);
}
.tile-bar-fill {
  display: block;
  height: 100%;
  background: var(--accent);
}
.tile-bar-count {
  flex-shrink: 0;
  min-width: 1.6rem;
  font-family: var(--font-mono);
  font-size: 0.82rem;
  color: var(--text-secondary);
  text-align: right;
}

/* ── Revenue Mix (Show Setup → Revenue) ──
   The dashboard tiles' list/pie/bar language, retargeted at money: shares
   of the total money in. Clicks jump to each stream's home, not a filter. */
.rev-mix {
  border: 1px solid var(--border);
  background: var(--bg-inset);
  padding: 0.85rem 1rem 0.55rem;
  margin: 0.15rem 0 1.2rem;
}
.rev-mix-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  flex-wrap: wrap;
  margin-bottom: 0.4rem;
}
.rev-mix .tile-pie-chart { width: 168px; }
/* Dollar figures run long — a slightly smaller center number keeps
   "$12,345" inside the donut hole. */
.tile-pie-total--money { font-size: 1.15rem; letter-spacing: 0; }
.rev-mix-note {
  font-style: normal;
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-muted);
  margin-left: 0.4rem;
}
.tile-bars--rev .tile-bar-name { flex: 0 0 9.5rem; }
.tile-bars--rev .tile-bar-count { min-width: 6.5rem; }
.tile-bar-count em {
  font-style: normal;
  color: var(--text-muted);
  margin-left: 0.45rem;
  font-size: 0.72rem;
}

/* ── Roster tile-filter chip ──
   Shows the club/class/make picked from a dashboard tile; × clears it. */
.reg-results-row {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  flex-wrap: wrap;
  /* Clears the sticky nav when a tile filter scrolls this row into view. */
  scroll-margin-top: 88px;
}
.roster-filter-chip {
  display: inline-flex;
  align-items: center;
  margin: -0.25rem 0 0.6rem;
  border: 1px solid var(--accent);
  background: var(--accent-dim);
  font-size: 0.78rem;
}
.roster-filter-chip.hidden { display: none; }
.roster-filter-chip-kind {
  padding: 0.28rem 0.5rem;
  background: var(--accent);
  color: var(--on-accent, #0D0D12);
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
}
.roster-filter-chip-value {
  padding: 0.28rem 0.55rem;
  font-family: var(--font-body);
  font-weight: 600;
  color: var(--text-primary);
}
.roster-filter-chip-x {
  padding: 0.28rem 0.55rem;
  background: transparent;
  border: none;
  border-left: 1px solid var(--accent);
  color: var(--accent-ink, var(--accent));
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
  transition: background 0.12s;
}
.roster-filter-chip-x:hover { background: var(--accent); color: var(--on-accent, #0D0D12); }
.roster-filter-chip-x:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

@media (prefers-reduced-motion: reduce) {
  .tile-view-btn, .tile-pie-slice, .tile-pie-key, .tile-bar-row, .roster-filter-chip-x { transition: none; }
}

/* ═══════════════════════════════════════════════════════════
   TOOLBAR
═══════════════════════════════════════════════════════════ */
.toolbar {
  display: flex;
  gap: 0.65rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
  overflow: visible;
}

.search-input {
  flex: 1 1 280px;
  max-width: 360px;
  min-width: 200px;
  background: var(--bg-card);
  border: 1px solid var(--border-mid);
  padding: 0.6rem 1rem;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.9rem;
  transition: border-color 0.15s;
}
.search-input:focus { outline: none; border-color: var(--accent); }
.search-input::placeholder { color: var(--text-muted); }

/* Search match count — a small label above the table, shown only while a
   query is active. Accent-inked so it reads as live feedback, not chrome. */
.reg-results {
  margin: -0.25rem 0 0.6rem;
  font-family: var(--font-label);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}
.reg-results.hidden { display: none; }

.btn-add {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--accent);
  color: var(--on-accent, #0D0D12);
  border: 1px solid var(--accent);
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  white-space: nowrap;
  transition: background 0.15s;
  text-align: center;
}
.btn-add:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
/* Quiet secondary — one accent-filled primary per surface; the rest sit on
   the card surface with a hairline border and only borrow the accent on
   hover. Keeps the toolbar readable whatever color the organizer picks. */
.btn-add.btn-quiet {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border-mid);
}
.btn-add.btn-quiet:hover {
  background: var(--accent-dim);
  border-color: var(--accent);
  color: var(--text-primary);
}
/* Toolbar — search input + every button share an exact 40px height.
   Buttons sized to content (with a small min) so they don't spill over
   onto a second row when the toolbar grows; search is fixed-narrower
   so it stops dominating the rhythm. */
.toolbar {
  justify-content: flex-start;
  align-items: stretch;
  gap: 0.5rem;
}
.toolbar > .search-input,
.toolbar .btn-add {
  height: 40px;
  line-height: 40px;
  padding: 0 0.85rem;
  font-size: 0.88rem;
  box-sizing: border-box;
  margin: 0;
  vertical-align: middle;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 0;
}
/* Post Show wrapper inherits the toolbar rhythm too — without this
   the wrapper div sat as inline-block at its content's natural size,
   so the button inside skipped the height of its siblings.  */
.toolbar .post-show-wrap {
  display: inline-flex;
  align-items: stretch;
}
.toolbar > .search-input {
  flex: 0 0 220px;
  max-width: 220px;
  padding: 0 0.85rem;
  text-align: left;
  justify-content: flex-start;
}
/* Toolbar hierarchy (critique P2): the two constant-use controls, search
   and + Add Registration, sit together on the LEFT in reading order; the
   occasional utilities (Import, Manage…, Post Show) cluster as one group
   hugging the right edge. The old layout pushed the accent primary across
   a dead gap to sit last among the quiet buttons. */
.toolbar-utils {
  margin-left: auto;
  display: flex;
  align-items: stretch;
  flex-wrap: wrap;
  gap: 0.5rem;
}
@media (max-width: 760px) {
  .toolbar > .search-input { flex: 1 1 100%; max-width: 100%; }
  /* Stacked phone toolbar: utilities flow under the primary, left-aligned. */
  .toolbar-utils { margin-left: 0; }
}

/* DISTANCE TRAVELED — scrollable top-10 list (matches Club Participation
   layout). Long city/state names truncate with ellipsis. */
.distance-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 280px;
  overflow-y: auto;
}
.distance-item {
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: 0.65rem;
  padding: 0.55rem 0.6rem;
  border-bottom: 1px solid var(--border);
}
.distance-item:last-child { border-bottom: none; }
.distance-rank {
  font-family: var(--font-label);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  color: var(--accent-ink, var(--accent));
  min-width: 1.6rem;
}
.distance-info {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.distance-name {
  font-family: var(--font-body);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-primary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.distance-vehicle,
.distance-origin {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-secondary);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.distance-miles {
  font-family: var(--font-label);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  color: var(--success-ink);
  background: var(--success-bg, rgba(34,197,94,0.12));
  border: 1px solid var(--success);
  padding: 0.3rem 0.6rem;
  white-space: nowrap;
}

/* Judged column — judge attribution in black, duration on next line. */
.col-judged .judged-meta {
  margin-top: 0.35rem;
  line-height: 1.25;
}
.col-judged .judged-meta-name {
  font-family: var(--font-body);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text-primary);
}
.col-judged .judged-meta-time {
  font-family: var(--font-label);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  color: var(--text-secondary);
  margin-top: 0.1rem;
}
.col-judged .judged-meta--penalty {
  font-family: var(--font-label);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--error);
  margin-top: 0.15rem;
}

.btn-stickers {
  background: transparent;
  color: var(--accent-ink, var(--accent));
  border: 1px solid var(--border-mid);
}
.btn-stickers:hover { background: var(--accent-dim); border-color: var(--accent); }

/* Inline class-picker dropdown inside the registrations table.
   Use the same flat / appearance:none look as the modal selects so
   the grey native control doesn't look out of place. */
.class-select {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--bg-card);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.85rem;
  padding: 0.35rem 1.6rem 0.35rem 0.6rem;
  width: 100%;
  max-width: 180px;
  cursor: pointer;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%23888' d='M6 8L0 0h12z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.55rem center;
  transition: border-color 0.15s;
}
.class-select:focus { outline: none; border-color: var(--accent); }
.class-select:disabled { opacity: 0.6; cursor: wait; }
/* Static class label in the registrations table — replaces the inline
   select. Plain typography, same column width, edited via the row's
   Edit button → registration modal. */
.col-class .class-static {
  display: inline-block;
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--text-primary);
}

/* ═══════════════════════════════════════════════════════════
   TABLE
═══════════════════════════════════════════════════════════ */
.table-wrap {
  overflow-x: auto;
  border: 1px solid var(--border);
  background: var(--bg-card);
  box-shadow: var(--shadow);
  /* Stable min-height so the page doesn't collapse upward as the user
     types in the search box and the filtered row count shrinks. */
  min-height: 60vh;
}
/* Day-zero (0 registrations): the tall reserve is a wall of blank white
   under the first-car CTA — let the empty state size itself. And the award
   tiles are skeleton wallpaper with no data to teach from — hidden until
   the first car registers (admin.js toggles both). */
.table-wrap.table-wrap--day-zero { min-height: 0; }
#awards-grid.grid--day-zero { display: none !important; }

table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }

thead { background: var(--bg-inset); }

th {
  padding: 0.65rem 0.75rem;
  text-align: left;
  font-family: var(--font-label);
  font-size: 0.95rem;
  font-weight: 400;
  letter-spacing: 0.08em;
  color: var(--accent-ink, var(--accent));
  text-transform: uppercase;
  white-space: nowrap;
  user-select: none;
  border-bottom: 1px solid var(--border-mid);
}

th[data-sort] { cursor: pointer; }
th[data-sort]:hover { color: var(--text-primary); }
th.sort-asc::after  { content: ' ↑'; }
th.sort-desc::after { content: ' ↓'; }

td {
  padding: 0.7rem 0.75rem;
  border-bottom: 1px solid var(--border-dim);
  vertical-align: top;
  text-align: left;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.83rem;
}

tr:last-child td { border-bottom: none; }
tr:nth-child(even) { background: var(--row-alt); }
tr:hover { background: var(--row-hover); }

.table-empty {
  text-align: center;
  color: var(--text-muted);
  padding: 3rem;
  font-family: var(--font-body);
  font-style: italic;
}

/* Badges */
/* Chip voice (DESIGN.md): small uppercase Archivo 700, 0.7rem, 0.1em. */
.badge {
  display: inline-block;
  padding: 0.18rem 0.55rem;
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.badge-paid    { background: var(--success-dim); color: var(--success-ink); border: 1px solid var(--success-border); }
.badge-pending { background: rgba(var(--accent-rgb, 88, 160, 216),0.1); color: var(--warning); border: 1px solid rgba(var(--accent-rgb, 88, 160, 216),0.3); }
.badge-failed  { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
/* Already judged — green (same treatment as Paid): a glance down the column
   reads green = done. */
.badge-judged     { background: var(--success-dim); color: var(--success-ink); border: 1px solid var(--success-border); }
/* Non-Judged (chosen at registration) — a true yellow, distinct from the
   orange accent and the red "not yet judged," so it reads as an intentional
   state, not a missing action. Pale yellow fill + dark-amber ink clears AA;
   dark theme flips to a light-yellow ink on a faint tint. */
.badge-nonjudged  { background: #FEF3C7; color: #854D0E; border: 1px solid #FCD34D; }
html[data-theme="dark"] .badge-nonjudged { background: rgba(250,204,21,0.14); color: #FDE047; border-color: rgba(250,204,21,0.40); }
/* Eligible-but-not-yet-judged — red so a single glance over the table
   surfaces who still needs to be scored / who still owes votes. */
.badge-no         { background: var(--error-bg); color: var(--error); border: 1px solid var(--error-border); }
/* Disqualified — solid red with the reason inline; the loudest badge in the
   column because it's the only one that means "out of the show's standings." */
.badge-dq         { background: var(--error); color: #fff; border: 1px solid var(--error); }
/* The DQ badge is a white label on a solid red fill. On the dark theme the
   standard danger (#ef4444) is too light for white text (~3.8:1, fails WCAG
   AA), so the fill deepens to keep the label >=4.5:1 while staying vivid red.
   Light theme's #ba1a1a already clears it. */
html[data-theme="dark"] .badge-dq { background: #c81e1e; border-color: #c81e1e; }

/* Cross-show DQ warning — this registrant matches someone disqualified at a
   PRIOR show (master dq_records). Advisory ONLY; it never blocks registration.
   HIGH = email/phone match (near-certain same person); MEDIUM = name + car /
   zip / club (possible — verify before acting). Sits under the name so it's
   impossible to miss but never masquerades as this show's own DQ badge. */
.dq-warn {
  display: block;
  margin-top: 5px;
  padding: 6px 8px;
  max-width: 34ch;
  font-size: 11px;
  line-height: 1.35;
  border-left: 3px solid var(--error);
  background: var(--error-bg);
  color: var(--error);
}
.dq-warn strong {
  display: block;
  margin-bottom: 2px;
  font-family: var(--font-label, 'Archivo'), sans-serif;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
}
/* A prior-show DQ is a caution regardless of confidence — always red, never
   the show's accent (which --warning follows and could be green/any color).
   The "· verify" label carries the high-vs-medium distinction, not the hue. */
.dq-warn--medium {
  border-left-color: var(--error);
  background: var(--error-bg);
  color: var(--error);
}

/* Hide the Class column on Top Picks. The <th> uses data-tier-not, but
   the <td> is rendered after applyTierGate() runs, so we use the
   body-level tier attribute (set by applyTierGate at boot) to gate
   them via CSS instead of re-running the JS engine after every render. */
body[data-tier="top-picks"] th[data-sort="judging_class"],
body[data-tier="top-picks"] td.col-class { display: none !important; }

/* Hide the vehicle-type field in Add Registration when "Best of Show
   Only" is on — there's only one winner across all entries, so the
   admin doesn't need to mark each car as Car/Truck/Bike. The select
   stays in the DOM (still serializes 'car' as the default) so existing
   row data isn't disturbed. */
body[data-bos-only="true"] #modal-reg-vehicle-type,
body[data-bos-only="true"] #modal-reg-vehicle-type ~ label,
body[data-bos-only="true"] .mfield:has(#modal-reg-vehicle-type) { display: none !important; }

/* Disabled best-of-show per-type toggles read as half-transparent so
   admins know they can't be flipped while Best of Show Only is on. */
.results-toggle.disabled {
  opacity: 0.4;
  pointer-events: none;
}

.col-regnum {
  font-family: var(--font-mono);
  color: var(--accent-ink, var(--accent));
  font-size: 0.78rem;
  width: 3rem;
}

/* Roster name links open the check-in card preview — the most-clicked text in
   the app. Ink shade (raw accent is ~2.7:1 on near-white, fails AA in exactly
   the sunlight this tool is used in) + a persistent underline so "click the
   name" is discoverable instead of color-only. */
.name-link {
  color: var(--accent-ink, var(--accent));
  text-decoration: underline;
  text-decoration-color: color-mix(in srgb, currentColor 45%, transparent);
  text-underline-offset: 3px;
  font-weight: 600;
  cursor: pointer;
}
.name-link:hover { text-decoration-color: currentColor; }

.action-btns { display: flex; gap: 0.4rem; }
.btn-edit, .btn-delete-row {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 0.28rem 0.6rem;
  min-height: 44px;
  cursor: pointer;
  border: 1px solid;
  background: transparent;
  transition: opacity 0.15s, background 0.15s;
}
.btn-edit { color: var(--accent-ink, var(--accent)); border-color: var(--border-mid); }
.btn-edit:hover { background: var(--accent-dim); border-color: var(--accent); }
.btn-delete-row { color: var(--error); border-color: var(--error-border); }
.btn-delete-row:hover { background: var(--error-bg); }

.judge-meta { margin-top: 0.25rem; color: var(--text-muted); font-size: 0.7rem; }
.judge-meta strong { color: var(--accent-ink, var(--accent)); }

/* Vehicle photo popup */
.vehicle-cell.has-photo { cursor: zoom-in; }
.photo-indicator { font-size: 0.85em; margin-left: 0.2rem; opacity: 0.6; }

.vehicle-photo-popup {
  position: fixed;
  z-index: var(--z-menu); /* hover popup: above modals, below toasts */
  padding: 5px;
  background: var(--bg-card);
  border: 1px solid var(--accent);
  box-shadow: var(--shadow-lg);
  pointer-events: none;
  transition: opacity 0.12s;
}
.vehicle-photo-popup.hidden { opacity: 0; }
.vehicle-photo-popup:not(.hidden) { opacity: 1; }
.vehicle-photo-popup img { display: block; width: 100%; height: auto; }

/* ═══════════════════════════════════════════════════════════
   MODALS
═══════════════════════════════════════════════════════════ */
.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 1rem;
}

/* ── Feedback motion ──────────────────────────────────────────
   The admin's only motion layer: state changes get a short, cheap
   acknowledgment (transform/opacity, ≤250ms, ease-out). Modals and
   panels toggle via .hidden (display:none), so entrances are keyframe
   animations — they fire on un-hide and always end fully visible. */
:root { --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1); }

@keyframes el-fade-in { from { opacity: 0; } }
@keyframes el-rise-in { from { opacity: 0; transform: translateY(6px); } }

.modal:not(.hidden) .modal-backdrop { animation: el-fade-in 0.18s ease-out; }
.modal:not(.hidden) .modal-card    { animation: el-rise-in 0.2s var(--ease-out-quint); }

/* Switching tabs inside a modal: the incoming panel rises in briefly. */
.modal-tab-panel:not(.hidden) { animation: el-rise-in 0.15s var(--ease-out-quint); }

/* Success / error notices register as they appear. */
.modal-notice, .modal-error { animation: el-rise-in 0.2s var(--ease-out-quint); }

/* Press feedback — the system's stamped :active (matches marketing .btn). */
.btn-save:active, .btn-add:active, .btn-delete:active, .btn-cancel:active,
.btn-edit:active, .btn-delete-row:active, .btn-settings:active, .modal-tab:active {
  transform: scale(0.97);
}

@media (prefers-reduced-motion: reduce) {
  .modal:not(.hidden) .modal-backdrop,
  .modal:not(.hidden) .modal-card,
  .modal-tab-panel:not(.hidden),
  .modal-notice, .modal-error { animation: none; }
  .btn-save:active, .btn-add:active, .btn-delete:active, .btn-cancel:active,
  .btn-edit:active, .btn-delete-row:active, .btn-settings:active, .modal-tab:active {
    transform: none;
  }
}

.modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(5,5,12,0.82);
  backdrop-filter: blur(4px);
}

.modal-card {
  position: relative;
  background: var(--modal-bg);
  border: 1px solid var(--border-mid);
  border-top: 3px solid var(--accent);
  width: 100%;
  /* As a flex child of the centered .modal overlay, the default min-width:auto
     floors this at its content's min-content width — so wide content (e.g. the
     Manage Classes rows) forced the card PAST its max-width, running off-screen
     and killing the right-justify. min-width:0 lets it honor max-width, so rows
     fit, fields cap at their widths, and .class-row-actions can pin right. */
  min-width: 0;
  max-width: 680px;
  max-height: 90vh;
  overflow-y: auto;
  padding: var(--sp-32);
  z-index: 1;
  box-shadow: var(--shadow-lg);
}

.modal-card--sm       { max-width: 420px; }
.modal-card--wide     { max-width: 880px; }
.modal-card--qr       { max-width: 440px; text-align: center; }
/* Settings card clips and lays out as a flex column so the header + tab bar
   stay put and ONLY .settings-tabs-body scrolls (no nested double scrollbar). */
.modal-card--settings { max-width: min(880px, 92vw); max-height: 88vh; padding: 1.75rem 1.75rem 2rem; overflow: hidden; display: flex; flex-direction: column; }

/* Awards PDF preview — wide, tall card with a Print/Download action row
   in the header and a borderless full-bleed iframe below it. The iframe
   itself holds the same /api/admin/awards*.pdf the download endpoint
   serves, so Print prints byte-for-byte the same artifact. */
.modal-card--awards   {
  max-width: min(1080px, 96vw);
  max-height: 94vh;
  padding: 1.25rem 1.25rem 1.5rem;
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
.modal-card--awards .modal-header {
  flex: 0 0 auto;
  margin-bottom: 1rem;
  gap: 0.6rem;
}
.awards-preview-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.awards-preview-actions .btn-save,
.awards-preview-actions .btn-cancel {
  font-size: 0.7rem;
  padding: 0.45rem 0.9rem;
}
.awards-preview-download {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
}
.awards-preview-body {
  flex: 1 1 auto;
  min-height: 50vh;
  background: var(--surface-low, #f7f7f9);
  border: 1px solid var(--border-mid);
  overflow-y: auto;
  padding: 1rem 1rem 1.5rem;
}
.awards-preview-state {
  font-family: var(--font-label);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  font-size: 0.8rem;
  color: var(--text-muted, #6b6b78);
  padding: 1.5rem 0.5rem;
  text-align: center;
}
.awards-preview-content {
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  max-width: 760px;
  margin: 0 auto;
}
/* Head block — show name (large centered), date, "Presented by …", tier tag. */
.awards-preview-head {
  text-align: center;
  display: flex;
  flex-direction: column;
  gap: 0.2rem;
  padding-bottom: 0.8rem;
  border-bottom: 2px solid var(--accent, #58A0D8);
}
.awards-preview-head-title {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.7rem;
  letter-spacing: 0.02em;
  color: var(--text-primary, #0d0d12);
}
.awards-preview-head-sub {
  font-family: var(--font-body);
  font-size: 0.9rem;
  color: var(--text-muted, #6b6b78);
}
.awards-preview-head-meta {
  font-family: var(--font-label);
  font-weight: 600;
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent, #58A0D8));
  margin-top: 0.4rem;
}
.awards-preview-section {
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}
.awards-preview-section-head {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.05rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent, #58A0D8));
  text-align: center;
  padding: 0.2rem 0 0.5rem;
}
/* Best of Show card — orange outline, soft tint to draw the eye. */
.awards-bos-card {
  background: rgba(88, 160, 216, 0.07);
  border: 1.5px solid var(--accent, #58A0D8);
  padding: 0.85rem 1rem;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.awards-bos-label {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent, #58A0D8));
}
.awards-bos-name {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--text-primary, #0d0d12);
}
.awards-bos-sub {
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--text-muted, #6b6b78);
}
.awards-bos-sponsor {
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-style: italic;
  color: var(--accent-ink, var(--accent));
  margin-top: 0.1rem;
}
/* Top-N + class-podium row — single line, table-like. Place | #Num | Name | Vehicle. */
.awards-toplist {
  background: #ffffff;
  border: 1px solid var(--border-mid);
}
.awards-toplist-row {
  /* Rank + ONE flowing line ("#39 Jasmine Springlett — 2006 Pontiac Grand
     Prix · Crowned Loyalty"). The old 3-column grid had 4 children, so the
     vehicle wrapped into the 3rem rank column one word per line. */
  display: grid;
  grid-template-columns: 3rem 1fr;
  align-items: baseline;
  gap: 0.8rem;
  padding: 0.55rem 0.9rem;
  border-top: 1px solid #eeeef2;
}
.awards-toplist-line { min-width: 0; overflow-wrap: anywhere; }
.awards-toplist-num { margin-right: 0.2rem; }
.awards-toplist-row:first-child { border-top: 0; }
.awards-toplist-rank {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent, #58A0D8));
}
.awards-toplist-num {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 0.88rem;
  color: var(--text-primary, #0d0d12);
}
.awards-toplist-name {
  font-family: var(--font-body);
  font-weight: 600;
  font-size: 0.95rem;
  color: var(--text-primary, #0d0d12);
}
.awards-toplist-vehicle {
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-muted, #6b6b78);
}
/* Class-winners card (Ultimate + Voting) — class header + rows. */
.awards-class-card {
  background: #ffffff;
  border: 1px solid var(--border-mid);
}
.awards-class-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border-mid);
  flex-wrap: wrap;
}
.awards-class-name {
  font-family: var(--font-label);
  font-weight: 700;
  font-size: 0.95rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-primary, #0d0d12);
}
.awards-class-sponsor {
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-muted, #6b6b78);
}
.awards-preview-empty {
  font-family: var(--font-body);
  font-size: 0.88rem;
  color: var(--text-muted, #6b6b78);
  padding: 0.6rem 0.2rem;
  text-align: center;
}
.awards-preview-empty--soft {
  font-style: italic;
  background: #ffffff;
  border: 1px dashed var(--border-mid);
  padding: 0.9rem;
}
#awards-preview-print-frame {
  position: absolute;
  width: 0;
  height: 0;
  border: 0;
  opacity: 0;
  pointer-events: none;
}
@media (max-width: 640px) {
  .modal-card--awards { padding: 1rem 0.85rem 1.2rem; }
  .awards-preview-body { padding: 0.75rem 0.5rem 1rem; }
  .awards-preview-head-title { font-size: 1.35rem; }
  .awards-preview-section-head { font-size: 0.9rem; letter-spacing: 0.14em; }
  .awards-toplist-row {
    grid-template-columns: 2.4rem 1fr;
    gap: 0.5rem;
    padding: 0.55rem 0.7rem;
  }
  .awards-toplist-vehicle { font-size: 0.78rem; }
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 1.5rem;
}

.modal-title {
  font-family: var(--font-label);
  font-size: 1.5rem;
  letter-spacing: 0.1em;
  color: var(--accent-ink, var(--accent));
}

.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 1.5rem;
  cursor: pointer;
  line-height: 1;
  transition: color 0.15s;
}
.modal-close:hover { color: var(--text-primary); }

/* Entry-number badge in the Add/Edit Registration modal header. Subtle
   bordered box; the number reads in mono. Swaps to an editable input when
   the "manually overwrite entry number" setting is on. */
.entry-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  margin-right: 0.75rem;
  padding: 0.3rem 0.6rem;
  border: 1px solid rgba(88, 160, 216, 0.3);
  background: var(--accent-dim);
}
.entry-badge-label {
  font-family: var(--font-label);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--text-muted);
  white-space: nowrap;
}
.entry-badge-value {
  font-family: var(--font-mono);
  font-size: 1.05rem;
  font-weight: 600;
  line-height: 1;
  color: var(--accent-ink, var(--accent));
}
.entry-badge-input {
  width: 4.5rem;
  padding: 0.2rem 0.35rem;
  border: 1px solid rgba(88, 160, 216, 0.5);
  background: var(--input-bg, var(--bg-card));
  color: var(--accent-ink, var(--accent));
  font-family: var(--font-mono);
  font-size: 1.05rem;
  font-weight: 600;
  text-align: center;
}
.entry-badge-input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-dim);
}
/* Hide the number spinners — the mono digits read cleaner without them. */
.entry-badge-input::-webkit-outer-spin-button,
.entry-badge-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.entry-badge-input { -moz-appearance: textfield; appearance: textfield; }

.modal-error {
  background: var(--error-bg);
  border: 1px solid var(--error-border);
  border-top: 2px solid var(--error);
  padding: 0.7rem 0.9rem;
  color: var(--error);
  font-family: var(--font-body);
  font-size: 0.85rem;
  margin-bottom: 1rem;
}

/* Success notice — green, matching the --success palette used elsewhere
   (e.g. the post-show "saved" badges). Mirrors .modal-error's shape. */
.modal-notice {
  background: var(--success-dim);
  border: 1px solid var(--success-border);
  border-top: 2px solid var(--success);
  color: var(--success-ink);
  padding: 0.7rem 0.9rem;
  font-size: 0.85rem;
  margin: 0.75rem 0;
}

.modal-section-label {
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.22em;
  color: var(--accent-ink, var(--accent));
  text-transform: uppercase;
  margin: 1.35rem 0 0.75rem;
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.modal-section-label::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border);
}

.modal-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0.9rem; }
.mfield { display: flex; flex-direction: column; gap: 0.3rem; }
.mfield--full { grid-column: 1 / -1; }

.mfield label, .optional {
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: var(--text-muted);
  text-transform: uppercase;
}
/* Checkbox variant — a .mfield whose whole content is one label+checkbox
   (e.g. the reg modal's "Unjudged"). Undoes the full-width input treatment
   and the uppercase micro-label so the row reads as a sentence. */
.mfield--check label {
  font-family: var(--font-body);
  font-size: 0.9rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--text-primary);
}
.mfield--check input[type="checkbox"] {
  width: 16px; height: 16px; flex: 0 0 auto;
  padding: 0; min-height: 0;
  margin-top: 0.15em;
  accent-color: var(--accent);
  /* The generic .mfield input rule below strips native rendering
     (appearance: none) — restore it, or the checked state draws no
     checkmark at all (the box "just shows the outline"). Mirrors
     .mfield-checkline further down. */
  appearance: auto; -webkit-appearance: auto;
}

.mfield input, .mfield select, .mfield textarea {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  padding: 0.6rem 0.75rem;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1rem; /* design-system field size — matches the unified selects (and stops iOS zoom-on-focus) */
  width: 100%;
  min-height: 2.6rem;
  line-height: 1.3;
  appearance: none;
  -webkit-appearance: none;
  transition: border-color 0.15s;
}
.mfield textarea { resize: vertical; }
/* ZIP-only entry: read-only City/State resolved from the ZIP. Rendered as a
   <div> (not an input) so it is genuinely non-editable. Reads as derived/locked
   — dashed border + mono value, going solid/accent once a ZIP resolves. */
.zip-resolved-hint { color: var(--text-muted); font-weight: 400; text-transform: none; letter-spacing: 0; }
.zip-resolved-box {
  background: var(--input-bg);
  border: 1px dashed var(--border-mid);
  padding: 0.6rem 0.75rem;
  min-height: 2.6rem;
  display: flex;
  align-items: center;
  /* Body face, not mono: this box speaks prose ("Enter a ZIP above", "ZIP not
     found") as often as it echoes a resolved town, and mono is reserved for
     IDs, stats and kickers (DESIGN.md §3). */
  font-family: var(--font-body);
  font-size: 1rem;
  color: var(--text-muted);
  line-height: 1.3;
  user-select: none;
}
.zip-resolved-box--found { color: var(--text-primary); border-style: solid; border-color: var(--accent); }
.zip-resolved-box--missing { color: var(--error); }
/* Inline checkbox row inside a modal field (e.g. Sponsor/Vendor). The generic
   .mfield input sizing above would stretch the checkbox full-width — undo it. */
.mfield .mfield-checkline {
  display: flex; align-items: center; gap: 0.55rem;
  min-height: 2.6rem; cursor: pointer;
  font-family: var(--font-body); font-size: 1rem; color: var(--text-primary);
  /* The generic `.mfield label` rule also matches this inner label —
     undo its label-voice styling: this line is body copy. */
  font-weight: 400; text-transform: none; letter-spacing: 0.015em;
}
.mfield .mfield-checkline input[type="checkbox"] {
  width: 1.1rem; height: 1.1rem; min-height: 0; padding: 0; flex-shrink: 0;
  accent-color: var(--accent); cursor: pointer;
  appearance: auto; -webkit-appearance: auto;
}
.mfield select {
  padding-right: 2rem;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%23888' d='M6 8L0 0h12z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
}
.mfield input:focus, .mfield select:focus, .mfield textarea:focus { outline: none; border-color: var(--accent); }
.mfield select option { background: var(--modal-bg); }

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.65rem;
  margin-top: 2rem;
}
/* Split: a destructive action (e.g. Clear judging) anchored left, the
   primary action right. Collapses to a stacked, full-width pair on phones. */
.modal-actions--split { justify-content: space-between; align-items: center; }
@media (max-width: 480px) {
  .modal-actions--split { flex-wrap: wrap; }
  .modal-actions--split > * { flex: 1 1 100%; }
}

/* Shared action buttons */
.btn-cancel {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: transparent;
  color: var(--accent-ink, var(--accent));
  border: 1px solid var(--border-mid);
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}
.btn-cancel:hover { background: var(--accent-dim); border-color: var(--accent); }

.modal-photo-upload {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  flex-wrap: wrap;
}
.modal-photo-preview {
  position: relative;
  display: inline-block;
}
.modal-photo-preview img {
  display: block;
  max-width: 96px;
  max-height: 96px;
  border: 1px solid var(--border);
  background: var(--bg-card);
  object-fit: cover;
}
.modal-photo-remove {
  position: absolute;
  top: -8px; right: -8px;
  width: 22px; height: 22px;
  background: var(--bg-card);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
  color: var(--text-primary);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
}

.btn-save {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--accent);
  color: var(--on-accent, #0D0D12);
  border: none;
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  transition: background 0.15s;
}
.btn-save:hover { background: var(--accent-hover); }
.btn-save:disabled { opacity: 0.5; cursor: not-allowed; }
/* "Small" keeps the tighter padding but the standard 0.88rem button type —
   two type sizes for the same control read as inconsistency, not hierarchy. */
.btn-save--sm { padding: 0.32rem 0.75rem; font-size: 0.88rem; }

.btn-delete {
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  background: var(--error);
  color: #fff;
  border: none;
  padding: 0.6rem 1.1rem;
  cursor: pointer;
  transition: opacity 0.15s;
}
.btn-delete:hover { opacity: 0.85; }

/* ═══════════════════════════════════════════════════════════
   MODAL TABS
═══════════════════════════════════════════════════════════ */
.modal-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0;
  margin: 0.25rem 0 1rem;
  border-bottom: 1px solid var(--border);
}
.modal-tab {
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  color: var(--text-secondary);
  /* Control face, same as every other tab strip on the platform
     (platform-admin .tab, marketing .us-map-tab): Archivo 600,
     sentence case, .btn-sm size. Tabs are controls, not headlines. */
  font-family: var(--font-body);
  font-size: 0.88rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-transform: none;
  padding: 0.65rem 1.1rem;
  cursor: pointer;
  margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.modal-tab:hover { color: var(--text-primary); }
.modal-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: -2px;
  color: var(--text-primary);
}
.modal-tab--active { color: var(--accent-ink, var(--accent)); border-bottom-color: var(--accent); }

/* ═══ SHOW SETUP PAGE (/admin/setup) ═══════════════════════
   Same DOM as the Manage Classes modal, re-rendered as a full page:
   admin.js adds body.setup-page and opens the "modal", these rules
   flatten the overlay into the document and turn the tab strip into a
   left section rail on desktop. Dashboard bands hide — this page is
   only the setup surface. (docs/SHOW_SETUP_BRIEF.md) */
/* Roster hit count under the toolbar — mono telemetry line, count in the
   accent so the number reads first. */
.reg-results {
  margin: -0.35rem 0 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.74rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-secondary);
}
.reg-results strong { color: var(--accent-ink, var(--accent)); font-weight: 700; font-size: 0.95rem; letter-spacing: 0.04em; }
.reg-results em { font-style: normal; text-transform: none; letter-spacing: 0.02em; color: var(--text-primary); }

body.setup-page .stats-row,
body.setup-page #awards-grid,
body.setup-page .toolbar,
body.setup-page .table-wrap,
body.setup-page #reg-results-status,
/* The emptied dashboard <main> still rendered as a 48px white band between
   the nav and the setup header (its own padding) — hide the container too. */
body.setup-page .admin-main,
body.setup-page #setup-guide { display: none !important; }

/* The sticky nav can only stick within its parent #dashboard, and with the
   dashboard bands hidden that parent collapses to the nav's own height —
   so the bar scrolled away with the page. Dissolving the wrapper puts the
   nav in the body's flow, where its sticky range covers the whole page
   (the rehomed classes modal is a body-level sibling, and it must stay
   one: the print rules key on `body.is-printing > *`). `.hidden`'s
   !important display:none still wins pre-login. */
body.setup-page #dashboard { display: contents; }

body.setup-page #classes-modal {
  position: static;
  inset: auto;
  display: block;
  padding: 0;
  z-index: auto;
}
body.setup-page #classes-modal .modal-backdrop { display: none; }
body.setup-page #classes-modal .modal-card {
  /* Stays in the document flow at every width. The phone rule below turns
     working modals into a fixed full-screen sheet; on the setup page that
     pinned a viewport-tall box whose overflow (this rule's overflow:
     visible) spilled below the fold where neither the sheet nor the page
     could scroll to it — every Show Setup section was cut off on phones. */
  position: relative;
  inset: auto;
  max-width: none;
  max-height: none;
  overflow: visible;
  margin: 0;
  /* The header row butts straight against the nav — no accent line and no
     white gap above it (the pinned row reads as part of the top chrome). */
  border-top: 0;
  padding-top: 0;
}
/* Desktop: header spans the top; the tab strip becomes a left rail with
   the section content beside it. Active section reads as a stamped tint
   plus accent ink (no side-stripe indicators). */
@media (min-width: 961px) {
  body.setup-page #classes-modal .modal-card {
    display: grid;
    grid-template-columns: 13rem minmax(0, 1fr);
    column-gap: 2rem;
    align-items: start;
  }
  body.setup-page #classes-modal .modal-header,
  body.setup-page #classes-modal .modal-error,
  body.setup-page #classes-modal .modal-notice { grid-column: 1 / -1; }
  /* The header row (title + Export/Import actions + Back) pins directly
     under the sticky nav so the actions stay reachable down a long
     section. Opaque card background + border so content slides beneath,
     not through; the old margin becomes padding for the same reason
     (content would show through a transparent margin gap). admin.js
     publishes the row's measured height as --setup-head-h so the rail
     below can clear it. */
  body.setup-page #classes-modal .modal-header {
    position: sticky;
    top: var(--nav-h, 64px);
    z-index: calc(var(--z-nav) - 1); /* under the nav, over the sections */
    background: var(--modal-bg);
    margin-bottom: 0;
    padding: 0.9rem 0;
    border-bottom: 1px solid var(--border-mid);
  }
  /* Cap the measure. The section column is minmax(0, 1fr), so on a wide
     monitor every settings row's grey slab — and every description
     paragraph — ran the full width of the screen with its controls
     stranded at the far left. These same panels live inside an 880px
     modal on the dashboard, so a cap here can't squeeze anything that
     doesn't already work: it just stops the page reading as a banner. */
  body.setup-page #classes-modal .modal-tab-panel {
    grid-column: 2; grid-row: 3; margin-top: 1rem;
    max-width: 60rem;
  }
  body.setup-page #classes-modal .modal-tabs {
    grid-column: 1;
    grid-row: 3;
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
    border-bottom: 0;
    margin: 1.5rem 0 0;
    position: sticky;
    /* Clear the sticky nav AND the sticky header row above: both heights
       are published by admin.js from measured sizes (they grow with the
       show-name line, wrapped action buttons, and browser text scaling —
       hardcoded offsets would tuck the first section under them). */
    top: calc(var(--nav-h, 64px) + var(--setup-head-h, 0px) + 1rem);
  }
  body.setup-page #classes-modal .modal-tab {
    text-align: left;
    border-bottom: 0;
    border: 1px solid transparent;
    padding: 0.7rem 0.85rem;
    margin: 0;
  }
  body.setup-page #classes-modal .modal-tab--active {
    background: var(--accent-dim);
    border-color: var(--accent);
  }
}

/* ═══ PHONE MODALS ══════════════════════════════════════════
   Desktop scale reads oversized inside a ~375px card: the 1.5rem title
   collides with the close button, 1.4rem section titles shout, and tab
   bars wrap into ragged two-line stacks. Tighten the type ramp and turn
   every tab bar into a single scrollable strip (design doc §6).
   Field inputs deliberately STAY ≥16px — shrinking them re-triggers
   iOS Safari's zoom-on-focus at the check-in gate. */
@media (max-width: 640px) {
  .modal-card { padding: 1.25rem 1rem 1.5rem; }
  .modal-header { gap: 0.75rem; }
  .modal-title { font-size: 1.15rem; letter-spacing: 0.08em; }

  /* ONE phone geometry for every working modal: a full-screen sheet, same
     as the Show Setup page. The floating inset card cramped 15-field forms
     and every modal floated at a slightly different size — the sheet is
     the standard. Small confirm/notice dialogs (.modal-card--sm) stay
     centered cards: they're a question, not a workspace. */
  .modal-card:not(.modal-card--sm) {
    position: fixed;
    inset: 0;
    width: auto;
    max-width: none;
    height: auto;
    max-height: none;
    margin: 0;
    border: 0;
    border-top: 3px solid var(--accent);
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: max(1.5rem, env(safe-area-inset-bottom));
  }

  /* The Add Registration header chip (entry number / "next entry") sat in
     the title row and clipped off the right edge — wrap it onto its own
     full-width line under the title instead. */
  .modal-header { flex-wrap: wrap; }
  .entry-badge {
    order: 3;
    flex-basis: 100%;
    justify-content: space-between;
    margin: 0;
  }

  /* Tab bars: one horizontal strip that scrolls instead of wrapping.
     Scrollbar hidden — the cut-off tab peeking at the edge is the
     affordance; every tab keeps its full-height touch target. */
  .modal-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    /* The Settings card is a column flex; once this bar scrolls
       (overflow ≠ visible) its min-height resolves to 0 and the column
       squashes it flat — pin its height. */
    flex-shrink: 0;
  }
  .modal-tabs::-webkit-scrollbar { display: none; }
  .modal-tab {
    flex: 0 0 auto;
    font-size: 0.85rem;
    padding: 0.6rem 0.8rem;
    white-space: nowrap;
  }

  /* Section heads step down one notch — still the loudest thing in the
     card, no longer competing with the page. Sub copy tightens with them. */
  .settings-section-title { font-size: 1.05rem; }
  .settings-section-sub   { font-size: 0.8rem; }
}

/* Touch floor: on coarse pointers every button in a working surface meets
   the 44px minimum. Row buttons (user Edit/Reset/Delete, class Edit/Delete)
   measured 25-36px — fine for a mouse, misses for a thumb in the field.
   Keyed to pointer type, not width: a phone in landscape still needs it. */
@media (pointer: coarse) {
  .modal-card button:not(.modal-close),
  #classes-modal button:not(.modal-close),
  .modal-card select,
  #classes-modal select {
    min-height: 44px;
  }
  .modal-close { min-width: 44px; min-height: 44px; }
}

/* ═══════════════════════════════════════════════════════════
   SETTINGS MODAL
═══════════════════════════════════════════════════════════ */
/* (.settings-body / .settings-col removed — the two-column layout was
   replaced by the tabbed panels below.) */

/* ── The one admin toast ──────────────────────────────────────────────────
   EVERY transient confirmation in the admin renders through this component:
   settings auto-save, sponsor assignment, adding a user, clearing votes.
   There used to be two — a bottom-center mono "<item> saved" pill and a
   bottom-right solid-fill toast built from inline styles — so the same action
   class confirmed itself two different ways depending on which screen you were
   on. One component, one position, one voice.

   Body face, never mono: mono is the telemetry register for IDs, stats and
   kickers (DESIGN.md §3), not for prose a human reads once and dismisses.

   Stacked by #admin-toast-host; `.settings-toast` stays as an alias so any
   straggling markup keeps working. */
.admin-toast,
.settings-toast {
  padding: 0.55rem 1rem;
  background: var(--bg-card);
  border: 1px solid var(--border-strong);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  line-height: 1.4;
  text-align: center;
  max-width: min(420px, calc(100vw - 2rem));
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
  opacity: 0;
  transform: translateY(0.5rem);
  pointer-events: none;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

/* Light-green success treatment matching the PAID badge — but OPAQUE: the
   toast floats over the Settings modal's dark backdrop, so a translucent
   --success-dim wash turns dark and buries the emerald text. Layering the
   wash over the solid card color keeps the badge look on any backdrop. */
.admin-toast--success,
.settings-toast {
  background: linear-gradient(var(--success-dim), var(--success-dim)) var(--bg-card);
  border-color: var(--success-border);
  color: var(--success-ink);
}
.admin-toast--success::before,
.settings-toast::before { content: '✓ '; font-weight: 700; }

.admin-toast--error {
  background: linear-gradient(var(--error-dim, rgba(200,40,40,0.10)), var(--error-dim, rgba(200,40,40,0.10))) var(--bg-card);
  border-color: var(--error);
  color: var(--error);
}

.admin-toast.is-visible,
.settings-toast.is-visible { opacity: 1; transform: translateY(0); }

/* Fixed bottom-center stack. Centering the column (rather than pinning each
   toast with its own translate) lets several stack without fighting over the
   same transform the show/hide transition animates. */
#admin-toast-host {
  position: fixed;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  pointer-events: none;
}

@media (prefers-reduced-motion: reduce) {
  .admin-toast,
  .settings-toast { transition: opacity 0.18s ease; }
}

/* Tabbed settings: the ONLY scroll region. flex:1 + min-height:0 lets it take
   the leftover card height and scroll, while the card itself clips (no nested
   double scrollbar) and the header + tab bar stay fixed above it. */
.settings-tabs-body { flex: 1 1 auto; min-height: 0; overflow-y: auto; padding-right: 0.4rem; }
.settings-tab-panel { min-width: 0; }
.settings-tab-panel.hidden { display: none; }

/* Phone: let the card use nearly the full viewport. The tab panels are a
   single column at every size, so no column collapse is needed. */
@media (max-width: 760px) {
  .modal-card--settings { max-width: calc(100vw - 1.25rem); max-height: 92vh; padding: 1.5rem 1.25rem 1.75rem; }
}

.settings-row {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 1.15rem 0;
  border-top: 1px solid var(--border);
  margin-top: 0;
}
.settings-row:first-of-type { border-top: none; margin-top: 0.25rem; }
.settings-row--stacked { flex-direction: column; align-items: stretch; gap: 0.45rem; }
.settings-row-main { flex: 1 1 auto; min-width: 0; }

/* Row variants — the layout vocabulary the settings panels share, so the
   markup never re-states flex recipes inline. */
.settings-row--toggle { justify-content: space-between; }            /* label left, switch right */
/* Show Options → Awards: Best Car / Truck / Bike read as children of the
   Best of Show Only toggle above them — indented behind a hairline rail.
   (Only ON force-disables all three; wireBestOfShowOnly handles that.) */
.bos-sub-toggles { margin-left: 0.4rem; padding-left: 0.9rem; border-left: 2px solid var(--border); }
/* Same rail for a single child option inside a stacked row (Women's Ride →
   "give it to the highest score"), which reads as a sub-setting of the
   toggle above it rather than a sibling. */
.settings-row--child { margin: 0.5rem 0 0 0.4rem; padding-left: 0.9rem; border-left: 2px solid var(--border); }
.settings-row--lead-toggle { gap: 0.85rem; }                          /* switch leads, label follows */
.settings-row--lead-toggle .settings-row-title { margin-left: 0.5rem; }
.settings-row--actions { flex-wrap: wrap; gap: 0.5rem; }              /* button clusters (reports) */
.settings-row--flush { border-top: none; padding-top: 0; margin-top: 0; } /* row glued to its heading */

/* The switch + On/Off state cluster that sits at the right edge of a toggle
   row (or on its own line inside a stacked row). */
.settings-toggle { display: flex; align-items: center; gap: 0.5rem; flex-shrink: 0; }
/* Title + toggle line inside a stacked row (title left, switch right, sub-copy below). */
.settings-toggle-line { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }

/* Inline status line under a setting (save confirmations, validation notes). */
.settings-row-status {
  margin-top: 0.4rem;
  font-family: var(--font-body);
  font-size: 0.72rem;
  color: var(--text-muted);
}

/* Two-up field grid (milestone pickers, photo uploads, social links).
   Rows inside lose their hairlines — a partial border on one grid cell
   reads as a glitch — and the grid collapses to one column on phones. */
.settings-grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0.75rem 1rem; }
.settings-grid-2 > .settings-row { border-top: none; margin-top: 0; padding-top: 0; }
.settings-grid-2 .settings-grid-span { grid-column: 1 / -1; }
@media (max-width: 760px) {
  .settings-grid-2 { grid-template-columns: 1fr; }
}

/* Radio pair (flyer contact: email or phone). */
.settings-radio-row { display: flex; align-items: center; gap: 0.85rem; margin-top: 0.25rem; }
.settings-radio { display: flex; align-items: center; gap: 0.4rem; font-size: 0.85rem; cursor: pointer; }

/* Check-in printer row: test-print button + inert-state note share a line.
   Controls read as disabled (not just dead) while no-printer mode is on. */
.checkin-printer-actions { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.6rem; flex-wrap: wrap; }
/* Show Setup → Pre-Registration: the shareable link + Copy button. */
.prereg-link-row { display: flex; align-items: center; gap: 0.75rem; margin-top: 0.5rem; flex-wrap: wrap; min-width: 0; }
.prereg-url {
  font-family: var(--font-mono); font-size: 0.85rem;
  color: var(--accent-ink, var(--accent));
  overflow-wrap: anywhere; min-width: 0;
}
.settings-text-input:disabled,
#checkin-printer-row .btn-add:disabled { opacity: 0.5; cursor: not-allowed; }

.settings-row-title {
  display: block;
  font-family: var(--font-label);
  /* Option labels read as form labels, clearly subordinate to the 1.4rem
     accent section titles — matching the dashboard's compact Archivo label
     scale rather than competing as headings. */
  font-size: 0.95rem;
  font-weight: 400;
  letter-spacing: 0.04em;
  color: var(--text-primary);
}
/* Inline "(optional)" parenthetical inside a settings-row-title: drops the
   uppercase/tracking back to sentence-case muted body so it reads as an aside. */
.settings-row-note {
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--text-muted);
}
.settings-row-sub {
  display: block;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin-top: 0.25rem;
  line-height: 1.45;
}
/* Inline "Opens" / "Closes" caption above each datetime input inside a
   stacked settings row. Inputs sit full-width below, matching the
   "Judging closes at" pattern. */
.settings-inline-check {
  display: flex; align-items: center; gap: 0.5rem; cursor: pointer;
  font-family: var(--font-body); font-size: 0.85rem;
  color: var(--text-secondary); user-select: none;
}
.settings-inline-check input {
  width: 1rem; height: 1rem; accent-color: var(--accent); cursor: pointer;
}
.settings-window-sublabel {
  display: block;
  font-family: var(--font-label);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-top: 0.65rem;
  margin-bottom: 0.25rem;
}

/* Live preview of how the day-of milestones read on the public flyer's
   "The Rundown" line. Stamped left-rule + inset surface so it reads as a
   distinct "this is the output" panel, not another input. */
.flyer-rundown-preview {
  margin-top: 0.85rem;
  padding: 0.85rem 1rem;
  background: var(--bg-inset, #f3f3f4);
  border: 1px solid var(--border);
}
.flyer-rundown-eyebrow {
  display: block;
  font-family: var(--font-label);
  font-size: 0.65rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent, #58A0D8));
  margin-bottom: 0.4rem;
}
.flyer-rundown-text {
  margin: 0;
  font-family: var(--font-body);
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--text, #1a1c1d);
}
.flyer-rundown-text .run-time {
  font-family: var(--font-mono);
  font-weight: 700;
  color: var(--accent-ink, var(--accent, #58A0D8));
}
.flyer-rundown-text .flyer-rundown-empty {
  color: var(--text-muted);
  font-style: italic;
}

/* Section heads carry the panel's rhythm: a generous break ABOVE each group
   (2rem margin + 1.35rem padding over the rule) against the tight 1.15rem
   row spacing inside a group — related rows read as one unit, sections as
   distinct chapters. */
.settings-section-head {
  display: block;
  margin: 2rem 0 0.4rem;
  padding-top: 1.35rem;
  border-top: 1px solid var(--border);
}
/* First head in a tab panel sits flush against the tab bar. */
.settings-section-head--flush {
  border-top: none;
  margin-top: 0;
  padding-top: 0;
}
/* A head already separates: no extra hairline between it and its first row. */
.settings-section-head + .settings-row { border-top: none; margin-top: 0; }
/* On top-picks the Timing panel hides "Voting & Windows", leaving Show
   Schedule as the first visible head — let it sit flush like the others. */
body[data-tier="top-picks"] [data-panel="timing"] .settings-section-head:not([data-tier-not]) {
  border-top: none;
  margin-top: 0;
  padding-top: 0;
}
.settings-section-title {
  display: block;
  font-family: var(--font-label);
  font-size: 1.4rem;
  font-weight: 400;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}
.settings-section-sub {
  display: block;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-muted);
  margin-top: 0.3rem;
  line-height: 1.45;
}

.settings-text-input {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.55rem 0.7rem;
  font-family: var(--font-body);
  font-size: 1rem; /* match the design-system field size + the unified selects */
  /* Same 44px field height as the unified selects and row buttons — a text
     input sitting shorter than the dropdown beside it read as two different
     control sizes in one row (Sponsors tab). Phones already enforced this. */
  min-height: 44px;
  width: 100%;
  transition: border-color 0.15s;
}
.settings-text-input:focus { outline: none; border-color: var(--accent); }
/* Legible placeholder — the browser default fails the 4.5:1 sunlight bar. */
.settings-text-input::placeholder { color: var(--text-muted); opacity: 1; }
.settings-text-input--num { width: 6rem; }
/* Datetime-local pickers are noticeably more compact than text fields —
   constrain so the date+time + calendar icon fits without empty space. */
.settings-text-input[type="datetime-local"] {
  width: auto;
  max-width: 14rem;
}
.settings-text-input.saved { border-color: var(--success); }

/* Day-of milestone times — native <input type="time">, matching the
   datetime-local show-start field above it. Mono digits for the kinetic
   data treatment; accent-tinted picker indicator; compact, square. */
.settings-text-input--time {
  width: auto;
  max-width: 9rem;
  margin-top: 0.1rem;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  accent-color: var(--accent);
}
.settings-text-input--time::-webkit-calendar-picker-indicator {
  cursor: pointer;
  /* Tint the UA clock glyph toward the kinetic accent. */
  filter: invert(54%) sepia(72%) saturate(2100%) hue-rotate(343deg) brightness(101%) contrast(98%);
}

/* Results / judging toggles */
.results-toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.65rem;
  border: 1px solid var(--border);
  background: var(--bg-inset);
  cursor: pointer;
  user-select: none;
  font-size: 0.78rem;
  color: var(--text-secondary);
}
/* Invisible hit-area expansion: the visible chip is ~32px tall, but organizers
   flip these on phones at the venue — pad the tappable area to ≥44px without
   changing the visual. */
.results-toggle::before { content: ''; position: absolute; inset: -7px -4px; }
.results-toggle input[type="checkbox"] { position: absolute; opacity: 0; pointer-events: none; width: 0; height: 0; }
/* Keyboard focus lands on the hidden checkbox — surface it on the chip. */
.results-toggle:has(input:focus-visible) {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}
.results-toggle-label {
  font-family: var(--font-label);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  font-size: 0.65rem;
  color: var(--accent-ink, var(--accent));
}
.results-toggle-track {
  position: relative;
  display: inline-block;
  width: 30px; height: 17px;
  background: var(--toggle-off);
  border: 1px solid var(--toggle-border);
  transition: background 0.15s;
}
.results-toggle-thumb {
  position: absolute;
  top: 2px; left: 2px;
  width: 11px; height: 11px;
  background: var(--text-muted);
  transition: transform 0.15s, background 0.15s;
}
.results-toggle input:checked + .results-toggle-track { background: var(--accent-dim); border-color: var(--accent); }
.results-toggle input:checked + .results-toggle-track .results-toggle-thumb { transform: translateX(13px); background: var(--accent); }
.results-toggle-state {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.06em;
  min-width: 1.5rem;
  text-transform: uppercase;
}

/* Custom select — a JS-driven picker (vehicle type, user role). Styled to look
   exactly like a native <select> (the unified dropdown control below): same
   Archivo body face, sentence case, size, border, height and chevron — so it
   no longer reads as an uppercase Archivo label next to the real selects. */
.custom-select { position: relative; display: inline-block; }
.custom-select-trigger {
  background: var(--input-bg);
  color: var(--text-primary);
  border: 1px solid var(--border-mid);
  padding: 0.5rem 0.75rem;
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  min-height: 44px;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  min-width: 110px;
  transition: border-color 0.15s;
}
.custom-select-trigger:hover { border-color: var(--accent); }
.custom-select-chev { font-size: 0.6rem; opacity: 0.7; }
.custom-select-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 100%;
  background: var(--modal-bg);
  border: 1px solid var(--border-mid);
  padding: 0.3rem;
  z-index: var(--z-menu);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  box-shadow: var(--shadow-lg);
}
.custom-select-menu.hidden { display: none; }
.custom-select-option {
  background: transparent;
  border: none;
  text-align: left;
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1rem;
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
  white-space: nowrap;
}
.custom-select-option:hover     { background: var(--accent-dim); }
.custom-select-option.is-active { background: var(--accent); color: var(--on-accent, #0D0D12); font-weight: 600; }

/* ═══════════════════════════════════════════════════════════
   REPORTS MENU
═══════════════════════════════════════════════════════════ */
.reports-btn-wrap { position: relative; display: inline-block; overflow: visible; margin-left: auto; }
.reports-menu {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  min-width: 160px;
  background: var(--modal-bg);
  border: 1px solid var(--border-mid);
  padding: 0.3rem;
  z-index: var(--z-menu);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
  box-shadow: var(--shadow-lg);
}
.reports-menu.hidden { display: none; }
.reports-menu button {
  background: transparent;
  border: none;
  text-align: left;
  color: var(--accent-ink, var(--accent));
  font-family: var(--font-body);
  font-size: 0.82rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  padding: 0.5rem 0.7rem;
  cursor: pointer;
  white-space: nowrap;
}
.reports-menu button:hover { background: var(--accent-dim); }

/* Post Show toolbar dropdown wrapper — same structure as Reports. */
.post-show-wrap { position: relative; display: inline-block; overflow: visible; }
.post-show-wrap .reports-menu { right: 0; left: auto; }

/* Modal subtabs (used inside Post Show > Metrics tab) */
.modal-subtabs {
  display: flex;
  gap: 1.4rem;
  border-bottom: 1px solid var(--border);
  margin: 0 0 0.9rem;
  padding: 0;
}
.modal-subtab {
  background: none; border: 0;
  padding: 0.5rem 0;
  font-family: var(--font-body);
  font-size: 0.88rem; font-weight: 600;
  letter-spacing: 0.01em; text-transform: none;
  color: var(--text-muted); cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.modal-subtab:hover { color: var(--text-primary); }
.modal-subtab--active {
  color: var(--accent-ink, var(--accent));
  border-bottom-color: var(--accent);
}
.modal-subtab-panel { padding-top: 0.4rem; }

/* Scrollable post-show modal panels (Metrics + Survey Responses).
   The window itself is sized appropriately; contents that exceed the
   viewport scroll inside the panel rather than growing the modal. */
.ps-scroll {
  max-height: 65vh;
  overflow-y: auto;
  padding-right: 0.4rem;
}

/* ═══════════════════════════════════════════════════════════
   CLASSES / SPECIALTY
═══════════════════════════════════════════════════════════ */
.classes-section { margin-top: 1.25rem; }
.classes-section-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 0.6rem;
}
.classes-section-head h3 {
  font-family: var(--font-label);
  font-size: 0.82rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
  margin: 0;
}
.classes-section-sub { color: var(--text-muted); font-size: 0.7rem; margin-left: 0.4rem; }

.classes-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  /* No max-height + overflow:auto here — that clipped the inline vehicle-type
     dropdown when it opened downward. The modal scrolls as a whole instead. */
  padding: 0.2rem 0.2rem 0.5rem 0;
}

/* Persistent add row — pinned below the list so you can keep entering names
   one after another without scrolling up. Input matches the Settings format
   (.settings-text-input); identical for classes and specialty awards. */
.class-add {
  display: flex; align-items: center; flex-wrap: wrap;
  gap: 0.6rem;
  margin-top: 0.75rem; padding-top: 1rem;
  border-top: 1px solid var(--border);
}
.class-add-input { flex: 1 1 220px; min-width: 180px; }
/* Dollar-amount fields hold "125.50", not a sentence — the add-row's 220px
   flex basis reads as a runaway box on phones. Keep them ~8rem. */
.class-add-input--amount { flex: 0 1 8rem; min-width: 6rem; }
.class-add-btn { flex: 0 0 auto; white-space: nowrap; }
/* Vehicle type picker in the add-class row — holds one short word, so it
   sizes to its content instead of taking the text input's 220px basis. */
.class-add-vt { flex: 0 0 auto; min-width: 7rem; width: auto; }
.class-add-vt[hidden] { display: none; }
/* Buttons that share a row with 44px fields match their height, so an add
   form or inline mini-form never mixes control sizes. */
.class-add .btn-save, .sponsor-add .btn-save,
.sponsor-inline-add .btn-save, .sponsor-inline-add .btn-cancel { min-height: 44px; }
.class-add-status {
  flex-basis: 100%; min-height: 0.9rem;
  font-size: 0.72rem; color: var(--text-muted);
}
.class-add-status.error { color: #BA1A1A; }

/* ── Sponsors: per-class dropdown + the Sponsors tab rows ── */
.class-sponsor-select {
  background: var(--input-bg); border: 1px solid var(--border-mid);
  color: var(--text-primary); font-family: var(--font-body);
  padding: 0.3rem 0.45rem; max-width: 190px;
  /* font-size comes from the unified dropdown rule (1rem) so it matches
     the inputs beside it. */
}
.class-sponsor-select:focus { outline: none; border-color: var(--accent); }
.club-sponsor-head { margin-top: 1.25rem; }
/* Sponsor rows have no inner .class-row-view to absorb narrow widths — the
   controls sit directly in the row, so the .class-row nowrap they inherit
   would shove the whole page sideways on phones. Let the row itself wrap. */
.class-row.sponsor-row { flex-wrap: wrap; }
.sponsor-row .sponsor-name { flex: 0 1 220px; min-width: 150px; }
/* Editable sponsor name — same slot the static label used to occupy;
   width:auto lets flex-basis size it instead of settings-text-input's 100%. */
.sponsor-row .sponsor-name-input { flex: 0 1 220px; min-width: 150px; width: auto; }
.sponsor-row .sponsor-tier { max-width: 170px; }
/* Sponsor/Vendor Fee — "$" prefix glued to a compact amount box — and the
   Unpaid/Paid/N/A dropdown: success-green once the fee is collected, muted
   for N/A (free sponsorship, no fee expected). */
.sponsor-fee-wrap { display: inline-flex; align-items: center; gap: 0.25rem; flex: 0 0 auto; }
.sponsor-fee-cur { font-family: var(--font-mono); font-size: 0.85rem; color: var(--text-muted); }
.sponsor-fee-input { width: 4.6rem; font-family: var(--font-mono); text-align: right; }
.sponsor-paid { max-width: 120px; }
.sponsor-paid--yes {
  color: var(--success-ink, var(--success));
  border-color: var(--success-border);
  background: var(--success-dim);
}
.sponsor-paid--na { color: var(--text-muted); }
/* Best of Show Votes tile — desktop vote-entry row (judge + category + car #). */
.bos-vote-entry { display: flex; align-items: center; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.6rem; }
.bos-vote-entry .class-sponsor-select { max-width: 11rem; }
.bos-vote-entry .settings-text-input { width: auto; }
.sponsor-value {
  font-family: var(--font-mono); font-size: 0.85rem; font-weight: 600;
  color: var(--accent-ink, var(--accent)); min-width: 3rem;
}
.sponsor-backs {
  flex-basis: 100%; font-size: 0.72rem; color: var(--text-muted); margin: 0.1rem 0 0;
}
/* Per-sponsor Facebook/website link and comment — each its own full-width
   line under the controls. */
.sponsor-link-input { flex: 1 1 100%; margin-top: 0.15rem; }
.sponsor-notes-input { flex: 1 1 100%; margin-top: 0.15rem; }
.sponsor-backs--none { font-style: italic; }
.sponsor-add {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.6rem;
  margin-top: 0.75rem; padding-top: 1rem; border-top: 1px solid var(--border);
}
/* Inline "+ New sponsor…" mini-form — appears beside the picker that
   invoked it; full-width line inside the flex row. */
.sponsor-inline-add {
  flex-basis: 100%;
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.45rem;
  padding: 0.5rem 0.6rem;
  background: var(--accent-dim);
  border: 1px solid var(--accent);
}
.sponsor-inline-add .sponsor-inline-name { flex: 1 1 160px; min-width: 140px; }
.sponsor-inline-add .sponsor-inline-tier { flex: 0 0 auto; min-width: 8rem; }
/* Same cap as the catalog rows' name boxes — a growing basis let this one
   swallow the whole row on desktop, a runaway box for a 2-word name. */
.sponsor-add-name { flex: 0 1 220px; min-width: 160px; }
.sponsor-tier-select { flex: 0 0 auto; min-width: 150px; }

/* ── Sponsorship Summary tab ──
   Money stat cards reuse the Post Show metrics components; the lists reuse
   metrics-section-head + metrics-state-row so the report reads like the rest
   of the console's telemetry. The print header only exists on paper. */
.summary-print-head { display: none; }
.summary-stats {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 0.6rem; margin: 0.75rem 0 0.25rem;
}
/* Compound selectors: these must out-rank the later metrics-* rules they
   piggyback on (.metrics-stat-num color, .metrics-state-row span:last-child). */
.metrics-stat-num.summary-stat-num--paid { color: var(--success-ink, var(--success)); }
.summary-stat-note { font-family: var(--font-body); font-size: 0.72rem; color: var(--text-muted); margin-top: 0.3rem; }
.metrics-state-row.summary-row .summary-row-meta { color: var(--text-muted); font-family: var(--font-body); font-weight: 400; font-size: 0.75rem; }
.summary-row span:last-child .summary-paid { color: var(--success-ink, var(--success)); }
.summary-row span:last-child .summary-unpaid { color: var(--accent-ink, var(--accent)); }
.summary-all-good { font-family: var(--font-body); font-size: 0.8rem; color: var(--success-ink, var(--success)); margin: 0.15rem 0 0; }
.summary-actions {
  display: flex; justify-content: flex-end;
  margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--border);
}

.class-row {
  display: flex;
  /* The OUTER row never wraps: drag handle, #badge, and the controls block
     keep one axis. Only .class-row-view wraps internally when narrow —
     wrap-in-wrap made each row break at a content-dependent width, so
     neighboring rows showed different heights (badges stranded mid-row). */
  flex-wrap: nowrap;
  gap: 0.6rem;
  align-items: center;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  padding: 0.6rem 0.75rem;
}
.class-row > .class-name { flex: 0 1 200px; min-width: 160px; max-width: 200px; }

/* Drag-to-reorder handle (grip). Only this element starts a drag, so the
   row's buttons keep working. */
.class-drag {
  cursor: grab; color: var(--text-muted);
  font-size: 1.05rem; line-height: 1; letter-spacing: -0.1em;
  padding: 0 0.15rem; user-select: none; align-self: center;
}
.class-drag:hover { color: var(--accent-ink, var(--accent)); }
.class-drag:active { cursor: grabbing; }
.class-row.is-dragging { opacity: 0.45; outline: 1px dashed var(--accent); }

/* View vs edit sub-trees inside a class row. View mode is the default
   and shows the static name + vehicle-type label + Edit/Delete. Edit
   mode (revealed when the row carries .is-editing) shows the input,
   the Car/Truck/Bike dropdown, and Save/Cancel. Both sub-trees inherit
   the row's flex layout so the items still flow horizontally. */
.class-row .class-row-view,
.class-row .class-row-edit {
  display: flex;
  flex: 1 1 auto;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.6rem;
  min-width: 0;
}
.class-row .class-row-view.hidden,
.class-row .class-row-edit.hidden { display: none; }
/* Static labels mimic the input/dropdown look without being editable. */
.class-row .class-name-static {
  flex: 0 1 200px;
  min-width: 130px;
  max-width: 200px;
  /* Long unbroken names ("TOYOTA/LEXUS/SCION") otherwise inflate this
     box's minimum width and make just those rows wrap a hair earlier
     than their neighbors — mixed row heights in one list. */
  overflow-wrap: anywhere;
  /* Archivo body at the 1rem design-system FIELD size — this box sits
     inline with the sponsor/judge selects and the edit-mode input, all 1rem;
     at 0.875rem it read as a different type size in the same row. */
  font-family: var(--font-body);
  font-size: 1rem;
  letter-spacing: normal;
  color: var(--text-primary);
  /* 0.72rem vertical padding lands the box at the same 44px field height as
     the selects/inputs/buttons sharing its row. */
  padding: 0.72rem 0.6rem;
  min-height: 44px;
  background: #fff;
  border: 1px solid var(--border);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.class-row .class-vt-static {
  /* Fixed width so CAR/TRUCK/BIKE rows share one wrap point — with auto
     width, TRUCK rows wrapped a viewport-hair earlier than CAR rows and
     the list showed mixed row heights. 4.5rem fits the widest (TRUCK). */
  flex: 0 0 4.5rem;
  text-align: center;
  font-family: var(--font-label);
  font-size: 0.7rem;
  font-weight: 700; /* chip voice — Archivo 700 uppercase tracked (design doc) */
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
  padding: 0.45rem 0.7rem;
  background: var(--bg-card);
  border: 1px solid var(--border);
}
/* Class-row action cluster — mirrors the registrants table's .action-btns
   exactly (transparent outline Edit, red-outline Delete, same font/size) and
   pins to the right edge of the row. The buttons live in this flex container,
   so margin-left:auto here is what actually right-justifies them (it does
   nothing on the buttons themselves — the wrapper was previously an unstyled
   block div). */
.class-row .class-row-actions {
  display: flex;
  gap: 0.4rem;
  align-items: center;
  margin-left: auto;
}
/* Winner is the one filled action (specialty rows) — line it up with the
   44px-tall outline Edit/Delete beside it. */
.class-row .class-row-actions .class-winner { min-height: 44px; }
/* Cancel sits next to Save in edit mode and inherits .btn-edit's
   orange-text/grey-border outline. The class-specific override below
   keeps it neutral so it doesn't read as a primary action. */
.class-row .class-cancel {
  color: var(--text-secondary);
  border-color: var(--border-mid);
}
.class-row .class-cancel:hover {
  background: var(--bg-inset);
  border-color: var(--border-mid);
}
/* The persistent new-row Add/Cancel keep a fixed width so the add form reads
   as one rhythm. The existing-row actions (Save/Delete/Winner) do NOT — they
   are text-sized like the registrants-table Edit/Delete (see .class-row-actions
   below), so no fixed width here. */
.class-row--new .class-add-btn,
.class-row--new .class-cancel-btn {
  flex: 0 0 110px;
  width: 110px;
  min-width: 110px;
  padding: 0.6rem 0.5rem;
  text-align: center;
}
.class-row--rubric .class-name-static {
  font-family: var(--font-label);
  font-size: 1rem;
  letter-spacing: 0.04em;
  color: var(--text-primary);
  flex: 1 1 200px;
}
.class-row--rubric .rubric-cat-count {
  font-family: var(--font-label);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-secondary);
}
.class-row--rubric .rubric-toggle { margin-left: auto; min-width: 90px; }
/* Class-row Delete carries `btn-delete-row class-delete` and uses the canonical
   registrants-table red-outline style straight from `.btn-delete-row` — no
   per-row override. (This previously forced a black chip with raw-accent text,
   which drifted teal under a themed accent and diverged from every other Delete
   in the app.) */
/* The new-row form (no #N badge yet) collapses the leading column. */
.class-row--new { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-dim); }
/* Cancel in the new-row form mirrors the Edit/Delete/Save buttons in
   the existing rows — same fixed width and height so the action column
   reads as one rhythm down the modal. */
.class-row--new .class-cancel-btn {
  flex: 0 0 110px;
  width: 110px;
  min-width: 110px;
  height: 38px;
  padding: 0 0.6rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  margin-left: auto; /* pin Cancel to the right edge */
}
/* Cancel keeps the neutral grey-on-white treatment so it doesn't
   accidentally read as the primary commit action — Enter on the input
   commits the new class. */
.class-row--new .class-cancel-btn {
  background: var(--bg-card);
  color: var(--text-primary);
  border: 1px solid var(--border);
}
.class-row--new .class-cancel-btn:hover {
  background: var(--bg-inset);
  color: var(--text-primary);
  opacity: 1;
}
/* Dropdown (Car / Truck / Bike) matches the white text-input look — the
   trigger picks up the same white background, mid-grey border, and
   consistent height as .class-name. */
/* Vehicle-type dropdown is narrow — it only ever holds Car / Truck /
   Bike, so it doesn't need to be the same width as the class-name
   input. Sized so the row's Save + Delete buttons fit on the same
   visual line without wrapping. */
.class-row--new .class-vehicle-type,
.class-row .class-vehicle-type {
  flex: 0 0 110px;
  min-width: 110px;
  max-width: 110px;
}
.class-row--new .class-vehicle-type .custom-select-trigger,
.class-row .class-vehicle-type .custom-select-trigger {
  background: var(--bg-card) !important;
  color: var(--text-primary) !important;
  border: 1px solid var(--border-mid) !important;
  padding: 0.55rem 0.7rem !important;
  font-size: 1rem !important;
  font-family: var(--font-body) !important;
  height: auto !important;
}

.class-number-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.4rem;
  padding: 0.25rem 0.55rem;
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  color: var(--accent-ink, var(--accent));
  font-family: var(--font-label);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
}

.class-row input {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.55rem 0.7rem;
  font-family: var(--font-body);
  font-size: 0.875rem;
  min-width: 0;
}
.class-row .class-name { font-size: 1rem; }
.class-row .class-row-status,
.class-row .class-truck,
.class-row .class-vehicle-type .custom-select-trigger {
  font-size: 0.85rem;
}
.class-row input:focus { outline: none; border-color: var(--accent); }

.class-truck {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-family: var(--font-body);
  font-size: 0.78rem;
  color: var(--text-secondary);
  cursor: pointer;
  user-select: none;
}
.class-truck input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }
.class-truck--placeholder { width: 0; }
.class-vehicle-type { min-width: 100px; }
.class-vehicle-type .custom-select-trigger { width: 100%; padding: 0.38rem 0.6rem; }
/* Specialty winner's name — sits inline in .class-row-view between the
   sponsor select and the Winner/Edit/Delete cluster, shrinking (with an
   ellipsis) before the row wraps. A full-width basis here inside the
   nowrap .class-row used to shove the view block into a stacked mess. */
.class-winner-line {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--font-label);
  font-size: 0.7rem;
  font-weight: 600;
  color: var(--accent-ink, var(--accent));
  letter-spacing: 0.04em;
}

.class-row-status { font-family: var(--font-label); font-size: 0.65rem; letter-spacing: 0.04em; min-width: 3.5rem; text-align: right; }
/* Idle status reserves no width — its 3.5rem was pushing the row's
   Edit/Delete onto a mostly-empty second flex line at laptop widths. */
.class-row-status:empty { min-width: 0; }
/* The three view-mode dropdowns (sponsor / judge / awards) shrink before
   the row wraps, so one row of controls survives down to ~1200px. */
.class-row-view > .class-sponsor-select { flex: 1 1 130px; min-width: 110px; }
/* Specialty rows carry a single dropdown, so the sponsor name can use the
   row's slack instead of clipping at the shared 190px select cap. */
.class-row--specialty .class-row-view > .class-sponsor-select { max-width: 320px; }
/* Built-in award rows (club + Best of Show) read like every other award
   row: one line tall, with the "Built-in — …" note inline after the sponsor
   picker (ellipsized) instead of stacking a second full-width line that
   turned each row into a slab. */
.class-row--club .sponsor-backs, .class-row--bos .sponsor-backs, .class-row--computed .sponsor-backs {
  flex: 1 1 auto; flex-basis: auto; min-width: 0; margin: 0;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 640px) {
  .class-row--club .sponsor-backs, .class-row--bos .sponsor-backs, .class-row--computed .sponsor-backs { flex-basis: 100%; white-space: normal; }
}
.class-row-status.saving { color: var(--warning); }
.class-row-status.saved  { color: var(--success-ink); }
.class-row-status.error  { color: var(--error); }

@media (max-width: 640px) {
  .class-row > .class-name { flex: 1 1 100%; }
  /* Sponsor dropdown gets its own full-width line on phones so it's an obvious,
     tappable picker rather than a squeezed sliver. */
  .class-sponsor-select { flex: 1 1 100%; max-width: none; order: 3; }
  /* Winner's name gets its own line under the sponsor picker on phones. */
  .class-winner-line { flex: 1 1 100%; order: 4; white-space: normal; }
  /* Sponsor catalog rows: the order:3 full-width treatment above is meant
     for the per-class sponsor picker. Here the tier/paid selects keep their
     DOM slots so the wrapped row reads name → tier/fee → paid/Delete. */
  .sponsor-row .sponsor-name-input { flex: 1 1 100%; }
  .sponsor-row .class-sponsor-select { order: 0; flex: 0 1 auto; }
  /* The add form's name box stays a full-width tap target on phones. */
  .sponsor-add-name { flex: 1 1 100%; }
}

/* ═══════════════════════════════════════════════════════════
   QR MODAL
═══════════════════════════════════════════════════════════ */
.qr-subtitle {
  font-family: var(--font-label);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--text-muted);
  text-transform: uppercase;
  margin: -0.5rem 0 1.25rem;
}
/* Metrics view "Print" button row — keeps the Judging/Show subtab strip
   on the left and the Print action right-aligned. */
.metrics-tab-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.6rem;
}
.metrics-tab-row .modal-subtabs { margin-bottom: 0; flex: 1 1 auto; min-width: 0; }
.metrics-print-btn { flex: 0 0 auto; }
/* Print-only letterhead for the metrics report — hidden on screen. */
.metrics-print-head { display: none; }

/* Print stylesheet — toggled on by body.is-printing for the duration
   of window.print(). Two surfaces print this way: the Post Show metrics
   sub-panel and the Manage Classes Sponsorship Summary. Only the OPEN
   modal renders — the :not(.hidden) guards keep the closed one from
   being force-shown into the other's printout. */
@media print {
  body.is-printing > *:not(#post-show-modal):not(#classes-modal) { display: none !important; }
  body.is-printing #post-show-modal:not(.hidden),
  body.is-printing #classes-modal:not(.hidden) {
    position: static !important;
    inset: auto !important;
    background: #fff !important;
    display: block !important;
  }
  body.is-printing #post-show-modal.hidden,
  body.is-printing #classes-modal.hidden { display: none !important; }
  body.is-printing #post-show-modal .modal-backdrop { display: none !important; }
  body.is-printing #post-show-modal .modal-card {
    background: #fff !important;
    box-shadow: none !important;
    border: 0 !important;
    max-width: 100% !important;
    max-height: none !important;
    overflow: visible !important;
    padding: 0 !important;
  }
  body.is-printing #post-show-modal .modal-close,
  body.is-printing #post-show-modal .metrics-tab-row,
  body.is-printing #post-show-modal .modal-subtabs,
  body.is-printing #post-show-modal .metrics-print-btn { display: none !important; }
  body.is-printing #post-show-modal .modal-tab-panel.hidden { display: none !important; }
  body.is-printing #post-show-modal .modal-subtab-panel.hidden { display: none !important; }
  body.is-printing #post-show-modal .modal-subtab-panel:not(.hidden) { display: block !important; }
  /* Force readable contrast on paper. */
  body.is-printing #post-show-modal,
  body.is-printing #post-show-modal * {
    color: #000 !important;
    background: #fff !important;
    border-color: #ccc !important;
    text-shadow: none !important;
    box-shadow: none !important;
  }
  /* Bars + accents — keep the structure but render in grayscale. */
  body.is-printing #post-show-modal .metrics-dist-bar,
  body.is-printing #post-show-modal .metrics-dist-bar--mid,
  body.is-printing #post-show-modal .metrics-dist-bar--far,
  body.is-printing #post-show-modal .metrics-dist-bar--class {
    background: #444 !important;
  }
  body.is-printing #post-show-modal h1,
  body.is-printing #post-show-modal h2,
  body.is-printing #post-show-modal h3 { page-break-after: avoid; }
  body.is-printing #post-show-modal .metrics-section-head { page-break-after: avoid; }
  /* Drop any height-capped scroll containers so all rows print. */
  body.is-printing #post-show-modal .ps-scroll,
  body.is-printing #post-show-modal .metrics-class-scroll {
    max-height: none !important;
    overflow: visible !important;
  }

  /* ── Metrics "Show Report" letterhead + kinetic print styling ──
     Stylized like the winners-list PDF: centered show name, a teal report
     title + date, an accent rule, uppercase Archivo Black section heads with a
     teal underline, and mono metric values. All teal rules use class-level
     specificity + !important so they beat the blanket monochrome above.
     Grayscale-safe: structure comes from type/rules, never color alone. */
  body.is-printing #post-show-modal .modal-header { display: none !important; }
  body.is-printing #post-show-modal .metrics-print-head {
    display: block !important;
    text-align: center;
    border-bottom: 2px solid #58A0D8 !important;
    padding-bottom: 0.55rem;
    margin: 0 0 1.15rem;
  }
  body.is-printing #post-show-modal .metrics-print-show {
    display: block;
    font-family: var(--font-label, 'Archivo', system-ui, sans-serif);
    font-size: 1.6rem; font-weight: 700;
    letter-spacing: 0.05em; text-transform: uppercase;
    line-height: 1.05; color: #000 !important;
  }
  body.is-printing #post-show-modal .metrics-print-title {
    display: block;
    font-family: var(--font-label, 'Archivo', system-ui, sans-serif);
    font-size: 0.8rem; font-weight: 700;
    letter-spacing: 0.16em; text-transform: uppercase;
    color: #58A0D8 !important; margin-top: 0.28rem;
  }
  body.is-printing #post-show-modal .metrics-print-date {
    display: block;
    font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace);
    font-size: 0.72rem; color: #333 !important; margin-top: 0.2rem;
  }
  /* Section heads (the Show subpanel already renders .metrics-section-head). */
  body.is-printing #post-show-modal .metrics-section-head {
    font-family: var(--font-label, 'Archivo', system-ui, sans-serif) !important;
    font-size: 0.8rem; font-weight: 700;
    letter-spacing: 0.11em; text-transform: uppercase;
    color: #58A0D8 !important;
    border-bottom: 1px solid #58A0D8 !important;
    padding-bottom: 0.3rem; margin: 1.2rem 0 0.6rem;
  }
  /* Tables: winners-list rows — uppercase teal-underlined header, hairline
     rows, mono values (a metrics table is data). */
  body.is-printing #post-show-modal .metrics-table {
    width: 100% !important; border-collapse: collapse !important;
    margin: 0 0 1.2rem;
  }
  body.is-printing #post-show-modal .metrics-table th {
    font-family: var(--font-label, 'Archivo', system-ui, sans-serif) !important;
    font-size: 0.66rem; font-weight: 700;
    letter-spacing: 0.08em; text-transform: uppercase;
    text-align: left; padding: 0.45rem 0.6rem;
    border-bottom: 2px solid #58A0D8 !important; color: #000 !important;
  }
  body.is-printing #post-show-modal .metrics-table td {
    font-family: var(--font-mono, 'JetBrains Mono', ui-monospace, monospace) !important;
    font-size: 0.74rem; padding: 0.42rem 0.6rem;
    border-bottom: 1px solid #cfcfcf !important; color: #111 !important;
  }
  /* Distribution bars — near/mid/far in the teal ramp (thin bars, low toner);
     bar length still carries the value for grayscale. */
  /* One color for every distribution bar in the printed PDF — the bar LENGTH
     carries the value and the row labels carry the near/mid/far distinction, so
     a single teal reads cleaner (and stays legible in grayscale) than a ramp. */
  body.is-printing #post-show-modal .metrics-dist-bar,
  body.is-printing #post-show-modal .metrics-dist-bar--mid,
  body.is-printing #post-show-modal .metrics-dist-bar--far,
  body.is-printing #post-show-modal .metrics-dist-bar--class { background: #58A0D8 !important; }

  /* ── Manage Classes → Sponsorship Summary ── */
  body.is-printing #classes-modal .modal-backdrop,
  body.is-printing #classes-modal .modal-header,
  body.is-printing #classes-modal .modal-tabs,
  body.is-printing #classes-modal .modal-error,
  body.is-printing #classes-modal .summary-actions,
  body.is-printing #classes-modal [data-panel="summary"] .settings-section-head { display: none !important; }
  body.is-printing #classes-modal .modal-card {
    background: #fff !important;
    box-shadow: none !important;
    border: 0 !important;
    max-width: 100% !important;
    max-height: none !important;
    overflow: visible !important;
    padding: 0 !important;
  }
  body.is-printing #classes-modal .modal-tab-panel.hidden { display: none !important; }
  body.is-printing #classes-modal,
  body.is-printing #classes-modal * {
    color: #000 !important;
    background: #fff !important;
    border-color: #ccc !important;
    text-shadow: none !important;
    box-shadow: none !important;
  }
  body.is-printing #classes-modal .metrics-section-head { page-break-after: avoid; }
  body.is-printing #classes-modal .metrics-stat-card { border: 1px solid #ccc !important; }
  /* Report letterhead — screen keeps the tab's own section head instead. */
  body.is-printing #classes-modal .summary-print-head {
    display: block !important;
    border-bottom: 2px solid #000 !important;
    padding-bottom: 0.6rem;
    margin-bottom: 1rem;
  }
  body.is-printing #classes-modal .summary-print-show {
    display: block;
    font-family: var(--font-label);
    font-size: 0.72rem; font-weight: 700;
    letter-spacing: 0.1em; text-transform: uppercase;
  }
  body.is-printing #classes-modal .summary-print-title {
    display: block;
    font-family: var(--font-label);
    font-size: 1.5rem; font-weight: 700;
    letter-spacing: 0.06em; text-transform: uppercase;
    margin: 0.15rem 0;
  }
  body.is-printing #classes-modal .summary-print-date {
    display: block;
    font-family: var(--font-mono);
    font-size: 0.72rem;
  }
}

.qr-nonjudged-banner {
  font-family: var(--font-label);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
  background: rgba(88, 160, 216, 0.10);
  border: 1px solid rgba(88, 160, 216, 0.35);
  border-top-width: 2px;
  padding: 0.65rem 0.9rem;
  margin: 0 0 1rem;
  text-align: center;
}
.qr-container {
  display: flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  padding: 1rem;
  margin: 0 auto 1rem;
  max-width: 300px;
}
.qr-container canvas { display: block; max-width: 100%; height: auto; }
.qr-url {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--text-muted);
  word-break: break-all;
  padding: 0.45rem 0.7rem;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  margin-bottom: 1rem;
}
.qr-contact {
  display: flex;
  flex-direction: column;
  gap: 0.4rem;
  padding: 0.75rem 0.9rem;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  margin-bottom: 1rem;
}
.qr-contact.hidden { display: none; }
.qr-contact-row { display: grid; grid-template-columns: 60px 1fr; gap: 0.5rem; align-items: baseline; }
.qr-contact-label {
  font-family: var(--font-label);
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}
.qr-contact-value { color: var(--text-primary); font-family: var(--font-body); font-size: 0.82rem; word-break: break-word; }

.qr-judging {
  margin-top: 1rem;
  padding: 0.85rem 1rem;
  background: var(--accent-dim);
  border: 1px solid var(--border-mid);
}
.qr-judging-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; }
.qr-judging-label {
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
}
.qr-judging-row { display: flex; justify-content: space-between; align-items: baseline; }
.qr-judging-key { font-family: var(--font-label); font-size: 0.72rem; color: var(--text-muted); }
.qr-judging-score { font-family: var(--font-label); font-size: 1.8rem; color: var(--accent-ink, var(--accent)); letter-spacing: 0.05em; }
.qr-judging-meta { margin: 0.3rem 0 0; color: var(--text-muted); font-size: 0.72rem; }

/* ═══════════════════════════════════════════════════════════
   WINNER + CLUB + HARDSHIP MODALS
═══════════════════════════════════════════════════════════ */
.winner-sponsor { color: var(--text-muted); font-style: italic; margin: 0 0 0.5rem; font-size: 0.85rem; }
.winner-sponsor.hidden { display: none; }
.winner-current { font-family: var(--font-label); font-size: 0.78rem; letter-spacing: 0.04em; color: var(--accent-ink, var(--accent)); margin: 0 0 1rem; }
.winner-field { margin-bottom: 0.75rem; }
.winner-field label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-ink, var(--accent));
  margin-bottom: 0.4rem;
}
.winner-field input[type="text"] {
  width: 100%;
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.5rem 0.65rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
}
.winner-field input[type="text"]:focus { outline: none; border-color: var(--accent); }

.club-modal-meta { color: var(--text-muted); font-size: 0.82rem; margin: 0 0 1rem; }
.club-member-list { list-style: none; display: flex; flex-direction: column; gap: 0.3rem; }
.club-member-list li { font-family: var(--font-body); font-size: 0.85rem; color: var(--text-primary); padding: 0.4rem 0; border-bottom: 1px solid var(--border); }

.hardship-modal-vehicle { font-family: var(--font-label); font-size: 0.78rem; color: var(--accent-ink, var(--accent)); margin: 0 0 0.5rem; }
.hardship-modal-text { font-family: var(--font-body); color: var(--text-secondary); line-height: 1.6; margin: 0 0 0.75rem; }
.hardship-modal-meta { font-size: 0.75rem; color: var(--text-muted); }

/* Hardship Picker — list of submissions surfaced from the dashboard
   tile header (// HARDSHIP AWARDS). Each row is a card with name,
   vehicle, text, and a Set-as-Winner button. */
.judge-card-label--btn {
  background: none;
  border: 0;
  padding: inherit;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
  display: block;
}
.judge-card-label--btn:hover { color: var(--accent-ink, var(--accent)); }

.hardship-picker-sub {
  font-size: 0.85rem;
  color: var(--text-secondary);
  margin: 0 0 1rem;
}
.hardship-picker-list {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  max-height: 60vh;
  overflow-y: auto;
  padding-right: 0.4rem;
}
.hardship-picker-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-areas:
    "name actions"
    "vehicle actions"
    "text text";
  gap: 0.25rem 1rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  background: var(--bg-card-alt);
}
.hardship-picker-row--winner {
  border-color: var(--accent);
  background: var(--accent-dim);
}
.hardship-picker-name    { grid-area: name; font-family: var(--font-label); font-weight: 700; color: var(--text-primary); font-size: 0.95rem; }
.hardship-picker-vehicle { grid-area: vehicle; font-size: 0.78rem; color: var(--accent-ink, var(--accent)); letter-spacing: 0.04em; }
.hardship-picker-text    { grid-area: text; color: var(--text-primary); font-size: 0.9rem; line-height: 1.5; padding-top: 0.4rem; white-space: pre-wrap; }
.hardship-picker-actions { grid-area: actions; align-self: center; }

/* ═══════════════════════════════════════════════════════════
   ADDRESS / AUTOCOMPLETE
═══════════════════════════════════════════════════════════ */
.address-wrap { position: relative; }
.address-suggestions {
  position: absolute;
  top: 100%; left: 0; right: 0;
  margin-top: 3px;
  background: var(--modal-bg);
  border: 1px solid var(--border-mid);
  max-height: 220px;
  overflow-y: auto;
  z-index: var(--z-menu);
  box-shadow: var(--shadow-lg);
}
.address-suggestions.hidden { display: none; }
.suggestion-item {
  display: block;
  width: 100%;
  padding: 0.5rem 0.75rem;
  text-align: left;
  background: transparent;
  border: none;
  border-bottom: 1px solid var(--border-dim);
  color: var(--text-primary);
  font-family: var(--font-body);
  cursor: pointer;
}
.suggestion-item:last-child { border-bottom: none; }
.suggestion-item:hover { background: var(--accent-dim); outline: none; }
.suggestion-primary { display: block; font-size: 0.85rem; }
.suggestion-secondary { display: block; font-size: 0.72rem; color: var(--text-muted); margin-top: 0.1rem; }

/* ═══════════════════════════════════════════════════════════
   MANAGE USERS
═══════════════════════════════════════════════════════════ */
.add-user-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.65rem;
  align-items: start; /* .mfield children carry their own label + input stack */
}
.add-user-form button[type="submit"] { grid-column: span 2; }
.add-user-form .custom-select { width: 100%; }
/* Fields use the shared .mfield vocabulary (uppercase label over a standard
   input) — the same voice as the Add Registration modal. Only the bare
   timeout input below keeps local styling. */
.add-user-timeout input {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.4rem 0.6rem;
  font-family: var(--font-body);
  font-size: 1rem;
}
.add-user-timeout input:focus { outline: none; border-color: var(--accent); }

/* Invite-link timeout — its own row under name/email/role. Judges: how long
   the emailed sign-in link works (their session lasts as long); admins: the
   set-password link. 0 = never expires. */
.add-user-timeout {
  grid-column: span 2;
  display: flex;
  align-items: center;
  gap: 0.55rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
  color: var(--text-secondary);
}
.add-user-form .add-user-timeout input { width: 6.5rem; flex: 0 0 auto; }
.add-user-timeout-hint { color: var(--text-muted); font-size: 0.8rem; white-space: nowrap; }

.users-hint { font-family: var(--font-body); color: var(--text-muted); font-size: 0.78rem; margin-top: 0.5rem; }
.users-hint code { background: var(--bg-inset); padding: 0.05rem 0.35rem; color: var(--accent-ink, var(--accent)); font-size: 0.8rem; border: 1px solid var(--border); }

/* Flex (not a fixed grid): judge rows carry one more chip than admin rows
   (the judging-time metric), and flex keeps both shapes tidy. Actions are
   pushed right via .user-edit's auto margin. */
.user-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0.6rem;
  align-items: center;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--border);
  background: var(--bg-inset);
}
/* Identity is DATA, not a label — body face like every other name in the
   admin (table cells, distance list, club modal), per the design doc. */
.user-row-name { font-family: var(--font-body); font-size: 0.95rem; font-weight: 600; color: var(--text-primary); min-width: 0; flex: 1 1 12rem; }
/* Meta (role badge, pace, last login) sits at the right of the first line. */
.user-row-meta { display: flex; align-items: center; gap: 0.45rem; margin-left: auto; flex: 0 0 auto; }
/* Action buttons take their own full-width line, right-aligned, so every row's
   toolbar lines up no matter how many buttons it has (admin rows have 3, judge
   rows 5) — instead of a ragged right-pushed group on the same line. */
.user-row-actions { flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 0.4rem; justify-content: flex-end; }
.user-row .user-edit { margin-left: 0; }
/* Per-judge pace metric (avg scan→submit), fed by /api/admin/metrics. */
.user-row-judgetime {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  color: var(--accent-ink, var(--accent));
  background: var(--accent-dim);
  border: 1px solid var(--border-mid);
  padding: 0.18rem 0.5rem;
  white-space: nowrap;
}
/* Row actions are ghosts — solid orange is reserved for the one primary
   action per surface (+ Add User). Delete stays a red outline. */
.user-row .btn-save {
  background: transparent;
  color: var(--accent-ink, var(--accent));
  border: 1px solid var(--border-mid);
}
.user-row .btn-save:hover { background: var(--accent-dim); border-color: var(--accent); }
.user-row .btn-delete {
  background: transparent;
  color: var(--error);
  border: 1px solid var(--error-border);
}
.user-row .btn-delete:hover { background: var(--error-bg); }
.user-row .btn-save, .user-row .btn-delete {
  /* Match the registrants-table row actions exactly (.btn-edit/.btn-delete-row). */
  padding: 0.28rem 0.6rem;
  font-size: 0.88rem;
  min-height: 44px;
}
.user-name-display { display: flex; flex-direction: column; gap: 0.1rem; line-height: 1.25; min-width: 0; }
.user-name-full { white-space: normal; word-break: break-word; }
.user-row-handle { display: block; font-family: var(--font-body); font-size: 0.8rem; font-weight: 400; color: var(--text-muted); letter-spacing: normal; margin: 0; }
.user-row-last-login { font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-muted); white-space: nowrap; }
/* Role reads as a small label beside the name (not a colored badge), so admin
   vs judge is told by the word, not a second hue. */
.user-name-line { display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap; }
.user-name-role {
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-ink);
}
.user-row-flag { font-size: 0.65rem; color: var(--text-muted); font-style: italic; }

.user-name-edit-form { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; }
.user-name-edit-form input {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.3rem 0.5rem;
  font-family: var(--font-body);
  font-size: 1rem; /* design-system field size */
  width: 110px;
}
.user-name-edit-form input:focus { outline: none; border-color: var(--accent); }

@media (max-width: 640px) {
  .add-user-form { grid-template-columns: 1fr; }
  .add-user-form button[type="submit"], .add-user-form .custom-select, .add-user-timeout { grid-column: 1; }
  .add-user-timeout { flex-wrap: wrap; }
  /* A 7-column grid can't fit a phone. Switch to a wrapping flex row: the name
     takes the top line, then role / last-login / action buttons flow and wrap
     beneath it (buttons grow to fill) so nothing spills past the modal edge. */
  .user-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.45rem 0.5rem;
  }
  .user-row-name { flex: 1 1 100%; min-width: 0; overflow-wrap: anywhere; }
  .user-row-role { flex: 0 0 auto; }
  .user-row-last-login { flex: 0 0 auto; margin-left: auto; }
  .user-row .btn-save,
  .user-row .btn-delete {
    flex: 1 1 auto; min-width: 0; min-height: 44px; white-space: nowrap;
  }
}

/* ═══════════════════════════════════════════════════════════
   SESSIONS
═══════════════════════════════════════════════════════════ */
.session-row {
  display: grid;
  grid-template-columns: 1fr auto;
  grid-template-rows: auto auto;
  align-items: center;
  gap: 0.2rem 1rem;
  padding: 0.65rem 0.75rem;
  border-bottom: 1px solid var(--border-dim);
}
.session-row--current { background: var(--accent-dim); border-top: 2px solid var(--accent); }
.session-row-main { display: flex; align-items: center; gap: 0.6rem; flex-wrap: wrap; }
.session-username { font-family: var(--font-label); font-weight: 600; font-size: 0.9rem; color: var(--text-primary); }
.session-badge {
  font-family: var(--font-body);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  background: var(--accent-dim);
  color: var(--accent-ink, var(--accent));
  border: 1px solid var(--border-mid);
  padding: 0.1rem 0.35rem;
}
.session-device { color: var(--text-secondary); font-family: var(--font-body); font-size: 0.8rem; }
.session-ip { color: var(--text-muted); font-family: var(--font-mono); font-size: 0.7rem; }
.session-row-meta { grid-column: 1; display: flex; gap: 1rem; font-size: 0.7rem; color: var(--text-muted); flex-wrap: wrap; font-family: var(--font-body); }
.session-revoke { grid-column: 2; grid-row: 1 / 3; align-self: center; }

/* ═══════════════════════════════════════════════════════════
   METRICS MODAL
═══════════════════════════════════════════════════════════ */
.metrics-table { width: 100%; border-collapse: collapse; font-size: 0.85rem; }
.metrics-table th {
  text-align: left;
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid var(--border-mid);
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
}
.metrics-table td { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--border-dim); color: var(--text-primary); font-family: var(--font-body); }
.metrics-empty { color: var(--text-muted); font-style: italic; padding: 1.5rem 0.75rem; text-align: center; font-family: var(--font-body); }
.metrics-judge-row { cursor: pointer; }
.metrics-judge-row:hover td { background: var(--row-hover); }
.metrics-judge-name { font-weight: 600; }
.metrics-expand-icon { color: var(--accent-ink, var(--accent)); font-size: 0.7rem; margin-left: 0.3rem; display: inline-block; transition: transform 0.15s; }
.metrics-breakdown-row td { background: var(--bg-inset); }
.metrics-breakdown-cell { padding: 0.5rem 0.75rem 0.75rem 1.5rem !important; }
.metrics-cls-chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.metrics-cls-chip { background: var(--accent-dim); border: 1px solid var(--border-mid); padding: 0.2rem 0.5rem; font-family: var(--font-body); font-size: 0.7rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-ink, var(--accent)); }
.metrics-show-grid { display: grid; grid-template-columns: repeat(4,1fr); gap: 0.75rem; margin-bottom: 1.25rem; }
@media (max-width: 640px) { .metrics-show-grid { grid-template-columns: repeat(2,1fr); } }
.metrics-stat-card { background: var(--bg-inset); border: 1px solid var(--border); padding: 0.9rem 1rem; text-align: center; }
.metrics-stat-card--accent { border-color: var(--accent); }
.metrics-stat-num { font-family: var(--font-label); font-size: 2rem; color: var(--accent-ink, var(--accent)); line-height: 1; letter-spacing: 0.04em; }
.metrics-stat-label { font-family: var(--font-label); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); margin-top: 0.3rem; }
.metrics-section-head { font-family: var(--font-label); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-ink, var(--accent)); margin: 1.1rem 0 0.5rem; }
.metrics-highlight-row { display: flex; align-items: baseline; gap: 0.75rem; background: var(--accent-dim); border: 1px solid var(--border-mid); padding: 0.5rem 0.85rem; }
.metrics-highlight-name { font-family: var(--font-body); font-weight: 600; color: var(--text-primary); }
.metrics-highlight-count { color: var(--accent-ink, var(--accent)); font-family: var(--font-mono); font-size: 0.85rem; }
.metrics-dist-bars { display: flex; flex-direction: column; gap: 0.5rem; }
/* Class breakdown lists (Biggest / Smallest): show ~5 rows then scroll. Each
   row is ~1.6rem incl. gap, so cap at 5 * 1.6rem ≈ 8rem of body. */
.metrics-class-scroll {
  max-height: 11rem;
  overflow-y: auto;
  padding-right: 0.4rem;
}
.metrics-dist-row { display: flex; align-items: center; gap: 0.6rem; }
.metrics-dist-label { min-width: 140px; font-family: var(--font-body); font-size: 0.78rem; color: var(--text-secondary); }
.metrics-dist-bar-wrap { flex: 1; background: var(--bg-inset); border: 1px solid var(--border); height: 8px; overflow: hidden; }
.metrics-dist-bar { height: 100%; background: var(--accent); min-width: 2px; }
.metrics-dist-bar--mid { background: var(--text-secondary); }
.metrics-dist-bar--far { background: var(--text-muted); }
.metrics-dist-count { font-family: var(--font-mono); font-size: 0.75rem; color: var(--text-primary); min-width: 2.5rem; text-align: right; }
.metrics-class-pct { color: var(--text-muted); font-size: 0.7rem; }
.metrics-dist-label--class { min-width: 160px; }
.metrics-dist-bar--class { background: var(--accent); opacity: 0.7; }
.metrics-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; margin-top: 0.25rem; }
@media (max-width: 480px) { .metrics-two-col { grid-template-columns: 1fr; } }
.metrics-mini-list { display: flex; flex-direction: column; gap: 0.3rem; }
.metrics-state-row { display: flex; justify-content: space-between; font-family: var(--font-body); font-size: 0.8rem; padding: 0.25rem 0; border-bottom: 1px solid var(--border-dim); color: var(--text-primary); }
.metrics-state-row span:last-child { color: var(--accent-ink, var(--accent)); font-family: var(--font-mono); font-weight: 600; }

/* Specialty Awards dashboard card — "no winner yet" state on a winner slot. */
.sa-winner--empty { color: var(--text-muted) !important; background: transparent !important; border: none !important; font-style: italic; }
/* Nomination leader — its own line under the award, like a club row's note,
   so a long car name never squeezes the award name. */
.sa-item:has(.sa-leader) { flex-wrap: wrap; row-gap: 0.15rem; }
.sa-leader {
  order: 5; flex: 0 0 100%;
  font-family: var(--font-mono); font-size: 0.72rem;
  color: var(--accent-ink, var(--accent));
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ═══════════════════════════════════════════════════════════
   POST SHOW
═══════════════════════════════════════════════════════════ */

/* ═══════════════════════════════════════════════════════════
   BLAST EMAIL
═══════════════════════════════════════════════════════════ */
.blast-meta { font-family: var(--font-body); font-size: 0.85rem; color: var(--text-secondary); margin: 0 0 1.25rem; }
.blast-field { margin-bottom: 1rem; }
.blast-label {
  display: block;
  font-family: var(--font-label);
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.4rem;
}
.blast-hint { font-family: var(--font-body); text-transform: none; letter-spacing: 0; font-size: 0.75rem; font-weight: 400; }
.blast-hint code { background: var(--accent-dim); color: var(--accent-ink, var(--accent)); padding: 0.05rem 0.3rem; border: 1px solid var(--border-mid); font-size: 0.78rem; }
.blast-input, .blast-textarea {
  width: 100%;
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.55rem 0.7rem;
  font-family: var(--font-body);
  font-size: 0.9rem;
}
.blast-textarea { resize: vertical; min-height: 180px; }
.blast-input:focus, .blast-textarea:focus { outline: none; border-color: var(--accent); }
.blast-status { font-family: var(--font-body); font-size: 0.85rem; color: var(--success-ink); margin: 0.5rem 0 0; }
.blast-status--error { color: var(--error); }

/* ═══════════════════════════════════════════════════════════
   SURVEY
═══════════════════════════════════════════════════════════ */
.survey-url-row {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  background: var(--bg-inset);
  border: 1px solid var(--border-mid);
  padding: 0.6rem 0.75rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.survey-url-label { font-family: var(--font-label); font-size: 0.6rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); flex-shrink: 0; }
.survey-url-code { flex: 1; color: var(--accent-ink, var(--accent)); font-family: var(--font-mono); font-size: 0.82rem; word-break: break-all; min-width: 0; }
.survey-q-list { margin-bottom: 0.75rem; }
.survey-q-row {
  display: grid;
  grid-template-columns: 1fr 130px auto auto auto;
  gap: 0.45rem;
  align-items: center;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--border-dim);
}
.survey-q-row:last-child { border-bottom: none; }
.survey-q-label-input, .survey-q-type {
  background: var(--input-bg);
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  padding: 0.4rem 0.55rem;
  font-family: var(--font-body);
  font-size: 0.85rem;
}
.survey-q-label-input:focus, .survey-q-type:focus { outline: none; border-color: var(--accent); }
.survey-q-add-row { margin-bottom: 1rem; }
.survey-responses-layout { display: grid; grid-template-columns: 1fr 210px; gap: 1.25rem; align-items: start; }
@media (max-width: 640px) { .survey-responses-layout { grid-template-columns: 1fr; } }
.survey-averages-panel { position: sticky; top: 0; background: var(--accent-dim); border: 1px solid var(--border-mid); padding: 1rem; }
.survey-avg-title { font-family: var(--font-label); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.15em; text-transform: uppercase; color: var(--accent-ink, var(--accent)); margin: 0 0 0.85rem; }
.survey-avg-item { margin-bottom: 1rem; }
.survey-avg-item:last-child { margin-bottom: 0; }
.survey-avg-label { font-family: var(--font-body); font-size: 0.72rem; color: var(--text-secondary); line-height: 1.35; margin: 0 0 0.2rem; }
.survey-avg-value { font-family: var(--font-label); font-size: 1.8rem; color: var(--text-primary); margin: 0; letter-spacing: 0.04em; }
.survey-avg-sub { font-family: var(--font-body); font-size: 0.65rem; color: var(--text-muted); display: block; margin-top: 0.1rem; }
.survey-resp-row { border: 1px solid var(--border); padding: 0.75rem 1rem; margin-bottom: 0.6rem; }
.survey-resp-who { font-family: var(--font-label); font-size: 0.75rem; font-weight: 600; color: var(--accent-ink, var(--accent)); margin: 0 0 0.35rem; letter-spacing: 0.04em; }
.survey-resp-vehicle { font-family: var(--font-body); font-size: 0.78rem; color: var(--text-muted); margin: 0 0 0.6rem; }
.survey-resp-answers { list-style: none; padding: 0; margin: 0; }
.survey-resp-answers li { font-family: var(--font-body); font-size: 0.82rem; color: var(--text-secondary); padding: 0.2rem 0; border-bottom: 1px solid var(--border-dim); }
.survey-resp-answers li:last-child { border-bottom: none; }
.survey-resp-answers li strong { color: var(--text-primary); }

/* ═══════════════════════════════════════════════════════════
   RUBRIC EDITOR
═══════════════════════════════════════════════════════════ */
.class-rubric-panel { background: var(--bg-inset); border: 1px solid var(--border); border-top: none; margin-bottom: 0.5rem; }
.rubric-editor { padding: 1rem 1.25rem 1.25rem; }
.rubric-editor-hint { font-family: var(--font-label); font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--accent-ink, var(--accent)); margin: 0 0 0.75rem; }
.rubric-cat-list { display: flex; flex-direction: column; gap: 0.35rem; margin-bottom: 0.75rem; }
.rubric-cat-row { display: flex; align-items: center; gap: 0.5rem; background: var(--bg-inset); border: 1px solid var(--border); padding: 0.3rem 0.5rem; }
.rubric-cat-handle { color: var(--text-muted); cursor: grab; font-size: 0.9rem; }
.rubric-cat-name { flex: 1; background: transparent; border: none; color: var(--text-primary); font-family: var(--font-body); font-size: 0.85rem; padding: 0.2rem 0.25rem; outline: none; }
.rubric-cat-name:focus { color: var(--accent-ink, var(--accent)); box-shadow: 0 0 0 2px var(--accent); }
.rubric-scale-row { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.75rem; }
.rubric-scale-label { font-family: var(--font-label); font-size: 0.6rem; font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text-muted); }
.rubric-scale-input { width: 4rem; background: var(--bg-inset); border: 1px solid var(--border); color: var(--text-primary); font-family: var(--font-mono, var(--font-body)); font-size: 0.85rem; padding: 0.3rem 0.4rem; text-align: center; }
.rubric-scale-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent); }
.rubric-scale-to, .rubric-scale-hint { font-family: var(--font-label); font-size: 0.65rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }
.modal-score-range { font-family: var(--font-body); font-size: 0.75rem; color: var(--text-muted); margin: 0.15rem 0 0.4rem; }
.rubric-editor-actions { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.rubric-status { font-family: var(--font-label); font-size: 0.65rem; margin-top: 0.5rem; display: block; }
.rubric-status.saved  { color: var(--success-ink); }
.rubric-status.saving { color: var(--text-muted); }
.rubric-status.error  { color: var(--error); }
/* Idle state. The Save button is gone, so the line's resting job is to say
   the form commits itself — quieter than the saved/saving/error states. */
.rubric-status--idle { color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.08em; }
/* Per-vehicle-type rubric sub-tabs (Car / Truck / Bike) in the Rubric editor. */
.rubric-type-tabs { display: flex; gap: 0.25rem; margin-bottom: 0.75rem; flex-wrap: wrap; }
.rubric-type-tab {
  font-family: var(--font-body); font-size: 0.85rem; font-weight: 600; letter-spacing: 0.01em;
  text-transform: none; color: var(--text-muted); background: var(--bg-inset);
  border: 1px solid var(--border); padding: 0.4rem 0.9rem; cursor: pointer;
}
.rubric-type-tab:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--accent); }
.rubric-type-tab--active { color: var(--on-accent, #fff); background: var(--accent); border-color: var(--accent); }
.rubric-type-panel.hidden { display: none; }
.rubric-editor-actions--save { margin-top: 0.75rem; }

/* ═══════════════════════════════════════════════════════════
   SPECIALTY / HARDSHIP / PEOPLES CHOICE (judge.css overrides)
═══════════════════════════════════════════════════════════ */
#awards-grid .club-name {
  font-family: var(--font-label);
  font-size: 0.85rem;
  font-weight: 600;
  letter-spacing: 0.03em;
}

/* Post-show button accent */
.btn-post-show {
  background: var(--accent-dim);
  border: 1px solid var(--border-mid);
  color: var(--accent-ink, var(--accent));
}
.btn-post-show:hover { background: var(--accent); color: var(--on-accent, #0D0D12); }

.btn-test-payment {
  background: rgba(88, 160, 216, 0.12);
  border: 1px dashed rgba(88, 160, 216, 0.5);
  color: var(--accent-ink, var(--accent));
}
.btn-test-payment:hover { background: rgba(88, 160, 216, 0.25); border-color: var(--accent); }

/* ═══════════════════════════════════════════════════════════
   NO-PRINTER CONFIRMATION — full-screen "write this on the card" panel
   shown after a registration saves when no-printer mode is on. Big,
   high-contrast, glanceable in sunlight; Archivo Black number to match the
   brand wordmark.
═══════════════════════════════════════════════════════════ */
.reg-confirm {
  position: fixed; inset: 0; z-index: var(--z-menu); /* takeover confirmation: above the reg modal */
  display: flex; align-items: center; justify-content: center;
  padding: 1rem;
  background: rgba(13, 13, 18, 0.97);
}
.reg-confirm.hidden { display: none; }
.reg-confirm-card {
  position: relative;
  width: 100%; max-width: 460px; text-align: center;
  background: var(--bg-card);
  border: 1px solid var(--border-mid);
  border-top: 4px solid var(--accent);
  padding: 1.5rem 1.25rem 1.75rem;
}
.rc-close {
  position: absolute; top: 0.25rem; right: 0.25rem;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 44px; min-height: 44px;
  background: transparent; border: none;
  color: var(--text-muted); font-size: 1.5rem; line-height: 1;
  cursor: pointer; transition: color 0.15s;
}
.rc-close:hover { color: var(--text-primary); }
.rc-eyebrow {
  font-family: var(--font-label); font-weight: 700;
  font-size: 0.72rem; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--text-muted);
}
.rc-entry-label {
  font-family: var(--font-label); font-weight: 700;
  font-size: 0.9rem; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--accent-ink, var(--accent)); margin-top: 1rem;
}
.rc-number {
  font-family: var(--font-wordmark); font-weight: 400;
  font-size: clamp(5rem, 34vw, 9rem); line-height: 0.92;
  letter-spacing: 0.02em; color: var(--text-primary);
}
/* The dash-card number stamps in — the hot-stamped-tag brand metaphor applied
   to the exact moment a number is issued. Re-fires on every new registration
   because the overlay re-toggles display. */
.reg-confirm:not(.hidden) .rc-number {
  animation: rc-stamp 0.35s cubic-bezier(0.22, 1, 0.36, 1);
}
@keyframes rc-stamp {
  0%   { opacity: 0; transform: scale(1.18); }
  55%  { opacity: 1; transform: scale(0.97); }
  100% { transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
  .reg-confirm:not(.hidden) .rc-number { animation: none; }
}
.rc-name {
  font-family: var(--font-label); font-weight: 700;
  font-size: clamp(1.35rem, 7vw, 2rem); letter-spacing: 0.02em;
  text-transform: uppercase; color: var(--text-primary); margin-top: 0.4rem;
}
.rc-vehicle {
  font-family: var(--font-body);
  font-size: clamp(1.05rem, 5vw, 1.35rem); color: var(--text-secondary);
  margin-top: 0.3rem;
}
.rc-vehicle:empty { display: none; }
.rc-class {
  font-family: var(--font-mono); font-size: 0.95rem;
  color: var(--accent-ink, var(--accent)); margin-top: 0.4rem;
}
.rc-class:empty { display: none; }
/* Prior-show DQ warning on the add-confirmation card. Full border + tinted fill
   (no side-stripe), square corners, uppercase Archivo label over the sentence.
   Mirrors the roster .dq-warn tokens (vetted for both admin themes): HIGH is the
   loud danger red; MEDIUM ("verify") steps down to the caution/accent tone. */
.rc-dq-warn {
  margin-top: 1.25rem;
  padding: 0.8rem 0.9rem;
  border: 1px solid var(--error);
  background: var(--error-bg);
  color: var(--error);
  font-family: var(--font-body);
  font-size: 0.9rem;
  line-height: 1.4;
  text-align: left;
}
.rc-dq-warn.hidden { display: none; }
.rc-dq-warn strong {
  display: block;
  margin-bottom: 0.25rem;
  font-family: var(--font-label);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
/* Always red — see .dq-warn--medium. */
.rc-dq-warn--medium {
  border-color: var(--error);
  background: var(--error-bg);
  color: var(--error);
}
.rc-actions { display: flex; gap: 0.75rem; margin-top: 1.75rem; }
.rc-btn { flex: 1; padding: 1rem; font-size: 1rem; min-height: 52px; }
/* Phones: stack the actions, "+ Add Another" full-width on top — the gate
   loop's next action is the biggest thing under the operator's thumb. */
@media (max-width: 480px) {
  .rc-actions { flex-direction: column; }
  .rc-btn--primary { order: -1; }
}
.rc-print-link {
  margin-top: 1rem; background: none; border: none;
  color: var(--text-muted);
  font-family: var(--font-label); font-size: 0.8rem;
  letter-spacing: 0.06em; text-transform: uppercase;
  text-decoration: underline; cursor: pointer;
}
.rc-print-link:hover { color: var(--text-secondary); }

/* ═══════════════════════════════════════════════════════════
   RESPONSIVE
═══════════════════════════════════════════════════════════ */
/* ── Registrations roster as CARDS on phones ───────────────────────────────
   A 9-column table can't work on a ~360px screen. Below 760px the #reg-table
   becomes a stack of cards — one car per box, each field labelled and going
   down — so it stays readable and tappable on the day of the show. Scoped to
   #reg-table so the metrics/awards tables are untouched. */
@media (max-width: 760px) {
  .table-wrap {
    overflow-x: visible;
    border: none; background: none; box-shadow: none;
    min-height: 0;
  }
  #reg-table, #reg-table tbody { display: block; width: 100%; }
  #reg-table thead { display: none; } /* labels move onto each cell via ::before */
  #reg-table tr {
    display: block;
    background: var(--bg-card);
    border: 1px solid var(--border-mid);
    margin-bottom: 0.75rem;
    padding: 0.55rem 0.9rem 0.7rem;
  }
  #reg-table td {
    display: block;
    padding: 0.32rem 0;
    border: none;
    text-align: left !important;
    white-space: normal !important;
    overflow-wrap: anywhere; /* long emails/club names wrap instead of overflowing */
    font-size: 0.95rem;
  }
  /* Field label (from data-label) sits above each value, except the headline
     entry-number cell and the actions cell. */
  #reg-table td:not(.col-regnum):not(.col-actions)::before {
    content: attr(data-label);
    display: block;
    font-family: var(--font-label); font-weight: 700;
    font-size: 0.62rem; letter-spacing: 0.1em; text-transform: uppercase;
    color: var(--text-muted);
    margin-bottom: 0.05rem;
  }
  /* Entry number = the card headline. */
  #reg-table td.col-regnum {
    font-family: var(--font-wordmark);
    font-size: 1.7rem; line-height: 1; color: var(--text-primary);
    padding: 0 0 0.5rem; margin-bottom: 0.35rem;
    border-bottom: 1px solid var(--border-mid);
  }
  /* Actions become full-width, thumb-sized buttons. */
  #reg-table td.col-actions { padding-top: 0.65rem; }
  #reg-table td.col-actions .action-btns { display: flex; gap: 0.5rem; }
  #reg-table td.col-actions .action-btns button {
    flex: 1; min-height: 44px; font-size: 0.85rem;
  }
  /* Empty / loading state spans the card cleanly. */
  #reg-table td.table-empty { text-align: center !important; padding: 2rem 0; color: var(--text-muted); }
  #reg-table td.table-empty::before { display: none; }
  #reg-table .skel-row td { padding: 0.5rem 0; }
}

@media (max-width: 640px) {
  .modal-grid { grid-template-columns: 1fr; }
  .add-user-form { grid-template-columns: 1fr; } /* same single-column collapse as .modal-grid */
  .add-user-form button[type="submit"], .add-user-timeout { grid-column: 1; }
  .mfield--full { grid-column: 1; }
  .admin-main { padding: 0 1rem 2rem; }
  .stats-row { margin: 0 -1rem 1.75rem; }
  /* 16px inputs stop iOS Safari from auto-zooming on focus; 44px min height
     gives a comfortable touch target for registering cars from a phone. */
  .mfield input, .mfield select,
  .settings-text-input { font-size: 16px; min-height: 44px; }

  /* ── Nav: stop the wordmark + show name + buttons from overlapping ──
     Auto height, wraps; the show name drops onto its own line under the
     wordmark; Settings/Logout stay a reachable row on the right. */
  .admin-nav {
    height: auto; min-height: 56px;
    flex-wrap: nowrap;            /* logo stays left, buttons stay right */
    align-items: flex-start;
    gap: 0.6rem;
    padding: 0.55rem 1rem;
  }
  .admin-nav-title {
    font-size: 1.3rem;
    flex: 1 1 auto; min-width: 0; line-height: 1.15;
  }
  /* Show name drops onto its own line under the ENTRYLANE wordmark.
     Word-boundary wrapping only — `anywhere` let flexbox squeeze the
     title to a one-character column of vertical letters. */
  .admin-nav-title .kw-show {
    margin-top: 0.1rem;
    font-size: 0.92rem; color: var(--text-secondary);
    overflow-wrap: break-word;
  }
  /* Settings/Logout pinned to the right; extra buttons (Switch Show, tier
     badge) wrap beneath them, still right-aligned, instead of pushing the
     whole group onto its own row. */
  .admin-nav-right {
    flex: 0 0 auto;
    flex-wrap: wrap;
    justify-content: flex-end;
    gap: 0.4rem;
  }
  /* Identity line is secondary day-of; hide it on phones to save the row. */
  .admin-nav-user { display: none; }
  .btn-settings, .btn-logout {
    min-height: 44px; padding: 0.4rem 0.75rem; font-size: 0.8rem;
  }

  /* ── Toolbar: search full-width, action buttons grow to fill + tappable ── */
  .toolbar { flex-wrap: wrap; gap: 0.5rem; }
  .toolbar .search-input { flex: 1 1 100%; max-width: 100%; min-height: 44px; }
  .toolbar > .btn-add,
  .toolbar > .post-show-wrap { flex: 1 1 8rem; }
  .toolbar > .btn-add { min-height: 44px; }
  .post-show-wrap > .btn-add { width: 100%; min-height: 44px; }
}

/* ══════════════════════════════════════════════════════════════════════
   UNIFIED DROPDOWN CONTROL
   Every <select> across the admin console shares one look — same border,
   chevron, typeface, and 44px height — so the page reads as one system on a
   PC. Placed last so it settles the per-context select rules above onto a
   single appearance (they keep only their own width/max-width).
   On a phone this styles the CLOSED control only: tapping any native <select>
   always opens the platform's own picker, which is exactly what we want there.
   ══════════════════════════════════════════════════════════════════════ */
select,
select.settings-text-input,
.mfield select,
.class-select,
.class-sponsor-select {
  appearance: none;
  -webkit-appearance: none;
  background-color: var(--input-bg);
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'><path fill='%23888' d='M6 8L0 0h12z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 0.7rem center;
  border: 1px solid var(--border-mid);
  color: var(--text-primary);
  font-family: var(--font-body);
  font-size: 1rem; /* design-system field size; also ≥16px so iOS doesn't zoom */
  min-height: 44px;
  padding: 0.5rem 2rem 0.5rem 0.75rem;
  line-height: 1.3;
  cursor: pointer;
  transition: border-color 0.15s;
}
select:focus,
.mfield select:focus,
.class-select:focus,
.class-sponsor-select:focus { outline: none; border-color: var(--accent); }

/* ── Revenue tab (Show Setup) ─────────────────────────────────────────── */
.metrics-stat-num.summary-stat-num--unpaid { color: var(--error); }

/* Net running-total banner — a full-width stat tile in the same box language
   as the stream boxes below it, promoted with size and the stamped shadow.
   IN THE BLACK / IN THE RED tag gives the verdict without reading a number. */
.rev-net-banner {
  background: var(--bg-inset);
  border: 1px solid var(--accent);
  box-shadow: 0 6px 0 rgba(0,0,0,0.07);
  padding: 1.05rem 1.2rem 1.1rem;
  text-align: center;
  margin: 0.9rem 0 0.75rem;
}
.rev-net-banner-num {
  font-family: var(--font-display); font-weight: 400;
  font-size: clamp(2.1rem, 5vw, 3rem); line-height: 1.05; letter-spacing: 0.01em;
  color: var(--text-primary); margin: 0.25rem 0 0.35rem;
}
.rev-net-banner-num--red { color: var(--error); }
/* The ± band under the projected net — it's an estimate, and says so. */
.rev-net-banner-range {
  font-family: var(--font-mono); font-size: 0.8rem;
  color: var(--text-secondary); margin: 0 0 0.6rem;
}
.rev-net-banner-row { display: flex; align-items: center; justify-content: center; gap: 0.7rem; flex-wrap: wrap; }
.rev-tag {
  display: inline-block;
  font-family: var(--font-label); font-weight: 700; font-size: 0.62rem;
  letter-spacing: 0.14em; text-transform: uppercase; padding: 0.24rem 0.55rem;
}
.rev-tag--black { color: var(--success-ink, var(--success)); border: 1px solid var(--success-border); background: var(--success-dim); }
.rev-tag--red   { color: var(--error); border: 1px solid var(--error-border); background: var(--error-bg); }
.expense-row-right { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; justify-content: flex-end; }
/* Description + price are live inputs now (blur-to-save, like sponsor rows);
   the description takes the row's slack, the $ box stays sponsor-fee sized. */
.expense-row { gap: 0.5rem; align-items: center; flex-wrap: wrap; padding: 0.35rem 0; }
.expense-desc-input { flex: 1 1 12rem; min-width: 9rem; }
.expense-receipt-link { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
/* Five stream boxes share the row on desktop (sponsorships, pre-reg, day-of,
   merch, expenses) and dollar amounts carry cents, so the figure scales with
   the viewport instead of clipping at the card edge. */
#revenue-stats .metrics-stat-card { padding: 0.9rem 0.6rem; min-width: 0; }
#revenue-stats .metrics-stat-num { font-size: clamp(1.45rem, 2.1vw, 2rem); letter-spacing: 0.02em; white-space: nowrap; }
#revenue-fee-form, #revenue-merch-form { margin-bottom: 1.4rem; }
/* Entry fee + processor fee share one row: label / field / label / field.
   Fields are short numbers, so they don't take the add-row's 220px flex
   basis; the second label gets a little air from the first field. */
.revenue-fee-row .settings-row-title { align-self: center; }
.revenue-fee-row .revenue-fee-field { flex: 0 1 8rem; min-width: 6rem; }
.revenue-fee-row .class-add-status:empty { display: none; }
.revenue-fee-row label + .revenue-fee-field + .class-add-status + label { margin-left: 1rem; }
@media (max-width: 640px) {
  .revenue-fee-row label + .revenue-fee-field + .class-add-status + label { margin-left: 0; }
}

/* ── Activity feed card (dashboard) ─────────────────────────────────────
   Two voices in one stream. Events read as telemetry rows: mono clock
   stamp · tracked-caps actor · plain sentence. Notes read as stamped
   charcoal tags — cream on dark with the solid offset shadow — so the
   team's own words stand out from the machine log. */
.activity-compose { display: flex; gap: 0.5rem; margin-bottom: 0.8rem; }
.activity-compose input { flex: 1 1 auto; min-width: 0; }
.activity-feed { max-height: 340px; overflow-y: auto; padding-right: 0.15rem; }

/* Event rows flow as one log sentence — stamp, actor, text inline. The
   old per-row grid started the text at a different x on every row (actor
   widths vary), which read as broken spacing between rows. */
.activity-row {
  padding: 0.44rem 0; border-bottom: 1px solid var(--border);
  font-size: 0.88rem; line-height: 1.5; overflow-wrap: anywhere;
  color: var(--text-primary);
}
.activity-row:last-child { border-bottom: 0; }
.activity-time {
  font-family: 'JetBrains Mono', monospace; font-size: 0.66rem; font-weight: 500;
  letter-spacing: 0.04em; color: var(--text-muted); white-space: nowrap;
  font-variant-numeric: tabular-nums; margin-right: 0.45rem;
}
.activity-actor {
  /* Same size as the sentence it opens — the shrunken tracked-caps take
     made the username read like a footnote. Bold alone carries it. */
  font-family: 'Archivo', sans-serif; font-weight: 700;
  color: var(--text-primary); margin-right: 0.35rem;
}
.activity-text { color: var(--text-primary); }

/* Notes share the event rows' face and ink exactly; only the Pin/Delete
   controls and the PINNED chip mark them as the team's own words. */
.activity-row--note {
  display: flex; align-items: baseline; gap: 0.6rem; justify-content: space-between;
}
.activity-row-main { min-width: 0; overflow-wrap: anywhere; }
.activity-row-tools { display: flex; align-items: baseline; gap: 0.55rem; flex: 0 0 auto; }
.activity-note-pinflag {
  font-family: 'JetBrains Mono', monospace; font-size: 0.58rem; font-weight: 700;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: #0D0D12; background: var(--accent, #58A0D8); padding: 0.1rem 0.35rem;
}
.activity-act {
  background: none; border: 0; padding: 0.1rem; cursor: pointer;
  font-family: 'JetBrains Mono', monospace; font-size: 0.6rem; font-weight: 700;
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-muted);
}
.activity-act:hover { color: var(--accent-ink, var(--accent)); }
.activity-empty { color: var(--text-muted); font-style: italic; font-size: 0.88rem; margin: 0.3rem 0; line-height: 1.5; }

/* Escape hatches from admin-kinetic's `.judge-card-body * { font/color
   !important }` nuke — the stamps/controls are mono and quieter than the
   forced text-primary, so they need matching-strength declarations. */
[data-theme] .judge-card-body .activity-time,
[data-theme] .judge-card-body .activity-act,
[data-theme] .judge-card-body .activity-note-pinflag {
  font-family: 'JetBrains Mono', ui-monospace, Menlo, monospace !important;
}
[data-theme] .judge-card-body .activity-time { color: var(--text-muted) !important; }
[data-theme] .judge-card-body .activity-act { color: var(--text-muted) !important; }
[data-theme] .judge-card-body .activity-act:hover { color: var(--accent-ink, var(--accent)) !important; }
[data-theme] .judge-card-body .activity-note-pinflag { color: #0D0D12 !important; }

/* Add-class box moved above the list (Classes tab): swap the bottom-anchor
   border/margin for a divider beneath it. */
.class-add--top {
  margin-top: 0; padding-top: 0; border-top: 0;
  margin-bottom: 1rem; padding-bottom: 1rem; border-bottom: 1px solid var(--border);
}

/* ══════════════════════════════════════════════════════════════════════
   Car Clubs (Show Setup → Specialty / Best Of)
   One row per club: name + counts on the left, judge slots in the middle,
   Clear/Delete on the right. Its own grid rather than .class-row — the
   club rows carry a two-line identity block and no drag handle, and the
   old .class-row-view flex made the judge dropdown stretch the full row
   (a wide white field floating in the grey row).
   ══════════════════════════════════════════════════════════════════════ */
.club-section { margin-top: 1.75rem; }
.club-row {
  display: grid;
  /* Fixed first column so the judge dropdowns line up down the whole list —
     an auto first column sized to each club's name and the controls came
     out ragged, row to row. */
  grid-template-columns: 15rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.5rem 0.9rem;
  background: var(--bg-inset);
  border: 1px solid var(--border);
  padding: 0.55rem 0.75rem;
}
/* An assigned club gets the accent edge — scanning the list tells you at a
   glance which clubs already have a judge walking them. */
.club-row--assigned {
  border-left: 3px solid var(--accent);
  padding-left: calc(0.75rem - 2px);
}
.club-row-id { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.club-row-name {
  font-family: var(--font-body); font-size: 1rem;
  color: var(--text-primary); overflow-wrap: anywhere;
}
/* Counts are data → mono kicker, same as entry numbers elsewhere. */
.club-row-count {
  font-family: var(--font-mono); font-size: 0.72rem;
  letter-spacing: 0.01em; color: var(--text-muted);
}
.club-row-count--empty { font-style: italic; }
.club-row-count-split {
  color: var(--accent-ink, var(--accent));
  cursor: help; text-decoration: underline dotted; text-underline-offset: 0.2em;
}
.club-row-judges { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; min-width: 0; }
/* Capped so a single judge dropdown reads as a control, not a banner. */
.club-row-judges .class-sponsor-select { flex: 1 1 150px; min-width: 130px; max-width: 240px; }
.club-row-note { font-size: 0.72rem; color: var(--text-muted); }
.club-row-actions { display: flex; gap: 0.4rem; justify-self: end; }
/* The add box holds a club name, not a sentence — a full-width field here
   read as a stray banner above the list. */
.club-section .class-add-input { flex: 0 1 22rem; }
.club-row-actions .btn-edit,
.club-row-actions .btn-delete-row,
.club-row-judges .club-judge-add { min-height: 44px; white-space: nowrap; }

@media (max-width: 820px) {
  .club-row { grid-template-columns: 1fr; align-items: stretch; }
  .club-row-actions { justify-self: start; }
  .club-row-judges .class-sponsor-select { max-width: none; }
}
/* The phone rule that drops a CLASS row's sponsor picker onto its own line
   (order: 3, further up) also matches these judge dropdowns and shuffled
   them behind "+ Judge". Club rows keep DOM order: judges, then + Judge. */
@media (max-width: 640px) {
  .club-row-judges .class-sponsor-select { order: 0; flex: 1 1 100%; max-width: none; }
}

/* ══════════════════════════════════════════════════════════════════════
   Judge mode from the admin side
   Two doors into judging for the person who runs the show AND scores
   cars: a nav button to the judge app, and — on a phone, where the roster
   is a stack of cards and the car is standing in front of you — a
   per-car button straight into that car's scoring flow.
   ══════════════════════════════════════════════════════════════════════ */

/* Nav button. It's an <a>, so it needs the anchor reset the .btn-settings
   buttons beside it don't; the label shortens where the nav row is tight. */
.btn-judge-mode { text-decoration: none; white-space: nowrap; }
.judge-mode-short { display: none; }
@media (max-width: 640px) {
  .judge-mode-long  { display: none; }
  .judge-mode-short { display: inline; }
}

/* "Judge this car" — phone only. On desktop the judge app is a tab away
   and the roster row has no room for a third action; on a phone it's the
   whole point: score the car in front of you without leaving admin. */
.btn-judge-car { display: none; }
@media (max-width: 760px) {
  #reg-table td.col-actions .btn-judge-car {
    display: flex; align-items: center; justify-content: center;
    width: 100%; margin-top: 0.5rem; min-height: 48px; /* field tap target */
    background: var(--accent); color: var(--on-accent, #0D0D12);
    border: 1px solid var(--accent);
    font-family: var(--font-label); font-weight: 700;
    font-size: 0.8rem; letter-spacing: 0.08em; text-transform: uppercase;
    text-decoration: none;
  }
  #reg-table td.col-actions .btn-judge-car:active { background: var(--accent-hover); }
}

/* A judge who opens /admin lands on the login screen — give them the way
   back to judging as a button, not a sentence with a link in it. */
.login-judge-note {
  background: var(--accent-dim);
  border: 1px solid var(--accent);
  color: var(--text-primary);
  padding: 0.85rem;
  margin-bottom: 1rem;
}
.login-judge-note-line { margin: 0; font-size: 0.9rem; line-height: 1.45; }
.login-judge-note-line--sub { margin-top: 0.65rem; color: var(--text-secondary); font-size: 0.82rem; }
.login-judge-note-btn {
  display: flex; align-items: center; justify-content: center;
  width: 100%; min-height: 48px; margin-top: 0.6rem;
  text-decoration: none; text-align: center;
}

/* ─── Specialty award nominations (dashboard card header + modal) ──────── */
.card-head-action {
  flex-shrink: 0;
  margin-left: auto;
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  padding: 0.3rem 0.65rem;
}
.nom-list {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  max-height: 62vh;
  overflow-y: auto;
  padding-right: 0.4rem;
}
.nom-award { border: 1px solid var(--border); background: var(--bg-card-alt); }
.nom-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.2rem 1rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--border);
}
.nom-name { font-family: var(--font-label); font-weight: 700; font-size: 0.95rem; color: var(--text-primary); }
.nom-meta { font-size: 0.78rem; color: var(--text-secondary); }
.nom-rows { list-style: none; margin: 0; padding: 0; }
.nom-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.4rem 1rem;
  padding: 0.65rem 1rem;
  border-top: 1px solid var(--border);
}
.nom-row:first-child { border-top: 0; }
.nom-row--winner { background: var(--accent-dim); }
.nom-row--empty { color: var(--text-muted); font-style: italic; font-size: 0.85rem; }
.nom-main { display: flex; flex-direction: column; gap: 0.1rem; min-width: 0; flex: 1 1 14rem; }
.nom-car { font-weight: 600; color: var(--text-primary); font-size: 0.92rem; }
.nom-sub { font-size: 0.78rem; color: var(--text-secondary); }
.nom-verify { flex-shrink: 0; }
.nom-conflict {
  flex-basis: 100%;
  padding: 0.7rem 0.8rem;
  border: 1px solid color-mix(in srgb, var(--accent) 55%, transparent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  font-size: 0.85rem;
  color: var(--text-primary);
}
.nom-conflict--block {
  border-color: color-mix(in srgb, var(--error, #c0392b) 55%, transparent);
  background: color-mix(in srgb, var(--error, #c0392b) 8%, transparent);
}
.nom-conflict p { margin: 0 0 0.6rem; }
.nom-conflict-actions { display: flex; gap: 0.6rem; flex-wrap: wrap; }
