/* game-charts.css -- the three game charts, wherever they are drawn.
 * ===========================================================================
 * Formation Mix, Series Arc and Play Frequency & Yardage started life inside
 * index.html's Last Game panel. On 2026-08-12 Dave asked for them on the Game
 * Report as its top item, with the dashboard becoming a season rollup -- so
 * the same three charts now render on two pages against different slices of
 * the same data.
 *
 * That is exactly the shape this repo has been burned by twice: one block of
 * styling copy-pasted into a second page, drifting silently until the two
 * surfaces disagree about what a colour means. The identity here is
 * load-bearing -- a formation's hue is the JOIN between the mix bars, the arc
 * cells and the play table's hover -- so two copies of the palette would not
 * just look different, they would break the cross-highlighting on whichever
 * page fell behind.
 *
 * Rules moved here VERBATIM, including the comments that record what was
 * measured. Anything index.html still owns alone (the card chrome, the hero
 * head, the tiles) stayed there.
 *
 * Include after the page's own <style> so a page can still override:
 *   <link rel="stylesheet" href="game-charts.css">
 * ========================================================================= */

/* The categorical palette. Eight fixed slots, assigned busiest-formation-first
   and never reassigned within a render, folding to neutral past slot 8. */
:root[data-theme="dark"]{
  --dash-series-1:#3987e5; --dash-series-2:#d95926; --dash-series-3:#199e70; --dash-series-4:#c98500;
  /* Slot 6 is #008300, not #4caf4c. The other fifteen values across both themes match this
     project's validated data-viz reference palette exactly; this one had drifted, which meant
     the dark set on screen was NOT the set that was validated -- the published figures (worst
     adjacent colourblind separation 8.4 dark, against a floor of 8) were measured with #008300
     in this slot. A lighter, less saturated green there eats into that margin against its
     neighbours, and the whole point of using a validated order is that it has been checked as
     a SET rather than swatch by swatch.
     Deliberately the same value as light slot 6: this hue is already at the dark band's edge,
     and the reference keeps it common to both modes rather than stepping it. */
  --dash-series-5:#d55181; --dash-series-6:#008300; --dash-series-7:#9085e9; --dash-series-8:#e66767;
}
:root[data-theme="light"]{
  --dash-series-1:#2a78d6; --dash-series-2:#eb6834; --dash-series-3:#1baf7a; --dash-series-4:#eda100;
  --dash-series-5:#e87ba4; --dash-series-6:#008300; --dash-series-7:#4a3aa7; --dash-series-8:#e34948;
}

.dash-subhead{font-family:'Roboto Condensed',sans-serif;font-weight:700;font-size:11px;text-transform:uppercase;letter-spacing:0.05em;color:var(--dim);margin:18px 0 10px;}
.dash-subhead:first-of-type{margin-top:0;}
.dash-blurb{font-size:12px;color:var(--dim);margin:0 0 16px;line-height:1.5;}
.dash-empty{text-align:center;padding:26px 16px;color:var(--dim);font-size:13px;line-height:1.6;}
/* Empty states carry "Open Play Canvas ›"-style links, so they need their own
   link styling -- inherited link colour on a dim panel is unreadable. */
.dash-empty a{color:var(--primary);font-weight:700;text-decoration:none;}
.dash-empty a:hover{text-decoration:underline;}
.dash-honesty{font-size:11px;color:var(--dim);font-style:italic;margin-top:16px;padding-top:12px;border-top:1px solid var(--outline-variant);}

/* ---- Formation mix -------------------------------------------------------
   Magnitude (bar length) + a same-hue opacity split for Right/Left. The SAME
   formation reads as the same colour everywhere on the panel; strength is a
   lower-emphasis layer on top, not a second competing identity. */
.dash-mix{display:flex;flex-direction:column;gap:7px;}
/* Name column widened from 120px to 168px: it now carries BOTH strength names of a family
   ("Texas / Houston"), and at the old width the second one ellipsed away -- which defeats the
   point of naming it. */
