/* ============================================================================
   components.css — the reusable "blocks" every module is built from. Keeping
   them here (not per-module) is what makes the platform consistent and easy to
   learn: a card, a status chip, a table look and behave the same everywhere.
   ============================================================================ */

/* ---- Buttons ---------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2);
  min-height: var(--tap); padding: 0 var(--sp-4); border-radius: 10px;
  font-weight: 700; font-size: var(--fs-sm); border: 1px solid var(--line);
  background: var(--surface); color: var(--ink); transition: background var(--dur), border-color var(--dur);
  white-space: nowrap;
}
/* Background AND border change on hover. Background alone is invisible when a
   button happens to sit on --surface-3 already, which is where the audit found
   a `.btn.sm` with no hover feedback at all: the rule was firing and changing
   the colour to the one already behind it. The border always differs. */
.btn:hover { background: var(--surface-3); border-color: var(--muted); text-decoration: none; }
.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.btn.primary:hover { background: var(--planetary-600); border-color: var(--planetary-600); }
.btn.ghost { background: transparent; border-color: transparent; }
/* The ghost variant needs its own border on hover: `.btn.ghost` sets
   border-color:transparent and sits AFTER `.btn:hover` in this file, so it wins
   at equal specificity and cancels the border change inherited from it. A small
   ghost button on a --surface-3 panel was left with no hover feedback at all. */
.btn.ghost:hover { background: var(--surface-3); border-color: var(--line); }
.btn.sm { min-height: 34px; padding: 0 var(--sp-3); font-size: var(--fs-xs); }
.btn.icon { padding: 0; width: var(--tap); flex: none; }
.btn.icon.sm { width: 34px; }
.btn[disabled] { opacity: .5; cursor: not-allowed; }

/* ---- Icon-button in the top bar --------------------------------------- */
.menu-btn { align-items: center; justify-content: center; width: var(--tap); height: var(--tap);
  border-radius: 10px; color: var(--ink); }
.menu-btn:hover { background: var(--surface-3); }

/* ---- Panels / cards --------------------------------------------------- */
.panel { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow-sm); padding: var(--sp-5); }
.panel-head { display: flex; align-items: flex-start; gap: var(--sp-3); margin-bottom: var(--sp-4); }
.panel-head .grow { min-width: 0; }
.panel-head h2, .panel-head h3 { margin: 0; }
.panel-head .desc { color: var(--muted); font-size: var(--fs-sm); margin-top: 2px; }

/* Page header block */
.page-header { margin-bottom: var(--sp-6); }
.page-header .eyebrow { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--accent); }
.page-header h1 { margin: 4px 0 6px; }
.page-header p { color: var(--body); }

/* ---- Stat tiles ------------------------------------------------------- */
.stat-grid { display: grid; gap: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); margin-bottom: var(--sp-6); }
.stat { background: var(--surface); border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4); box-shadow: var(--shadow-sm); }
.stat .k { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.stat .v { font-size: 1.7rem; /* audit-ok: stat-grid headline figure, larger than any body size */ font-weight: 800; color: var(--ink); line-height: 1.1; margin-top: 4px; font-variant-numeric: tabular-nums; }
.stat .meta { font-size: var(--fs-xs); color: var(--muted); margin-top: 4px; }

/* ---- Status chips ----------------------------------------------------- */
.chip {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-xs); font-weight: 700; padding: 3px 9px; border-radius: var(--radius-pill);
  border: 1px solid transparent; white-space: nowrap;
}
.chip::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; opacity: .9; }
.chip.ok      { color: var(--ok);      background: var(--ok-bg);      border-color: var(--ok-line); }
.chip.warn    { color: var(--warn);    background: var(--warn-bg);    border-color: var(--warn-line); }
.chip.bad     { color: var(--bad);     background: var(--bad-bg);     border-color: var(--bad-line); }
.chip.info    { color: var(--info);    background: var(--info-bg);    border-color: var(--info-line); }
.chip.neutral { color: var(--neutral); background: var(--neutral-bg); border-color: var(--neutral-line); }
.chip.plain::before { display: none; }

/* ---- Redacted (firewall) marker: shown where a role may NOT see a value */
.redacted { display: inline-flex; align-items: center; gap: 5px; color: var(--muted);
  font-size: var(--fs-xs); font-weight: 600; font-style: italic; }
.redacted svg { width: 13px; height: 13px; }

/* ---- Avatar ----------------------------------------------------------- */
.avatar { width: 34px; height: 34px; flex: none; border-radius: 50%; display: grid; place-items: center;
  color: var(--on-color); font-weight: 800; font-size: var(--fs-xs); }

/* ---- Data table ------------------------------------------------------- */
/* contain:paint is load-bearing, not decoration. overflow-x:auto alone still let
   a wide table contribute to the PAGE's scroll width on narrow viewports, so the
   whole document scrolled sideways instead of just the table. Paint containment
   keeps the overflow strictly inside this box. Sticky cells still pin correctly. */
.table-wrap { overflow-x: auto; contain: paint;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--surface); box-shadow: var(--shadow-sm); }
table.data { width: 100%; border-collapse: collapse; font-size: var(--fs-sm); min-width: 640px; }
table.data caption { text-align: left; padding: var(--sp-4) var(--sp-5) 0; color: var(--muted); font-size: var(--fs-xs); }
table.data th, table.data td { padding: var(--sp-3) var(--sp-4); text-align: left; vertical-align: middle; }
table.data thead th {
  position: sticky; top: 0; background: var(--surface-2); z-index: 1;
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .03em; text-transform: uppercase; color: var(--muted);
  border-bottom: 1px solid var(--line);
}
table.data thead th button.sort {
  display: inline-flex; align-items: center; gap: 4px; font: inherit; color: inherit;
  text-transform: inherit; letter-spacing: inherit;
}
table.data thead th button.sort .arrow { opacity: .4; transition: transform var(--dur); }
table.data thead th[aria-sort="ascending"] .arrow { opacity: 1; }
table.data thead th[aria-sort="descending"] .arrow { opacity: 1; transform: rotate(180deg); }
table.data tbody tr { border-bottom: 1px solid var(--line-soft); }
table.data tbody tr:last-child { border-bottom: none; }
table.data tbody tr:hover { background: var(--surface-2); }
table.data td .name-cell { display: flex; align-items: center; gap: var(--sp-3); }
table.data td .name-cell .who { display: flex; flex-direction: column; }
table.data td .name-cell .who b { color: var(--ink); font-weight: 700; }
table.data td .name-cell .who span { color: var(--muted); font-size: var(--fs-xs); }
.tnum { font-variant-numeric: tabular-nums; }

/* ---- Toolbar (filters row above a table) ------------------------------ */
.toolbar { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; margin-bottom: var(--sp-4); }
.search-field { position: relative; flex: 1 1 240px; min-width: 200px; }
.search-field svg { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--muted); pointer-events: none; }
.search-field input { width: 100%; min-height: var(--tap); padding: 0 var(--sp-4) 0 40px;
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink); }
.search-field input::placeholder { color: var(--muted); }

.field { min-height: var(--tap); padding: 0 34px 0 var(--sp-3); border: 1px solid var(--line);
  border-radius: 10px; background: var(--surface); color: var(--ink); font-weight: 600; font-size: var(--fs-sm);
  appearance: none; background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236a7391' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; }

/* ---- Segmented control (used by the role switcher, filters) ----------- */
.segmented { display: inline-flex; background: var(--surface-3); border-radius: 10px; padding: 3px; gap: 2px; }
.segmented button { min-height: 34px; padding: 0 var(--sp-3); border-radius: 8px; font-weight: 700;
  font-size: var(--fs-xs); color: var(--muted); }
.segmented button[aria-pressed="true"] { background: var(--surface); color: var(--ink); box-shadow: var(--shadow-sm); }

/* ---- Empty state ------------------------------------------------------ */
.empty { text-align: center; padding: var(--sp-10) var(--sp-4); color: var(--muted); }
/* h2 as well as h3: an empty state directly under the page h1 must not skip a
   level, and heading level is decided by where it sits in the document rather
   than by how it should look. */
.empty h2,
.empty h3 { color: var(--ink); margin-bottom: 6px; font-size: var(--fs-lg); }

/* ---- Role-switch bar (View as) ---------------------------------------- */
/* min-width:0 in both places so the select can actually shrink on a phone.
   A <select> is as wide as its longest option, and "Executive Suite (AD /
   Deputy / CFO)" was wide enough to push the theme toggle off-screen and
   inflate the whole app grid past the viewport. The wrapper needs it too:
   as a flex item its automatic minimum size is min-content, which pins it
   open no matter what the select itself is allowed to do. */
.viewas { display: flex; align-items: center; gap: var(--sp-3); flex-wrap: wrap; min-width: 0; }
.viewas .field { max-width: 320px; min-width: 0; flex: 1 1 auto;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.firewall-note { display: flex; gap: var(--sp-2); align-items: center; color: var(--muted); font-size: var(--fs-xs); }

/* ============================================================================
   Dashboard blocks (AD/GM command centre). Reusable where they can be.
   ============================================================================ */

.avatar.lg { width: 58px; height: 58px; font-size: var(--fs-md); border-radius: 16px; }

/* Owner identity header */
.identity { display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap; margin-bottom: var(--sp-5); }
.identity h1 { font-size: var(--fs-xl); margin: 0; }
.identity .grow { min-width: 220px; }

/* Explainer / callout note */
.note { display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-4); border-radius: var(--radius); font-size: var(--fs-sm); line-height: 1.5;
  margin-bottom: var(--sp-5); border: 1px solid var(--line); background: var(--surface-2); }
.note svg { flex: none; margin-top: 1px; }
.note.info { background: var(--info-bg); border-color: var(--info-line); color: var(--ink); }
.note.info svg { color: var(--info); }
.linklike { color: var(--accent); font: inherit; font-weight: 700; text-decoration: underline; padding: 0; }
.linklike:hover { color: var(--planetary-700); }

/* Two-column dashboard region — stacks on tablet/phone */
.dash-cols { display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr); gap: var(--sp-5); margin-bottom: var(--sp-5); align-items: start; }
@media (max-width: 860px) { .dash-cols { grid-template-columns: minmax(0, 1fr); } }

