/* ═════════════════════════════════════════════════════════════════════════
   sideline.css — the sporting chrome.

   Loaded after app.css, so this is where Sideline overrides the generic
   internal-tool shell: graphite sidebar carrying the school's colour, cut
   corners, uppercase micro-labels with wide tracking, tabular numerals, and a
   single diagonal accent that reads as speed without becoming decoration.

   Every colour here resolves through var(--school-primary) or the neutral ramp,
   so a school setting its own colour re-skins the whole app with no code change.
   ═════════════════════════════════════════════════════════════════════════ */

/* ── Type: the sporting register ───────────────────────────────────────────── */
body {
  font-family: var(--font-body);
  -webkit-font-smoothing: antialiased;
}

/* Digits never jitter in a column. */
.num, .tally, .kpi-value, .athlete-jersey, .entry-jersey, .qa-jersey,
.data-table td.num, .data-table th.num {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

h1, h2, h3, .card-header, .roster-team, .ath-name {
  font-family: var(--font-display);
  letter-spacing: -0.02em;
}

/* The label voice: small, uppercase, widely tracked — a timing-screen caption. */
.form-label, .panel-head h2, .data-table thead th, .kpi-label,
.danger-zone-title, .filter-count {
  text-transform: uppercase;
  letter-spacing: 0.09em;
  font-size: var(--fs-table-head);
  font-weight: 700;
  color: var(--text-muted);
}

/* ── Sidebar: pit wall ─────────────────────────────────────────────────────── */
.sidebar {
  background: var(--track-dark);
  border-right: 1px solid #000;
  /* DO NOT add `position: relative` here. It was here for the ::after stripe
     below, and it silently un-stuck the whole rail: app.css sets
     `position: sticky` on .sidebar, this file loads after it, both selectors
     have the same specificity, so the last one simply won and the sidebar
     scrolled away with the page. A media query does not raise specificity, so
     the desktop rule had no protection.
     The stripe does not need it — a sticky element is a positioned element and
     already forms the containing block an absolute child anchors to. */
}
/* The school's colour as a stripe down the working edge. */
.sidebar::after {
  content: "";
  position: absolute;
  top: 0; bottom: 0; right: 0;
  width: 3px;
  background: var(--school-bright, var(--school-primary));
}
.sidebar-brand {
  border-bottom: 1px solid rgba(255, 255, 255, .08);
  letter-spacing: -0.03em;
  font-weight: 800;
}
.sidebar-brand a { color: #fff; text-decoration: none; }
/* The team name is a real name, not a wordmark — it wraps rather than being
   forced onto one line, and it is not uppercased. */
.sidebar-brand { line-height: 1.15; }

/* The vendor, quietly, under the signed-in user. .wordmark below already gives
   "Sideline" its own treatment including the coloured second half — this only
   dims the surrounding weight so it reads as a credit, not a heading. */
.sidebar-foot .wordmark { color: rgba(255, 255, 255, .72); }

.nav-link {
  color: rgba(255, 255, 255, .62);
  border-radius: var(--radius-sm);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-size: var(--fs-xs);
  font-weight: 700;
  position: relative;
}
.nav-link:hover { color: #fff; background: rgba(255, 255, 255, .06); }
.nav-link.active {
  color: #fff;
  background: rgba(255, 255, 255, .09);
}
/* Active marker is a leading bar, like a selected car on a timing tower. */
.nav-link.active::before {
  content: "";
  position: absolute;
  left: 0; top: 50%;
  transform: translateY(-50%);
  width: 3px; height: 1.1em;
  background: var(--school-bright, var(--school-primary));
}
.sidebar-user { border-top: 1px solid rgba(255, 255, 255, .08); }
.sidebar-user-name { color: rgba(255, 255, 255, .82); }
.sidebar-user-logout { color: rgba(255, 255, 255, .5); }
.sidebar-user-logout:hover { color: #fff; }

.mobile-topbar {
  background: var(--track-dark);
  border-bottom: 3px solid var(--school-bright, var(--school-primary));
}
.mobile-topbar-brand { color: #fff; }

/* ── Buttons: cut, not rounded ─────────────────────────────────────────────── */
.btn {
  border-radius: var(--radius-sm);
  text-transform: uppercase;
  letter-spacing: 0.07em;
  font-size: var(--fs-xs);
  font-weight: 800;
  transition: transform var(--transition), background var(--transition), border-color var(--transition);
}
.btn:active { transform: translateY(1px); }

/* Filled controls use --school-btn, not --school-primary: Settings::strongFor()
   darkens the school's colour just enough that a WHITE 12px bold label clears
   4.5:1. Flipping the label to black would technically read better on a mid-tone
   blue and would look broken, so the fill moves instead. Stripes and edges below
   keep the school's actual colour, because nothing sits on top of them. */
.btn-primary {
  background: var(--school-btn, var(--school-primary));
  color: #fff;
  border: 1px solid var(--school-btn, var(--school-primary));
}
.btn-primary:hover {
  background: color-mix(in srgb, var(--school-btn, var(--school-primary)) 88%, #000);
  border-color: color-mix(in srgb, var(--school-btn, var(--school-primary)) 88%, #000);
}

.btn-secondary {
  background: var(--bg-surface);
  color: var(--text);
  border: 1px solid var(--border-strong);
}
.btn-secondary:hover { border-color: var(--text); }

/* ── Panels and tables ─────────────────────────────────────────────────────── */
.card, .panel {
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}
.card-header {
  text-transform: none;
  font-weight: 750;
  letter-spacing: -0.015em;
  border-bottom: 1px solid var(--border);
}

.data-table thead th {
  background: var(--bg-sunken);
  border-bottom: 2px solid var(--school-primary);
  white-space: nowrap;
}
.data-table tbody tr { transition: background var(--transition); }
.data-table tbody tr:hover { background: var(--bg-sunken); }
.table-link { font-weight: 650; color: var(--text); text-decoration: none; }
.table-link:hover { color: var(--school-primary); }

/* ── The page header: a start-line band ────────────────────────────────────── */
.admin-content-header {
  padding-bottom: var(--space-3);
  border-bottom: 1px solid var(--border);
  margin-bottom: var(--space-4);
  position: relative;
}
.admin-content-header h1 {
  font-size: var(--fs-2xl);
  font-weight: 800;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
/* A short school-colour rule under the title. One accent, not a stripe field. */
.admin-content-header::after {
  content: "";
  position: absolute;
  left: 0; bottom: -1px;
  width: 3.5rem; height: 2px;
  background: var(--school-primary);
}

/* ── Tabs: sector selectors ────────────────────────────────────────────────── */
.tabs { border-bottom: 1px solid var(--border); gap: 0; }
.tab, .tab-link {
  border-radius: 0;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--fs-xs);
  font-weight: 800;
  color: var(--text-muted);
  border-bottom: 2px solid transparent;
  background: none;
}
.tab:hover, .tab-link:hover { color: var(--text); }
.tab.is-active, .tab-link.is-active {
  color: var(--text);
  border-bottom-color: var(--school-primary);
  background: none;
}

/* ── Chips ─────────────────────────────────────────────────────────────────── */
.badge {
  border-radius: var(--radius-sm);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: var(--fs-xs);
  font-weight: 800;
}

/* ── Focus: visible, and in the school's colour ────────────────────────────── */
:where(a, button, input, select, textarea, [tabindex]):focus-visible {
  outline: 2px solid var(--school-primary);
  outline-offset: 1px;
  border-radius: var(--radius-sm);
}

/* ── Wordmark: "Side" in ink, "line" in the school's colour ────────────────── */
.wordmark { font-weight: 800; letter-spacing: -0.035em; }
/* On the dark sidebar, so the brightened variant — a navy school colour would
   otherwise be invisible against the graphite. */
.wordmark i { font-style: normal; color: var(--school-bright, var(--school-primary)); }

/* ── School colour picker (Settings) ───────────────────────────────────────── */
.color-row { display: flex; flex-wrap: wrap; gap: var(--space-4); }
.color-field { display: flex; flex-direction: column; gap: var(--space-2); }
.color-input { display: flex; align-items: center; gap: var(--space-2); }
.color-input input[type="color"] {
  inline-size: 3rem;
  block-size: var(--control-compact);
  padding: 2px;
  border: 1px solid var(--border-strong);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  cursor: pointer;
}
.color-input input[type="text"] {
  inline-size: 7.5rem;
  min-height: var(--control-compact);
  padding: 0 var(--space-3);
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  text-transform: lowercase;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--bg-surface);
  color: var(--text);
}

/* Live proof, showing exactly what the roster board shows: a school-colour
   leading edge, a graphite number plate, and the darkened primary button. A
   school-coloured plate here would be a preview of something that does not exist. */
.color-preview {
  display: flex;
  align-items: stretch;
  margin-top: var(--space-3);
  border: 1px solid var(--border);
  border-left: 3px solid var(--school-primary);
  border-radius: var(--radius-lg);
  overflow: hidden;
  min-height: 3.5rem;
}
.color-preview-plate {
  display: grid;
  place-items: center;
  min-inline-size: 3.5rem;
  margin: var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--track-dark);
  color: #fff;
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.04em;
  transform: skewX(var(--lean));
}
.color-preview-plate span { transform: skewX(calc(var(--lean) * -1)); }
.color-preview-body {
  flex: 1 1 auto;
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4) var(--space-2) var(--space-2);
  background: var(--bg-surface);
}

/* ── Dashboard ─────────────────────────────────────────────────────────────── */
.section-head {
  margin: var(--space-5) 0 var(--space-3);
  font-size: var(--fs-table-head);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--text-muted);
}
.kpi-card {
  border-radius: var(--radius-lg);
  border-left: 3px solid var(--school-primary);
  text-decoration: none;
}
.kpi-card.is-warn  { border-left-color: var(--st-warning-fg); }
.kpi-card.is-alert { border-left-color: var(--st-danger-fg); }
.kpi-value {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.kpi-card.is-warn  .kpi-value { color: var(--st-warning-fg); }
.kpi-card.is-alert .kpi-value { color: var(--st-danger-fg); }

.activity-feed { list-style: none; margin: 0; padding: 0; }
.activity-feed li {
  display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-3);
  padding: var(--space-2) 0; border-top: 1px solid var(--border);
  font-size: var(--fs-sm);
}
.activity-feed li:first-child { border-top: 0; }
.activity-who  { font-weight: 700; }
.activity-what { color: var(--text-soft); flex: 1 1 auto; }
.activity-when { color: var(--text-muted); font-variant-numeric: tabular-nums; }

/* Every scheduling surface says whose clock it is. */
.tz-note {
  margin-top: var(--space-5);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 700;
  color: var(--text-muted);
}

/* ── Impersonation banner ──────────────────────────────────────────────────────
   Deliberately loud and deliberately permanent. An impersonation you cannot tell
   you are in is how somebody "fixes" the wrong person's record. */
:root { --imp-h: 44px; }
.impersonating {
  /* Sits ABOVE .app, not inside it — see templates/layout.php. Sticky so it
     stays true while reading down a long roster: a banner you scroll past is a
     banner you forget, and forgetting is the failure mode it exists to stop. */
  position: sticky; top: 0; z-index: 60;
  /* Pinned to --imp-h so the sidebar's offset is a fact rather than a guess. */
  min-height: var(--imp-h); box-sizing: border-box;
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--st-warning-fg);
  color: #fff;
  font-size: var(--fs-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.07em;
}
.impersonating button {
  appearance: none; cursor: pointer;
  border: 1px solid rgba(255,255,255,.6); background: rgba(255,255,255,.14);
  color: #fff; border-radius: var(--radius-sm); padding: 5px var(--space-3);
  font: inherit; font-size: var(--fs-xs); font-weight: 800;
  text-transform: uppercase; letter-spacing: 0.07em;
}
.impersonating button:hover { background: rgba(255,255,255,.24); }

/* ── Users screen ─────────────────────────────────────────────────────────────*/
.user-row { margin-bottom: var(--space-3); }
.user-row.is-inactive { opacity: 0.6; }
/* A labelled control — role, and anything else that is a value rather than an
   action. The label sits above so the dropdown is not a bare box whose meaning
   you have to infer from its current value. */
.user-field { margin-top: var(--space-3); }
.user-field form { display: flex; gap: var(--space-2); align-items: center; }
.user-field .form-select { min-height: var(--control-compact); max-width: 16rem; }

/* Account actions. Separated from the fields above by a rule, and every control
   the same height — a row of buttons that do not line up reads as broken before
   it reads as anything else. */
.user-actions {
  display: flex; flex-wrap: wrap; gap: var(--space-2); align-items: center;
  margin-top: var(--space-3); padding-top: var(--space-3);
  border-top: 1px dashed var(--border);
}
.user-actions form { display: flex; gap: var(--space-2); align-items: center; }
.user-field .mini, .user-actions .mini, .team-assign .mini { min-height: var(--control-compact); }
.team-assign { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px dashed var(--border); }
.team-checks { display: flex; flex-wrap: wrap; gap: var(--space-2) var(--space-4); margin: var(--space-2) 0; }
.team-checks label { display: flex; align-items: center; gap: var(--space-2); font-size: var(--fs-sm); }

/* An unspent temporary password. Monospace, large and high-contrast, because it
   gets read aloud over a phone — a lowercase l next to a 1 is a support call.
   Sits inside the account's own row now rather than in a one-time banner, and
   removes itself when the account sets a real password. */
.issued {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-3);
  margin: var(--space-3) 0 0;
  padding: var(--space-3) var(--space-4);
  border: 1px solid color-mix(in srgb, var(--st-warning-fg) 35%, transparent);
  border-left-width: 3px;
  border-radius: var(--radius-sm);
  background: var(--st-warning-bg);
}
.issued-label {
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--st-warning-fg);
}
/* <code> is INLINE, and vertical padding on an inline box does not grow the
   line — the background just bleeds up over whatever is above it. As a bare
   inline this sat on top of the last line of its own explanation and hid it.
   display:block also lets a long URL wrap inside the box instead of past it. */
.issued-secret {
  display: block;
  padding: var(--space-2) var(--space-3);
  background: var(--track-dark); color: #fff;
  border-radius: var(--radius-sm);
  font-family: var(--font-mono); font-size: var(--fs-lg);
  letter-spacing: 0.08em; word-break: break-all;
  cursor: pointer;                       /* click-to-copy, see app.js [data-copy] */
}
/* A URL is read, not dictated. The tracking exists so a temporary password can
   be read aloud character by character, and it makes a link harder to scan. */
.issued-secret--url { font-size: var(--fs-sm); letter-spacing: 0; }
.issued-note { flex: 1 1 16rem; font-size: var(--fs-xs); color: var(--text-soft); }

/* A support session can WRITE, and it is inside somebody else's school. The AD
   banner is a warning; this one is an alarm, and it must not be mistaken for
   the read-only case at a glance. */
.impersonating.is-platform { background: var(--st-danger-fg); }
.ops-operator {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
  padding: var(--space-3) 0; border-bottom: 1px solid var(--border);
}
.ops-operator:last-child { border-bottom: none; }

/* Sideline's own staff working inside a school. The tenant is entitled to spot
   these at a glance in their own feed, not to find them by reading carefully. */
.activity-platform { border-left: 3px solid var(--st-warning-fg); padding-left: var(--space-3); }
/* Marks the actor as Sideline's own staff rather than someone at this school —
   the name alone would read as a colleague. */
.activity-badge {
  display: inline-block; margin-left: 4px; padding: 1px 6px;
  background: var(--st-warning-fg); color: #fff;
  border-radius: 999px; font-size: 10px; font-weight: 700; letter-spacing: .04em;
}

/* The banner is sticky at the top of the viewport, so the rail has to start
   below it and stop one banner-height short — otherwise it slides under the
   banner and runs past the bottom of the window.

   height stays EXPLICIT here. An earlier version used height:auto, which let the
   grid stretch the rail to the full page and killed the sticky entirely.

   --imp-h is exact rather than estimated because the banner is given the same
   min-height below. It only has to hold at >=768px, which is the only place a
   sidebar exists at all. */
body:has(.impersonating) .sidebar {
  top: var(--imp-h);
  height: calc(100dvh - var(--imp-h));
}

/* ── Schedule: month grid ──────────────────────────────────────────────────
   Seven equal columns at every width. A calendar that reflows into a list on
   a phone is just the list view with extra steps, so it keeps its shape and
   scrolls sideways instead — the whole point of asking for a month is seeing
   the month. */
.cal-head { display: flex; align-items: center; gap: var(--space-3); margin: var(--space-4) 0; }
.cal-head h2 { font-size: var(--fs-lg); margin: 0; margin-right: auto; }

.cal-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.cal-grid {
  display: grid; grid-template-columns: repeat(7, minmax(104px, 1fr));
  gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: var(--radius-lg); overflow: hidden;
}
.cal-dow {
  background: var(--bg-sunken); padding: var(--space-2);
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-soft); text-align: center;
}
.cal-day {
  background: var(--bg-raised); min-height: 92px; padding: var(--space-2);
  display: flex; flex-direction: column; gap: 3px;
}
/* Days either side of the month stay visible but recede — the week they belong
   to still has to read as a week. */
