/* ============================================================
   UBC Player Lab — app styles
   Committed light theme: this is a coaching tool whose end
   product is a printed page; screen and paper share one look.
   ============================================================ */

:root {
  /* ---------- brand ---------- */
  --navy: #002145;        /* UBC primary */
  --navy-2: #0B3665;
  --navy-3: #123A66;      /* raised navy, for panels sitting on navy */
  --gold: #F2A900;        /* UBC gold */
  --gold-ink: #8A5F00;
  --gold-line: #E4C36A;

  /* ---------- surface ramp ----------
     Four steps, all cool-biased toward the navy. The page ground sits a shade
     under white so a card separates by CONTRAST, not by a drop shadow — that
     is what keeps the cards feeling set into the page rather than floating. */
  --ice: #E9EEF4;         /* application ground */
  --card: #FFFFFF;        /* primary surface */
  --surface-2: #F5F8FC;   /* tinted panel sitting ON a card */
  --surface-3: #EFF3F9;   /* control ground */
  --selected: #E2EAF4;    /* quiet selected state */

  /* ---------- ink ---------- */
  --ink: #14202F;
  --ink-2: #2C3E55;       /* secondary value text */
  --sub: #5F6E85;
  --sub-2: #7C8A9E;       /* tertiary: captions, axis labels */

  /* ---------- lines ----------
     One hairline for structure, one softer for internal rhythm, one stronger
     for the few places that genuinely need to hold a column together. */
  --line: #DEE5EE;
  --line-soft: #EAEFF5;
  --line-strong: #C9D4E1;

  --green: #1E8A5A;
  --green-bg: #E7F4EE;
  --red: #C24444;
  --red-bg: #FAECEC;
  --amber: #B07A12;
  --amber-bg: #FBF3DF;
  --pink: #D655A2;

  --display: "Avenir Next Condensed", "Arial Narrow", "Helvetica Neue", sans-serif;
  --body: -apple-system, "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", Menlo, monospace;

  /* ---------- spacing ----------
     One 4px-based scale. Every gap, pad and margin in this file comes from it,
     so vertical rhythm is a decision made once instead of 200 times. */
  --s-1: 0.25rem;   /* 4  */
  --s-2: 0.5rem;    /* 8  */
  --s-3: 0.75rem;   /* 12 */
  --s-4: 1rem;      /* 16 */
  --s-5: 1.25rem;   /* 20 */
  --s-6: 1.75rem;   /* 28 */
  --s-7: 2.25rem;   /* 36 */
  --s-8: 3rem;      /* 48 */

  /* ---------- radii ----------
     Three steps only. Controls are tighter than cards; nothing is a pill
     except things that are genuinely pill-shaped. */
  --r-ctl: 8px;
  --r-tile: 8px;
  --r-card: 10px;
  --r-pill: 999px;

  /* ---------- elevation ----------
     Deliberately almost invisible. Separation is the surface ramp's job; this
     only stops a white card from looking pasted flat onto a white-ish ground. */
  --elev-1: 0 1px 1px rgba(0, 33, 69, .04), 0 2px 6px -2px rgba(0, 33, 69, .05);
  --elev-2: 0 1px 2px rgba(0, 33, 69, .05), 0 14px 28px -16px rgba(0, 33, 69, .22);

  /* ---------- control metrics ---------- */
  --ctl-h: 2rem;          /* 32px — compact controls */
  --ctl-h-lg: 2.25rem;    /* 36px — primary buttons */

  /* Motion. One curve and two durations for the whole app, so a press feels the
     same everywhere and a chart redraw settles the same way every time.
     --ease-settle overshoots nothing: it arrives fast and stops clean. */
  --ease-settle: cubic-bezier(.32, .72, 0, 1);
  --ease-out: cubic-bezier(.23, 1, .32, 1);
  --t-press: 90ms;
  --t-settle: 260ms;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* Root stays at the browser's own size, so a coach who has raised their default
   text size gets a larger app. Body steps down to the app's reading size, and
   everything below is expressed relative to one of the two. */
html { font-size: 100%; -webkit-text-size-adjust: 100%; }

body {
  background: var(--ice);
  color: var(--ink);
  font-family: var(--body);
  font-size: 0.9375rem;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--navy-2); }
button { font: inherit; cursor: pointer; }

:focus-visible { outline: 2px solid var(--gold); outline-offset: 2px; }

/* ---------------- press feedback ----------------
   Every control acknowledges the pointer going DOWN, not the click coming back
   up. On a dugout iPad there is no hover at all, so without this a tap has no
   feedback until the re-render lands. The scale is deliberately small — this is
   an acknowledgement, not an animation. */

.btn, .seg button, .am-filter button, .zone-toggle, .lock-btn,
.topnav a, .brand, .pcard {
  -webkit-tap-highlight-color: transparent;  /* we draw our own, no grey flash */
  touch-action: manipulation;                /* drops the legacy double-tap delay */
}

/* The selects keep the platform tap highlight — opening the native picker is
   their feedback, and we are not drawing a substitute for it. */
.lvl-select { touch-action: manipulation; }

.btn, .seg button, .am-filter button, .zone-toggle, .lock-btn, .brand {
  transition: transform var(--t-press) var(--ease-out),
              background-color var(--t-press) var(--ease-out),
              border-color var(--t-press) var(--ease-out);
}

/* Transform only. The active pill is gold and the resting link is transparent,
   so transitioning background-color here fades the pill through a muddy olive
   on every page change. The page state snaps; only the press moves. */
.topnav a { transition: transform var(--t-press) var(--ease-out); }

/* applyRoleChrome() hides the links a player has no business following by
   setting `hidden`. The browser's own `[hidden] { display: none }` is a weaker
   selector than `.topnav a { display: flex }`, so it lost — and a player was
   shown the coach-only Manage tab. The route and the API both refuse them, but
   an offer that gets refused is still an offer. */
.topnav a[hidden] { display: none; }

.btn:active, .seg button:active, .am-filter button:active,
.zone-toggle:active, .lock-btn:active { transform: scale(.97); }
.topnav a:active { transform: scale(.96); }
.brand:active { transform: scale(.99); }

/* A card is a surface, so pressing it puts it back down on the page rather than
   shrinking it — the inverse of the hover lift. */
.pcard:active { transform: translateY(0); box-shadow: var(--elev-1); }

/* ---------------- top bar ---------------- */

/* A floating layer rather than an opaque strip: content passes under it and
   stays faintly readable through the navy, which is what makes the header read
   as a pane of the app instead of a band pasted on top. The solid colour below
   is the fallback; the translucent version only applies where it is supported. */
.topbar {
  background: var(--navy);
  color: #fff;
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: var(--s-2) var(--s-5);
  min-height: 3.5rem;
  position: sticky;
  top: 0;
  z-index: 10;
  flex-wrap: wrap;
  box-shadow: inset 0 -1px 0 rgba(255, 255, 255, .07);
}

.demo-banner[hidden] { display: none; }
.demo-banner {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--s-2);
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-ctl);
  background: var(--gold);
  color: var(--navy);
  font-size: 0.6875rem;
  line-height: 1.35;
  letter-spacing: .01em;
  text-align: center;
}
.demo-banner strong {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: .08em;
  white-space: nowrap;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
  .topbar {
    background: rgba(0, 33, 69, .86);
    -webkit-backdrop-filter: blur(20px) saturate(160%);
            backdrop-filter: blur(20px) saturate(160%);
  }
}

.brand-title { white-space: nowrap; }
.brand-sub { white-space: nowrap; }

.brand { display: flex; align-items: center; gap: var(--s-3); text-decoration: none; color: #fff; }
.brand-logo {
  width: 2.375rem;
  height: 2.375rem;
  border-radius: 6px;
  display: block;
  flex: none;
}
.brand-text { display: flex; flex-direction: column; line-height: 1.1; }
.brand-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.3125rem;
  text-transform: uppercase;
  letter-spacing: .045em;
}
.brand-sub {
  font-size: 0.625rem;
  color: #A9BCD4;
  letter-spacing: .1em;
  text-transform: uppercase;
  margin-top: 1px;
}

.topnav { display: flex; gap: var(--s-1); margin-left: var(--s-2); }
.topnav a {
  color: #C9D6E6;
  text-decoration: none;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: .005em;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-ctl);
  line-height: 1;
  display: flex;
  align-items: center;
}
.topnav a:hover { color: #fff; background: rgba(255, 255, 255, .1); }
.topnav a.active { color: var(--navy); background: var(--gold); }
.topnav a.active:hover { color: var(--navy); background: var(--gold); }

/* ---- Development Tools menu ----
   A <details> styled to sit in the nav like the links beside it. The summary
   is the button; the panel is absolutely positioned so opening the menu never
   reflows the bar underneath it. */
/* TWO things clip the menu panel out of existence, and only one of them is
   obvious. The nav scrolls horizontally on a phone, so `overflow: auto` cuts
   off an absolutely positioned child — that one is easy to guess. The other is
   the fade `mask-image` in the narrow-screen rule below: a mask clips every
   descendant to its own box, and unlike overflow it cannot be escaped with
   z-index and does not show up in getBoundingClientRect. The panel was being
   laid out at the correct size, in front of everything, and simply never
   painted.

   Both are released only while the menu is open, so the scroll affordance and
   its fade survive for everyone who is not looking at the menu. */
.topnav:has(.navmenu[open]) {
  overflow: visible;
  -webkit-mask-image: none;
          mask-image: none;
}
.navmenu { position: relative; }
.navmenu > summary {
  color: #C9D6E6;
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: .005em;
  padding: var(--s-2) var(--s-3);
  border-radius: var(--r-ctl);
  line-height: 1;
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
  /* The default disclosure triangle is replaced by the caret below. */
  list-style: none;
}
.navmenu > summary::-webkit-details-marker { display: none; }
.navmenu > summary::after {
  content: "";
  width: 5px; height: 5px;
  border-right: 1.5px solid currentColor;
  border-bottom: 1.5px solid currentColor;
  transform: translateY(-1px) rotate(45deg);
  transition: transform var(--t-press, 120ms) var(--ease-out, ease);
}
.navmenu[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.navmenu > summary:hover { color: #fff; background: rgba(255, 255, 255, .1); }
.navmenu[open] > summary { color: #fff; background: rgba(255, 255, 255, .12); }
/* Gold when the page you are on lives inside this menu, matching a live link. */
.navmenu.active > summary { color: var(--navy); background: var(--gold); }

.navmenu-panel {
  position: absolute;
  top: calc(100% + 6px);
  left: 0;
  min-width: 232px;
  background: #fff;
  border-radius: var(--r-card, 12px);
  box-shadow: 0 1px 3px rgba(16, 24, 40, .12), 0 12px 32px rgba(16, 24, 40, .18);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  z-index: 60;
}
.navmenu-panel a {
  color: var(--ink, #101828);
  padding: 9px 11px;
  border-radius: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}
.navmenu-panel a:hover { color: var(--navy); background: rgba(13, 45, 94, .08); }
.navmenu-panel a.active { color: var(--navy); background: var(--gold); }

/* A player sees their own profile and nothing that implies a roster. */
body.role-player .navmenu { display: none; }

@media (max-width: 760px) {
  .navmenu-panel { left: auto; right: 0; }
}

.topbar-right { margin-left: auto; display: flex; align-items: center; gap: var(--s-2); }
.lvl-label {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: #A9BCD4;
  font-weight: 600;
}
.lvl-select {
  background: rgba(255, 255, 255, .08);
  color: #fff;
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--r-ctl);
  padding: var(--s-1) var(--s-2);
  font-family: var(--body);
  font-size: 0.8125rem;
  min-height: var(--ctl-h);
}
.lvl-select:hover { border-color: rgba(255, 255, 255, .32); }

/* ---------------- layout ---------------- */

main { max-width: 71.25rem; margin: 0 auto; padding: var(--s-6) var(--s-5) var(--s-8); }

.foot {
  max-width: 71.25rem;
  margin: 0 auto;
  padding: var(--s-4) var(--s-5) var(--s-7);
  color: var(--sub-2);
  font-size: 0.6875rem;
  font-family: var(--mono);
  letter-spacing: .01em;
}

/* The rule after the title is a hairline, not a divider — it should register as
   alignment, not as a border. */
.section-title {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.3125rem;
  text-transform: uppercase;
  letter-spacing: .045em;
  line-height: 1.15;
  color: var(--navy);
  display: flex;
  align-items: center;
  gap: var(--s-3);
  margin: var(--s-7) 0 var(--s-3);
}
.section-title::after { content: ""; flex: 1; height: 1px; background: var(--line); }
.section-title:first-child { margin-top: 0; }

.card {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--s-4) var(--s-5);
  box-shadow: var(--elev-1);
}

/* ---------------- chips, scores ---------------- */

.chip {
  display: inline-flex;
  align-items: center;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: .05em;
  padding: var(--s-1) var(--s-2);
  border-radius: var(--r-pill);
  border: 1px solid var(--line);
  background: var(--surface-3);
  color: var(--ink-2);
  text-transform: uppercase;
  line-height: 1.4;
}
.chip.gold { background: var(--amber-bg); border-color: var(--gold-line); color: var(--gold-ink); }
.chip.navy { background: var(--selected); border-color: #B9CCE3; color: var(--navy-2); }

/* Metric values are read in columns, so they are tabular and tightened — a
   proportional 1 next to a proportional 8 breaks the column. */
.score {
  display: inline-block;
  min-width: 2.5rem;
  text-align: center;
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 0.8125rem;
  letter-spacing: -.01em;
  padding: 2px var(--s-2);
  border-radius: 6px;
}
.score.s-elite { background: var(--green-bg); color: var(--green); }
.score.s-good  { background: var(--selected); color: var(--navy-2); }
.score.s-avg   { background: var(--amber-bg); color: var(--amber); }
.score.s-low   { background: var(--red-bg); color: var(--red); }

.delta-up { color: var(--green); font-weight: 700; font-size: 0.75rem; font-variant-numeric: tabular-nums; }

/* ---------------- roster ---------------- */

/* Deliberately quiet. This is available, not important: the roster below it is
   the page. The dashed 2px box it replaced pulled the eye first on every load. */
.upload-zone {
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  background: var(--surface-2);
  padding: var(--s-3) var(--s-4);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-3);
  flex-wrap: wrap;
  color: var(--sub);
  font-size: 0.8125rem;
  margin-bottom: var(--s-5);
}
/* The one moment it should shout: a file is over the target. */
.upload-zone.dragover {
  border-color: var(--gold);
  background: var(--amber-bg);
  box-shadow: inset 0 0 0 1px var(--gold);
}
.upload-zone strong { color: var(--ink); font-weight: 600; }
.upload-actions { margin-top: 0; display: flex; gap: var(--s-2); justify-content: flex-end; flex-wrap: wrap; }
/* The ingest result takes the full width under the row, and takes no space at
   all until there is something to say. */
#upload-msg { flex: 1 0 100%; }
#upload-msg:empty { display: none; }

/* Roster-key strip: the state of the file that re-identifies this roster.
   Sits under the upload row on the same surface, not as a second card. */
.key-bar {
  flex: 1 0 100%;
  display: flex;
  align-items: center;
  gap: var(--s-2) var(--s-3);
  flex-wrap: wrap;
  border-top: 1px solid var(--line);
  padding-top: var(--s-3);
  margin-top: var(--s-2);
}
.key-state {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--sub-2); white-space: nowrap;
}
/* Linked is a good state, so it reads as one — but quietly, in the UBC green
   already used for a positive delta, not as a badge competing with the roster. */
.key-state.on { color: var(--green); }
.key-state.on::before {
  content: "●"; font-size: 8px; vertical-align: middle;
  margin-right: var(--s-1); position: relative; top: -1px;
}
.key-detail { font-size: 0.75rem; color: var(--sub); flex: 1 1 18rem; }
.key-detail b { color: var(--ink-2); font-family: var(--mono); font-size: 0.6875rem; }
.key-bar .btn { min-height: var(--ctl-h); font-size: 0.75rem; padding: 0 var(--s-3); }

/* Why the roster reads P-001 and where the names went. Quiet, but always
   present once a roster exists without a key to unlock it. */
.anon-note {
  flex: 1 0 100%;
  font-size: 0.75rem;
  line-height: 1.55;
  color: var(--sub);
  border-top: 1px solid var(--line);
  padding-top: var(--s-2);
  margin-top: var(--s-1);
  max-width: 80ch;
}

.btn {
  background: var(--navy);
  color: #fff;
  border: 1px solid var(--navy);
  border-radius: var(--r-ctl);
  padding: 0 var(--s-4);
  min-height: var(--ctl-h-lg);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: .005em;
  text-decoration: none;
  white-space: nowrap;
}
.btn:hover { background: var(--navy-2); border-color: var(--navy-2); }
.btn.ghost { background: var(--card); color: var(--navy); border-color: var(--line-strong); }
.btn.ghost:hover { background: var(--surface-3); border-color: var(--navy-2); }
.btn.gold { background: var(--gold); color: var(--navy); border-color: var(--gold); }
.btn.gold:hover { background: #FFB800; border-color: #FFB800; }

.roster-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16.5rem, 1fr));
  gap: var(--s-3);
}

/* The data is the hero: the name is quiet, the metadata quieter still, and the
   Stuff+ column is the only thing with real weight. */
.pcard {
  display: block;
  text-decoration: none;
  color: var(--ink);
  background: var(--card);
  border: 1px solid var(--line);
  border-top: 3px solid var(--navy);
  border-radius: var(--r-card);
  padding: var(--s-4);
  box-shadow: var(--elev-1);
  transition: box-shadow 150ms var(--ease-out), transform 150ms var(--ease-out),
              border-color 150ms var(--ease-out);
}
.pcard:hover { box-shadow: var(--elev-2); transform: translateY(-1px); border-color: var(--line-strong); }
.pcard-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 1.1875rem;
  line-height: 1.1;
  text-transform: uppercase;
  letter-spacing: .015em;
  color: var(--navy);
}
.pcard-meta {
  color: var(--sub-2);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: .07em;
  font-weight: 600;
  margin: var(--s-1) 0 var(--s-3);
}
.pcard-rows { display: flex; flex-direction: column; gap: var(--s-2); }
.pcard-row { display: flex; align-items: center; gap: var(--s-2); font-size: 0.75rem; }
.pcard-row .dot { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.pcard-row .pt { width: 4.5rem; flex: none; color: var(--sub); letter-spacing: .005em; }
.pcard-row .bar-track {
  flex: 1;
  height: 5px;
  background: var(--line-soft);
  border-radius: var(--r-pill);
  overflow: hidden;
  box-shadow: inset 0 0 0 1px rgba(0, 33, 69, .03);
}
/* display:block is load-bearing: the fill is a <span>, and height has no effect
   on an inline box — the track has been rendering empty. */
.pcard-row .bar { display: block; height: 100%; border-radius: var(--r-pill); }
.pcard-row .sc {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--ink);
  width: 2.125rem;
  text-align: right;
  letter-spacing: -.01em;
}

.empty-note { color: var(--sub); padding: var(--s-7) 0; text-align: center; }

/* ---------------- report ---------------- */

.report-head { display: flex; align-items: flex-start; gap: var(--s-4); flex-wrap: wrap; margin-bottom: var(--s-2); }
.report-logo { width: 3.5rem; height: 3.5rem; border-radius: var(--r-tile); flex: none; }
/* A photograph is not a crest: it needs cropping to the square the layout
   reserves, and a rim so a dark headshot does not bleed into the page. Round,
   to match the athlete card and to read as a person rather than a logo. */
.report-photo {
  object-fit: cover;
  border-radius: var(--r-pill);
  border: 2px solid var(--line);
  background: var(--wash);
}
.report-name {
  font-family: var(--display);
  font-weight: 700;
  font-size: 2.625rem;
  line-height: 1;
  text-transform: uppercase;
  color: var(--navy);
  letter-spacing: .005em;
  /* This is an <h1>, so the browser's own heading margin has to be turned off
     — every size here is set deliberately and none of them is an em of it. */
  margin: 0;
}
.report-chips { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; padding-top: var(--s-2); }
/* The note is the last child of a flex row, so it needs to claim a whole line
   rather than compete for space with the name and the buttons. It happens to
   wrap onto its own line at today's widths because the row above is already
   full — that is luck, not layout, and it would break the moment a shorter
   name or one fewer chip left room beside the actions. */
.report-head-note { flex: 1 0 100%; max-width: 68ch; }
.report-actions { margin-left: auto; display: flex; gap: var(--s-2); align-items: center; padding-top: var(--s-1); }

.level-strip {
  display: flex;
  gap: var(--s-5);
  align-items: center;
  flex-wrap: wrap;
  background: var(--navy);
  color: #C7D6E8;
  border-radius: var(--r-card);
  padding: var(--s-3) var(--s-4);
  margin: var(--s-4) 0 var(--s-2);
  font-size: 0.8125rem;
}
.level-strip b { color: #fff; font-weight: 600; }
.level-strip .gold-num { color: var(--gold); font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 700; }
.level-strip .focus { flex: 1 1 20rem; min-width: 16rem; }

.tbl-wrap { overflow-x: auto; }
table.arsenal { border-collapse: collapse; width: 100%; font-size: 0.8125rem; min-width: 43.75rem; }
.arsenal th {
  text-align: left;
  font-size: 0.625rem;
  text-transform: uppercase;
  letter-spacing: .09em;
  font-weight: 700;
  color: var(--sub-2);
  border-bottom: 1px solid var(--line-strong);
  padding: var(--s-2) var(--s-2);
  white-space: nowrap;
}
.arsenal td { padding: var(--s-2) var(--s-2); border-bottom: 1px solid var(--line-soft); white-space: nowrap; }
.arsenal tbody tr:last-child td { border-bottom: 0; }
.arsenal td.num { font-family: var(--mono); font-variant-numeric: tabular-nums; letter-spacing: -.01em; color: var(--ink-2); }
.arsenal .ptype { font-weight: 600; color: var(--ink); }
.arsenal .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: var(--s-2); }

.charts-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }
.chart-card h3 {
  font-family: var(--display);
  font-size: 1rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .055em;
  line-height: 1.2;
  color: var(--navy);
  margin-bottom: 0;
}
.chart-card .hint { color: var(--sub); font-size: 0.75rem; margin-bottom: var(--s-2); }
.chart-card svg { width: 100%; height: auto; display: block; }

.dev-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: var(--s-3); }
.dev-card { border-left: 3px solid var(--line); }
.dev-card .dev-head { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-2); }
.dev-card .dev-pitch {
  font-family: var(--display); font-weight: 700; font-size: 1.125rem;
  text-transform: uppercase; letter-spacing: .03em; color: var(--navy);
}
.dev-card .sens {
  font-size: 0.6875rem; color: var(--sub); font-family: var(--mono);
  font-variant-numeric: tabular-nums; margin: var(--s-1) 0 var(--s-2);
}
.lever { padding: var(--s-2) 0; border-top: 1px solid var(--line-soft); }
.lever-title { font-weight: 600; font-size: 0.8125rem; }
.lever-title .gain { color: var(--green); font-variant-numeric: tabular-nums; }
.lever-cue { color: var(--sub); font-size: 0.75rem; line-height: 1.5; }

.bp-head { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-1); }
.bp-name {
  font-family: var(--display); font-weight: 700; font-size: 1.5rem;
  text-transform: uppercase; letter-spacing: .02em; line-height: 1.1; color: var(--navy);
}
.bp-phil { color: var(--sub); font-size: 0.8125rem; line-height: 1.55; margin-bottom: var(--s-3); max-width: 70ch; }
.bp-item { display: flex; gap: var(--s-3); padding: var(--s-2) 0; border-top: 1px solid var(--line-soft); font-size: 0.8125rem; }
.bp-check { font-weight: 700; width: 1.125rem; flex: none; text-align: center; }
.bp-check.yes { color: var(--green); }
.bp-check.no { color: var(--amber); }
.bp-target { color: var(--sub); }

