/* DeckSide — Web Lineup Builder
   Dark pool-deck theme that mirrors the desktop app's lineup builder.
   No build step; one CSS file. */

/* Token migration: the lineup originally used `--accent` for the green
   "committed/active" state and `--accent-2` for cyan brand. Both have
   been renamed to match the canonical desktop-app names in tokens.css:
   - var(--primary) = emerald green (was lineup's --accent)
   - var(--primary)  = sky cyan       (was lineup's --accent-2)
   Lineup-specific surface tokens (--bg / --bg-elevated / --bg-card /
   --border / --text / --text-muted / --text-dim) are kept here because
   their values differ slightly from tokens.css's canonical values
   (lineup uses a slightly darker pool-deck navy). --danger / --warn
   match tokens.css but are kept here for the lineup's standalone
   styles.css to remain self-sufficient. */
:root {
  --bg: #0e1320;
  --bg-elevated: #151b2c;
  --bg-card: #1a2236;
  --border: #2a3450;
  --text: #e6edf6;
  --text-muted: #8794ad;
  --text-dim: #6478a0;
  --danger: #ef4444;
  --warn: #f5c518;
  /* --accent (cyan) and --primary (emerald) inherited from /tokens.css */
}

/* Light mode — flipped by the toolbar ☀/☾ toggle, which sets
   data-theme="light" on <html>. These MUST live here (not only in
   tokens.css): the lineup's own :root block above redeclares the surface
   tokens, and since styles.css loads after tokens.css with equal
   specificity, tokens.css's [data-theme="light"] would lose on source
   order. Using :root[data-theme="light"] (specificity 0,2,0) guarantees
   the win over the plain :root (0,1,0) regardless of load order. */
:root[data-theme="light"] {
  --bg: #f8fafc;
  --bg-elevated: #ffffff;
  --bg-card: #eef2f7;
  --border: #cbd5e1;
  --text: #0f172a;
  --text-muted: #475569;
  --text-dim: #64748b;
  --danger: #dc2626;
  --warn: #b45309;
}

/* Light-mode legibility fixes. Several pills/badges hardcode pale text
   colors (#fca5a5 red / #b6e8b8 green / #fde68a amber) that were tuned
   for dark backgrounds and wash out on light tints. Remap them to darker,
   accessible shades. The rgba accent tints behind them read fine as soft
   pastels on white, so only the text (and a couple of health-pill tints)
   need adjusting. */
