/* detail.css
   Game / practice detail panels and their launcher buttons.

   Part of the dashboard stylesheet, split by concern. index.html links these
   in source order; keep that order, the cascade depends on it. */

/* ── Game detail popover ────────────────────────────────────────────── */
@keyframes gdPush { from { opacity: 0; transform: translateX(46px); } to { opacity: 1; transform: none; } }
@keyframes gdLeave { from { opacity: 1; transform: none; } to { opacity: 0; transform: translateX(46px); } }
.game-detail {
  background: var(--surface); border-radius: var(--r-xl); box-shadow: var(--shadow-lg);
  width: min(94vw, 780px); height: min(92vh, 860px);
  display: flex; flex-direction: column; overflow: hidden;
}
.game-detail.gd-pop  { animation: scaleIn 0.2s cubic-bezier(0.34,1.56,0.64,1) both; }
.game-detail.gd-push { animation: gdPush 0.28s cubic-bezier(0.32,0.72,0,1) both; }
.game-detail.gd-leaving { animation: gdLeave 0.19s ease forwards; }
.gd-head { display: flex; align-items: center; justify-content: space-between; padding: 11px 12px 10px; border-bottom: 1px solid var(--border-2); flex-shrink: 0; }
.gd-nav-btn { display: inline-flex; align-items: center; gap: 1px; background: none; border: none; color: var(--accent); font: inherit; font-weight: 600; font-size: 14px; cursor: pointer; padding: 5px 8px 5px 5px; border-radius: var(--r-sm); transition: background 0.13s; }
.gd-nav-btn:hover { background: var(--accent-light); }
.gd-nav-btn svg { width: 19px; height: 19px; }
.gd-close { color: var(--label-3); font-size: 15px; width: 32px; height: 32px; justify-content: center; padding: 0; }
.gd-close:hover { background: var(--surface-2); color: var(--label); }
/* --gd-pad-top exists so .gd-grade-head's `top: calc(-1 * ...)` (below) can
   never drift out of sync with this padding value — a `position: sticky;
   top: 0` child of a padded scroll container sticks to the PADDING edge, but
   that padding only reserves blank space on the very first, unscrolled
   paint; once scrolled, ordinary content can still occupy that band, since
   nothing then stops a row's normal-flow top from reaching all the way up to
   this element's actual border-box edge. The negative top offset cancels
   the padding out so the sticky header aligns with the true edge instead,
   closing exactly that gap. */
.gd-body { --gd-pad-top: 14px; padding: var(--gd-pad-top) 28px 28px; overflow-y: auto; flex: 1; }
.gd-body::-webkit-scrollbar { width: 8px; }
.gd-body::-webkit-scrollbar-thumb { background: var(--border); border-radius: 8px; border: 2px solid transparent; background-clip: padding-box; }
.gd-title-row { display: flex; align-items: center; gap: 12px; }
.gd-logo { width: 42px; height: 42px; object-fit: contain; flex-shrink: 0; }
.gd-title { font-size: 22px; font-weight: 760; letter-spacing: -0.02em; line-height: 1.1; }
.gd-edit { margin-left: auto; flex-shrink: 0; color: var(--label-3); }
.gd-edit:hover { color: var(--accent); background: var(--accent-light); }
.gd-sub { font-size: 12.5px; color: var(--label-2); margin-top: 5px; }
.gd-week { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 20px; }
.gd-field { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.gd-label { font-size: 11px; font-weight: 650; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label-2); }
.gd-input { width: 100%; border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-2); padding: 11px 12px; font: inherit; color: var(--label); outline: none; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; }
.gd-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); background: var(--surface); }
.gd-input::placeholder { color: var(--label-3); font-weight: 500; }
.gd-big { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.gd-section-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label-2); margin: 24px 0 11px; }
.gd-mustwins { display: flex; flex-direction: column; gap: 9px; }
/* flex-start + the marker offset keeps the numbered circle on the FIRST line
   of a must win that has wrapped onto two or three. */
.gd-mw { display: flex; align-items: flex-start; gap: 12px; }
.gd-mw-num { width: 25px; height: 25px; border-radius: 50%; background: var(--accent-light); color: var(--accent); font-weight: 750; font-size: 12.5px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 5px; }
.gd-mw-input { flex: 1; min-width: 0; border: none; border-bottom: 1.5px solid var(--border); background: none; padding: 5px 2px; font-size: 18px; font-weight: 600; letter-spacing: -0.01em; color: var(--label); outline: none; transition: border-color 0.15s; }
.gd-mw-input:focus { border-bottom-color: var(--accent); }
.gd-mw-input::placeholder { color: var(--label-3); font-weight: 500; }
/* One shared grid so the "Grade" header column lines up EXACTLY with the circles column. */
/* No `overflow: hidden` here — an ancestor with overflow other than `visible`
   becomes ITS OWN scrolling box per spec, even when nothing about it actually
   scrolls, and a sticky descendant sticks relative to the nearest one of
   those. .gd-grades has no scrollbar of its own, so that box just travels
   with the page scroll like anything else — meaning .gd-grade-head would
   "stick" to a box that itself scrolls away, i.e. not stick at all from the
   viewer's perspective. The rounded corners it used to clip are handled
   directly on .gd-grade-head (top) and the closing row below instead. */
.gd-grades { display: grid; grid-template-columns: minmax(0,0.85fr) auto minmax(0,1.5fr); border: 1px solid var(--border-2); border-radius: var(--r-md); }
.gd-grade-head, .gd-grade-row { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; align-items: center; }
.gd-grade-head { background: var(--surface-2); font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-2); position: sticky; top: calc(-1 * var(--gd-pad-top, 0px)); z-index: 3; border-radius: var(--r-md) var(--r-md) 0 0; }
/* The actual pin: a fixed-position clone of .gd-grade-head that pinColumnHeader
   (grades.js) shows the instant the real row scrolls above the visible top of
   its scrollport, positioned via inline left/width/top set from measurements
   at that moment — position/top/left/width here are just safe defaults before
   the first sync. `position: sticky` above is left in place as a harmless
   fallback; this is what actually guarantees the row can't be scrolled past. */