.cal-day.is-outside { background: var(--bg-sunken); }
.cal-day.is-outside .cal-daynum { opacity: .45; }
.cal-day.is-today { box-shadow: inset 0 0 0 2px var(--school-bright, var(--school-primary)); }
.cal-daynum { font-size: var(--fs-xs); font-weight: 700; color: var(--text-soft); }
.cal-day.is-today .cal-daynum { color: var(--school-primary); }

.cal-item {
  display: block; padding: 2px var(--space-2);
  border-radius: var(--radius-sm);
  background: var(--bg-sunken); color: var(--text);
  font-size: var(--fs-xs); line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
a.cal-item:hover { background: var(--border); text-decoration: none; }
/* A game is the thing people scan for; practices and dinners are context. */
.cal-item.is-game { background: color-mix(in srgb, var(--school-primary) 16%, transparent); font-weight: 600; }
.cal-item.is-cancelled { text-decoration: line-through; opacity: .6; }

/* On a phone the month kept its 104px columns and scrolled sideways, which in
   practice meant Sunday-to-Wednesday and no hint there was more. The month's
   whole point is seeing the month — so at phone width the seven columns fit
   the screen and each event collapses to a DOT. A 104px chip cannot exist in a
   48px cell, and a truncated "5:3…" tells nobody anything; a dot at least says
   "something is on Thursday", and the words are one tap away on the List tab.
   Games keep their color so the scan for game days survives the shrink. */
.cal-day { position: relative; }
/* The day-sized tap target exists at every width and only ACTS on the phone:
   on desktop the chips are the links, and two overlapping click surfaces on
   the same square answer differently — so the overlay simply is not there. */
.cal-day-hit { display: none; }

@media (max-width: 640px) {
  .cal-scroll { overflow-x: visible; }
  .cal-day-hit { display: block; position: absolute; inset: 0; z-index: 1; }
  .cal-grid { grid-template-columns: repeat(7, minmax(0, 1fr)); }
  .cal-day {
    min-height: 52px; padding: 3px 4px;
    flex-direction: row; flex-wrap: wrap; align-content: flex-start; gap: 3px;
  }
  .cal-daynum { width: 100%; }
  .cal-item {
    font-size: 0; line-height: 0; padding: 0;
    width: 9px; height: 9px; border-radius: 999px;
    background: var(--text-soft);
  }
  .cal-item.is-game { background: var(--school-bright, var(--school-primary)); }
  .cal-item.is-cancelled { opacity: .35; }
}

/* ── Team meal: one row per athlete ────────────────────────────────────────*/
.meal-row {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) 0; border-bottom: 1px solid var(--border);
}
.meal-row:last-child { border-bottom: none; }
.meal-who { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; margin-right: auto; }
.meal-actions, .meal-money { display: flex; align-items: center; gap: var(--space-2); }
.meal-qty { width: 4.5rem; min-height: var(--control-compact); }