[data-theme="light"] .upload-error        { color: #b91c1c; }
[data-theme="light"] .coach-chip          { color: #15803d; }
[data-theme="light"] .rr-quickadd         { color: #15803d; }
[data-theme="light"] .rr-assigned-chip    { color: #166534; }
[data-theme="light"] .evt-badge-open      { color: #b91c1c; }
[data-theme="light"] .evt-badge-cap       { color: #b45309; }
[data-theme="light"] .evt-badge-scratched { color: #b91c1c; }

[data-theme="light"] .sb-health-good,
[data-theme="light"] .sb-health-good .sb-num { color: #15803d; }
[data-theme="light"] .sb-health-mid,
[data-theme="light"] .sb-health-mid .sb-num  { color: #a16207; }
[data-theme="light"] .sb-health-low,
[data-theme="light"] .sb-health-low .sb-num  { color: #b91c1c; }
/* Firm up the health-pill tints so the colored chip still reads on white. */
[data-theme="light"] .sb-health-good { background: rgba(22, 163, 74, 0.14); border-color: rgba(22, 163, 74, 0.5); }
[data-theme="light"] .sb-health-mid  { background: rgba(217, 119, 6, 0.14);  border-color: rgba(217, 119, 6, 0.5); }
[data-theme="light"] .sb-health-low  { background: rgba(220, 38, 38, 0.12);  border-color: rgba(220, 38, 38, 0.5); }

* { box-sizing: border-box; }

/* Honor the HTML `hidden` attribute even when an element has an
   explicit `display: flex`/`grid`/`block` rule below. Without this,
   the picker overlay + lineup view (both display:flex) ignore the
   `hidden` attribute and render on top of the upload card on first
   paint. */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, sans-serif;
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
}

.muted { color: var(--text-muted); }

/* Fixed back-to-site button — top-left, visible across all views.
   Reads as a real button rather than a stray underlined link. */
.back-to-site {
  position: fixed;
  top: 16px;
  right: 16px;
  z-index: 200;
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 9px 16px 9px 12px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  color: var(--text);
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.01em;
  text-decoration: none;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.25);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background .12s ease, color .12s ease, border-color .12s ease, transform .12s ease, box-shadow .12s ease;
}
.back-to-site svg {
  display: block;
  color: var(--accent);
  transition: transform .12s ease;
}
.back-to-site:hover,
.back-to-site:focus-visible {
  background: var(--bg-elevated);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 4px 14px rgba(56, 189, 248, 0.18);
  transform: translateY(-1px);
  outline: none;
}
.back-to-site:hover svg,
.back-to-site:focus-visible svg {
  transform: translateX(-2px);
}
.back-to-site:active {
  transform: translateY(0);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
/* Hide the floating back button while the coach is inside the builder —
   the .lineup-header runs full width and the in-app #btn-back already
   covers exiting back to the upload card. Without this, the floating
   button overlaps the meet-name header. The upload, password, and
   manual views still show it (their cards are centered with empty
   top-left space). */
body:has(#lineup-view:not([hidden])) .back-to-site {
  display: none;
}

button.btn {
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 12px;
  font-size: 13px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
button.btn:hover {
  background: var(--bg-elevated);
  border-color: var(--primary);
}
button.btn-primary {
  background: var(--primary);
  color: #fff;
  border-color: var(--primary);
}
button.btn-primary:hover { background: #3da041; }
button.btn-small { padding: 4px 8px; font-size: 12px; }
button.btn-secondary { background: var(--bg-card); }

input[type="search"] {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 10px;
  font-size: 13px;
  width: 100%;
}
input[type="search"]:focus {
  outline: none;
  border-color: var(--primary);
}

/* ───────────────────────── Upload view ───────────────────────── */

.upload-view {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

/* Save + Print modals reuse the .upload-view styling but appear ON TOP
   of the lineup view rather than instead of it (like upload / password /
   manual views do). Without overlay positioning they'd render in the
   document flow BELOW the 100vh-tall lineup view — coach clicks Save or
   Print, modal opens far below the fold, looks like nothing happened.
   Fix: pin them as full-viewport overlays with a dark scrim. The
   inherited display:flex + center alignment still centers the card. */
#save-modal,
#print-modal {
  position: fixed;
  inset: 0;
  z-index: 2000;
  background: rgba(15, 23, 42, 0.85);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  min-height: 0;
}

.upload-card {
  max-width: 600px;
  width: 100%;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 32px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
}
.brand {
  margin: 0 0 4px;
  font-size: 28px;
  color: var(--accent);
}
.brand-sub {
  display: inline-block;
  margin-left: 8px;
  font-size: 14px;
  font-weight: 400;
  color: var(--text-muted);
  vertical-align: middle;
}

.upload-drop {
  display: block;
  margin-top: 24px;
  padding: 32px;
  border: 2px dashed var(--border);
  border-radius: 10px;
  background: var(--bg-card);
  cursor: pointer;
  text-align: center;
  transition: border-color 0.12s, background 0.12s;
}
.upload-drop:hover, .upload-drop.is-dragover {
  border-color: var(--primary);
  background: var(--bg-elevated);
}
.upload-drop-icon { font-size: 40px; margin-bottom: 12px; }
.upload-drop-text strong { display: block; font-size: 16px; margin-bottom: 4px; }
.upload-drop-text span { color: var(--text-muted); font-size: 13px; }
.upload-drop-text code {
  background: var(--bg);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 12px;
}

.upload-howto {
  margin-top: 18px;
  font-size: 13px;
  color: var(--text-muted);
}
.upload-howto summary {
  cursor: pointer;
  user-select: none;
  padding: 6px 0;
}
.upload-howto summary:hover { color: var(--text); }
.upload-howto ol {
  margin: 8px 0 0 4px;
  padding-left: 20px;
  line-height: 1.6;
}
.upload-howto code {
  background: var(--bg);
  padding: 1px 5px;
  border-radius: 3px;
  font-size: 12px;
}

.upload-error {
  margin-top: 18px;
  padding: 10px 14px;
  background: rgba(239, 68, 68, 0.12);
  border: 1px solid rgba(239, 68, 68, 0.4);
  border-radius: 6px;
  color: #fca5a5;
  font-size: 13px;
}

/* ─────────────── Upload card: alt actions (manual / sample) ─────────────── */

.upload-or-divider {
  margin: 18px 0 14px;
  text-align: center;
  position: relative;
  color: var(--text-muted);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.upload-or-divider::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 1px;
  background: var(--border);
}
.upload-or-divider span {
  position: relative;
  background: var(--bg-elevated);
  padding: 0 10px;
}
.upload-alt-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}
.upload-alt-actions .btn {
  flex: 1 1 220px;
  padding: 10px 14px;
  font-size: 14px;
}

/* ─────────────── Manual view (CSV roster paste) ─────────────── */

.manual-field {
  display: block;
  margin-top: 20px;
}
.manual-label {
  display: block;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.manual-csv {
  width: 100%;
  box-sizing: border-box;
  font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
  font-size: 13px;
  line-height: 1.5;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  resize: vertical;
  min-height: 240px;
}
.manual-csv:focus {
  outline: none;
  border-color: var(--primary);
}

/* ─────────────── Password view (encrypted envelope) ─────────────── */

.brand-sub-strong {
  display: inline-block;
  margin-left: 8px;
  font-size: 18px;
  font-weight: 600;
  color: var(--text);
  vertical-align: middle;
  word-break: break-word;
}
.password-field {
  display: block;
  margin-top: 24px;
}
.password-label {
  display: block;
  font-size: 13px;
  color: var(--text-muted);
  margin-bottom: 6px;
}
/* Every input inside a `.password-field` shares the same full-width
   chrome — applies to the password XXXX field AND the email field
   added in the coach-attribution PR. */
.password-field input {
  width: 100%;
  padding: 12px 14px;
  font-size: 15px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-sizing: border-box;
}
/* Monospace + letter spacing only for the password (so XXXX-XXXX… is
   visually crisp). The email field stays in the default UI font so a
   normal address renders comfortably. */
#password-input {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.04em;
}
.password-field input:focus {
  outline: none;
  border-color: var(--primary);
}
.password-actions {
  display: flex;
  gap: 10px;
  margin-top: 16px;
  flex-wrap: wrap;
}
#password-decrypt {
  min-width: 120px;
}
#password-decrypt:disabled {
  opacity: 0.6;
  cursor: progress;
}

/* ─────────────── Save modal (encrypted export) ─────────────── */

.save-modal-filename {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 13px;
  word-break: break-all;
  margin: 4px 0 0;
}
.save-modal-pw-row {
  display: flex;
  gap: 8px;
  align-items: stretch;
}
.save-modal-pw-row #save-modal-password {
  flex: 1;
  padding: 12px 14px;
  font-size: 17px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.06em;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-sizing: border-box;
  text-align: center;
}
.save-modal-pw-row #save-modal-password:focus {
  outline: none;
  border-color: var(--primary);
}
.save-modal-pw-row #save-modal-copy {
  padding: 0 14px;
}
.save-modal-copy-status {
  display: inline-block;
  margin-top: 8px;
  font-size: 12px;
  color: var(--primary);
}
.save-modal-warning {
  margin-top: 14px;
  padding: 10px 14px;
  background: rgba(245, 197, 24, 0.08);
  border: 1px solid rgba(245, 197, 24, 0.3);
  border-radius: 6px;
  color: var(--warn);
  font-size: 13px;
}

/* ─────────────── Plain-file warning banner ─────────────── */

.plain-file-warning {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: rgba(245, 197, 24, 0.08);
  border-bottom: 1px solid rgba(245, 197, 24, 0.3);
  color: var(--warn);
  font-size: 13px;
}
.plain-file-warning-icon { font-size: 14px; }
.plain-file-warning-text { flex: 1; }
.plain-file-warning-close {
  background: transparent;
  border: 1px solid transparent;
  color: var(--warn);
  cursor: pointer;
  font-size: 16px;
  line-height: 1;
  padding: 2px 8px;
  border-radius: 4px;
}
.plain-file-warning-close:hover {
  border-color: rgba(245, 197, 24, 0.5);
  background: rgba(245, 197, 24, 0.08);
}

/* ───────────────────────── Lineup view ───────────────────────── */

.lineup-view {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.lineup-header {
  padding: 12px 20px;
  border-bottom: 1px solid var(--border);
  background: var(--bg-elevated);
  position: sticky;
  top: 0;
  z-index: 10;
}
.lineup-title-row {
  display: flex;
  align-items: center;
  gap: 10px;
}
.meet-name {
  margin: 0;
  font-size: 18px;
  color: var(--text);
}
.meet-meta {
  font-size: 13px;
}
.coach-chip {
  display: inline-flex;
  align-items: center;
  padding: 3px 10px;
  font-size: 12px;
  font-weight: 700;
  color: #b6e8b8;
  background: rgba(76, 175, 80, 0.16);
  border: 1px solid rgba(76, 175, 80, 0.5);
  border-radius: 10px;
  letter-spacing: 0.2px;
}
.header-spacer { flex: 1; }

/* Multi-coach merge — loaded-alternates strip + per-coach chips. */
.alternates-strip {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
  padding: 6px 10px;
  background: rgba(56, 189, 248, 0.06);
  border: 1px solid rgba(56, 189, 248, 0.3);
  border-radius: 4px;
  font-size: 12px;
}
.alternates-strip-label {
  font-weight: 700;
  color: var(--text-muted);
  letter-spacing: 0.3px;
  text-transform: uppercase;
  font-size: 10px;
}
#alternates-strip-list {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 6px;
}
.alt-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 8px;
  background: color-mix(in srgb, var(--alt-color) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--alt-color) 55%, transparent);
  border-radius: 10px;
  font-size: 12px;
  font-weight: 600;
  color: var(--text);
}
.alt-chip-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--alt-color);
}
.alt-chip-remove {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 14px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}
.alt-chip-remove:hover { color: var(--danger); }
.alternates-clear-all {
  margin-left: auto;
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 4px;
  font-size: 11px;
  padding: 3px 8px;
  cursor: pointer;
}
.alternates-clear-all:hover { color: var(--text); border-color: var(--accent); }

/* Per-slot alternate-coach overlay dots */
.slot-alt-overlay {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding-left: 4px;
}
.slot-alt-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--alt-color);
  border: 1px solid rgba(0, 0, 0, 0.35);
  cursor: help;
}
.slot.has-alt-overlay { box-shadow: inset 0 -2px 0 0 rgba(56, 189, 248, 0.35); }