/* Approvals inbox */
.approvals { list-style: none; display: flex; flex-direction: column; gap: 2px; }
.approval { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-3);
  border-radius: 12px; border: 1px solid transparent; }
.approval + .approval { border-top: 1px solid var(--line-soft); border-radius: 0; }
.approval:hover { background: var(--surface-2); border-color: var(--line-soft); border-radius: 12px; }
.approval-ic { width: 36px; height: 36px; flex: none; border-radius: 10px; display: grid; place-items: center;
  background: var(--accent-tint); color: var(--accent); }
.approval .grow { min-width: 0; }
.approval-what { font-weight: 600; color: var(--ink); font-size: var(--fs-sm); }
.approval-from { color: var(--muted); font-size: var(--fs-xs); margin-top: 2px; }

/* Connected dashboards */
.connected { display: flex; flex-direction: column; gap: var(--sp-2); }
.connect-card { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  padding: var(--sp-3); border: 1px solid var(--line); border-radius: 12px; background: var(--surface);
  transition: border-color var(--dur), background var(--dur); min-height: var(--tap); }
.connect-card:hover { border-color: var(--info-line); background: var(--surface-2); }
.connect-ic { width: 34px; height: 34px; flex: none; border-radius: 10px; display: grid; place-items: center; color: var(--on-color); }
.connect-card .grow { min-width: 0; }
.connect-name { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }
.connect-who { font-size: var(--fs-xs); }
.connect-metric { font-size: var(--fs-xs); color: var(--body); font-weight: 600; margin-top: 2px; }
.connect-go { color: var(--muted); flex: none; }

/* Budget bars */
.budget-list { display: flex; flex-direction: column; gap: var(--sp-3); }
.budget-row { display: grid; grid-template-columns: minmax(140px, 1.2fr) 2fr auto auto; gap: var(--sp-3);
  align-items: center; font-size: var(--fs-sm); }
.budget-name { display: flex; align-items: center; gap: var(--sp-2); color: var(--ink); font-weight: 600; min-width: 0; }
.budget-name .dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.budget-bar { height: 9px; border-radius: var(--radius-pill); background: var(--surface-3); overflow: hidden; }
.budget-fill { display: block; height: 100%; border-radius: var(--radius-pill); }
.budget-fill.ok { background: var(--ok); } .budget-fill.warn { background: var(--warn); } .budget-fill.bad { background: var(--bad); }
.budget-pct { font-weight: 700; color: var(--ink); text-align: right; min-width: 42px; }
.budget-amt { font-size: var(--fs-xs); white-space: nowrap; }
@media (max-width: 620px) {
  .budget-row { grid-template-columns: 1fr auto; row-gap: 4px; }
  .budget-bar { grid-column: 1 / -1; order: 3; }
  .budget-amt { display: none; }
}

/* ---- Eligibility board (Compliance) ----------------------------------- */
.elig-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.elig-card { text-align: left; padding: var(--sp-4); border: 1px solid var(--line); border-radius: 12px;
  background: var(--surface); transition: border-color var(--dur), background var(--dur); }
.elig-card:hover { border-color: var(--info-line); background: var(--surface-2); }
.elig-bar { display: flex; height: 8px; border-radius: var(--radius-pill); overflow: hidden; background: var(--surface-3); gap: 2px; }
.elig-bar .seg { display: block; height: 100%; }
.elig-bar .seg.ok { background: var(--ok); } .elig-bar .seg.warn { background: var(--warn); } .elig-bar .seg.bad { background: var(--bad); }

/* ---- Fixture board (Schedules) ---------------------------------------- */
.fixtures { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); }
.fixture { display: flex; gap: var(--sp-4); padding: var(--sp-4); border: 1px solid var(--line);
  border-radius: 12px; background: var(--surface); }
.fixture .grow { min-width: 0; }
.fixture-date { flex: none; width: 66px; text-align: center; padding-right: var(--sp-3);
  border-right: 1px solid var(--line-soft); display: flex; flex-direction: column; justify-content: center; }
.fx-day  { font-size: var(--fs-lg); font-weight: 800; color: var(--ink); line-height: 1.1; font-variant-numeric: tabular-nums; }
.fx-dow  { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--accent); }
.fx-time { font-size: var(--fs-xs); color: var(--muted); margin-top: 3px; }
.fixture-opp { color: var(--body); font-size: var(--fs-sm); }
.fixture-opp b { color: var(--ink); }

/* Notes disclosure — keyboard/touch accessible, unlike a title tooltip */
.fixture-notes, .cell-notes { margin-top: var(--sp-2); }
.fixture-notes summary, .cell-notes summary {
  cursor: pointer; font-size: var(--fs-xs); font-weight: 700; color: var(--accent);
  list-style: none; display: inline-flex; align-items: center; gap: 4px; min-height: 24px;
}
.fixture-notes summary::-webkit-details-marker, .cell-notes summary::-webkit-details-marker { display: none; }
.fixture-notes summary::before, .cell-notes summary::before { content: "▸"; transition: transform var(--dur); display: inline-block; }
.fixture-notes[open] summary::before, .cell-notes[open] summary::before { transform: rotate(90deg); }
.fixture-notes p, .cell-notes p { font-size: var(--fs-xs); color: var(--body); margin-top: 4px; max-width: 46ch; }

/* ============================================================================
   Athlete App. One component, two presentations:
     .is-live    — the athlete's own app: full width, bottom tab bar, real product
     .is-preview — the same thing inside a phone frame for staff to look at
   ============================================================================ */
.athlete-app { display: flex; flex-direction: column; background: var(--bg); min-height: 0; }
.aa-screen { flex: 1 1 auto; overflow-y: auto; padding: var(--sp-4); }

/* Live: fills the content area, tabs pinned to the bottom of the viewport */
.athlete-app.is-live { height: calc(100dvh - var(--topbar-h) - 30px); }

/* Preview: sized like a phone inside a frame */
.aa-preview-wrap { display: grid; grid-template-columns: minmax(300px, 340px) minmax(0, 1fr); gap: var(--sp-8); align-items: start; }
@media (max-width: 880px) { .aa-preview-wrap { grid-template-columns: minmax(0, 1fr); } }
.aa-phone { border: 10px solid var(--device-bezel); border-radius: 34px; overflow: hidden; background: var(--bg);
  box-shadow: var(--shadow-lg); height: 620px; display: flex; }
.aa-phone .athlete-app { flex: 1 1 auto; min-height: 0; }
.aa-caption h2 { font-size: var(--fs-lg); margin-bottom: var(--sp-3); }
.aa-caption p { color: var(--body); font-size: var(--fs-sm); margin-bottom: var(--sp-3); }

/* Bottom tab bar */
.aa-tabs { display: flex; flex: none; background: var(--surface); border-top: 1px solid var(--line);
  padding-bottom: env(safe-area-inset-bottom); }
.aa-tab { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--sp-2) 2px; min-height: 56px; justify-content: center;
  color: var(--muted); font-size: var(--fs-2xs); font-weight: 700; border-top: 2px solid transparent; }
.aa-tab[aria-selected="true"] { color: var(--accent); border-top-color: var(--accent); background: var(--accent-tint); }
.aa-tab:hover { color: var(--ink); }