.notes-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(17.5rem, 1fr)); gap: var(--s-3); }
.note-card {
  border-radius: var(--r-card);
  border: 1px solid var(--line);
  background: var(--card);
  padding: var(--s-3) var(--s-4);
  box-shadow: var(--elev-1);
}
.note-kind { font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .11em; margin-bottom: var(--s-1); }
.note-kind.strength { color: var(--green); }
.note-kind.fix { color: var(--amber); }
.note-kind.note { color: var(--sub-2); }
.note-title { font-weight: 700; font-size: 0.8125rem; margin-bottom: 2px; letter-spacing: -.005em; }
.note-body { font-size: 0.75rem; line-height: 1.55; color: var(--ink-2); }

.bias-why { margin-top: var(--s-2); }
.bias-why li { list-style: none; font-size: 0.8125rem; line-height: 1.5; padding: var(--s-1) 0 var(--s-1) var(--s-4); position: relative; }
.bias-why li::before { content: "\25CF"; color: var(--gold); position: absolute; left: 0; font-size: 8px; top: 9px; }
.bias-caveat { font-size: 0.6875rem; line-height: 1.55; color: var(--sub-2); margin-top: var(--s-2); }

/* ---------------- arm slot & release panel ---------------- */

.slot-panel {
  background: var(--navy);
  border-radius: var(--r-card);
  padding: var(--s-4);
  display: grid;
  grid-template-columns: 11.875rem 16.25rem 1fr;
  gap: var(--s-4);
  align-items: start;
}
/* WHEN A GRAPHIC IS OMITTED, ITS COLUMN GOES WITH IT.
   The template above names three columns by position, so a Showcase panel that
   drops the empty scatter used to leave the tiles in the scatter's 16.25rem
   slot with the 1fr remainder blank: a smaller empty box replaced by a larger
   one. The renderer says which parts it drew and the template follows. */
.slot-panel.slot-no-scatter { grid-template-columns: 11.875rem 1fr; }
.slot-panel.slot-no-figure { grid-template-columns: 16.25rem 1fr; }
.slot-panel.slot-tiles-only { grid-template-columns: 1fr; }
/* Cap both graphics: stretched to a full grid column the release scatter
   balloons, which is exactly the "tight cluster" signal we're trying to show. */
.slot-panel .slot-figure svg { width: 100%; max-width: 11.25rem; height: auto; display: block; }
.slot-panel .slot-release svg { width: 100%; max-width: 15.625rem; height: auto; display: block; }
.slot-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.25rem, 1fr));
  gap: var(--s-2);
}
.slot-tile {
  background: var(--navy-3);
  border: 1px solid rgba(255, 255, 255, .09);
  border-radius: var(--r-tile);
  padding: var(--s-2) var(--s-3);
}
.slot-tile .st-l { font-size: 0.625rem; color: #93AAC7; letter-spacing: .07em; text-transform: uppercase; font-weight: 600; }
.slot-tile .st-v {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 0.9375rem; font-weight: 700; color: #fff; margin-top: 2px; letter-spacing: -.01em;
}
.slot-tile .st-pm { font-size: 0.6875rem; color: #93AAC7; font-weight: 400; }
.slot-note { grid-column: 1 / -1; font-size: 0.6875rem; line-height: 1.55; color: #93AAC7; }

/* ---------------- admin: age status ---------------- */
.adm-age { display: grid; gap: 4px; }
.adm-age-status { display: flex; align-items: center; gap: 6px; }
.adm-age-ok { font-size: 0.6875rem; color: var(--green); }
.adm-age-pending { font-size: 0.6875rem; color: var(--amber); font-weight: 600; }
.adm-age-check { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.adm-age-check .lvl-label { font-size: 0.625rem; }
.adm-age-dob { width: 9.5rem; }
.adm-age-msg { font-size: 0.6875rem; color: var(--sub); }
.adm-age-msg.bad { color: var(--red); }
.adm-age-cell { min-width: 15rem; }

/* ---------------- MLB pitching comp ----------------
   Sits between the release panel and the movement chart, and is deliberately
   one card rather than a dashboard: a name, the release numbers, the primary
   pitch side by side, and a two-dot picture. Observational, so Showcase and
   varsity render exactly the same markup. */
.mc-card { padding: var(--s-3); }
.mc-main {
  display: grid;
  grid-template-columns: minmax(11rem, 1fr) minmax(14rem, 1.35fr) auto;
  gap: var(--s-3);
  align-items: start;
}
.mc-name { font-size: 1.25rem; font-weight: 700; letter-spacing: -.015em; }
.mc-basis { font-size: 0.75rem; color: var(--sub); margin-top: 2px; }
.mc-hand { font-size: 0.8125rem; font-weight: 500; color: var(--sub); margin-left: 6px; letter-spacing: 0; }
.mc-h {
  font-size: 0.625rem; letter-spacing: .07em; text-transform: uppercase;
  color: var(--sub); font-weight: 700; margin: 0 0 var(--s-1);
}
.mc-rel { margin-top: var(--s-3); }
.mc-rel-row {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 0.8125rem; padding: 3px 0;
}
.mc-rel-row b { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.mc-delta { border-top: 1px solid var(--line); margin-top: 3px; padding-top: 5px; color: var(--sub); }
.mc-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }
.mc-table th, .mc-table td { padding: 4px 6px; border-bottom: 1px solid var(--line); text-align: left; }
.mc-table thead th { font-size: 0.6875rem; color: var(--sub); font-weight: 600; }
.mc-table .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.mc-table tbody th { font-weight: 500; color: var(--sub); }
.mc-dist { font-size: 0.8125rem; margin-top: var(--s-2); }
.mc-dist b { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.mc-sec { margin-top: var(--s-3); }
.mc-sec-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: var(--s-2);
  align-items: baseline; font-size: 0.8125rem; padding: 3px 0;
}
.mc-sec-l { color: var(--sub); }
.mc-sec-d { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--sub); font-size: 0.75rem; }
.mc-viz { justify-self: center; }
.mc-plot { display: block; }
.mc-key { display: flex; gap: var(--s-2); justify-content: center; font-size: 0.6875rem; color: var(--sub); }
.mc-key span::before {
  content: ""; display: inline-block; width: 8px; height: 8px;
  border-radius: 50%; margin-right: 4px; vertical-align: middle;
}
.mc-key-p::before { background: #0B2B5B; }
.mc-key-m::before { border: 2px solid #B7892B; }
.mc-foot { font-size: 0.6875rem; line-height: 1.55; color: var(--sub); margin: var(--s-3) 0 0; }
.mc-empty-msg { font-size: 0.875rem; margin: 0; }
.mc-empty .mc-foot { margin-top: var(--s-2); }
.mc-src { margin-top: 2px; }

@media (max-width: 60rem) {
  .mc-main { grid-template-columns: 1fr; }
  .mc-viz { justify-self: start; }
}

/* ---------------- chart headings, legend, zones ---------------- */

.chart-head { display: flex; align-items: center; justify-content: space-between; gap: var(--s-2); margin-bottom: var(--s-2); }
.hint-inline {
  font-family: var(--body); font-size: 0.6875rem; font-weight: 400;
  color: var(--sub-2); text-transform: none; letter-spacing: .01em;
}
.zone-toggle {
  border: 1px solid var(--line);
  background: var(--surface-3);
  color: var(--sub);
  border-radius: var(--r-ctl);
  padding: 0 var(--s-3);
  min-height: var(--ctl-h);
  display: inline-flex;
  align-items: center;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}
.zone-toggle:hover { border-color: var(--line-strong); color: var(--ink-2); }
.zone-toggle.on { background: var(--amber-bg); border-color: var(--gold-line); color: var(--gold-ink); }
.legend { display: flex; flex-wrap: wrap; gap: var(--s-3); margin: var(--s-2) 0 var(--s-1); }
.lg-item { display: flex; align-items: center; gap: var(--s-1); font-size: 0.6875rem; color: var(--sub); font-weight: 500; }
.lg-item .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
.title-note {
  font-family: var(--body); font-size: 0.75rem; font-weight: 400;
  color: var(--sub-2); text-transform: none; letter-spacing: 0;
}

/* Explanatory copy: the quietest text in the app, and the one place leading
   opens up, because it is read in sentences rather than scanned. */
.hint { color: var(--sub); font-size: 0.75rem; line-height: 1.6; }

/* ---------------- bias card ---------------- */

.bias-call {
  font-family: var(--display); font-weight: 700; font-size: 1.625rem;
  text-transform: none; letter-spacing: -.015em; line-height: 1.1;
  color: var(--navy); margin-bottom: var(--s-2);
}
.eff-bar { margin-bottom: var(--s-2); }
.eff-track { display: flex; height: 10px; border-radius: var(--r-pill); overflow: hidden; }
.eff-seg { display: block; }
.eff-labels { display: flex; justify-content: space-between; gap: var(--s-2); font-size: 0.6875rem; margin-top: var(--s-1); flex-wrap: wrap; }
.eff-labels .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.axis-dev { font-size: 0.8125rem; color: var(--ink-2); margin-bottom: var(--s-2); }

/* ---------------- projector ---------------- */

.proj-level { display: flex; align-items: center; gap: var(--s-2); margin: var(--s-2) 0 var(--s-3); font-size: 0.8125rem; }
.lvl-select.light {
  background: var(--card); color: var(--ink);
  border-color: var(--line-strong);
}
.lvl-select.light:hover { border-color: var(--navy-2); }

/* Grades live above the fold — watching them move IS the tool.
   Kept on the light card surface so the score colours (which are designed for
   light grounds) stay legible; on navy the darker bands sank into the panel. */
.proj-hero {
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--s-3) var(--s-4);
  margin-bottom: var(--s-4);
  box-shadow: var(--elev-1);
}
.ph-inner { display: flex; align-items: center; gap: var(--s-2) var(--s-5); flex-wrap: wrap; }

/* Cells keep their intrinsic width and the row wraps whole cells instead —
   otherwise the mono shape text gets squeezed and stacks one word per line. */
.ph-cell { display: flex; align-items: center; gap: var(--s-2); flex: 0 0 auto; }
.ph-label {
  font-size: 0.6875rem; font-weight: 700; letter-spacing: .09em;
  text-transform: uppercase; color: var(--sub-2); white-space: nowrap;
}
.ph-score {
  font-family: var(--display); font-weight: 700; font-size: 1.625rem; line-height: 1;
  letter-spacing: -.005em;
  padding: var(--s-1) var(--s-2); border-radius: var(--r-tile); white-space: nowrap;
}
.ph-shape {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 0.6875rem; color: var(--sub); white-space: nowrap;
}

/* Platoon sides. The stronger matchup is emphasised, since "who is this
   pitch actually for" is the coaching question. */
.ph-split {
  display: flex; flex-direction: column; gap: 1px;
  font-size: 0.625rem; color: var(--sub-2); white-space: nowrap;
  border-left: 1px solid var(--line); padding-left: var(--s-2);
}
.ph-split b { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 0.75rem; color: var(--ink-2); }
.ph-split b.hi { color: var(--navy); }

.ph-meta { margin-left: auto; font-size: 0.6875rem; color: var(--sub-2); white-space: nowrap; }
.ph-meta b { color: var(--ink); }

/* On a phone there is not enough width for a whole cell on one line, and the
   rule above — cells at intrinsic width, the ROW wrapping between them — made
   the page scroll sideways instead, clipping the shape text mid-sentence.
   Below this width each pitch takes its own row and the shape text moves to a
   line of its own inside it. That keeps the reason for the original rule: the
   mono text still gets a full line to itself and never stacks one word high. */
@media (max-width: 560px) {
  .ph-cell { flex: 1 1 100%; flex-wrap: wrap; }
  .ph-shape { flex: 1 0 100%; }
  .ph-meta { margin-left: 0; }
}

.ps-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }

/* Velocity + spin lock */
.lock-group {
  position: relative; border-left: 2px solid transparent;
  padding-left: var(--s-3); margin-left: calc(var(--s-3) * -1 - 2px);
  transition: border-color 150ms var(--ease-out);
}
.lock-group.locked { border-left-color: var(--gold); }
.lock-btn {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-ctl);
  padding: 0 var(--s-3); min-height: var(--ctl-h);
  display: inline-flex; align-items: center; gap: var(--s-1);
  font-size: 0.75rem; font-weight: 600; color: var(--sub);
  margin-bottom: var(--s-2); white-space: nowrap;
}
.lock-btn:hover { border-color: var(--line-strong); color: var(--ink-2); }
.lock-btn.on { background: var(--amber-bg); border-color: var(--gold-line); color: var(--gold-ink); }
.lock-note { font-size: 0.6875rem; color: var(--sub); margin: 0 0 var(--s-3); line-height: 1.55; }
.lock-note b { color: var(--ink); font-family: var(--mono); font-variant-numeric: tabular-nums; }

.proj-grid { display: grid; grid-template-columns: 20rem 1fr; gap: var(--s-4); align-items: start; }
.slider-row { margin-bottom: var(--s-3); }
.slider-row .sl-head { display: flex; justify-content: space-between; align-items: baseline; font-size: 0.8125rem; margin-bottom: var(--s-1); }
.slider-row .sl-val {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-weight: 700; color: var(--navy); letter-spacing: -.01em;
}
.slider-row input[type="range"] { width: 100%; accent-color: var(--navy); }

/* Segmented control: one continuous surface, dividers between segments rather
   than a border around each — the selected segment is the only filled thing. */
.seg {
  display: flex; border: 1px solid var(--line); border-radius: var(--r-ctl);
  overflow: hidden; margin-bottom: var(--s-3); background: var(--surface-3);
}
.seg button {
  flex: 1; padding: 0 var(--s-3); min-height: var(--ctl-h);
  background: transparent; border: none; border-left: 1px solid var(--line);
  font-weight: 600; font-size: 0.8125rem; color: var(--sub);
}
.seg button:first-child { border-left: 0; }
.seg button:hover { color: var(--ink-2); background: rgba(0, 33, 69, .04); }
.seg button.on { background: var(--navy); color: #fff; border-left-color: var(--navy); }
.seg button.on:hover { background: var(--navy-2); }

/* ---------------- glossary ---------------- */

.gloss { display: grid; grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr)); gap: var(--s-4) var(--s-5); }
.gloss dt {
  font-weight: 700; font-family: var(--display); font-size: 1rem;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
}
.gloss dd { color: var(--ink-2); font-size: 0.8125rem; line-height: 1.55; margin-top: 2px; }

/* Shown only when scripts don't run — a preview pane, or JS disabled.
   Without this the page is genuinely blank, since every report is built in JS. */
.noscript-note {
  max-width: 40rem;
  margin: var(--s-8) auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 4px solid var(--gold);
  border-radius: var(--r-card);
  padding: var(--s-5) var(--s-6);
  box-shadow: var(--elev-1);
}
.noscript-note h2 {
  font-family: var(--display);
  font-size: 1.375rem;
  text-transform: uppercase;
  letter-spacing: .045em;
  color: var(--navy);
  margin-bottom: var(--s-2);
}
.noscript-note p { margin-bottom: var(--s-2); color: var(--ink-2); }

/* ---------------- pitch-type map ---------------- */

.am-controls {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-3);
}
.am-controls .seg { flex: 0 0 auto; width: 9.375rem; margin: 0; }
.am-controls .lock-btn { margin: 0; flex: 0 0 auto; }
.am-filter { display: flex; gap: var(--s-1); flex-wrap: wrap; margin-left: auto; }

/* Hover readout — fixed minimum height so the chart never shifts under the
   cursor as names appear and disappear. */
.am-readout {
  display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap;
  min-height: 2.25rem; padding: var(--s-2) var(--s-3); margin-bottom: var(--s-3);
  background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-tile);
}
.am-ro-empty { font-size: 0.75rem; color: var(--sub-2); }
.am-ro-dot { width: 10px; height: 10px; border-radius: 50%; flex: none; align-self: center; }
.am-ro-name { font-size: 0.875rem; color: var(--ink); letter-spacing: -.005em; }
.am-ro-nums { font-family: var(--mono); font-variant-numeric: tabular-nums; font-size: 0.6875rem; color: var(--sub); }
.am-ro-note { font-size: 0.75rem; line-height: 1.55; color: var(--ink-2); flex: 1 1 100%; }
.am-filter button {
  display: inline-flex; align-items: center; gap: var(--s-1);
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-pill);
  padding: 0 var(--s-3); min-height: var(--ctl-h);
  font-size: 0.75rem; font-weight: 600; color: var(--sub); white-space: nowrap;
}
.am-filter button:hover { border-color: var(--line-strong); color: var(--ink-2); }
.am-filter button.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.am-filter .dot { width: 7px; height: 7px; border-radius: 50%; }

.am-group { margin-bottom: var(--s-5); }
.am-group-head {
  display: flex; align-items: center; gap: var(--s-2);
  font-family: var(--display); font-weight: 700; font-size: 0.9375rem;
  text-transform: uppercase; letter-spacing: .055em; color: var(--navy);
  margin-bottom: var(--s-2);
}
.am-group-head .dot { width: 9px; height: 9px; border-radius: 50%; }
.am-table { min-width: 40rem; }
.am-table td.am-note { white-space: normal; color: var(--sub); font-size: 0.75rem; line-height: 1.5; min-width: 16.25rem; }

/* ---------------- fastball profile ---------------- */