.lineup-warnings {
  margin-top: 8px;
  padding: 6px 10px;
  background: rgba(245, 197, 24, 0.12);
  border: 1px solid rgba(245, 197, 24, 0.4);
  border-radius: 4px;
  font-size: 12px;
  color: var(--warn);
}

.lineup-filters {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.filter-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-muted);
}
.filter-field select {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 13px;
}
.filter-field select:focus { outline: none; border-color: var(--primary); }
.filter-count { font-size: 12px; margin-left: auto; }

#btn-focus.is-active,
#btn-density.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: #0e1320;
}

/* Column-resize handle between roster panel and events grid */
.lineup-main { position: relative; }
.col-resize-handle {
  position: absolute;
  top: 16px;
  bottom: 16px;
  /* Wide invisible hit zone (16px total) so the handle is easy to grab.
     The visible 1px line sits in the middle via the ::before pseudo. */
  width: 1px;
  background: transparent;
  cursor: col-resize;
  z-index: 4;
  border-left: 10px solid transparent;
  border-right: 10px solid transparent;
  /* Center on the grid gap between the roster col and events col. The
     gap starts at: lineup-main padding-left (20px) + roster width, and is
     16px wide, so its centre is +8px into the gap. Earlier this omitted
     the 20px padding offset, which dropped the hit zone onto the roster
     scrollbar — making the bar hard to grab and the resize too easy to
     trigger. JS sets --roster-width on drag so the handle tracks the col. */
  left: calc(var(--roster-width, 340px) + 28px);
  margin-left: -10px;
  box-sizing: content-box;
}
.col-resize-handle::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 1px;
  height: 30px;
  margin-top: -15px;
  background: var(--border);
  border-radius: 1px;
}
.col-resize-handle:hover::before { background: var(--accent); }
body.is-col-resizing { cursor: col-resize !important; user-select: none; }
body.is-col-resizing * { cursor: col-resize !important; }
@media (max-width: 720px) { .col-resize-handle { display: none; } }

/* Compact mode — tighter cards, hide secondary info */
body.is-compact .event-card { padding: 8px 10px; gap: 2px; }
body.is-compact .event-meta { display: none; }
body.is-compact .evt-badges { gap: 2px; }
body.is-compact .slot { padding: 4px 8px; }
body.is-compact .slot-time { display: none; }
body.is-compact .add-entry { padding: 4px 8px; font-size: 11px; }
body.is-compact .add-team { padding: 4px 8px; font-size: 11px; }
body.is-compact .relay-team { padding: 6px; gap: 2px; }
body.is-compact .roster-row { padding: 5px 8px 5px 26px; gap: 2px; }
body.is-compact .rr-select { top: 8px; }
body.is-compact .rr-bio { display: none; }
body.is-compact .rr-pbs, body.is-compact .rr-pbs-empty { display: none; }

.lineup-main {
  display: grid;
  grid-template-columns: var(--roster-width, 340px) 1fr;
  gap: 16px;
  padding: 16px 20px;
  flex: 1;
  align-items: start;
}

/* Roster panel */
.roster-panel {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  position: sticky;
  top: 70px;
  max-height: calc(100vh - 90px);
  overflow: hidden;
}
.roster-header { display: flex; justify-content: space-between; align-items: baseline; }
.roster-header h3 { margin: 0; font-size: 14px; }
.roster-list {
  display: flex;
  flex-direction: column;
  gap: 4px;
  overflow-y: auto;
  padding-right: 4px;
}

.roster-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 3px;
  /* Extra left padding reserves a quiet column for the bulk-select
     checkbox so it never crowds the swimmer name. Reserved unconditionally
     so the layout doesn't jitter when the checkbox fades in on hover. */
  padding: 8px 10px 8px 30px;
  border-radius: 6px;
  border: 1px solid transparent;
  background: var(--bg-card);
  cursor: grab;
  user-select: none;
  font-size: 13px;
}
.rr-select {
  position: absolute;
  /* Vertically centered on the swimmer name's optical center (name is
     16px font, ~22px line box starting at the 8px row-top — so the
     checkbox's center should land ~19px from the top). */
  top: 11px;
  left: 9px;
  margin: 0;
  cursor: pointer;
  accent-color: var(--accent);
  opacity: 0;
  transition: opacity 0.12s;
  z-index: 2;
}
.roster-row:hover .rr-select,
.roster-row.is-selected .rr-select { opacity: 1; }
.roster-row.is-selected {
  background: rgba(56, 189, 248, 0.18);
  border-color: rgba(56, 189, 248, 0.55);
}

.bulk-actions {
  position: sticky;
  bottom: 32px;
  left: 50%;
  transform: translateX(-50%);
  width: fit-content;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 8px 14px;
  background: var(--bg-elevated);
  border: 1px solid var(--accent);
  border-radius: 8px;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
  z-index: 50;
}
.bulk-count {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
}

