/* ============================================================
   Prairie Falcons — WMD Defensive Dashboard

   Visual language: a working analytics tool, not a brochure.
   Flat surfaces, hairline rules, one accent used sparingly, and
   type that puts the number first. Red carries structure and
   brand; gold is reserved for the value a coach is looking for,
   so it never ends up competing with itself.
   ============================================================ */

/* Inter, served from this site rather than Google's: a font request to
   another company's server sends every visitor's address with it. Variable
   weight 400-900, the latin and latin-ext subsets (licence: fonts/OFL.txt). */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url(fonts/inter-latin-ext.woff2?v=34b9c504cab7) format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url(fonts/inter-latin.woff2?v=3100e775e861) format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* surfaces — layered by value rather than by gradient */
  --bg:         #0a0c0f;
  --surface:    #12151a;
  --surface-2:  #181c22;
  --surface-3:  #1f242b;
  --line:       #242a32;
  --line-soft:  #1b2027;

  --red:         #d81f2a;
  --red-bright:  #f03340;
  --red-deep:    #8f141c;
  --red-wash:    rgba(216,31,42,.12);
  --yellow:      #f5c518;
  --yellow-dim:  #c9a013;
  --yellow-wash: rgba(245,197,24,.10);

  --txt:    #f0f2f5;
  --txt-2:  #a3acb9;
  --txt-3:  #6f7a88;   /* 5.3:1 on --bg; small bold labels need 4.5:1 */

  --good: #35c48b;
  --bad:  #ff5d5d;

  /* Square. Rounded corners are the thing that made this read as a template
     rather than an instrument. */
  --r:    0;
  --r-sm: 0;
  --r-xs: 0;

  --font: "Inter", "Segoe UI", system-ui, -apple-system, sans-serif;

  /* one shared rhythm, so cards and grids line up across every tab */
  --gap: 16px;
  --pad: 20px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--txt);
  font-family: var(--font);
  font-size: 15px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

/* Numbers are the point of this thing: line them up everywhere. */
table, .k-value, .home-big, input[type=number] { font-variant-numeric: tabular-nums; }

a { color: var(--yellow); text-decoration: none; }
a:hover { text-decoration: underline; }

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

.wrap {
  /* No column cap: on a wide monitor a centred 1480px strip wastes half the
     display, and this is a tool people keep open, not a landing page. */
  max-width: none;
  margin: 0;
  padding: 0 24px 40px;
}
@media (max-width: 720px) { .wrap { padding: 0 14px 32px; } }

/* ---------- header ---------- */

header.top {
  position: sticky;
  top: 0;
  z-index: 40;
  background: var(--surface);
  border-bottom: 1px solid var(--line);
}
/* A thin brand rule instead of a glow — reads as chrome, not decoration. */
header.top::before {
  content: "";
  display: block;
  height: 3px;
  background: var(--red);
}

.top-inner {
  max-width: none;
  margin: 0;
  padding: 12px 24px 0;
}
@media (max-width: 720px) { .top-inner { padding: 10px 14px 0; } }

.brand {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

.crest {
  width: 38px;
  height: 38px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--red);
  display: grid;
  place-items: center;
  font-weight: 800;
  font-size: 15px;
  letter-spacing: -.2px;
  color: #fff;
}

.brand h1 {
  margin: 0;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: -.2px;
}

.brand .sub {
  margin: 1px 0 0;
  font-size: 12px;
  color: var(--txt-3);
  font-weight: 450;
}

.brand-spacer { flex: 1 1 auto; }

.season-pill {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .4px;
  padding: 5px 10px;
  border-radius: var(--r-xs);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--txt-2);
  white-space: nowrap;
}

/* ---------- tabs ---------- */

nav.tabs {
  display: flex;
  gap: 2px;
  margin-top: 12px;
  overflow-x: auto;
  scrollbar-width: none;
}
nav.tabs::-webkit-scrollbar { display: none; }

.tab {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--txt-3);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 10px 14px 12px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  white-space: nowrap;
  transition: color .12s, border-color .12s;
}
.tab:hover { color: var(--txt-2); }
.tab.active { color: var(--txt); border-bottom-color: var(--red); }

/* ---------- panels ---------- */

.view { display: none; padding-top: 24px; }
.view.active { display: block; }

.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: var(--pad);
  display: flex;
  flex-direction: column;
}

/* A square chart in a card stretched by its taller neighbour leaves a pool of
   dead space underneath. Let it take the slack and sit in the middle. */
.chart-center {
  flex: 1;
  display: grid;
  place-items: center;
  min-height: 0;
}

.card-hd {
  display: flex;
  flex-wrap: wrap;            /* three buttons and a hint do not fit a 320px phone */
  align-items: center;
  gap: 10px;
  margin-bottom: 3px;
  min-height: 24px;
}

.card-hd h2 {
  margin: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--txt);
}

.card-hd .hint {
  font-size: 11.5px;
  color: var(--txt-3);
  margin-left: auto;
  text-align: right;
  font-weight: 500;
}

.card-note {
  margin: 8px 0 16px;
  font-size: 12.5px;
  color: var(--txt-3);
  line-height: 1.55;
  max-width: 68ch;
}