.gd-grade-head--pinned {
  z-index: 5; box-shadow: var(--shadow-sm); pointer-events: none;
  border: 1px solid var(--border-2); border-bottom: none; box-sizing: border-box;
}
.gd-grade-head span { padding: 9px 12px; }
.gd-grade-head span + span { border-left: 1px solid var(--border-2); }
.gd-grade-head span:nth-child(2) { text-align: center; padding-left: 0; padding-right: 0; }
.gd-grade-row { border-top: 1px solid var(--border-2); }
.gd-grade-unit { padding: 11px 12px; font-size: 13px; font-weight: 600; }
.gd-grade-dots { display: flex; gap: 9px; justify-content: center; padding: 9px 14px; border-left: 1px solid var(--border-2); border-right: 1px solid var(--border-2); }
.gd-dot { width: 21px; height: 21px; border-radius: 50%; border: none; cursor: pointer; padding: 0; display: flex; align-items: center; justify-content: center; transition: filter 0.28s ease, opacity 0.28s ease, transform 0.2s cubic-bezier(0.34,1.56,0.64,1), box-shadow 0.2s ease; }
.gd-dot--green { background: var(--green); }
.gd-dot--amber { background: #fcba03; }
.gd-dot--red   { background: var(--red); }
.gd-dot svg { width: 11px; height: 11px; color: #fff; opacity: 0; transform: scale(0.3); transition: opacity 0.18s ease, transform 0.22s cubic-bezier(0.34,1.56,0.64,1); }
.gd-dot--amber svg { color: #4a3600; }   /* dark check stays legible on bright yellow */
.gd-dot.is-on { transform: scale(1.14); box-shadow: 0 2px 7px rgba(0,0,0,0.2); }
.gd-dot.is-on svg { opacity: 1; transform: scale(1); }
/* Once a grade is picked, the others desaturate + fade (animated via the transition above). */
.gd-grade-dots:not([data-grade=""]) .gd-dot:not(.is-on) { filter: grayscale(1) opacity(0.4); }
.gd-grade-dots:not([data-grade=""]) .gd-dot:not(.is-on):hover { filter: grayscale(0.25) opacity(0.8); }
.gd-grade-notes { border: none; background: none; padding: 11px 12px; font: inherit; font-size: 13px; color: var(--label); outline: none; width: 100%; transition: background 0.12s; }
.gd-grade-notes:focus { background: var(--accent-light); }
.gd-grade-notes::placeholder { color: var(--label-3); }

/* ── Player grade table ─────────────────────────────────────────────────
   The same grid, but a row is a player and a banded heading opens each
   position group. Long, so it stays readable: sticky headings, a tighter
   row, and a fixed-width jersey column that keeps every name left-aligned. */
.gd-grades--players .gd-grade-row { border-top: 1px solid var(--border-2); }
.gd-grades--players .gd-grade-unit {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 12px; font-size: 13px; font-weight: 550;
}
.gd-jersey {
  flex-shrink: 0; min-width: 26px; text-align: center;
  font-size: 11.5px; font-weight: 750; font-variant-numeric: tabular-nums;
  color: var(--label-2); background: var(--surface-2);
  border-radius: var(--r-sm); padding: 2px 6px;
}
.gd-grade-group {
  grid-column: 1 / -1; display: flex; align-items: center; gap: 8px;
  /* Deliberately NOT `position: sticky` — every .gd-grade-group in the table
     shares the same containing block (.gd-grades, the whole table, since
     that's the nearest ancestor forming one for an in-flow descendant), so
     each heading's sticky range is bounded by the end of the WHOLE table,
     not the end of its own group: once triggered it would never get pushed
     off, and every earlier group's heading would pile up at the same spot
     permanently. Left as plain flow instead — the banded heading scrolls
     with its own rows like anything else; only the outer column-title row
     (.gd-grade-head) stays pinned. */
  padding: 7px 12px; border-top: 1px solid var(--border-2);
  background: var(--surface-2);
  font-size: 10.5px; font-weight: 750; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--label-2);
}
.gd-grade-group:first-of-type { border-top: none; }
.gd-grade-group-n {
  font-size: 10px; font-weight: 700; color: var(--label-3);
  background: var(--surface); border-radius: var(--r-full);
  padding: 1px 7px; font-variant-numeric: tabular-nums;
}
/* A big notes field for the whole position group, right under its banded
   heading and above its players — same field as the day note, smaller scale. */
.gd-groupnote-wrap { grid-column: 1 / -1; padding: 8px 12px; background: var(--surface-2); }
.gd-groupnote {
  width: 100%; resize: none; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface); padding: 8px 10px; font: inherit; font-size: 12.5px;
  color: var(--label); outline: none;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.gd-groupnote:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); background: var(--surface); }
.gd-groupnote::placeholder { color: var(--label-3); }
/* Three lights per player — assignment, effort, technique — instead of the
   unit table's one, so the grid gets three narrow centered columns in place
   of the single wide "Grade" one.
   Scrolls with the rest of the panel (.gd-body is the scrollport) rather
   than in its own private scrollbox, so the day note and Margin of Error
   block above it scroll away with everything else instead of staying
   pinned. The sticky column-title / position-group headings above still
   work — they stick relative to .gd-body, the nearest scrolling ancestor,
   same as any other sticky element in the panel. */
.gd-grades--players {
  grid-template-columns: minmax(0,1fr) auto auto auto 48px minmax(0,0.95fr);
}
.gd-grade-head--cats span:not(:first-child):not(:last-child) { text-align: center; padding-left: 0; padding-right: 0; }
.gd-grades--players .gd-grade-dots { gap: 6px; padding: 6px 10px; }
.gd-grades--players .gd-dot { width: 18px; height: 18px; }
.gd-grades--players .gd-dot svg { width: 9px; height: 9px; }
.gd-grades--players .gd-grade-notes { padding: 8px 12px; }
.gd-grade-ma {
  width: calc(100% - 12px); margin: 0 6px; box-sizing: border-box; border: 1px solid var(--border-2); border-radius: var(--r-sm, 6px);
  background: none; font: inherit; font-size: 13px; color: var(--label); text-align: center;
  padding: 4px 2px; outline: none; transition: background 0.12s, border-color 0.12s;
}
.gd-grade-ma:focus { background: var(--accent-light); border-color: var(--accent); }
@media (max-width: 560px) {
  .gd-grades--players { grid-template-columns: minmax(0, 1fr) auto auto auto 40px minmax(0, 1fr); }
  .gd-grades--players .gd-grade-unit { gap: 7px; padding: 8px 9px; }
  .gd-jersey { min-width: 22px; padding: 2px 4px; }
  .gd-grades--players .gd-grade-dots { gap: 4px; padding: 6px 6px; }
  .gd-grades--players .gd-dot { width: 16px; height: 16px; }
  .gd-grade-ma { font-size: 12px; padding: 3px 2px; width: calc(100% - 8px); margin: 0 4px; }
}

/* ── Trend icon + popover ───────────────────────────────────────────────
   The chart icon sits to the left of the jersey number in each player row;
   clicking it opens the season rank-trend popover (openPlayerRankTrend in
   rankings.js). Shared with the disabled grade-trend viewer in grades.js,
   which used the same .tr-* classes. */
.gd-trend-btn {
  flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  width: 22px; height: 22px; border: none; border-radius: var(--r-sm);
  background: none; color: var(--label-3); cursor: pointer;
  transition: background 0.13s, color 0.13s;
}
.gd-trend-btn svg { width: 14px; height: 14px; }
.gd-trend-btn:hover, .gd-trend-btn:focus-visible { background: var(--accent-light); color: var(--accent); outline: none; }

.trend-modal { max-width: 700px; }
.tr-wrap { display: flex; flex-direction: column; gap: 20px; }
.tr-section { display: flex; flex-direction: column; gap: 8px; }
.tr-section-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label-2); }
.tr-legend { display: flex; gap: 16px; flex-wrap: wrap; }
.tr-legend-item { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; font-weight: 600; color: var(--label-2); }
.tr-legend-swatch { width: 22px; height: 8px; flex-shrink: 0; overflow: visible; }
/* mountTrendChart (grades.js) measures this div's real width and draws the
   SVG to fit it exactly, redrawing on resize — so the chart itself needs no
   sizing rules here. `.tr-chart-scroll` only appears in the DOM on the rare
   chart dense enough to hit the point-spacing floor (MIN_PX_PER_POINT); it's
   what lets that one case scroll instead of squeezing unreadable. */