/* Content blocks */
.aa-stack { display: flex; flex-direction: column; gap: var(--sp-3); }
.aa-h { font-size: var(--fs-lg); margin-bottom: var(--sp-1); }
.aa-card { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; padding: var(--sp-4); }
.aa-title { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }
.aa-klbl { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted); margin-bottom: var(--sp-2); padding: 0; }
.aa-card p { font-size: var(--fs-xs); margin-top: 2px; }

.aa-hero { background: linear-gradient(135deg, var(--planetary), var(--galaxy)); color: var(--on-color);
  border-radius: 14px; padding: var(--sp-5) var(--sp-4); }
.aa-hero-date { font-size: var(--fs-xs); opacity: .85; }
.aa-hero-name { font-size: 1.3rem; /* audit-ok: athlete-app hero, sits between --fs-lg and --fs-xl */ font-weight: 800; margin: 2px 0; }
.aa-hero-chip { background: rgba(255,255,255,.2); color: var(--on-color); border-color: transparent; }

.aa-next { border-left: 3px solid var(--bad); }
.aa-next .aa-klbl { color: var(--bad); }
.aa-btn { min-height: 40px; flex: 1 1 auto; }
.aa-btn.full { width: 100%; margin-top: var(--sp-3); }

.aa-row { display: flex; align-items: center; gap: var(--sp-3); padding: var(--sp-2) 0; }
.aa-row + .aa-row { border-top: 1px solid var(--line-soft); }
.aa-time { flex: none; width: 62px; font-size: var(--fs-xs); font-weight: 700; color: var(--accent); }

.aa-prompt { background: var(--accent-tint); border-color: var(--info-line); }
.aa-prompt-ic { width: 34px; height: 34px; flex: none; border-radius: 9px; background: var(--accent);
  color: var(--accent-ink); display: grid; place-items: center; }

.aa-ann { padding: var(--sp-2) 0; }
.aa-ann + .aa-ann { border-top: 1px solid var(--line-soft); }

/* Wellness radio groups — real inputs, large tap targets */
.aa-field { border: 1px solid var(--line); }
.aa-opts { display: flex; gap: var(--sp-2); flex-wrap: wrap; }
.aa-opt { flex: 1 1 auto; }
.aa-opt input { position: absolute; opacity: 0; pointer-events: none; }
.aa-opt label { display: flex; align-items: center; justify-content: center; min-height: var(--tap);
  padding: 0 var(--sp-3); border: 1px solid var(--line); border-radius: 10px; background: var(--surface);
  font-size: var(--fs-sm); font-weight: 600; color: var(--body); cursor: pointer; transition: .15s; }
.aa-opt label:hover { border-color: var(--muted); color: var(--ink); }
.aa-opt input:checked + label { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }
.aa-opt input:focus-visible + label { outline: 3px solid var(--focus); outline-offset: 2px; }

.aa-me { display: flex; flex-direction: column; align-items: center; text-align: center; padding: var(--sp-4) 0; }
.aa-me .avatar { width: 76px; height: 76px; font-size: var(--fs-lg); border-radius: 22px; }
.aa-me-name { font-weight: 800; color: var(--ink); font-size: var(--fs-md); margin-top: var(--sp-3); }

.aa-link { display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  min-height: var(--tap); padding: var(--sp-2) 0; color: var(--body); font-size: var(--fs-sm); font-weight: 600; }
.aa-link + .aa-link { border-top: 1px solid var(--line-soft); }
/* Deliberately not a <button>. A greyed-out button still invites the press and
   then refuses; this reads as a line of text that names what is coming. */
.aa-link.is-soon { cursor: default; color: var(--muted); }
.aa-link.is-soon svg { color: var(--muted); }
.aa-link.is-soon small { flex: none; font-size: var(--fs-xs); }
.aa-link:hover { color: var(--ink); }
.aa-link svg { flex: none; color: var(--accent); }

/* ---- Games management -------------------------------------------------- */
.games-layout { display: grid; grid-template-columns: minmax(280px, 340px) minmax(0, 1fr); gap: var(--sp-5); align-items: start; }
@media (max-width: 900px) { .games-layout { grid-template-columns: minmax(0, 1fr); } }

.game-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.game-card { text-align: left; width: 100%; padding: var(--sp-3); border: 1px solid var(--line);
  border-radius: 12px; background: var(--surface); transition: border-color var(--dur), background var(--dur); }
.game-card:hover { border-color: var(--info-line); background: var(--surface-2); }
.game-card[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-tint);
  box-shadow: 0 0 0 1px var(--accent) inset; }
.game-opp { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }

/* Checklist */
.check-phase { border: 0; padding: 0; margin: 0 0 var(--sp-4); }
.check-phase legend { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); padding: 0 0 var(--sp-2); }
.check-row { display: flex; align-items: flex-start; gap: var(--sp-3); padding: var(--sp-2) 0; }
.check-row + .check-row { border-top: 1px solid var(--line-soft); }
.check-row input[type="checkbox"] { flex: none; width: 20px; height: 20px; margin-top: 2px; accent-color: var(--accent); cursor: pointer; }
/* 44px minimum: the label is the tap target, and a two-line item can otherwise
   fall under the floor on a phone. */
.check-row label { display: flex; flex-direction: column; gap: 1px; cursor: pointer; min-height: 44px; justify-content: center; }
.check-label { font-weight: 600; color: var(--ink); font-size: var(--fs-sm); }
.check-detail { color: var(--muted); font-size: var(--fs-xs); }
.check-row input:checked + label .check-label { text-decoration: line-through; color: var(--muted); }
.check-row input:focus-visible + label { outline: 3px solid var(--focus); outline-offset: 3px; border-radius: 6px; }

.stack { display: flex; flex-direction: column; }

/* ---- Communication Hub -------------------------------------------------- */
.comms-layout { display: grid; grid-template-columns: minmax(260px, 320px) minmax(0, 1fr); gap: var(--sp-5); align-items: start; }
@media (max-width: 900px) { .comms-layout { grid-template-columns: minmax(0, 1fr); } }

.ch-list { display: flex; flex-direction: column; gap: var(--sp-2); }
.ch-card { width: 100%; text-align: left; padding: var(--sp-3); border: 1px solid var(--line);
  border-radius: 12px; background: var(--surface); transition: border-color var(--dur), background var(--dur); }
.ch-card:hover:not([disabled]) { border-color: var(--info-line); background: var(--surface-2); }
.ch-card[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-tint);
  box-shadow: 0 0 0 1px var(--accent) inset; }
.ch-card[disabled] { opacity: .65; cursor: not-allowed; }
.ch-ic { width: 30px; height: 30px; flex: none; border-radius: 9px; display: grid; place-items: center;
  background: var(--surface-3); color: var(--muted); }
.ch-card[aria-pressed="true"] .ch-ic { background: var(--accent); color: var(--accent-ink); }
.ch-name { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }
.ch-unread { flex: none; min-width: 20px; height: 20px; padding: 0 6px; border-radius: var(--radius-pill);
  background: var(--bad); color: var(--on-color); font-size: var(--fs-2xs); font-weight: 800; display: grid; place-items: center; }

/* Messages */
.msg-list { display: flex; flex-direction: column; gap: var(--sp-3); max-height: 380px; overflow-y: auto;
  padding-right: var(--sp-2); margin-bottom: var(--sp-4); }
.msg { display: flex; gap: var(--sp-3); }
.msg .grow { min-width: 0; }
.msg-from { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }
.msg-text { font-size: var(--fs-sm); color: var(--body); margin-top: 2px; }
.msg.me .msg-text { background: var(--accent-tint); border: 1px solid var(--info-line);
  padding: var(--sp-2) var(--sp-3); border-radius: 10px; }

.composer { display: flex; gap: var(--sp-2); }
.msg-input { flex: 1 1 auto; min-width: 0; min-height: var(--tap); padding: 0 var(--sp-3);
  border: 1px solid var(--line); border-radius: 10px; background: var(--surface); color: var(--ink); }
.msg-input::placeholder { color: var(--muted); }

.ann-compose { display: flex; flex-direction: column; gap: var(--sp-2); padding: var(--sp-3);
  border: 1px dashed var(--line); border-radius: 12px; background: var(--surface-2); margin-bottom: var(--sp-4); }

/* Week calendar */
.cal-key { display: inline-flex; align-items: center; gap: 5px; font-size: var(--fs-xs); color: var(--muted); font-weight: 600; }
.cal-key .dot { width: 8px; height: 8px; border-radius: 50%; }
.week { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: var(--sp-2); }
@media (max-width: 760px) { .week { grid-template-columns: minmax(0, 1fr); } }
.week-day { min-width: 0; }
.week-dow { font-size: var(--fs-xs); font-weight: 800; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); padding-bottom: var(--sp-2); border-bottom: 1px solid var(--line); margin-bottom: var(--sp-2); }
.week-ev { border-left: 3px solid var(--accent); background: var(--surface-2); border-radius: 0 8px 8px 0;
  padding: var(--sp-2); margin-bottom: var(--sp-2); }