/* ── Season meal grid ──────────────────────────────────────────────────────*/
.meal-athlete { padding: var(--space-3) 0; border-bottom: 1px solid var(--border); }
.meal-athlete:last-child { border-bottom: none; }
.meal-line {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
}
.meal-line-what { margin-right: auto; display: flex; align-items: baseline; gap: var(--space-2); flex-wrap: wrap; }
.meal-line-state { min-width: 6rem; }

/* Voluntold: a select and a button on one line, sized so the family names fit
   without pushing the slot's other controls off the row. */
.assign-form { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); }
.assign-form .form-select { min-height: var(--control-compact); max-width: 18rem; }

/* ── Staff agenda: one timeline, grouped by day ────────────────────────────*/
.agenda-day { margin-bottom: var(--space-4); }
.agenda-date {
  font-size: var(--fs-xs); font-weight: 800; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text-soft);
  padding-bottom: var(--space-2); margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--border);
}
/* A sign-up is a task, not a fixture — tinted so the eye can separate the two
   without reading either. */
.event.is-signup { background: color-mix(in srgb, var(--bg-sunken) 70%, transparent); }

/* ── Sign-up cards ─────────────────────────────────────────────────────────
   Without this the card is an <a> full of spans, which lays out as one run of
   inline text — every sheet on the page reading as a single sentence. */