.tr-chart-host { min-height: 200px; }
.tr-chart-host--rank { min-height: 170px; }
.tr-chart-scroll { overflow-x: auto; max-width: 100%; }
.tr-svg { display: block; flex-shrink: 0; }
.tr-grid { stroke: var(--border-2); stroke-width: 1; }
.tr-axis-y, .tr-axis-x { font-size: 9.5px; font-weight: 650; fill: var(--label-3); font-family: var(--font); }
.tr-line { fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.tr-line--overlap { stroke-width: 2.5; }   /* a hair thicker so the striped colors stay legible at small sizes */
.tr-dot { stroke: var(--surface); stroke-width: 2; transition: r 0.15s; }
.tr-dot:hover { r: 6; }
.tr-empty { padding: 30px 0; text-align: center; color: var(--label-3); font-size: 13.5px; }
.tr-table-wrap { border-top: 1px solid var(--border-2); padding-top: 10px; }
.tr-table-wrap summary { cursor: pointer; font-size: 12.5px; font-weight: 650; color: var(--accent); user-select: none; }
.tr-table { width: 100%; border-collapse: collapse; margin-top: 8px; font-size: 12.5px; }
.tr-table th, .tr-table td { padding: 6px 8px; text-align: left; border-bottom: 1px solid var(--border-2); }
.tr-table th { color: var(--label-2); font-weight: 700; text-transform: uppercase; font-size: 10px; letter-spacing: 0.04em; }
.tr-cell--green { color: var(--green); font-weight: 650; }
.tr-cell--amber { color: var(--amber); font-weight: 650; }
.tr-cell--red   { color: var(--red); font-weight: 650; }
.tr-cell--none  { color: var(--label-3); }

/* ─── Goals (per game: offense / defense / special teams) ────────────────────
   One card per side; each row is a fixed goal statement with a single output
   cell — a green and a red circle. Clicking a circle lights the whole cell in
   that colour (see `.gd-goal-cell` below) and clicking the lit one again clears
   it. Replaces the old free-editable KPI table this section used to hold. */
.gd-goals { display: flex; flex-direction: column; gap: 14px; }
.gd-goals-side { border: 1px solid var(--border-2); border-radius: var(--r-md); overflow: hidden; }
.gd-goals-side-head { background: var(--surface-2); font-size: 11px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label-2); padding: 9px 12px; border-bottom: 1px solid var(--border-2); }
.gd-goals-head, .gd-goals-row { display: grid; grid-template-columns: minmax(0,1fr) minmax(150px, 220px); align-items: center; }
.gd-goals-head { padding: 7px 12px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-3); }
.gd-goals-head span:last-child { justify-self: end; padding-right: 8px; }
.gd-goals-row { border-top: 1px solid var(--border-2); padding: 8px 12px 8px 14px; gap: 10px; }
.gd-goals-label { font-size: 12.5px; font-weight: 700; letter-spacing: 0.01em; color: var(--label); }
.gd-goal-cell { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 6px 10px; border-radius: var(--r-sm); background: transparent; box-shadow: inset 0 0 0 1px transparent; transition: background-color 0.25s ease, box-shadow 0.25s ease; }
.gd-goal-cell.is-green { background: var(--green-light); box-shadow: inset 0 0 0 1px var(--green); }
.gd-goal-cell.is-red   { background: var(--red-light);   box-shadow: inset 0 0 0 1px var(--red); }
.gd-goal-cell.gd-goal-pulse { animation: gd-goal-pulse 0.45s ease; }
@keyframes gd-goal-pulse {
  0%   { transform: scale(0.95); }
  55%  { transform: scale(1.06); }
  100% { transform: scale(1); }
}
.gd-goal-dot { width: 26px; height: 26px; border-radius: 50%; border: 2px solid var(--border-2); background: var(--surface); color: transparent; display: flex; align-items: center; justify-content: center; cursor: pointer; flex-shrink: 0; transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease, transform 0.12s ease; }
.gd-goal-dot svg { width: 13px; height: 13px; }
.gd-goal-dot--green { border-color: var(--green); }
.gd-goal-dot--red   { border-color: var(--red); }
.gd-goal-dot:hover:not(:disabled) { transform: scale(1.1); }
.gd-goal-dot--green.is-on { background: var(--green); border-color: var(--green); color: #fff; box-shadow: 0 0 0 4px var(--green-light), 0 2px 12px rgba(31, 157, 85, 0.45); }
.gd-goal-dot--red.is-on   { background: var(--red);   border-color: var(--red);   color: #fff; box-shadow: 0 0 0 4px var(--red-light),   0 2px 12px rgba(224, 52, 42, 0.45); }
.gd-goal-dot:disabled { cursor: default; }
.gd-goal-text { flex: 1; min-width: 0; border: 1px solid var(--border); background: var(--surface); font: inherit; font-size: 12.5px; font-weight: 600; color: var(--label); outline: none; padding: 5px 9px; border-radius: 6px; text-align: right; transition: background 0.14s, border-color 0.14s, box-shadow 0.14s; }
.gd-goal-text:hover:not(:disabled) { border-color: var(--label-3); }
.gd-goal-text:focus { border-color: var(--accent); background: var(--surface); box-shadow: 0 0 0 3px var(--accent-light); }
.gd-goal-text::placeholder { color: var(--label-3); font-weight: 500; }
.gd-goal-text:disabled { border-style: dashed; cursor: default; }
.gd-kpi-del { border: none; border-left: 1px solid var(--border-2); background: none; color: var(--label-3); font-size: 12px; cursor: pointer; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.13s, color 0.13s; }
.gd-kpi-row:hover .gd-kpi-del, .gd-kpi-del:focus-visible { opacity: 1; }
.gd-kpi-del:hover { color: var(--red); }
.gd-kpi-empty { padding: 16px 12px; text-align: center; font-size: 12.5px; color: var(--label-2); }
.gd-kpi-add { display: inline-flex; align-items: center; gap: 5px; margin-top: 10px; background: none; border: 1px dashed var(--border); color: var(--label-2); font: inherit; font-weight: 600; font-size: 12.5px; padding: 8px 13px; border-radius: var(--r-sm); cursor: pointer; transition: color 0.14s, background 0.14s, border-color 0.14s; }
.gd-kpi-add:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-light); }
.gd-kpi-add svg { width: 14px; height: 14px; }
.gd-postgame { width: 100%; min-height: 92px; resize: vertical; border: 1px solid var(--border); border-radius: var(--r-sm); background: var(--surface-2); padding: 11px 12px; font: inherit; font-size: 14px; line-height: 1.5; color: var(--label); outline: none; transition: border-color 0.15s, box-shadow 0.15s, background 0.15s; }
.gd-postgame:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); background: var(--surface); }
@media (max-width: 520px) {
  .gd-week { grid-template-columns: 1fr; }
  .gd-body { padding: 12px 16px 20px; }
  .pr-reps { grid-template-columns: 1fr; }
  .gd-goals-head, .gd-goals-row { grid-template-columns: 1fr; }
  .gd-goals-head span:last-child { justify-self: start; padding-right: 0; }
  .gd-goal-cell { justify-content: flex-start; margin-top: 6px; }
}

/* ── Fall Camp ──────────────────────────────────────────────────────── */
.game-loc.is-empty { color: var(--label-3); font-style: italic; }
.pr-graded {
  font-size: 11px; font-weight: 750; color: var(--green); background: var(--green-light);
  padding: 2px 7px; border-radius: var(--r-full); flex-shrink: 0; font-variant-numeric: tabular-nums;
}

/* Practice itinerary — same visual language as the KPI table */
.pr-itinerary { border: 1px solid var(--border-2); border-radius: var(--r-md); overflow: hidden; }
.pr-it-head, .pr-it-row { display: grid; grid-template-columns: 64px minmax(0,0.55fr) 1fr 30px; align-items: stretch; }
.pr-it-head { background: var(--surface-2); font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-2); }
.pr-it-head span { padding: 9px 12px; display: flex; align-items: center; }
.pr-it-head span + span { border-left: 1px solid var(--border-2); }
.pr-it-row { border-top: 1px solid var(--border-2); }
.pr-it-drill { border: none; background: none; font: inherit; font-size: 13px; color: var(--label); outline: none; padding: 11px 12px; width: 100%; transition: background 0.12s; }
.pr-it-per { display: flex; align-items: center; justify-content: center; font-family: var(--font); font-size: 13px; font-weight: 650; font-variant-numeric: tabular-nums; color: var(--accent); background: var(--surface-2); }
.pr-it-type {
  border: none; border-left: 1px solid var(--border-2); background: none; font: inherit; font-size: 12.5px;
  font-weight: 600; color: var(--label-2); text-transform: uppercase; letter-spacing: 0.02em; outline: none;
  padding: 11px 12px; width: 100%; min-width: 0; transition: background 0.12s;
}
.pr-it-type:focus { background: var(--accent-light); color: var(--label); }
.pr-it-type::placeholder { color: var(--label-3); text-transform: none; letter-spacing: normal; font-weight: 500; }
.pr-it-drill { border-left: 1px solid var(--border-2); font-weight: 550; }
.pr-it-drill:focus { background: var(--accent-light); }
.pr-it-drill::placeholder { color: var(--label-3); font-weight: 500; }
.pr-it-del { border-left: 1px solid var(--border-2); }
@media (max-width: 560px) {
  .pr-it-head, .pr-it-row { grid-template-columns: 44px minmax(0,0.7fr) 1fr 30px; }
  .pr-it-type { padding: 9px 8px; font-size: 11px; }
}