.grid { display: grid; gap: var(--gap); }
.g-2 { grid-template-columns: repeat(2, minmax(0,1fr)); }
.g-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
.g-3 { grid-template-columns: repeat(3, minmax(0,1fr)); }
.g-12-8 { grid-template-columns: minmax(0,1.5fr) minmax(0,1fr); }
.g-8-12 { grid-template-columns: minmax(0,1fr) minmax(0,1.5fr); }

/* Past this width, two-column rows leave columns too wide to read. Split the
   overview's paired rows into four so the space buys more information rather
   than bigger boxes. */
@media (min-width: 1800px) {
  .g-12-8, .g-8-12 { grid-template-columns: repeat(2, minmax(0,1fr)); }
}
@media (max-width: 1400px) { .g-4 { grid-template-columns: repeat(2, minmax(0,1fr)); } }
@media (max-width: 1080px) {
  .g-2, .g-3, .g-4, .g-12-8, .g-8-12 { grid-template-columns: minmax(0,1fr); }
}

/* ---------- stat strip ---------- */

/* One connected strip rather than six floating cards: it reads as a
   scoreboard, and the shared rules keep the eye moving sideways. */
.kpis {
  display: grid;
  grid-template-columns: repeat(6, minmax(0,1fr));
  gap: 0;
  margin-bottom: var(--gap);
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  overflow: hidden;
}
@media (max-width: 1180px) { .kpis { grid-template-columns: repeat(3, minmax(0,1fr)); } }
@media (max-width: 640px)  { .kpis { grid-template-columns: repeat(2, minmax(0,1fr)); } }

/* A four-tile strip (the Team tab) follows the same steps down. Held in a
   class rather than an inline style: inline, it outranked the media queries
   and stayed four-across on a phone, which clipped "Season complete". */
.kpis.k-4 { grid-template-columns: repeat(4, minmax(0,1fr)); }
@media (max-width: 900px) {
  .kpis.k-4 { grid-template-columns: repeat(2, minmax(0,1fr)); }
  .kpis.k-4 .kpi:nth-child(2n) { border-right: 0; }
  .kpis.k-4 .kpi:nth-child(-n+2) { border-bottom: 1px solid var(--line); }
}

.kpi {
  position: relative;
  padding: 15px 18px 16px;
  border-right: 1px solid var(--line);
}
.kpi:last-child { border-right: 0; }
@media (max-width: 1180px) {
  .kpi:nth-child(3n) { border-right: 0; }
  .kpi:nth-child(-n+3) { border-bottom: 1px solid var(--line); }
}
@media (max-width: 640px) {
  .kpi:nth-child(3n) { border-right: 1px solid var(--line); }
  .kpi:nth-child(2n) { border-right: 0; }
  .kpi:nth-child(-n+4) { border-bottom: 1px solid var(--line); }
}

.kpi .k-label {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .9px;
  text-transform: uppercase;
  color: var(--txt-3);
}
.kpi .k-value {
  margin-top: 8px;
  font-size: 27px;
  font-weight: 700;
  letter-spacing: -1.2px;
  line-height: 1.1;
  color: var(--txt);
  /* An opponent's name can land in a value tile. Let it wrap rather than run
     out of the tile and get cut in half. */
  overflow-wrap: anywhere;
}
/* The unit rides with the number, small and grey, so a points tile is never
   read as a count of anything. It never takes the tile's colour. */
.kpi .k-value .k-unit {
  margin-left: 5px;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--txt-3);
}
/* Gold marks the headline number only. Alternating it across the strip
   made the emphasis look arbitrary — every tile shouted equally. */
.kpis .kpi:first-child .k-value { color: var(--yellow); }
.kpi .k-sub {
  margin-top: 7px;
  font-size: 11.5px;
  color: var(--txt-3);
  line-height: 1.4;
}
.kpi .k-sub b { color: var(--txt-2); font-weight: 650; }

/* The asterisk under the strip. Quiet, but it sits with the numbers it
   explains rather than at the bottom of the page where nobody reads it. */
.kpi-note {
  margin: -6px 0 var(--gap);
  padding: 8px 2px 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--txt-3);
}
.kpi-note b { color: var(--txt-2); font-weight: 650; }
/* Inside a card the strip has no outer margin of its own to cancel. */
.card > .kpi-note { margin: 10px 0 0; padding: 0; }

/* The second strip on the game page — what the defense gave up. Ruled off
   from the WMD numbers above it, because it is a different kind of number:
   charted off the game sheet, not earned in the scoring system. */
.allowed-strip {
  margin-top: 16px;
  padding-top: 14px;
  border-top: 1px solid var(--line);
}

/* ---------- tables ---------- */

.table-scroll { overflow-x: auto; margin: 0 calc(var(--pad) * -1); padding: 0 var(--pad); }

table.tbl {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

table.tbl th {
  text-align: right;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--txt-3);
  padding: 0 10px 9px;
  border-bottom: 1px solid var(--line);
  white-space: nowrap;
}
table.tbl th.l, table.tbl td.l { text-align: left; }
/* A second line under a column heading, for saying what the column is made
   of where the heading alone leaves it ambiguous. */
table.tbl th .th-sub {
  display: block;
  margin-top: 3px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .3px;
  text-transform: none;
  color: var(--txt-3);
  opacity: .85;
}
table.tbl th.sortable { cursor: pointer; user-select: none; }
table.tbl th.sortable:hover { color: var(--txt-2); }
table.tbl th.sorted { color: var(--yellow); }