.slot-suggest {
  color: var(--text-muted);
  font-style: italic;
  opacity: 0.75;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.slot-suggest[draggable="true"] { cursor: grab; }
.slot-suggest[draggable="true"]:active { cursor: grabbing; }

.filter-search {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 4px 8px;
  font-size: 12px;
  max-width: 200px;
}
.filter-search:focus { outline: none; border-color: var(--primary); }

.rr-scratch {
  position: absolute;
  top: 4px;
  right: 4px;
  background: transparent;
  border: 1px solid transparent;
  color: var(--text-dim);
  border-radius: 4px;
  padding: 2px 6px;
  font-size: 14px;
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s, color 0.12s, background 0.12s, border-color 0.12s;
  z-index: 2;
}
.roster-row:hover .rr-scratch { opacity: 1; }
.rr-scratch:hover {
  background: rgba(239, 68, 68, 0.15);
  border-color: rgba(239, 68, 68, 0.4);
  color: var(--danger);
}
.roster-row.is-scratched .rr-scratch {
  opacity: 1;
  color: var(--danger);
  background: rgba(239, 68, 68, 0.18);
  border-color: rgba(239, 68, 68, 0.55);
}
.roster-row:hover { border-color: var(--border); }
.roster-row:active { cursor: grabbing; }
.roster-row.is-committed {
  box-shadow: inset 3px 0 0 0 rgba(76, 175, 80, 0.85);
}
.roster-row.is-uncommitted { opacity: 0.45; }
.roster-row.is-uncommitted:hover { opacity: 0.7; }
.roster-row.is-scratched {
  background: rgba(217, 115, 102, 0.35);
  border-color: rgba(217, 115, 102, 0.85);
  cursor: default;
}
.roster-row.is-over-cap {
  background: rgba(239, 68, 68, 0.18);
  border-color: rgba(239, 68, 68, 0.65);
  box-shadow: inset 3px 0 0 0 var(--danger);
}
.roster-row.is-collapsed .rr-bio,
.roster-row.is-collapsed .rr-pbs,
.roster-row.is-collapsed .rr-pbs-empty,
.roster-row.is-collapsed .rr-assigned { display: none; }
.roster-row.is-collapsed .rr-name::before {
  content: '▸ ';
  color: var(--text-dim);
  font-size: 11px;
}
.roster-row:not(.is-collapsed) .rr-name::before {
  content: '▾ ';
  color: var(--text-dim);
  font-size: 11px;
}
.rr-name { cursor: pointer; }
.rr-name:hover { color: var(--accent); }

.rr-quickadd-row {
  margin-top: 4px;
  padding-top: 4px;
  border-top: 1px dashed var(--border);
}
.rr-quickadd {
  display: block;
  width: 100%;
  background: rgba(76, 175, 80, 0.18);
  border: 1px solid rgba(76, 175, 80, 0.55);
  color: #b6e8b8;
  border-radius: 4px;
  padding: 6px 10px;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  text-align: left;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.rr-quickadd:hover {
  background: rgba(76, 175, 80, 0.32);
  border-color: var(--primary);
  color: #fff;
}

.roster-group-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 6px 10px;
  margin-top: 4px;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 4px;
  cursor: pointer;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text);
  user-select: none;
}
.roster-group-header:hover { background: var(--bg-card); }
.roster-group-header::before {
  content: '▾';
  margin-right: 6px;
  color: var(--text-dim);
}
.roster-group-header.is-collapsed::before { content: '▸'; }
.rg-label { flex: 1; }
.rg-count {
  color: var(--text-muted);
  font-weight: 500;
  font-size: 11px;
  margin-left: 6px;
}
.rg-used {
  font-size: 11px;
  font-weight: 600;
  color: var(--text-muted);
  letter-spacing: normal;
  text-transform: none;
}
.rg-used-num {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.roster-row.is-scratched .rr-name { text-decoration: line-through; opacity: 0.85; }
.roster-row.is-dragging { opacity: 0.5; }

.rr-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  /* Reserve room on the right for the absolutely-positioned .rr-scratch
     button so the counts text ('1/4 · 1 ind · 0 relay') never sits
     underneath it. Space is reserved regardless of hover so the layout
     doesn't jitter when the button fades in. */
  padding-right: 30px;
}
.rr-name { font-weight: 700; font-size: 16px; }
.rr-counts {
  font-size: 13px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.rr-bio { font-size: 14px; color: var(--text-muted); }
.rr-pbs { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 2px; }
.rr-pb {
  display: inline-flex;
  align-items: baseline;
  gap: 4px;
  padding: 2px 6px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}
.rr-pb-stroke {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.3px;
  color: var(--text-muted);
}
.rr-pb-time { font-weight: 600; }
.rr-pbs-empty { font-style: italic; font-size: 12px; }
.rr-pbs-label, .rr-assigned-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.3px;
  text-transform: uppercase;
  color: var(--text-dim);
  margin-right: 2px;
  align-self: center;
}
.rr-assigned {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  margin-top: 2px;
  padding-top: 4px;
  border-top: 1px dashed var(--border);
}
.rr-assigned-chip {
  background: rgba(76, 175, 80, 0.12);
  border: 1px solid rgba(76, 175, 80, 0.4);
  color: #b6e8b8;
  border-radius: 3px;
  padding: 1px 6px;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  font-variant-numeric: tabular-nums;
}
.rr-assigned-chip:hover {
  background: rgba(76, 175, 80, 0.25);
  border-color: var(--primary);
  color: #fff;
}
/* Multi-select: selected chip uses accent fill + dark text so it reads as
   "active" against the unselected translucent state. Coach clicks several
   chips on one roster card to highlight all those event cards at once. */
.rr-assigned-chip.is-selected,
.rr-assigned-chip.is-selected:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: #0b1f0d;
}

/* Highlight pulse for the event card a coach just jumped to */
@keyframes card-jumped {
  0%   { box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.8); }
  100% { box-shadow: 0 0 0 0 rgba(76, 175, 80, 0); }
}
.event-card.was-just-jumped { animation: card-jumped 2.2s ease-out; }

/* Sustained highlight for multi-selected chips. Unlike .was-just-jumped
   (a one-shot pulse), this stays on until the coach clears the selection.
   Kept subtle: a coach with 5 chips selected will see 5 lit cards and we
   don't want it to look like an alarm. */
.event-card.is-multi-highlighted {
  border-color: var(--primary);
  box-shadow: 0 0 0 1px rgba(76, 175, 80, 0.55), 0 2px 10px rgba(76, 175, 80, 0.18);
}

/* Event grid */
.events-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: 10px;
}
.event-card {
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 12px 14px;
  display: flex;
  flex-direction: column;
  gap: 4px;
  transition: opacity 0.12s, filter 0.12s;
}
.events-grid.is-dragging .event-card.is-drag-ineligible {
  opacity: 0.25;
  filter: grayscale(1);
  pointer-events: none;
}
.events-grid.is-dragging .event-card.is-drag-eligible {
  order: -1;
}
.event-card.is-filtered-out { display: none; }
.events-grid.is-focus-active .event-card.is-focus-dimmed {
  opacity: 0.25;
  filter: grayscale(0.6);
}
.event-card.is-focused {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px rgba(56, 189, 248, 0.35);
}
.event-card .event-header { cursor: default; }
.events-grid.is-focus-active .event-card:not(.is-focus-dimmed) .event-header,
.event-card.is-focused .event-header { cursor: pointer; }