.week-time { font-size: var(--fs-2xs); font-weight: 700; color: var(--accent); }
.week-title { font-size: var(--fs-xs); font-weight: 600; color: var(--ink); line-height: 1.3; }

/* ---- Users & Roles: the permission matrix -------------------------------- */
/* The matrix is 11 columns wide, so it scrolls inside .table-wrap on phones
   rather than squashing. The role column is sticky so a horizontal scroll never
   loses the row label — otherwise the dots are meaningless. */
.matrix th, .matrix td { text-align: center; padding: var(--sp-2) var(--sp-1); }
.matrix thead th { font-size: var(--fs-xs); white-space: nowrap; }
.matrix .matrix-role { position: sticky; left: 0; z-index: 1; text-align: left;
  background: var(--surface); min-width: 190px; box-shadow: 1px 0 0 var(--line); }
.matrix thead .matrix-role { background: var(--surface-2); }
.matrix-name { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); line-height: 1.25; }
.matrix-tag { font-size: var(--fs-xs); color: var(--muted); font-weight: 400; }
.matrix-cell { min-width: 46px; }

/* One dot per permission level. The glyph carries the meaning for anyone who
   cannot distinguish the colours; the colour is reinforcement, never the signal. */
.lvl { display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 999px; font-size: 0.85rem; /* audit-ok: plan-card body, between --fs-sm and --fs-base */ line-height: 1;
  background: var(--surface-3); color: var(--muted); }
.lvl.ok      { background: color-mix(in srgb, var(--ok) 18%, transparent);   color: var(--ok); }
.lvl.info    { background: color-mix(in srgb, var(--accent) 16%, transparent); color: var(--accent); }
.lvl.neutral { background: var(--surface-3); color: var(--muted); }
.lvl.warn    { background: color-mix(in srgb, var(--warn) 20%, transparent); color: var(--warn); }
.lvl.bad     { background: transparent; color: var(--line-strong, var(--muted)); }

/* ---- Audit Log ---------------------------------------------------------- */
.note.ok  { background: var(--ok-bg);  border-color: var(--ok-line);  color: var(--ink); }
.note.ok svg  { color: var(--ok); }
.note.bad { background: var(--bad-bg); border-color: var(--bad-line); color: var(--ink); }
.note.bad svg { color: var(--bad); }

/* The chain strip: prev-hash → own-hash for the most recent entries. */
.chain { display: flex; flex-direction: column; gap: 4px; margin-top: var(--sp-3); }
.chain-link { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap;
  font-size: var(--fs-xs); }
.chain-seq { font-weight: 700; color: var(--muted); min-width: 34px; }
.chain-hash { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.72rem; /* audit-ok: plan feature list, between --fs-2xs and --fs-xs */
  background: var(--surface-3); border-radius: 6px; padding: 2px 6px; color: var(--muted); }
.chain-hash.own { background: var(--accent-tint); color: var(--accent); font-weight: 700; }
.chain-arrow { color: var(--muted); }

.audit-list { list-style: none; }
.audit-row { display: flex; gap: var(--sp-3); align-items: flex-start;
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--line); }
.audit-row:last-child { border-bottom: 0; }
.audit-row.altered { background: var(--bad-bg); border-radius: var(--radius);
  padding-left: var(--sp-3); padding-right: var(--sp-3); }
.audit-seq { font-weight: 700; color: var(--muted); font-size: var(--fs-xs);
  min-width: 40px; padding-top: 2px; font-variant-numeric: tabular-nums; }
.audit-action { font-weight: 700; color: var(--ink); }
.audit-detail { color: var(--ink-2, var(--ink)); font-size: var(--fs-sm); margin: 6px 0 4px; }
.audit-detail.withheld { display: flex; align-items: center; gap: var(--sp-2); flex-wrap: wrap; }
.audit-detail.withheld .muted { font-size: var(--fs-xs); }
.audit-meta { color: var(--muted); font-size: var(--fs-xs); }
.audit-hash { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.7rem; /* audit-ok: install-step numbering, deliberately below --fs-2xs */
  background: var(--surface-3); border-radius: 6px; padding: 2px 6px; color: var(--muted);
  flex: none; align-self: center; }

@media (max-width: 560px) {
  /* The hash is reference data, not something to read on a phone. */
  .audit-hash { display: none; }
  .audit-row { gap: var(--sp-2); }
}

/* ---- Sports: program list + detail (same two-column shape as Games) ------ */
.sports-layout { display: grid; gap: var(--sp-5); grid-template-columns: minmax(0, 320px) minmax(0, 1fr); align-items: start; }
.sport-list { display: flex; flex-direction: column; gap: var(--sp-2); max-height: 620px; overflow-y: auto; }
.sport-card { display: block; width: 100%; text-align: left; padding: var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface); }
.sport-card:hover { background: var(--surface-2); }
.sport-card[aria-pressed="true"] { border-color: var(--accent); background: var(--accent-tint); box-shadow: var(--shadow-sm); }
.sport-name { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); }

/* Key/value block used by the program overview. */
.kv-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); }
.kv-k { font-size: var(--fs-xs); font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--muted); }
.kv-v { color: var(--ink); font-size: var(--fs-sm); font-weight: 600; margin-top: 2px; }

/* ---- Inventory ---------------------------------------------------------- */
/* A real checkbox with a label, sized to the 44px touch floor. */
.check-inline { display: inline-flex; align-items: center; gap: var(--sp-2);
  min-height: var(--tap); padding: 0 var(--sp-2); cursor: pointer; color: var(--ink);
  font-size: var(--fs-sm); font-weight: 600; }
.check-inline input { width: 18px; height: 18px; accent-color: var(--accent); }

/* A table row implicated in a conflict — tinted, but the chip carries meaning. */
.row-flagged { background: var(--warn-bg); }
.row-flagged:hover { background: var(--warn-bg); }

@media (max-width: 900px) {
  .sports-layout { grid-template-columns: minmax(0, 1fr); }
  .sport-list { max-height: none; }
}

/* ---- Transportation: assignment verdict cards --------------------------- */
.check-card { border: 1px solid var(--line); border-left: 3px solid var(--neutral); border-radius: var(--radius);
  padding: var(--sp-3); background: var(--surface); }
.check-card.ok  { border-left-color: var(--ok); }
.check-card.warn{ border-left-color: var(--warn); background: var(--warn-bg); }
.check-card.bad { border-left-color: var(--bad); background: var(--bad-bg); }
.check-title { font-weight: 700; color: var(--ink); }

/* Rule name + explanation. The label carries the meaning, colour reinforces. */
.problem-list { list-style: none; margin-top: var(--sp-2); display: flex; flex-direction: column; gap: 6px; }
.problem-list li { display: flex; gap: var(--sp-2); align-items: flex-start;
  font-size: var(--fs-sm); color: var(--ink); }
.problem-rule { flex: none; font-size: var(--fs-xs); font-weight: 800; letter-spacing: .04em;
  text-transform: uppercase; padding: 2px 7px; border-radius: var(--radius-pill);
  background: var(--neutral-bg); color: var(--neutral); }
.problem-rule.bad  { background: var(--bad-bg);  color: var(--bad); }
.problem-rule.warn { background: var(--warn-bg); color: var(--warn); }

/* ---- Ticket sales ------------------------------------------------------- */
.event-row { border: 1px solid var(--line); border-radius: var(--radius); padding: var(--sp-3);
  background: var(--surface); }
.event-row.flagged { border-color: var(--bad-line); background: var(--bad-bg); }
.event-name { font-weight: 700; color: var(--ink); }

/* ---- Media: public bio cards ------------------------------------------- */
/* Deliberately small and uniform: these carry public fields only, and the
   layout should make it obvious there is nothing else behind them. */
.bio-grid { display: grid; gap: var(--sp-3); grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); }
.bio-card { display: flex; gap: var(--sp-3); align-items: flex-start; padding: var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--surface-2); }
.bio-num { font-weight: 800; color: var(--accent); font-variant-numeric: tabular-nums; flex: none; }
.bio-name { font-weight: 700; color: var(--ink); font-size: var(--fs-sm); line-height: 1.2; }

/* ---- Athlete app access -------------------------------------------------
   A chip that is also a control. The chip carries the state; making it a real
   <button> means the keyboard can reach it and a screen reader announces it as
   something that can be pressed, rather than as decoration next to a row. */