/* ── Practice Overview ──────────────────────────────────────────────────
   One bordered stack of four blocks (themes of the day, dress, rep
   management, points of emphasis) — same box language as the itinerary and
   KPI tables. Repeatable rows borrow the Must Wins underline-input look at a
   smaller size so they sit a step below "Theme of the Practice". */
.pr-ov { border: 1px solid var(--border-2); border-radius: var(--r-md); overflow: hidden; }
.pr-ov-block { padding: 13px 14px 14px; }
.pr-ov-block + .pr-ov-block { border-top: 1px solid var(--border-2); }
.pr-ov-label { display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-2); margin-bottom: 10px; }
.pr-ov-rows { display: flex; flex-direction: column; gap: 7px; }
/* Markers sit against the first line so a wrapped theme/point still reads as
   one numbered item (see textarea.autogrow). */
.pr-ov-row { display: flex; align-items: flex-start; gap: 11px; }
.pr-ov-num { width: 22px; height: 22px; border-radius: 50%; background: var(--accent-light); color: var(--accent); font-size: 11.5px; font-weight: 750; font-variant-numeric: tabular-nums; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 5px; }
.pr-ov-dot { width: 22px; height: 22px; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 5px; }
.pr-ov-dot::before { content: ''; width: 6px; height: 6px; border-radius: 50%; background: var(--accent); }
.pr-ov-unit { width: 40px; height: 22px; border-radius: var(--r-full); background: color-mix(in srgb, var(--u) 14%, transparent); color: var(--u); font-size: 10px; font-weight: 800; letter-spacing: 0.03em; display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 5px; }
.pr-ov-input { flex: 1; min-width: 0; border: none; border-bottom: 1.5px solid var(--border); background: none; padding: 6px 2px; font: inherit; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; color: var(--label); outline: none; transition: border-color 0.15s; }
.pr-ov-input:focus { border-bottom-color: var(--accent); }
.pr-ov-input::placeholder { color: var(--label-3); font-weight: 500; }
.pr-ov-del { width: 24px; height: 24px; flex-shrink: 0; margin-top: 4px; border: none; border-radius: 6px; background: none; color: var(--label-3); font-size: 11px; cursor: pointer; display: flex; align-items: center; justify-content: center; opacity: 0; transition: opacity 0.13s, color 0.13s, background 0.13s; }
.pr-ov-row:hover .pr-ov-del, .pr-ov-del:focus-visible { opacity: 1; }
.pr-ov-del:hover { color: var(--red); background: var(--red-light); }
.pr-ov-empty { font-size: 12.5px; color: var(--label-2); padding: 2px 2px 4px; }
.pr-ov-add { margin-top: 9px; font-size: 12px; padding: 7px 12px; }   /* tighter .gd-kpi-add for inside a block */
.pr-ov-text { min-height: 64px; font-size: 13.5px; }                  /* tighter .gd-postgame */
.pr-ov-notes { min-height: 92px; }                                    /* …but notes get room to write */
/* Rep management — a labelled line each for the 1's and the 2's. */
.pr-reps { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; }
.pr-rep { display: flex; align-items: flex-start; gap: 10px; min-width: 0; }
.pr-rep-tag { flex-shrink: 0; margin-top: 6px; padding: 3px 9px; border-radius: var(--r-full); background: var(--accent-light); color: var(--accent); font-size: 11.5px; font-weight: 750; font-variant-numeric: tabular-nums; }
.pr-rep-input { flex: 1; min-width: 0; border: none; border-bottom: 1.5px solid var(--border); background: none; padding: 6px 2px; font: inherit; font-size: 14px; font-weight: 600; color: var(--label); outline: none; transition: border-color 0.15s; }
.pr-rep-input:focus { border-bottom-color: var(--accent); }
.pr-rep-input::placeholder { color: var(--label-3); font-weight: 500; }