.sheet-cards {
  display: grid; gap: var(--space-3);
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  margin-top: var(--space-3);
}
.sheet-card {
  display: flex; flex-direction: column; gap: 4px;
  padding: var(--space-4);
  background: var(--bg-raised); border: 1px solid var(--border);
  border-left: 3px solid var(--school-bright, var(--school-primary));
  border-radius: var(--radius-lg);
  color: var(--text); text-decoration: none;
}
.sheet-card:hover { border-color: var(--border-strong); text-decoration: none; }
.sheet-card-kind {
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-soft);
}
.sheet-card-title { font-weight: 700; font-size: var(--fs-md); }
.sheet-card-sub { font-size: var(--fs-sm); color: var(--text-soft); }
.sheet-card .flag { align-self: flex-start; margin-top: var(--space-2); }

/* A filter that is ON should look pressed, not merely linked. */
.btn.is-active { border-color: var(--color-primary); color: var(--color-primary); font-weight: 700; }

/* ── Game day jobs ─────────────────────────────────────────────────────────*/
.job-line {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) 0; border-bottom: 1px solid var(--border);
}
/* On a phone the row becomes a fixed anatomy — name on its own line, then
   fraction left and button right — so every job's controls land in the same
   two places instead of wherever the note's length happened to push them. */