.chip-btn {
  background: none; border: 0; padding: 0; cursor: pointer;
  border-radius: var(--radius-pill);
}
.chip-btn:hover .chip { filter: brightness(0.96); }

/* The invitation link, shown verbatim so the coach can see exactly what the
   athlete will receive. Monospace and wrapping, because a link that is cut off
   is a link nobody can check. */
.link-box {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fs-xs); line-height: 1.5;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--radius-sm); padding: var(--sp-2) var(--sp-3);
  color: var(--ink);
}

/* ---- Plan cards ---------------------------------------------------------
   Three abreast on a wide screen, stacking on narrow. The current plan and the
   recommended one are marked with a flag rather than a colour alone, because a
   colour alone is invisible to a third of colour-blind readers. */
.plan-grid {
  display: grid; gap: var(--sp-4); margin-top: var(--sp-4);
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
}
.plan-card {
  position: relative; display: flex; flex-direction: column; gap: var(--sp-2);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: var(--sp-4); background: var(--surface);
}
.plan-card.popular { border-color: var(--accent-edge); box-shadow: var(--shadow-sm); }
.plan-card.current { border-color: var(--ok); }
.plan-card h3 { margin-bottom: 0; }
.plan-flag {
  position: absolute; top: calc(-1 * var(--sp-2)); left: var(--sp-4);
  font-size: var(--fs-xs); font-weight: 700; letter-spacing: .02em;
  background: var(--accent); color: var(--accent-ink);
  padding: 2px var(--sp-2); border-radius: var(--radius-pill);
}
.plan-flag.on { background: var(--ok); color: var(--on-color); }
.plan-seats { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); }
.plan-features { list-style: none; padding: 0; margin: var(--sp-2) 0 var(--sp-4); display: grid; gap: 6px; }
.plan-features li { display: flex; align-items: flex-start; gap: var(--sp-2); font-size: var(--fs-sm); }
.plan-features li svg { color: var(--ok); flex: none; margin-top: 3px; }
.plan-card .btn { margin-top: auto; }

/* ---- The sign-in gate ---------------------------------------------------
   A whole screen, not a modal. A modal implies the application is behind it;
   for somebody with no session there is no application behind it — nothing is
   built, no data is fetched. The visual language should say so. */
.signin-shell {
  min-height: 100dvh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--sp-5);
  padding: var(--sp-6) var(--sp-4); background: var(--bg);
}
.signin-card {
  width: 100%; max-width: 420px;
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius-lg); box-shadow: var(--shadow);
  padding: var(--sp-6);
}
.signin-brand { margin-bottom: var(--sp-5); }
.signin-brand .sg-logo { width: 200px; max-width: 100%; height: auto; }
:root[data-theme="dark"] .signin-brand,
:root:not([data-theme="light"]) .signin-brand {
  background: #FFFFFF; border-radius: var(--radius-sm);
  padding: var(--sp-2) var(--sp-3); display: inline-block;
}
@media (prefers-color-scheme: light) {
  :root:not([data-theme="dark"]) .signin-brand { background: none; padding: 0; }
}
.signin-card h1 { font-size: var(--fs-xl); margin-bottom: var(--sp-2); }
.signin-form { display: grid; gap: var(--sp-4); margin-top: var(--sp-5); }
.signin-form input {
  width: 100%; min-height: var(--tap);
  padding: 0 var(--sp-3); border: 1px solid var(--line);
  border-radius: var(--radius-sm); background: var(--surface-2); color: var(--ink);
}
.signin-submit { width: 100%; min-height: var(--tap); justify-content: center; }
.signin-error {
  background: var(--bad-bg); border: 1px solid var(--bad-line);
  color: var(--bad); border-radius: var(--radius-sm);
  padding: var(--sp-3); font-size: var(--fs-sm); font-weight: 600;
}
.signin-error[hidden] { display: none; }
.signin-foot { color: var(--muted); font-size: var(--fs-xs); text-align: center; max-width: 420px; }

/* The confirmation after asking for a reset link. Not an error, not a success
   in the celebratory sense — a neutral statement that says nothing about
   whether the address exists, matching what the server will admit to. */
.signin-notice {
  background: var(--info-bg); border: 1px solid var(--info-line);
  color: var(--info); border-radius: var(--radius-sm);
  padding: var(--sp-3); font-size: var(--fs-sm); font-weight: 600;
}
.signin-notice[hidden] { display: none; }
.signin-alt {
  background: none; border: 0; padding: 0; cursor: pointer;
  color: var(--link); font-size: var(--fs-sm); font-weight: 600;
  text-align: center; text-decoration: underline;
}

/* ---- Install ------------------------------------------------------------
   Shown inside Help rather than as a banner over the app. A prompt that covers
   the screen somebody is trying to read is the one everybody dismisses without
   reading, and then never sees again. */
.install-card {
  display: flex; align-items: center; gap: var(--sp-4); flex-wrap: wrap;
  border: 1px solid var(--accent-edge); border-radius: var(--radius);
  background: var(--accent-tint); padding: var(--sp-4);
  margin-bottom: var(--sp-4);
}
.install-card b { color: var(--ink); display: block; margin-bottom: 4px; }
.install-card p { margin: 0; font-size: var(--fs-sm); }
.install-steps {
  margin: var(--sp-2) 0 var(--sp-2); padding-left: var(--sp-5);
  display: grid; gap: 6px; font-size: var(--fs-sm); color: var(--body);
}
.install-steps b { display: inline; }
.install-glyph { font-size: 1.1em; vertical-align: -2px; }

/* ---- Touch targets ------------------------------------------------------
   CONTRIBUTING has required 44px minimum touch targets since the accessibility
   pass, and on a phone the app was not meeting its own rule: the season
   selector, the three top-bar icon buttons and every `.sm` button measured
   under 40px. Small enough to be missed by a thumb, which on a sideline in the
   rain is the difference between recording an injury and giving up.

   Scoped to coarse pointers so a mouse-driven desktop keeps its denser layout —
   a 44px minimum everywhere would inflate tables and toolbars for people who
   are hitting a 3px target accurately anyway. */
@media (pointer: coarse) {
  .btn,
  .btn.sm,
  .chip-btn,
  .linklike,
  .row-open,
  select,
  input[type="search"],
  .field input {
    min-height: var(--tap);
  }

  /* Icon-only buttons need the width too — a 44px-tall, 28px-wide target is
     still a miss waiting to happen. */
  .btn.icon,
  .btn.icon.sm,
  .menu-btn {
    min-width: var(--tap);
    min-height: var(--tap);
  }

  /* Rows people tap to open a record. */
  .approval,
  .nav-link,
  .feed-row,
  .search-hit {
    min-height: var(--tap);
  }

  /* These two need naming explicitly rather than relying on the element
     selectors above. `.season-select` sets min-height:34px at class
     specificity, which beats a bare `select` rule no matter where it sits in
     the file; `.conn-pill` sets only padding, so it has no height to raise.
     A generic rule that loses on specificity is a rule that silently does
     nothing, which is how the first attempt at this passed review and failed
     measurement. */
  /* Scoped to its parent so it wins on SPECIFICITY rather than on order.
     chrome.css loads after this file and sets min-height:34px at the same
     specificity, so an unscoped `.season-select` here loses every time — which
     it did, and the measurement caught it. */
  .sb-season .season-select { min-height: var(--tap); }

  .conn-pill {
    min-height: var(--tap);
    display: inline-flex; align-items: center;
    padding-top: 0; padding-bottom: 0;
  }
}

/* A list that is a list semantically and not visually. Used wherever rows
   carry their own structure — report pickers, note feeds — and a bullet would
   just be noise. It had been used in two modules with no rule behind it, so
   both rendered browser default bullets. */
ul.plain, ol.plain { list-style: none; margin: 0; padding: 0; }
ul.plain > li, ol.plain > li { margin: 0; }

/* ---- Reports: the picker and the suppressed row ------------------------- */

.report-pick {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  padding: var(--sp-3); border: 1px solid transparent; border-left: 3px solid transparent;
  border-radius: var(--radius-sm); background: none; color: var(--body); font: inherit;
  cursor: pointer; min-height: var(--tap);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.report-pick:hover { background: var(--surface-2); }
.report-pick[aria-current='true'] {
  background: var(--accent-tint); border-left-color: var(--accent);
}
.report-pick .grow { display: flex; flex-direction: column; min-width: 0; }
.report-name { color: var(--ink); font-weight: 600; }
.report-desc { color: var(--muted); font-size: var(--fs-xs); }

/* A suppressed row is deliberately quiet rather than alarming. It is not an
   error — the figure exists and is correct, it is simply too small to publish
   without naming somebody. Styling it red would teach people to try to
   "fix" it. */
tr.is-suppressed td { background: var(--surface-2); }
tr.is-suppressed td:first-child { font-style: normal; }

/* ============================================================================
   CALENDAR — the week grid

   Absolutely positioned events over an hour rail. The grid scrolls
   horizontally on a phone rather than reflowing to a list: a week that
   collapses into seven stacked days stops being a week, and comparing Tuesday
   against Thursday is the entire reason to look at one.
   ============================================================================ */

.cal-bar {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3);
  justify-content: space-between; margin-bottom: var(--sp-4);
}
.cal-range { color: var(--ink); white-space: nowrap; }
.flip { transform: rotate(180deg); }

