/* ── Kinetic Industrial — Admin & Judge override layer ──
   Loaded AFTER admin.css and judge.css.
   Redefines the design tokens those stylesheets read from, so the entire
   organizer console + judge UI flips visual identity (Midnight Amber →
   Kinetic Industrial) without rewriting markup or class names.
   Also forces sharp 0px corners and the kinetic font stack:
   Archivo Black (display headings) · Archivo (labels/body/controls) · JetBrains Mono (IDs/data)
   Archivo Black is the wordmark and display face. */

@import url('/css/fonts-bebas.css');
@import url('https://fonts.googleapis.com/css2?family=Archivo+Black&family=Archivo:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;600;700&display=swap');

/* ── Keyboard focus — branded orange ring across interactive elements.
   :focus-visible so only keyboard navigation shows it; inputs keep their
   own border-color focus treatment. Platform-wide for consistency. ── */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
[tabindex]:focus-visible,
[role="button"]:focus-visible,
[role="link"]:focus-visible,
[role="tab"]:focus-visible {
  outline: 2px solid var(--primary, #58A0D8);
  outline-offset: 2px;
}

/* ── Screen-reader-only: visually hidden, still in the accessibility tree.
   Used for headings/labels that give structure without occupying the dense
   operator layout. ── */
.sr-only {
  position: absolute;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}

/* ── Tokens: Light (Kinetic primary) ── */
html[data-theme="light"] {
  --bg-base:         #f9f9fa;
  --bg-card:         #ffffff;
  --bg-card-alt:     #f3f3f4;
  --bg-inset:        #eeeeef;
  --bg-nav:          #1A1A1B;

  --border:          #E4E4E7;
  --border-dim:      #EEEEEF;
  --border-mid:      #d4d4d8;
  --border-strong:   #58A0D8;

  --accent:          #58A0D8;
  --accent-hover:    #3F86C2;
  --accent-dim:      rgba(88, 160, 216,0.08);
  --accent-glow:     rgba(88, 160, 216,0.25);  /* DESIGN.md §4; matches lib/accentTokens.js */
  /* Text/icon-safe accent for light surfaces. Pure #58A0D8 reads ~2.7:1 on
     white (fails AA, washes out in sunlight). Derived against bg-inset
     #EEEEEF — the darkest light surface ink sits on (table header band,
     dense rows): 5.0:1 there, 5.8:1 on white. Matches what the runtime
     accent pipeline computes for the stock orange. Fills/borders/glows
     keep the bright --accent; only ink (text + icons) uses this. */
  --accent-ink:      #1F6BA5;

  --text-primary:    #1a1c1d;
  --text-secondary:  #5f5e5f;
  --text-muted:      #6c6c70;

  --error:           #ba1a1a;
  /* DESIGN.md and judge.css name this role --danger; admin.css named it
     --error. The admin/judge surfaces don't load kinetic.css (which defines
     --danger), so three People's Choice integrity rules below resolved to
     nothing and shipped with no border. Alias, don't fork. */
  --danger:          var(--error);
  --error-bg:        #ffdad6;
  --error-border:    rgba(186,26,26,0.35);

  --success:         #10B981;
  --success-dim:     rgba(16,185,129,0.10);
  --success-border:  rgba(16,185,129,0.28);
  /* Text/icon-safe success. #10B981 is ~2.5:1 on white (fails even the 3:1
     large-icon bar); this deep emerald clears ~5.5:1. */
  --success-ink:     #047857;

  --warning:         #58A0D8;

  --shadow:          0 1px 0 rgba(0,0,0,0.06);
  --shadow-lg:       0 6px 0 rgba(0,0,0,0.07);

  --modal-bg:        #ffffff;
  --input-bg:        #ffffff;
  --ticker-bg:       #1A1A1B;
  --ticker-div:      #2a2a2c;
  --row-alt:         rgba(0,0,0,0.022);
  --row-hover:       rgba(88, 160, 216,0.06);
  --toggle-off:      #d4d4d8;
  --toggle-border:   #c4c4c8;
}

/* ── Tokens: Dark (Kinetic alt — industrial console) ── */
html[data-theme="dark"] {
  --bg-base:         #0e0e10;
  --bg-card:         #1A1A1B;
  --bg-card-alt:     #232326;
  --bg-inset:        rgba(255,255,255,0.04);
  --bg-nav:          #09090A;

  --border:          #2a2a2c;
  --border-dim:      #1f1f21;
  --border-mid:      #3a3a3c;
  --border-strong:   #58A0D8;

  --accent:          #58A0D8;
  --accent-hover:    #7DB6E4;
  --accent-dim:      rgba(88, 160, 216,0.10);
  --accent-glow:     rgba(88, 160, 216,0.25);  /* DESIGN.md §4; matches lib/accentTokens.js */
  /* On carbon the bright accent already clears AA (~6.3:1), so ink == accent. */
  --accent-ink:      #58A0D8;

  --text-primary:    #f4f4f5;
  --text-secondary:  #a1a1aa;
  --text-muted:      #8a8a93;

  /* Dark-theme danger: the sanctioned --danger-on-dark (#ef4444). Canonical
     #ba1a1a fails contrast on carbon (~2.7:1); this clears ~4.6:1. See DESIGN.md. */
  --error:           #ef4444;
  --danger:          var(--error);
  --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.12);
  --success-border:  rgba(16,185,129,0.30);
  /* Lighter emerald for text/icons on carbon (clears AA comfortably). */
  --success-ink:     #34D399;

  --warning:         #58A0D8;

  --shadow:          0 1px 0 rgba(0,0,0,0.6);
  --shadow-lg:       0 6px 0 rgba(0,0,0,0.55);

  --modal-bg:        #1A1A1B;
  --input-bg:        #0e0e10;
  --ticker-bg:       #09090A;
  --ticker-div:      #2a2a2c;
  --row-alt:         rgba(255,255,255,0.02);
  --row-hover:       rgba(88, 160, 216,0.08);
  --toggle-off:      #2a2a2c;
  --toggle-border:   #3a3a3c;
}

/* ── Sharp 0px corners (Kinetic shape language) ──
   admin.css and judge.css have hundreds of border-radius declarations.
   Force them all flat in admin/judge scope. The .full-radius class
   (avatars etc.) keeps its circle. */
[data-theme] body,
[data-theme] body *:not(.preserve-radius):not(.avatar):not(.spinner):not(.dot):not(.qr-spinner):not(.heatmap-dot):not(.heatmap-dot-inner):not(.heatmap-dot-legend) {
  border-radius: 0 !important;
}
/* Avatars: still circular per design spec */
[data-theme] .avatar,
[data-theme] .avatar-circle,
[data-theme] [class*="avatar"] {
  border-radius: 50% !important;
}

/* ── Typography ──
   Archivo (body, labels, controls) · Archivo Black (page headings, wordmark,
   stat values) · JetBrains Mono (IDs/data). */