.ff-card { border-left-color: var(--red); }
.ff-borderline {
  font-size: 0.75rem; line-height: 1.55; color: var(--gold-ink); background: var(--amber-bg);
  border: 1px solid var(--gold-line); border-radius: var(--r-tile);
  padding: var(--s-2) var(--s-3); margin-bottom: var(--s-3);
}
.ch-axis.ff-above { border-color: #A8D8BF; background: var(--green-bg); }
.ch-axis.ff-above .ch-axis-v { color: var(--green); }
.ff-slot-note { font-size: 0.8125rem; line-height: 1.6; color: var(--ink-2); max-width: 72ch; margin-bottom: var(--s-2); }
.ff-examples { font-size: 0.75rem; color: var(--sub); margin-bottom: var(--s-2); }
.ff-examples b { color: var(--ink-2); }

/* ---------------- changeup profile ---------------- */

.ch-card { border-left: 3px solid var(--pink); }
.ch-head { display: flex; align-items: baseline; gap: var(--s-3); flex-wrap: wrap; margin-bottom: var(--s-3); }
.ch-name {
  font-family: var(--display); font-weight: 700; font-size: 1.5rem;
  text-transform: uppercase; letter-spacing: .02em; line-height: 1.1; color: var(--navy);
}
.ch-usage { font-size: 0.6875rem; color: var(--sub-2); margin-left: auto; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.chip.ch-verdict { text-transform: uppercase; }
.chip.ch-verdict.elite,
.chip.ch-verdict.excellent { background: var(--green-bg); color: var(--green); border-color: #A8D8BF; }
.chip.ch-verdict.good { background: var(--selected); color: var(--navy-2); border-color: #B9CCE3; }
.chip.ch-verdict.average,
.chip.ch-verdict.fair { background: var(--amber-bg); color: var(--gold-ink); border-color: var(--gold-line); }
.chip.ch-verdict.poor,
.chip.ch-verdict.worst { background: var(--red-bg); color: var(--red); border-color: #E4A9A9; }

.ch-axes { display: grid; grid-template-columns: repeat(auto-fit, minmax(13.75rem, 1fr)); gap: var(--s-2); margin-bottom: var(--s-3); }
.ch-axis { background: var(--surface-2); border: 1px solid var(--line); border-radius: var(--r-tile); padding: var(--s-3); }
.ch-axis-l { font-size: 0.625rem; text-transform: uppercase; letter-spacing: .09em; font-weight: 600; color: var(--sub-2); }
.ch-axis-v {
  font-family: var(--mono); font-variant-numeric: tabular-nums;
  font-size: 1.375rem; font-weight: 700; color: var(--ink); margin: 2px 0; letter-spacing: -.02em;
}
.ch-axis-v span { font-size: 0.8125rem; color: var(--sub); font-weight: 400; letter-spacing: 0; }
.ch-axis-b { font-size: 0.6875rem; line-height: 1.5; color: var(--sub-2); }
.ch-summary { font-size: 0.875rem; line-height: 1.6; max-width: 72ch; }
.ch-fix { font-size: 0.8125rem; line-height: 1.6; color: var(--ink-2); max-width: 72ch; margin-top: var(--s-2); }

/* Development target, said in words. Sits under the generic shape advice
   because it is the specific instruction: what THIS pitch should become. The
   hold variant is green, not blue — "you are already there" is a different
   message from "here is the work", and a coach should not have to read the
   sentence to tell them apart. */
.dev-reason {
  margin-top: var(--s-3); padding: var(--s-2) var(--s-3);
  border-left: 3px solid var(--navy); border-radius: var(--r-tile);
  background: var(--surface-2); max-width: 72ch;
}
.dev-reason.hold { border-left-color: var(--green); background: var(--green-bg); }
.dev-reason-head {
  font-size: 0.75rem; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--navy); margin-bottom: 2px;
}
.dev-reason.hold .dev-reason-head { color: var(--green); }
.dev-reason-why { font-size: 0.8125rem; line-height: 1.6; color: var(--ink-2); }

/* ---------------- hitting ---------------- */

.hcard { border-top-color: var(--green); }
.damage-line { display: flex; align-items: center; gap: var(--s-2); margin: 2px 0 var(--s-3); }
.damage-lab { font-size: 0.6875rem; text-transform: uppercase; letter-spacing: .07em; color: var(--sub-2); font-weight: 700; }

.damage-hero {
  display: grid;
  grid-template-columns: auto auto auto 1fr;
  gap: var(--s-4);
  align-items: center;
  background: var(--navy);
  border-radius: var(--r-card);
  padding: var(--s-4) var(--s-5);
  margin-bottom: var(--s-2);
}
.dh-score { text-align: center; }
.dh-num {
  font-family: var(--display);
  font-weight: 700;
  font-size: 2.875rem;
  line-height: 1;
  letter-spacing: -.01em;
  padding: var(--s-1) var(--s-3);
  border-radius: var(--r-tile);
  display: block;
}
.dh-lab { font-size: 0.625rem; text-transform: uppercase; letter-spacing: .1em; color: #93AAC7; margin-top: var(--s-1); font-weight: 600; }
.dh-arrow { color: #5D7DA6; font-size: 1.375rem; }
.dh-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: var(--s-2); }

.comp-row { padding: var(--s-3) 0; border-bottom: 1px solid var(--line-soft); }
.comp-row:last-child { border-bottom: 0; }
.comp-head { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-2); margin-bottom: var(--s-2); }
.comp-label { font-weight: 600; font-size: 0.875rem; letter-spacing: -.005em; }
.comp-weight { font-size: 0.6875rem; color: var(--sub-2); text-transform: uppercase; letter-spacing: .07em; font-weight: 600; }
.comp-track {
  position: relative; height: 8px; background: var(--line-soft);
  border-radius: var(--r-pill); overflow: visible;
  box-shadow: inset 0 0 0 1px rgba(0, 33, 69, .03);
}
.comp-fill { display: block; height: 100%; border-radius: var(--r-pill); }
.comp-target {
  position: absolute; top: -4px; width: 2px; height: 16px;
  background: var(--navy); border-radius: 1px;
}
.comp-foot {
  display: flex; justify-content: space-between; gap: var(--s-2);
  font-size: 0.6875rem; color: var(--sub); margin-top: var(--s-1); flex-wrap: wrap;
}
.comp-foot .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; font-weight: 700; color: var(--ink); }
.comp-foot .pos { color: var(--green); font-weight: 700; }
.comp-foot .neg { color: var(--amber); font-weight: 700; }

/* ---------------- responsive ---------------- */

@media (max-width: 980px) {
  .slot-panel { grid-template-columns: 9.375rem 1fr; }
  .slot-tiles { grid-column: 1 / -1; }
}
@media (max-width: 900px) {
  .brand-sub { display: none; }
  /* Hidden from view, not from assistive tech: display:none would drop the
     select's accessible name along with the visible text. This keeps the
     label in the accessibility tree while taking up no visual space. */
  .lvl-label {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
  }
}
@media (max-width: 860px) {
  .charts-row { grid-template-columns: 1fr; }
  .proj-grid { grid-template-columns: 1fr; }
}
@media (max-width: 780px) {
  .damage-hero { grid-template-columns: auto auto auto; }
  .dh-tiles { grid-column: 1 / -1; }
}

/* Phone: the header was taking a fifth of the screen because five nav items
   wrapped onto three rows. Brand and level pickers hold row one; the nav gets
   row two to itself and scrolls sideways. Every destination stays a tap away —
   no hamburger, no hidden pages — and the header halves in height. */
@media (max-width: 760px) {
  .topbar {
    padding: var(--s-2) var(--s-3);
    gap: var(--s-2);
    row-gap: var(--s-2);
    min-height: 0;
  }
  .brand-logo { width: 1.875rem; height: 1.875rem; }
  .brand-title { font-size: 1.0625rem; }
  .brand { gap: var(--s-2); }
  .topbar-right { gap: var(--s-1); }
  .lvl-select { font-size: 0.75rem; padding: 2px var(--s-1); min-height: 1.75rem; max-width: 7rem; }

  .topnav {
    order: 3;
    flex: 1 0 100%;
    margin: 0 calc(var(--s-3) * -1);
    padding: 0 var(--s-3);
    overflow-x: auto;
    flex-wrap: nowrap;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
    /* Fade at both ends so a cut-off item reads as "scroll", not as "broken". */
    -webkit-mask-image: linear-gradient(to right, transparent 0, #000 var(--s-3), #000 calc(100% - var(--s-3)), transparent 100%);
            mask-image: linear-gradient(to right, transparent 0, #000 var(--s-3), #000 calc(100% - var(--s-3)), transparent 100%);
  }
  .topnav::-webkit-scrollbar { display: none; }
  .topnav a { white-space: nowrap; flex: none; padding: var(--s-2) var(--s-3); }
  /* The mask above only reads as "scroll" once an item is actually caught
     mid-fade at the edge — the nav items happen to end almost flush with the
     container here, so nothing is ever visibly cut off and the hint never
     fires. A standing vignette after the last item is a simpler, reliable
     "this strip scrolls" cue that doesn't depend on exactly where the items
     happen to land. */
  .topnav::after {
    content: "";
    display: block;
    order: 99;
    flex: none;
    align-self: stretch;
    width: 1.25rem;
    pointer-events: none;
    background: linear-gradient(to right, transparent, rgba(0, 22, 46, .85));
  }

  main { padding: var(--s-5) var(--s-4) var(--s-7); }
  .foot { padding: var(--s-3) var(--s-4) var(--s-6); }
  .section-title { margin-top: var(--s-6); }
  .card { padding: var(--s-3) var(--s-4); }
  .report-name { font-size: 2rem; }
  /* Buttons run left-to-right from the prompt instead of hugging the right
     edge, so a wrapped third button lines up under the first two. */
  .upload-zone { justify-content: flex-start; }
  .upload-actions { margin-left: 0; justify-content: flex-start; flex: 1 0 100%; }
}
@media (max-width: 560px) {
  .slot-panel { grid-template-columns: 1fr; }
  .demo-banner { flex-direction: column; gap: 0; }
}
@media (max-width: 460px) {
  .damage-hero { grid-template-columns: 1fr 1fr; }
  .dh-arrow { display: none; }
}

/* ---------------- sign in ----------------
   The signed-out screens hide the chrome entirely: no nav, no level pickers,
   nothing that implies a page the visitor cannot reach. */

body.signed-out .topnav,
body.signed-out .topbar-right { display: none; }
body.signed-out .topbar { justify-content: center; }
body.signed-out main { padding-top: var(--s-8); }

.auth-wrap { max-width: 24rem; margin: 0 auto; }
.auth-card {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: var(--s-6) var(--s-5); box-shadow: var(--elev-1); text-align: center;
}
.auth-logo { width: 3.25rem; height: 3.25rem; border-radius: var(--r-tile); margin-bottom: var(--s-3); }
.auth-title {
  font-family: var(--display); text-transform: uppercase; letter-spacing: .04em;
  font-size: 1.625rem; font-weight: 700; color: var(--navy); line-height: 1.1; margin-bottom: var(--s-2);
}
.auth-sub { font-size: 0.8125rem; color: var(--sub); line-height: 1.55; margin-bottom: var(--s-5); }
.auth-sub b { color: var(--ink); }
.auth-id { font-family: var(--mono); font-size: 0.75rem; color: var(--navy-2); }

.auth-form { display: flex; flex-direction: column; gap: var(--s-3); text-align: left; }
.auth-field { display: flex; flex-direction: column; gap: var(--s-1); }
.auth-field > span {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--sub);
}
.auth-field input {
  font-family: var(--body); font-size: 0.9375rem; color: var(--ink);
  background: var(--surface-2); border: 1px solid var(--line-strong);
  border-radius: var(--r-ctl); padding: 0 var(--s-3); min-height: var(--ctl-h-lg);
  width: 100%;
}
.auth-field input:focus { border-color: var(--navy-2); background: var(--card); }
.auth-field small { font-size: 0.6875rem; color: var(--sub-2); }
.auth-submit { width: 100%; justify-content: center; margin-top: var(--s-2); }

.auth-error {
  background: var(--red-bg); border: 1px solid #E4A9A9; color: var(--red);
  border-radius: var(--r-ctl); padding: var(--s-2) var(--s-3);
  font-size: 0.8125rem; line-height: 1.45;
}
/* The demo's way in. Above the form and visually ahead of it, because on this
   host it is what almost everyone wants; the form stays below, unchanged, for
   the case where somebody has been given a different account. */
.auth-demo {
  text-align: left;
  background: var(--surface-2);
  border: 1px solid var(--line-strong);
  border-radius: var(--r-card);
  padding: var(--s-4);
  margin-bottom: var(--s-5);
}
.auth-demo-lead {
  font-size: 0.8125rem; line-height: 1.55; color: var(--sub); margin: 0 0 var(--s-3);
}
.auth-demo-lead strong { color: var(--ink); }
.auth-demo-go { margin-top: 0; }
.auth-demo-creds {
  margin: var(--s-3) 0 0; font-size: 0.6875rem; color: var(--sub-2);
  text-align: center; line-height: 1.6;
}
.auth-demo-creds code {
  font-family: var(--mono); font-size: 0.6875rem; color: var(--navy-2);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-ctl); padding: 1px 4px;
  /* A long address must wrap rather than widen the card on a phone. */
  overflow-wrap: anywhere;
}

.auth-alt { margin-top: var(--s-4); font-size: 0.75rem; color: var(--sub); }
.auth-foot {
  margin-top: var(--s-4); font-size: 0.6875rem; line-height: 1.55;
  color: var(--sub-2); text-align: center;
}

/* Who is signed in, and the way out. */
.whoami { display: flex; align-items: center; gap: var(--s-2); }
.whoami-role {
  font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
  color: var(--navy); background: var(--gold); border-radius: var(--r-pill);
  padding: 2px var(--s-2); line-height: 1.5;
}
.whoami-email { font-size: 0.6875rem; color: #A9BCD4; font-family: var(--mono); }
.whoami .btn { min-height: 1.75rem; font-size: 0.6875rem; padding: 0 var(--s-2); }
@media (max-width: 760px) { .whoami-email { display: none; } }

/* ---------------- reduced motion / transparency ----------------
   Reduced motion does not mean no feedback. Presses still acknowledge, they
   just stop moving: the scale becomes a dim. Chart redraws stop travelling
   across the plot (flipRedraw in dom.js checks the same query and skips
   straight to the new frame). */

@media (prefers-reduced-motion: reduce) {
  .btn, .seg button, .am-filter button, .zone-toggle, .lock-btn,
  .topnav a, .brand, .pcard, .lock-group { transition-duration: 1ms; }

  .btn:active, .seg button:active, .am-filter button:active,
  .zone-toggle:active, .lock-btn:active, .topnav a:active,
  .brand:active, .pcard:hover, .pcard:active { transform: none; }

  /* The press still reads, as a darkening rather than a movement. */
  .btn:active, .seg button:active, .am-filter button:active,
  .zone-toggle:active, .lock-btn:active, .brand:active { filter: brightness(.92); }

  .am-dot, .am-label, .pp-dot, .pp-label { transition: none !important; }
}

/* The header is the one genuinely translucent surface, so it goes solid first;
   the tinted panels lose their transparency-adjacent softness alongside it. */
@media (prefers-reduced-transparency: reduce) {
  .topbar {
    background: var(--navy);
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
  }
  .topnav a:hover { background: var(--navy-3); }
  .lvl-select { background: var(--navy-2); border-color: #2A4F80; }
  .am-readout, .ch-axis { background: var(--card); }
}

/* ---------------- print ---------------- */

@page { size: letter; margin: 0.5in 0.55in; }

@media print {
  body { background: #fff; font-size: 12px; }
  .no-print, .topbar, .foot, .report-actions { display: none !important; }
  main { max-width: none; padding: 0; }
  .card { box-shadow: none; border-color: #BBB; break-inside: avoid; }
  .pcard, .note-card, .proj-hero { box-shadow: none; }
  .section-title { break-after: avoid; margin-top: 18px; }
  .charts-row { grid-template-columns: 1fr 1fr; }
  .dev-grid, .notes-grid { grid-template-columns: 1fr 1fr; }
  .report-name { font-size: 30px; }
  .level-strip, .slot-panel, .damage-hero {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .damage-hero { break-inside: avoid; }
  .slot-panel { break-inside: avoid; }
  .zone-toggle { display: none !important; }
  .arsenal th { color: #333; }
  a { text-decoration: none; color: inherit; }
  .print-footer { display: block !important; }
}

.print-footer {
  display: none;
  margin-top: var(--s-5);
  padding-top: var(--s-2);
  border-top: 1px solid #BBB;
  font-size: 9.5px;
  color: #555;
  font-family: var(--mono);
}

/* The stable id, kept beside a coach-visible name on roster cards. */
.pcard-id { font-family: var(--mono); letter-spacing: 0; color: var(--navy-2); }

/* A player has no roster, so the report's way back to one is meaningless.
   Their profile IS the landing page. */
body.role-player .report-actions a[href="#"] { display: none; }

/* ---------------- roster admin ----------------
   A working table, not a report: controls sit inline in the row because the
   coach's task is scanning for who still needs an invite and acting on the spot. */

.adm-add { margin-bottom: var(--s-4); }
.adm-add-form { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: flex-end; }
.adm-f { display: flex; flex-direction: column; gap: var(--s-1); }
.adm-f > span {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--sub);
}
.adm-f input[type="text"] {
  font-family: var(--body); font-size: 0.875rem; color: var(--ink);
  background: var(--card); border: 1px solid var(--line-strong);
  border-radius: var(--r-ctl); padding: 0 var(--s-3); min-height: var(--ctl-h);
  min-width: 14rem;
}
.adm-f-check { align-self: center; padding-bottom: var(--s-1); }
.adm-note { font-size: 0.75rem; color: var(--sub-2); margin-top: var(--s-3); max-width: 70ch; }

.adm-msg { font-size: 0.8125rem; margin-top: var(--s-3); }
.adm-msg:empty { display: none; }
.adm-msg.ok { color: var(--green); }
.adm-msg.bad { color: var(--red); }

.adm-table td { vertical-align: middle; white-space: nowrap; }
.adm-table .lvl-select { min-height: 1.75rem; font-size: 0.75rem; }
.adm-name { font-weight: 600; }
.adm-sub { display: block; font-size: 0.6875rem; color: var(--sub-2); }
.adm-check { display: inline-flex; align-items: center; gap: var(--s-1); font-size: 0.75rem; color: var(--sub); }
.adm-check input { accent-color: var(--navy); }

/* Height and weight. Three narrow boxes on one row, sized to their content —
   nobody is 4 digits tall, and a full-width input in a table cell reads as a
   place to type prose. */
.adm-size { display: flex; flex-direction: column; gap: var(--s-1); }
.adm-size-h, .adm-size-w { display: inline-flex; align-items: center; gap: 0.25rem; }
/* Widths count the padding — box-sizing is border-box — so these are the digits
   plus room for it. At a bare 3ch a three-digit weight clipped to "19". */
.adm-size input {
  width: 4ch; padding: 0.2rem 0.25rem; text-align: center;
  font: inherit; font-variant-numeric: tabular-nums;
  border: 1px solid var(--line); border-radius: var(--r-s); background: var(--card);
  color: inherit;
}
.adm-size-w input { width: 5ch; }
/* Spinners steal half the width of a 3ch box for a control nobody uses here. */
.adm-size input::-webkit-outer-spin-button,
.adm-size input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.adm-size input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.adm-size em { font-style: normal; font-size: 0.6875rem; color: var(--sub); }

/* Bats and throws, stacked the way height and weight are directly beside them.
   Each select is labelled with a single letter rather than a word: the column
   heading already says B/T, and two full words in a table cell push every other
   column narrower for no added meaning. */
.adm-bt { display: flex; flex-direction: column; gap: var(--s-1); }
.adm-bt-f { display: inline-flex; align-items: center; gap: 0.35rem; }
.adm-bt-f > span { font-size: 0.6875rem; color: var(--sub); width: 1ch; }
.adm-bt select { padding: 0.15rem 0.25rem; font-size: 0.75rem; }

/* Inactive athletes stay visible but recede — they are history, not noise. */
.row-off { opacity: .55; }

/* Access is the widest column because it is the reason the page exists. */
.adm-access { white-space: normal; min-width: 20rem; }
.acc-state {
  display: inline-block; font-size: 0.625rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .07em; margin-right: var(--s-2);
}
.acc-state.on { color: var(--green); }
.acc-state.pending { color: var(--amber); }
.acc-state.off { color: var(--sub-2); }
.acc-state.minor { color: var(--sub-2); }
.acc-hint { font-size: 0.6875rem; color: var(--sub-2); margin-right: var(--s-2); }
.acc-code {
  font-family: var(--mono); font-size: 0.8125rem; font-weight: 700; letter-spacing: .04em;
  background: var(--amber-bg); border: 1px solid var(--gold-line); color: var(--gold-ink);
  border-radius: var(--r-ctl); padding: 1px var(--s-2); margin-right: var(--s-2);
}
.acc-btn { min-height: 1.75rem; font-size: 0.6875rem; padding: 0 var(--s-2); margin-right: var(--s-1); }

/* Permanent deletion. Marked, but not shouting — it sits beside Deactivate,
   which is the reversible answer and the one a coach usually wants. */
/* Two classes, to outrank `.btn.ghost` — a single `.adm-danger` loses the
   specificity contest and renders in the ordinary navy, which is exactly what
   it did until the browser check caught it. */
.btn.adm-danger { color: #96341f; border-color: #e8cabf; }
.btn.adm-danger:hover { background: #fbe9e4; border-color: #dda893; }
.btn.adm-danger:focus-visible { outline: 2px solid #96341f; outline-offset: 2px; }
@media (prefers-color-scheme: dark) {
  .btn.adm-danger { color: #f0a894; border-color: #5d3227; }
  .btn.adm-danger:hover { background: #331a14; border-color: #7a4234; }
}

/* The code, once, big enough to read aloud without squinting. */
.invite-result {
  border: 1px solid var(--gold-line); background: var(--amber-bg);
  border-radius: var(--r-card); padding: var(--s-3) var(--s-4); margin-top: var(--s-2);
}
.ir-head { font-size: 0.8125rem; color: var(--gold-ink); }
.ir-code {
  font-family: var(--mono); font-size: 1.5rem; font-weight: 700; letter-spacing: .12em;
  color: var(--navy); margin: var(--s-2) 0; user-select: all;
}
.ir-note { font-size: 0.75rem; color: var(--gold-ink); line-height: 1.55; max-width: 60ch; }

/* ============================================================
   Phase 2 — the athlete's file
   Sub-tabs, session history, assessments and trends.
   ============================================================ */

/* ---------------- sub-tabs ----------------
   A second level of navigation under the top nav, so it is deliberately
   quieter: an underline rather than a filled pill, because two competing
   selected states on one screen makes neither read as current. */

.ptabs {
  display: flex;
  gap: var(--s-1);
  border-bottom: 1px solid var(--line);
  margin: var(--s-5) 0 var(--s-5);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.ptabs::-webkit-scrollbar { display: none; }

.ptab {
  display: inline-flex;
  align-items: center;
  gap: var(--s-2);
  padding: 0 var(--s-3);
  min-height: 2.5rem;
  background: transparent;
  border: 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--sub);
  font-weight: 600;
  font-size: 0.8125rem;
  white-space: nowrap;
  transition: color var(--t-press) var(--ease-out);
}
.ptab:hover { color: var(--ink-2); }
.ptab.on { color: var(--navy); border-bottom-color: var(--gold); }
.ptab-n {
  font-family: var(--mono);
  font-size: 0.6875rem;
  font-variant-numeric: tabular-nums;
  color: var(--sub-2);
  background: var(--surface-3);
  border-radius: var(--r-pill);
  padding: 0 var(--s-2);
  line-height: 1.5;
}
.ptab.on .ptab-n { background: var(--selected); color: var(--navy-2); }

/* ---------------- session history rail ----------------
   The dates are the interface. Newest first and selected by default, because
   the commonest arrival is "we just did this". */

.hist-rail {
  display: flex;
  align-items: center;
  gap: var(--s-3);
  flex-wrap: wrap;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--r-card);
  padding: var(--s-2) var(--s-3);
  margin-bottom: var(--s-3);
}
.hist-head { display: flex; align-items: baseline; gap: var(--s-2); }
.hist-label {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2);
}
.hist-count {
  font-family: var(--mono); font-size: 0.6875rem; color: var(--sub-2);
}
.hist-chips {
  display: flex; gap: var(--s-1); flex: 1; overflow-x: auto;
  -webkit-overflow-scrolling: touch; scrollbar-width: none; padding: 2px;
}
.hist-chips::-webkit-scrollbar { display: none; }

.hist-chip {
  display: inline-flex; flex-direction: column; align-items: flex-start;
  gap: 1px; padding: var(--s-1) var(--s-3);
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-ctl); color: var(--sub);
  white-space: nowrap; line-height: 1.2;
}
.hist-chip:hover { border-color: var(--line-strong); color: var(--ink-2); }
.hist-chip.on {
  background: var(--navy); border-color: var(--navy); color: #fff;
}
.hc-date { font-size: 0.8125rem; font-weight: 700; }
.hc-year { font-family: var(--mono); font-size: 0.625rem; opacity: .7; }
.hc-tag {
  font-size: 0.5625rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--gold-ink);
}
.hist-chip.on .hc-tag { color: var(--gold); }
.hist-del { min-height: 1.75rem; font-size: 0.6875rem; padding: 0 var(--s-2); }
/* Extension trust: a coach's control on one bullpen. Inside .hist-rail, so never printed. */
.ext-trust { flex-basis: 100%; grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--s-2); font-size: 0.75rem; }
.ext-trust-l { font-weight: 600; }
.ext-trust-audit, .ext-trust-basis { color: var(--sub); }
.ext-trust-warn { color: var(--amber); font-weight: 600; }
.ext-trust-actions { display: inline-flex; flex-wrap: wrap; gap: var(--s-2); }
.ext-trust-btn { min-height: 1.75rem; font-size: 0.6875rem; padding: 0 var(--s-2); }
.ext-trust.dialog { flex-direction: column; align-items: flex-start; }
.ext-trust-field { display: flex; align-items: center; gap: var(--s-2); }
.ext-trust-preview { border-collapse: collapse; }
.ext-trust-preview th, .ext-trust-preview td { padding: 0.125rem 0.5rem; text-align: left; }

/* What you are looking at, in one line under the rail. */
.sess-meta {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4);
  align-items: baseline; margin-bottom: var(--s-4); font-size: 0.75rem;
}
.sess-when { font-weight: 700; color: var(--navy); font-size: 0.875rem; }
.sess-cover, .sess-prev { color: var(--sub); }

/* ---------------- change marks ----------------
   Direction is the model's call, never the sign's: a sprint that got faster is
   a negative number and an improvement. */

.delta-since {
  font-family: var(--mono); font-size: 0.625rem; font-weight: 700;
  margin-left: 3px; letter-spacing: -.02em;
}
.delta-since.d-up { color: var(--green); }
.delta-since.d-down { color: var(--red); }

.chg {
  display: inline-flex; align-items: center;
  font-family: var(--mono); font-size: 0.75rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.chg.up { color: var(--green); }
.chg.down { color: var(--red); }
.chg.flat, .chg.none { color: var(--sub-2); font-family: var(--body); font-weight: 400; font-size: 0.6875rem; }

.trend-delta {
  font-family: var(--mono); font-size: 0.75rem; font-weight: 700;
  margin-left: auto; white-space: nowrap;
}
.trend-delta.up { color: var(--green); }
.trend-delta.down { color: var(--red); }

.trend-svg { width: 100%; height: auto; display: block; }
.trend-empty {
  color: var(--sub); font-size: 0.8125rem; line-height: 1.6;
  padding: var(--s-5) 0; text-align: center; max-width: 60ch; margin: 0 auto;
}
.spark { width: 4.875rem; height: 1.375rem; display: block; }
.spark-none { color: var(--sub-2); font-size: 0.75rem; }

/* ---------------- movement screen ---------------- */

.scr-pill {
  display: inline-flex; align-items: center;
  font-size: 0.625rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; padding: 1px var(--s-2);
  border-radius: var(--r-pill); border: 1px solid var(--line);
  background: var(--surface-3); color: var(--sub-2);
}
.scr-pill.green { background: var(--green-bg); border-color: #A8D8BF; color: var(--green); }
.scr-pill.yellow { background: var(--amber-bg); border-color: var(--gold-line); color: var(--gold-ink); }
.scr-pill.red { background: var(--red-bg); border-color: #E4A9A9; color: var(--red); }

.scr-tally {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: var(--s-3); margin-bottom: var(--s-3);
}
.scr-t-cell {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--r-card);
  padding: var(--s-3) var(--s-4); display: flex; flex-direction: column; gap: 2px;
}
.scr-t-cell.wide { grid-column: span 2; }
.scr-t-cell span { font-size: 0.75rem; color: var(--sub); }
.scr-n {
  font-family: var(--display); font-size: 1.625rem; font-weight: 700;
  line-height: 1; color: var(--navy);
}
.scr-n.green { color: var(--green); }
.scr-n.yellow { color: var(--amber); }
.scr-n.red { color: var(--red); }

.scr-asym { border-left: 3px solid var(--amber); margin-bottom: var(--s-3); }
.scr-asym-head {
  font-family: var(--display); font-size: 1rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
  display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-2);
}
.scr-asym-row {
  display: grid; grid-template-columns: minmax(9rem, 1fr) auto; gap: var(--s-1) var(--s-3);
  padding: var(--s-2) 0; border-top: 1px solid var(--line-soft);
}
.scr-asym-name { font-weight: 600; font-size: 0.8125rem; color: var(--ink); }
.scr-asym-sides { font-size: 0.6875rem; color: var(--sub-2); display: flex; gap: var(--s-2); align-items: center; }
.scr-asym-why { grid-column: 1 / -1; font-size: 0.75rem; color: var(--sub); line-height: 1.55; }

.scr-card { padding: 0; overflow: hidden; }
.scr-group + .scr-group { border-top: 1px solid var(--line); }
.scr-group-head {
  background: var(--surface-2); padding: var(--s-2) var(--s-4);
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2);
}
.scr-row {
  display: grid; grid-template-columns: minmax(8rem, 1fr) auto;
  gap: var(--s-1) var(--s-3); padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line-soft); border-left: 3px solid transparent;
}
.scr-row.yellow { border-left-color: var(--gold-line); }
.scr-row.red { border-left-color: var(--red); background: rgba(194, 68, 68, .025); }
.scr-name { font-weight: 600; font-size: 0.8125rem; color: var(--ink); }
.scr-codes { display: flex; gap: var(--s-2); align-items: center; }
.scr-side { font-size: 0.625rem; color: var(--sub-2); display: inline-flex; gap: 3px; align-items: center; }
.scr-why { grid-column: 1 / -1; font-size: 0.75rem; color: var(--sub); line-height: 1.55; max-width: 70ch; }

/* Every screen at once: tests down, dates across. A restriction that has been
   there all year looks completely different from one that just appeared. */
.scr-grid th:first-child { text-align: left; }
.scr-grid td { text-align: center; }
.grid-cell {
  display: inline-block; width: 1.125rem; height: 1.125rem;
  border-radius: 3px; border: 1px solid var(--line);
  background: var(--surface-3);
}
.grid-cell.green { background: var(--green); border-color: var(--green); }
.grid-cell.yellow { background: var(--gold); border-color: var(--gold); }
.grid-cell.red { background: var(--red); border-color: var(--red); }
.grid-cell.none { background: repeating-linear-gradient(45deg, #fff, #fff 3px, var(--surface-3) 3px, var(--surface-3) 6px); }

/* ---------------- measured tests ---------------- */

.kpi-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--s-3); margin-bottom: var(--s-4);
}
.kpi-card {
  background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--line-strong);
  border-radius: var(--r-card); padding: var(--s-3) var(--s-4);
  box-shadow: var(--elev-1);
}
.kpi-card.elite { border-left-color: var(--green); }
.kpi-card.good { border-left-color: var(--navy-2); }
.kpi-card.fair { border-left-color: var(--gold); }
.kpi-card.poor { border-left-color: var(--red); }
.kpi-label {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--sub-2); margin-bottom: var(--s-1);
}
.kpi-value {
  font-family: var(--display); font-size: 1.75rem; font-weight: 700;
  line-height: 1; color: var(--navy); font-variant-numeric: tabular-nums;
}
.kpi-unit { font-size: 0.8125rem; color: var(--sub); margin-left: 3px; font-family: var(--body); font-weight: 400; }
.kpi-track {
  position: relative; height: 5px; border-radius: var(--r-pill);
  background: var(--surface-3); margin: var(--s-2) 0 var(--s-1); overflow: hidden;
}
.kpi-fill { display: block; height: 100%; background: var(--navy-2); border-radius: var(--r-pill); }
.kpi-mark {
  position: absolute; top: -2px; bottom: -2px; width: 2px;
  background: var(--gold-ink); opacity: .55;
}
.kpi-foot { font-size: 0.6875rem; color: var(--sub); }
.kpi-none { font-size: 0.6875rem; color: var(--sub-2); margin-top: var(--s-2); }
.kpi-band { font-weight: 700; text-transform: uppercase; letter-spacing: .05em; }

.meas-group { margin-bottom: var(--s-4); }
.meas-group-head {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2); margin-bottom: var(--s-2);
}
.meas-table { min-width: 34rem; }
.meas-row { cursor: pointer; }
.meas-row:hover { background: var(--surface-2); }
.meas-row.on { background: var(--selected); }
.meas-row.on .meas-name { color: var(--navy); }
.meas-name { font-weight: 600; color: var(--ink); white-space: normal; max-width: 22rem; }
.meas-why {
  display: block; font-weight: 400; font-size: 0.6875rem;
  color: var(--sub-2); line-height: 1.45; margin-top: 2px;
}
.meas-val { font-family: var(--mono); font-weight: 700; color: var(--navy) !important; }
.meas-unit { font-family: var(--body); font-weight: 400; color: var(--sub-2); font-size: 0.6875rem; margin-left: 2px; }
.meas-spark { width: 5.5rem; }

/* ---------------- recording a session ---------------- */

.rec-form { margin-top: var(--s-6); }
.rec-head {
  display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: flex-end;
  justify-content: space-between; padding-bottom: var(--s-3);
  border-bottom: 1px solid var(--line);
}
.rec-title {
  font-family: var(--display); font-size: 1.125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
}
.rec-blurb { font-size: 0.75rem; color: var(--sub); max-width: 62ch; line-height: 1.55; margin-top: 2px; }
.rec-date {
  display: flex; flex-direction: column; gap: var(--s-1);
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--sub-2);
}

.rec-group { border: 0; padding: 0; margin: var(--s-4) 0 0; }
.rec-group legend {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2); padding: 0;
}
.rec-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--s-2) var(--s-4); margin-top: var(--s-2);
}
.rec-f {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-3); padding: var(--s-1) 0; border-bottom: 1px solid var(--line-soft);
}
.rec-l { font-size: 0.8125rem; color: var(--ink-2); }
.rec-num { display: inline-flex; align-items: center; gap: var(--s-1); }
.rec-unit { font-size: 0.6875rem; color: var(--sub-2); min-width: 1.75rem; }
.rec-sides { display: inline-flex; align-items: center; gap: var(--s-1); }
.rec-side { font-size: 0.625rem; font-weight: 700; color: var(--sub-2); }