.dash-mix-row{display:grid;grid-template-columns:minmax(70px,168px) 1fr 34px;align-items:center;gap:10px;font-size:12px;}
.dash-mix-name{font-family:'Roboto Condensed',sans-serif;font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
/* The other strength name is the same formation, so it is a lower-emphasis continuation of the
   label rather than a second identity competing with it. */
.dash-mix-name-alt{font-weight:400;color:var(--dim);}
/* Bar height up from 14px to 17px so a two-digit count fits inside its own half legibly. */
.dash-mix-track{height:17px;border-radius:4px;background:var(--surface-container-high);overflow:hidden;display:flex;}
.dash-mix-seg{height:100%;display:flex;align-items:center;justify-content:center;}
/* Per-side counts, sitting inside their own half of the bar. White with a soft dark shadow
   rather than a theme token: these sit on eight different identity hues at two opacities, and
   the shadow is what keeps them readable on the lightest of them without picking a colour per
   hue. pointer-events:none so the row's title tooltip still works across the whole bar. */
.dash-mix-seg-n{
  font-family:'Roboto Condensed',sans-serif;font-size:10.5px;font-weight:700;line-height:1;
  color:#fff;text-shadow:0 1px 2px rgba(0,0,0,0.55);pointer-events:none;
  font-variant-numeric:tabular-nums;
}
/* A hard divider between the two halves, so the split reads as a split even where both segments
   carry the same hue. Uses --surface-container-high (the track's own background) so the divider
   reads as a gap cut through the bar. NOT --surface, which these pages do not define -- the same
   undefined-variable trap that produced an unreadable toast on Game Day Canvas. */
.dash-mix-seg-split{box-shadow:inset -2px 0 0 var(--surface-container-high);}
.dash-mix-count{text-align:right;color:var(--dim);font-variant-numeric:tabular-nums;}
/* The mix's own dim state, mirroring .dash-arc-dim. Applied to the ROW rather than to the bar
   alone so the name fades with its bar -- a greyed bar beside a full-strength label reads as a
   rendering fault rather than as "not this one". Slightly less aggressive than the arc's 0.15:
   these rows carry text that should stay legible while dimmed. */
.dash-mix-row{transition:opacity .12s ease;}
.dash-mix-row.dash-mix-dim{opacity:0.25;}
.dash-mix-row[data-formation-key]{cursor:default;border-radius:4px;}
.dash-mix-row[data-formation-key]:hover,
.dash-mix-row[data-formation-key]:focus-visible{background:var(--surface-container-high);outline:none;}
.dash-mix-legend{display:flex;gap:16px;font-size:10.5px;color:var(--dim);margin:10px 0 4px;}
.dash-mix-legend span{display:inline-flex;align-items:center;gap:5px;}
.dash-mix-legend i{width:9px;height:9px;border-radius:2px;display:inline-block;}
/* On a phone the 168px name column is most of the width, leaving the bar with
   nothing to be long in -- which turns a magnitude chart into a list. */
@media (max-width:640px){
  .dash-mix-row{grid-template-columns:76px 1fr 30px;}
}

/* ---- Series arc ----------------------------------------------------------
   Categorical identity (colour = formation), one cell per captured snap, no
   axis. A formation past the 8 fixed slots folds into "Other" rather than
   generating a new hue. */
.dash-arc{display:flex;gap:2px;flex-wrap:wrap;margin-bottom:8px;}
.dash-arc-cell{width:9px;height:22px;border-radius:2px;flex:0 0 auto;}
.dash-arc-gap{width:9px;flex:0 0 auto;}
/* Dimming the REST, not brightening the match -- in a dense strip of small cells, removing
   contrast from everything else isolates a formation far more clearly than adding a little to
   one. Fast transition because this fires on mouse movement across a list. */
.dash-arc-cell{transition:opacity .12s ease;}
/* Outside the chosen scope (a quarter or a half): still drawn, so the arc keeps its
   comparison, but quiet. Declared BEFORE the hover dim so that on a cell that is both,
   the hover wins. */
.dash-arc-cell.dash-arc-out{opacity:0.22;}
.dash-arc-band-label.dash-arc-out{opacity:0.45;}
.dash-arc-cell.dash-arc-dim{opacity:0.15;}
.dash-arc-legend{display:flex;flex-wrap:wrap;gap:8px 14px;font-size:10.5px;color:var(--dim);}
.dash-arc-legend span{display:inline-flex;align-items:center;gap:5px;}
.dash-arc-legend i{width:9px;height:9px;border-radius:2px;display:inline-block;flex:0 0 auto;}

/* THE BANDED ARC -- Dave, 2026-08-12: "Group the series by quarter - row so
   that at the end of the game there will be four rows."
   -------------------------------------------------------------------------
   A label rail down the left and one lane per band, rather than four separate
   .dash-arc strips stacked. The rail is a grid column, so every lane starts at
   the same x and the eye can read DOWN the quarters -- comparing where in each
   quarter the offence was in a given formation is the whole reason to band it,
   and four independently-indented strips would make that comparison eyeball
   work.

   An EMPTY band still renders its row (see the JS): a quarter that has not
   happened yet and a quarter nobody captured look identical if the row is
   simply absent, and the first is normal while the second is a problem. */
.dash-arc-bands{display:grid;grid-template-columns:auto 1fr;gap:7px 12px;align-items:center;margin-bottom:10px;}
.dash-arc-band-label{
  font-family:'Roboto Condensed',sans-serif;font-weight:700;font-size:11px;
  letter-spacing:.06em;text-transform:uppercase;color:var(--dim);white-space:nowrap;
  display:flex;align-items:baseline;gap:6px;
}
.dash-arc-band-label b{color:var(--on-background);font-size:13px;}
.dash-arc-band-n{font-weight:400;font-size:10.5px;font-variant-numeric:tabular-nums;}
.dash-arc-band{display:flex;gap:2px;flex-wrap:wrap;min-height:22px;align-content:center;}
.dash-arc-band-empty{font-size:11px;color:var(--dim);font-style:italic;}

/* ---- Play frequency & yardage -------------------------------------------- */
.dash-grid-wrap{overflow-x:auto;}
.dash-ff-grid{border-collapse:separate;border-spacing:2px;font-size:11.5px;width:100%;}
.dash-ff-grid th{
  font-family:'Roboto Condensed',sans-serif;font-weight:700;font-size:10px;color:var(--dim);
  text-transform:uppercase;letter-spacing:0.02em;padding:4px 6px;text-align:center;white-space:nowrap;
}
.dash-ff-grid th.dash-ff-rowhead{text-align:left;}
.dash-ff-grid td{padding:0;text-align:center;}
.dash-ff-grid td.dash-ff-rowname{text-align:left;}
.dash-ff-cell{
  width:100%;height:30px;display:flex;align-items:center;justify-content:center;
  border-radius:4px;font-variant-numeric:tabular-nums;font-size:11.5px;
}
.dash-ff-cell.z{color:var(--dim);opacity:0.5;}
.dash-ff-rowname{font-family:'Roboto Condensed',sans-serif;font-weight:600;font-size:12px;padding-right:8px !important;white-space:nowrap;}
/* The play name stays put while the numbers scroll under it. Same rule Dave
   asked for on the Game Report's defender tables, applied here because this is
   the other table in the app that goes wider than its box -- on the dashboard
   at phone width, and in the Game Report's one-third column.
   A sticky cell paints over what slides beneath it, so it needs its own
   background; this table uses border-collapse:separate, so unlike the report's
   grids its border survives and no shadow stand-in is needed. */
.dash-play-table .dash-ff-rowname{
  position:sticky;left:0;z-index:2;background:var(--surface-container);
}
.dash-play-table thead .dash-ff-rowhead{position:sticky;left:0;z-index:3;background:var(--surface-container);}
.dash-ff-total{font-weight:700;color:var(--dim);}

/* Formation breakdown under each play. Indented and de-emphasised so the play rows still scan
   as the list, with the sub-lines reading as detail hanging off them rather than as peers --
   otherwise a play called from three formations looks like four separate plays. */
.dash-play-sub > td{padding-top:2px;padding-bottom:2px;}
/* Indented past the CHEVRON, not level with it. The chevron (17px) plus its 3px gap means
   the play NAME starts ~20px into the cell, so an 18px indent left the formation sitting under
   the arrow and reading as a peer of the play rather than a child of it. */
.dash-play-sub .dash-ff-rowname{padding-left:34px;font-weight:400;color:var(--dim);font-size:11px;}
.dash-play-sub .dash-ff-cell{color:var(--dim);font-size:11px;}
/* A visible tie between a play and its formations, so the group reads as one block. */
.dash-play-main > td{padding-top:9px;border-top:1px solid var(--outline-variant);}
.dash-play-table tbody tr:first-child > td{border-top:none;}
/* Roll-up control on the play name. A real button so it is keyboard-reachable and can carry
   aria-expanded, but styled as the row label it replaces. */
/* THE HIT AREA IS BIGGER THAN THE INK. Measured on the live report: the chevron
   button was 28px tall, well under the 44px a finger wants, and it is the
   control a coach taps most often on this table. Padding grows the target and
   an equal negative margin takes the growth back out of the layout, so the row
   does not get taller -- the alternative, a genuinely 44px row, would add
   nearly 40% to the height of a table that already runs long. */
.dash-play-toggle{
  display:inline-flex;align-items:center;gap:3px;
  background:none;border:none;cursor:pointer;
  /* Sized to stay INSIDE its own row. At 9px of padding the target measured
     60px in a 51px row, which means two stacked chevrons overlap by about 9px
     and a tap in that band lands on whichever paints last -- trading a target
     that is too small for one that hits the wrong play. 5px keeps it under the
     row while still nearly doubling the original 28px. */
  padding:5px 6px 5px 0;margin:-5px 0;
  font:inherit;color:inherit;text-align:left;
}
.dash-play-chev{font-size:17px;line-height:1;color:var(--dim);transition:transform .15s ease;flex:0 0 auto;}
/* Collapsed points right, expanded points down -- the chevron reports the state rather than
   the action, which is the convention every file tree uses. */
.dash-play-toggle[aria-expanded="false"] .dash-play-chev{transform:rotate(-90deg);}
/* Only while rolled up: expanded, the sub-lines themselves say it better than a count. */
.dash-play-looks{font-weight:400;color:var(--dim);font-size:11px;}
.dash-play-toggle[aria-expanded="true"] .dash-play-looks{display:none;}
.dash-row-hidden{display:none;}

/* PLAY TABLE ALIGNMENT. Numbers are RIGHT-aligned, not centred -- they were
   centred by inheritance from the Formation x front heatmap this table
   replaced, where cells were coloured squares and centring was correct.
   Centred numbers give the eye no vertical edge to land on, which is what made
   a long row feel long (measured: 246px of empty space between the end of a
   play name and its Calls figure). Not zebra striping: both MD3 and Salesforce
   Lightning right-align numerics and lead with dividers plus a hover state,
   and striping would cut across the top-border that groups a play with its
   formations. Scoped to .dash-play-table so the heatmap styles stay intact if
   that chart ever comes back. */
.dash-play-table th:not(.dash-ff-rowhead){text-align:right;padding-right:10px;}
.dash-play-table td:not(.dash-ff-rowname) .dash-ff-cell{justify-content:flex-end;padding-right:10px;}
/* Stops the table stretching to fill the card, which is what actually created the long run.
   `.dash-ff-grid` sets width:100%, so every column absorbs a share of the slack -- capping the
   Play column alone just handed that space to the numeric columns and pushed the figures
   further right (measured: 54%->41% Play, and the gap moved 246px->245px, i.e. not at all). */
.dash-play-table{width:100%;min-width:min(100%,430px);}
/* Row tracking, the aid BOTH design systems prefer over striping: invisible until used, and it
   costs no ink at rest. Matters most on the sub-lines, the longest run to the numbers. */
.dash-play-table tbody tr:hover .dash-ff-cell,
.dash-play-table tbody tr:hover td.dash-ff-rowname{background:var(--surface-container-high);}
.dash-play-coverage{font-size:11px;color:var(--dim);margin-top:8px;font-style:italic;}