/* ── Injury-report launcher button (inside game / practice detail) ──── */
.gd-inj-btn {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 13px 14px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-2); cursor: pointer; color: var(--label);
  transition: border-color 0.14s, background 0.14s, box-shadow 0.14s;
}
.gd-inj-btn:hover { border-color: var(--red); background: var(--red-light); }
.gd-inj-ic { width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; background: var(--red-light); color: var(--red); }
.gd-inj-btn:hover .gd-inj-ic { background: var(--red); color: #fff; }
.gd-inj-ic svg { width: 17px; height: 17px; }
.gd-inj-main { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.gd-inj-title { font-size: 14px; font-weight: 650; }
.gd-inj-sub { font-size: 12px; color: var(--label-2); }
.gd-inj-count { font-size: 12.5px; font-weight: 750; color: var(--red); background: var(--red-light); border-radius: var(--r-full); min-width: 22px; text-align: center; padding: 2px 7px; font-variant-numeric: tabular-nums; }
.gd-inj-count:empty { display: none; }

/* Position-group launcher — the injury button's neutral sibling, sitting under
   the three unit grades. Slate rather than red: it opens detail, not an alarm. */
.gd-ug-btn:hover { border-color: var(--slate); background: var(--slate-light); }
.gd-ug-btn { margin-top: 12px; }
.gd-ug-ic { background: var(--slate-light); color: var(--slate); }
.gd-ug-btn:hover .gd-ug-ic { background: var(--slate); color: #fff; }
.gd-ug-count { color: var(--slate); background: var(--slate-light); }

/* Penalties launcher — sits directly beneath the Player Grades button, in the
   flag's own gold rather than the alarm-red of the injury button or the
   neutral slate of the position-group one. */
.gd-pen-btn:hover { border-color: var(--gold); background: var(--gold-light); }
.gd-pen-btn { margin-top: 8px; }
.gd-pen-ic { background: var(--gold-light); color: var(--gold); }
.gd-pen-btn:hover .gd-pen-ic { background: var(--gold); color: #fff; }
.gd-pen-count { color: var(--gold); background: var(--gold-light); }

/* ── Penalty report (inside the penalty popover) ───────────────────────
   A summary chip pair, then the log itself as a bordered grid table — same
   visual language as the KPI / MOE tables above: a sticky-feeling header row,
   dividers between columns, and a delete action that only appears on hover. */
.pen-summary { display: flex; gap: 8px; margin: 2px 0 14px; }
.pen-summary:empty { display: none; }
.pen-sum-chip {
  font-size: 11.5px; font-weight: 750; color: var(--gold); background: var(--gold-light);
  border-radius: var(--r-full); padding: 3px 10px; font-variant-numeric: tabular-nums;
}
.pen-table { border: 1px solid var(--border-2); border-radius: var(--r-md); overflow: hidden; }
.pen-head, .pen-row {
  display: grid; grid-template-columns: minmax(0,0.85fr) 46px minmax(0,1fr) minmax(0,1.25fr) 60px 30px;
  align-items: center;
}
.pen-head { background: var(--surface-2); font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-2); }
.pen-head span { padding: 9px 10px; }
.pen-head span + span { border-left: 1px solid var(--border-2); }
.pen-row { border-top: 1px solid var(--border-2); }
.pen-row:hover { background: var(--surface-2); }
.pen-cell { padding: 10px; font-size: 13px; color: var(--label); border-left: 1px solid var(--border-2); }
.pen-jersey { font-variant-numeric: tabular-nums; font-weight: 650; }
.pen-yds { font-variant-numeric: tabular-nums; font-weight: 600; text-align: right; }
.pen-unit {
  justify-self: start; margin: 0 10px; padding: 3px 9px; border-radius: var(--r-full);
  background: color-mix(in srgb, var(--u) 14%, transparent); color: var(--u);
  font-size: 10.5px; font-weight: 750; letter-spacing: 0.02em; white-space: nowrap;
}
.pen-del {
  border: none; border-left: 1px solid var(--border-2); background: none; color: var(--label-3);
  cursor: pointer; display: flex; align-items: center; justify-content: center;
  height: 100%; opacity: 0; transition: opacity 0.13s, color 0.13s;
}
.pen-del svg { width: 14px; height: 14px; }
.pen-row:hover .pen-del, .pen-del:focus-visible { opacity: 1; }
.pen-del:hover { color: var(--red); }
@media (max-width: 620px) {
  .pen-head, .pen-row { grid-template-columns: minmax(0,0.7fr) 34px minmax(0,0.9fr) minmax(0,1fr) 48px 26px; }
  .pen-head span, .pen-cell { padding: 8px 6px; font-size: 12px; }
  .pen-unit { margin: 0 6px; padding: 2px 6px; font-size: 9.5px; }
}

/* Per-side heading inside the position-group panel, with its completion count. */
.ug-side { display: flex; align-items: center; gap: 9px; }
.ug-done {
  font-size: 11px; font-weight: 750; letter-spacing: 0; text-transform: none;
  color: var(--label-3); background: var(--surface-2);
  border-radius: var(--r-full); padding: 1px 8px; font-variant-numeric: tabular-nums;
}
.ug-done.is-complete { color: var(--green); background: var(--green-light); }
.ug-done:empty { display: none; }

/* Full-dashboard tab strip: pick one side at a time instead of stacking all
   three. Reuses the app's generic .tabs/.tab pill component; only the
   position-group panel's own accent and the count badge are custom. */
.ug-tabs { margin: 4px 0 20px; }
.ug-tabs .tab { display: inline-flex; align-items: center; gap: 7px; }
.ug-tabs .tab.active { background: var(--surface); color: var(--accent); box-shadow: var(--shadow-sm); }
.ug-tab-count {
  font-size: 11px; font-weight: 750; letter-spacing: 0; margin-left: 0;
  color: var(--label-3); background: var(--surface-2);
  border-radius: var(--r-full); padding: 1px 7px; font-variant-numeric: tabular-nums;
}
.ug-tab-count.is-complete { color: var(--green); background: var(--green-light); }
.ug-tab-count:empty { display: none; }

/* The player-grades panel's own .gd-body (marked .ug-body — this shape is
   specific to openUnitGrades, not the generic .gd-body every other detail
   panel also uses): just a flex column for stacking, no fill-to-height or
   private scrollport. The whole panel — day note, Margin of Error, table —
   scrolls together as one block within .gd-body, same as every other
   detail panel, so nothing stays pinned while the rest scrolls past it. */
.ug-body { display: flex; flex-direction: column; }
.ug-panel { display: flex; flex-direction: column; }
.ug-panel:not(.is-active) { display: none; }
.ug-table { display: flex; flex-direction: column; }

/* Day note above each side's player grade table. */
.gd-daynote {
  width: 100%; resize: none; border: 1px solid var(--border); border-radius: var(--r-sm);
  background: var(--surface-2); padding: 10px 12px; font: inherit; font-size: 13px;
  color: var(--label); outline: none; margin-bottom: 12px; flex-shrink: 0;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.gd-daynote:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); background: var(--surface); }
.gd-daynote::placeholder { color: var(--label-3); }

/* Margin of Error — offense only, right below the day note: cumulative and
   weekly ("2's") error counts and the share of plays they represent. */
.gd-moe-wrap { flex-shrink: 0; margin-bottom: 16px; }
.gd-moe-title { margin: 0 0 11px; }
.gd-moe {
  display: grid;
  grid-template-columns: minmax(0,0.9fr) repeat(6, minmax(0,0.85fr)) minmax(0,0.9fr);
  border: 1px solid var(--border-2); border-radius: var(--r-md); overflow: hidden;
  background: var(--surface);
}
.gd-moe-head, .gd-moe-row { display: grid; grid-template-columns: subgrid; grid-column: 1 / -1; align-items: center; }
.gd-moe-head {
  background: var(--surface-2); font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--label-2);
}
.gd-moe-head span { padding: 9px 10px; text-align: center; }
.gd-moe-head span + span { border-left: 1px solid var(--border-2); }
.gd-moe-row { border-top: 1px solid var(--border-2); }
.gd-moe-label { padding: 11px 12px; font-size: 13px; font-weight: 600; }
.gd-moe-input {
  width: calc(100% - 16px); margin: 8px; padding: 7px 8px; text-align: center; font: inherit;
  font-size: 13px; color: var(--label); background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-sm); outline: none; -moz-appearance: textfield;
  transition: border-color 0.15s, box-shadow 0.15s, background 0.15s;
}
.gd-moe-input::-webkit-outer-spin-button, .gd-moe-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.gd-moe-input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-light); background: var(--surface); }
.gd-moe-pct { padding: 11px 10px; text-align: center; font-size: 13px; font-weight: 700; color: var(--accent); }
.gd-moe-pct.is-good { color: var(--green); }
.gd-moe-pct.is-bad { color: var(--red); }
@media (max-width: 620px) {
  .gd-moe { grid-template-columns: minmax(0,0.8fr) repeat(6, minmax(0,0.7fr)) minmax(0,0.75fr); }
  .gd-moe-head span { padding: 8px 4px; font-size: 9.5px; }
  .gd-moe-label { padding: 9px 8px; font-size: 12px; }
  .gd-moe-input { margin: 6px; font-size: 12px; padding: 5px 4px; }
  .gd-moe-pct { padding: 9px 4px; font-size: 12px; }
}
.gd-inj-chev { color: var(--label-3); display: inline-flex; flex-shrink: 0; }
.gd-inj-chev svg { width: 18px; height: 18px; }
.ir-list { display: flex; flex-direction: column; gap: 2px; margin-bottom: 4px; }