input.rec-in {
  width: 5rem; min-height: var(--ctl-h); text-align: right;
  border: 1px solid var(--line-strong); border-radius: var(--r-ctl);
  background: var(--card); color: var(--ink); font-family: var(--mono);
  font-size: 0.8125rem; padding: 0 var(--s-2);
}
input.rec-in:focus { outline: 2px solid var(--navy-2); outline-offset: 1px; }
select.rec-in { min-width: 6.5rem; }

.rec-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--s-3);
  margin-top: var(--s-5); padding-top: var(--s-3); border-top: 1px solid var(--line);
}
.rec-note { font-size: 0.75rem; color: var(--sub); }

/* ---------------- overview ---------------- */

/* The development summary: what athleteSummary() already computes for the
   take-home sheet, read live. Same auto-fit approach as .ov-grid below it —
   three columns wide, one narrow, no separate breakpoint needed. */
.dev-summary {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
  gap: var(--s-5) var(--s-6); margin-bottom: var(--s-4);
}
.dev-col { display: flex; flex-direction: column; gap: var(--s-3); min-width: 0; }
.dev-h {
  font-family: var(--display); font-size: 0.8125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy); margin: 0;
}
.dev-h.good { color: var(--green); }
.dev-h.work { color: var(--amber); }
.dev-line { border-left: 3px solid var(--line); padding-left: var(--s-3); }
.dev-line.good { border-left-color: var(--green); }
.dev-line.work { border-left-color: var(--gold); }
.dev-line-h { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-2); }
.dev-line-t { font-weight: 700; font-size: 0.8125rem; color: var(--navy); }
.dev-line-a { font-size: 0.6875rem; color: var(--sub-2); white-space: nowrap; }
.dev-line-b { font-size: 0.75rem; color: var(--ink-2); line-height: 1.45; margin-top: 1px; }
.dev-steps { margin: 0; padding-left: 1.1rem; display: flex; flex-direction: column; gap: var(--s-3); }
.dev-steps li::marker { font-family: var(--display); font-weight: 700; color: var(--gold-ink); }
.dev-step-t { font-weight: 700; font-size: 0.8125rem; color: var(--navy); }
.dev-step-b { font-size: 0.75rem; color: var(--ink-2); line-height: 1.45; }
.dev-step-w { font-size: 0.6875rem; color: var(--sub); line-height: 1.4; margin-top: 1px; }

.ov-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: var(--s-3);
}
.ov-card {
  text-align: left; background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: var(--s-3) var(--s-4);
  box-shadow: var(--elev-1); display: flex; flex-direction: column; gap: var(--s-1);
  transition: border-color var(--t-press) var(--ease-out);
}
.ov-card:hover { border-color: var(--line-strong); }
.ov-card.empty { background: var(--surface-2); box-shadow: none; }
.ov-c-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2); }
.ov-c-label {
  font-family: var(--display); font-size: 1.0625rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
}
.ov-c-n {
  font-family: var(--mono); font-size: 0.6875rem; color: var(--navy-2);
  background: var(--selected); border-radius: var(--r-pill); padding: 0 var(--s-2);
}
.ov-c-none { font-size: 0.6875rem; color: var(--sub-2); }
.ov-c-when { font-size: 0.75rem; color: var(--sub); }
.ov-c-lead { font-size: 0.875rem; font-weight: 600; color: var(--ink); }
.ov-delta { font-family: var(--mono); font-size: 0.75rem; font-weight: 700; }
.ov-delta.up { color: var(--green); }
.ov-delta.down { color: var(--red); }
.ov-flags { font-size: 0.75rem; color: var(--sub); display: flex; gap: var(--s-3); align-items: baseline; }
.ov-flags .scr-n { font-size: 1rem; }
.ov-head-metric { font-size: 0.75rem; color: var(--sub); }
.ov-head-metric b { font-family: var(--mono); color: var(--ink); }

.ov-arsenal { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--s-2) var(--s-4); }
.ov-pitch { display: flex; align-items: center; gap: var(--s-2); padding: var(--s-1) 0; }
.ov-pitch .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.ov-p-type { font-weight: 600; font-size: 0.8125rem; color: var(--ink); flex: 1; }
.ov-p-velo { font-family: var(--mono); font-size: 0.75rem; color: var(--sub); }

.ov-log { display: flex; flex-direction: column; }
.ov-log-row {
  display: flex; gap: var(--s-4); align-items: baseline; text-align: left;
  padding: var(--s-2) 0; border-bottom: 1px solid var(--line-soft);
  background: transparent; border-left: 0; border-right: 0; border-top: 0;
}
.ov-log-row:last-child { border-bottom: 0; }
.ov-log-row:hover .ov-log-what { color: var(--navy); }
.ov-log-when { font-family: var(--mono); font-size: 0.75rem; color: var(--sub); min-width: 8rem; }
.ov-log-what { font-weight: 600; font-size: 0.8125rem; color: var(--ink-2); }

/* ---------------- roster: athletes with no bullpen ---------------- */

.noball-list { padding: 0; overflow: hidden; }
.noball-row {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line-soft);
  text-decoration: none;
}
.noball-row:hover { background: var(--surface-2); }
.noball-id { font-size: 0.75rem; color: var(--sub-2); min-width: 3.5rem; }
.noball-name { font-weight: 600; font-size: 0.875rem; color: var(--navy); flex: 1; }
.noball-meta { font-size: 0.75rem; color: var(--sub); }
.noball-list .hint { padding: var(--s-3) var(--s-4); }

/* ---------------- narrow screens ---------------- */

@media (max-width: 40rem) {
  .scr-t-cell.wide { grid-column: span 2; }
  .scr-row, .scr-asym-row { grid-template-columns: 1fr; }
  .scr-codes { justify-content: flex-start; }
  .rec-f { flex-wrap: wrap; }
  .ov-log-when { min-width: 6.5rem; }
  .hist-rail { padding: var(--s-2); }
  .meas-name { max-width: none; }
}

/* ============================================================
   The athlete sheet — three A4 pages that leave the building
   ============================================================ */

@page { size: A4 portrait; margin: 12mm; }

.sh-bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-4); flex-wrap: wrap;
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: var(--s-3) var(--s-4); margin-bottom: var(--s-4);
}
.sh-bar-t {
  font-family: var(--display); font-size: 1.125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
}
.sh-bar-n {
  display: block; font-family: var(--body); font-size: 0.75rem; font-weight: 400;
  text-transform: none; letter-spacing: 0; color: var(--sub); margin-top: 2px;
}

.sheets { display: flex; flex-direction: column; align-items: center; gap: var(--s-6); }

/* On screen a sheet is a real A4 page, so what you see is the pagination you
   get. Print strips the page furniture and lets @page own the margins. */
.sheet {
  width: 210mm;
  min-height: 297mm;
  padding: 12mm 14mm;
  background: #fff;
  border: 1px solid var(--line);
  box-shadow: var(--elev-2);
  color: #14202F;
  font-size: 9.5pt;
  line-height: 1.45;
  display: flex;
  flex-direction: column;
}