.cal-toggle {
  font: inherit; font-size: var(--fs-xs); font-weight: 600;
  padding: 5px var(--sp-3); border-radius: var(--radius-pill);
  border: 1px solid var(--line); background: var(--surface); color: var(--muted);
  cursor: pointer; min-height: 30px;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
              border-color var(--dur) var(--ease), opacity var(--dur) var(--ease);
}
.cal-toggle:hover { border-color: var(--muted); }
/* Pressed is the ON state — the calendar is showing. Unpressed fades rather
   than disappearing, so it is obvious something was switched off rather than
   never present. */
.cal-toggle[aria-pressed='true'] {
  background: var(--accent-tint); border-color: var(--accent); color: var(--ink);
}
.cal-toggle[aria-pressed='false'] { opacity: .55; text-decoration: line-through; }

.cal-outside {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-2);
  padding: var(--sp-3); margin-bottom: var(--sp-3);
  background: var(--surface-2); border-radius: var(--radius-sm);
  font-size: var(--fs-sm);
}

/* The day header's height is shared: the hour rail must be padded by exactly
   this much or every hour label sits a few pixels off its own gridline. It was
   written as a bare 42px in both places, which is the same number in two
   files' worth of distance — and the style audit was right to flag it. */
.cal-grid-wrap { overflow-x: auto; --cal-head-h: 42px; }
.cal-grid {
  display: grid;
  grid-template-columns: 62px repeat(7, minmax(112px, 1fr));
  min-width: 860px;
}

.cal-rail { padding-top: var(--cal-head-h); }
.cal-hour {
  position: relative; border-top: 1px solid var(--line-soft);
  color: var(--muted); font-size: var(--fs-2xs); text-align: right;
  padding-right: var(--sp-2);
}
.cal-hour span { position: relative; top: -7px; }

.cal-day { border-left: 1px solid var(--line-soft); min-width: 0; }
.cal-day.is-today { background: var(--accent-tint); }

.cal-dayhead {
  height: var(--cal-head-h); display: flex; flex-direction: column; align-items: center;
  justify-content: center; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; background: var(--surface); z-index: 2;
}
.cal-dayhead.is-today { background: var(--accent); color: var(--accent-ink); }
.cal-dow { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .06em; }
.cal-date { font-weight: 700; font-size: var(--fs-sm); }

.cal-slots { position: relative; height: var(--cal-h); }
.cal-slot { border-top: 1px solid var(--line-soft); }

.cal-event {
  position: absolute; overflow: hidden; text-align: left;
  padding: 3px 5px; border-radius: 6px; cursor: pointer;
  border: 1px solid transparent; border-left: 3px solid var(--neutral);
  background: var(--surface-3); color: var(--ink);
  font: inherit; font-size: var(--fs-2xs); line-height: 1.25;
  display: flex; flex-direction: column; gap: 1px;
  transition: filter var(--dur) var(--ease);
}
.cal-event:hover { filter: brightness(.96); z-index: 3; }
.cal-event.ok      { border-left-color: var(--ok); }
.cal-event.warn    { border-left-color: var(--warn); }
.cal-event.bad     { border-left-color: var(--bad); }
.cal-event.info    { border-left-color: var(--info); }
.cal-event.neutral { border-left-color: var(--neutral); }
.cal-time { color: var(--muted); font-variant-numeric: tabular-nums; }
.cal-title { font-weight: 600; overflow: hidden; text-overflow: ellipsis; }
.cal-lock { position: absolute; top: 3px; right: 4px; color: var(--muted); }

@media (max-width: 720px) {
  .cal-bar { flex-direction: column; align-items: stretch; }
}

/* ---- Messages -------------------------------------------------------------- */

.msg-pick, .msg-dir-row {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  padding: var(--sp-3); border: 1px solid transparent; border-left: 3px solid transparent;
  border-radius: var(--radius-sm); background: none; color: var(--body); font: inherit;
  cursor: pointer; min-height: var(--tap);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.msg-pick:hover, .msg-dir-row:hover { background: var(--surface-2); }
.msg-pick[aria-current='true'], .msg-dir-row[aria-pressed='true'] {
  background: var(--accent-tint); border-left-color: var(--accent);
}
.msg-pick .grow, .msg-dir-row .grow { display: flex; flex-direction: column; min-width: 0; }
.msg-subject { color: var(--ink); font-weight: 600; }
.msg-people, .msg-preview {
  color: var(--muted); font-size: var(--fs-xs);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-when { color: var(--muted); font-size: var(--fs-xs); flex: none; }

.msg-dir { max-height: 46vh; overflow-y: auto; display: flex; flex-direction: column; gap: 2px; }

.msg-thread {
  display: flex; flex-direction: column; gap: var(--sp-4);
  padding: var(--sp-4); max-height: 52vh; overflow-y: auto;
}
.msg-row { display: flex; gap: var(--sp-3); align-items: flex-start; }
.msg-text {
  margin: var(--sp-1) 0 0; color: var(--body);
  /* Long single-token pastes — a URL, a file path — must wrap rather than
     stretch the panel and force the whole page to scroll sideways. */
  overflow-wrap: anywhere;
}

.msg-composer { padding: var(--sp-4); border-top: 1px solid var(--line); }
.msg-composer textarea { width: 100%; resize: vertical; }

/* ---- Settings -------------------------------------------------------------- */

/* Who a setting affects, stated on every panel. Deliberately quiet — it is
   context, not a warning, and styling it as an alert would make people stop
   reading the ones that ARE warnings. */
.set-affects {
  display: flex; align-items: center; gap: var(--sp-2);
  margin: 0 0 var(--sp-3); padding: var(--sp-2) var(--sp-3);
  background: var(--surface-2); border-radius: var(--radius-sm);
  color: var(--muted); font-size: var(--fs-xs);
}

.set-row {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) 0; border-bottom: 1px solid var(--line-soft);
}
.set-row:last-child { border-bottom: 0; }
.set-row .grow { min-width: 0; }
.set-row .muted { font-size: var(--fs-xs); }

.set-h3 {
  margin: 0 0 var(--sp-2); font-size: var(--fs-sm); color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}

.set-swatch {
  width: 26px; height: 26px; border-radius: 7px;
  border: 1px solid var(--line); display: inline-block;
}

.set-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fs-2xs); background: var(--surface-3);
  padding: 2px var(--sp-2); border-radius: 5px; color: var(--body);
}

/* The one panel that destroys things. Bordered, not filled — a solid red block
   reads as "something is broken" rather than "be careful here". */
.panel.is-danger { border-color: var(--bad); }
.panel.is-danger .panel-head { border-bottom-color: var(--bad); }

/* ---- Search screen --------------------------------------------------------- */

.sr-field {
  display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-4); color: var(--muted);
}
.sr-field input {
  flex: 1; font: inherit; font-size: var(--fs-md); color: var(--ink);
  background: none; border: 0; padding: var(--sp-3) 0; min-height: var(--tap);
}
.sr-field input:focus { outline: none; }
/* The ring goes on the wrapper, so the whole field lights up rather than a
   hairline around the text — the input has no border of its own. */
.sr-field:focus-within { box-shadow: inset 0 0 0 2px var(--focus); border-radius: var(--radius); }

.sr-hit {
  display: flex; align-items: center; gap: var(--sp-3); width: 100%; text-align: left;
  padding: var(--sp-3) var(--sp-4); background: none; border: 0;
  border-left: 3px solid transparent; color: var(--body); font: inherit;
  cursor: pointer; min-height: var(--tap);
  transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease);
}
.sr-hit:hover { background: var(--surface-2); border-left-color: var(--accent); }
.sr-hit .grow { display: flex; flex-direction: column; min-width: 0; }