.evt-badges {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 2px 0 4px;
}
.evt-badge {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  padding: 2px 6px;
  border-radius: 3px;
  font-size: 12px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  border: 1px solid transparent;
}
.evt-badge-open {
  color: #fca5a5;
  background: rgba(239, 68, 68, 0.15);
  border-color: rgba(239, 68, 68, 0.4);
}
.evt-badge-cap {
  color: #fde68a;
  background: rgba(245, 197, 24, 0.15);
  border-color: rgba(245, 197, 24, 0.45);
}
.evt-badge-uncommitted {
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border-color: rgba(56, 189, 248, 0.35);
}
.evt-badge-scratched {
  color: #fca5a5;
  background: rgba(217, 115, 102, 0.18);
  border-color: rgba(217, 115, 102, 0.6);
}
.event-card.is-relay {
  background: linear-gradient(180deg, rgba(56, 189, 248, 0.05), var(--bg));
}
.event-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
  /* Keep the title visible while scrolling through tall relay cards. The
     top offset matches the lineup-header's full height (~150px) so titles
     don't slide under the sticky header. */
  position: sticky;
  top: 0;
  background: var(--bg);
  z-index: 3;
}
.event-card.is-relay .event-header { background: linear-gradient(180deg, rgba(56, 189, 248, 0.06), var(--bg)); }
.event-title { font-size: 15px; font-weight: 600; }
.event-meta { font-size: 13px; color: var(--text-muted); }
.event-points {
  margin-left: auto;
  font-size: 12px;
  font-weight: 700;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 2px 7px;
  font-variant-numeric: tabular-nums;
}

.slots {
  display: flex;
  flex-direction: column;
  gap: 5px;
  margin-top: 4px;
}
.relay-team {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 8px;
  background: rgba(56, 189, 248, 0.05);
  border-radius: 6px;
  border: 1px solid var(--border);
}
.relay-team-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2px;
}
.relay-team-label {
  display: inline-block;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 2px 8px;
}
.relay-team-remove {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 14px;
  cursor: pointer;
  padding: 0 4px;
}
.relay-team-remove:hover { color: var(--danger); }
.add-team {
  display: block;
  width: 100%;
  margin-top: 4px;
  padding: 7px 8px;
  background: transparent;
  border: 1px dashed rgba(56, 189, 248, 0.45);
  border-radius: 4px;
  color: var(--accent);
  font-size: 13px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.add-team:hover {
  background: rgba(56, 189, 248, 0.08);
  border-color: var(--accent);
}
.add-team-pool {
  font-size: 11px;
  font-weight: 500;
  color: var(--text-muted);
  margin-left: 4px;
}

/* Pulse animation applied to slots that just changed via Auto-fill */
@keyframes slot-pulse {
  0%   { background: rgba(76, 175, 80, 0.35); box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.55); }
  100% { background: var(--bg-card); box-shadow: none; }
}
.slot.was-just-changed { animation: slot-pulse 2.2s ease-out; }
.slot {
  display: grid;
  grid-template-columns: 32px 1fr auto auto auto;
  gap: 8px;
  align-items: center;
  padding: 7px 10px;
  border-radius: 4px;
  background: var(--bg-card);
  font-size: 14px;
  cursor: pointer;
}
/* Relay leg slots use the stroke name (BACK / BREAST / FLY / FREE) in
   the first column, so it needs to be wide enough for the longest
   label ("BREAST") with breathing room. Individual slots keep the
   compact 32px column for "#1"/"#2". */
.relay-team .slot {
  grid-template-columns: 60px 1fr auto auto auto;
}
.slot:hover { background: var(--bg-elevated); }
.slot.is-drop-target {
  background: rgba(76, 175, 80, 0.15);
  outline: 2px dashed var(--primary);
  outline-offset: -2px;
}
.slot-num {
  color: var(--text-muted);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
}
.slot-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.slot-name.is-empty { color: var(--text-dim); font-style: italic; }
.slot-name.is-filled.is-committed { font-weight: 600; }
.slot-find {
  background: transparent;
  border: none;
  color: inherit;
  font: inherit;
  text-align: left;
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.slot-find:hover { text-decoration: underline; color: var(--accent); }
.slot-time { font-size: 13px; color: var(--text-muted); font-variant-numeric: tabular-nums; }
.slot-points {
  font-size: 13px;
  font-weight: 700;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 1px 6px;
  font-variant-numeric: tabular-nums;
}
.slot-actions {
  display: inline-flex;
  align-items: center;
  gap: 4px;
}
.slot-swap {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--text-muted);
  border-radius: 3px;
  font-size: 13px;
  padding: 3px 8px;
  cursor: pointer;
}
.slot-swap:hover { color: var(--text); border-color: var(--primary); }
.slot-remove {
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 16px;
  cursor: pointer;
  padding: 0 4px;
}
.slot-remove:hover { color: var(--danger); }

.add-entry {
  display: block;
  width: 100%;
  margin-top: 2px;
  padding: 7px 8px;
  background: transparent;
  border: 1px dashed var(--border);
  border-radius: 4px;
  color: var(--text-muted);
  font-size: 13px;
  cursor: pointer;
  transition: border-color 0.12s, color 0.12s, background 0.12s;
}
.add-entry:hover {
  border-color: var(--primary);
  color: var(--text);
  background: var(--bg-card);
}

/* Picker overlay */
.picker-overlay {
  position: fixed;
  inset: 0;
  background: rgba(14, 19, 32, 0.85);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 24px;
}
.picker-modal {
  width: 100%;
  max-width: 520px;
  max-height: 80vh;
  background: var(--bg-elevated);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.picker-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.picker-header h3 { margin: 0; font-size: 16px; }
.picker-list {
  flex: 1;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
.picker-row {
  padding: 8px 10px;
  background: var(--bg-card);
  border-radius: 4px;
  cursor: pointer;
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: 12px;
}
.picker-row:hover { background: var(--bg); border: 1px solid var(--primary); padding: 7px 9px; }
.picker-row.is-assigned { opacity: 0.4; cursor: not-allowed; }
.picker-section-header {
  padding: 6px 4px 4px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--accent);
  border-bottom: 1px dashed rgba(56, 189, 248, 0.4);
  margin-top: 4px;
}
.picker-section-header-sub {
  color: var(--text-muted);
  border-bottom-color: var(--border);
  margin-top: 8px;
}

.help-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  padding: 4px;
}
.help-row {
  display: flex;
  align-items: baseline;
  gap: 10px;
  font-size: 13px;
  color: var(--text);
}
.help-row span { color: var(--text-muted); }
.help-row kbd {
  display: inline-block;
  min-width: 24px;
  padding: 2px 6px;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-bottom-width: 2px;
  border-radius: 4px;
  font-family: monospace;
  font-size: 12px;
  text-align: center;
}
.picker-row-meta {
  font-size: 11px;
  color: var(--text-muted);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-variant-numeric: tabular-nums;
}
.pk-place {
  color: var(--text);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 4px;
  font-size: 10px;
}
.pk-points {
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 1px 4px;
  font-size: 10px;
  font-weight: 700;
}
.pk-indv {
  color: var(--warn);
  background: rgba(245, 197, 24, 0.12);
  border: 1px solid rgba(245, 197, 24, 0.4);
  border-radius: 3px;
  padding: 1px 4px;
  font-size: 9px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.4px;
}
.pk-name { display: inline-flex; align-items: center; }