.sh-head {
  display: flex; align-items: center; gap: var(--s-4);
  border-bottom: 2px solid var(--navy); padding-bottom: var(--s-2); margin-bottom: var(--s-3);
}
.sh-logo { height: 34px; width: auto; }
.sh-who { flex: 1; }
.sh-name {
  font-family: var(--display); font-size: 20pt; font-weight: 700;
  text-transform: uppercase; letter-spacing: .02em; color: var(--navy); line-height: 1;
}
.sh-meta { font-size: 8.5pt; color: #5F6E85; margin-top: 2px; }
.sh-page { text-align: right; }
.sh-page-n {
  font-family: var(--mono); font-size: 7.5pt; text-transform: uppercase;
  letter-spacing: .08em; color: #7C8A9E;
}
.sh-page-t {
  font-family: var(--display); font-size: 12pt; font-weight: 700;
  text-transform: uppercase; letter-spacing: .03em; color: var(--gold-ink);
}

.sh-when { font-size: 8.5pt; color: #5F6E85; margin-bottom: var(--s-3); }

.sh-h2 {
  font-family: var(--display); font-size: 12pt; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
  margin: var(--s-4) 0 var(--s-2); padding-bottom: 2px;
  border-bottom: 1px solid #DEE5EE;
}
.sh-h2:first-of-type { margin-top: 0; }
.sh-h2.good { color: var(--green); border-bottom-color: #A8D8BF; }
.sh-h2.work { color: var(--amber); border-bottom-color: var(--gold-line); }

.sh-foot {
  margin-top: auto; padding-top: var(--s-2); border-top: 1px solid #DEE5EE;
  font-size: 7.5pt; color: #7C8A9E; font-family: var(--mono);
}
.sh-empty, .sh-none {
  font-size: 9pt; color: #5F6E85; padding: var(--s-3) 0; font-style: italic;
}

/* ---------------- page 1 ---------------- */

.sh-hero {
  background: var(--navy); color: #fff; border-radius: var(--r-card);
  padding: var(--s-3) var(--s-4); margin-bottom: var(--s-3);
}
.sh-hero-l {
  font-size: 7.5pt; font-weight: 700; text-transform: uppercase;
  letter-spacing: .09em; color: var(--gold); margin-bottom: 4px;
}
.sh-hero-row { display: flex; gap: var(--s-5); flex-wrap: wrap; align-items: flex-end; }
.sh-stat { display: flex; flex-direction: column; }
.sh-stat b { font-family: var(--display); font-size: 22pt; font-weight: 700; line-height: 1; }
.sh-stat span { font-size: 7.5pt; text-transform: uppercase; letter-spacing: .06em; color: #A9C0DC; margin-top: 2px; }
.sh-stat-score { margin-left: auto; text-align: right; }
.sh-stat-score b { color: var(--gold); }
.sh-hero-note { font-size: 8pt; color: #A9C0DC; margin-top: var(--s-2); }

.sh-table { width: 100%; border-collapse: collapse; font-size: 8.5pt; }
.sh-table th {
  text-align: left; font-size: 7.5pt; text-transform: uppercase; letter-spacing: .06em;
  color: #5F6E85; border-bottom: 1px solid #C9D4E1; padding: 3px 5px; font-weight: 700;
}
.sh-table td { padding: 4px 5px; border-bottom: 1px solid #EAEFF5; }
.sh-table .num { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
.sh-table th.num { text-align: right; }
.sh-pt { font-weight: 700; }
.sh-pt .dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; margin-right: 5px; }

.sh-chg { font-family: var(--mono); font-weight: 700; font-size: 8pt; }
.sh-chg.up { color: var(--green); }
.sh-chg.down { color: var(--red); }
.sh-chg.flat, .sh-chg.none { color: #7C8A9E; font-weight: 400; }

.sh-split { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-4); margin-top: var(--s-3); }
.sh-plot svg { width: 100%; height: auto; }
.sh-caption { font-size: 7.5pt; color: #5F6E85; line-height: 1.5; margin-top: 4px; }

.sh-key dl { margin: 0; }
.sh-key dt {
  font-weight: 700; font-size: 8.5pt; color: var(--navy); margin-top: var(--s-2);
}
.sh-key dt:first-child { margin-top: 0; }
.sh-key dd { margin: 1px 0 0; font-size: 8pt; color: #2C3E55; line-height: 1.5; }
/* ---- "Where you stand", full width ----
   The Skill Profile used to sit in the right-hand column of page one, stacked
   above Biomechanics. At 89 mm the radar and its six percentile lines have
   nowhere to go but down, and the block ran to 159 mm — more than half an A4
   page for one chart, and the single biggest reason page one printed at 387 mm
   against a 297 mm page.

   Floated rather than gridded: .skill-panel's children are a heading, the SVG,
   a caveat and a list, in that order, and a float needs to know nothing about
   how many of them there are. The radar takes a fixed column on the left and
   the prose flows beside and under it, which is also how a reader wants it —
   the spoke and the number it stands for end up next to each other. */
.sh-stand {
  margin-top: var(--s-4);
  padding-top: var(--s-3);
  border-top: 1px solid var(--line-soft);
}
.sh-stand .skill-panel { text-align: left; }
/* Clear the float so the footer cannot ride up beside the radar. */
.sh-stand .skill-panel::after { content: ""; display: block; clear: both; }
.sh-stand .skill-h { margin-bottom: var(--s-2); }
.sh-stand .skill-radar {
  float: left;
  width: 58mm;
  height: auto;
  margin: 0 var(--s-4) var(--s-2) 0;
}
.sh-stand .skill-note { margin: 0 0 var(--s-2); text-align: left; }
.sh-stand .skill-rows { margin-top: 0; }

.sh-key-wide { margin-top: var(--s-4); }

/* ---- sheet density ----
   THE SHEET IS A FIXED-SIZE OBJECT, which makes vertical rhythm a budget rather
   than a preference. Three A4 pages give 273 mm of content each once @page's
   12 mm margins are taken, and the sheet was spending 293 mm on page one and
   277 mm on page three — so it printed on four or five sheets of paper while its
   own masthead said "Page 1 of 3".

   These are the app's ordinary spacing steps taken down one notch, inside .sheet
   only. Nothing is hidden and no type drops below 7.5 pt; the sheet simply stops
   using screen-sized gaps on paper. The screen version is the same object at the
   same size — it is drawn as an A4 page — so this is not a print-only override.

   Measured after every change: see the page dimensions in the deploy notes. */
.sheet .sh-head { padding-bottom: var(--s-1); margin-bottom: var(--s-2); }
.sheet .sh-logo { height: 28px; }
.sheet .sh-when { margin-bottom: var(--s-2); }
.sheet .sh-h2 { margin: var(--s-3) 0 var(--s-1); }
.sheet .sh-h2:first-of-type { margin-top: 0; }

.sheet .sh-hero { padding: var(--s-2) var(--s-3); margin-bottom: var(--s-2); }
.sheet .sh-stat b { font-size: 19pt; }
.sheet .sh-hero-note { margin-top: var(--s-1); }

.sheet .sh-split { margin-top: var(--s-2); }

/* The movement plot is square, so at full column width it is 89 mm of page for
   a chart that reads perfectly well at 78 mm. Capping it also balances the two
   halves of the split — the biomechanics column beside it is about 92 mm — so
   the row is no longer taller than either of the things in it needs to be.
   Deliberately a max-width, not a width: on a phone, where the split stacks,
   the plot still takes the space it is given. */
.sheet .sh-plot svg { display: block; max-width: 78mm; margin: 0 auto; }
.sheet .sh-stand { margin-top: var(--s-2); padding-top: var(--s-2); }

/* Six rows of one line each. The label is nowrap and the value is the part that
   wrapped, so the saving is in the padding, not in the words. */
.sheet .skill-row { padding: .15rem 0; }
.sheet .skill-note { margin-bottom: var(--s-1); }

/* Page three is the densest: three headed lists and the coach's note.
   Deliberately NOT touching .sh-line's padding or .sh-lines' gap. The line
   blocks carry a left border and left padding only, so adding a shorthand here
   put vertical padding where there was none and made the page 8 mm TALLER —
   measured, then reverted. The savings on this page are in the margins between
   blocks, which is where the slack actually was. */
.sheet .sh-steps li { margin-bottom: var(--s-2); }
.sheet .sh-note { padding: var(--s-3); margin-top: var(--s-3); }
.sheet .sh-close { padding: var(--s-2) var(--s-3); margin-top: var(--s-3); }
.sh-key-wide dl { columns: 2; column-gap: var(--s-5); }
.sh-key-wide dd { break-inside: avoid; }

/* ---------------- page 2 ---------------- */

.sh-tally { display: flex; gap: var(--s-3); margin-bottom: var(--s-3); }
.sh-t {
  flex: 1; border: 1px solid #DEE5EE; border-radius: var(--r-tile);
  padding: 4px var(--s-3); display: flex; flex-direction: column;
}
.sh-t b { font-family: var(--display); font-size: 18pt; font-weight: 700; line-height: 1; color: var(--navy); }
.sh-t span { font-size: 7.5pt; text-transform: uppercase; letter-spacing: .06em; color: #5F6E85; }
.sh-t.green { border-left: 3px solid var(--green); } .sh-t.green b { color: var(--green); }
.sh-t.yellow { border-left: 3px solid var(--gold); } .sh-t.yellow b { color: var(--amber); }
.sh-t.red { border-left: 3px solid var(--red); } .sh-t.red b { color: var(--red); }

.sh-scr-list { columns: 2; column-gap: var(--s-5); }
.sh-scr-row {
  display: flex; justify-content: space-between; gap: var(--s-2);
  font-size: 8.5pt; padding: 2px 0 2px 5px; border-left: 3px solid #DEE5EE;
  break-inside: avoid; margin-bottom: 2px;
}
.sh-scr-row.green { border-left-color: var(--green); }
.sh-scr-row.yellow { border-left-color: var(--gold); }
.sh-scr-row.red { border-left-color: var(--red); background: #FAECEC; }
.sh-scr-name { color: #14202F; }
.sh-scr-code { color: #5F6E85; white-space: nowrap; font-size: 7.5pt; text-transform: uppercase; letter-spacing: .04em; }
.sh-scr-code em { font-style: normal; color: #7C8A9E; }

.sh-flag {
  border: 1px solid var(--gold-line); background: #FBF3DF;
  border-radius: var(--r-tile); padding: var(--s-2) var(--s-3);
  font-size: 8pt; line-height: 1.5; margin-top: var(--s-3); color: #4A3A10;
}

.sh-kpis { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2) var(--s-4); }
.sh-kpi { border-left: 3px solid #C9D4E1; padding-left: var(--s-2); break-inside: avoid; }
.sh-kpi.elite { border-left-color: var(--green); }
.sh-kpi.good { border-left-color: var(--navy-2); }
.sh-kpi.fair { border-left-color: var(--gold); }
.sh-kpi.poor { border-left-color: var(--red); }
.sh-kpi-top { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-2); }
.sh-kpi-l { font-size: 8pt; color: #2C3E55; }
.sh-kpi-v { font-family: var(--mono); font-size: 10pt; font-weight: 700; color: var(--navy); white-space: nowrap; }
.sh-kpi-v em { font-style: normal; font-size: 7.5pt; color: #7C8A9E; }
.sh-kpi-track {
  position: relative; height: 4px; border-radius: var(--r-pill);
  background: #EFF3F9; margin: 3px 0 2px; overflow: hidden;
}
.sh-kpi-fill { display: block; height: 100%; background: var(--navy-2); }
.sh-kpi-mark { position: absolute; top: -1px; bottom: -1px; width: 2px; background: var(--gold-ink); }
.sh-kpi-foot { font-size: 7pt; color: #7C8A9E; }

.sh-meas-cols { columns: 2; column-gap: var(--s-5); margin-top: var(--s-3); }
.sh-meas-group { break-inside: avoid; margin-bottom: var(--s-2); }
.sh-meas-group h3 {
  font-size: 7.5pt; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: #7C8A9E; margin: 0 0 2px;
}
.sh-meas {
  display: flex; justify-content: space-between; gap: var(--s-2);
  font-size: 8.5pt; padding: 1px 0; border-bottom: 1px solid #EAEFF5;
}
.sh-meas-l { color: #2C3E55; }
.sh-meas-v { font-family: var(--mono); font-weight: 700; color: var(--navy); white-space: nowrap; }
.sh-meas-v em { font-style: normal; font-weight: 400; font-size: 7.5pt; color: #7C8A9E; margin-left: 1px; }

/* ---------------- page 3 ---------------- */

.sh-lines { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-2) var(--s-4); }
.sh-line { border-left: 3px solid #DEE5EE; padding-left: var(--s-3); break-inside: avoid; }
.sh-line.good { border-left-color: var(--green); }
.sh-line.work { border-left-color: var(--gold); }
.sh-line-h { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s-2); }
.sh-line-t { font-weight: 700; font-size: 9pt; color: var(--navy); }
.sh-line-a {
  font-size: 6.5pt; text-transform: uppercase; letter-spacing: .08em;
  color: #7C8A9E; white-space: nowrap;
}
.sh-line-b { font-size: 8pt; color: #2C3E55; line-height: 1.45; margin-top: 1px; }

.sh-steps { margin: 0; padding-left: 18px; }
.sh-steps li { margin-bottom: var(--s-3); break-inside: avoid; }
.sh-steps li::marker { font-family: var(--display); font-weight: 700; color: var(--gold-ink); }
.sh-step-t { font-weight: 700; font-size: 9.5pt; color: var(--navy); }
.sh-step-b { font-size: 8.5pt; color: #2C3E55; line-height: 1.45; }
.sh-step-w { font-size: 7.5pt; color: #7C8A9E; line-height: 1.4; margin-top: 1px; }

.sh-close {
  margin-top: var(--s-4); padding: var(--s-3);
  background: #F5F8FC; border-radius: var(--r-tile);
  font-size: 8pt; color: #2C3E55; line-height: 1.5;
}

/* ---------------- narrow screens ----------------
   A 210mm page does not fit a laptop column, let alone a phone. The preview
   reflows; @page still decides what actually prints. */

@media (max-width: 900px) {
  .sheet { width: 100%; min-height: 0; padding: var(--s-5); font-size: 0.875rem; }
  .sh-split, .sh-kpis, .sh-lines { grid-template-columns: 1fr; }
  /* A 62 mm radar beside text in a phone-width column leaves the rows a
     sliver. Below the sheet's own breakpoint it goes back to stacking. */
  .sh-stand .skill-radar { float: none; width: 100%; margin: 0 0 var(--s-2); }
  .sh-scr-list, .sh-meas-cols, .sh-key-wide dl { columns: 1; }
  .sh-name { font-size: 1.375rem; }
  .sh-stat b { font-size: 1.5rem; }
}

/* ---------------- printing ---------------- */

@media print {
  /* On the sheet tab, the three pages are the whole document. */
  body.sheet-mode .report-head,
  body.sheet-mode .ptabs,
  body.sheet-mode .sh-bar,
  body.sheet-mode #foot { display: none !important; }

  body.sheet-mode .sheets { display: block; gap: 0; }
  body.sheet-mode .sheet {
    width: auto; min-height: 0; max-width: none;
    padding: 0; border: 0; box-shadow: none;
    break-after: page; page-break-after: always;
  }
  body.sheet-mode .sheet:last-child { break-after: auto; page-break-after: auto; }

  /* Colour is load-bearing here: red means restricted and green means passed,
     and a greyscale print of this page says nothing. */
  .sheet, .sh-hero, .sh-scr-row, .sh-t, .sh-flag, .sh-close, .sh-kpi-track, .sh-kpi-fill {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
  .sh-foot { margin-top: 12px; }
}

/* "8.4" glove" — the number is the measurement, the word is the direction. */
.sh-table .num em, .sh-stat b em {
  font-style: normal; font-family: var(--body); color: #7C8A9E;
  font-size: 7.5pt; margin-left: 2px; font-weight: 400;
}
.sh-stat b em { color: #A9C0DC; font-size: 8pt; }

/* The slider variant, beside the tag it hides under. */
.chip.sl-code {
  background: var(--selected); border-color: #B9CCE3; color: var(--navy-2);
  letter-spacing: .06em;
}
.sl-card { border-left: 3px solid var(--navy-2); }
.sl-notes {
  margin: var(--s-2) 0 0; padding-left: var(--s-4);
  font-size: 0.8125rem; color: var(--ink-2); line-height: 1.6;
}
.sl-notes li { margin-bottom: var(--s-1); }

/* A curveball is the pitch most constrained by what else is in the arsenal. */
.cb-card { border-left: 3px solid var(--green); }
.cb-pairing {
  border: 1px solid var(--gold-line); background: var(--amber-bg);
  border-radius: var(--r-tile); padding: var(--s-2) var(--s-3);
  font-size: 0.8125rem; line-height: 1.6; color: var(--gold-ink);
  margin-top: var(--s-3);
}

/* A cutter is judged by the job it does, not by its own grade. */
.ct-card { border-left: 3px solid var(--navy-2); }
.ct-role {
  border: 1px solid var(--line); background: var(--surface-2);
  border-radius: var(--r-tile); padding: var(--s-2) var(--s-3); margin-top: var(--s-3);
}
.ct-role-h {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2); margin-bottom: var(--s-1);
}
.ct-role-row {
  font-size: 0.8125rem; line-height: 1.6; color: var(--ink-2);
  padding: var(--s-1) 0 var(--s-1) var(--s-3); border-left: 3px solid var(--line-strong);
  margin-bottom: 2px;
}
.ct-role-row.bridge { border-left-color: var(--green); }
.ct-role-row.gap, .ct-role-row.redundant { border-left-color: var(--gold); }
.ct-role-row.fastball, .ct-role-row.mislabelled { border-left-color: var(--navy-2); }
.ct-role-row.split-zone, .ct-role-row.crowds-changeup { border-left-color: var(--gold); }
.ct-role-row.cut-heater { border-left-color: var(--navy-2); }

/* A sinker runs the other way: less carry is better, and the seams do the work. */
.si-card { border-left: 3px solid var(--gold); }
/* Carry is a COST here, so it gets the warning colour and depth gets the green. */
.ch-axis.si-carry { border-color: var(--gold-line); background: var(--amber-bg); }
.ch-axis.si-carry .ch-axis-v { color: var(--gold-ink); }
.si-seam {
  border: 1px solid var(--line); background: var(--surface-2);
  border-radius: var(--r-tile); padding: var(--s-2) var(--s-3); margin-top: var(--s-3);
}
.si-seam-h {
  font-size: 0.6875rem; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2); margin-bottom: var(--s-2);
}
.si-seam-row {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-4);
  font-size: 0.8125rem; color: var(--ink-2);
}
.si-seam-row b { color: var(--ink); font-variant-numeric: tabular-nums; }
.si-seam-b {
  margin-top: var(--s-2); font-size: 0.75rem; line-height: 1.6; color: var(--sub-2);
}
/* The slot is a prerequisite for some shapes and a credit for others. */
.si-slot {
  border-radius: var(--r-tile); padding: var(--s-2) var(--s-3); margin-top: var(--s-3);
  font-size: 0.8125rem; line-height: 1.6;
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink-2);
}
.si-slot.unavailable {
  border-color: var(--gold-line); background: var(--amber-bg); color: var(--gold-ink);
}
.si-slot.rare, .si-slot.met { border-color: #A8D8BF; background: var(--green-bg); }

/* ============================================================
   The take-home card
   ------------------------------------------------------------
   A coach's controls on the left; on the right the exact page
   the athlete receives, at true A4 proportions. The preview
   being the real page — not an impression of it — is what stops
   a coach designing something that breaks in the printer.
   ============================================================ */

.cd-wrap {
  display: grid;
  grid-template-columns: minmax(20rem, 25rem) 1fr;
  gap: 1.75rem;
  align-items: start;
}

.cd-editor { min-width: 0; margin: 0; padding: 0; border: 0; }

.cd-panel { padding: 1rem 1.125rem 1.125rem; margin-bottom: 1rem; }
.cd-panel h3 { margin: 0 0 .625rem; font-size: .9375rem; letter-spacing: .01em; }

.cd-panel-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: .75rem;
  margin-bottom: .625rem;
}
.cd-panel-head h3 { margin: 0; }

.cd-tips {
  margin: 0 0 .875rem; padding: .625rem .75rem .625rem 1.75rem;
  background: var(--surface-2); border-radius: var(--r-ctl);
  font-size: .8125rem; color: var(--sub); line-height: 1.5;
}
.cd-tips li { margin: .1875rem 0; }
.cd-tips b { color: var(--ink-2); }

.cp-group { margin-bottom: .875rem; }
.cp-group h4 {
  margin: 0 0 .25rem; font-size: .6875rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2);
}

.cp-row {
  display: grid; grid-template-columns: auto 1fr auto auto;
  align-items: center; gap: .5rem;
  padding: .375rem .5rem; border-radius: var(--r-ctl);
  font-size: .8125rem; cursor: pointer;
}
.cp-row:hover { background: var(--surface-3); }
.cp-row.on { background: var(--selected); }
.cp-row.full { opacity: .45; cursor: not-allowed; }
.cp-name { color: var(--ink-2); }
.cp-val { color: var(--sub); font-variant-numeric: tabular-nums; }
.cp-why {
  font-size: .625rem; text-transform: uppercase; letter-spacing: .06em;
  color: var(--gold-ink); background: #FDF3DC;
  padding: .0625rem .375rem; border-radius: var(--r-pill);
}

.cd-f { display: block; margin-bottom: .75rem; }
.cd-f > span {
  display: block; margin-bottom: .25rem;
  font-size: .75rem; color: var(--sub); letter-spacing: .01em;
}
.cd-f > span em { font-style: normal; color: var(--sub-2); }
.cd-f input[type="text"], .cd-f textarea, .cd-ai textarea {
  width: 100%; padding: .5rem .625rem;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: var(--card); color: var(--ink);
  font: inherit; font-size: .875rem; line-height: 1.5; resize: vertical;
}
.cd-f input:focus, .cd-f textarea:focus, .cd-ai textarea:focus {
  outline: 2px solid var(--gold); outline-offset: -1px; border-color: transparent;
}

.cd-ai { margin-top: .875rem; padding-top: .875rem; border-top: 1px solid var(--line-soft); }
.cd-check { display: flex; align-items: center; gap: .5rem; font-size: .875rem; cursor: pointer; }
.cd-ai-note { margin: .25rem 0 .625rem; font-size: .75rem; color: var(--sub-2); line-height: 1.45; }
.cd-ai-btns { display: flex; gap: .5rem; margin-top: .5rem; }

/* ---------------- the quick-add focus list ---------------- */

.cd-chosen { list-style: none; margin: .5rem 0 .75rem; padding: 0; }
.cd-chosen li {
  display: flex; align-items: flex-start; gap: .5rem;
  padding: .4375rem .5rem; margin-bottom: .25rem;
  background: var(--surface-2); border-radius: var(--r-ctl);
  font-size: .8125rem; line-height: 1.45; color: var(--ink-2);
}
.cd-chosen li span { flex: 1; }
.cd-x {
  flex: none; border: 0; background: none; cursor: pointer;
  color: var(--sub-2); font-size: 1.125rem; line-height: 1; padding: 0 .125rem;
}
.cd-x:hover { color: var(--ink); }

.cd-sugg h4 {
  margin: .5rem 0 .375rem; font-size: .6875rem; text-transform: uppercase;
  letter-spacing: .07em; color: var(--sub-2);
}
/* Each suggestion is a whole sentence plus why it was offered, so a coach who
   does not know which numbers matter can still pick a real one. */
.cd-add {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: .4375rem .5625rem; margin-bottom: .25rem;
  border: 1px solid var(--line); border-radius: var(--r-ctl);
  background: var(--card); font: inherit;
}
.cd-add:hover { border-color: var(--gold); background: #FFFDF7; }
.cd-add span { display: block; font-size: .8125rem; color: var(--ink-2); line-height: 1.4; }
.cd-add em {
  display: block; margin-top: .125rem; font-style: normal;
  font-size: .6875rem; color: var(--sub-2);
}

.cd-actions { display: flex; align-items: center; gap: .625rem; flex-wrap: wrap; }
.cd-saved { font-size: .75rem; color: var(--sub-2); }
.cd-saved.error { color: var(--red); }

.cd-preview-label {
  margin-bottom: .5rem;
  font-size: .6875rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--sub-2);
}

/* ---------------- A4 ----------------
   Fixed geometry, scaled to whatever space the pane has. `zoom` rather than a
   transform so the shrunk page still takes its real height in the layout —
   `transform: scale()` leaves the original box behind and the panel below it
   ends up a page-length away. */

.cd-page, .cd-solo {
  width: 210mm;
  transform-origin: top left;
  zoom: var(--cd-zoom, 1);
}
/* Both wrappers, not just .cd-page. A player gets the card with no editor
   beside it and no .cd-page around it — scaling only the
   coach's preview left the athlete's own view stuck at a fixed 210mm and
   scrolling sideways on a phone, which is the one device they will open it on. */
@media (max-width: 1500px) { .cd-page, .cd-solo { --cd-zoom: .86; } }
@media (max-width: 1320px) { .cd-page, .cd-solo { --cd-zoom: .74; } }
@media (max-width: 1180px) { .cd-page, .cd-solo { --cd-zoom: .62; } }

.cd-sheet {
  box-sizing: border-box;
  width: 210mm; min-height: 297mm;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: 4px;
  box-shadow: 0 1px 2px rgba(20, 32, 47, .06), 0 8px 24px rgba(20, 32, 47, .07);
  padding: 16mm 15mm 12mm;
  display: flex; flex-direction: column;
}
.cd-solo { margin: 0 auto; }

/* ---------------- the card itself ---------------- */

.cd-head {
  display: flex; align-items: center; gap: 1rem;
  padding-bottom: .875rem;
  /* Navy over gold: the club's two colours, in the order they appear on the
     crest, rather than a single rule that could belong to any document. */
  border-bottom: 2.5px solid var(--navy);
  box-shadow: 0 2.5px 0 var(--gold);
  margin-bottom: 2.5px;
}
.cd-photo {
  width: 4.25rem; height: 4.25rem; border-radius: var(--r-pill);
  object-fit: cover; flex: none; border: 2px solid var(--line);
}

/* The photo control in the editor. The preview reuses .cd-photo so a coach is
   looking at the same crop the card will print, not an approximation of it. */
.cd-photo-row { display: flex; gap: 1rem; align-items: flex-start; }
.cd-photo-empty {
  background: var(--wash);
  border-style: dashed;
}
.cd-photo-acts { flex: 1; min-width: 0; display: flex; flex-wrap: wrap; gap: .5rem; }
/* A file input cannot be styled, so the label is the button and the input
   inside it stays hidden. Clicking the label opens the picker either way. */
.cd-photo-pick { cursor: pointer; }
.cd-photo-pick input { position: absolute; width: 0; height: 0; opacity: 0; }
.cd-photo-acts .cd-ai-note { flex-basis: 100%; margin: 0; }
.cd-who { flex: 1; min-width: 0; }
.cd-who h1 {
  margin: 0; font-size: 2rem; line-height: 1.05; color: var(--navy);
  letter-spacing: -.022em;
}
.cd-sub { margin: .25rem 0 0; font-size: .8125rem; color: var(--sub); letter-spacing: .01em; }

/* The wordmark, stacked so it reads as a mark rather than a caption. */
.cd-mark {
  flex: none; text-align: right; line-height: 1.15;
  font-size: .5625rem; letter-spacing: .18em; color: var(--sub-2);
}
.cd-mark span {
  display: block; font-size: .875rem; letter-spacing: .06em;
  color: var(--navy); font-weight: 700;
}

/* The coach's one line. It is the only sentence above the numbers, so it is
   given room and a gold rule rather than being left to float as body text. */
.cd-headline {
  margin: .875rem 0 0; padding-left: .6875rem;
  font-size: 1.1875rem; font-weight: 600;
  color: var(--navy-2); letter-spacing: -.012em; line-height: 1.35;
  border-left: 3px solid var(--gold);
}

.cd-grid {
  display: grid; grid-template-columns: repeat(3, 1fr);
  gap: .5rem; margin: .875rem 0 0;
}

/* ---- tiles ----
   The box metrics below are load-bearing: this page is 210x297mm and the tiles
   are what fills it, so padding, radius and min-height are unchanged from the
   layout that was proven to fit. Everything added here is colour and weight. */
.cw-tile {
  margin: 0; padding: .75rem .8125rem .875rem;
  background: var(--card, #fff);
  border: 1px solid var(--line-soft);
  border-radius: 12px;
  display: flex; flex-direction: column; gap: .1875rem;
  min-height: 8.75rem;
  position: relative;
  overflow: hidden;
}
/* A domain stripe. Four kinds of number share one page, and without this the
   card reads as twelve unrelated boxes — a coach looking for "the pitching
   ones" has to read every label. The stripe is inside the border rather than a
   border-top, so the radius stays intact and the height does not change. */
.cw-tile::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--line-strong);
}
.cw-tile[data-group="Pitching"]::before { background: var(--navy); }
.cw-tile[data-group="Hitting"]::before  { background: var(--gold); }
.cw-tile[data-group="Power"]::before    { background: var(--green, #2e7d54); }
.cw-tile[data-group="Body"]::before     { background: var(--line-strong); }
/* The stripe sits above the padding, so the first row needs to clear it. */
.cw-tile > .cw-top { padding-top: .125rem; }
.cw-empty {
  align-items: center; justify-content: center;
  border: 1px dashed var(--line-strong); background: transparent;
  color: var(--sub-2); font-size: 1.5rem;
}
.cw-empty::before { display: none; }

/* Two lines reserved whether the label needs them or not, so the big numbers
   sit on the same line across a row instead of stepping down it. */
.cw-top {
  display: flex; align-items: baseline; justify-content: space-between; gap: .375rem;
  min-height: 1.75rem;
}
.cw-label {
  font-size: .625rem; text-transform: uppercase; letter-spacing: .07em;
  color: var(--sub); line-height: 1.35; min-width: 0;
}
.cw-badge {
  flex: none;
  font-size: .5rem; text-transform: uppercase; letter-spacing: .05em;
  color: var(--green); background: var(--green-bg);
  padding: .09375rem .3125rem; border-radius: var(--r-pill); white-space: nowrap;
}

.cw-value { display: flex; align-items: baseline; gap: .1875rem; }
.cw-value b {
  font-size: 2.25rem; line-height: 1; color: var(--navy);
  font-variant-numeric: tabular-nums; letter-spacing: -.035em;
  font-weight: 700;
}
.cw-unit { font-size: .75rem; color: var(--sub); }
.cw-delta { margin-left: auto; font-size: .75rem; color: var(--green); font-weight: 600; }

.cw-spark { display: block; width: 100%; height: 26px; margin-top: .25rem; }

/* Pushed to the bottom so the tiles line up whatever the label above did. */
.cw-plain {
  font-size: .6875rem; color: var(--sub); line-height: 1.4;
  margin-top: auto; padding-top: .3125rem;
}
/* ---------------- progression chart ---------------- */

.cd-chart { margin-top: .875rem; }
.cg-wrap {
  margin: 0; padding: .75rem .875rem .5rem;
  background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: 12px;
}
.cg-head { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; }
.cg-title {
  font-size: .625rem; text-transform: uppercase; letter-spacing: .07em; color: var(--sub);
}
.cg-span { font-size: .625rem; color: var(--sub-2); }
.cg-svg { display: block; width: 100%; height: auto; margin-top: .25rem; }
.cg-ax { font-size: 8px; fill: var(--sub-2); font-variant-numeric: tabular-nums; }

/* ---------------- focus + note ---------------- */

.cd-focus {
  margin-top: .875rem; padding: .75rem .9375rem;
  background: #FFFCF4; border: 1px solid var(--gold-line); border-radius: 10px;
}
.cd-focus h2, .cd-note h2 {
  margin: 0 0 .375rem; font-size: .625rem; text-transform: uppercase;
  letter-spacing: .08em; color: var(--sub-2);
}
.cd-focus ul { margin: 0; padding-left: 1.125rem; }
.cd-focus li { font-size: .8125rem; line-height: 1.55; color: var(--ink-2); margin: .1875rem 0; }

.cd-note {
  margin-top: .875rem; padding: .875rem 1rem;
  background: var(--surface-2); border-left: 3px solid var(--gold);
  border-radius: 0 10px 10px 0;
}
.cd-note p { margin: 0 0 .5rem; font-size: .875rem; line-height: 1.6; color: var(--ink-2); }
.cd-note p:last-child { margin-bottom: 0; }

.cd-foot {
  margin-top: auto; padding-top: .75rem; border-top: 1px solid var(--line-soft);
  font-size: .625rem; color: var(--sub-2); text-align: center; letter-spacing: .04em;
}

@media (max-width: 68rem) {
  .cd-wrap { grid-template-columns: 1fr; }
  .cd-page, .cd-solo { --cd-zoom: .58; }
}
@media (max-width: 40rem) {
  .cd-page, .cd-solo { --cd-zoom: .42; }
}

/* ---------------- printing the card ----------------
   One A4 page, and only the card on it. The controls, the app chrome and the
   preview label are scaffolding for building the thing, not part of it. */

/* Named so the zero-margin card cannot change ordinary reports or the existing
   three-page athlete sheet. */
@page athlete-card { size: A4 portrait; margin: 0; }

@media print {
  body.card-mode .report-head,
  body.card-mode .ptabs,
  body.card-mode .cd-controls,
  body.card-mode .cd-preview-label,
  body.card-mode #foot { display: none !important; }

  body.card-mode .cd-wrap { display: block; }
  body.card-mode .cd-page,
  body.card-mode .cd-solo { width: auto; zoom: 1; --cd-zoom: 1; }
  body.card-mode .cd-sheet {
    page: athlete-card;
    width: auto; min-height: 0; height: 297mm;
    border: 0; border-radius: 0; box-shadow: none;
    padding: 14mm 14mm 10mm;
    break-inside: avoid; page-break-inside: avoid;
  }

  /* The gold rule, the green badges and the amber focus lines all carry
     meaning; a greyscale card loses which number was worth being proud of. */
  .cd-sheet, .cd-head, .cd-note, .cd-focus, .cw-tile, .cw-badge, .cw-delta,
  .cg-wrap, .cd-mark span {
    -webkit-print-color-adjust: exact; print-color-adjust: exact;
  }
}

/* ---------------- connection failure ----------------
   Shown in place of a roster the app could not fetch. Deliberately not styled
   as a destructive error: the message is that nothing is lost, so it reads as
   an interruption rather than an alarm. */

.conn-error {
  max-width: 34rem;
  margin: 3rem auto;
  padding: 1.5rem 1.75rem;
  background: var(--card);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: var(--r-card);
}
.conn-error h2 { margin: 0 0 .5rem; font-size: 1.125rem; color: var(--navy); }
.conn-error p { margin: 0 0 .75rem; font-size: .875rem; line-height: 1.6; color: var(--ink-2); }
.conn-detail { color: var(--sub); font-size: .8125rem !important; }

/* ---------------- roster group tabs ---------------- */

/* One row of groups above the roster. Scrolls sideways rather than wrapping to
   a second line, so the roster below never shifts down as groups are added. */
.rtabs {
  display: flex; gap: var(--s-1); margin: 0 0 var(--s-3);
  overflow-x: auto; padding-bottom: 2px;
}
.rtab {
  display: inline-flex; align-items: center; gap: .4rem; flex: none;
  padding: .4rem .75rem; border-radius: var(--r-pill); cursor: pointer;
  font: inherit; font-size: .8125rem; font-weight: 600;
  color: var(--sub); background: var(--card);
  border: 1px solid var(--line);
}
.rtab:hover { color: var(--navy); border-color: var(--navy); }
.rtab.on { background: var(--navy); border-color: var(--navy); color: #fff; }
.rtab-n {
  font-variant-numeric: tabular-nums; font-size: .75rem;
  padding: 0 .35rem; border-radius: var(--r-pill);
  background: var(--wash); color: var(--sub);
}
.rtab.on .rtab-n { background: rgba(255, 255, 255, .22); color: #fff; }

/* ---------------- staff access ---------------- */

/* Group checkboxes stack rather than run across the cell — a coach reads down
   this column asking "which of these is ticked", and a wrapped row makes that
   a scan instead of a glance. */
.stf-groups { display: flex; flex-direction: column; gap: .2rem; align-items: flex-start; }
.stf-groups .adm-sub { display: block; }
.stf-groups .adm-sub.bad { color: var(--bad, #C24444); font-weight: 600; }
.stf-scope { min-width: 12rem; }
.stf-scope + .adm-sub { display: block; margin-top: .2rem; }
/* A grant to a retired group: still shown, still removable, visibly inert. */
.stf-retired span { color: var(--sub); }
.stf-retired em { font-style: normal; opacity: .85; }

/* ---------------- skill profile radar ---------------- */

.skill-panel { text-align: center; }
.skill-h {
  margin: 0 0 var(--s-2); font-family: var(--display); font-weight: 700;
  font-size: .9375rem; letter-spacing: .06em; text-transform: uppercase; color: var(--sub);
}
/* The viewBox is 1.5:1 (label room), so the cap is on the whole canvas rather
   than on the chart — 22rem here drew a radar half the size it used to be. */
.skill-radar { display: block; margin: 0 auto; max-width: 32rem; }
/* The sample size is part of the chart, so it never sits far from it. */
.skill-note { margin: var(--s-2) 0 0; font-size: .75rem; color: var(--sub); }
/* THE CAVEAT IS CONTEXT, NOT A HEADLINE.
   Gold at 600 weight and full size made the sample-size sentence the loudest
   thing in the panel, which put the reader's eye on the disclaimer rather than
   on the percentiles it qualifies. Same colour, same words, one step down in
   size and weight: still plainly a caution, no longer the first thing read. */
.skill-note.small { color: var(--amber); font-weight: 500; font-size: .6875rem; }
@media print {
  .skill-radar { max-width: 26rem; }
}
/* On the printed sheet the radar sits above the glossary in the right column. */
.sh-skill { margin: 0 0 var(--s-4); padding-bottom: var(--s-3); border-bottom: 1px solid var(--line-soft); }
.cd-skill { margin-top: var(--s-3); }

/* ---- the movement plot, when chosen as a card chart ----
   Framed like the progression chart so two different charts on one card read as
   the same kind of object rather than two designs sharing a page. */
.cd-plot {
  margin: .875rem 0 0; padding: .75rem .875rem .5rem;
  background: var(--surface-2); border: 1px solid var(--line-soft); border-radius: 12px;
}
.cd-plot svg { display: block; width: 100%; height: auto; margin: 0 auto; }

/* Two charts have to share the width, or the second one pushes the coach's
   note off a page that is exactly one side of A4. Side by side above the
   narrow breakpoint, stacked below it. */
.cd-sheet .cd-chart + .cd-skill,
.cd-sheet .cd-chart + .cd-plot,
.cd-sheet .cd-skill + .cd-chart,
.cd-sheet .cd-skill + .cd-plot,
.cd-sheet .cd-plot + .cd-chart,
.cd-sheet .cd-plot + .cd-skill { margin-top: .5rem; }

/* Keep a chart whole across a page break rather than splitting it in half. */
@media print {
  .cd-chart, .cd-skill, .cd-plot { break-inside: avoid; page-break-inside: avoid; }
}
.skill-card { padding: var(--s-4); margin: var(--s-4) 0; }

/* Per-axis detail: each percentile beside the group it is a percentile of. */
.skill-rows { list-style: none; margin: var(--s-3) 0 0; padding: 0; text-align: left; }
.skill-row {
  display: flex; justify-content: space-between; gap: var(--s-3);
  padding: .25rem 0; font-size: .75rem; border-bottom: 1px solid var(--line-soft);
}
.skill-row:last-child { border-bottom: 0; }
.skill-row-l { font-weight: 700; color: var(--ink); white-space: nowrap; }
/* The metric is the subject of the row and the comparison is what is said
   about it, so the label leads at .75rem/700 and the comparison follows a step
   below it. The percentile keeps a semibold of its own — it is the reading, and
   it has to survive being the smaller text on the line. */
.skill-row-v { color: var(--sub); text-align: right; font-size: .6875rem; font-weight: 400; }
.skill-row-v b { font-weight: 600; }
.skill-row.small .skill-row-v { color: var(--amber); font-weight: 400; }
.skill-row.none .skill-row-v { color: var(--sub-2); font-style: italic; }
/* A cohort of one. Not amber like a small sample — there is no sample yet, and
   colouring it as a warning implies the reading is poor rather than lonely. */
.skill-row.alone .skill-row-v { color: var(--sub-2); }
/* Where each half of a hitter's chart came from. Two measurements, two days —
   printed together so they are never read as one afternoon. */
.skill-dates {
  margin: var(--s-1) 0 0; font-size: .75rem; color: var(--sub-2);
  font-variant-numeric: tabular-nums;
}
/* Said once, under the dates. Amber rather than red: the readings are both
   real and both the newest of their kind, they are simply far apart. */
.skill-gap {
  margin: var(--s-1) 0 0; font-size: .75rem; color: var(--amber); font-weight: 600;
}
/* Which source is missing and what would fill it — guidance, not an error. */
.skill-missing { margin: var(--s-1) 0 0; font-size: .75rem; color: var(--sub); }

/* Why there is no chart, in the space the chart would have taken. */
.skill-panel.empty { text-align: left; }
.skill-empty {
  margin: 0; font-size: .8125rem; line-height: 1.5; color: var(--sub);
}

/* A hitter's sheet page one: the skill profile is the page, not a sidebar
   panel, because there is no bullpen beside it to share the width with. */
.sh-skill-only { max-width: 34rem; margin: var(--s-5) auto 0; }

/* Inviting a colleague: three fields on one line, group tickboxes when scoped. */
.stf-invite { padding: var(--s-4); margin: var(--s-4) 0; }
.adm-h { margin: 0 0 var(--s-2); font-size: 1rem; font-weight: 700; color: var(--ink); }
.stf-invite-row { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: flex-end; }
.stf-invite-row .adm-f { flex: 1 1 12rem; }
.stf-invite-teams { display: flex; flex-wrap: wrap; gap: var(--s-3); margin: var(--s-3) 0 0; }
.stf-invite #stf-invite-send { margin-top: var(--s-3); }
/* A pending invitation is a state, not a fault — sub-coloured, not red. */
.adm-mini { padding: .2rem .55rem; font-size: .75rem; margin-top: .25rem; }
/* The signed-out account screens reuse the auth shell. */
.auth-note { margin: 0 0 var(--s-3); font-size: .875rem; color: var(--sub); line-height: 1.5; }

/* ---------------- standardized athlete card ----------------
   Fixed rows, so two cards can be read side by side. The grid is always five
   cells wide whether or not the data exists — a card that changed shape when a
   session was missing would defeat the point of standardizing it. */
.sc-bar {
  display: flex; align-items: center; gap: var(--s-2);
  flex-wrap: wrap; margin-bottom: var(--s-3);
}
.sc-bar .btn { margin-left: auto; }

.sc-card {
  background: #fff;
  border-radius: var(--r-card, 14px);
  box-shadow: 0 1px 3px rgba(16, 24, 40, .08), 0 8px 24px rgba(16, 24, 40, .06);
  padding: var(--s-4, 22px);
  max-width: 940px;
}
.sc-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--s-3); border-bottom: 2px solid var(--navy); padding-bottom: var(--s-2);
}
.sc-head h1 {
  margin: 0; font-size: 1.5rem; line-height: 1.1; color: var(--navy);
  letter-spacing: -.01em; text-transform: uppercase;
}
.sc-sub { margin: 4px 0 0; font-size: .8125rem; color: var(--muted, #5a6478); }
.sc-kind {
  font-size: .6875rem; font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--navy);
  background: var(--gold); border-radius: var(--r-pill, 999px);
  padding: 5px 10px; white-space: nowrap;
}

.sc-grid {
  display: grid; grid-template-columns: repeat(5, 1fr);
  gap: var(--s-2); margin: var(--s-3) 0;
}
.sc-cell {
  background: var(--surface-3, #f3f5fa);
  border-radius: var(--r-ctl, 10px);
  padding: 12px 14px; min-width: 0;
}
.sc-cell b {
  display: block; font-size: 1.625rem; line-height: 1.05;
  color: var(--navy); font-variant-numeric: tabular-nums;
}
/* A graded number carries a colour class from the model, which also brings the
   small pill sizing used in tables. Here it is a headline: keep the colour,
   take back the size. */
.sc-cell b span {
  font-size: inherit; font-weight: inherit; line-height: inherit;
  background: none; padding: 0; border-radius: 0;
}
.sc-cell span {
  display: block; margin-top: 3px; font-size: .6875rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--muted, #5a6478);
}
.sc-cell i { display: block; margin-top: 2px; font-size: .6875rem; color: var(--muted, #5a6478); font-style: normal; }
/* An absent number is shown, not hidden — "we have not measured this" is
   information, and a missing cell would misalign two cards being compared. */
.sc-cell.sc-empty b { color: var(--muted, #9aa3b5); }

.sc-body { display: grid; grid-template-columns: minmax(280px, 340px) 1fr; gap: var(--s-3); align-items: start; }
.sc-radar { min-width: 0; }
.sc-detail { min-width: 0; }
.sc-detail h2 {
  margin: 0 0 var(--s-2); font-size: .75rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--navy);
}
.sc-table { width: 100%; border-collapse: collapse; font-size: .8125rem; }
.sc-table th {
  text-align: left; font-size: .625rem; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted, #5a6478); padding: 0 8px 6px 0; border-bottom: 1px solid var(--line, #e4e8f0);
}
.sc-table td { padding: 7px 8px 7px 0; border-bottom: 1px solid var(--line, #eef1f6); }
.sc-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.sc-none { color: var(--muted, #5a6478); font-size: .8125rem; padding: var(--s-2) 0; }
.sc-foot {
  margin-top: var(--s-3); padding-top: var(--s-2);
  border-top: 1px solid var(--line, #e4e8f0);
  font-size: .6875rem; color: var(--muted, #7a8397);
}

/* Five cells: 3+2 reads far better than 2+2+1 when the row has to wrap. */
@media (max-width: 980px) {
  .sc-grid { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 860px) {
  .sc-body { grid-template-columns: 1fr; }
}
@media (max-width: 560px) {
  .sc-grid { grid-template-columns: repeat(2, 1fr); }
  .sc-cell b { font-size: 1.375rem; }
}
@media print {
  .sc-bar, .no-print { display: none !important; }
  .sc-card { box-shadow: none; max-width: none; padding: 0; }
}

/* ---------------- skill profile history ----------------
   Secondary to the radar above it: closed by default, and styled as a panel
   rather than as another chart competing for the same attention. */
.hist-card { padding: 0; overflow: hidden; }
.hist-card > summary {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-3); cursor: pointer; list-style: none;
}
.hist-card > summary::-webkit-details-marker { display: none; }
.hist-card > summary::after {
  content: ""; margin-left: auto; flex: none;
  width: 6px; height: 6px;
  border-right: 1.5px solid var(--sub, #5a6478);
  border-bottom: 1.5px solid var(--sub, #5a6478);
  transform: translateY(-2px) rotate(45deg);
  transition: transform 120ms ease;
}
.hist-card[open] > summary::after { transform: translateY(1px) rotate(-135deg); }
.hist-head { display: flex; align-items: center; gap: var(--s-2); }
.hist-h { margin: 0; font-size: .9375rem; color: var(--navy); }
.hist-count { font-size: .75rem; color: var(--sub-2, #7a8397); }
.hist-kind { display: flex; align-items: center; gap: 6px; }
.hist-body { padding: 0 var(--s-3) var(--s-3); }
.hist-bar {
  display: flex; align-items: center; gap: var(--s-2);
  flex-wrap: wrap; margin-bottom: var(--s-2);
}
.hist-table { width: 100%; }
.hist-axis { font-weight: 600; }
.hist-n { font-size: .75rem; color: var(--sub-2, #7a8397); white-space: nowrap; }
.hist-n em { font-style: normal; color: var(--amber, #b5760a); }
/* A move, coloured by direction. Percentiles are the comparable thing, so this
   is a change in standing rather than in a raw reading. */
.hist-move { font-weight: 600; white-space: nowrap; }
.hist-move.up { color: var(--green, #2e7d54); }
.hist-move.down { color: var(--red, #b4443c); }
.hist-move.flat { color: var(--sub-2, #7a8397); font-weight: 500; }
/* An axis with nothing on one side. Deliberately quiet and deliberately
   present: a gap is information, and hiding it would make the comparison look
   more complete than it is. */
.hist-gap { color: var(--sub-2, #9aa3b5); font-style: italic; }
.hist-note {
  margin: var(--s-2) 0 0; font-size: .75rem; line-height: 1.5;
  color: var(--sub-2, #7a8397);
}
@media print { .hist-card { display: none; } }

/* ---------------- the coach's note ----------------
   Beside the generated columns, and visibly a different kind of thing: the
   model's text is evidence with numbers attached, this is somebody's opinion.
   The gold rule and the label are what say so without a sentence explaining it. */
.note-card { border-left: 3px solid var(--gold); }
.note-head {
  display: flex; align-items: center; gap: var(--s-2);
  margin-bottom: var(--s-2);
}
.note-h {
  margin: 0; font-size: .75rem; font-weight: 700;
  letter-spacing: .07em; text-transform: uppercase; color: var(--navy);
}
.note-tag {
  font-size: .625rem; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  color: var(--navy); background: var(--gold);
  padding: 2px 7px; border-radius: var(--r-pill, 999px); white-space: nowrap;
}
.note-head .btn { margin-left: auto; }
.note-body p { margin: 0 0 .5rem; font-size: .9375rem; line-height: 1.6; color: var(--ink-2); }
.note-body p:last-child { margin-bottom: 0; }
.note-by {
  margin: var(--s-2) 0 0; font-size: .75rem; color: var(--sub-2, #7a8397);
}
.note-empty .note-head { margin-bottom: 0; }
.note-input {
  width: 100%; box-sizing: border-box; resize: vertical;
  font: inherit; font-size: .9375rem; line-height: 1.6;
  padding: .625rem .75rem;
  border: 1px solid var(--line); border-radius: var(--r-ctl, 10px);
  background: var(--surface-2);
}
.note-input:focus { outline: 2px solid var(--gold); outline-offset: 1px; }
.note-acts { display: flex; gap: .5rem; margin-top: var(--s-2); flex-wrap: wrap; }
.note-acts .note-clear { margin-left: auto; color: var(--red, #b4443c); }

/* On paper the note prints with everything else, but its controls do not. */
@media print {
  .note-acts, .note-head .btn, .note-empty { display: none !important; }
  .note-card { break-inside: avoid; page-break-inside: avoid; }
}

/* The coach's note on the printed sheet. Same idea as on screen: a gold rule
   marks the one part of the page a person wrote rather than a model. */
.sh-note {
  margin-top: 6mm; padding: 3mm 4mm;
  border-left: 1mm solid var(--gold); background: #FFFCF4;
  break-inside: avoid; page-break-inside: avoid;
}
.sh-note-body p { margin: 0 0 2mm; }
.sh-note-body p:last-child { margin-bottom: 0; }
.sh-note-by { margin-top: 2mm; font-size: 8pt; color: var(--sub-2, #7a8397); }

/* ---------------- Uplift biomechanics ----------------
   A page about a player's body, so it is deliberately quiet: no red for a
   raised observation, no green for an absent one, no traffic lights anywhere.
   The only saturated colour on the page is the three segment colours, which
   identify a line on a chart rather than judging anything. */

.up-bar {
  display: flex; align-items: center; gap: var(--s-2);
  flex-wrap: wrap; margin-bottom: var(--s-3);
}
.up-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-3); }
.up-h { margin: 0; font-size: 1.125rem; letter-spacing: -0.01em; }
.up-card { margin-top: var(--s-3); }
.up-sh {
  margin: 0 0 var(--s-3); font-size: .8125rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--sub);
}
.up-gh {
  margin: var(--s-3) 0 var(--s-2); font-size: .75rem; letter-spacing: .05em;
  text-transform: uppercase; color: var(--sub-2, #7a8397);
}

/* Section 1. The percentage is the headline, so it is the only number on the
   page at this size — everything else is read after it, not alongside it. */
.up-seq-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--s-4); align-items: start;
}
.up-big b {
  display: block; font-size: 3rem; line-height: 1;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.up-big span { display: block; margin-top: var(--s-2); font-size: .8125rem; color: var(--sub); }
.up-order { display: block; font-size: 1.125rem; margin-top: .15rem; }
.up-otherseq { margin: var(--s-2) 0 0; padding-left: 1rem; font-size: .75rem; color: var(--sub-2, #7a8397); }

/* Figures. Tabular numerals so a column of them lines up on the decimal. */
.up-figs { display: flex; flex-wrap: wrap; gap: var(--s-4) var(--s-5); margin-bottom: var(--s-3); }
.up-fig b {
  display: block; font-size: 1.75rem; line-height: 1.1;
  font-variant-numeric: tabular-nums; color: var(--ink);
}
.up-fig-none b { color: var(--sub-2, #7a8397); }
.up-fu { font-size: .8125rem; margin-left: .2rem; color: var(--sub); font-weight: 400; }
.up-fl { display: block; margin-top: .2rem; font-size: .8125rem; color: var(--ink-2); }
.up-fn { display: block; font-size: .6875rem; color: var(--sub-2, #7a8397); }

/* Uplift reference band. Secondary to the number on purpose — a small tinted
   pill rather than a filled card, so eight metrics do not turn the page into a
   traffic light. The status word is always rendered beside the dot: colour is
   reinforcement, never the message. */
.up-ref {
  display: inline-flex; align-items: center; gap: .375rem;
  margin-top: .5rem; padding: .125rem .5rem .1875rem;
  border-radius: 999px; font-size: .6875rem; font-weight: 600;
  line-height: 1.5; white-space: nowrap; border: 1px solid transparent;
}
.up-ref:focus-visible { outline: 2px solid var(--accent, #2f6fd0); outline-offset: 2px; }
.up-dot { width: .375rem; height: .375rem; border-radius: 50%; background: currentColor; flex: none; }

.up-ref-high { color: #1a6b3c; background: #e6f4ec; border-color: #bfe0cd; }
.up-ref-mid  { color: #7a5a12; background: #fbf1dc; border-color: #ecd9ac; }
.up-ref-low  { color: #96341f; background: #fbe9e4; border-color: #f0c9bd; }
/* A MIDDLE metric sitting in the middle. Deliberately colourless — green would
   say the centre of the population is the good place to be, which is the one
   thing Uplift declines to say about these metrics. Reuses the page's own ink
   and control ground rather than introducing a fourth hue. */
.up-ref-neutral { color: var(--ink-2, #2C3E55); background: var(--surface-3, #EFF3F9); border-color: var(--line, #DEE5EE); }

.up-rn { display: block; margin-top: .25rem; font-size: .625rem; color: var(--sub-2, #7a8397); }
.up-rn-none { font-style: italic; }

/* The published p25–p75 range, under the percentile line. One step quieter
   again: it is the context for the label above it, not a second reading. */
.up-rr {
  display: block; margin-top: .0625rem; font-size: .625rem;
  color: var(--sub-2, #7a8397); font-variant-numeric: tabular-nums;
}

/* "Could improve". Deliberately not a card — a rule and two small lines, so it
   reads as a footnote to the sections above rather than as the conclusion of
   the page. No icon and no red: the colour a metric already earned upstream is
   the only colour on this page that means anything. */
.up-improve { margin: var(--s-4) 0 0; padding-top: var(--s-3); border-top: 1px solid var(--line); }
.up-ih {
  margin: 0 0 var(--s-2); font-size: .625rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase; color: var(--sub-2, #7a8397);
}
.up-irow { margin: 0; display: flex; flex-wrap: wrap; align-items: baseline; gap: .25rem .5rem; }
.up-iname { font-size: .875rem; color: var(--ink); }
.up-ival { font-size: .75rem; color: var(--sub); font-variant-numeric: tabular-nums; }
.up-inote { margin: .1875rem 0 0; font-size: .6875rem; color: var(--sub-2, #7a8397); }
.up-inone { margin: 0; font-size: .75rem; color: var(--sub-2, #7a8397); }

@media (prefers-color-scheme: dark) {
  .up-ref-high { color: #8fd9b0; background: #14301f; border-color: #24523a; }
  .up-ref-mid  { color: #e3c47c; background: #2f2712; border-color: #55492a; }
  .up-ref-low  { color: #f0a894; background: #331a14; border-color: #5d3227; }
  .up-ref-neutral { color: #b9c4d2; background: #1b2430; border-color: #2f3c4c; }
}

/* Charts. */
.up-chart { margin: var(--s-3) 0 var(--s-2); }
.up-chart .trend-svg { width: 100%; height: auto; }
.up-keys {
  display: flex; align-items: center; gap: var(--s-3);
  flex-wrap: wrap; font-size: .6875rem; color: var(--sub-2, #7a8397);
}
.up-key { display: inline-flex; align-items: center; gap: .3rem; color: var(--ink-2); }
.up-key i { width: .625rem; height: .625rem; border-radius: 2px; display: inline-block; }
.up-unit { margin-left: auto; }
.up-band-note { flex-basis: 100%; }

/* Observations. Left-ruled in the neutral line colour, NOT in red: this is a
   list of things a sensor noticed, and colouring it as an alert would turn a
   measurement into a verdict on a person. */
.up-flags { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-3); }
.up-flags li {
  padding-left: var(--s-3); border-left: 2px solid var(--line);
}
.up-flag-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-2); }
.up-flag-t { font-weight: 600; font-size: .9375rem; }
.up-flag-pct { font-variant-numeric: tabular-nums; font-size: .9375rem; color: var(--ink-2); }
.up-flag-b { display: block; margin-top: .15rem; font-size: .8125rem; color: var(--sub); }
.up-flag-n { display: block; margin-top: .15rem; font-size: .6875rem; color: var(--sub-2, #7a8397); }

/* One cell per capture, in order: which pitches, not only how many. */
.up-strip { display: flex; gap: 2px; margin-top: var(--s-2); }
.up-strip i {
  flex: 1 1 auto; min-width: 3px; height: .75rem; border-radius: 1px;
  background: var(--surface-3);
}
.up-strip i.on { background: var(--navy-2); }
/* Not reported. Deliberately fainter than "checked and not detected" and not a
   colour of its own: it is an absence of information, not a third finding. */
.up-strip i.unknown { background: transparent; border: 1px dashed var(--line-strong); }

.up-phase + .up-phase { margin-top: var(--s-4); }
.up-quiet { margin-top: var(--s-4); font-size: .8125rem; color: var(--sub); }
.up-quiet summary { cursor: pointer; color: var(--ink-2); }
.up-quiet p { margin: var(--s-2) 0 0; color: var(--sub-2, #7a8397); }
/* Partially reported zeroes are shown, not folded away: the whole point of the
   group is the denominator, and a coach cannot check a denominator they have to
   click to see. */
.up-partial .up-flags { margin-top: var(--s-3); }

.up-note { margin: var(--s-3) 0 0; font-size: .75rem; color: var(--sub-2, #7a8397); line-height: 1.55; }
.up-none {
  padding: var(--s-3); border: 1px dashed var(--line); border-radius: var(--r-ctl, 10px);
  font-size: .8125rem; color: var(--sub);
}
.up-source { margin: var(--s-4) 0 0; font-size: .6875rem; color: var(--sub-2, #7a8397); }

/* The formats list on the Upload page. */
.upload-page { margin-bottom: var(--s-4); }
.up-formats dd .sub { display: block; color: var(--sub-2, #7a8397); font-size: .8125rem; }

@media (max-width: 700px) {
  .up-seq-grid { grid-template-columns: minmax(0, 1fr); }
  .up-big b { font-size: 2.25rem; }
}

/* The biomechanics block on the printed sheet, where the metric glossary was.
   Sized to that same quarter-column: four figures, a date, and what Uplift
   noted. Print units, because this page is A4 first and a screen second. */
.sh-bio-when {
  margin: 0 0 2mm; font-size: 8pt; color: var(--sub-2, #7a8397);
}
.sh-bio { margin: 0; }
.sh-bio > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 3mm; padding: 1.2mm 0; border-bottom: 0.2mm solid var(--line);
}
.sh-bio > div:last-child { border-bottom: 0; }
.sh-bio dt { font-size: 9pt; color: var(--ink-2); }
.sh-bio dd { margin: 0; text-align: right; }
.sh-bio dd b { font-size: 11pt; font-variant-numeric: tabular-nums; }
.sh-bio dd span { display: block; font-size: 7.5pt; color: var(--sub-2, #7a8397); }
.sh-bio-flags { margin-top: 3mm; }
.sh-bio-fh {
  display: block; font-size: 7.5pt; letter-spacing: .06em; text-transform: uppercase;
  color: var(--sub-2, #7a8397); margin-bottom: 1.5mm;
}
.sh-bio-flag {
  display: inline-flex; align-items: baseline; gap: 1.5mm;
  margin: 0 2mm 1.5mm 0; padding: 0.8mm 2mm;
  border: 0.2mm solid var(--line); border-radius: 1mm;
  font-size: 8pt; color: var(--ink-2);
}
.sh-bio-flag i { font-style: normal; font-variant-numeric: tabular-nums; color: var(--sub); }
.sh-bio-note { display: block; margin-top: 1mm; font-size: 7pt; color: var(--sub-2, #7a8397); }
.sh-bio-none { font-size: 9pt; }

/* ---------------- "which athlete is this file for?" ----------------
   Calm, not alarming: this is a normal step in a normal upload, not an error
   the coach caused. Left-ruled in the accent rather than the red used for
   failures, and sitting inside the upload zone rather than over it — a modal
   would say something went wrong. */
.rsv {
  margin-top: var(--s-4); padding: var(--s-4);
  text-align: left;
  border: 1px solid var(--line); border-left: 3px solid var(--navy-2);
  border-radius: var(--r-ctl, 10px); background: var(--surface);
}
.rsv-h {
  margin: 0 0 var(--s-3); font-size: .8125rem; font-weight: 700;
  letter-spacing: .04em; text-transform: uppercase; color: var(--sub);
}
.rsv-label { display: block; font-size: .75rem; color: var(--sub-2, #7a8397); }
.rsv-file { margin-bottom: var(--s-4); }
.rsv-name { display: block; margin-top: .1rem; font-size: 1.125rem; color: var(--ink); }
.rsv-src { display: block; margin-top: .15rem; font-size: .6875rem; color: var(--sub-2, #7a8397); }
.rsv-ask { margin-bottom: var(--s-2); }

.rsv-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--s-2); }
.rsv-opt, .rsv-create {
  width: 100%; text-align: left; cursor: pointer;
  padding: var(--s-3); font: inherit;
  border: 1px solid var(--line); border-radius: var(--r-ctl, 10px);
  background: var(--surface-2, #fff); color: var(--ink);
  display: flex; flex-wrap: wrap; align-items: baseline; gap: var(--s-2);
}
.rsv-opt:hover, .rsv-create:hover { border-color: var(--line-strong); }
/* The chosen one is stated with a border and a tint, not with a tick that
   disappears at a glance — this is the single decision on the screen. */
.rsv-opt.on, .rsv-create.on {
  border-color: var(--navy-2); box-shadow: inset 0 0 0 1px var(--navy-2);
  background: var(--surface-3);
}
.rsv-opt:focus-visible, .rsv-create:focus-visible,
.rsv-more:focus-visible { outline: 2px solid var(--navy-2); outline-offset: 2px; }
.rsv-opt-name { font-weight: 600; }
.rsv-opt-sub { font-size: .8125rem; color: var(--sub); }
.rsv-tag {
  margin-left: auto; font-size: .6875rem; letter-spacing: .03em;
  text-transform: uppercase; color: var(--sub-2, #7a8397);
}

/* Creating somebody is deliberately the secondary path: attaching to an athlete
   already on the roster is nearly always the right answer. */
.rsv-alt { margin-top: var(--s-3); }
.rsv-create { font-size: .875rem; color: var(--sub); }
.rsv-warn { margin: var(--s-2) 0 0; font-size: .75rem; color: var(--sub-2, #7a8397); }
.rsv-none { margin: 0 0 var(--s-3); font-size: .875rem; color: var(--sub); }
.rsv-note { margin: var(--s-3) 0 0; font-size: .8125rem; color: var(--ink-2); }

.rsv-actions { display: flex; gap: var(--s-2); margin-top: var(--s-4); }
/* An action that cannot run must not look like one that can. Until a weak match
   is explicitly resolved this button is disabled, and a full-strength primary
   button with a pointer cursor reads as "press me" — which is the opposite of
   what the disabled state is there to say. */
.rsv-actions .btn[disabled] { opacity: .45; cursor: not-allowed; }
.rsv-more {
  margin-top: var(--s-3); padding: 0; border: 0; background: none;
  font: inherit; font-size: .8125rem; color: var(--navy-2);
  text-decoration: underline; cursor: pointer;
}

@media (max-width: 520px) {
  .rsv-actions { flex-direction: column; }
  .rsv-tag { margin-left: 0; flex-basis: 100%; }
}

/* ---------------- Recruitment Showcase ---------------- */

/* Cards, not a table: a program runs a handful of showcase groups at once, and
   four rows of a four-column table spend more of the page on rules than on the
   counts that are the whole content. */
.shw-grid {
  display: grid; gap: var(--s-3);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.shw-card {
  display: block; padding: var(--s-4); border-radius: var(--r-card);
  background: var(--card); border: 1px solid var(--line); text-decoration: none;
  border-left: 3px solid var(--gold);
}
.shw-card:hover { border-color: var(--line-strong); border-left-color: var(--gold); }
/* Wraps rather than truncating. A showcase is often named for its date and its
   region at once, and an elided name is a group a coach cannot tell apart from
   the one below it. */
.shw-card-name {
  font-weight: 700; font-size: 0.9375rem; color: var(--navy);
  overflow-wrap: anywhere;
}
.shw-card-n {
  font-family: var(--display); font-size: 1.5rem; color: var(--ink);
  line-height: 1.1; margin-top: var(--s-2); font-variant-numeric: tabular-nums;
}
.shw-card-meta { font-size: 0.75rem; color: var(--sub); margin-top: var(--s-1); }
.shw-card-data {
  display: flex; align-items: center; gap: .4rem;
  font-size: 0.75rem; color: var(--sub); margin-top: var(--s-2);
}
.shw-foot { margin-top: var(--s-4); }

/* One dot, two states, used on both the card and every athlete row so "has
   data" looks the same wherever it is answered. */
.shw-dot {
  width: .5rem; height: .5rem; border-radius: 50%; flex: none;
  background: var(--line-strong);
}
.shw-dot.on { background: var(--green); }

.shw-empty { padding: var(--s-6); max-width: 42rem; }
.shw-empty-h { margin: 0 0 var(--s-3); font-size: 1.0625rem; color: var(--navy); }
.shw-empty-p { margin: 0 0 var(--s-3); color: var(--sub); font-size: 0.875rem; line-height: 1.55; }

.shw-back {
  display: inline-block; margin-bottom: var(--s-3);
  font-size: 0.8125rem; font-weight: 600; color: var(--sub); text-decoration: none;
}
.shw-back:hover { color: var(--navy); }

.shw-head { margin-bottom: var(--s-4); }
.shw-eyebrow {
  font-size: 0.6875rem; font-weight: 700; letter-spacing: .08em;
  text-transform: uppercase; color: var(--gold-ink);
}
.shw-title {
  font-family: var(--display); font-size: 1.75rem; color: var(--navy);
  margin: var(--s-1) 0 var(--s-3); line-height: 1.1; overflow-wrap: anywhere;
}
/* Wraps to as many rows as it needs. Four figures fit one line on a laptop and
   two on a phone; neither is a scroll. */
.shw-stats { display: flex; flex-wrap: wrap; gap: var(--s-5); }
.shw-stat { display: flex; flex-direction: column; }
.shw-stat-v {
  font-family: var(--display); font-size: 1.5rem; color: var(--ink);
  line-height: 1; font-variant-numeric: tabular-nums;
}
.shw-stat-l { font-size: 0.6875rem; color: var(--sub); text-transform: uppercase; letter-spacing: .05em; }
.shw-note { margin: var(--s-3) 0 0; font-size: 0.8125rem; color: var(--sub); }

.shw-controls {
  display: flex; gap: var(--s-3); align-items: center; flex-wrap: wrap;
  margin-bottom: var(--s-2);
}
.shw-tabs { margin: 0; }
.shw-search {
  flex: 1; min-width: 10rem; max-width: 18rem;
  padding: .4rem .7rem; border-radius: var(--r-pill);
  border: 1px solid var(--line); background: var(--card);
  font: inherit; font-size: 0.8125rem; color: var(--ink);
}
.shw-search:focus { outline: 2px solid var(--navy); outline-offset: 1px; }

.shw-list { padding: 0; overflow: hidden; }
/* A grid rather than a flex row so the four columns line up down the list and
   the data state stays in one place for the eye to run along.
   Every track is sized from the container, never from its contents: each ROW is
   its own grid container, so a content-sized track (`auto`, or a `minmax` whose
   floor the text can push past) resolves to a different width on every row and
   the columns come out visibly ragged down the page. `minmax(0, Nfr)` and a
   fixed rem width resolve identically for all of them. */
.shw-row {
  display: grid; align-items: baseline; gap: var(--s-2) var(--s-3);
  grid-template-columns: minmax(0, 1.6fr) 4.5rem minmax(0, 1fr) 12rem;
  padding: var(--s-3) var(--s-4); border-bottom: 1px solid var(--line-soft);
  text-decoration: none;
}
.shw-row:last-child { border-bottom: 0; }
.shw-row:hover { background: var(--surface-2); }
.shw-row-name { font-weight: 600; font-size: 0.875rem; color: var(--navy); overflow-wrap: anywhere; }
.shw-row-id { font-size: 0.75rem; color: var(--sub-2); }
.shw-row-meta { font-size: 0.75rem; color: var(--sub); }
.shw-data {
  display: inline-flex; align-items: center; gap: .4rem;
  font-size: 0.75rem; color: var(--sub-2); justify-self: end;
}
.shw-data.on { color: var(--ink-2); }

/* At phone width the four columns become two lines: who they are, then what is
   on file. Nothing is hidden — an athlete's id and level are why a coach can
   tell two people with similar names apart. */
@media (max-width: 34rem) {
  .shw-row {
    grid-template-columns: 1fr auto;
    row-gap: var(--s-1);
  }
  .shw-row-name { grid-column: 1; }
  .shw-row-id { grid-column: 2; justify-self: end; }
  .shw-row-meta { grid-column: 1; }
  .shw-data { grid-column: 2; justify-self: end; }
  .shw-stats { gap: var(--s-4); }
  .shw-search { max-width: none; }
}

/* ---------------- showcase: evaluation board ---------------- */

/* Two views of one group, not two destinations. Underlined-tab shape rather
   than the pill used by the role filters, so the eye reads them as different
   kinds of control: one switches the view, the others narrow it. */
.shw-modes {
  display: flex; gap: var(--s-4); margin: 0 0 var(--s-3);
  border-bottom: 1px solid var(--line);
}
.shw-mode {
  padding: 0 0 var(--s-2); text-decoration: none;
  font-size: 0.875rem; font-weight: 600; color: var(--sub);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.shw-mode:hover { color: var(--navy); }
.shw-mode.on { color: var(--navy); border-bottom-color: var(--gold); }

/* ---------------- showcase: status board and results workbook ---------------- */

/* Completeness is a word per source, not a colour verdict. "Missing" is
   quieter than "Complete" so the eye finds the gaps without being shouted at. */
.shw-status-totals { margin: 0 0 var(--s-3); }
.shw-status-note { margin: 0 0 var(--s-3); max-width: 54rem; }
.ev-table .shw-st-h { padding: var(--s-3) var(--s-4) var(--s-3) var(--s-2); text-align: left; }
/* A person's name breaks between words here, never inside one: the table
   scrolls sideways on a phone rather than squeezing "Castellanos" in half. */
.shw-status .ev-name { overflow-wrap: normal; }
.shw-status .ev-who { min-width: 12rem; }
.shw-st {
  padding: var(--s-3) var(--s-4) var(--s-3) var(--s-2); font-size: 0.8125rem;
  color: var(--ink-2); white-space: nowrap; vertical-align: top;
}
.shw-st-complete { font-weight: 600; color: var(--navy); }
.shw-st-missing { color: var(--sub-2); }
.shw-st-na { color: var(--sub-2); }
.shw-st-n { font-size: 0.75rem; color: var(--sub); }

.shw-wb { padding: var(--s-4) var(--s-5); margin: 0 0 var(--s-4); }
.shw-wb-h { margin: 0 0 var(--s-2); font-size: 0.9375rem; color: var(--navy); }
.shw-wb-p { margin: 0 0 var(--s-3); font-size: 0.875rem; color: var(--sub); line-height: 1.55; max-width: 54rem; }
.shw-wb-controls { display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: end; margin: var(--s-3) 0 0; }
.shw-wb-date { display: flex; flex-direction: column; gap: var(--s-1); font-size: 0.75rem; color: var(--sub); }
.shw-wb-date input {
  font: inherit; font-size: 0.875rem; padding: .4rem .6rem;
  border: 1px solid var(--line-strong); border-radius: var(--r-input, 6px); background: var(--card); color: var(--ink);
}
.shw-wb-error {
  margin: 0 0 var(--s-3); padding: var(--s-3); border-radius: var(--r-card);
  background: #fff4f0; color: #96341f; font-size: 0.8125rem;
}

.shw-pf { font-size: 0.8125rem; line-height: 1.5; }
.shw-pf-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: var(--s-2) var(--s-4); margin: 0 0 var(--s-3); }
.shw-pf-k { display: block; font-size: 0.6875rem; color: var(--sub); text-transform: uppercase; letter-spacing: .05em; }
.shw-pf-sheets, .shw-pf-sources, .shw-pf-problems { margin: 0 0 var(--s-3); padding-left: 1.1rem; }
.shw-pf-sheets li, .shw-pf-problems li { margin: 0 0 var(--s-1); }
.shw-pf-sources li { display: inline-block; margin: 0 var(--s-4) var(--s-2) 0; }
.shw-pf-sources .shw-pf-k { display: inline; margin-right: .3rem; }
.shw-pf-ignored { font-size: 0.75rem; color: var(--sub); }
.shw-pf-h { margin: var(--s-3) 0 var(--s-2); font-size: 0.8125rem; color: var(--navy); }
.shw-pf-confirm { display: flex; gap: .5rem; align-items: baseline; cursor: pointer; }

@media (max-width: 34rem) {
  .shw-wb-controls .btn { flex: 1 1 100%; }
  .shw-pf-grid { grid-template-columns: 1fr; }
}

.ev-caption {
  margin: 0 0 var(--s-3); font-size: 0.8125rem; color: var(--sub);
  line-height: 1.5; max-width: 54rem;
}

/* The table scrolls inside its own box rather than the page, so the group
   header and the filters stay put while a wide board is read across. */
.ev-wrap {
  overflow-x: auto; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--r-card);
}
.ev-table { width: 100%; border-collapse: collapse; font-size: 0.8125rem; }

.ev-h {
  text-align: right; font-weight: 600; color: var(--sub);
  padding: 0; border-bottom: 1px solid var(--line);
  white-space: nowrap; vertical-align: bottom;
}
.ev-who-h { text-align: left; padding: var(--s-3) var(--s-4); }
/* The whole header cell is the control, so the target is the column heading a
   coach is already pointing at rather than a small glyph beside it. */
.ev-sort {
  width: 100%; padding: var(--s-3) var(--s-3) var(--s-3) var(--s-2);
  font: inherit; font-weight: 600; color: var(--sub);
  background: none; border: 0; cursor: pointer; text-align: right;
}
.ev-sort:hover { color: var(--navy); background: var(--surface-2); }
.ev-sort.on { color: var(--navy); }
.ev-sort:focus-visible { outline: 2px solid var(--navy); outline-offset: -2px; }
.ev-arrow { display: inline-block; min-width: .7em; color: var(--gold-ink); }

.ev-r { border-bottom: 1px solid var(--line-soft); }
.ev-r:last-child { border-bottom: 0; }
.ev-r:hover { background: var(--surface-2); }

.ev-who {
  text-align: left; font-weight: 400; padding: var(--s-3) var(--s-4);
  vertical-align: top; min-width: 11rem;
}
.ev-name {
  display: block; font-weight: 600; font-size: 0.875rem;
  color: var(--navy); text-decoration: none; overflow-wrap: anywhere;
}
.ev-name:hover { text-decoration: underline; }
.ev-meta { display: block; font-size: 0.75rem; color: var(--sub); margin-top: 1px; }
/* What qualifies the row: sample size, small-sample flag, date. Quiet, and on
   every row rather than in a footnote nobody reads twice. */
.ev-q { font-size: 0.6875rem; color: var(--sub-2); margin-top: 2px; }

.ev-c {
  text-align: right; padding: var(--s-3); vertical-align: top;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ev-v { font-weight: 600; color: var(--ink); font-size: 0.9375rem; }
.ev-u { font-weight: 500; color: var(--sub-2); font-size: 0.6875rem; margin-left: .15em; }
/* Which pitch a secondary Stuff+ came from. */
.ev-n { display: block; font-size: 0.6875rem; color: var(--sub); font-weight: 400; }
/* Never a dash and never a zero: a reader takes both for a small number. */
.ev-none { color: var(--sub-2); font-size: 0.75rem; font-weight: 400; }

/* Columns drop out as the table narrows, rather than the board becoming a
   spreadsheet that scrolls two athletes apart. Priority 1 always survives. */
@media (max-width: 60rem) {
  .ev-p3 { display: none; }
}
@media (max-width: 44rem) {
  .ev-p2 { display: none; }
  .ev-who { min-width: 0; padding: var(--s-3); }
  .ev-c { padding: var(--s-3) var(--s-2); }
  .ev-sort { padding: var(--s-3) var(--s-2); }
}
/* The hand, beside the role. A recruiting board's first filter, and what makes
   the run column readable. */
.ev-hand {
  margin-left: .4rem; padding: 0 .3rem; border-radius: 3px;
  background: var(--surface-3); color: var(--sub); font-size: 0.6875rem; font-weight: 600;
}

/* ---------------- showcase: recruiting leaders ---------------- */

.ld-caption {
  margin: 0 0 var(--s-4); font-size: 0.8125rem; color: var(--sub);
  line-height: 1.55; max-width: 54rem;
}
.ld-levels { margin: 0 0 var(--s-3); }
.ld-blurb { margin: 0 0 var(--s-3); font-size: 0.8125rem; color: var(--sub); }

/* Small cards, several across on a laptop and one across on a phone. A coach
   reads one board at a time; the grid only decides how many fit beside it. */
.ld-grid {
  display: grid; gap: var(--s-3); margin-bottom: var(--s-5);
  grid-template-columns: repeat(auto-fill, minmax(17rem, 1fr));
}
.ld-card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--r-card); padding: var(--s-4);
}
.ld-h {
  margin: 0 0 var(--s-1); font-size: 0.8125rem; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em; color: var(--navy);
  display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap;
}
/* The one thing on a board that must not be missed: this column is a model
   score against level expectations, not a measurement. */
.ld-tag {
  text-transform: none; letter-spacing: 0; font-size: 0.625rem; font-weight: 700;
  padding: .1rem .35rem; border-radius: var(--r-pill);
  background: var(--amber-bg); color: var(--amber); border: 1px solid var(--gold-line);
}
.ld-sub { margin: 0 0 var(--s-2); font-size: 0.6875rem; color: var(--sub-2); line-height: 1.45; }

.ld-list { list-style: none; margin: 0; padding: 0; }
.ld-row {
  display: grid; grid-template-columns: 1.1rem 1fr auto;
  gap: var(--s-2); align-items: baseline;
  padding: var(--s-2) 0; border-bottom: 1px solid var(--line-soft);
}
.ld-row:last-child { border-bottom: 0; }
.ld-rank {
  font-family: var(--display); font-size: 0.875rem; color: var(--sub-2);
  font-variant-numeric: tabular-nums;
}
.ld-who { min-width: 0; }
.ld-name {
  display: block; font-weight: 600; font-size: 0.875rem;
  color: var(--navy); text-decoration: none; overflow-wrap: anywhere;
}
.ld-name:hover { text-decoration: underline; }
.ld-meta { font-size: 0.6875rem; color: var(--sub); }
.ld-note { display: block; font-size: 0.6875rem; color: var(--sub-2); }
.ld-v {
  font-weight: 700; font-size: 1.0625rem; color: var(--ink);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.ld-u { font-size: 0.6875rem; font-weight: 500; color: var(--sub-2); margin-left: .15em; }
.ld-empty { margin: 0; font-size: 0.75rem; color: var(--sub-2); }

@media (max-width: 34rem) {
  .ld-grid { grid-template-columns: 1fr; }
}
/* The catalogue's own grouping of the testing battery, so fifteen boards read
   as four kinds of test rather than one undifferentiated run. */
.ld-group {
  margin: 0 0 var(--s-2); font-size: 0.6875rem; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase; color: var(--gold-ink);
}

/* Leaders: the tiers. Native <details>, so the disclosure opens on click and on
   Enter, is announced as expandable, and needs no script. */
.ld-more { margin: 0 0 var(--s-5); }
.ld-more-s {
  display: inline-flex; align-items: center; gap: var(--s-2); cursor: pointer;
  font-size: 0.8125rem; font-weight: 600; color: var(--sub);
  padding: var(--s-2) 0; list-style: none;
}
.ld-more-s::-webkit-details-marker { display: none; }
.ld-more-s::before { content: "▸"; color: var(--sub-2); }
.ld-more[open] .ld-more-s::before { content: "▾"; }
.ld-more-s:hover { color: var(--navy); }
.ld-more-n {
  font-size: 0.6875rem; font-variant-numeric: tabular-nums;
  padding: 0 .35rem; border-radius: var(--r-pill);
  background: var(--wash, var(--surface-3)); color: var(--sub);
}
.ld-more-body { padding-top: var(--s-2); }

/* Level-adjusted boards sit apart, because position reinforces the tag: a
   model score beside a velocity board reads as a correction of it. */
.ld-context {
  margin: 0 0 var(--s-5); padding: var(--s-4) var(--s-4) var(--s-2);
  background: var(--surface-2); border: 1px solid var(--line);
  border-radius: var(--r-card);
}
.ld-context-sub { max-width: 46rem; margin-bottom: var(--s-3); }

/* ---------------- Showcase athlete card ----------------
   ONE US LETTER PAGE. The card is sized in inches because that is the paper it
   is printed on, and the print rules below strip everything that is not the
   card so a browser's Print → Save as PDF produces exactly what is on screen.
   A second page is a bug here, not a layout preference. */

.shc-page { display: grid; gap: var(--s-3); justify-items: center; }
.shc-bar { display: flex; justify-content: flex-end; width: 7.5in; max-width: 100%; }

.shc-card {
  width: 7.5in;                 /* letter minus the @page margins below */
  max-width: 100%;
  background: #fff;
  color: #14202F;
  border: 1px solid var(--line);
  box-shadow: var(--elev-2);
  padding: 0.3in 0.35in;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 0.16in;
  font-size: 10pt;
}

.shc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--s-4);
  border-bottom: 2px solid var(--navy); padding-bottom: 0.12in; }
.shc-brand { display: grid; gap: 2px; justify-items: start; }
/* Printed as well as shown: a card is handed to a recruit on paper more often
   than it is read on screen, and a crest that only exists in the browser makes
   the PDF look unfinished. `print-color-adjust` keeps it from being dropped or
   washed out by a printer's ink-saving pass. */
.shc-logo {
  height: 0.42in; width: auto; display: block; margin-bottom: 2px;
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
.shc-prog { font-size: 7.5pt; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sub); }
.shc-who { text-align: right; }
.shc-name { font-size: 19pt; line-height: 1.05; margin: 0; color: var(--navy); }
.shc-idline { display: grid; gap: 1px; margin-top: 3px; }
.shc-id { font-size: 8.5pt; letter-spacing: 0.06em; color: var(--sub); }
.shc-bio { font-size: 9.5pt; font-weight: 600; }

.shc-sec { break-inside: avoid; }
.shc-h { font-size: 8.5pt; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--sub); margin: 0 0 0.07in; border-bottom: 1px solid var(--line); padding-bottom: 3px; }
.shc-note { text-transform: none; letter-spacing: 0; font-weight: 400; }
/* The explainer above the three radars: three short sentences set as prose
   under the heading, rather than crammed into it as a dash-joined aside.
   Sized to stay quiet beside the figures. */
.shc-lede { margin: 0 0 0.08in; font-size: 7.4pt; line-height: 1.35; color: var(--sub);
  max-width: 64ch; }

.shc-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 0.09in; }
.shc-cell { border: 1px solid var(--line); border-radius: 4px; padding: 0.07in 0.09in;
  display: grid; gap: 1px; }
.shc-l { font-size: 7.5pt; color: var(--sub); }
.shc-v { font-size: 13pt; line-height: 1.1; font-variant-numeric: tabular-nums; }
.shc-u { font-size: 7.4pt; color: var(--sub); margin-left: 1px; font-weight: 400; }
/* A dash is a statement that nobody measured this. It is quiet, never red. */
.shc-none { color: var(--sub); font-weight: 400; }

.shc-tbl { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
.shc-tbl th { font-size: 7.5pt; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--sub); text-align: right; font-weight: 600; padding: 2px 4px; }
.shc-tbl th:first-child, .shc-tbl td:first-child { text-align: left; }
.shc-tbl td { font-size: 10pt; padding: 3px 4px; text-align: right;
  border-top: 1px solid var(--line); }
.shc-pt { font-weight: 600; }

.shc-awaiting { font-size: 9.5pt; color: var(--sub); margin: 0; }
.shc-foot { display: flex; justify-content: space-between; font-size: 7.5pt;
  color: var(--sub); border-top: 1px solid var(--line); padding-top: 0.08in; }

@page showcase-card { size: letter portrait; margin: 0.5in; }

@media print {
  /* Only the card prints. Everything else on the page is furniture. */
  /* WHAT LEAVES THE PRINTER IS THE CARD. Named the way sheet-mode names its
     furniture: the card renders inside <main id="app">, so a rule hiding every
     direct child of body that is not the card would hide the card with it. */
  body.showcase-card-mode .topbar,
  body.showcase-card-mode .shw-back,
  body.showcase-card-mode .shc-bar,
  body.showcase-card-mode #foot,
  body.showcase-card-mode .no-print { display: none !important; }
  body.showcase-card-mode .shc-page { display: block; }
  body.showcase-card-mode .shc-card {
    page: showcase-card;
    width: auto;
    border: 0;
    box-shadow: none;
    padding: 0;
    /* The card is one page. Nothing inside it may break onto a second. */
    break-inside: avoid;
    page-break-inside: avoid;
  }
  /* Light ground and real ink, whatever the browser's theme was. */
  body.showcase-card-mode { background: #fff !important; color: #14202F !important; }
  body.showcase-card-mode .shc-card { background: #fff !important; }

  /* ---------------- one card, one page ----------------
     A recruit is handed a sheet, so a card that runs to two is a bug and not a
     layout preference. A showcase that ran four power tests and one that ran a
     force-plate morning produce very different amounts of paper, so the card
     names its own density from how many measured rows it carries — see
     COMPACT_ROWS / DENSE_ROWS in showcasecardviews.js — and these rules set the
     same content more tightly rather than dropping any of it. Nothing below
     hides a measurement, a rank, an axis or a caption.

     REFLOW BEFORE SHRINKING. The saving is structural: a rank row that spent
     two lines on its value and its place spends one, and a battery of eighteen
     tests wraps into two sub-columns instead of one column three times the
     height of the two beside it. Type sizes move very little, and only in the
     dense case. */

  /* A rank row: label on its own line, then the reading and its place beneath.
     This was one line — label, value, place — which fitted while the place read
     "1 of 24". Printing the percentile beside it roughly triples that cell, and
     in a 2.4in column the label's track is what pays: "On-plane efficiency"
     came apart across three lines and "Most arm-side run" broke mid-word.
     Giving the label the full width costs nothing in height, because those
     wrapped labels were already two and three lines tall. */
  .shc-card.compact .shc-rank,
  .shc-card.dense .shc-rank {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "label label" "value place";
    column-gap: 0.10in;
    padding: 1.5px 0;
  }
  .shc-card.compact .shc-rank-p,
  .shc-card.dense .shc-rank-p { min-width: 0.62in; }

  /* The explainer set across the full card rather than a reading measure, and
     led tighter. On screen 64ch keeps it comfortable; in print that measure
     wraps three sentences onto three lines, and a card carrying a full battery
     has no three lines to give — with the percentile now printed on every row
     as well, the two together were exactly one line more than a Letter page
     holds. Nothing is dropped: it is the same three sentences. */
  .shc-card.compact .shc-lede,
  .shc-card.dense .shc-lede {
    font-size: 6.8pt; line-height: 1.25; margin-bottom: 0.05in; max-width: none;
  }

  /* A TWO-SIDED ROW CARRIES FOUR NUMBERS WHERE AN ORDINARY ROW CARRIES TWO,
     so it stacks: label, readings, places, each on the row's full width.
     Sharing one line between the readings and the places starved the value's
     track to zero in a 2.4in column and the readings painted over the places
     — correct numbers, unreadable row. Scoped to `.shc-rank-sides`, so every
     one-sided row on the same card keeps the compact one-line layout, which is
     what leaves the height for this one to spend. */
  .shc-card.compact .shc-rank.shc-rank-sides,
  .shc-card.dense .shc-rank.shc-rank-sides {
    grid-template-columns: minmax(0, 1fr);
    grid-template-areas: "label" "sides";
    row-gap: 0;
  }
  /* A switch hitter's row is three lines where everybody else's is one, and
     that difference is what a Letter page has to absorb. Led tight rather than
     shrunk: the readings keep their size, and only the space between them
     gives — which is the room the page needed and no more. */
  .shc-card.compact .shc-rank.shc-rank-sides .shc-side-line,
  .shc-card.dense .shc-rank.shc-rank-sides .shc-side-line { line-height: 1.1; }
  .shc-card.compact .shc-rank.shc-rank-sides .shc-rank-l,
  .shc-card.dense .shc-rank.shc-rank-sides .shc-rank-l { line-height: 1.15; }

  /* The parent's page prints on the same sheet as the cards it explains. */
  .shg-page { break-inside: avoid; }
  .shg-cols { column-gap: 0.18in; }

  .shc-card.compact, .shc-card.dense { gap: 0.11in; }
  .shc-card.compact .shc-figs, .shc-card.dense .shc-figs { gap: 0.10in; }
  .shc-card.compact .shc-fig, .shc-card.dense .shc-fig { gap: 0.04in; }
  .shc-card.compact .shc-head, .shc-card.dense .shc-head { padding-bottom: 0.08in; }
  .shc-card.compact .shc-foot, .shc-card.dense .shc-foot { padding-top: 0.06in; }
  /* An arsenal is one row per pitch type, so the table is the other thing on
     the card that grows without limit. */
  .shc-card.compact .shc-tbl td, .shc-card.dense .shc-tbl td { padding: 2px 4px; }

  /* Dense: the force-plate battery. A three-column grid gives each list a third
     of the card whatever its length, so eighteen power tests stack into one
     tall column while pitching's four leave two-thirds of that height empty.
     Here the lists stop being columns and become one block that flows across
     the whole card, so the same rows fill three short columns instead of one
     long one. The columns stay the width they already were — narrowing them
     would only make the labels wrap, which costs more height than it saves.
     Each list keeps its name, because once they flow together the figcaption
     above the radar no longer sits over its own rows. */
  .shc-card.dense .shc-cols {
    display: block;
    columns: 3;
    column-gap: 0.18in;
    column-rule: 1px solid var(--line-soft);
  }
  .shc-card.dense .shc-ranks { display: block; }
  .shc-card.dense .shc-ranks-h {
    display: block;
    font-size: 7.5pt; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
    color: var(--navy); margin: 0.06in 0 2px;
    /* A heading orphaned at the foot of a column names the wrong list. */
    break-after: avoid;
    break-inside: avoid;
  }
  .shc-card.dense .shc-ranks:first-child .shc-ranks-h { margin-top: 0; }
  /* A row may not be split down the middle by a column break. */
  .shc-card.dense .shc-rank { break-inside: avoid; }
  /* LARGER, not smaller. The dense card is the one with the most axes on its
     radar, and a spoke label is only readable if there is arc to put it on —
     which grows with the radius. Flowing the lists across the card freed the
     height this costs, so the crowded figure gets the room rather than paying
     for the saving twice. */
  .shc-card.dense .shc-radar { max-width: 2.35in; }
  .shc-card.dense .shc-tiles { margin-top: 0.06in; }
}
.shc-rel { font-size: 8.5pt; color: var(--sub); margin: 0.06in 0 0; }

/* ---------------- Cards: the release board ----------------
   A screen, not a page: this one is read on a laptop beside the check-in desk
   and never printed, so it uses the application's own surfaces rather than the
   card's inches. Nothing here is coloured by how GOOD an athlete is — the only
   colour on the board is whether a station has opened, which is a fact about
   the room. */

.shc-tally { display: flex; flex-wrap: wrap; gap: var(--s-4); margin: 0 0 var(--s-4); }
.shc-t { font-size: 0.85rem; color: var(--sub); }
.shc-t b { font-size: 1.35rem; font-weight: 700; color: var(--ink); margin-right: var(--s-1);
  font-variant-numeric: tabular-nums; }
.shc-t.ready b { color: var(--green); }

.shc-stations { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--s-3); margin-bottom: var(--s-4); }
.shc-st { border: 1px solid var(--line); border-radius: var(--r-card);
  background: var(--card); padding: var(--s-3); display: grid; gap: var(--s-2);
  align-content: start; }
.shc-st-h { font-size: 0.7rem; letter-spacing: 0.08em; text-transform: uppercase; color: var(--sub); }
.shc-st-n { font-size: 1.6rem; line-height: 1; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--ink); }
.shc-st-n span { font-size: 0.85rem; font-weight: 400; color: var(--sub-2); margin-left: 2px; }
/* The bar is a count against the threshold, not a score. */
.shc-st-bar { height: 4px; border-radius: var(--r-pill); background: var(--surface-3); overflow: hidden; }
.shc-st-bar i { display: block; height: 100%; background: var(--line-strong); }
.shc-st.open .shc-st-bar i { background: var(--green); }
.shc-st-s { font-size: 0.75rem; color: var(--sub); }
.shc-st.open .shc-st-s { color: var(--green); }

.shc-rule { font-size: 0.8rem; color: var(--sub); max-width: 46rem; margin: 0 0 var(--s-4); }

.shc-queue { padding: 0; overflow: hidden; }
.shc-q-row { display: grid; grid-template-columns: 5.5rem minmax(0, 1fr) 6rem minmax(0, 1.6fr);
  gap: var(--s-3); align-items: baseline; padding: var(--s-3) var(--s-4);
  border-top: 1px solid var(--line-soft); }
.shc-q-row:first-child { border-top: 0; }
.shc-q-id { font-family: var(--mono); font-size: 0.75rem; color: var(--sub-2); }
.shc-q-name { font-weight: 600; color: var(--ink); }
a.shc-q-name:hover { color: var(--navy-2); }
.shc-q-role, .shc-q-s { font-size: 0.8rem; color: var(--sub); }
.shc-q-ready .shc-q-s { color: var(--green); font-weight: 600; }
.shc-q-none .shc-q-name { color: var(--sub-2); font-weight: 400; }

/* The held explanation. Deliberately not styled as a warning: nobody did
   anything wrong, the room is simply not full yet. */
.shc-held { max-width: 34rem; padding: var(--s-5); display: grid; gap: var(--s-3); }
.shc-held-h { font-family: var(--display); font-size: 1.25rem; color: var(--navy); }
.shc-held p { margin: 0; color: var(--ink-2); }
.shc-held-list { margin: 0; padding-left: var(--s-5); display: grid; gap: var(--s-1);
  color: var(--ink-2); }
.shc-held-note { font-size: 0.85rem; color: var(--sub); }

/* ---------------- Cards: the card's own additions ----------------
   Everything below sits inside .shc-card and is therefore sized in points and
   inches like the rest of it. */

.shc-brand i { font-style: normal; font-size: 9.5pt; font-weight: 600; color: var(--navy); }

/* Pitching: the table and the movement plot side by side, the way the plot is
   read — a number in the table and its dot on the same line of sight. */
.shc-pitch { display: grid; grid-template-columns: minmax(0, 1fr) 2.3in;
  gap: 0.16in; align-items: start; }
.shc-plot svg { width: 100%; height: auto; display: block; }
/* The release strip: three numbers, and the slot as a picture beside them.
   THE FIGURE COSTS NO PAGE. The movement plot makes the right-hand column of
   .shc-pitch the taller one by ~46px, so the drawing is sized to occupy the
   tile row plus that space and the section's height does not move. */
.shc-release { display: grid; grid-template-columns: minmax(0, 1fr) 1.15in;
  gap: 0.08in; margin-top: 0.09in; align-items: stretch; }
.shc-tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 0.07in;
  align-content: start; }
/* Navy, because the figure is drawn in the varsity page's colours — a white
   hand on a white card would be invisible. `print-color-adjust` so the panel
   survives a printer's colour economising, the same rule the crest carries. */
.shc-slot { margin: 0; background: var(--navy); border-radius: 4px;
  padding: 0.04in 0.04in 0.02in; display: grid; gap: 0; align-content: center;
  max-height: 0.98in;
  print-color-adjust: exact; -webkit-print-color-adjust: exact; }
/* HEIGHT IS THE CONSTRAINT, NOT WIDTH. The panel may only take the space the
   movement plot beside it already leaves, so the drawing is capped in height
   and centred in whatever width is left over — sized to the page rather than
   the page resized around it. */
.shc-slot svg { max-height: 0.80in; width: auto; max-width: 100%;
  height: auto; display: block; margin: 0 auto; }
.shc-slot figcaption { font-size: 6pt; letter-spacing: 0.06em; text-transform: uppercase;
  color: #9FB4D0; text-align: center; }
.shc-tiles div { border: 1px solid var(--line); border-radius: 4px; padding: 0.05in 0.07in;
  display: grid; gap: 1px; }
.shc-tiles span { font-size: 7pt; color: var(--sub); }
.shc-tiles b { font-size: 10pt; font-variant-numeric: tabular-nums; }

.shc-figs { display: grid; gap: 0.16in; justify-content: center; }
.shc-figs.n1 { grid-template-columns: minmax(0, 2.6in); }
.shc-figs.n2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.shc-figs.n3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
/* `align-content: start` because the three figures are columns of one grid row
   and stretch to the tallest. Without it a hitting column of two readings
   spreads those two down the height of a pitching column of four, and the two
   lists stop reading as lists. */
.shc-fig { margin: 0; display: grid; gap: 0.06in; justify-items: center; align-content: start; }
.shc-fig figcaption { font-size: 8pt; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--navy); text-align: center; }
.shc-fig figcaption i { display: block; font-style: normal; font-weight: 400;
  letter-spacing: 0; text-transform: none; color: var(--sub); }
.shc-radar { width: 100%; height: auto; max-width: 2.3in; }

/* The lists sit in their own row under the radars, in the same columns, so a
   list is still read directly beneath the figure it belongs to. Separating
   them is what lets the print rules reflow the lists without moving the
   radars — see the density rules under @media print. */
.shc-cols { display: grid; gap: 0.16in; justify-content: center; margin-top: 0.06in; }
.shc-cols.n1 { grid-template-columns: minmax(0, 2.6in); }
.shc-cols.n2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.shc-cols.n3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

.shc-ranks { width: 100%; display: grid; gap: 2px; align-content: start; }
/* The figcaption above the radar already names the section. This repeat is for
   the dense print layout, where the lists leave their columns. */
.shc-ranks-h { display: none; }
.shc-rank { display: grid; grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "label value" "label place"; column-gap: 0.08in;
  align-items: baseline; padding: 2px 0; border-top: 1px solid var(--line-soft); }
.shc-rank-l { grid-area: label; font-size: 8.5pt; color: var(--ink-2); }
/* THE READING, NOT THE HEADLINE. At 10pt bold these numbers were the loudest
   thing on the card — louder than the athlete's name — so an eye went to a
   jump-mat reading before the radar the card is built around. Stepped down and
   unbolded so the row reads label, number, place as one line of equals; still
   tabular, so a column of them lines up. */
.shc-rank-v { grid-area: value; font-size: 8.8pt; font-weight: 600;
  font-variant-numeric: tabular-nums; }
/* The place line is support, not content: it qualifies the value above it.
   A step down from 7.5pt separates the two without making either hard to read,
   and it is the only thing on the card that shrank — the athlete's name, the
   section headings, the metric labels and the measurements are all unchanged. */
.shc-rank-p { grid-area: place; font-size: 7pt; color: var(--sub); text-align: right; }

/* ---- the parent's page: every number on the card, in plain words --------
   Same sheet as the card, same chrome, three columns of short definitions.
   Set small on purpose: it is a reference somebody scans for one term, not
   prose they read start to finish, and it has to be one page or it is two. */
.shg-page { background: #fff; color: var(--ink); width: 100%;
  display: grid; gap: 0.10in; align-content: start;
  font-size: 10pt; line-height: 1.35; }
.shg-lede { margin: 0; font-size: 7.6pt; line-height: 1.4; color: var(--ink-2);
  max-width: 68ch; }
/* Three columns that flow, so a block continues in the next column rather than
   leaving a ragged hole — the definitions have no reason to align in rows. */
.shg-cols { columns: 3; column-gap: 0.20in; column-rule: 1px solid var(--line-soft); }
.shg-block { break-inside: auto; margin: 0 0 0.09in; }
.shg-h { font-size: 7pt; font-weight: 700; letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--navy); margin: 0 0 0.04in; break-after: avoid; }
.shg-list { margin: 0; display: grid; gap: 0.045in; }
/* A term and its sentence must not be split across a column. */
.shg-item { break-inside: avoid; }
.shg-item dt { font-size: 7.4pt; font-weight: 700; color: var(--ink); }
.shg-item dd { margin: 0; font-size: 7pt; line-height: 1.32; color: var(--ink-2); }

/* ---- a switch hitter: two batting sides, one report ----------------------
   The picker chooses which side the radar draws, because a radar can only
   plot one set of percentiles and averaging two sides would describe a hitter
   who does not exist. The LIST below it always shows both sides at once, so
   nothing is hidden behind the control — the toggle changes the figure, never
   what is legible.

   Styled as a segmented control rather than as two buttons: it is a choice
   between two views of one athlete, not two actions. */
/* The preflight's roster block: who stays, joins and leaves the event. Three
   plain counts with the names under them, because a count of people leaving an
   event is not something a coach can check without the names. */
.shw-pf-rec { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin: 6px 0 2px; }
.shw-pf-rec-b { border: 1px solid var(--line); border-radius: 10px; padding: 8px 10px;
  background: var(--surface-2, #f5f5f7); }
.shw-pf-rec-b b { display: block; font-size: 1.4rem; line-height: 1.15; color: var(--ink); }
.shw-pf-rec-b i { display: block; font-style: normal; font-size: 0.74rem; color: var(--sub); }
.shw-pf-rec-b.add { border-color: color-mix(in srgb, var(--navy) 40%, var(--line)); }
.shw-pf-rec-b.drop { border-color: color-mix(in srgb, #b3261e 34%, var(--line)); }
.shw-pf-who { margin: 6px 0 0; padding-left: 1.1em; font-size: 0.78rem; color: var(--ink-2); }
.shw-pf-who li { margin: 1px 0; }
.shw-pf-sides { margin: 4px 0 0; padding-left: 1.1em; font-size: 0.82rem; color: var(--ink-2); }

@media (max-width: 48rem) {
  .shw-pf-rec { grid-template-columns: minmax(0, 1fr); }
}

.shc-sidepick { display: inline-flex; border: 1px solid var(--line); border-radius: 999px;
  overflow: hidden; background: var(--surface-2, #f5f5f7); }
.shc-sidebtn { appearance: none; border: 0; background: transparent; cursor: pointer;
  font: inherit; font-size: 7.5pt; font-weight: 600; letter-spacing: 0.02em;
  color: var(--sub); padding: 2px 9px; line-height: 1.5; }
.shc-sidebtn + .shc-sidebtn { border-left: 1px solid var(--line); }
.shc-sidebtn.on { background: var(--navy); color: #fff; }
.shc-sidebtn:not(.on):hover { color: var(--ink); }
.shc-sidebtn:focus-visible { outline: 2px solid var(--navy); outline-offset: -2px; }

/* Both sides on one row. The value cell holds two readings, each tagged with
   its side, and the place cell holds both placings — so the row says what each
   side measured and where each side stands, and never a blend of the two. */
/* A TWO-SIDED ROW STACKS: label, then the two readings, then the two places.
   Each takes the row's full width, and that is the whole point. The row is
   ~2.4in and the place cell cannot wrap — it carries two percentiles and two
   placings — so at 2.2in it very nearly fills the row on its own. Sharing a
   line with the readings left the value's track 0–10px against 97px of
   content, and the readings painted straight over the places: every number
   correct and the row unreadable. Given the whole width each fits with room,
   and nothing has to wrap or shrink.

   One line taller per row, which only the switch hitter pays, and the printed
   card still holds on one Letter page — see the print rules. */
.shc-rank-sides {
  grid-template-columns: minmax(0, 1fr);
  grid-template-areas: "label" "sides";
}
/* One line per side: the side letter, the reading, its unit, then its place.
   Grid-flowed rather than absolutely placed, so two sides or one both work. */
.shc-rank-sides .shc-side-line { grid-column: 1; display: flex; align-items: baseline;
  gap: 0.04in; font-variant-numeric: tabular-nums; }
.shc-rank-sides .shc-side-line > i { font-style: normal; font-size: 7pt; font-weight: 700;
  color: var(--sub); letter-spacing: 0.04em; min-width: 0.07in; }
.shc-rank-sides .shc-side-line > b { font-size: 8.8pt; font-weight: 600; }
.shc-rank-sides .shc-side-line[data-empty] > b { font-weight: 400; color: var(--sub); }
/* The place trails the reading on the same line, quiet, and is the only part
   allowed to shrink — it is support for the number in front of it. */
.shc-side-p { font-size: 7pt; color: var(--sub); min-width: 0; }
.shc-side { display: inline-flex; align-items: baseline; gap: 2px; }
.shc-side > i { font-style: normal; font-size: 7pt; font-weight: 700; color: var(--sub);
  letter-spacing: 0.04em; }
.shc-side[data-empty] { color: var(--sub); }


@media (max-width: 60rem) {
  .shc-stations { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .shc-q-row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .shc-q-id { grid-column: 1 / -1; }
}