.sr-kind {
  flex: none; min-width: 84px; font-size: var(--fs-2xs); color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em;
}
.sr-title { color: var(--ink); font-weight: 600; }
.sr-sub, .sr-where {
  color: var(--muted); font-size: var(--fs-xs);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sr-where { flex: none; }

/* Why a row matched. Underline as well as colour, so the mark survives a
   monochrome print and a colour-blind reader. */
.sr-title mark {
  background: var(--accent-tint); color: inherit;
  text-decoration: underline; text-underline-offset: 2px; border-radius: 3px;
}

@media (max-width: 720px) {
  .sr-kind, .sr-where { display: none; }
}


/* ---- The athlete app's own header -------------------------------------------
   Staff see this component inside a phone frame with the sidebar still beside
   them. An ATHLETE sees it as the entire product, so without this bar there was
   no institution name and — the part that matters — no way to end the session.
   -------------------------------------------------------------------------- */

.aa-top { display: flex; align-items: center; gap: var(--sp-3);
  padding: var(--sp-3) var(--sp-4); background: var(--surface);
  border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 5; }
.aa-org { font-size: var(--fs-md); font-weight: 650; margin: 0; line-height: 1.2; }
.aa-sub { font-size: var(--fs-xs); color: var(--muted); }

/* Sign out. Same control in the athlete header, the athlete's Me tab and the
   staff sidebar, because it is the same action and should not be learned three
   times. Sized to the 44px touch target a phone needs. */
.aa-signout, .sb-signout {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none; width: 40px; height: 40px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
  color: var(--muted); cursor: pointer;
}
.aa-signout:hover, .sb-signout:hover { color: var(--ink); border-color: var(--ink); }
.aa-signout:disabled, .sb-signout:disabled { opacity: .5; cursor: default; }
.sb-signout { width: 32px; height: 32px; }


/* ---- Conversation rows in the athlete app ----------------------------------
   These used .aa-card.aa-link, and .aa-link is a flex ROW meant for
   icon + label + chevron. Subject, participants and preview became three
   columns a third of a phone wide each, so a five-word subject wrapped over
   four lines. This is the same information with a hierarchy instead.
   -------------------------------------------------------------------------- */

.aa-convos { display: flex; flex-direction: column; gap: var(--sp-2); }

.aa-convo {
  position: relative;
  display: flex; align-items: flex-start; gap: var(--sp-3);
  width: 100%; text-align: left;
  padding: var(--sp-3); padding-left: calc(var(--sp-3) + 3px);
  background: var(--surface); color: var(--body);
  border: 1px solid var(--line); border-radius: var(--radius);
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease, transform .15s ease;
}

/* The stripe. Flush to the rounded edge, and the thing that carries state:
   invisible at rest, the brand's electric cyan when there is something to
   read, and full height on hover. A whole tinted card for every unread row
   would make a busy inbox look like an emergency. */
.aa-convo::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  border-radius: var(--radius) 0 0 var(--radius);
  background: transparent;
  transition: background .15s ease;
}
.aa-convo.is-theirs::before { background: var(--accent-edge); }

.aa-convo:hover { background: var(--accent-tint); border-color: var(--accent-edge); }
.aa-convo:hover::before { background: var(--accent-edge); }
.aa-convo:active { transform: translateY(1px); }

/* Keyboard users get the same signal, not a browser default that the tinted
   hover would otherwise be the only visible affordance. */
.aa-convo:focus-visible {
  outline: 2px solid var(--accent-edge); outline-offset: 2px;
  background: var(--accent-tint);
}

.aa-convo .avatar { flex: none; margin-top: 2px; }

.aa-convo-body { min-width: 0; flex: 1; }   /* min-width:0 is what lets the
                                               children below actually ellipsis
                                               inside a flex parent */

.aa-convo-head { display: flex; align-items: baseline; gap: var(--sp-2); }
.aa-convo-subject {
  font-size: var(--fs-sm); font-weight: 650; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.aa-convo-when { flex: none; font-size: var(--fs-2xs); color: var(--muted); }
.aa-convo.is-theirs .aa-convo-when { color: var(--accent); font-weight: 600; }

.aa-convo-who {
  font-size: var(--fs-2xs); color: var(--muted); margin-top: 1px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Two lines, then ellipsis. One line loses the sense of most messages; three
   turns a list into a transcript and the subjects stop being scannable. */
.aa-convo-last {
  margin: var(--sp-1) 0 0; font-size: var(--fs-xs); color: var(--muted);
  line-height: 1.45;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}
.aa-convo-last b { color: var(--body); font-weight: 600; }
.aa-convo.is-theirs .aa-convo-last { color: var(--body); }

.aa-convo-chev { flex: none; color: var(--muted); align-self: center; }
.aa-convo:hover .aa-convo-chev { color: var(--accent); }

@media (prefers-reduced-motion: reduce) {
  .aa-convo, .aa-convo::before { transition: none; }
  .aa-convo:active { transform: none; }
}


/* ============================================================================
   THE ATHLETE'S PHONE

   When the signed-in role is `athlete`, the staff shell is not chrome around
   the product — it is the wrong product. Hidden rather than restyled: there is
   no phone-sized arrangement of a module sidebar, a staff search and a season
   archive selector that an eighteen-year-old wants on their home screen.

   Everything here is driven by [data-athlete="live"] on the shell root, so it
   applies at every width. A real athlete opening this on a laptop should get
   their app too, not a desktop version of somebody else's console.
   ============================================================================ */

.app[data-athlete="live"] > .sidebar,
.app[data-athlete="live"] > .topbar,
.app[data-athlete="live"] > .scrim,
.app[data-athlete="live"] > .demo-ribbon { display: none; }

.app[data-athlete="live"] {
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: minmax(0, 1fr);
  grid-template-areas: "main";
  /* Dynamic viewport height, not 100vh. On iOS Safari 100vh is the height with
     the browser bars HIDDEN, so a bottom tab bar sits permanently under the
     address bar until you scroll — the single most common way a web app
     announces it is a web app. */
  height: 100dvh;
}
.app[data-athlete="live"] > .main { grid-area: main; min-height: 0; overflow: hidden; }
.app[data-athlete="live"] .view {
  padding: 0; max-width: none; height: 100%;
  display: flex; flex-direction: column; min-height: 0;
}

/* The app fills what is left, and only the middle scrolls. */
.app[data-athlete="live"] .athlete-app.is-live { height: auto; flex: 1 1 auto; min-height: 0; }

/* ---- Header ----------------------------------------------------------------
   Sticky, and padded past the notch. env(safe-area-inset-top) is 0 on every
   device that does not need it, so there is no separate desktop case.
   -------------------------------------------------------------------------- */
.aa-top {
  padding-top: calc(var(--sp-3) + env(safe-area-inset-top));
  padding-left: calc(var(--sp-4) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-4) + env(safe-area-inset-right));
}
.aa-org { font-size: var(--fs-md); line-height: 1.2; }

/* Exit from a preview. Deliberately plain and slightly apologetic — it is
   scaffolding for whoever is demonstrating, not part of an athlete's app. */
.aa-exit {
  display: inline-flex; align-items: center; gap: 6px; flex: none;
  min-height: var(--tap); padding: 0 var(--sp-3);
  border: 1px solid var(--line); border-radius: var(--radius-pill);
  background: var(--surface-2); color: var(--muted);
  font-size: var(--fs-2xs); font-weight: 700; cursor: pointer;
}
.aa-exit:hover { color: var(--ink); border-color: var(--accent-edge); }

/* ---- The scrolling middle --------------------------------------------------
   overscroll-behavior stops a flick at the end of the list from rubber-banding
   the page behind it, or triggering pull-to-refresh halfway through reading a
   message.
   -------------------------------------------------------------------------- */
.athlete-app.is-live .aa-screen {
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
  padding-left: calc(var(--sp-4) + env(safe-area-inset-left));
  padding-right: calc(var(--sp-4) + env(safe-area-inset-right));
  padding-bottom: var(--sp-6);
}

/* ---- Bottom tabs -----------------------------------------------------------
   Already padded for the home indicator. What is added: the horizontal safe
   area for landscape on a notched phone, and a tap highlight that does not
   flash a grey box over the whole tab.
   -------------------------------------------------------------------------- */
.athlete-app.is-live .aa-tabs {
  padding-left: env(safe-area-inset-left);
  padding-right: env(safe-area-inset-right);
  position: relative; z-index: 2;
}
.aa-tab { -webkit-tap-highlight-color: transparent; }

/* ---- Phones ---------------------------------------------------------------- */
@media (max-width: 560px) {
  /* 44px is the smallest thing a thumb reliably hits. The desktop sizes are
     fine with a mouse and are not fine here. */
  .app[data-athlete="live"] .aa-signout,
  .app[data-athlete="live"] .aa-exit { min-height: var(--tap); min-width: var(--tap); }

  .app[data-athlete="live"] .aa-card { padding: var(--sp-3); }
  .app[data-athlete="live"] .aa-hero { padding: var(--sp-4) var(--sp-3); }

  /* Full-width buttons stack rather than squeezing two per row. */
  .app[data-athlete="live"] .aa-opts { flex-direction: column; }
  .app[data-athlete="live"] .aa-opts .aa-btn { width: 100%; }

  /* A drawer that slides in from the side on a phone covers the content it
     came from and looks like a desktop pattern shrunk. Coming up from the
     bottom is where a thumb already is, and leaves the context visible. */
  .app[data-athlete="live"] ~ .detail-drawer,
  .athlete-shell .detail-drawer {
    width: 100vw; max-width: 100vw;
    top: auto; bottom: 0; height: min(88dvh, 100%);
    border-radius: 16px 16px 0 0;
    transform: translateY(100%);
    padding-bottom: calc(var(--sp-4) + env(safe-area-inset-bottom));
  }
  .athlete-shell .detail-drawer.open { transform: translateY(0); }
}

/* ---- Landscape on a phone ---------------------------------------------------
   A short viewport is nearly all header and tab bar otherwise, leaving a strip
   of content between them.
   -------------------------------------------------------------------------- */
@media (max-height: 480px) and (orientation: landscape) {
  .app[data-athlete="live"] .aa-top { padding-top: calc(var(--sp-1) + env(safe-area-inset-top)); padding-bottom: var(--sp-1); }
  .app[data-athlete="live"] .aa-sub { display: none; }
  .app[data-athlete="live"] .aa-tab { min-height: 44px; }
  .app[data-athlete="live"] .aa-tab span { display: none; }   /* icons only */
}

/* ---- Touch targets ----------------------------------------------------------
   KEYED ON THE POINTER, NOT ON THE WIDTH. These rules lived in
   `max-width: 560px` and a landscape phone is 852px wide — still a phone, still
   a thumb — so every one of them switched off exactly when the screen got
   tighter. `(pointer: coarse)` asks the question that is actually being asked.

   The width clause is kept beside it for desktop browsers narrowed to a phone
   shape, which report a fine pointer and are how most of this gets looked at.
   -------------------------------------------------------------------------- */
@media (pointer: coarse), (max-width: 560px) {
  /* Measured, not assumed: these came back at 40px, 30px and 23px. */
  .app[data-athlete="live"] .aa-btn { min-height: var(--tap); }
  .app[data-athlete="live"] .aa-react { width: var(--tap); height: var(--tap); }
  .app[data-athlete="live"] .aa-signout,
  .app[data-athlete="live"] .aa-exit { min-height: var(--tap); }

  /* An inline link cannot simply be made 44px tall — it sits in a row beside a
     heading and would push the row open. So the hit area is grown past the text
     with a pseudo-element, which costs no layout at all. */
  .app[data-athlete="live"] .linklike { position: relative; }
  .app[data-athlete="live"] .linklike::after {
    content: ''; position: absolute; left: 0; right: 0;
    top: 50%; transform: translateY(-50%); height: var(--tap);
  }
}

/* ---- iOS zoom on focus ------------------------------------------------------
   Safari zooms the page when a focused input's text is under 16px, and does not
   zoom back out. Every athlete-facing field is therefore at least 16px — this
   is a functional rule wearing a typographic hat.
   -------------------------------------------------------------------------- */
.athlete-shell input,
.athlete-shell select,
.athlete-shell textarea { font-size: max(16px, var(--fs-sm)); }


/* ---- The athlete's notifications --------------------------------------------
   The bell lives in the athlete's own header because the staff top bar that
   used to carry one is hidden on their shell.
   -------------------------------------------------------------------------- */

.aa-bell {
  position: relative; flex: none;
  display: inline-flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: var(--radius-sm);
  border: 1px solid var(--line); background: var(--surface-2);
  color: var(--muted); cursor: pointer;
}
.aa-bell:hover { color: var(--ink); border-color: var(--accent-edge); }

/* Sits half outside the button. A count inside a 40px control either shrinks
   the icon or clips at two digits. */
.aa-bell-count {
  position: absolute; top: -5px; right: -5px; min-width: 18px; height: 18px;
  padding: 0 4px; border-radius: var(--radius-pill);
  background: var(--bad); color: #fff;
  font-size: 11px; /* audit-ok: a count badge, below the smallest text token */
  font-weight: 800; line-height: 18px; text-align: center;
}

.aa-notes { display: flex; flex-direction: column; gap: var(--sp-2); }

.aa-note {
  position: relative; display: flex; align-items: flex-start; gap: var(--sp-3);
  padding: var(--sp-3); padding-left: calc(var(--sp-3) + 3px);
  background: var(--surface); border: 1px solid var(--line);
  border-radius: var(--radius);
}
/* The stripe carries the tone, so severity survives being read in a hurry. */
.aa-note::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  border-radius: var(--radius) 0 0 var(--radius); background: var(--line);
}
.aa-note.tone-bad::before  { background: var(--bad); }
.aa-note.tone-warn::before { background: var(--warn); }
.aa-note.tone-info::before { background: var(--info); }
.aa-note.tone-ok::before   { background: var(--ok); }