@media (max-width: 640px) {
  .job-line {
    display: grid; grid-template-columns: 1fr auto;
    grid-template-areas: "what what" "who actions";
    align-items: center; row-gap: var(--space-2);
  }
  .job-what    { grid-area: what; margin-right: 0; }
  .job-who     { grid-area: who; }
  .job-actions { grid-area: actions; justify-self: end; }
  .job-edit    { grid-column: 1 / -1; grid-row: 3; }
}
.job-line:last-child { border-bottom: none; }
.job-what { display: flex; flex-direction: column; gap: 2px; margin-right: auto; min-width: 10rem; }
.job-who { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }
.job-person { display: inline-flex; align-items: center; gap: var(--space-2); }
.job-actions { display: flex; align-items: center; gap: var(--space-2); flex-wrap: wrap; }

/* A squad sits under its program. The indent IS the relationship, so it needs
   no column of its own repeating the program's name on every row. */
.table-link.is-squad { padding-left: var(--space-5); font-weight: 500; }
.table-link.is-squad::before { content: "\21B3\00a0"; color: var(--text-soft); }

/* ── The job row ─────────────────────────────────────────────────────────────
   One row, one height. The row holds at most two control groups — the family's
   "I'll do this" and the manager's assign — and every control on it sits at
   --control-compact, because three different heights side by side is most of
   what "ugly" meant here. Everything else lives behind the disclosure. */