table.tbl td {
  text-align: right;
  padding: 8px 10px;
  border-bottom: 1px solid var(--line-soft);
  color: var(--txt-2);
  white-space: nowrap;
}
table.tbl tbody tr:hover td,
table.tbl tr:hover td { background: rgba(255,255,255,.022); }
table.tbl tr:last-child td { border-bottom: 0; }

td.name { color: var(--txt); font-weight: 600; cursor: pointer; }
td.name:hover { color: var(--yellow); }

th.total { color: var(--txt); }
td.total { color: var(--yellow); font-weight: 700; }
td.zero { color: var(--txt-3); opacity: .4; }

.rank {
  display: inline-grid;
  place-items: center;
  width: 19px; height: 19px;
  border-radius: var(--r-xs);
  font-size: 11px;
  font-weight: 700;
  background: var(--surface-3);
  color: var(--txt-3);
}
.rank.r1 { background: var(--yellow); color: #1a1400; }
.rank.r2 { background: #8e97a4; color: #0f1216; }
.rank.r3 { background: #9c6233; color: #120c06; }

.jersey {
  display: inline-block;
  min-width: 21px;
  margin-right: 8px;
  padding: 1px 4px;
  border-radius: var(--r-xs);
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-3);
  font-size: 11px;
  font-weight: 700;
  text-align: center;
}

.delta { font-weight: 700; font-size: 12px; }
.delta.up   { color: var(--good); }
.delta.down { color: var(--bad); }
.delta.flat { color: var(--txt-3); }

/* ---------- small bits ---------- */

.chip {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 3px 7px;
  border-radius: var(--r-xs);
  background: var(--surface-2);
  border: 1px solid var(--line);
  color: var(--txt-3);
}
.chip.red   { background: var(--red-wash);     border-color: rgba(216,31,42,.3);   color: #ff939a; }
.chip.gold  { background: var(--yellow-wash);  border-color: rgba(245,197,24,.28); color: var(--yellow); }
.chip.green { background: rgba(53,196,139,.1); border-color: rgba(53,196,139,.28); color: var(--good); }
.chip.warn  { background: rgba(245,165,36,.1);  border-color: rgba(245,165,36,.3);  color: #f5a524; }

/* One colour per position group, used wherever a group is named so the
   secondary is the same yellow on the roster sheet, the filter and the
   production chart. */
.chip.grp-DL { background: rgba(193,18,31,.12);  border-color: rgba(193,18,31,.35);  color: #ff939a; }
.chip.grp-LB { background: rgba(245,165,36,.12); border-color: rgba(245,165,36,.32); color: #f5a524; }
.chip.grp-DB { background: rgba(245,197,24,.12); border-color: rgba(245,197,24,.3);  color: var(--yellow); }
/* Other is deliberately the quietest of the four: it is a gap to close, not a
   unit to be proud of. */
.chip.grp-OTH { background: var(--surface-2); border-color: var(--line); color: var(--txt-3); }

/* ---------- segmented filter ---------- */

/* A row of mutually exclusive filters. One is always on, so it reads as a
   state the page is in rather than a set of switches to hunt through. */
.seg {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin: 0 0 14px;
}
.seg[hidden] { display: none; }
.seg-btn {
  appearance: none;
  cursor: pointer;
  font-family: inherit;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  padding: 7px 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--txt-3);
  transition: color .12s, border-color .12s, background .12s;
}
.seg-btn:hover { color: var(--txt-2); border-color: var(--surface-3); }
.seg-btn.on {
  color: var(--txt);
  background: var(--red-wash);
  border-color: rgba(216,31,42,.45);
}
.seg-n {
  margin-left: 7px;
  font-size: 11px;
  font-weight: 700;
  color: var(--txt-3);
}
.seg-btn.on .seg-n { color: #ff939a; }
.seg-note { font-size: 11px; color: var(--txt-3); margin-left: 4px; }

/* The roster sheet's position boxes are single tokens, so they do not need
   a full-width field and read better centred like the stat entry numbers. */
.r-pos { text-align: center; text-transform: uppercase; }

/* ---------- player card ---------- */

/* The card is a poster, not a data chart: it is meant to be seen whole, so
   it scales down to whatever room it has instead of scrolling sideways. The
   cap stops it stretching to the width of a desktop card, where the type
   would grow past the size the layout was drawn at. */
.player-card {
  max-width: 640px;
  margin: 0 auto;
}
.player-card svg.chart { width: 100%; height: auto; display: block; }

/* The selected week in the game log, and the row's invitation to pick one. */
.log-row { cursor: pointer; }
.log-row:hover td { background: #161a20; }
.log-row.on td { background: var(--yellow-wash); }
.log-row.on td:first-child { box-shadow: inset 2px 0 0 var(--yellow); }

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

/* #printRoot is empty and hidden until a card is sent to the printer. The
   print sheet then hides everything else on the page, so what comes out is
   the card and nothing around it — no navigation, no banner, no scrollbars
   baked into the paper. */
#printRoot { display: none; }

@media print {
  body.printing > *:not(#printRoot) { display: none !important; }
  body.printing #printRoot { display: block; }

  /* One player per sheet. The last card does not force a trailing blank. */
  #printRoot .print-card { break-after: page; page-break-after: always; }
  #printRoot .print-card:last-child { break-after: auto; page-break-after: auto; }
  #printRoot svg { width: 100%; height: auto; display: block; }

  /* The card already draws its own white background for print, and its
     panels carry meaning, so the browser must not helpfully drop them. */
  #printRoot, #printRoot * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  @page { margin: 12mm; }
}

.legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px 16px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
  font-size: 11.5px;
  color: var(--txt-2);
}
.legend span { display: inline-flex; align-items: center; gap: 6px; }
.legend i { width: 9px; height: 9px; border-radius: 0; display: inline-block; }

.bar-track {
  height: 4px;
  border-radius: 0;
  background: var(--surface-3);
  overflow: hidden;
  min-width: 56px;
}
.bar-fill { height: 100%; border-radius: 0; }

.mini-note {
  font-size: 11.5px;
  color: var(--txt-3);
  line-height: 1.6;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line-soft);
}
.mini-note b { color: var(--txt-2); font-weight: 650; }

.empty {
  padding: 32px 20px;
  text-align: center;
  color: var(--txt-3);
  font-size: 13px;
}

/* ---------- findings ---------- */

.insight {
  display: flex;
  gap: 12px;
  padding: 12px 0;
  border-bottom: 1px solid var(--line-soft);
  font-size: 13px;
  line-height: 1.6;
  color: var(--txt-2);
}
.insight:last-child { border-bottom: 0; padding-bottom: 2px; }
.insight .ic {
  flex: none;
  width: 22px; height: 22px;
  margin-top: 1px;
  border-radius: var(--r-xs);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 700;
  background: var(--surface-3);
  color: var(--txt-3);
}
.insight .ic.red { background: var(--red-wash); color: #ff939a; }
.insight .ic.ok  { background: rgba(53,196,139,.1); color: var(--good); }
.insight b { color: var(--txt); font-weight: 650; }

/* ---------- forms ---------- */

label.fld {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .8px;
  text-transform: uppercase;
  color: var(--txt-3);
  margin-bottom: 6px;
}

input[type=text], input[type=password], input[type=number], input[type=date], select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--txt);
  font-family: inherit;
  font-size: 13.5px;
  /* Stated rather than left to "normal" so every field in a row works out to
     the same height from the same arithmetic: this line plus the padding
     plus the border. A row bottom-aligns its fields, so one field type that
     is taller than its neighbours drags its own label up out of line. */
  line-height: 1.2;
  padding: 9px 11px;
  outline: none;
  transition: border-color .12s, box-shadow .12s;
}
input:focus, select:focus {
  border-color: var(--red);
  box-shadow: 0 0 0 3px var(--red-wash);
}
input[type=number] { text-align: center; }
input::placeholder { color: var(--txt-3); opacity: .7; }

/* iOS ignores the padding above and draws a date field in its own native
   box, about ten pixels taller than a text input or a select. Next to them
   in a row that is enough to shove the DATE label a line higher than its
   neighbours'. Drop the native box so the shared rule sets the height, and
   clear the margin the value's pseudo-element carries inside it. */
input[type=date] {
  -webkit-appearance: none;
  appearance: none;
  min-height: 0;
}
input[type=date]::-webkit-date-and-time-value {
  margin: 0;
  min-height: 0;
  line-height: 1.2;
  text-align: left;
}

.btn {
  appearance: none;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--txt);
  font-family: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 9px 15px;
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background .12s, border-color .12s;
}
.btn:hover { background: var(--surface-3); border-color: #323a44; }
.btn:active { transform: translateY(.5px); }

.btn.primary { background: var(--red); border-color: var(--red); color: #fff; }
.btn.primary:hover { background: var(--red-bright); border-color: var(--red-bright); }

.btn.gold { background: var(--yellow); border-color: var(--yellow); color: #1a1400; }
.btn.gold:hover { background: #ffd633; border-color: #ffd633; }

.btn.ghost { background: transparent; }
.btn.danger { color: #ff939a; border-color: rgba(216,31,42,.32); background: transparent; }
.btn.danger:hover { background: var(--red-wash); }
.btn.sm { padding: 6px 11px; font-size: 12px; }

/* Export buttons: quiet until you go looking for them. */
.btn.xp {
  padding: 3px 8px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .6px;
  color: var(--txt-3);
  background: transparent;
  border-color: var(--line-soft);
}
.btn.xp:hover { color: var(--yellow); border-color: rgba(245,197,24,.35); background: var(--yellow-wash); }
.card-hd .hint + .btn.xp { margin-left: 10px; }
.card-hd .btn.xp:first-of-type:not(.hint + .btn) { margin-left: auto; }

.row { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; }
.row > .grow { flex: 1 1 180px; min-width: 0; }
#gameActions[hidden] { display: none; }

/* ---------- stat entry ---------- */

.entry-tbl { width: 100%; border-collapse: collapse; font-size: 13px; }
.entry-tbl th {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  color: var(--txt-3);
  padding: 0 4px 8px;
  border-bottom: 1px solid var(--line);
  text-align: center;
}
.entry-tbl th:first-child { text-align: left; width: 196px; }
.entry-tbl td { padding: 4px 3px; border-bottom: 1px solid var(--line-soft); }
.entry-tbl tr:last-child td { border-bottom: 0; }
.entry-tbl td.pts {
  text-align: center;
  font-weight: 700;
  color: var(--yellow);
  min-width: 44px;
}
.entry-tbl input[type=number] { padding: 7px 4px; font-size: 13px; }
.entry-tbl input[type=checkbox] { width: 16px; height: 16px; accent-color: var(--red); cursor: pointer; }

/* The OUT box ends the row, where the eye already is after the last number.
   It changes a player's season, so it must not be something to hunt for. */
.entry-tbl th.out-col,
.entry-tbl td.out-col {
  text-align: center;
  border-left: 1px solid var(--line);
  padding-left: 12px;
  padding-right: 12px;
}
.entry-tbl th.out-col { color: #ff939a; }
.entry-tbl td.out-col input[type=checkbox] { width: 17px; height: 17px; }

/* Plays and loafs are a separate chart from the box score, so the header is
   tinted to set them apart. */
.entry-tbl th.loaf-col { color: var(--yellow); white-space: nowrap; }
/* Two more columns would squeeze every stat box below a readable width, so
   each number box holds a floor and the sheet scrolls sideways instead. */
.entry-tbl input[type=number] { min-width: 38px; }

/* The loaf card on a player's profile. */
.loaf-tbl input[type=number] { width: 48px; padding: 5px 2px; font-size: 13px; text-align: center; }

/* The loaf sheet under the stat sheet on Add Data. */
.loaf-sheet { margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--line); }
.loaf-legend { list-style: none; margin: 0 0 12px; padding: 0; display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px 18px; font-size: 12px; color: var(--txt-2); }
.loaf-legend b { display: inline-block; width: 18px; color: var(--yellow); }
@media (max-width: 640px) { .loaf-legend { grid-template-columns: minmax(0, 1fr); } }
.entry-tbl td.loaf-who { white-space: nowrap; font-weight: 600; padding-right: 10px; }
.loaf-tbl tr.on td { background: var(--yellow-wash); }

/* The loaf web beside its numbered key. */
.loaf-web { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 24px; align-items: center; margin-bottom: 18px; }
@media (max-width: 900px) { .loaf-web { grid-template-columns: minmax(0, 1fr); } }
.loaf-key { list-style: none; margin: 0; padding: 0; }
.loaf-key li { display: flex; align-items: center; gap: 12px; padding: 8px 0; border-bottom: 1px solid var(--line); font-size: 13px; }
.loaf-key li:last-child { border-bottom: 0; }
.loaf-key .loaf-n { flex: none; width: 24px; height: 24px; border-radius: 50%; display: grid; place-items: center;
  background: var(--red-wash); color: #ff939a; font-weight: 800; font-size: 12px; }
.loaf-key .loaf-l { flex: 1; color: var(--txt-2); }
.loaf-key b { min-width: 2ch; text-align: right; }

/* The opponent's own numbers sit under the player sheet, inside the same
   card and above the one Save button, because a game is one thing: the
   players' night and the team's night are saved together or not at all. */
.entry-team {
  margin-top: 20px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
}
.entry-save {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--line);
  align-items: center;
}
.entry-save .btn.primary { margin-left: auto; }

/* The banner that says the sheet is editing a game rather than starting one. */
#entryMode {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  margin: 0 0 14px;
  padding: 10px 12px;
  border: 1px solid rgba(245,197,24,.35);
  background: var(--yellow-wash);
  color: var(--txt-2);
}
#entryMode[hidden] { display: none; }
#entryMode b { color: var(--txt); }
#entryMode .btn { margin-left: auto; }

/* ---------- demo ---------- */

/* On every tab of the demo team. Loud enough that a screenshot of any page
   carries the warning with it, quiet enough to read past on the second look. */
.demo-banner {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  margin-bottom: var(--gap);
  padding: 12px 16px;
  border: 1px solid rgba(245,197,24,.45);
  border-left: 3px solid var(--yellow);
  background: var(--yellow-wash);
  color: var(--txt-2);
  font-size: 12.5px;
  line-height: 1.55;
}
.demo-banner[hidden] { display: none; }
.demo-banner b { color: var(--txt); }
.demo-flag {
  flex: none;
  padding: 3px 8px;
  background: var(--yellow);
  color: #1a1400;
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1.2px;
}

/* The refresh button on the demo's Team tab. It is wired to nothing, and the
   asterisk beside it is the point rather than a footnote. */
.demo-warn {
  margin-top: 12px;
  padding: 12px 14px;
  border: 1px dashed rgba(245,197,24,.55);
  background: rgba(245,197,24,.06);
  color: var(--txt-2);
  font-size: 12.5px;
  line-height: 1.6;
}
.demo-warn b { color: var(--yellow); }
.btn.demo-dead {
  opacity: .55;
  cursor: not-allowed;
}

/* ---------- charts ---------- */

/* Charts are drawn at a fixed viewBox and scale to their container, so on a
   wide monitor they blow up to cartoon size. Cap them and centre; tables and
   text still use the full width of the card. */
svg.chart {
  display: block;
  width: 100%;
  max-width: 880px;
  margin: 0 auto;
  height: auto;
  overflow: visible;
}

/* Radial charts are square; left to fill a wide card they become absurdly
   tall. Cap and centre them. The cap is the width the radar's axis labels
   need to stay readable — below it they shrink with the web. */
.chart-sm { max-width: 364px; margin: 6px auto 0; }

/* The donut carries a label column either side of the ring, so it is wider
   than it is tall and wants more room than a plain radial chart. */
.chart-donut { max-width: 520px; margin: 6px auto 0; width: 100%; }

/* A chart scaled into a container narrower than it was drawn shrinks its
   labels past reading — on a phone, and equally in a narrow desktop column.
   So a chart never renders below the width it was drawn for: it holds that
   size and scrolls sideways instead. --chart-min comes from the chart itself.
   overflow-x is the trap to know about: the moment one axis scrolls, the
   other stops being visible and starts clipping, which is why every chart
   keeps its ink inside its viewBox. */
.chart-scroll { overflow-x: auto; }
/* min() is doing the work: hold the drawn width when there is room for it,
   but never grow past the container, which would cut the value labels off
   the right-hand edge of a card that is only a little too narrow. Below the
   phone breakpoint the container is far too narrow for that compromise, and
   the rule under there holds the width and scrolls instead. */
.chart-scroll svg.chart { min-width: min(var(--chart-min, 540px), 100%); }

@media (max-width: 760px) {
  .chart-scroll,
  .chart-center {
    overflow-x: auto;
    margin: 0 calc(var(--pad) * -1);
    padding: 0 var(--pad);
  }
  /* A centred child that overflows cannot be scrolled back to on the left,
     so on a phone the chart starts at the left edge instead. */
  .chart-center { display: block; }
  .chart-center svg.chart { min-width: var(--chart-min, 540px); }
}

/* A chart or table that runs past the screen looks cut off rather than
   scrollable, so the edge with something behind it carries a shadow — and
   the edge with nothing behind it does not. The two cover gradients scroll
   with the content and hide the shadow when that side is fully in view. */
@media (max-width: 760px) {
  .table-scroll,
  .chart-scroll,
  .chart-center {
    background:
      linear-gradient(to right, var(--surface) 45%, rgba(18,21,26,0)) left center / 30px 100% no-repeat local,
      linear-gradient(to left,  var(--surface) 45%, rgba(18,21,26,0)) right center / 30px 100% no-repeat local,
      linear-gradient(to right, rgba(0,0,0,.5), rgba(0,0,0,0)) left center / 15px 100% no-repeat scroll,
      linear-gradient(to left,  rgba(0,0,0,.5), rgba(0,0,0,0)) right center / 15px 100% no-repeat scroll;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
  }

  /* Scrolled sideways through fourteen stat columns, a row of numbers with no
     name attached is unreadable. Pin the name column. */
  .table-scroll table.tbl th.l:first-child,
  .table-scroll table.tbl td.l:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
  .table-scroll table.tbl tr:hover td.l:first-child { background: #161a20; }

  /* Same reason on the entry sheet: the player picker has to stay put while
     the stat columns scroll under the thumb. */
  .table-scroll table.entry-tbl th:first-child,
  .table-scroll table.entry-tbl td:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
  .entry-tbl th:first-child { width: 150px; }
  .entry-tbl select { min-width: 140px; }

  /* Nine stat columns, a points column and a checkbox do not fit across a
     phone, and a table told to be 100% wide answers that by squeezing every
     number box down to about thirty pixels: too narrow to read what is in it
     and too narrow to tap the right one. Hold each box at a thumb's width
     instead and let the sheet run past the screen, which it is already set up
     to do — the name column is pinned above and the edges carry the scroll
     shadow, so the row stays readable while the numbers slide under it. */
  .entry-tbl input.e-stat { min-width: 46px; }
  .entry-tbl th { white-space: nowrap; }
}

svg.chart [data-tip] { transition: opacity .12s, fill .12s; }
svg.chart:hover [data-tip] { opacity: .32; }
svg.chart [data-tip]:hover { opacity: 1; }
svg.chart .hit { cursor: pointer; }

/* .mk groups the parts of one mark (a bar's track and its segments, a
   column's hit band and its stack), so pointing anywhere in the group keeps
   the whole group lit while the tooltip names the exact piece under the
   cursor. Declared after the element rules so the group wins. */
svg.chart .mk:hover [data-tip] { opacity: 1; }

/* Transparent hit bands have nothing to dim, so wash them instead. */
svg.chart [fill="transparent"][data-tip]:hover { fill: rgba(255,255,255,.045); }

.tip {
  position: fixed;
  left: 0; top: 0;
  z-index: 200;
  pointer-events: none;
  max-width: 270px;
  background: #05070a;
  border: 1px solid var(--line);
  border-left: 2px solid var(--yellow);
  border-radius: var(--r-sm);
  padding: 9px 12px 10px;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--txt-2);
  box-shadow: 0 12px 28px rgba(0,0,0,.7);
  opacity: 0;
  transition: opacity .1s;
}
.tip.show { opacity: 1; }
.tip b {
  display: block;
  color: var(--txt);
  font-size: 13px;
  font-weight: 700;
  margin-bottom: 3px;
}

/* ---------- player profile ---------- */

.p-hero {
  display: flex;
  align-items: center;
  gap: 16px;
  flex-wrap: wrap;
  padding-bottom: 18px;
  margin-bottom: 18px;
  border-bottom: 1px solid var(--line);
}
.p-avatar {
  width: 52px; height: 52px;
  flex: none;
  border-radius: var(--r-sm);
  background: var(--red);
  display: grid; place-items: center;
  font-size: 18px; font-weight: 800;
  color: #fff;
  letter-spacing: -.3px;
}
.p-hero h2 { margin: 0; font-size: 23px; font-weight: 700; letter-spacing: -.6px; }
.p-hero .p-meta { margin-top: 7px; display: flex; gap: 6px; flex-wrap: wrap; }

/* ---------- home ---------- */

.home-big {
  font-size: 25px;
  font-weight: 700;
  letter-spacing: -.8px;
  line-height: 1.15;
  margin-top: 6px;
}
.home-sub { margin-top: 8px; font-size: 13px; color: var(--txt-2); line-height: 1.55; }
.home-next { border-color: rgba(245,197,24,.25); }
.home-next .home-big { color: var(--yellow); }
.home-hero { padding: 18px var(--pad); }

/* ---------- unlocking the Add Data tab ----------
   The dashboard itself is open to read, so this is not a gate in front of the
   page — it is a card that sits on the one tab that writes. */

.entry-lock {
  display: grid;
  place-items: center;
  padding: 40px 0 56px;
}
.entry-lock[hidden] { display: none; }

/* The sheet is a grid, and `display: grid` outranks the `hidden` attribute,
   so hiding it from a read-only visitor has to be said out loud. */
#entryBody[hidden] { display: none; }

.login-card {
  width: 100%;
  max-width: 360px;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--r);
  padding: 32px 28px 26px;
  text-align: center;
  position: relative;
  overflow: hidden;
}
.login-card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  background: var(--red);
}
.login-crest {
  width: 46px; height: 46px;
  margin: 6px auto 18px;
  border-radius: var(--r-sm);
  background: var(--red);
  display: grid; place-items: center;
  font-size: 17px; font-weight: 800;
  color: #fff;
}
.login-card h2 { margin: 0; font-size: 17px; font-weight: 700; letter-spacing: -.2px; }
.login-card h2::after {
  content: "";
  display: block;
  width: 26px;
  height: 2px;
  margin: 10px auto 0;
  background: var(--line);
  border-radius: 2px;
}
.login-card p { margin: 8px 0 22px; font-size: 12.5px; color: var(--txt-3); line-height: 1.55; }
.login-card input { text-align: center; font-size: 15px; letter-spacing: 3px; padding: 11px; }
.login-card .btn { width: 100%; margin-top: 12px; padding: 11px; font-size: 13.5px; }
.login-error {
  margin-top: 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: #ff939a;
  background: var(--red-wash);
  border: 1px solid rgba(216,31,42,.28);
  border-radius: var(--r-sm);
  padding: 9px 12px;
}
.login-foot { margin-top: 18px; font-size: 11.5px; color: var(--txt-3); line-height: 1.5; }

/* A button that is there but not yours: the Refresh from MaxPreps button on
   the Team tab while editing is locked. Its title says what it needs. */
.btn.locked-out { opacity: .5; cursor: not-allowed; }

/* ---------- status ---------- */

.sync-status {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .3px;
  color: var(--txt-3);
  padding: 5px 10px;
  border-radius: var(--r-xs);
  border: 1px solid var(--line);
  background: var(--surface-2);
  white-space: nowrap;
}
.sync-status.ok   { color: var(--good);   border-color: rgba(53,196,139,.28); background: rgba(53,196,139,.08); }
.sync-status.warn { color: var(--yellow); border-color: rgba(245,197,24,.28); background: var(--yellow-wash); }

.demo-badge {
  font-size: 11px;
  font-weight: 800;
  letter-spacing: 1px;
  padding: 5px 10px;
  border-radius: var(--r-xs);
  color: #ff939a;
  background: var(--red-wash);
  border: 1px solid rgba(216,31,42,.32);
  white-space: nowrap;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%) translateY(80px);
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-left: 2px solid var(--yellow);
  color: var(--txt);
  font-size: 13px;
  font-weight: 600;
  padding: 11px 18px;
  border-radius: var(--r-sm);
  box-shadow: 0 14px 34px rgba(0,0,0,.6);
  opacity: 0;
  pointer-events: none;
  transition: transform .2s cubic-bezier(.2,.9,.3,1), opacity .2s;
  z-index: 100;
}
.toast.show { transform: translateX(-50%) translateY(0); opacity: 1; }

/* ---------- reading a pasted stat sheet ---------- */

.import-block {
  margin-top: 22px;
  padding-top: 20px;
  border-top: 1px solid var(--line);
}

textarea#pasteBox {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  color: var(--txt);
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 12px;
  line-height: 1.6;
  padding: 11px 12px;
  outline: none;
  resize: vertical;
  white-space: pre;
  overflow-x: auto;
  transition: border-color .12s, box-shadow .12s;
}
textarea#pasteBox:focus { border-color: var(--red); box-shadow: 0 0 0 3px var(--red-wash); }
textarea#pasteBox::placeholder { color: var(--txt-3); opacity: .55; }

.import-msg {
  margin-top: 12px;
  padding: 11px 13px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  line-height: 1.6;
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--txt-2);
}
.import-msg.good { border-color: rgba(53,196,139,.3); background: rgba(53,196,139,.07); }
.import-msg.bad  { border-color: rgba(216,31,42,.3);  background: var(--red-wash); }
.import-msg b { color: var(--txt); }

.import-note {
  margin-top: 10px;
  padding: 11px 13px;
  border-radius: var(--r-sm);
  border: 1px solid rgba(245,197,24,.28);
  background: var(--yellow-wash);
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--txt-2);
}
.import-note b { color: var(--txt); }
.import-note ul { margin: 7px 0 0; padding-left: 18px; }
.import-note li { margin-bottom: 3px; }
.import-note li span { color: var(--txt-3); }

/* On a phone the brand row wraps mid-name, leaving the crest stranded on its
   own line. Keep the crest with the title and push the status chips to a
   second row of their own. */
@media (max-width: 620px) {
  .brand { gap: 10px 12px; }
  .brand .sub { display: none; }
  .brand h1 { font-size: 16px; }
  .brand-spacer { flex-basis: 100%; height: 0; margin: 0; }
  .crest { width: 32px; height: 32px; font-size: 13px; }
  .tab { padding: 9px 11px 11px; font-size: 12.5px; }
  .kpi .k-value { font-size: 23px; }
  .kpi .k-value .k-unit { font-size: 11px; margin-left: 4px; }
  .home-big { font-size: 21px; }

  /* A 20px gutter on each side of a 360px screen spends a ninth of the
     width on nothing. Tighten the shared rhythm and the charts, tables and
     cards all get the room back together. */
  :root { --pad: 14px; --gap: 12px; }
  .kpi { padding: 12px 13px 13px; }
  .card-note { font-size: 12.5px; }
}

/* Byline. Quiet by design — it closes the page off without competing with
   anything on it, so it reads the same under a leaderboard as under a chart.
   The rule above it is what separates it from the last card. */
.site-foot {
  margin: 0 24px;
  padding: 18px 0 30px;
  border-top: 1px solid var(--line-soft);
  font-size: 11.5px;
  color: var(--txt-3);
  letter-spacing: .2px;
  text-align: center;
}
.site-foot b { color: var(--txt-2); font-weight: 650; }
.site-foot a {
  display: inline-block;
  color: var(--yellow);
  font-weight: 600;
}
.site-foot a:hover { text-decoration: underline; }
@media (max-width: 720px) { .site-foot { margin: 0 14px; } }


/* ---------- audit 2026-10-06: access and layout ---------- */

/* Read by screen readers, not drawn. The passcode box's label lives here:
   a placeholder is not a label, it vanishes on the first keystroke. */
.sr-only {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* Upright and landscape tablets get the phone's table treatment: a data
   table wider than its card shows that it scrolls (the edge shadow) and keeps
   the name column pinned, and its cells tighten so most of it fits. Before,
   from 761 to 1100 px the leaderboard ran 200 px off the card's right edge
   with nothing to say the columns were there. */
@media (min-width: 761px) and (max-width: 1199px) {
  table.tbl th { padding: 0 6px 9px; }
  table.tbl td { padding: 8px 6px; }
  .table-scroll,
  .chart-scroll {
    background:
      linear-gradient(to right, var(--surface) 45%, rgba(18,21,26,0)) left center / 30px 100% no-repeat local,
      linear-gradient(to left,  var(--surface) 45%, rgba(18,21,26,0)) right center / 30px 100% no-repeat local,
      linear-gradient(to right, rgba(0,0,0,.5), rgba(0,0,0,0)) left center / 15px 100% no-repeat scroll,
      linear-gradient(to left,  rgba(0,0,0,.5), rgba(0,0,0,0)) right center / 15px 100% no-repeat scroll;
    overscroll-behavior-x: contain;
  }
  .table-scroll table.tbl th.l:first-child,
  .table-scroll table.tbl td.l:first-child {
    position: sticky;
    left: 0;
    z-index: 2;
    background: var(--surface);
    box-shadow: 1px 0 0 var(--line);
  }
}

/* Phones and tablets: a chart holds the width it was drawn at, so its labels
   never print under 11px; the card scrolls it sideways instead. (The phone
   block above already said so in its comment, but only .chart-center had the
   rule: every .chart-scroll chart was shrunk to the card, printing names at
   9px on a 320px phone.) */
@media (max-width: 1199px) {
  .chart-scroll svg.chart { min-width: var(--chart-min, 540px); }
}

/* A finger needs about 44 px. On a touch screen the small buttons, the tabs
   and the form controls grow to that; the mouse layout is untouched. */
@media (pointer: coarse) {
  .btn, .tab, select, input:not([type=checkbox]):not([type=radio]), .seg-btn {
    min-height: 44px;
  }
}

/* Reduced motion: nothing slides, fades or eases. Durations go to (almost)
   zero rather than `animation: none`, so every animation still lands on its
   final frame and nothing is left half-faded. !important because the motion
   is declared on many specific selectors this must beat. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* The roster editor and the saved-games list sit in the narrow right-hand
   column of the Add Data tab. At 1366 px the games list ran 76 px past the
   card, so each game's Delete button sat off the edge, and the roster's
   season column was cut mid-word ("2 game"). Tighter cells, names that wrap,
   and a position box wide enough to show "MLB" beside the list arrow. */
#rosterEditor table.tbl th, #rosterEditor table.tbl td,
#gameManager table.tbl th, #gameManager table.tbl td { padding-left: 6px; padding-right: 6px; }
#rosterEditor table.tbl td:last-child, #gameManager table.tbl td.l { white-space: normal; }
#rosterEditor .r-pos { min-width: 76px; }