.aa-note-icon { flex: none; color: var(--muted); margin-top: 1px; }
.aa-note.tone-bad .aa-note-icon  { color: var(--bad); }
.aa-note.tone-warn .aa-note-icon { color: var(--warn); }

.aa-note-body { min-width: 0; flex: 1; }
.aa-note-title { font-size: var(--fs-sm); font-weight: 650; color: var(--ink); }
.aa-note-sub { font-size: var(--fs-xs); color: var(--body); margin-top: 1px; }
.aa-note-when { font-size: var(--fs-2xs); color: var(--muted); margin-top: var(--sp-1); }

/* Folded by default. Opening it is a deliberate act by whoever is holding the
   phone — see the header comment in athleteApp.js. */
.aa-note-reveal { display: inline-block; margin-top: var(--sp-1); font-size: var(--fs-2xs); }
.aa-note-detail {
  margin-top: var(--sp-2); padding: var(--sp-2);
  background: var(--surface-2); border-radius: var(--radius-sm);
  font-size: var(--fs-xs);
}
.aa-note-detail[hidden] { display: none; }

.aa-note-x {
  flex: none; width: 28px; height: 28px; border-radius: var(--radius-sm);
  display: inline-flex; align-items: center; justify-content: center;
  background: transparent; border: 0; color: var(--muted); cursor: pointer;
}
.aa-note-x:hover { color: var(--ink); background: var(--surface-2); }

@media (pointer: coarse), (max-width: 560px) {
  .app[data-athlete="live"] .aa-bell,
  .app[data-athlete="live"] .aa-note-x { min-width: var(--tap); min-height: var(--tap); }
}

/* ---- First-run checklist ------------------------------------------------
   The checklist sits in one half of a two-column dashboard row, so on a
   1000px-ish window each step gets about 250px — and a row that keeps its
   button on the same line as the text squeezes the text to four words wide:

     Add your
     programmes
     Almost everything
     else hangs off a
     programme — rosters,

   Below 30rem of CONTAINER width, not viewport width, the button drops under
   the text and takes the full row. A container query is the right tool because
   the constraint here is the column, not the screen: the same panel is
   comfortable at this width in a single-column layout. */
/* The mark is 22px wide with an sp-2 gap; everything that lines up under the
   label shares this one measurement rather than repeating the arithmetic. */
.firstrun-steps { container-type: inline-size; --firstrun-indent: calc(22px + var(--sp-2)); }

@container (max-width: 30rem) {
  /* The button drops below the text and lines up under the label, past the
     mark. The mark itself lives inside the label line — see firstrun.js — so
     nothing here has to keep it company. */
  .firstrun-steps .set-row { flex-wrap: wrap; }
  .firstrun-steps .set-row > .btn,
  .firstrun-steps .set-row > .chip { margin-left: var(--firstrun-indent); margin-top: var(--sp-1); }
}

/* The label line: mark, step name, and the count when there is one. The count
   wraps under the label in a narrow column rather than being squeezed into two
   words at the right-hand edge. */
.firstrun-title { display: flex; align-items: baseline; gap: var(--sp-2); flex-wrap: wrap; }
.firstrun-title > b { flex: 0 1 auto; }

/* The tick or chevron. Kept out of the flow of the row so a two-line "why"
   cannot push it to the middle of nowhere. */
.firstrun-mark {
  flex: none;
  display: inline-grid;
  place-items: center;
  width: 22px; height: 22px;
  border-radius: 50%;
  color: var(--muted);
  background: var(--surface-2, var(--bg-soft, transparent));
}
.firstrun-steps .muted { margin-left: var(--firstrun-indent); }

/* "The GM or a head coach does this" — a sentence, so it wraps like one, and
   sits under the step it belongs to rather than beside it. */
.firstrun-who {
  flex: 1 1 100%;
  margin-left: var(--firstrun-indent);
  font-style: italic;
}
.set-row:has(.chip.ok) .firstrun-mark { color: var(--ok); }