.job-line { align-items: center; }
.job-actions .btn,
.job-actions .mini,
.job-actions .form-select { min-height: var(--control-compact); }
.assign-form { display: flex; align-items: center; gap: var(--space-2); }
.assign-form .form-select { width: auto; max-width: 15rem; }

/* Per-job edit, folded away until wanted. A job list is read far more often
   than it is changed, so the controls do not sit open on every row.
   flex-basis 100% inside .job-line puts it on its OWN row. The summary is
   drawn as a quiet chip so it reads as a control, not as stray text. */
.job-edit { flex: 0 0 100%; margin-top: var(--space-2); }
.job-edit > summary {
  display: inline-block; cursor: pointer;
  font-size: var(--fs-xs); font-weight: 600; color: var(--text-muted);
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: 3px var(--space-3); background: var(--bg-raised);
  list-style: none;
}
.job-edit > summary::-webkit-details-marker { display: none; }
.job-edit > summary:hover { color: var(--text); border-color: var(--text-muted); }
.job-edit[open] {
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4) var(--space-4);
  background: var(--bg-raised);
}
.job-edit[open] > summary { margin-bottom: var(--space-3); border-color: var(--text-muted); }
.job-edit form { margin-top: var(--space-2); }
/* The headcount is two digits; it does not need half the row. */
.job-edit .job-edit-n { flex: 0 0 9rem; }
/* The one destructive act, quiet and last, past a hairline. */
.job-remove { margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border); }