/* ── Practices launcher button (inside game detail) — green sibling of the injury button ── */
.gd-prac-btn {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 13px 14px; border: 1px solid var(--border); border-radius: var(--r-md);
  background: var(--surface-2); cursor: pointer; color: var(--label);
  transition: border-color 0.14s, background 0.14s, box-shadow 0.14s;
}
.gd-prac-btn:hover { border-color: var(--green); background: var(--green-light); }
.gd-prac-ic { width: 34px; height: 34px; border-radius: 10px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; background: var(--green-light); color: var(--green); }
.gd-prac-btn:hover .gd-prac-ic { background: var(--green); color: #fff; }
.gd-prac-ic svg { width: 17px; height: 17px; }
.gd-prac-count { font-size: 12.5px; font-weight: 750; color: var(--green); background: var(--green-light); border-radius: var(--r-full); min-width: 22px; text-align: center; padding: 2px 7px; font-variant-numeric: tabular-nums; }
.gd-prac-count:empty { display: none; }

/* ── Read-only detail panels ─────────────────────────────────────────────
   A read-only account still opens every panel; every input/textarea in it
   already carries the disabled attribute (set in JS), so one rule covers the
   theme field, KPIs, must-wins, day notes, group notes, MOE inputs, itinerary
   and overview lists. The grade lights are plain buttons with no meaningful
   disabled state of their own — their click listener is simply never
   attached — so they need their own rule. */
.gd-overlay.is-readonly input:disabled,
.gd-overlay.is-readonly textarea:disabled,
.gd-overlay.is-readonly select:disabled {
  opacity: 0.6; cursor: not-allowed;
}
.gd-overlay.is-readonly .gd-dot { pointer-events: none; opacity: 0.55; cursor: default; }

/* ── Position rankings ──────────────────────────────────────────────────
   One card per position group, each an ordered list a coach drags into the
   order he wants. Every rank badge reads the same — the position in the list
   is the whole statement, and colouring the top few made a claim about them
   the coach hadn't. */
.rk { display: flex; flex-direction: column; }
.rk-intro {
  display: flex; align-items: center; gap: 9px; margin-bottom: 16px;
  padding: 10px 13px; border-radius: var(--r-md);
  background: var(--indigo-light); color: var(--label-2);
  font-size: 12.5px; font-weight: 550; line-height: 1.35;
}
.rk-intro-ic { display: inline-flex; flex-shrink: 0; color: var(--indigo); }
.rk-intro-ic svg { width: 16px; height: 16px; }

/* Every position group gets its own card, wide enough to hold a coordinator
   column and a coach column side by side; one per row once the panel narrows
   past holding that (see the media query below), so stacking fourteen of them
   (special teams) never turns into two endless columns instead of one. */
.rk-groups { display: grid; grid-template-columns: repeat(auto-fill, minmax(440px, 1fr)); gap: 14px; align-items: start; }
/* …except a group ranked in more than two columns — the offensive line, read
   by a coordinator and both of its position coaches — which takes a whole row
   of that grid to itself so its three lists keep a readable width instead of
   being squeezed into one card's half. Harmless once the grid is a single
   column: a full row is all a card gets there anyway. */
.rk-group--wide { grid-column: 1 / -1; }
.rk-group {
  border: 1px solid var(--border-2); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden;
}
.rk-group-head {
  display: flex; align-items: center; gap: 8px; padding: 8px 10px;
  background: var(--surface-2); border-bottom: 1px solid var(--border-2);
}
.rk-code {
  flex-shrink: 0; padding: 2px 7px; border-radius: var(--r-sm);
  background: var(--indigo-light); color: var(--indigo);
  font-size: 10px; font-weight: 800; letter-spacing: 0.04em;
}
.rk-group-label {
  flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 11px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.04em; color: var(--label-2);
}
.rk-group-n {
  flex-shrink: 0; font-size: 10px; font-weight: 700; color: var(--label-3);
  background: var(--surface); border-radius: var(--r-full); padding: 1px 7px; font-variant-numeric: tabular-nums;
}

/* The coordinator's read and the position coach's read, side by side — the
   same list twice over, divided by a hairline, so the two orders can be
   compared at a glance instead of one overwriting the other. minmax(0, 1fr)
   rather than a bare 1fr on every track: a bare 1fr still sizes each track's
   floor to its own content, so a column whose header happens to be showing a
   Reset button would push wider than one next to it that isn't — minmax(0, …)
   pins the floor to zero, so the tracks split the row evenly regardless of
   what any header holds. */
.rk-cols { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
.rk-cols--1 { grid-template-columns: minmax(0, 1fr); }
.rk-cols--3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.rk-col + .rk-col { border-left: 1px solid var(--border-2); }
.rk-col-head {
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
  padding: 7px 9px; background: var(--surface); border-bottom: 1px solid var(--border-2);
  flex-wrap: wrap;   /* a narrow half-width column wraps its actions under the title rather than clipping them */
}
/* The title, and under it the carried-forward tag when there is one. A column
   rather than a third item in the header row: at a third of a card's width
   there is no room beside the Done and Reset buttons. */
.rk-col-heading { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.rk-col-title {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--label-3);
}

/* "from vs Towson" — a column nobody has entered anything into this week opens
   in the order it was last left in, and says so. Hidden until it is actually
   the order on screen, the same display:none-plus-a-class the Reset button
   uses, so a drag or a reset flips it with no re-render. */
.rk-carried {
  display: none; align-self: flex-start; align-items: center; gap: 3px;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  padding: 1px 6px 1px 4px; border-radius: var(--r-full);
  background: var(--surface-2); color: var(--label-3);
  font-size: 9px; font-weight: 750; letter-spacing: 0.03em; text-transform: uppercase;
}
.rk-carried svg { width: 9px; height: 9px; flex-shrink: 0; }
.rk-col.is-carried .rk-carried, .rk-unit.is-carried .rk-carried { display: inline-flex; }
/* Carried ranks read as provisional — an outline where a settled rank has a
   filled badge — so a glance down a panel separates this week's work from last
   week's, without dimming names nobody has any reason to read as inactive. */
.rk-col.is-carried .rk-num, .rk-unit.is-carried .rk-num {
  background: none; box-shadow: inset 0 0 0 1px var(--border);
}
.rk-col--coordinator .rk-col-title { color: var(--indigo); }
.rk-col--coach .rk-col-title { color: var(--accent); }
.rk-col--coach2 .rk-col-title { color: var(--teal); }
.rk-col.is-done .rk-col-head { background: var(--green-light); }
/* A reserved floor, because Reset only appears once a column is customized:
   without it a header with no Reset would be shorter than the one beside it,
   and the two lists underneath would start at different heights. */
.rk-col-actions { display: flex; align-items: center; gap: 4px; flex-shrink: 0; min-height: 19px; }

/* The coach's own "I've reviewed this" signal — separate from whether the
   order has ever been dragged, since a group that's fine in roster order
   still needs a way to be marked reviewed. */
.rk-done-btn {
  display: inline-flex; align-items: center; gap: 4px; border: none; cursor: pointer;
  padding: 3px 8px 3px 6px; border-radius: var(--r-full);
  background: var(--surface-2); color: var(--label-3);
  font: inherit; font-size: 10.5px; font-weight: 750; letter-spacing: 0.01em;
  transition: background 0.15s, color 0.15s;
}
.rk-done-btn svg { width: 11px; height: 11px; }
.rk-done-btn:hover, .rk-done-btn:focus-visible { background: var(--border-2); outline: none; }
.rk-done-btn.is-done { background: var(--green-light); color: var(--green); }
.rk-done-btn.is-done:hover, .rk-done-btn.is-done:focus-visible { background: var(--green); color: #fff; }

/* Only a column that actually carries a saved order has anything to reset. */
.rk-reset {
  flex-shrink: 0; border: none; background: none; padding: 2px 4px; cursor: pointer;
  font: inherit; font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.03em;
  color: var(--label-3); border-radius: 6px; display: none; transition: color 0.13s, background 0.13s;
}
.rk-col.is-custom .rk-reset, .rk-unit.is-custom .rk-reset { display: inline-block; }
.rk-reset:hover, .rk-reset:focus-visible { color: var(--accent); background: var(--accent-light); outline: none; }

.rk-list { list-style: none; margin: 0; padding: 0; user-select: none; -webkit-user-select: none; }
.rk-item {
  display: grid; grid-template-columns: 24px 28px minmax(0,1fr) auto auto;
  align-items: center; gap: 7px; padding: 6px 9px;
  border-top: 1px solid var(--border-2); background: var(--surface);
  transition: transform 0.19s cubic-bezier(0.32,0.72,0,1), background 0.22s, box-shadow 0.19s;
}
.rk-item:first-child { border-top: none; }
.rk-list:not(.is-readonly) .rk-item:hover { background: var(--surface-2); }
.rk-num {
  width: 24px; height: 24px; border-radius: 50%; display: flex; align-items: center; justify-content: center;
  font-size: 11.5px; font-weight: 800; font-variant-numeric: tabular-nums;
  color: var(--label-3); background: var(--surface-2);
}
.rk-jersey {
  text-align: right; font-size: 11.5px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: var(--label-3);
}
/* Only the whole-unit list mixes positions, so only it needs the tag — and
   only it gets the extra grid column to hold one. */
.rk-list[data-rk-list="__unit__"] .rk-item { grid-template-columns: 24px 30px 28px minmax(0,1fr) auto auto; }
.rk-pos {
  flex-shrink: 0; padding: 1px 5px; border-radius: 5px; text-align: center;
  background: var(--surface-2); color: var(--label-3);
  font-size: 9px; font-weight: 800; letter-spacing: 0.04em;
}
.rk-name {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 13px; font-weight: 600; letter-spacing: -0.01em; color: var(--label);
}
/* His three lights for this same game, unlabelled: ranking and grading are one
   judgement, so the coach should not have to switch views to see what he wrote. */
.rk-lights { display: inline-flex; gap: 4px; flex-shrink: 0; }
.rk-light { width: 7px; height: 7px; border-radius: 50%; background: var(--border); }
.rk-light--green { background: var(--green); }
.rk-light--amber { background: #fcba03; }
.rk-light--red   { background: var(--red); }
.rk-grip {
  width: 26px; height: 26px; flex-shrink: 0; display: flex; align-items: center; justify-content: center;
  border: none; border-radius: 6px; background: none; color: var(--label-3);
  cursor: grab; padding: 0; touch-action: none;   /* the handle drags; the rest of the row still scrolls */
  transition: color 0.13s, background 0.13s;
}
.rk-grip svg { width: 15px; height: 15px; }
.rk-grip:hover, .rk-grip:focus-visible { color: var(--indigo); background: var(--indigo-light); outline: none; }

/* The row under the pointer: lifted out of the list, following it exactly, so
   no transition of its own (grades.js drives its transform frame by frame).
   .is-settling keeps the lift for the glide back down after the drop. */
.rk-item.is-dragging, .rk-item.is-settling {
  position: relative; z-index: 3;
  background: var(--surface); border-radius: var(--r-sm);
  box-shadow: var(--shadow-md);
}
.rk-item.is-dragging { transition: none; }
.rk-list.is-sorting { cursor: grabbing; }
.rk-list.is-sorting .rk-grip { cursor: grabbing; }
body.rk-dragging { cursor: grabbing; }

/* Rankings launcher + panel — indigo, the one hue not already spoken for by
   injuries (red), penalties (gold), practices (green) or grades (slate). */
.gd-rk-btn { margin-top: 12px; }
.gd-rk-btn:hover { border-color: var(--indigo); background: var(--indigo-light); }
.gd-rk-ic { background: var(--indigo-light); color: var(--indigo); }
.gd-rk-btn:hover .gd-rk-ic { background: var(--indigo); color: #fff; }
.rk-title-ic { background: var(--indigo-light); color: var(--indigo); }
.rk-tabs { margin: 4px 0 20px; }
.rk-tabs .tab { display: inline-flex; align-items: center; gap: 7px; }
.rk-tabs .tab.active { background: var(--surface); color: var(--indigo); box-shadow: var(--shadow-sm); }
.rk-tab-count {
  font-size: 11px; font-weight: 750; letter-spacing: 0; margin-left: 0;
  color: var(--label-3); background: var(--surface-2);
  border-radius: var(--r-full); padding: 1px 7px; font-variant-numeric: tabular-nums;
}
.rk-tab-count.is-complete { color: var(--green); background: var(--green-light); }
.rk-tab-count:empty { display: none; }

/* Position Groups / Unit Ranking — the toggle inside one side's panel, one
   level below the Offense / Defense / Special Teams tabs above it. Same
   .tabs/.tab pill, sized down slightly so the hierarchy still reads as a
   sub-choice rather than a sibling of the side tabs. */
.rk-subtabs { margin: 2px 0 16px; }
.rk-subtabs .tab { font-size: 11.5px; padding: 4px 10px; }
.rk-subtabs .tab.active { background: var(--surface); color: var(--indigo); box-shadow: var(--shadow-sm); }
.rk-subpanel:not(.is-active) { display: none; }

/* The whole-side, position-agnostic order — its own sub-tab, the coordinator's
   single read of the entire unit. Set apart with its own icon-and-heading
   header rather than the terse group-card style, since it's a different kind
   of ranking, not one more position. */
.rk-unit {
  border: 1px solid var(--border-2); border-radius: var(--r-md);
  background: var(--surface); overflow: hidden; margin-bottom: 16px;
}
.rk-unit-head {
  display: flex; align-items: center; gap: 10px; padding: 10px 12px;
  background: var(--surface-2); border-bottom: 1px solid var(--border-2);
}
.rk-unit.is-done .rk-unit-head { background: var(--green-light); }
.rk-unit-ic {
  flex-shrink: 0; display: inline-flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: var(--r-sm);
  background: var(--indigo-light); color: var(--indigo);
}
.rk-unit-ic svg { width: 16px; height: 16px; }
.rk-unit-heading { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.rk-unit-title { font-size: 12.5px; font-weight: 780; color: var(--label); }
.rk-unit-sub { font-size: 10.5px; font-weight: 550; color: var(--label-3); }

/* Three lists sharing the width two normally have (the offensive line, ranked
   by a coordinator and both of its position coaches): the same rows, trimmed —
   smaller badges, tighter gaps — so a name still has room before it has to
   ellipsize. Only above the breakpoint: below it every column stacks to full
   width anyway, and these would otherwise outrank the mobile sizes below. */
@media (min-width: 701px) {
  .rk-cols--3 .rk-col-head { padding: 7px 7px; }
  .rk-cols--3 .rk-item { grid-template-columns: 22px 24px minmax(0,1fr) auto auto; gap: 5px; padding: 6px 7px; }
  .rk-cols--3 .rk-num { width: 22px; height: 22px; font-size: 11px; }
  .rk-cols--3 .rk-name { font-size: 12.5px; }
  .rk-cols--3 .rk-grip { width: 24px; height: 24px; }
}

@media (max-width: 700px) {
  .rk-groups { grid-template-columns: 1fr; gap: 12px; }
  .rk-cols { grid-template-columns: 1fr; }
  .rk-col + .rk-col { border-left: none; border-top: 1px solid var(--border-2); }
  .rk-item { grid-template-columns: 22px 26px minmax(0,1fr) auto auto; gap: 6px; padding: 6px 8px; }
  .rk-list[data-rk-list="__unit__"] .rk-item { grid-template-columns: 22px 26px 26px minmax(0,1fr) auto auto; }
  .rk-num { width: 22px; height: 22px; font-size: 11px; }
  .rk-name { font-size: 12.5px; }
}

/* ─── Player Info profile (js/dashboard/player.js) ───────────────────────────
   The popover a search hit opens: who he is, four numbers, then his season as
   charts. The charts themselves reuse the `.tr-*` chart primitives above —
   same axes, same lines, same legend — so a grade trend and a rank trend read
   as one component wherever they appear. */
.player-modal { max-width: 720px; }
.pl-profile { display: flex; flex-direction: column; gap: 24px; }

.pl-hero { display: flex; align-items: center; gap: 14px; }
.pl-hero-num {
  width: 46px; height: 46px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--indigo-light); color: var(--indigo);
  font-size: 18px; font-weight: 800; font-variant-numeric: tabular-nums;
}
.pl-hero-main { min-width: 0; }
.pl-hero-pos { display: flex; flex-wrap: wrap; gap: 6px; }
.pl-chip {
  font-size: 10.5px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.03em;
  padding: 4px 10px; border-radius: var(--r-full);
  background: var(--slate-light); color: var(--slate);
}
.pl-chip--offense { background: var(--gold-light); color: var(--gold); }
.pl-chip--defense { background: var(--accent-light); color: var(--accent); }

.pl-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; }
.pl-kpi { background: var(--surface-2); border: 1px solid var(--border-2); border-radius: var(--r-md); padding: 11px 13px; }
.pl-kpi-val { font-size: 20px; font-weight: 780; letter-spacing: -0.02em; line-height: 1; font-variant-numeric: tabular-nums; }
.pl-kpi-lbl { font-size: 11.5px; font-weight: 600; color: var(--label-2); margin-top: 6px; }
.pl-kpi-sub { font-size: 10.5px; color: var(--label-3); margin-top: 2px; }

.pl-sec { display: flex; flex-direction: column; gap: 10px; }
.pl-sec-head { display: flex; align-items: baseline; gap: 10px; }
.pl-sec-title { font-size: 12px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.05em; color: var(--label-2); }
.pl-sec-note { margin-left: auto; font-size: 11.5px; font-weight: 600; color: var(--label-3); font-variant-numeric: tabular-nums; }
.pl-chart { min-height: 190px; }
.pl-chart--rank { min-height: 172px; }
.pl-rank-block { display: flex; flex-direction: column; gap: 18px; }
.pl-rank-block + .pl-rank-block { border-top: 1px solid var(--border-2); padding-top: 16px; }
.pl-rank-side { font-size: 11px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.05em; color: var(--indigo); }
.pl-sub { display: flex; flex-direction: column; gap: 8px; }
.pl-sub-title { font-size: 12.5px; font-weight: 650; color: var(--label); }

/* Every light he has been given, in one bar — the shape of a season a line
   chart sixty practices wide can't show at a glance. */
.pl-dist { display: flex; flex-direction: column; gap: 10px; padding-top: 2px; }
.pl-dist-rows { display: flex; flex-direction: column; gap: 7px; }
.pl-dist-row { display: grid; grid-template-columns: 82px minmax(0, 1fr) 38px; align-items: center; gap: 10px; }
.pl-dist-cat { font-size: 12px; font-weight: 600; color: var(--label-2); }
.pl-dist-score { font-size: 12px; font-weight: 750; color: var(--label); text-align: right; font-variant-numeric: tabular-nums; }
.pl-dist-bar { display: flex; height: 10px; border-radius: var(--r-full); overflow: hidden; background: var(--surface-2); }
.pl-dist-seg { height: 100%; }
.pl-dist-seg--green { background: var(--green); }
.pl-dist-seg--amber { background: var(--amber); }
.pl-dist-seg--red   { background: var(--red); }
.pl-dist-keys { display: flex; flex-wrap: wrap; gap: 16px; }
.pl-dist-key { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--label-2); }
.pl-dist-key strong { color: var(--label); font-weight: 750; font-variant-numeric: tabular-nums; }
.pl-dist-pct { color: var(--label-3); font-variant-numeric: tabular-nums; }
.pl-dist-dot { width: 8px; height: 8px; border-radius: 50%; }
.pl-dist-dot--green { background: var(--green); }
.pl-dist-dot--amber { background: var(--amber); }
.pl-dist-dot--red   { background: var(--red); }

.pl-none { padding: 18px 0; text-align: center; font-size: 13px; color: var(--label-3); }

.pl-pens { display: flex; flex-direction: column; gap: 2px; }
.pl-pen { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; align-items: center; gap: 12px; padding: 9px 11px; border-radius: var(--r-md); }
.pl-pen:nth-child(odd) { background: var(--surface-2); }
.pl-pen-unit {
  font-size: 10px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.03em;
  white-space: nowrap; padding: 3px 9px; border-radius: var(--r-full);
  color: var(--u); background: color-mix(in srgb, var(--u) 15%, transparent);
}
.pl-pen-call { display: block; font-size: 13px; font-weight: 650; }
.pl-pen-where { display: block; font-size: 11.5px; color: var(--label-2); margin-top: 1px; }
.pl-pen-yds { font-size: 12.5px; font-weight: 750; color: var(--label-2); font-variant-numeric: tabular-nums; white-space: nowrap; }

@media (max-width: 560px) {
  .pl-kpis { grid-template-columns: 1fr 1fr; }
  .pl-pen { grid-template-columns: auto minmax(0, 1fr); }
  .pl-pen-yds { grid-column: 2; justify-self: start; }
}
.pl-sub-of { font-size: 11px; font-weight: 600; color: var(--label-3); margin-left: 7px; font-variant-numeric: tabular-nums; }
.pl-chart--bars { min-height: 168px; }
.pl-bar { transition: opacity 0.14s; }
.pl-bar:hover { opacity: 0.78; }
.pl-bar-val { font-size: 10px; font-weight: 750; fill: var(--label-2); font-family: var(--font); font-variant-numeric: tabular-nums; }
.pl-legend-sq { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }

/* Chart furniture takes no pointer events, so the transparent column bands
   behind them (`.pl-pick`, drawn first) stay clickable everywhere the dots
   themselves aren't. Clicking a column opens that day's grade table. */
.tr-line, .tr-grid, .tr-axis-x, .tr-axis-y, .pl-bar-val { pointer-events: none; }
.pl-pick { fill: transparent; cursor: pointer; transition: fill 0.12s; }
.pl-pick:hover { fill: var(--surface-2); }
.tr-dot--pick { cursor: pointer; }
/* Where that glide lands: his row in the grade table, held long enough to find. */
.gd-grade-row.is-spotlight { animation: pl-spotlight 2.2s ease both; }
@keyframes pl-spotlight {
  0%, 66% { background: var(--indigo-light); box-shadow: inset 2px 0 0 var(--indigo); }
  100%    { background: transparent; box-shadow: inset 2px 0 0 transparent; }
}

/* ─── Transfer Portal profile (js/dashboard/portal.js) ───────────────────────
   The popover a portal search hit opens. Reuses the Player Info profile's own
   `.pl-profile` / `.pl-sec` / `.pl-kpis` shell above — same tile and section
   language for projections/financials as for grades/rankings — since the
   data underneath (Teleport's own client modules, see teleport-bridge.js) is
   the only thing genuinely new here. */
.portal-modal { max-width: 640px; }
.pf-hero { display: flex; align-items: center; gap: 14px; }
.pf-hero-main { flex: 1; min-width: 0; }
.pf-hero-line { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 650; letter-spacing: -0.01em; }
.prow-pos {
  font-size: 10.5px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.02em;
  padding: 2px 8px; border-radius: var(--r-full);
  background: var(--indigo-light); color: var(--indigo); flex-shrink: 0;
}
.pf-hero-meta { display: flex; flex-wrap: wrap; gap: 4px 10px; margin-top: 3px; font-size: 12px; color: var(--label-2); }
.pf-hero-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
.pff-btn {
  display: inline-flex; align-items: center; gap: 3px;
  height: 30px; padding: 0 10px 0 12px;
  font-size: 12px; font-weight: 700; letter-spacing: 0.01em;
  color: var(--label); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-full);
  transition: color 0.15s, background 0.15s, border-color 0.15s;
}
.pff-btn:hover { color: var(--accent); background: var(--accent-light); border-color: transparent; }
.pff-btn svg { width: 12px; height: 12px; }
.pf-notes-btn { position: relative; }
.pf-notes-btn .card-notes-count { margin-left: 4px; }

/* Evaluation tone pills — Teleport's own grade vocabulary, so the same
   `pill pill--<tone>` markup Teleport's ui/pills.js already renders needs no
   translation here. */
.pill { display: inline-flex; align-items: center; font-size: 10.5px; font-weight: 750; text-transform: uppercase; letter-spacing: 0.02em; padding: 3px 9px; border-radius: var(--r-full); white-space: nowrap; }
.pill--green     { background: var(--green-light);  color: var(--green); }
.pill--gold      { background: var(--gold-light);   color: var(--gold);  }
.pill--slate     { background: var(--slate-light);  color: var(--slate); }
.pill--red       { background: var(--red-light);    color: var(--red);   }
.pill--ungraded  { background: var(--surface-2);    color: var(--label-3); }

.pf-eval { padding: 11px 0; border-top: 1px solid var(--border-2); }
.pf-eval:first-child { border-top: none; padding-top: 0; }
.pf-eval-head { display: flex; align-items: center; flex-wrap: wrap; gap: 7px; }
.pf-eval-spacer { flex: 1; }
.pf-eval-meta { font-size: 11.5px; color: var(--label-3); white-space: nowrap; }
.pf-eval-body { margin-top: 6px; font-size: 13px; line-height: 1.5; color: var(--label); white-space: pre-wrap; }

@media (max-width: 560px) {
  .pf-hero { flex-wrap: wrap; }
  .pf-hero-actions { width: 100%; justify-content: flex-end; }
}