.roster-controls {
  display: flex;
  gap: 6px;
  align-items: center;
}
.roster-controls input[type="search"] { flex: 1; }
.roster-sort {
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 5px 6px;
  font-size: 11px;
  max-width: 130px;
}
.roster-sort:focus { outline: none; border-color: var(--primary); }
.roster-toggle {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 12px;
  color: var(--text-muted);
  cursor: pointer;
  user-select: none;
}
.roster-toggle input[type="checkbox"] {
  accent-color: var(--primary);
  cursor: pointer;
}
.roster-toggle:hover { color: var(--text); }

/* Pulse animation applied to the roster row a coach just jumped to */
@keyframes row-found {
  0%   { box-shadow: 0 0 0 3px rgba(56, 189, 248, 0.85); }
  100% { box-shadow: 0 0 0 0 rgba(56, 189, 248, 0); }
}
.roster-row.was-just-found { animation: row-found 2.2s ease-out; }

/* Bottom-of-workspace status bar */
.lineup-statusbar {
  position: sticky;
  bottom: 0;
  z-index: 9;
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 8px 20px;
  background: var(--bg-elevated);
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--text-muted);
}
.sb-stat { display: inline-flex; align-items: center; gap: 4px; }
.sb-num { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.sb-points .sb-num { color: var(--accent); }
.sb-warn { color: var(--warn); }
.sb-warn .sb-num { color: var(--warn); }
.sb-sep { color: var(--text-dim); margin: 0 2px; }
.sb-health {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: 4px;
  border: 1px solid transparent;
  font-weight: 700;
}
.sb-health .sb-num { font-size: 13px; }
.sb-health-label { font-size: 10px; font-weight: 600; opacity: 0.8; text-transform: uppercase; letter-spacing: 0.3px; }
.sb-health-good { background: rgba(76, 175, 80, 0.18); border-color: rgba(76, 175, 80, 0.55); color: #b6e8b8; }
.sb-health-good .sb-num { color: #b6e8b8; }
.sb-health-mid  { background: rgba(245, 197, 24, 0.18); border-color: rgba(245, 197, 24, 0.55); color: #fde68a; }
.sb-health-mid .sb-num  { color: #fde68a; }
.sb-health-low  { background: rgba(239, 68, 68, 0.18); border-color: rgba(239, 68, 68, 0.55); color: #fca5a5; }
.sb-health-low .sb-num  { color: #fca5a5; }

.sb-chip {
  background: transparent;
  border: 1px solid rgba(245, 197, 24, 0.45);
  border-radius: 4px;
  color: var(--warn);
  font-size: 12px;
  padding: 2px 8px;
  cursor: pointer;
  font-weight: 600;
}
.sb-chip:hover { background: rgba(245, 197, 24, 0.1); }
.sb-chip.is-active {
  background: rgba(245, 197, 24, 0.2);
  border-color: var(--warn);
  color: var(--text);
}
.sb-chip.sb-clear {
  border-color: var(--border);
  color: var(--text-muted);
}
.sb-chip.sb-clear:hover { background: var(--bg-card); color: var(--text); }

/* Per-swimmer projected points chip on roster cards */
.rr-points {
  color: var(--accent);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* Mobile-only buttons (e.g. roster toggle) — hidden on desktop */
.btn-mobile-only { display: none; }

/* Responsive — phone / tablet */
@media (max-width: 720px) {
  .btn-mobile-only { display: inline-flex; }
  .lineup-main {
    grid-template-columns: 1fr;
    padding: 10px;
    gap: 10px;
  }
  .lineup-header { padding: 10px 12px; }
  .lineup-title-row { flex-wrap: wrap; gap: 6px; }
  .meet-name { font-size: 16px; }
  .meet-meta { font-size: 12px; }
  .lineup-filters { gap: 8px; }
  .filter-field { font-size: 11px; }
  .filter-field select { padding: 3px 6px; font-size: 12px; }
  .filter-count { width: 100%; margin-left: 0; }

  /* Roster panel: collapsible. When body.roster-hidden, it's removed
     from layout entirely; otherwise it sits above the events grid in a
     fixed-height scrollable region. */
  .roster-panel {
    position: relative;
    top: 0;
    max-height: 240px;
  }
  body.roster-hidden .roster-panel { display: none; }

  .events-grid { grid-template-columns: 1fr; gap: 8px; }
  .event-card { padding: 10px 12px; }
  .slot { grid-template-columns: 28px 1fr auto auto auto; gap: 4px; padding: 6px 8px; font-size: 13px; }
  .slot-name { font-size: 13px; }
  .slot-points, .slot-time, .slot-swap { font-size: 12px; }
  .event-title { font-size: 14px; }
  .event-meta { font-size: 12px; }

  .rr-name { font-size: 14px; }
  .rr-counts, .rr-bio { font-size: 12px; }
  .rr-pb { font-size: 11px; padding: 1px 4px; }
  .rr-pb-stroke { font-size: 10px; }
  .rr-pbs-label, .rr-assigned-label { font-size: 10px; }
  .rr-assigned { gap: 2px; padding-top: 3px; }
  .rr-assigned-chip { font-size: 10px; padding: 1px 4px; }
  .roster-toggle { font-size: 11px; }

  .lineup-statusbar { padding: 6px 10px; font-size: 11px; gap: 6px; }
}

@media (max-width: 480px) {
  .lineup-title-row .btn { padding: 6px 10px; font-size: 12px; }
  .meet-name { font-size: 15px; flex-basis: 100%; }
  .meet-meta { flex-basis: 100%; }
  .header-spacer { display: none; }

  /* Bigger tap targets on phones — slot row + action buttons. */
  .slot { padding: 10px 10px; gap: 6px; min-height: 44px; }
  .slot-swap { padding: 6px 10px; font-size: 13px; min-height: 32px; }
  .slot-remove { font-size: 22px; padding: 0 8px; min-width: 32px; min-height: 32px; }
  .add-entry, .add-team { padding: 10px; font-size: 13px; min-height: 40px; }
}

/* Print — coach-friendly handout. 2-column grid, ink-light, no chrome.
   The meet header is preserved on the first page so coaches can identify
   which printout this is at-a-glance. */
@media print {
  @page { margin: 0.5in 0.4in; }
  /* Neutralize the on-screen text-size zoom so the heat-sheet layout
     prints at its tuned scale regardless of the coach's chosen size. */
  html { zoom: 1 !important; }
  html, body { background: #fff; color: #000; font-size: 11px; }
  .upload-view, .roster-panel, .picker-overlay, .lineup-filters,
  .lineup-warnings, .lineup-statusbar, button { display: none !important; }
  /* Override the blanket button hide for the .slot-find button that
     wraps each filled swimmer's name inside a lane — without this,
     filled slots print with the lane number but no swimmer name
     (the empty-slot '— pick swimmer —' span prints fine because
     it's not a button). Strip the button chrome so it reads as
     plain text in print. */
  .slot button.slot-find {
    display: inline !important;
    background: transparent !important;
    color: #000 !important;
    border: 0 !important;
    padding: 0 !important;
    font: inherit !important;
    text-align: left !important;
    cursor: default !important;
  }
  .lineup-view { display: block; }
  .lineup-header {
    position: static;
    padding: 0 0 10px;
    background: #fff;
    border-bottom: 1px solid #000;
    margin-bottom: 10px;
  }
  .lineup-title-row { gap: 6px; }
  .meet-name { font-size: 16px; color: #000; }
  .meet-meta { color: #444; font-size: 11px; }
  .lineup-main { display: block; padding: 0; }
  .events-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .event-card {
    background: #fff;
    border: 1px solid #888;
    break-inside: avoid;
    padding: 6px 8px;
    gap: 3px;
  }
  .event-card.is-focus-dimmed, .event-card.is-filtered-out { display: none !important; }
  .event-header { border-bottom: 1px solid #ccc; padding-bottom: 3px; }
  .event-title { font-size: 12px; font-weight: 700; color: #000; }
  .event-meta { font-size: 10px; color: #444; }
  .event-points {
    background: #fff;
    border: 1px solid #888;
    color: #000;
  }
  .evt-badges { display: none; }
  .slot {
    background: #fff;
    color: #000;
    border-bottom: 1px dotted #ccc;
    border-radius: 0;
    padding: 3px 4px;
    font-size: 11px;
  }
  .slot-num, .slot-time, .slot-points { color: #444; }
  .slot-points {
    background: #fff;
    border: 1px solid #888;
  }
  .slot-name.is-empty { color: #888; }
  .relay-team { background: #fff; border: 1px solid #aaa; padding: 4px; }
  .relay-team-label {
    background: #fff;
    border: 1px solid #888;
    color: #000;
  }

  /* By-swimmer print mode (body.print-by-swimmer) — hide the events
     grid, show the dedicated swimmer-list container instead. */
  body.print-by-swimmer .lineup-view,
  body.print-by-swimmer .lineup-main,
  body.print-by-swimmer .lineup-header,
  body.print-by-swimmer .events-grid,
  body.print-by-swimmer .roster-panel,
  body.print-by-swimmer .picker-overlay { display: none !important; }
  body.print-by-swimmer #print-by-swimmer-view {
    display: block !important;
  }
  .print-by-swimmer-header {
    border-bottom: 1px solid #000;
    margin-bottom: 10px;
    padding-bottom: 6px;
  }
  .print-by-swimmer-header h1 {
    font-size: 16px;
    font-weight: 700;
    color: #000;
    margin: 0;
  }
  .print-by-swimmer-header .print-by-swimmer-sub {
    font-weight: 400;
    color: #444;
    font-size: 13px;
  }
  .print-by-swimmer-header p {
    font-size: 11px;
    color: #444;
    margin: 2px 0 0;
  }
  .print-swimmer-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .print-swimmer-card {
    border: 1px solid #888;
    padding: 6px 8px;
    break-inside: avoid;
  }
  .print-swimmer-header {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    border-bottom: 1px solid #ccc;
    padding-bottom: 3px;
    margin-bottom: 4px;
    gap: 8px;
  }
  .print-swimmer-name {
    font-weight: 700;
    color: #000;
    font-size: 12px;
  }
  .print-swimmer-meta {
    color: #444;
    font-size: 10px;
    flex-shrink: 0;
  }
  .print-swimmer-events {
    margin: 0;
    padding: 0;
    list-style: none;
  }
  .print-swimmer-events li {
    display: flex;
    align-items: baseline;
    gap: 6px;
    padding: 2px 0;
    border-bottom: 1px dotted #ccc;
    font-size: 11px;
  }
  .print-swimmer-events li:last-child {
    border-bottom: none;
  }
  .print-swimmer-empty {
    border: 1px dashed #888;
    padding: 24px 18px;
    text-align: center;
    color: #555;
    font-size: 12px;
    line-height: 1.5;
    border-radius: 4px;
  }
  .print-swim-num {
    color: #555;
    font-size: 10px;
    font-variant-numeric: tabular-nums;
    min-width: 24px;
  }
  .print-swim-evk {
    color: #000;
    font-weight: 600;
    flex: 1;
  }
  .print-swim-sub {
    color: #666;
    font-size: 10px;
    flex-shrink: 0;
  }
}

/* Screen styles for the print-by-swimmer container — hidden on screen,
   only revealed via @media print + body.print-by-swimmer above. */
#print-by-swimmer-view { display: none; }

/* Print chooser modal — uses the same .upload-view / .upload-card
   pattern as the password / save modals. The two options below are
   the actual decisive surface. */
.print-option-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  margin: 14px 0 4px;
}
.print-option {
  text-align: left;
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  cursor: pointer;
  color: var(--text);
  font-family: inherit;
  transition: border-color .12s, background .12s, transform .12s;
}
.print-option:hover,
.print-option:focus-visible {
  border-color: var(--accent);
  background: var(--bg-elevated);
  transform: translateY(-1px);
  outline: none;
}
.print-option-title {
  font-weight: 700;
  font-size: 14px;
  margin-bottom: 3px;
}
.print-option-title .muted {
  font-weight: 400;
  color: var(--text-muted);
}
.print-option-desc {
  font-size: 12px;
  color: var(--text-muted);
  line-height: 1.4;
}

/* ───────────────────────── Toasts ─────────────────────────
   Brief on-screen confirmations for actions that would otherwise be
   silent (bulk scratch, auto-fill completes, save success/failure,
   restore-from-history). Stacked in the bottom-right; bottom-center on
   narrow screens so they don't dodge the thumb. */

.toast-container {
  position: fixed;
  bottom: 16px;
  right: 16px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  z-index: 9999;
  pointer-events: none;  /* container itself is transparent to clicks */
  max-width: calc(100vw - 32px);
}

.toast {
  pointer-events: auto;
  max-width: 360px;
  padding: 12px 16px;
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
  font-size: 13px;
  line-height: 1.4;
  display: flex;
  align-items: flex-start;
  gap: 8px;
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}
.toast.is-visible {
  opacity: 1;
  transform: translateY(0);
}
.toast.is-leaving {
  opacity: 0;
  transform: translateY(8px);
}
.toast-icon {
  flex: 0 0 auto;
  font-weight: 600;
  line-height: 1.4;
}
.toast-msg {
  flex: 1 1 auto;
  word-wrap: break-word;
}
.toast.toast-success { border-left-color: var(--primary); }
.toast.toast-success .toast-icon { color: var(--primary); }
.toast.toast-error { border-left-color: var(--danger); }
.toast.toast-error .toast-icon { color: var(--danger); }
.toast.toast-info { border-left-color: var(--accent); }
.toast.toast-info .toast-icon { color: var(--accent); }

@media (max-width: 600px) {
  .toast-container {
    right: 16px;
    left: 16px;
    bottom: 12px;
    align-items: center;
    max-width: none;
  }
  .toast { width: 100%; max-width: 480px; }
}

/* ─── Onboarding tour ─────────────────────────────────────────────
   First-time spotlight tour. Backdrop intercepts pointer events so the
   underlying app is non-interactive while the tour is open. The
   spotlight ring is visual only — it sits above the backdrop but the
   actual button beneath stays unclickable. */
.tour-root {
  position: fixed;
  inset: 0;
  z-index: 9000;
  pointer-events: none;
}
.tour-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(15, 23, 42, 0.78);
  pointer-events: auto;
}
.tour-spotlight {
  position: absolute;
  border-radius: 10px;
  border: 2px solid var(--accent);
  box-shadow: 0 0 0 4px rgba(56, 189, 248, 0.25), 0 0 24px 4px rgba(56, 189, 248, 0.45);
  pointer-events: none;
  transition: left .15s ease, top .15s ease, width .15s ease, height .15s ease;
}
.tour-card {
  position: absolute;
  width: min(360px, calc(100vw - 24px));
  background: var(--bg-card);
  color: var(--text);
  border: 1px solid var(--accent);
  border-radius: 12px;
  padding: 18px 18px 14px;
  box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
  pointer-events: auto;
  box-sizing: border-box;
}
.tour-card.tour-card-centered {
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}
.tour-step-count {
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--text-muted);
  margin-bottom: 6px;
}
.tour-title {
  margin: 0 0 8px;
  font-size: 17px;
  color: var(--accent);
}
.tour-body {
  margin: 0 0 14px;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
}
.tour-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
}
.tour-close {
  position: absolute;
  top: 6px;
  right: 8px;
  background: transparent;
  border: none;
  color: var(--text-muted);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 8px;
  border-radius: 6px;
}
.tour-close:hover { color: var(--text); background: rgba(255, 255, 255, 0.06); }

@media (max-width: 600px) {
  .tour-card {
    /* On narrow screens always center — button-relative positioning
       gets cramped, and the spotlight ring still tells the coach which
       button the step is about. */
    left: 12px !important;
    right: 12px;
    top: auto !important;
    bottom: 16px;
    width: auto;
    max-width: none;
  }
  .tour-card.tour-card-centered {
    bottom: auto;
    top: 50% !important;
    transform: translate(0, -50%);
    left: 12px !important;
    right: 12px;
  }
}

/* ─── Chip peek ───────────────────────────────────────────────────
   Tiny floating preview of an event card, shown after a 150ms hover
   on a roster card's `In:` chip. Mirrors the real .event-card visual
   language (border, padding, points pill, slot rows) but tighter so
   it reads as a preview, not a copy. Read-only (pointer-events: none
   so the hover target is the chip, not the peek). Desktop-only —
   gated in main.js via window.matchMedia('(hover: hover) and
   (pointer: fine)'). Positioned via position: fixed so the
   underlying scroll doesn't drag it; z-index sits above the event
   grid but below toasts (9999) and the tour overlay. */
.chip-peek {
  position: fixed;
  top: 0;
  left: 0;
  width: 280px;
  max-height: 320px;
  overflow-y: auto;
  padding: 10px 12px;
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.4);
  font-size: 13px;
  line-height: 1.4;
  color: var(--text);
  pointer-events: none;
  z-index: 5000;
}
.chip-peek[hidden] { display: none; }
.chip-peek-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  padding-bottom: 4px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 6px;
}
.chip-peek-title {
  font-size: 14px;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chip-peek-meta {
  font-size: 11px;
  color: var(--text-muted);
}
.chip-peek-points {
  margin-left: auto;
  font-size: 11px;
  font-weight: 700;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 1px 6px;
  font-variant-numeric: tabular-nums;
}
.chip-peek-lanes {
  display: flex;
  flex-direction: column;
  gap: 3px;
}
.chip-peek-lane {
  display: flex;
  gap: 6px;
  align-items: baseline;
  font-size: 12px;
}
.chip-peek-lane-label {
  flex: 0 0 auto;
  min-width: 36px;
  color: var(--text-muted);
  font-variant-numeric: tabular-nums;
}
.chip-peek-lane-name {
  flex: 1 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.chip-peek-lane-name.is-empty {
  color: var(--text-dim);
  font-style: italic;
}
.chip-peek-team {
  margin-top: 4px;
  padding: 6px 8px;
  background: rgba(56, 189, 248, 0.05);
  border: 1px solid var(--border);
  border-radius: 4px;
}
.chip-peek-team + .chip-peek-team { margin-top: 6px; }
.chip-peek-team-label {
  display: inline-block;
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--accent);
  background: rgba(56, 189, 248, 0.12);
  border: 1px solid rgba(56, 189, 248, 0.35);
  border-radius: 3px;
  padding: 1px 6px;
  margin-bottom: 4px;
}

/* ────────── Coach attribution + changeLog (per-slot badge,
   activity tab, conflict marker, draft-restore banner) ────────── */

/* "Last edited by" badge — small colored initials in the slot corner. */
.slot-attr-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  margin-left: 6px;
  border-radius: 9px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.02em;
  color: #0b1220;
  background: var(--accent);
  cursor: help;
}
.slot.has-conflict { box-shadow: inset 0 0 0 1px rgba(239, 68, 68, 0.55); }
.slot-conflict-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-left: 6px;
  padding: 0;
  border: 1px solid rgba(239, 68, 68, 0.7);
  border-radius: 50%;
  background: rgba(239, 68, 68, 0.15);
  color: #ef4444;
  font-size: 12px;
  font-weight: 700;
  cursor: pointer;
  line-height: 1;
}
.slot-conflict-btn:hover { background: rgba(239, 68, 68, 0.3); }

.alt-chip-conflict {
  background: rgba(239, 68, 68, 0.1);
  border-color: rgba(239, 68, 68, 0.5);
}

/* History overlay — tab strip + activity entries. */
.history-tabs {
  display: flex;
  gap: 4px;
  margin: 8px 0;
  border-bottom: 1px solid var(--border);
}
.history-tab {
  padding: 6px 12px;
  background: transparent;
  border: none;
  color: var(--muted);
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.history-tab:hover { color: var(--fg); }
.history-tab.is-active {
  color: var(--accent);
  border-bottom-color: var(--accent);
}
.activity-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 22px;
  height: 22px;
  padding: 0 5px;
  margin-right: 8px;
  border-radius: 11px;
  font-size: 10px;
  font-weight: 700;
  color: #0b1220;
  flex-shrink: 0;
}
.activity-coach {
  display: inline-block;
  font-weight: 600;
  color: var(--fg);
  margin-right: 6px;
}
.activity-msg {
  color: var(--muted);
  font-size: 13px;
}
.picker-row.is-clickable { cursor: pointer; }
.picker-row.is-clickable:hover { background: rgba(56, 189, 248, 0.05); }