/* The dashboard's reminder strip reuses the sheet cards; the cap stops one
   busy season from pushing the calendar below the fold on a laptop. */
.signup-strip { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }

/* ── Waiting on you ──────────────────────────────────────────────────────────
   Row anatomy: what | when | state | verb. The verb is drawn as a quiet
   outlined chip so the row answers "what do I do" at a glance; the whole row
   is the link, so the chip is an affordance, not a second target. */
.todo-list { list-style: none; margin: 0; padding: 0; }
.todo-list li + li { border-top: 1px solid var(--border); }
.todo-list a {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  padding: var(--space-3) var(--space-4);
  color: var(--text); text-decoration: none;
}
.todo-list a:hover { background: var(--bg-sunken); text-decoration: none; }
.todo-main { display: flex; flex-direction: column; gap: 2px; margin-right: auto; min-width: 0; }
.todo-title { font-weight: 700; }
.todo-sub { font-size: var(--fs-sm); color: var(--text-soft); }
.todo-list .flag { flex: none; }
.todo-go {
  flex: none; font-size: var(--fs-xs); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--school-bright, var(--school-primary));
  border: 1px solid currentColor; border-radius: var(--radius-sm);
  padding: 3px 10px; white-space: nowrap;
}
.todo-list a:hover .todo-go { background: var(--school-bright, var(--school-primary)); color: #fff; }

/* A filter that flips, drawn as a switch rather than a lone tab. */
.filter-toggle {
  display: inline-flex; align-items: center; gap: var(--space-2);
  padding: 6px var(--space-3); min-height: var(--control-compact);
  border: 1px solid var(--border); border-radius: 999px;
  font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--text-soft);
  background: var(--bg-raised); text-decoration: none;
}
.filter-toggle:hover { text-decoration: none; color: var(--text); }
.filter-toggle-dot {
  width: 10px; height: 10px; border-radius: 999px;
  background: var(--border-strong, var(--border));
}
.filter-toggle.is-on {
  color: var(--school-bright, var(--school-primary));
  border-color: currentColor;
}
.filter-toggle.is-on .filter-toggle-dot { background: currentColor; }