[data-theme] body,
[data-theme] input,
[data-theme] textarea,
[data-theme] select {
  font-family: 'Archivo', system-ui, -apple-system, sans-serif !important;
  font-weight: 400 !important;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
/* Loosen the table body / cell text — kinetic.css's heavier defaults
   were running letters together at small sizes. */
[data-theme] table td,
[data-theme] .modal-body,
[data-theme] .modal-form,
[data-theme] p:not([class*="-eyebrow"]):not([class*="-kicker"]),
[data-theme] li,
[data-theme] span:not([class*="-label"]):not([class*="-title"]):not([class*="-eyebrow"]):not([class*="-role"]):not([class*="-badge"]):not([class*="-static"]):not(.accent) {
  font-weight: 400 !important;
  letter-spacing: 0.015em;
}
[data-theme] h1, [data-theme] h2, [data-theme] h3, [data-theme] h4,
[data-theme] .stat-value,
[data-theme] .login-title,
[data-theme] .nav-show-name,
[data-theme] .modal-title,
[data-theme] .section-title,
[data-theme] [class*="-title"] {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 700 !important;
  letter-spacing: 0.04em !important;
  text-transform: uppercase;
}
/* Catch-all for every "*-label" class. Its :not() chain outranks the specific
   rules below, so whatever tracking it sets is the tracking the admin gets —
   which is why tile headers read at 0.08em while field labels set 0.06em two
   files away. Pointed at the shared token so there is one answer. */
[data-theme] .stat-label,
[data-theme] .label,
[data-theme] [class*="-label"]:not([class*="-labels"]):not(label) {
  font-family: 'Archivo', system-ui, sans-serif !important;
  letter-spacing: var(--label-tracking) !important;
  text-transform: uppercase;
}
[data-theme] code, [data-theme] pre,
[data-theme] .mono, [data-theme] [class*="-mono"] {
  font-family: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace !important;
}

/* ── Selection ── */
[data-theme] ::selection { background: var(--accent); color: var(--on-accent, #0D0D12); }

/* ── Buttons: body face (Archivo), sentence case ──
   Previously uppercase + wide tracking; that read as generic
   "AI dashboard" chrome. Buttons now use the body font so labels read
   like the rest of the copy; caps stay for headings/stat labels. */
[data-theme] .btn,
[data-theme] button,
[data-theme] [class*="-btn"]:not(input) {
  font-family: 'Archivo', system-ui, sans-serif !important;
  letter-spacing: 0.01em !important;
  border-radius: 0 !important;
  font-weight: 600;
  text-transform: none;
}
/* Tab strips (.modal-tab / .modal-subtab / .rubric-type-tab) are controls
   and stay on the control face above, matching platform-admin's .tab and
   the marketing map tabs. Only card labels that happen to be <button>
   elements keep the Archivo label voice. */
[data-theme] .judge-card-label--btn {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 700 !important;
  letter-spacing: 0.1em !important;
  text-transform: uppercase !important;
}

/* ── Headings are ink, not accent ──
   Modal titles, settings groups, and the auth-card title were the only
   headings on the platform set in the accent. Every other heading (page
   heroes, card titles, dashboard states) is ink; the accent stays with
   eyebrows, links, and actions. */
[data-theme] .modal-title,
[data-theme] .settings-section-title,
[data-theme] .classes-section-head h3,
[data-theme] .login-title {
  color: var(--text-primary) !important;
}
/* Auth-card wordmark: the same Archivo Black ENTRY|LANE as every top bar. */
[data-theme] .login-title.login-wordmark { letter-spacing: 0.01em !important; font-size: 2.4rem; line-height: 1; }
[data-theme] .login-title.login-wordmark .kw-entry,
[data-theme] .login-title.login-wordmark .kw-lane {
  font-family: 'Bebas Neue', 'Archivo Black', 'Archivo', sans-serif !important;
  font-weight: 400 !important;
}
[data-theme] .login-title.login-wordmark .kw-entry { color: var(--text-primary); }
[data-theme] .login-title.login-wordmark .kw-lane  { color: #58A0D8 !important; margin-left: 0.05em; }
/* Settings option labels: the blanket [class*="-title"] rule above clamps
   their tracking to 0.02em — too cramped for uppercase Archivo. Match the
   admin.css intent (positive label tracking). */
[data-theme] .settings-row-title {
  font-size: var(--label-size) !important;
  font-weight: 700 !important;
  letter-spacing: var(--label-tracking) !important;
}

/* Form field labels — the "FIRST NAME" / "ZIP / POSTAL" voice in every modal.
   Were 0.62rem at 0.12em: small enough that the label read as a caption
   floating above an unrelated 44px box instead of naming it. Same voice as
   the tile headers and settings rows above. */
[data-theme] .mfield label:not(.mfield-checkline):not(.import-option),
[data-theme] .optional {
  font-size: var(--label-size) !important;
  font-weight: 700 !important;
  letter-spacing: var(--label-tracking) !important;
}
/* Display face: page + section h1/h2 and the big stat numerals are Archivo
   Black. Everything smaller (h3/h4, card + modal titles) stays Archivo 700
   caps so dense operator screens don't shout. */
[data-theme] h1, [data-theme] h2,
[data-theme] .stat-value,
[data-theme] .login-title {
  font-family: 'Archivo Black', 'Archivo', system-ui, sans-serif !important;
  font-weight: 400 !important;
  letter-spacing: 0.01em !important;
}
/* Label-voice chips that the blanket span rule above flattens to weight 400 /
   0.015em tracking — restore the Archivo chip treatment (design doc: badges are
   Archivo 700, uppercase, positive tracking):
   .user-name-role      — Admin/Judge label beside a user's name
   .class-number-badge  — the "#1" class chip in Manage Classes
   .class-vt-static     — the CAR/TRUCK/BIKE chip in Manage Classes */
[data-theme] .user-name-role,
[data-theme] .class-number-badge,
[data-theme] .class-vt-static {
  font-weight: 700 !important;
  letter-spacing: 0.08em !important;
}

/* ── Top app bar: keep dark in both themes (Kinetic spec calls for
       a consistent industrial dark nav even in light mode) ── */
[data-theme] .nav, [data-theme] .top-nav, [data-theme] .admin-nav,
[data-theme] .ticker {
  border-radius: 0 !important;
}

/* ── Shared app top-bar anatomy ──
   Every logged-in / per-show surface (admin, judge, vote, survey, results,
   hardship, account) carries the same bar:
     [ENTRY|LANE wordmark]  [show name under it]   …   [// PAGE LABEL] [user] [buttons]
   The wordmark is the one Archivo Black exception; the page label and show
   name are Archivo label voice. Defined once here so no page re-declares it. */
/* Grid, not a wrapping flex row: ENTRY + LANE always share one line (the
   wordmark never breaks in half on a narrow bar), the show name takes the
   full second row. min-width keeps the mark from collapsing under the
   right-hand cluster; the cluster wraps instead (admin.css ≤480 rule). */
[data-theme] .admin-nav-title {
  display: inline-grid;
  grid-template-columns: max-content max-content;
  justify-content: start;
  align-items: baseline;
  min-width: 7rem;
}
[data-theme] .admin-nav-title .kw-entry,
[data-theme] .admin-nav-title .kw-lane { white-space: nowrap; }
[data-theme] .admin-nav-title .kw-entry { color: #F3EFE8; }
/* Top-left wordmark: LANE (and its period) wear the show's chosen accent —
   the one place the mark follows the theme, by request. */
[data-theme] .admin-nav-title .kw-lane  { color: var(--accent, #58A0D8) !important; margin-left: 0.05em; }
[data-theme] .admin-nav-title .kw-lane::after { color: var(--accent, #58A0D8); }
[data-theme] .admin-nav-title--link { text-decoration: none; }
[data-theme] .admin-nav-title--link:hover .kw-entry { opacity: 0.85; }
/* Show name on its own line under the wordmark. flex-basis:100% forces it
   below ENTRYLANE; min-width:0 + break-word let a long name wrap inside the
   title column instead of pushing the bar's buttons off-screen. */
[data-theme] .admin-nav-title .kw-show {
  grid-column: 1 / -1;
  /* width:0 + min-width:100% — the spanning row must NOT feed the column
     sizing (a long show name would otherwise widen the whole bar); it just
     fills whatever width the wordmark row established, and wraps. */
  width: 0;
  min-width: 100%;
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: break-word;
  color: rgba(255,255,255,0.82);
  margin-top: 0.5rem;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.85rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  line-height: 1.2;
}
[data-theme] .admin-nav-title .kw-show:empty { display: none; }
/* Page label on the right of the bar ("// Judging", "// Results"). Same
   voice as the kinetic .kbar-nav links. */
[data-theme] .admin-nav-page {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 700 !important;
  font-size: 0.78rem;
  letter-spacing: 0.12em !important;
  text-transform: uppercase;
  color: rgba(255,255,255,0.82);
  white-space: nowrap;
}
[data-theme] .admin-nav-page .accent { color: var(--accent); }
@media (max-width: 760px) {
  [data-theme] .admin-nav-right { flex: 0 1 auto; min-width: 0; flex-wrap: wrap; justify-content: flex-end; row-gap: 0.4rem; }
  [data-theme] .admin-nav-page { font-size: 0.7rem; white-space: normal; text-align: right; }
}

/* ── Awards-grid tile overrides ──
   judge.css hardcodes teal/navy values on .judge-card* (the cyber theme
   from the original driven_to_duty fork) instead of reading CSS vars,
   so admin-kinetic.css's token overrides can't reach them. Force the
   tiles into Kinetic Industrial colors here. */
[data-theme] .judge-card {
  background: var(--bg-card) !important;
  border: 1px solid var(--border) !important;
  box-shadow: var(--shadow) !important;
  padding: 0 !important;
  cursor: default !important;
}
[data-theme="dark"] .judge-card {
  background: var(--bg-card) !important;
}
/* File upload inputs — style the "Choose File" button to match the
   kinetic .btn-add / .btn-save pattern so file pickers don't read as
   raw browser-chrome UI inside our forms. */
[data-theme] input[type="file"] {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-size: 0.85rem !important;
  color: var(--text-secondary) !important;
}
/* Controls-not-headlines: the file button is a control, so it takes the
   quiet body face and the derived on-accent color, not label caps. */
[data-theme] input[type="file"]::file-selector-button {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 600 !important;
  font-size: 0.85rem !important;
  letter-spacing: 0.01em !important;
  text-transform: none !important;
  background: var(--accent) !important;
  color: var(--on-accent, #0D0D12) !important;
  border: none !important;
  border-radius: 0 !important;
  padding: 0.55rem 0.95rem !important;
  margin-right: 0.85rem !important;
  cursor: pointer !important;
  transition: filter 0.12s !important;
}
[data-theme] input[type="file"]::file-selector-button:hover { filter: brightness(1.08) !important; }
[data-theme] input[type="file"]::-webkit-file-upload-button {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 600 !important;
  font-size: 0.85rem !important;
  letter-spacing: 0.01em !important;
  text-transform: none !important;
  background: var(--accent) !important;
  color: var(--on-accent, #0D0D12) !important;
  border: none !important;
  border-radius: 0 !important;
  padding: 0.55rem 0.95rem !important;
  margin-right: 0.85rem !important;
  cursor: pointer !important;
}
[data-theme] .judge-card-label {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-size: var(--label-size) !important;
  font-weight: 700 !important;
  letter-spacing: var(--label-tracking) !important;
  color: var(--text-primary) !important;
  padding: 0.85rem 1rem !important;
  margin-bottom: 0 !important;
  border-bottom: 1px solid var(--border) !important;
  text-transform: uppercase !important;
}
[data-theme] .judge-card-body {
  padding: 1rem !important;
  color: var(--text-primary) !important;
  /* Any card body that scrolls (admin.css caps them at 280px — e.g. the
     Heatmap card) gets the same thin accent scrollbar as the list tiles,
     instead of the native gray one. */
  scrollbar-width: thin !important;
  scrollbar-color: var(--accent) rgba(0,0,0,0.06) !important;
}
[data-theme] .judge-card-body::-webkit-scrollbar {
  width: 8px !important;
}
[data-theme] .judge-card-body::-webkit-scrollbar-track {
  background: rgba(0,0,0,0.06) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card-body::-webkit-scrollbar-thumb {
  background: var(--accent) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card-body::-webkit-scrollbar-thumb:hover {
  background: var(--accent-hover) !important;
}
[data-theme] .judge-empty {
  color: var(--text-muted) !important;
  font-style: italic !important;
  margin: 0 !important;
}
/* Heatmap card border/background — judge.css hardcodes #00c8b4 */
[data-theme] .heatmap-map-wrap {
  border: 1px solid var(--accent) !important;
  background: var(--bg-card) !important;
}

/* Mapbox zoom + / − controls — recolor the embedded SVG icons to the
   neutral white on the dark chip. (Previously a filter chain hardcoded the
   icons to orange, which clashed the moment a show picked a non-orange
   accent — the surrounding border/hover tints follow --accent-rgb, so
   white icons stay consistent under any accent.) */
[data-theme] .heatmap-map-wrap .mapboxgl-ctrl-group button .mapboxgl-ctrl-icon {
  filter: brightness(0) invert(1) !important;
}
[data-theme] .heatmap-map-wrap .mapboxgl-ctrl-group {
  background: rgba(13,13,18,0.88) !important;
  border: 1px solid rgba(var(--accent-rgb, 88, 160, 216),0.35) !important;
  box-shadow: none !important;
}
[data-theme] .heatmap-map-wrap .mapboxgl-ctrl-group button {
  background: transparent !important;
}
[data-theme] .heatmap-map-wrap .mapboxgl-ctrl-group button:hover {
  background: rgba(var(--accent-rgb, 88, 160, 216),0.15) !important;
}
[data-theme] .heatmap-map-wrap .mapboxgl-ctrl-group button + button {
  border-top: 1px solid rgba(var(--accent-rgb, 88, 160, 216),0.25) !important;
}
/* Heatmap dot — soft glowing orange CIRCLE that flashes on its own
   in addition to the outward pulse rings. Bigger so it reads clearly. */
[data-theme] .heatmap-dot {
  width: 22px !important;
  height: 22px !important;
  border-radius: 50% !important;
}
[data-theme] .heatmap-dot-inner {
  border-radius: 50% !important;
  background: radial-gradient(circle,
    rgba(var(--accent-rgb, 88, 160, 216),1) 0%,
    rgba(var(--accent-rgb, 88, 160, 216),0.7) 50%,
    rgba(var(--accent-rgb, 88, 160, 216),0.0) 100%) !important;
  border: none !important;
  box-shadow:
    0 0 6px rgba(var(--accent-rgb, 88, 160, 216),0.9),
    0 0 16px rgba(var(--accent-rgb, 88, 160, 216),0.55) !important;
  animation: heatmap-flash 1.4s ease-in-out infinite !important;
}
[data-theme] .heatmap-dot::before,
[data-theme] .heatmap-dot::after {
  border-radius: 50% !important;
  background: radial-gradient(circle,
    rgba(var(--accent-rgb, 88, 160, 216),0.55) 0%,
    rgba(var(--accent-rgb, 88, 160, 216),0.0) 70%) !important;
  border: none !important;
}
@keyframes heatmap-flash {
  0%, 100% { opacity: 1; transform: scale(1); }
  50%      { opacity: 0.55; transform: scale(0.85); }
}
@media (prefers-reduced-motion: reduce) {
  [data-theme] .heatmap-dot-inner { animation: none !important; }
}
[data-theme] .heatmap-dot { white-space: pre-line; }

/* Tile link blocks (Hardship + People's Choice tiles surface a public URL
   for the organizer to share with registrants/attendees). */
[data-theme] .tile-link-block {
  border: 1px solid var(--border) !important;
  background: var(--bg-card-alt) !important;
  padding: 0.6rem 0.75rem !important;
  margin-bottom: 0.7rem !important;
}
[data-theme] .tile-link-blurb {
  font-size: 0.8rem !important;
  color: var(--text-secondary) !important;
  margin: 0 0 0.35rem 0 !important;
  line-height: 1.35 !important;
}
[data-theme] .tile-link {
  display: inline-block !important;
  font-family: 'JetBrains Mono', ui-monospace, monospace !important;
  font-size: 0.78rem !important;
  font-weight: 600 !important;
  color: var(--accent-ink, var(--accent)) !important;
  text-decoration: none !important;
  word-break: break-all !important;
}
[data-theme] .tile-link:hover { text-decoration: underline !important; }

/* ── Delete buttons: RED OUTLINE (flag-red text + red border, transparent
   fill), matching the platform-admin Delete and the red "No" chip. It reads as
   destructive without the heavy filled box, and stays uniform with the other
   outline action buttons in a row. Hover fills a light red wash. */
[data-theme] .btn-delete {
  background: transparent !important;
  color: var(--error) !important;
  border: 1px solid var(--error-border) !important;
}
[data-theme] .btn-delete:hover {
  background: var(--error-bg) !important;
  color: var(--error) !important;
  border-color: var(--error) !important;
  opacity: 1 !important;
}
/* Small variant override: when the .btn-delete also has .btn-save--sm
   (survey-question rows, etc.) we want the small padding to actually
   stick — .btn-delete's full-size padding wins on source order without
   this combined selector. */
[data-theme] .btn-delete.btn-save--sm {
  padding: 0.3rem 0.6rem !important;
  font-size: 0.8rem !important;
  line-height: 1 !important;
}
/* Class-row Delete now uses the .btn-delete-row outline style so no
   per-row override is needed — the standard red text + red-tinted
   border come from public/css/admin.css. */
/* Manage Users row Delete: red outline, same as every other per-row Delete.
   Transparent fill keeps it uniform with the outline action buttons beside it
   (Edit / Copy link / …); the red text + border mark it as the destructive one.
   Hover fills a light red wash. */
[data-theme] .user-row .user-delete {
  background: transparent !important;
  color: var(--error) !important;
  border: 1px solid var(--error-border) !important;
}
[data-theme] .user-row .user-delete:hover {
  background: var(--error-bg) !important;
  color: var(--error) !important;
  border-color: var(--error) !important;
}

/* ── Manage Users row: three clean zones — identity (name + email, grows),
   a meta cluster (role badge / pace / last-login / flag), and a uniform
   action-button group that hugs the right and wraps to its own line as a
   single block when space is tight. Replaces the old 7-column grid, which
   couldn't hold a judge's five buttons and scattered them across two ragged
   rows with mismatched sizes. */
[data-theme] .user-row {
  display: flex !important;
  flex-wrap: wrap !important;
  align-items: center !important;
  column-gap: 0.6rem !important;
  row-gap: 0.5rem !important;
}
[data-theme] .user-row .user-row-name { flex: 1 1 14rem !important; min-width: 0 !important; }
[data-theme] .user-row .user-edit { margin-left: 0 !important; }  /* no longer floats on its own */
/* The inline edit form gains an email field — give it room to show an address. */
[data-theme] .user-name-edit-form .user-edit-email { width: 200px !important; max-width: 100% !important; }

[data-theme] .user-row-meta {
  display: flex !important;
  align-items: center !important;
  gap: 0.4rem !important;
  flex-wrap: wrap !important;
}
[data-theme] .user-row-meta .user-row-role,
[data-theme] .user-row-meta .user-row-last-login,
[data-theme] .user-row-meta .user-row-flag {
  white-space: nowrap !important;
  margin: 0 !important;
}

[data-theme] .user-row-actions {
  display: flex !important;
  align-items: center !important;
  gap: 0.45rem !important;
  flex-wrap: wrap !important;
  margin-left: auto !important;            /* hug the right; wrap below when tight */
  justify-content: flex-end !important;
}
/* Every action button is the same size, so Edit / Copy link / Resend link /
   Reset device / Delete read as one matched group. (Delete keeps its filled
   treatment for now — that's the open question, not the sizing.) */
[data-theme] .user-row-actions .btn-save,
[data-theme] .user-row-actions .btn-delete {
  flex: 0 0 7.5rem !important;
  width: 7.5rem !important;
  min-width: 7.5rem !important;
  padding: 0.6rem 0.5rem !important;
  /* Standard 0.88rem button type (design doc) — these sat at 0.7rem while
     the identical row actions in Manage Classes were 0.88rem. */
  font-size: 0.88rem !important;
  text-align: center !important;
  white-space: nowrap !important;
}

/* Phone: identity takes the full first line; the action group fills the next
   line with the buttons stretching evenly so they stay tap-friendly. */
@media (max-width: 640px) {
  [data-theme] .user-row .user-row-name { flex: 1 1 100% !important; }
  [data-theme] .user-row-actions {
    margin-left: 0 !important;
    justify-content: flex-start !important;
    flex: 1 1 100% !important;
  }
  [data-theme] .user-row-actions .btn-save,
  [data-theme] .user-row-actions .btn-delete {
    flex: 1 1 auto !important;
    width: auto !important;
    min-width: 0 !important;
  }
}

/* ── Settings → Dashboard Tiles: 2-up landscape grid instead of a
   vertical stack. Fits more rows on-screen and matches the "control panel"
   aesthetic. */
[data-theme] .tile-visibility-grid {
  display: grid !important;
  grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
  gap: 0.5rem 1rem !important;
}
[data-theme] .tile-visibility-grid .settings-row {
  border-top: none !important;
  padding: 0.5rem 0.6rem !important;
  border: 1px solid var(--border) !important;
  background: var(--bg-card-alt) !important;
  margin: 0 !important;
}

/* Settings "saved" toast: green success treatment. Stacking comes from
   admin.css via var(--z-toast) — the toast tier already sits above every
   modal, so no !important escape hatch here. */
[data-theme] .settings-toast {
  border-color: var(--success-border) !important;
}
[data-theme] .settings-toast::before { color: var(--success-ink) !important; }

/* ── Stats ticker (light, borderless, centered to the page width) ──
   Originally rendered as a dark bar bleeding to the edges; the new
   product look reads as a clean row of stats sitting on the page
   surface, so we drop the dark background, the inter-card dividers,
   and use black text. On landscape phones the row stays a single line
   and scrolls horizontally instead of squashing onto two rows. */
/* The signature dark stats TICKER (design doc §1/§5): a full-bleed carbon
   band under the nav, cells split by hairline dividers — operator chrome
   that stays dark in both themes, like the nav. A previous pass had
   flattened this to transparent floating numbers, which left the page's
   key figures with no hierarchy and no identity. */
[data-theme] .stats-row {
  background: var(--ticker-bg, #1A1A1B) !important;
  border: 0 !important;
  border-bottom: 2px solid var(--accent) !important;
  margin: 0 calc(-1 * var(--sp-24, 1.5rem)) 2rem !important;
}
[data-theme] .stat-card {
  border: 0 !important;
  border-right: 1px solid var(--ticker-div, #2a2a2c) !important;
  background: transparent !important;
  text-align: left !important;
  padding: 1.1rem 1.5rem !important;
}
[data-theme] .stat-card:last-child { border-right: 0 !important; }
[data-theme] .stats-row .stat-num {
  color: #F4F4F5 !important;
  /* The Judged stat sits in a <button>, which was rendering heavier than the
     sibling <div> stats — pin them all to the same light weight. */
  font-weight: 400 !important;
}
[data-theme] .stats-row .stat-label {
  color: rgba(255,255,255,0.62) !important;
}
/* On carbon the bright accent clears AA on its own — no derived ink needed. */
[data-theme] .stats-row .stat-num.accent,
[data-theme] .stats-row .stat-pct,
[data-theme] .stats-row .stat-sub,
[data-theme] .stats-row .stat-tap-hint {
  color: var(--accent) !important;
}
[data-theme] .stat-card--btn:hover .stat-tap-hint { color: #fff !important; }
/* Landscape phones (and any time the row is wider than the viewport):
   keep the 5 cards on a single line and let the user swipe horizontally
   instead of breaking the row into wrapped rows. */
/* Phone gutter is 1rem (admin.css ≤640px .admin-main) — the ticker's
   full-bleed margins must match or the page scrolls sideways by the
   difference (the 383px-wide-document bug at 375px). */
@media (max-width: 640px) {
  [data-theme] .stats-row { margin: 0 -1rem 1.5rem !important; }
}

@media (max-width: 960px) and (orientation: landscape),
       (max-width: 760px) {
  [data-theme] .stats-row {
    display: flex !important;
    grid-template-columns: none !important;
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    scroll-snap-type: x proximity !important;
    padding-bottom: 0.4rem !important;
  }
  [data-theme] .stats-row .stat-card {
    flex: 0 0 auto !important;
    min-width: 9.5rem !important;
    scroll-snap-align: start !important;
  }
}

/* Landscape phones: switch the tile grid into a horizontal swipe-rail
   so all the cards (Distance, Club Participation, Club Quality, Top 3
   Classes, Specialty, Hardship, etc.) stay full-height and the user
   pans left/right between them instead of squashing them into two
   shrunken columns. */
@media (max-width: 960px) and (orientation: landscape) {
  [data-theme] .judge-grid {
    display: flex !important;
    grid-template-columns: none !important;
    flex-wrap: nowrap !important;
    overflow-x: auto !important;
    -webkit-overflow-scrolling: touch !important;
    scroll-snap-type: x proximity !important;
    gap: 0.85rem !important;
    padding-bottom: 0.5rem !important;
  }
  [data-theme] .judge-grid .judge-card {
    flex: 0 0 78%;
    max-width: 78%;
    scroll-snap-align: start !important;
  }
}

/* Tile bodies — same font, dark text, no italics on counts. */
[data-theme] .judge-card-body,
[data-theme] .judge-card-body * {
  font-family: 'Archivo', system-ui, sans-serif !important;
  color: var(--text-primary) !important;
}
[data-theme] .judge-empty {
  color: var(--text-secondary) !important;
}
/* Top-N class numbers — judge.css uses --primary which still resolves to
   the cyber teal in some places. Force the kinetic accent. */
[data-theme] .top-class-list li,
[data-theme] .top-class-list .top-class-rank,
[data-theme] [class*="top-class"] [class*="rank"],
[data-theme] [id="classes-body"] *[style*="color"] {
  color: var(--accent-ink, var(--accent)) !important;
}

/* ── Top-bar buttons (Switch Show / Settings / Logout) ──
   Quiet ghosts on the dark bar. These are chrome, not actions — three
   accent-filled slabs in the nav shouted over the actual work, and with
   an organizer-picked accent (magenta, yellow…) they turned unreadable.
   The accent appears only on hover/focus, per the one-accent discipline. */
[data-theme] .btn-settings,
[data-theme] .btn-logout {
  background: rgba(255,255,255,0.05) !important;
  color: rgba(255,255,255,0.92) !important;
  border: 1px solid rgba(255,255,255,0.22) !important;
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 600 !important;
  letter-spacing: 0.01em !important;
  text-transform: none !important;
  padding: 0.5rem 1.1rem !important;
}
[data-theme] .btn-settings:hover {
  background: rgba(255,255,255,0.10) !important;
  border-color: var(--accent) !important;
  color: #ffffff !important;
}
[data-theme] .btn-logout:hover {
  background: rgba(255,255,255,0.10) !important;
  border-color: rgba(239,68,68,0.55) !important;
  color: #ffffff !important;
}
[data-theme] .btn-settings:focus-visible,
[data-theme] .btn-logout:focus-visible {
  outline: 2px solid var(--accent) !important;
  outline-offset: 2px;
}

/* ── Judge page: replace all hardcoded #00c8b4 teal with Kinetic Teal ──
   judge.css uses hardcoded hex throughout instead of CSS vars, so token
   overrides above don't reach it. Force every teal occurrence here. */

/* Hide vehicle photo column — judges don't need to see car photos */
[data-theme] .col-photo,
[data-theme] th.col-photo,
[data-theme] td.col-photo { display: none !important; }

/* Stats row */
[data-theme] .stat-card--btn:hover { border-color: var(--accent) !important; }
[data-theme] .stat-card--btn:focus { outline-color: var(--accent-glow) !important; }
[data-theme] .top-class-rank { color: var(--accent-ink) !important; }

/* Judge card labels (label color set in the main block above) */
[data-theme] .judge-card.dragging { border-color: var(--accent) !important; }

/* Roster section */
[data-theme] .roster-section-label { color: var(--accent-ink) !important; }
[data-theme] .roster-table .col-regnum { color: var(--accent-ink) !important; }
/* In card mode the entry number is the headline, and the admin roster card
   sets it in ink, not accent. Higher specificity + !important to beat the
   accent rule above, which still applies to the desktop table. */
@media (max-width: 760px) {
  [data-theme] .roster-table td.col-regnum { color: var(--text-primary) !important; }
}
[data-theme] .roster-table .col-class-text { color: var(--accent-ink) !important; }
[data-theme] .roster-row:focus-visible { outline-color: var(--accent-ink) !important; }

/* Class selector */
[data-theme] .class-select:hover { border-color: var(--accent) !important; }
[data-theme] .class-select:focus { border-color: var(--accent) !important; }
[data-theme] .class-save-status.saved { color: var(--accent-ink) !important; }
[data-theme] .class-save-status.saving { color: var(--accent-ink) !important; }

/* Roster thumb */
[data-theme] .roster-thumb:hover,
[data-theme] .roster-thumb:focus { border-color: var(--accent) !important; }

/* Judge modal */
[data-theme] .judge-modal-class { color: var(--accent-ink) !important; }
[data-theme] .judge-modal-open-btn {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: var(--on-accent, #0D0D12) !important;
}

/* Manual lookup */
[data-theme] .manual-lookup-label { color: var(--accent-ink) !important; }
[data-theme] .manual-lookup-row input:focus { border-color: var(--accent) !important; }
[data-theme] .manual-lookup-btn {
  background: var(--accent) !important;
  border-color: var(--accent) !important;
  color: var(--on-accent, #0D0D12) !important;
}

/* Farthest distance badge */
[data-theme] .farthest-distance {
  background: var(--accent-dim) !important;
  border-color: var(--accent) !important;
  color: var(--accent-ink) !important;
}

/* Club list */
[data-theme] .club-rank { color: var(--accent-ink) !important; }
[data-theme] .club-count { background: var(--accent-dim) !important; }
[data-theme] .club-item--clickable:hover { background: var(--accent-dim) !important; }

/* Heatmap legend dot */
[data-theme] .heatmap-dot-legend { background: var(--accent) !important; }

/* Timeline SVG — can't use CSS vars in SVG attrs, but color/fill work via CSS */
[data-theme] .timeline-line { stroke: var(--accent) !important; }
[data-theme] .timeline-dot { fill: var(--accent) !important; }
[data-theme] .timeline-stat-num { color: var(--accent-ink) !important; }

/* People's choice */
[data-theme] .pc-row-item--leader .pc-row-name { color: var(--accent-ink) !important; }
/* People's Choice is universal 4th place: a car that already took a class
   place is out of the running, so it reads back without competing with the
   car actually taking the award. */
[data-theme] .pc-row-item--placed { opacity: 0.62; }
.pc-row-flag {
  font-family: var(--font-label); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.07em; font-size: 0.62rem; padding: 0.1rem 0.34rem;
  border-radius: 0; margin-left: 0.3rem; white-space: nowrap; vertical-align: middle;
}
[data-theme] .pc-row-flag--winner { background: var(--accent); color: var(--on-accent); }
[data-theme] .pc-row-flag--placed { background: transparent; color: var(--text-muted); border: 1px solid var(--border); }
[data-theme] .pc-row-flag--offsite { background: transparent; color: var(--danger); border: 1px solid var(--danger); }

/* "Who is having their friends vote for them" — votes cast from outside the
   geofence, grouped by car. */
.pc-offsite { margin-top: 0.85rem; padding-top: 0.7rem; border-top: 1px solid var(--border); }
.pc-offsite-title {
  font-family: var(--font-label); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.08em; font-size: 0.68rem; margin: 0 0 0.45rem;
}
[data-theme] .pc-offsite-title { color: var(--danger); }
.pc-offsite-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.pc-offsite-list li {
  display: flex; align-items: baseline; gap: 0.5rem; flex-wrap: wrap;
  font-size: 0.82rem; line-height: 1.3;
}
.pc-offsite-car { font-weight: 600; }
[data-theme] .pc-offsite-car { color: var(--text-primary); }
.pc-offsite-count { font-family: var(--font-mono); font-weight: 700; font-size: 0.78rem; }
[data-theme] .pc-offsite-count { color: var(--danger); }
[data-theme] .pc-offsite-dist { color: var(--text-muted); font-size: 0.76rem; }
[data-theme] .pc-row-votes { color: var(--accent-ink) !important; }

/* Specialty awards */
[data-theme] .sa-btn:hover { background: var(--accent-dim) !important; }
[data-theme] .sa-name { color: var(--accent-ink) !important; }

/* Award tile lists — every dashboard tile shares the same internal
   height (Club Participation's). Sized to show the full top-6 the
   awards feed returns; anything past six rows scrolls inside, so all
   the cards in the row stay visually aligned no matter how many
   entries each contains. judge.css ships a hardcoded teal scrollbar
   for .specialty-awards-body — overridden here too. */
[data-theme] .judge-card .club-list,
[data-theme] .judge-card .hardship-list,
[data-theme] .judge-card .distance-list,
[data-theme] .judge-card .specialty-awards-body {
  max-height: 14.5rem !important;
  overflow-y: auto !important;
  padding-right: 0.4rem !important;
  scrollbar-width: thin !important;
  scrollbar-color: var(--accent) rgba(0,0,0,0.06) !important;
}
/* Hardship + Specialty tile bodies — judge.css gives each its own
   max-height, creating a second scroll surface in addition to the
   inner .hardship-list / .club-list. Kill those so only the inner
   list scrolls and pinned content (e.g. the survey link) stays put. */
[data-theme] .judge-card .hardship-list-body,
[data-theme] .judge-card .specialty-awards-body {
  max-height: none !important;
  overflow: visible !important;
  padding-right: 0 !important;
}
[data-theme] .judge-card .club-list::-webkit-scrollbar,
[data-theme] .judge-card .hardship-list::-webkit-scrollbar,
[data-theme] .judge-card .distance-list::-webkit-scrollbar,
[data-theme] .judge-card .specialty-awards-body::-webkit-scrollbar {
  width: 8px !important;
}
[data-theme] .judge-card .club-list::-webkit-scrollbar-track,
[data-theme] .judge-card .hardship-list::-webkit-scrollbar-track,
[data-theme] .judge-card .distance-list::-webkit-scrollbar-track,
[data-theme] .judge-card .specialty-awards-body::-webkit-scrollbar-track {
  background: rgba(0,0,0,0.06) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card .club-list::-webkit-scrollbar-thumb,
[data-theme] .judge-card .hardship-list::-webkit-scrollbar-thumb,
[data-theme] .judge-card .distance-list::-webkit-scrollbar-thumb,
[data-theme] .judge-card .specialty-awards-body::-webkit-scrollbar-thumb {
  background: var(--accent) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card .club-list::-webkit-scrollbar-thumb:hover,
[data-theme] .judge-card .hardship-list::-webkit-scrollbar-thumb:hover,
[data-theme] .judge-card .distance-list::-webkit-scrollbar-thumb:hover,
[data-theme] .judge-card .specialty-awards-body::-webkit-scrollbar-thumb:hover {
  background: var(--accent-hover) !important;
}
[data-theme] .judge-card .club-list .club-item {
  padding: 0.55rem 0.6rem !important;
  border-bottom: 1px solid var(--border) !important;
}
[data-theme] .judge-card .club-list .club-item:last-child {
  border-bottom: none !important;
}

/* Club modal members — judge.css ships white-on-dark Orbitron text;
   override for the kinetic light theme so names are actually legible
   and use the same typeface as the rest of the admin. */
[data-theme] .club-member-row {
  border-bottom: 1px solid var(--border) !important;
  padding: 0.6rem 0.25rem !important;
}
[data-theme] .club-member-name {
  font-family: 'Archivo', system-ui, sans-serif !important;
  color: var(--text-primary) !important;
  font-weight: 600 !important;
  letter-spacing: 0 !important;
}
[data-theme] .club-member-car {
  font-family: 'Archivo', system-ui, sans-serif !important;
  color: var(--text-secondary) !important;
}
[data-theme] .club-member-empty {
  color: var(--text-muted) !important;
}

/* Distance Traveled + Top Models — make the entire .judge-card-body
   the scroll surface so the box is a fixed-height square that scrolls
   when there's more content than fits, and stays the same height as
   the heatmap tile next to it on Top Picks. The inner list rule above
   already had max-height; promoting the cap to the body kills the
   "empty space below short list" look. */
[data-theme] .judge-card[data-card-id="distance"] .judge-card-body,
[data-theme] .judge-card[data-card-id="top-models"] .judge-card-body {
  /* 14.5rem list + 2rem body padding — matches the club/class tiles'
     inner height so the row lines up. */
  max-height: 16.5rem !important;
  overflow-y: auto !important;
  scrollbar-width: thin !important;
  scrollbar-color: var(--accent) rgba(0,0,0,0.06) !important;
}
[data-theme] .judge-card[data-card-id="distance"] .judge-card-body::-webkit-scrollbar,
[data-theme] .judge-card[data-card-id="top-models"] .judge-card-body::-webkit-scrollbar {
  width: 8px !important;
}
[data-theme] .judge-card[data-card-id="distance"] .judge-card-body::-webkit-scrollbar-track,
[data-theme] .judge-card[data-card-id="top-models"] .judge-card-body::-webkit-scrollbar-track {
  background: rgba(0,0,0,0.06) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card[data-card-id="distance"] .judge-card-body::-webkit-scrollbar-thumb,
[data-theme] .judge-card[data-card-id="top-models"] .judge-card-body::-webkit-scrollbar-thumb {
  background: var(--accent) !important;
  border-radius: 0 !important;
}
[data-theme] .judge-card[data-card-id="distance"] .judge-card-body::-webkit-scrollbar-thumb:hover,
[data-theme] .judge-card[data-card-id="top-models"] .judge-card-body::-webkit-scrollbar-thumb:hover {
  background: var(--accent-hover) !important;
}
/* Inner list inside those tiles no longer needs its own cap — the body
   handles scroll. Reset their max-height so they don't double-clip. */
[data-theme] .judge-card[data-card-id="distance"] .distance-list,
[data-theme] .judge-card[data-card-id="top-models"] .club-list {
  max-height: none !important;
  overflow: visible !important;
  padding-right: 0 !important;
}

/* Cars To Judge roster table — judge.css ships Orbitron + dark-theme
   colors (legacy Driven-to-Duty look). Repaint each column in the
   kinetic typeface so the page reads like the rest of the admin:
   class number in the accent, name in the accent (Archivo 700), car body black
   (Archivo), class chip black on the soft accent fill. */
[data-theme] .roster-table .col-regnum {
  font-family: 'Archivo', sans-serif !important;
  color: var(--accent-ink, var(--accent)) !important;
  letter-spacing: 0.04em !important;
}
[data-theme] .roster-table .col-owner {
  font-family: 'Archivo', sans-serif !important;
  font-weight: 600 !important;
  color: var(--text-primary) !important;
  letter-spacing: 0 !important;
}
[data-theme] .roster-table .col-vehicle {
  font-family: 'Archivo', sans-serif !important;
  color: var(--text-primary) !important;
}
/* Colour and club stay secondary to the vehicle and owner. */
[data-theme] .roster-table .col-club,
[data-theme] .roster-table .veh-color {
  color: var(--text-muted) !important;
  font-weight: 400 !important;
}
[data-theme] .roster-table .col-class-text {
  font-family: 'Archivo', sans-serif !important;
  color: var(--text-primary) !important;
  font-size: 0.85rem !important;
  letter-spacing: 0.04em !important;
}

/* Hardship — judge.css ships Orbitron (Driven-to-Duty cyber font) on
   .hardship-list-name and .hardship-list-snippet. Override with the
   kinetic typeface stack so names match the rest of the admin. */
[data-theme] .hardship-list-btn:hover { background: var(--accent-dim) !important; }
[data-theme] .hardship-list-item--winner .hardship-list-name { color: var(--accent-ink) !important; }
[data-theme] .hardship-list-name {
  font-family: 'Archivo', system-ui, sans-serif !important;
  font-weight: 700 !important;
  letter-spacing: 0.04em !important;
  color: var(--text-primary) !important;
}
[data-theme] .hardship-list-snippet {
  font-family: 'Archivo', system-ui, sans-serif !important;
  text-transform: none !important;
  letter-spacing: 0 !important;
  color: var(--text-secondary) !important;
}
/* judge.css ships an opinionated dark/teal box for .hardship-modal-text
   (the legacy Driven-to-Duty palette). Override with kinetic tokens so
   the admin's hardship modal reads on the light theme. */
[data-theme] .hardship-modal-text {
  color: var(--text-primary) !important;
  background: var(--bg-card-alt) !important;
  border: 1px solid var(--border) !important;
  border-radius: 0 !important;
  padding: 0.75rem 0.9rem !important;
  white-space: pre-wrap !important;
  word-wrap: break-word !important;
  margin: 0 0 0.75rem !important;
  font-size: 0.95rem !important;
  line-height: 1.55 !important;
}

/* Admin nav links */
[data-theme] .admin-nav-link:hover {
  color: var(--accent) !important;
  border-color: var(--accent) !important;
}

/* ── Kinetic interaction parity ──
   Brings the native system's stamped-button lift and orange input focus
   glow to admin / judge / participant surfaces. Uses this file's own
   theme-aware tokens (--shadow-lg = the 0 6px 0 stamp, --accent-glow), so
   it adapts to light and dark console themes automatically. */

/* Input focus glow — additive to admin.css's border-color:accent focus.
   A real legibility aid for the form-heavy admin and judge surfaces. */
[data-theme] input:focus,
[data-theme] textarea:focus,
[data-theme] select:focus {
  box-shadow: 0 0 0 3px var(--accent-glow) !important;
}

/* Placeholder contrast — the browser default (~#757575, ≈4.0:1) misses the
   4.5:1 body floor and the outdoor-sunlight bar. admin.css only remediated
   .search-input / .settings-text-input; this covers every modal/login field. */
[data-theme] input::placeholder,
[data-theme] textarea::placeholder {
  color: var(--text-muted) !important;
  opacity: 1;
}

/* Disabled primary actions — without this a disabled .btn-save renders
   identical to an enabled one (the Specialty Winner save button ships
   disabled). No stamped lift, clearly non-interactive. */
[data-theme] .btn-save:disabled,
[data-theme] .btn-add:disabled,
[data-theme] .btn-primary:disabled,
[data-theme] .btn-danger:disabled,
[data-theme] .btn-save[disabled],
[data-theme] .btn-add[disabled] {
  opacity: 0.5 !important;
  cursor: not-allowed !important;
  filter: grayscale(0.15) !important;
}
[data-theme] .btn-save:disabled:hover,
[data-theme] .btn-add:disabled:hover,
[data-theme] .btn-primary:disabled:hover,
[data-theme] .btn-danger:disabled:hover,
[data-theme] .btn-save[disabled]:hover,
[data-theme] .btn-add[disabled]:hover {
  transform: none !important;
  box-shadow: none !important;
}

/* Stamped hover — scoped to the filled primary actions only (Save / Add /
   Primary / confirm-Danger), not every button. Product restraint: motion
   marks the main action, dense secondary controls stay calm. */
/* One press vocabulary (DESIGN.md Press Rule): hover lifts, :active presses
   flush. Covers the admin .btn tier AND the field-flow primaries (survey,
   scan, people's choice, judge lookup, login) so every surface stamps the
   same way. */
[data-theme] .btn-save,
[data-theme] .btn-add,
[data-theme] .btn-primary,
[data-theme] .btn-danger,
[data-theme] .btn-login,
[data-theme] .sv-btn,
[data-theme] .scan-btn,
[data-theme] .pc-btn,
[data-theme] .manual-lookup-btn,
[data-theme] .judge-modal-open-btn {
  transition: transform 0.12s cubic-bezier(0.22,1,0.36,1),
              box-shadow 0.12s cubic-bezier(0.22,1,0.36,1),
              filter 0.12s, background 0.12s, color 0.12s, border-color 0.12s !important;
}
[data-theme] .btn-save:hover,
[data-theme] .btn-add:hover,
[data-theme] .btn-primary:hover,
[data-theme] .btn-danger:hover,
[data-theme] .btn-login:hover,
[data-theme] .sv-btn:hover,
[data-theme] .scan-btn:hover,
[data-theme] .pc-btn:hover,
[data-theme] .manual-lookup-btn:hover,
[data-theme] .judge-modal-open-btn:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lg) !important;
}
[data-theme] .btn-save:active,
[data-theme] .btn-add:active,
[data-theme] .btn-primary:active,
[data-theme] .btn-danger:active,
[data-theme] .btn-login:active,
[data-theme] .sv-btn:active,
[data-theme] .scan-btn:active,
[data-theme] .pc-btn:active,
[data-theme] .manual-lookup-btn:active,
[data-theme] .judge-modal-open-btn:active {
  transform: translateY(1px) scale(0.99);
  box-shadow: var(--shadow) !important;
}
@media (prefers-reduced-motion: reduce) {
  [data-theme] .btn-save:hover, [data-theme] .btn-add:hover,
  [data-theme] .btn-primary:hover, [data-theme] .btn-danger:hover,
  [data-theme] .btn-login:hover, [data-theme] .sv-btn:hover,
  [data-theme] .scan-btn:hover, [data-theme] .pc-btn:hover,
  [data-theme] .manual-lookup-btn:hover, [data-theme] .judge-modal-open-btn:hover,
  [data-theme] .btn-save:active, [data-theme] .btn-add:active,
  [data-theme] .btn-primary:active, [data-theme] .btn-danger:active,
  [data-theme] .btn-login:active, [data-theme] .sv-btn:active,
  [data-theme] .scan-btn:active, [data-theme] .pc-btn:active,
  [data-theme] .manual-lookup-btn:active, [data-theme] .judge-modal-open-btn:active {
    transform: none;
    box-shadow: var(--shadow) !important;
  }
}

/* ── Skeleton loading ──────────────────────────────────────────────────────
   A quiet shimmer that holds the layout while data loads, instead of bare
   "Loading…" text (product: skeletons, not spinners-in-content). Theme-aware
   (admin runs light, judge dark) and reduced-motion safe. */
[data-theme] .skel {
  position: relative; overflow: hidden;
  background: rgba(0,0,0,0.08); border-radius: 0;
}
html[data-theme="dark"] .skel { background: rgba(255,255,255,0.07); }
[data-theme] .skel::after {
  content: ''; position: absolute; inset: 0; transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.55), transparent);
  animation: skel-shimmer 1.3s ease-in-out infinite;
}
html[data-theme="dark"] .skel::after {
  background: linear-gradient(90deg, transparent, rgba(255,255,255,0.10), transparent);
}
@keyframes skel-shimmer { 100% { transform: translateX(100%); } }
[data-theme] .skel-num  { display: inline-block; width: 1.8ch; height: 1em; vertical-align: -0.12em; }
[data-theme] .skel-cell { display: inline-block; height: 0.85rem; width: 80%; }
/* Organic width variance comes from the sibling pattern, not inline styles —
   the old markup carried ~80 style="width:NN%" attributes for this. */
[data-theme] .skel-line { display: block; width: 90%; }
[data-theme] .skel-line:nth-child(3n+2), [data-theme] .skel-line:nth-of-type(3n+2) { width: 76%; }
[data-theme] .skel-line:nth-child(3n),   [data-theme] .skel-line:nth-of-type(3n)   { width: 62%; }
[data-theme] .skel-cell:nth-child(1) { width: 96%; }
[data-theme] .skel-cell:nth-child(2) { width: 86%; }
[data-theme] .skel-cell:nth-child(3) { width: 92%; }
[data-theme] .skel-cell:nth-child(4) { width: 82%; }
[data-theme] .skel-cell:nth-child(5) { width: 90%; }
[data-theme] .skel-cell:nth-child(6) { width: 78%; }
/* Standalone short skeletons (stat values, single captions). */
[data-theme] .skel-short { width: 45%; }
@media (prefers-reduced-motion: reduce) {
  [data-theme] .skel::after { animation: none; }
}

/* ── CSV Import modal ──────────────────────────────────────────────────────
   Bulk roster import (Top Picks + Ultimate). Preview table flags rows that
   won't be sent; theme-aware to match the rest of the admin shell. */
[data-theme] .import-help {
  font-size: 0.85rem; line-height: 1.45; margin: 0 0 0.6rem;
  color: var(--ink-muted, #6b6b6b);
}
[data-theme] .import-help a { color: var(--accent, #58A0D8); font-weight: 600; }
[data-theme] .import-preview { margin-top: 0.9rem; }
[data-theme] .import-counts {
  font-family: 'Archivo', sans-serif; text-transform: uppercase;
  letter-spacing: 0.04em; font-size: 0.8rem; font-weight: 600;
  margin-bottom: 0.5rem;
}
[data-theme] .import-counts .ok   { color: #1a8a3c; }
[data-theme] .import-counts .warn { color: #c0392b; }
[data-theme] .import-table-wrap {
  max-height: 38vh; overflow: auto; border: 1px solid rgba(0,0,0,0.12);
}
html[data-theme="dark"] .import-table-wrap { border-color: rgba(255,255,255,0.14); }
[data-theme] .import-table {
  width: 100%; border-collapse: collapse;
  font-family: 'JetBrains Mono', monospace; font-size: 0.72rem;
}
[data-theme] .import-table th,
[data-theme] .import-table td {
  padding: 0.3rem 0.5rem; text-align: left; white-space: nowrap;
  border-bottom: 1px solid rgba(0,0,0,0.08);
}
html[data-theme="dark"] .import-table th,
html[data-theme="dark"] .import-table td { border-bottom-color: rgba(255,255,255,0.08); }
[data-theme] .import-table thead th {
  position: sticky; top: 0; background: var(--surface, #fff);
  font-family: 'Archivo', sans-serif; text-transform: uppercase;
  font-size: 0.68rem; letter-spacing: 0.04em;
}
html[data-theme="dark"] .import-table thead th { background: #1d1d1f; }
[data-theme] .import-row--invalid { background: rgba(192,57,43,0.10); }
[data-theme] .import-row--invalid td { color: #c0392b; }
[data-theme] .import-reason { font-style: italic; }
[data-theme] .import-checking { font-style: italic; opacity: 0.6; }
[data-theme] .import-table td .ok { font-family: 'JetBrains Mono', monospace; white-space: nowrap; }
[data-theme] .import-result {
  margin-top: 0.9rem; padding: 0.7rem 0.8rem;
  border: 1px solid var(--accent, #58A0D8);
  background: rgba(88, 160, 216,0.08); font-size: 0.85rem; line-height: 1.5;
}

/* ── First-run: setup guide + roster empty state ───────────────────────────
   The setup guide (built by admin-setup.js) fronts the dashboard until a
   new show is operational. It's the one stamped emphasis tile on the
   screen — everything below it stays hairline-rest. */
[data-theme] .setup-guide {
  background: var(--bg-card);
  border: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  margin-bottom: 24px;
}
[data-theme] .setup-guide-head {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  padding: 0.85rem 1rem;
  border-bottom: 1px solid var(--border);
}
[data-theme] .setup-guide-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--accent-ink);
}
[data-theme] .setup-guide-progress {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 0.72rem;
  font-weight: 700;
  color: var(--text-muted);
}
[data-theme] .setup-guide-hide {
  margin-left: auto;
  background: none;
  border: 0;
  padding: 0.25rem 0; /* slim inline control; the guide itself is dismissible, not precious */
  cursor: pointer;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.78rem;
  color: var(--text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}
[data-theme] .setup-guide-hide:hover { color: var(--text-primary); }

/* Steps grid — 1px gaps over the border color draw the dividers in both
   directions no matter how the columns wrap. */
[data-theme] .setup-guide-steps {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 1px;
  background: var(--border);
}
[data-theme] .setup-step {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 1rem;
  background: var(--bg-card);
  animation: setup-step-in 0.18s cubic-bezier(0.22,1,0.36,1);
}
@keyframes setup-step-in {
  from { opacity: 0.4; }
  to   { opacity: 1; }
}
@media (prefers-reduced-motion: reduce) {
  [data-theme] .setup-step { animation: none; }
}
[data-theme] .setup-step-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 1.3rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-muted);
}
[data-theme] .setup-step.is-next .setup-step-num { color: var(--accent-ink); }
[data-theme] .setup-step.is-done .setup-step-num { color: var(--success-ink); }
[data-theme] .setup-step-text {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
}
[data-theme] .setup-step-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.84rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
}
[data-theme] .setup-step-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.83rem;
  line-height: 1.45;
  color: var(--text-secondary);
  max-width: 42ch;
}
[data-theme] .setup-step.is-done .setup-step-title,
[data-theme] .setup-step.is-done .setup-step-sub { color: var(--text-muted); }
[data-theme] .setup-step-btn {
  align-self: flex-start;
  margin-top: auto;
  min-height: 44px;
}
[data-theme] .setup-step-donenote {
  margin-top: auto;
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--success-ink);
}

/* Completed state — one send-off row, then Dismiss retires the guide. */
[data-theme] .setup-complete {
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 1rem;
}
[data-theme] .setup-complete-mark {
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--success-ink);
}
[data-theme] .setup-complete-text {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}
[data-theme] .setup-complete-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
}
[data-theme] .setup-complete-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.85rem;
  color: var(--text-secondary);
}
[data-theme] .setup-complete .setup-step-btn { margin-left: auto; margin-top: 0; }

/* Roster empty states — first-use teaches how cars arrive (with a blank
   stamped entry tag waiting for car #1); no-results offers the way back. */
[data-theme] .reg-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.65rem;
  padding: 2.5rem 1rem 2.75rem;
  text-align: center;
  font-style: normal; /* .table-empty is italic; this block is structured, not a shrug */
}
[data-theme] .reg-empty--search {
  padding: 1.75rem 1rem;
  gap: 0.8rem;
}
[data-theme] .reg-empty-tag {
  display: inline-flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15rem;
  padding: 0.6rem 1.1rem 0.7rem;
  border: 1px dashed var(--border-mid);
  margin-bottom: 0.35rem;
}
[data-theme] .reg-empty-tag-label {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--text-muted);
}
[data-theme] .reg-empty-tag-num {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1;
  color: var(--text-muted);
}
[data-theme] .reg-empty-title {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 1.05rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--text-primary);
}
[data-theme] .reg-empty-sub {
  font-family: 'Archivo', system-ui, sans-serif;
  font-size: 0.9rem;
  line-height: 1.5;
  color: var(--text-secondary);
  max-width: 52ch;
  margin: 0;
  font-style: normal;
}
[data-theme] .reg-empty-actions {
  display: flex;
  gap: 0.6rem;
  flex-wrap: wrap;
  justify-content: center;
  margin-top: 0.5rem;
}
[data-theme] .reg-empty-actions .btn-add,
[data-theme] .reg-empty--search .btn-add { min-height: 44px; }
/* The mobile card-table transform hides ::before labels on .table-empty;
   keep the first-use cell full-width and centered there too. */
@media (max-width: 760px) {
  [data-theme] #reg-table td.table-empty--first { padding: 0.5rem 0 !important; }
}

/* ══════════════════════════════════════════════════════════════════════
   PHONE PASS — dashboard at ≤640px (the day-of, one-hand view).
   Loaded last so it settles the earlier phone rules above:
   • Stats: three equal cells in one row, no sideways scroll, no clipped
     third card. Numbers shrink instead of the row.
   • Toolbar: search full-width, then every action in a 2-column grid of
     identical 44px buttons (the utilities cluster unwraps into the grid).
   • Roster cards: a two-column card — entry # + judged chip on the headline
     row, name and vehicle full-width, club / class paired, Edit / Delete as
     two thumb-sized buttons. The entry number never breaks across lines.
   ══════════════════════════════════════════════════════════════════════ */
@media (max-width: 640px) {
  [data-theme] .stats-row {
    display: grid !important;
    grid-template-columns: repeat(auto-fit, minmax(0, 1fr)) !important;
    overflow: hidden !important;
    scroll-snap-type: none !important;
  }
  [data-theme] .stats-row .stat-card {
    min-width: 0 !important;
    padding: 0.75rem 0.7rem 0.7rem !important;
    overflow: hidden;
  }
  [data-theme] .stats-row .stat-num {
    font-size: clamp(1.3rem, 6.5vw, 1.7rem) !important;
    line-height: 1.05 !important;
    white-space: nowrap;
  }
  [data-theme] .stats-row .stat-label {
    font-size: 0.56rem !important;
    letter-spacing: 0.1em !important;
    line-height: 1.25;
    margin-top: 0.25rem;
  }
  [data-theme] .stats-row .stat-sub,
  [data-theme] .stats-row .stat-tap-hint {
    font-size: 0.58rem !important;
    letter-spacing: 0.06em !important;
    margin-top: 0.3rem;
  }

  /* Toolbar → 2-column grid of identical buttons under a full-width search. */
  [data-theme] .toolbar {
    display: grid !important;
    grid-template-columns: 1fr 1fr !important;
    gap: 0.5rem !important;
    align-items: stretch !important;
  }
  [data-theme] .toolbar > .search-input {
    grid-column: 1 / -1;
    max-width: none !important; width: 100% !important;
    height: 44px !important; min-height: 44px !important;
  }
  /* The utilities wrapper unwraps so its buttons become grid cells too. */
  [data-theme] .toolbar .toolbar-utils { display: contents !important; }
  [data-theme] .toolbar .btn-add,
  [data-theme] .toolbar .post-show-wrap {
    width: 100% !important; min-width: 0 !important;
    height: 44px !important; min-height: 44px !important; line-height: 1.2 !important;
    padding: 0 0.6rem !important;
    display: inline-flex !important; align-items: center !important; justify-content: center !important;
    font-size: 0.88rem !important;
    transform: none !important; box-shadow: none !important;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  [data-theme] .toolbar .post-show-wrap { padding: 0 !important; }
  [data-theme] .toolbar .post-show-wrap > .btn-add { width: 100% !important; height: 100% !important; }
  /* Hidden buttons must not leave empty grid cells. */
  [data-theme] .toolbar .hidden,
  [data-theme] .toolbar .tier-hidden { display: none !important; }
  [data-theme] .reg-results { margin: 0.1rem 0 0.7rem; font-size: 0.7rem; }

  /* Roster cards. */
  [data-theme] #reg-table tr {
    display: grid !important;
    grid-template-columns: 1fr 1fr;
    column-gap: 0.9rem; row-gap: 0.15rem;
    padding: 0.7rem 0.9rem 0.8rem !important;
    margin-bottom: 0.6rem !important;
  }
  [data-theme] #reg-table td { padding: 0.2rem 0 !important; font-size: 0.95rem !important; }
  [data-theme] #reg-table td::before { font-size: 0.58rem !important; margin-bottom: 0 !important; }
  /* Headline: the entry number on ONE line (it was breaking "#129" into
     "#1 / 29"), with the rest of the row free for the judged chip. */
  [data-theme] #reg-table td.col-regnum {
    grid-column: 1 / -1;
    white-space: nowrap !important;
    font-size: 1.45rem !important; line-height: 1 !important;
    padding: 0 0 0.45rem !important; margin-bottom: 0.25rem !important;
    border-bottom: 1px solid var(--border-mid) !important;
  }
  [data-theme] #reg-table td[data-label="Name"] { grid-column: 1 / -1; font-size: 1.05rem !important; }
  [data-theme] #reg-table td[data-label="Name"] .name-link { font-weight: 700; }
  [data-theme] #reg-table td[data-label="Vehicle"] { grid-column: 1 / -1; }
  [data-theme] #reg-table td[data-label="Vehicle"] br { display: none; }
  [data-theme] #reg-table td[data-label="Vehicle"] small { margin-left: 0.4rem; }
  /* Club | Class share a row; Judged | Registered share the next. */
  [data-theme] #reg-table td[data-label="Car Club"],
  [data-theme] #reg-table td[data-label="Class"],
  [data-theme] #reg-table td[data-label="Judged"],
  [data-theme] #reg-table td[data-label="Registered"] { grid-column: auto; min-width: 0; }
  [data-theme] #reg-table td[data-label="Registered"] { font-size: 0.82rem !important; color: var(--text-secondary); }
  [data-theme] #reg-table td.col-actions { grid-column: 1 / -1; padding-top: 0.55rem !important; }
  [data-theme] #reg-table td.col-actions .action-btns { display: flex !important; gap: 0.5rem; }
  [data-theme] #reg-table td.col-actions .action-btns button {
    flex: 1 1 0; min-height: 44px !important; font-size: 0.88rem !important;
  }
  [data-theme] #reg-table td.table-empty { grid-column: 1 / -1; }
}

/* Roster check-in: the "Here" column and the live turnout readout.
   Mono numerals in the telemetry register, same as the other roster meta. */
.checkin-count {
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--accent-ink, #1F6BA5);
  white-space: nowrap;
}
.col-checkin { text-align: center; }
/* No explicit size: the Free Entry box one column over is a native checkbox,
   and the old hard 20px read as oversized beside it. Leaving the size alone is
   the only way the two stay matched across platforms, since the native box is
   13px on macOS and differs elsewhere. The gate still taps these while
   walking, so the finger-sized target comes back on a coarse pointer — the
   device that actually needs it. */
.col-checkin .checkin-check {
  cursor: pointer;
  accent-color: var(--accent, #58A0D8);
}
@media (pointer: coarse) {
  .col-checkin .checkin-check { width: 24px; height: 24px; }
}

/* Check-in turned off in Settings → Check In: the column and the turnout
   readout come out of the roster entirely rather than sitting there inert. */
#reg-table.checkin-off .col-checkin,
.checkin-count.hidden { display: none; }

/* ── Vendor & sponsor stall applications (Show Setup → Vendors) ─────────── */
.vendor-badge {
  display: inline-block; min-width: 18px; padding: 1px 5px; margin-left: 6px;
  background: var(--accent, #58A0D8); color: #0D0D12;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.62rem; font-weight: 700; line-height: 1.5; text-align: center;
}
.vendor-link-box { display: flex; align-items: stretch; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.5rem; }
.vendor-link {
  flex: 1 1 18rem; min-width: 0;
  padding: 0.6rem 0.75rem;
  background: var(--bg-inset, #EEEEEF);
  border: 1px solid var(--border, #E4E4E7);
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.8rem; color: var(--text, #1A1C1D);
  overflow-wrap: anywhere;
}
.vendor-filter { display: flex; flex-wrap: wrap; gap: 0.35rem; margin: 0.75rem 0 1rem; }
.vendor-list { display: grid; gap: 1rem; }

.vendor-card {
  background: var(--bg-card, #FFF);
  border: 1px solid var(--border, #E4E4E7);
  border-left: 3px solid var(--border-strong, #D4D4D8);
  padding: 1rem 1.1rem;
}
.vendor-card--pending    { border-left-color: var(--accent, #58A0D8); }
.vendor-card--approved   { border-left-color: var(--success, #10B981); }
.vendor-card--declined   { border-left-color: var(--border-strong, #D4D4D8); opacity: 0.72; }
.vendor-card--waitlisted { border-left-color: var(--accent-ink, #1F6BA5); }

.vendor-card-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 0.75rem; }
.vendor-biz {
  margin: 0; font-family: var(--font-label, 'Archivo', sans-serif); font-weight: 700;
  font-size: 1rem; letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--text, #1A1C1D);
}
.vendor-meta, .vendor-specs, .vendor-contact, .vendor-reviewed {
  margin: 0.2rem 0 0; font-size: 0.85rem; color: var(--text-muted, #5F5E5F);
}
.vendor-desc { margin: 0.6rem 0 0; font-size: 0.92rem; color: var(--text, #1A1C1D); }
.vendor-notes {
  margin: 0.5rem 0 0; padding-left: 0.7rem;
  border-left: 2px solid var(--border, #E4E4E7);
  font-size: 0.88rem; font-style: italic; color: var(--text-muted, #5F5E5F);
}
.vendor-status {
  flex: 0 0 auto;
  padding: 0.25rem 0.6rem;
  font-family: var(--font-label, 'Archivo', sans-serif); font-weight: 700;
  font-size: 0.66rem; letter-spacing: 0.1em; text-transform: uppercase;
  background: var(--bg-inset, #EEEEEF); color: var(--text-muted, #5F5E5F);
}
.vendor-status--pending    { background: var(--accent, #58A0D8); color: #0D0D12; }
.vendor-status--approved   { background: var(--success, #10B981); color: #0D0D12; }
.vendor-status--waitlisted { background: var(--accent-ink, #1F6BA5); color: #FFF; }

.vendor-spot { display: flex; align-items: flex-end; gap: 0.5rem; flex-wrap: wrap; margin-top: 0.9rem; }
.vendor-spot .settings-window-sublabel { flex: 1 0 100%; margin-bottom: 0.2rem; }
.vendor-spot-input { flex: 1 1 8rem; max-width: 12rem; }
.vendor-actions { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.9rem; }
.vendor-promoted {
  align-self: center;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.68rem; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--success-ink, #047857);
}
.vendor-reviewed {
  margin-top: 0.6rem;
  font-family: var(--font-mono, 'JetBrains Mono', monospace);
  font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase;
}

/* ── Show Options → Field Map ───────────────────────────────────────────── */
.fm-venue {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 12px; margin-bottom: 12px;
  background: var(--bg-inset); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
}
.fm-venue-label {
  font-family: var(--font-label); font-weight: 700; font-size: 0.66rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted);
}
.fm-venue-value { font-size: 0.9rem; color: var(--text-primary); }

/* The map centre is the ZIP-area point, not the street address — stated
   plainly rather than left to look correct. */
.fm-venue-approx {
  display: inline-block;
  margin-top: 0.25rem;
  font-family: 'JetBrains Mono', monospace;
  font-size: 0.68rem;
  letter-spacing: 0.02em;
  color: var(--warning);
}

.fm-toolbar { display: flex; align-items: center; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 10px; }
.fm-status {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted);
}
.fm-status.is-bad { color: var(--error); }

/* ── Your own map: opacity + placement handles ───────────────────────────
   The handles sit on the map canvas, so they carry their own contrast
   rather than inheriting the panel's: a corner marker has to stay findable
   over a dark aerial, a pale site plan, and anything in between. */
.fm-opacity { display: inline-flex; align-items: center; gap: 0.5rem; }
/* A class selector outranks the UA stylesheet's [hidden] { display: none },
   so without this the slider stays on screen after the map is removed. */
.fm-opacity[hidden] { display: none; }
.fm-opacity-label {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-muted);
}
.fm-opacity input[type="range"] { width: 108px; accent-color: var(--accent); cursor: pointer; }

/* Square like everything else, and sized for a fingertip — organizers place
   these on a laptop trackpad, often outdoors. */
.fm-handle {
  width: 18px; height: 18px; box-sizing: border-box; cursor: grab;
  background: var(--accent); border: 2px solid #F3EFE8;
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.55);
}
.fm-handle:active { cursor: grabbing; }
/* The centre handle reads as a different gesture, so it looks like one. */
.fm-handle--move {
  width: 26px; height: 26px; background: #F3EFE8; border-color: var(--accent);
  cursor: move; position: relative;
}
.fm-handle--move::after {
  content: ''; position: absolute; inset: 5px;
  border: 2px solid var(--accent);
}
#fm-place.is-on { background: var(--accent); color: var(--on-accent, #0C0C0E); }

.fm-mapwrap { position: relative; height: 460px; border: 1px solid var(--border); margin-bottom: 18px; }
#fm-map { position: absolute; inset: 0; }
.fm-fail {
  position: absolute; inset: 0; z-index: 5; overflow: auto; padding: 22px;
  background: var(--bg-card); font-size: 0.85rem; line-height: 1.7; color: var(--text-muted);
}
.fm-fail h4 {
  font-family: var(--font-display); font-size: 0.95rem; text-transform: uppercase;
  color: var(--error); margin: 0 0 10px;
}
.fm-fail code { font-family: var(--font-mono); color: var(--accent-ink); }
.fm-fail li { margin-bottom: 6px; }

/* Two columns on a console-width screen; stacked once it squeezes. */
.fm-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
@media (max-width: 960px) { .fm-grid { grid-template-columns: 1fr; } .fm-mapwrap { height: 360px; } }

.fm-zone {
  display: flex; align-items: center; gap: 9px; padding: 9px 10px; margin-bottom: 6px;
  background: var(--bg-card); border: 1px solid var(--border);
  border-left: 3px solid var(--zc, var(--accent)); cursor: pointer;
  transition: border-color 0.12s, transform 0.12s;
}
.fm-zone:hover { border-color: var(--border-strong); transform: translateY(-1px); }
.fm-zone.sel { border-color: var(--accent); box-shadow: 0 3px 0 var(--accent-hover); }
.fm-dot { width: 9px; height: 9px; flex: 0 0 auto; background: var(--zc, var(--accent)); }
.fm-zone-n {
  flex: 1; font-family: var(--font-label); font-weight: 700; font-size: 0.8rem;
  letter-spacing: 0.05em; text-transform: uppercase; color: var(--text-primary);
}
.fm-zone-w { font-size: 0.78rem; color: var(--text-primary); }
.fm-zone-w.none { color: var(--error); font-family: var(--font-mono); font-size: 0.64rem;
  letter-spacing: 0.08em; text-transform: uppercase; }
.fm-x {
  flex: 0 0 auto; min-width: 26px; min-height: 26px; border: 0; background: transparent;
  color: var(--text-secondary); font-size: 1.15rem; line-height: 1; cursor: pointer;
}
.fm-x:hover { color: var(--error); }

.fm-judge { padding: 10px 11px; margin-bottom: 6px; background: var(--bg-card); border: 1px solid var(--border); }
.fm-judge-top { display: flex; align-items: baseline; gap: 10px; }
.fm-judge-n { flex: 1; font-size: 0.9rem; font-weight: 600; color: var(--text-primary); }
.fm-judge-z {
  font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent-ink);
}
.fm-judge-z.none { color: var(--text-secondary); }
.fm-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }
.fm-tag {
  font-family: var(--font-label); font-weight: 700; font-size: 0.6rem;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 3px 7px; background: var(--bg-inset); color: var(--text-muted);
  border: 1px solid var(--border);
}
.fm-tag.none { border-style: dashed; color: var(--text-secondary); }
.fm-empty {
  padding: 14px; border: 1px dashed var(--border-strong); text-align: center;
  color: var(--text-muted); font-size: 0.85rem; line-height: 1.55;
}

/* Judge list inside the map popup */
.mapboxgl-popup-content {
  background: var(--bg-card); color: var(--text-primary); border: 1px solid var(--accent);
  border-radius: 0; padding: 0; box-shadow: 0 6px 0 rgba(0,0,0,0.3);
}
.mapboxgl-popup-tip { display: none; }
.mapboxgl-popup-close-button { color: var(--text-secondary); font-size: 19px; padding: 0 7px; background: none; }
.mapboxgl-popup-close-button:hover { color: var(--text-primary); background: none; }
.fm-pop { width: 266px; padding: 11px 12px 12px; }
.fm-pop-h {
  font-family: var(--font-display); font-size: 0.82rem; text-transform: uppercase;
  letter-spacing: 0.01em; padding-right: 16px; margin-bottom: 8px; color: var(--text-primary);
}
.fm-pop-lab {
  font-family: var(--font-label); font-weight: 700; font-size: 0.58rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-muted); margin: 9px 0 5px;
}
.fm-pop-scroll { max-height: 164px; overflow-y: auto; }
.fm-pop-row {
  display: flex; align-items: center; gap: 7px; padding: 6px 7px; margin-bottom: 4px;
  background: var(--bg-inset); border: 1px solid var(--border);
}
.fm-pop-row.on { border-color: var(--accent); background: var(--accent-dim); }
.fm-pop-n { flex: 1; font-size: 0.78rem; line-height: 1.3; }
.fm-pop-c {
  font-family: var(--font-mono); font-size: 0.56rem; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--text-muted);
}
.fm-pop-btn {
  flex: 0 0 auto; min-height: 26px; padding: 3px 9px; border: 0; cursor: pointer;
  font-family: var(--font-body); font-weight: 600; font-size: 0.7rem;
  background: var(--accent); color: var(--on-accent);
}
.fm-pop-btn:hover { filter: brightness(1.08); }
.fm-pop-btn.warn { background: transparent; color: var(--error); border: 1px solid var(--border-strong); }

/* ── Judges → Judges (roster) ──────────────────────────────────────────────
   Deliberately the same vocabulary as the .fm-* roster on the Field Map tab
   next door: the two are halves of one section, and a judge should look like
   the same object on both. Adds the things only this tab shows — the coverage
   counters and the unjudged-class gap. */

/* Inline link inside body copy that performs an action (opens a modal, jumps
   to a section). A button for real, because that is what it does. */
.link-btn {
  background: none; border: 0; padding: 0;
  font: inherit; color: var(--accent-ink); font-weight: 600;
  text-decoration: underline; text-underline-offset: 2px;
  cursor: pointer;
}
.link-btn:hover { color: var(--accent-hover); }
.link-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.jr-stats { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 14px; }
.jr-stat {
  min-width: 112px; padding: 9px 12px;
  background: var(--bg-inset); border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
}
/* A gap is the one thing on this screen worth interrupting for. */
.jr-stat.is-bad { border-left-color: var(--error); }
.jr-stat-n {
  display: block; font-family: var(--font-mono); font-size: 1.15rem;
  font-weight: 700; color: var(--text-primary); line-height: 1.2;
}
.jr-stat.is-bad .jr-stat-n { color: var(--error); }
.jr-stat-l {
  display: block; margin-top: 2px;
  font-family: var(--font-label); font-weight: 700; font-size: 0.6rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-muted);
}

.jr-judge { padding: 10px 11px; margin-bottom: 6px; background: var(--bg-card); border: 1px solid var(--border); }
/* A judge with no assignment is the catch-all, not an unfinished row: they
   judge everything nobody else claimed. Marked as a distinct role with the
   accent edge, never as a warning. */
.jr-judge.is-open { border-left: 3px solid var(--accent); }
.jr-judge-scope {
  margin-top: 6px; font-size: 0.78rem; color: var(--text-secondary);
}
.jr-judge-top { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.jr-judge-n { font-size: 0.9rem; font-weight: 600; color: var(--text-primary); }
.jr-judge-e {
  flex: 1; font-family: var(--font-mono); font-size: 0.68rem; color: var(--text-secondary);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.jr-judge-z {
  font-family: var(--font-mono); font-size: 0.64rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--accent-ink); margin-left: auto;
}
.jr-judge-z.none { color: var(--text-secondary); }

.jr-tags { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 7px; }
.jr-tag {
  font-family: var(--font-label); font-weight: 700; font-size: 0.6rem;
  letter-spacing: 0.08em; text-transform: uppercase;
  padding: 3px 7px; background: var(--bg-inset); color: var(--text-muted);
  border: 1px solid var(--border);
}
/* Clubs read as a different kind of thing from classes without a second hue.
   Labelled too, because a club name and a class name look identical. */
.jr-tag.is-club { border-style: dashed; color: var(--accent-ink); }
.jr-tag.is-club i { font-style: normal; opacity: 0.65; font-size: 0.9em; }
.jr-tag.is-gap { background: var(--error-bg); color: var(--error); border-color: var(--error-border); }
.jr-tag.none { border-style: dashed; color: var(--text-secondary); }

.jr-empty {
  margin: 0; padding: 14px 0; font-size: 0.85rem; color: var(--text-secondary);
}
.jr-empty.is-bad { color: var(--error); }

.jr-gap {
  margin-top: 14px; padding: 12px 13px;
  background: var(--bg-inset); border: 1px solid var(--error-border);
  border-left: 3px solid var(--error);
}
.jr-gap h4 {
  margin: 0; font-family: var(--font-display); font-size: 0.82rem;
  letter-spacing: 0.01em; text-transform: uppercase; color: var(--error);
}
.jr-gap p { margin: 9px 0 0; font-size: 0.8rem; color: var(--text-secondary); }

@media (max-width: 640px) {
  .jr-stat { flex: 1 1 100%; min-width: 0; }
  .jr-judge-e { flex: 1 1 100%; }
  .jr-judge-z { margin-left: 0; }
}

/* ── Judging applications (Show Setup → Judges → Applications) ─────────────
   One card is one person, read top to bottom: who they are, how to reach
   them, what they drive, then their own words. An organizer reads these on a
   phone between other jobs, so the card is single-column at every width and
   nothing is truncated — the answers are the reason they opened the tab.

   Status is a top rail plus a chip, not a coloured left edge: a 3px side
   stripe is decoration standing in for a label, and it carries no meaning to
   anyone who can't see the colour. The chip says the word. */
.ja-list { display: grid; gap: 1rem; }

.ja-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-top: 3px solid var(--border-mid);
  padding: 1rem 1.1rem 1.1rem;
}
.ja-card--pending    { border-top-color: var(--accent, #58A0D8); }
.ja-card--approved   { border-top-color: var(--success, #10B981); }
.ja-card--waitlisted { border-top-color: var(--accent-ink, #1F6BA5); }
.ja-card--declined   { border-top-color: var(--border-mid); opacity: 0.72; }

.ja-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 0.75rem; margin-bottom: 0.85rem;
}
.ja-ident { min-width: 0; }
.ja-name {
  font-family: var(--font-label, 'Archivo', sans-serif);
  font-weight: 700; font-size: 1.05rem; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text-primary);
  margin: 0 0 0.2rem; overflow-wrap: anywhere;
}
.ja-meta {
  margin: 0; font-size: 0.85rem; color: var(--text-secondary);
  overflow-wrap: anywhere;
}
.ja-status {
  flex: 0 0 auto;
  font-family: var(--font-label, 'Archivo', sans-serif);
  font-weight: 700; font-size: 0.68rem; letter-spacing: 0.1em;
  text-transform: uppercase; padding: 0.3rem 0.55rem;
  background: var(--bg-inset); color: var(--text-secondary);
  border: 1px solid var(--border-mid);
}
.ja-status--pending    { background: var(--accent, #58A0D8); color: var(--on-accent, #0D0D12); border-color: transparent; }
.ja-status--approved   { background: var(--success, #10B981); color: #0D0D12; border-color: transparent; }
.ja-status--waitlisted { background: var(--accent-dim); color: var(--accent-ink, #1F6BA5); border-color: var(--accent, #58A0D8); }

/* Facts as a definition list: label above value, so a long email or a long
   car name wraps under its own label instead of pushing a two-column grid
   sideways on a phone. */
.ja-facts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.7rem 1rem; margin: 0 0 0.9rem;
}
.ja-fact { min-width: 0; }
.ja-fact dt {
  font-family: var(--font-label, 'Archivo', sans-serif);
  font-weight: 700; font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-secondary); margin-bottom: 0.2rem;
}
.ja-fact dd {
  margin: 0; font-size: 0.95rem; color: var(--text-primary);
  overflow-wrap: anywhere;
}
.ja-fact dd a { color: var(--accent-ink, var(--accent)); }

.ja-photo {
  display: block; width: 100%; max-height: 260px; object-fit: cover;
  border: 1px solid var(--border); margin: 0 0 0.9rem;
}

/* Their own words. Set as prose, not as metadata: this is what the organizer
   is actually deciding on. */
.ja-answer { margin: 0 0 0.9rem; }
.ja-q {
  font-family: var(--font-label, 'Archivo', sans-serif);
  font-weight: 700; font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-secondary); margin: 0 0 0.3rem;
}
.ja-a {
  margin: 0; font-size: 0.95rem; line-height: 1.6; color: var(--text-primary);
  white-space: pre-wrap;        /* they typed line breaks; keep them */
  overflow-wrap: anywhere;
  text-wrap: pretty;
}
.ja-answer--private .ja-a {
  background: var(--bg-inset); border: 1px solid var(--border);
  padding: 0.6rem 0.75rem;
}

.ja-actions {
  display: flex; flex-wrap: wrap; gap: 0.6rem; align-items: center;
  margin-top: 1rem;
}
/* One row, one control size. The three actions come from two different
   components: Approve/Waitlist are .btn-add, whose height comes purely from
   padding (~42px), while Delete is .btn-delete-row, shared with table-row
   Edit, which carries its own min-height: 44px and tighter side padding. Side
   by side that rendered ragged. Pin height and padding here rather than on
   .btn-add globally — that class is all over the admin and this row is the
   only place the two components meet. 44px also keeps the touch target at the
   bar DESIGN.md sets. */
.ja-actions > button {
  min-height: 44px;
  padding: 0 1rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
.ja-onroster {
  font-family: var(--font-label, 'Archivo', sans-serif);
  font-weight: 700; font-size: 0.68rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--success-ink, #047857);
}
.ja-reviewed {
  margin: 0.7rem 0 0; font-size: 0.8rem; color: var(--text-secondary);
}

/* Phones: the organizer is holding this one-handed at a venue. Actions go
   full width and stack, so Approve is a thumb target rather than a chip
   sharing a row with Decline. */
@media (max-width: 640px) {
  .ja-head { flex-direction: column; gap: 0.5rem; }
  .ja-facts { grid-template-columns: 1fr; gap: 0.6rem; }
  .ja-actions { flex-direction: column; align-items: stretch; }
  .ja-actions > button { width: 100%; min-height: 48px; }
  .ja-onroster { text-align: center; padding: 0.3rem 0; }
}

/* The blurb editor is a label, an explanation, a multi-line field and a save
   button. .settings-row lays its children out in a centred flex ROW, which is
   right for a label-plus-switch and wrong here: on a phone it crushed the
   textarea to a few characters wide. Stack it. */
.ja-blurb-row {
  display: block;
}
.ja-blurb-head { margin-bottom: 0.6rem; }
.ja-blurb-input {
  display: block;
  width: 100%;
  min-height: 5.5rem;
  margin-bottom: 0.6rem;
  font-family: inherit;
  line-height: 1.5;
  resize: vertical;
}
.ja-blurb-save { min-height: 44px; }

/* Touch sizing for the controls this panel owns.
   admin.css caps every button inside #classes-modal at 44px on a coarse
   pointer, which is the console's house convention for a dense desk surface
   reached occasionally by thumb. This panel is the other case: an organizer
   reviewing strangers on a phone at a venue, deciding whether to hand someone
   a login. That is a field flow, so it takes the ≥48px field bar. The ID in
   the selector is deliberate — it is what outranks the blanket 44px rule
   without loosening it for the rest of Show Setup. The filter chips stay on
   the shared component's own sizing. */
@media (pointer: coarse) {
  #classes-modal .ja-actions > button,
  #classes-modal #judge-link-copy,
  #classes-modal .ja-blurb-save { min-height: 48px; }
}

/* ── Awards review ──────────────────────────────────────────────────────
   The guardrail pass between the roster and the podium: ties the judges
   still owe a decision, cards that scored within a hair of perfect, and
   cards submitted faster than the show's minimum. Three sections in one
   scroller, each a stack of cards — read on a laptop at the awards table,
   sometimes on a phone at the back of the field, so the rows stay legible
   and the pick buttons stay thumb-sized. */
.awards-review-body {
  max-height: min(62vh, 640px);
  overflow-y: auto;
  margin-bottom: 1.1rem;
}
.ar-section + .ar-section { margin-top: 1.4rem; }
.ar-section-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding-bottom: 6px; margin-bottom: 10px;
  border-bottom: 2px solid var(--accent);
}
.ar-section-title {
  font-family: var(--font-label); font-weight: 700; font-size: 0.74rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-primary);
}
.ar-section-sub { font-size: 0.82rem; line-height: 1.5; color: var(--text-muted); }
.ar-count {
  font-family: var(--font-mono); font-size: 0.66rem; letter-spacing: 0.08em;
  padding: 2px 7px; background: var(--bg-inset); color: var(--text-muted);
}
.ar-clear {
  padding: 0.9rem 1rem;
  background: var(--bg-inset); border: 1px solid var(--border);
  border-left: 3px solid var(--success, #2E7D32);
  font-size: 0.9rem; line-height: 1.55; color: var(--text-primary);
}

/* One tie, one card: the place at stake, then a row per car with the pick
   button on the right. */
.ar-card {
  padding: 0.85rem 0.95rem;
  background: var(--bg-card); border: 1px solid var(--border);
}
.ar-card + .ar-card { margin-top: 0.6rem; }
.ar-card--open { border-left: 3px solid var(--warning); }
.ar-card--done { border-left: 3px solid var(--success, #2E7D32); }
.ar-card-head {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  margin-bottom: 0.6rem;
}
.ar-card-title {
  font-family: var(--font-label); font-weight: 700; font-size: 0.78rem;
  letter-spacing: 0.08em; text-transform: uppercase; color: var(--text-primary);
}
/* Card heads that name a PERSON rather than a class or an award: the
   signage voice is for structure, and "DAVE MACKEY" in tracked caps reads
   like a headline instead of an entrant. */
.ar-card-name {
  font-size: 0.95rem; color: var(--text-primary);
}
.ar-card-meta {
  font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.04em;
  color: var(--text-muted);
}
.ar-tie-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 0.5rem 0;
}
.ar-tie-row + .ar-tie-row { border-top: 1px solid var(--border); }
.ar-tie-car { flex: 1 1 14rem; min-width: 0; }
.ar-tie-name { font-size: 0.92rem; color: var(--text-primary); }
.ar-tie-sub { font-size: 0.8rem; color: var(--text-muted); }
.ar-num {
  font-family: var(--font-mono); font-weight: 600;
  color: var(--accent-ink, var(--accent));
}
.ar-pick { min-height: 44px; white-space: nowrap; }
.ar-won {
  font-family: var(--font-label); font-weight: 700; font-size: 0.66rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  padding: 4px 8px; background: var(--accent); color: var(--on-accent, #0D0D12);
}
.ar-outcome { margin-top: 0.5rem; font-size: 0.82rem; color: var(--text-muted); }

/* The scorecard behind a near-perfect total — every category, so an
   all-maximum row reads at a glance. */
.ar-cats {
  display: flex; flex-wrap: wrap; gap: 4px 6px; margin-top: 0.55rem;
}
.ar-cat {
  font-family: var(--font-mono); font-size: 0.68rem; letter-spacing: 0.02em;
  padding: 3px 7px; background: var(--bg-inset); color: var(--text-muted);
  border: 1px solid var(--border);
}
.ar-cat--max { border-color: var(--warning); color: var(--text-primary); }
.ar-score {
  font-family: var(--font-mono); font-weight: 700; font-size: 0.95rem;
  color: var(--text-primary);
}
.ar-flag {
  font-family: var(--font-label); font-weight: 700; font-size: 0.64rem;
  letter-spacing: 0.12em; text-transform: uppercase;
  padding: 3px 7px; background: var(--warning); color: #1A1C1D;
}

@media (max-width: 560px) {
  .ar-tie-row { align-items: stretch; }
  .ar-pick { width: 100%; }
}

/* Club disqualification. The reason picker rides in the shared confirm
   dialog (see pickDqReason), so it only needs spacing; a club that is
   entirely out reads as out in its roster line. */
[data-theme] .club-dq-reason { margin-top: 0.75rem; width: 100%; }
[data-theme] .club-row-count--dq {
  color: var(--error, #BA1A1A);
  font-weight: 600;
}