/* ── Deadlines, promoted out of the small print ──────────────────────────────
   "Answer before X, pay before Y" is the parent's whole contract with a meal
   plan. is-soon = open and inside 48 hours. */
.deadline-strip { display: flex; flex-wrap: wrap; gap: var(--space-3); margin: var(--space-4) 0; }
.deadline {
  display: flex; flex-direction: column; gap: 1px;
  padding: var(--space-2) var(--space-4);
  background: var(--bg-raised); border: 1px solid var(--border);
  border-left: 3px solid var(--school-bright, var(--school-primary));
  border-radius: var(--radius-sm);
}
.deadline-k { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text-soft); }
.deadline-v { font-weight: 700; }
.deadline.is-soon { border-left-color: var(--st-warning-fg); background: var(--st-warning-bg); }
.deadline.is-soon .deadline-k { color: var(--st-warning-fg); }
.deadline.is-past { opacity: .6; border-left-color: var(--border-strong, var(--border)); }
/* The how-to-pay chip holds prose, not a date — let it wrap and breathe. */
.deadline--pay { max-width: 24rem; }
.deadline--pay .deadline-v { font-weight: 600; overflow-wrap: break-word; }

/* The chosen half of a yes/no keeps the state visible after the click. */
.seg { display: inline-flex; }
.seg .mini { border-radius: 0; }
.seg .mini:first-child { border-radius: var(--radius-sm) 0 0 var(--radius-sm); }
.seg .mini:last-child { border-radius: 0 var(--radius-sm) var(--radius-sm) 0; margin-left: -1px; }
.seg .mini.is-chosen {
  background: var(--school-bright, var(--school-primary));
  border-color: var(--school-bright, var(--school-primary));
  color: #fff; font-weight: 700;
}

/* The view toggle drawn as the links beside it, because it navigates. */
.sidebar-user form { display: contents; }
button.sidebar-user-logout { background: none; border: 0; padding: 0; cursor: pointer; text-align: left; font: inherit; }
button.more-toggle { background: none; border: 0; padding: 0; cursor: pointer; font: inherit; color: inherit; }

/* The voluntold confirm sits inline with the holder chip. */
.inline-confirm { display: inline-flex; margin-left: var(--space-2); }

/* Confirm-with-a-name: the input stays modest beside its button. */
.confirm-form { display: inline-flex; gap: var(--space-2); align-items: center; flex-wrap: wrap; }
.confirm-form .form-input { width: 15rem; max-width: 100%; min-height: var(--control-compact); }

.job-manage-who {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  margin-top: var(--space-3); padding-top: var(--space-3); border-top: 1px solid var(--border);
}
.job-edit .assign-form { margin-top: var(--space-3); }

/* Files hanging off a post or a game. */
.attach-list { list-style: none; margin: var(--space-3) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.attach-list li { display: flex; align-items: center; gap: var(--space-3); font-size: var(--fs-sm); }
.attach-list a { font-weight: 600; }
.event-files { display: flex; flex-wrap: wrap; gap: var(--space-3); margin-top: 2px; }
.event-files a { font-size: var(--fs-xs); font-weight: 600; }

/* "Show password" sits under the field it reveals, quiet and small. */
.pw-reveal { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-2); font-size: var(--fs-xs); color: var(--text-soft); cursor: pointer; }
.pw-reveal input { margin: 0; }
