:root {
  /* THE BOARD'S COLOURS, and every one of them is a DEFAULT here rather than a
     decision. views/partials/head.ejs writes the state folder's own values over
     the top, in both themes, so a board wearing another state's palette is not
     something a stylesheet can cause.

     These were --ky, --ky-deep and --ky-tint, which was accurate when there was
     one state and became a Kentucky literal in shared code the moment there
     were two. 225 usages across 50 files, renamed as tokens — no rule changed.

     --brand is the accent: buttons, chips, links, focus rings.
     --brand-deep is its pressed state, --brand-tint a wash for light surfaces.

     --header IS SEPARATE FROM --brand ON PURPOSE. Kentucky's masthead is the
     brand blue and the two were the same value, so one variable did both jobs.
     They are not the same job. Tennessee's logo sits on black and its accent is
     orange; painting the buttons black to fix the masthead would make the site
     look switched off. */
  --brand: #0033A0;
  --brand-deep: #002273;
  --brand-tint: #E9EFFA;
  --header: #0033A0;
  --bg: #f4f6f9;
  --card: #ffffff;
  --ink: #14181f;
  --ink-2: #5b6673;
  --ink-3: #8b95a3;
  --line: #e2e7ee;
  --live: #d8232a;
  --win: #0033A0;
  --accent: #0033A0;
  --radius: 12px;
  --wrap: 720px;

  /* PANEL WASHES, WITH BOTH THEIR THEMES.
     Every one of these started life as a pale hex typed into one page's <style>
     block, which is fine until the page is looked at in dark mode: the wash
     stays pale, --ink flips to near-white, and the words go. Named here so a
     panel gets the right ground in both themes without any page having to know
     which theme it is in. */
  --wash: #f6f8fa;      /* a neutral aside */
  --warn-bg: #fff8e1;   /* worth reading before you press the button */
  --bad-bg: #fdecea;    /* something is wrong */
  --alert-bg: #fff7f6;  /* something needs a person */
  --note-bg: #faf8ff;   /* an explanation, not a warning */
}
@media (prefers-color-scheme: dark) {
  :root {
    --bg: #0e1116;
    --card: #171b22;
    --ink: #e8ecf1;
    --ink-2: #9aa5b2;
    --ink-3: #6d7887;
    --line: #262c36;
    --win: #7FB0FF;
    --accent: #7FB0FF;

    /* THE TWO THAT WERE MISSING, AND WHY TEXT WENT INVISIBLE.
       A coach, unprompted:

         "The color of some of the wording makes it invisible on PC. I have to
          highlight boxes with a left-click and drag to get the words to show."

       --brand-tint is a WASH FOR LIGHT SURFACES and it had no dark value, so it
       stayed #E9EFFA — very nearly white — in dark mode. Fifty-seven places use
       it as a background: the drop boxes, every notice, the admin bar, the
       upload panels. In dark mode --ink flips to near-white, so all of them
       became pale text on a pale ground. Invisible until you drag-selected it,
       which is exactly what he had to do.

       --brand was the same story one step quieter. #0033A0 is a deep navy meant to
       sit on white; on a dark card it is a link you can barely find. Forty-two
       rules colour text with it.

       Both are now dark-mode values of their own. Every rule that used them
       keeps working — nothing had to be hunted down and changed — because the
       bug was never in those rules. It was two variables that only had half
       their definitions. */
    --brand: #6f9dff;
    --brand-deep: #4d7fe0;
    --brand-tint: #1a2333;
    --header: #0b1a3a;

    /* Same story, same fix. Each is dark enough for near-white text to sit on,
       and keeps the hue that told you what kind of panel it was. */
    --wash: #1a1f27;
    --warn-bg: #33290d;
    --bad-bg: #3a1512;
    --alert-bg: #331a17;
    --note-bg: #221d33;
  }
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  overflow-x: hidden;
  background: var(--bg);
  color: var(--ink);
  font: 16px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }
.wrap { max-width: var(--wrap); margin: 0 auto; padding: 0 14px; }

/* ---------------------------------------------------------- header */
.site-header {
  background: var(--header);
  color: #fff;
  position: sticky; top: 0; z-index: 50;
  box-shadow: 0 1px 0 rgba(0,0,0,.15);
}
.header-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; min-height: 56px;
  padding-top: 6px; padding-bottom: 6px;
  transition: padding .18s ease;
}
.site-header.slim .header-inner { padding-top: 0; padding-bottom: 0; }
.brand { display: flex; align-items: center; gap: 10px; color: #fff; min-width: 0; }
.brand:hover { text-decoration: none; }

/* The logo is a tall, near-square badge, so it needs real height to read — at
   header size it would be a smudge. The masthead therefore opens big and
   shrinks to a slim bar once you start scrolling, which keeps the board itself
   at the top of a phone screen where it belongs.

   The image travels inside this stylesheet as a data URL rather than as a file
   in /static: it caches with the CSS instead of costing a second request on a
   phone at a stadium, and it can never 404 if an image fails to reach a deploy.
   96-colour PNG, ~11 KB, drawn at 200px tall so it stays sharp on retina. */
.brand-logo {
  display: block; flex: none;
  width: 119px; height: 104px;
  background: url("/brand/logo.png") no-repeat center;
  background-size: contain;
  transition: width .18s ease, height .18s ease;
}
.vh {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}
/* Set by the small script in header.ejs once the page has scrolled past the
   masthead. Everything degrades to the tall state if that script never runs. */
.site-header.slim .brand-logo { width: 48px; height: 42px; }
.nav { display: flex; align-items: center; gap: 4px; min-width: 0; }
.cta-short { display: none; }
.nav a {
  color: #cfe0f2; font-size: 14px; padding: 7px 10px; border-radius: 8px; white-space: nowrap;
}
.nav a:hover { background: rgba(255,255,255,.1); text-decoration: none; }
.nav a.on { color: #fff; background: rgba(255,255,255,.14); }
.nav a.cta { background: #fff; color: var(--brand); font-weight: 650; }
.nav a.cta:hover { background: #e8f0fa; }
@media (max-width: 620px) {
  .nav a { padding: 7px 6px; font-size: 12.5px; }
  .nav a.cta { padding: 7px 9px; }
}
@media (max-width: 540px) {
  .cta-long { display: none; }
  .cta-short { display: inline; }
}
@media (max-width: 430px) {
  /* "Coaches" is three characters wider than the "Send" it replaced, and on a
     390-point phone the row was already close. Two pixels of gap and one of
     padding buy back more than the word costs, and the alternative — a nav that
     wraps or scrolls sideways — is worse than a slightly tighter one. */
  .nav { gap: 2px; }
  .nav a { padding: 7px 5px; font-size: 12px; }
  .nav a.cta { padding: 7px 8px; }
  .brand-logo { width: 84px; height: 74px; }
  .site-header.slim .brand-logo { width: 42px; height: 37px; }
  .nav { gap: 0; }
  .nav a { padding: 7px 5px; font-size: 12px; }
}

/* ---------------------------------------------------------- datebar */
.datebar {
  display: flex; align-items: center; gap: 8px;
  padding: 18px 0 10px;
}
.datebar-mid { flex: 1; text-align: center; }
.datebar h1 {
  margin: 0; font-size: 20px; letter-spacing: -.35px; font-weight: 700;
}
.today-link { font-size: 12px; color: var(--ink-2); }
.datenav {
  width: 38px; height: 38px; display: grid; place-items: center;
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  font-size: 20px; color: var(--ink-2); flex: none;
}
.datenav:hover { text-decoration: none; border-color: var(--accent); color: var(--accent); }

/* ---------------------------------------------------------- controls */
.controls { margin-bottom: 10px; }
.chips {
  display: flex; gap: 6px; overflow-x: auto; padding-bottom: 6px;
  scrollbar-width: none;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--card); border: 1px solid var(--line); color: var(--ink-2);
  border-radius: 999px; padding: 7px 13px; font-size: 13.5px; font-weight: 550;
  cursor: pointer; white-space: nowrap; font-family: inherit;
}
.chip:hover { border-color: var(--ink-3); }
.chip.on { background: var(--brand); border-color: var(--brand); color: #fff; }
.chip-n { font-size: 11.5px; opacity: .7; font-variant-numeric: tabular-nums; }
.controls-row { display: flex; gap: 8px; margin-top: 6px; }
#team-pick, #class-filter {
  font: inherit; font-size: 14px; padding: 9px 12px;
  border: 1px solid var(--line); border-radius: 10px;
  background: var(--card); color: var(--ink);
}
#class-filter { flex: none; }

/* Finding your own school is the most common first action on the board, so the
   picker sits above the date and gets the full width. */
.teampick { margin: 0 0 10px; }
#team-pick {
  width: 100%;
  font-size: 15px;
  padding: 11px 12px;
  font-weight: 550;
}
#team-pick:focus-visible, #class-filter:focus-visible {
  outline: 2px solid var(--brand); outline-offset: 2px;
}
/* Shown only when JS hasn't loaded; the script hides it and navigates on change. */
.teampick { display: flex; gap: 8px; }
.teampick-go {
  font: inherit; font-size: 14px; font-weight: 600; padding: 0 16px;
  border: 1px solid var(--brand); border-radius: 10px;
  background: var(--brand); color: #fff; cursor: pointer; flex: none;
}

.live-note {
  display: flex; align-items: center; gap: 7px;
  font-size: 12px; color: var(--ink-2); padding: 4px 2px 10px;
}

/* ---------------------------------------------------------- games */
.games { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
.game {
  position: relative;
  /* WRAPPING, SO A NOTE GETS ITS OWN LINE INSTEAD OF EATING THE ROW.
     A note on a board row is a SECOND LINE — "Resumes Monday Night" under the
     matchup — and .gnote asked for one with flex-basis:100%. On a nowrap row
     that basis is not a line break, it is a claim on the SAME line: the note
     took the width, .teams (min-width:0) shrank, and .tname ellipsised the two
     school names down to nothing. Both schools and both scores vanished off a
     row that was otherwise perfectly correct.
     It looked fine for weeks because no board row had a note on it yet. */
  display: flex; flex-wrap: wrap; align-items: stretch; gap: 8px;
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 9px 10px 9px 32px;
}
/* The matchup, the time and the chips stay on the first line and take what they
   need; only the note is allowed to wrap. Without this a narrow phone can drop
   the Fix chip onto a line of its own, which reads as a second row. */
.game > a.state, .game > .rowfix { flex-shrink: 0; }
.game.is-live { border-color: rgba(216,35,42,.45); }
.game.pinned { box-shadow: inset 3px 0 0 var(--accent); }

.star {
  position: absolute; left: 6px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; cursor: pointer; padding: 6px;
  font-size: 16px; line-height: 1; color: var(--line);
}
.star:hover { color: var(--ink-3); }
.star.on { color: #f0a500; }

.teams { flex: 1; min-width: 0; display: grid; gap: 3px; }
.row { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.tname {
  color: var(--ink); font-size: 15.5px; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Without these the long names (Christian Academy-Louisville) set the row's
     min-content width and push the whole card past the screen edge instead of
     ellipsising. Flex children don't shrink below min-content by default. */
  flex: 0 1 auto; min-width: 0;
}
.row.win .tname { font-weight: 700; color: var(--win); }
.tclass {
  font-size: 10.5px; color: var(--ink-3); border: 1px solid var(--line);
  border-radius: 4px; padding: 0 4px; flex: none; font-weight: 600;
}
/* Class chip and record travel together so the record never wraps away from
   the team it belongs to, and so an empty record collapses to nothing at all
   rather than leaving a gap the eye reads as a missing number. */
.tmeta { display: flex; align-items: baseline; gap: 6px; flex: none; }
.trec {
  font-size: 11.5px; color: var(--ink-3); font-weight: 600;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.trec:empty { display: none; }
.row.win .trec { color: var(--ink-2); }
/* On a narrow phone the score and the team name are what matter. The class
   chip goes first because the record is the thing being added; losing both
   would make the feature invisible on the device most people read this on. */
@media (max-width: 380px) {
  .tmeta .tclass { display: none; }
}
.tscore {
  margin-left: auto; font-size: 19px; font-weight: 700; color: var(--ink-2);
  font-variant-numeric: tabular-nums; flex: none; min-width: 30px; text-align: right;
}
.row.win .tscore { color: var(--ink); }

.state {
  flex: none; width: 78px; display: flex; flex-direction: column;
  align-items: flex-end; justify-content: center; gap: 3px; text-align: right;
}
.pill {
  font-size: 11px; font-weight: 750; letter-spacing: .4px;
  border-radius: 5px; padding: 3px 7px; white-space: nowrap;
  display: inline-flex; align-items: center; gap: 5px;
}
.pill.live { background: var(--live); color: #fff; }
.pill.final { background: var(--line); color: var(--ink-2); }
.pill.sched { background: transparent; color: var(--ink-2); border: 1px solid var(--line); font-weight: 600; }
.pill.off { background: #8b95a3; color: #fff; }
.state .sub { font-size: 11px; color: var(--ink-3); }
.state .ago { font-size: 10.5px; color: var(--ink-3); }

/* The admin's "Fix" chip on a board row.

   "i want to go to the board, find the way to change this easily"

   Only rendered for a logged-in admin, so a visitor never sees it. Deliberately
   quiet — the board's job is reading a score, and this sits to the side of the
   status rather than competing with it. */
/* A NOTE SOMEBODY TYPED ABOUT ONE GAME.
   Quiet on purpose. It sits under the matchup and must never compete with the
   score, which is the one thing the row exists to show. */
/* The note box on a coach's schedule row. NOT .cs-note — that name was already
   taken, in coach-schedule.ejs's own <style>, by the paragraphs of explanation
   all over that page. Giving an input the paragraphs' class put flex and a
   min-width on nine blocks of prose that wanted neither. Two things held the
   same name and meant different things by it. */
.cs-notebox { flex: 1 1 260px; min-width: 200px; }

.gnote {
  /* 0 0 100%, not 1 1 100%: it must OCCUPY a whole line rather than merely be
     willing to grow to one. The old basis let it share the first line with the
     matchup whenever the row had any slack. */
  flex: 0 0 100%;
  margin-top: 4px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--muted, #5c6675);
}

.rowfix {
  flex: none; align-self: center; margin-left: 8px;
  padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px;
  font-size: 11px; font-weight: 700; letter-spacing: .3px;
  color: var(--ink-3); text-decoration: none; white-space: nowrap;
}
.rowfix:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-tint); }
/* THE NOTE BOX ON A BOARD ROW. Shut by default and the same size as the Fix
   chip beside it, because the board is for reading: an input box open on forty
   rows is a form, not a scoreboard. Open, it takes the full width of the row so
   a sentence fits. */
/* ITS OWN LINE, ALWAYS, AND NOT BESIDE THE MATCHUP.
   Inline it cost about 64px, and .teams is the flexible child (flex:1,
   min-width:0) — so the chip took its width and .tname ellipsised both school
   names. Measured on a 390px phone: without the chip most names fit; with it
   inline, nearly every name in the state truncated. Fix stays where it has
   always been, because Fix alone leaves the names room. */
.rownote { flex: 0 0 100%; order: 9; margin: 5px 0 0; }
.game > .gnote { order: 8; }
.rownote > summary {
  display: inline-block; list-style: none; cursor: pointer;
  font-size: 11.5px; padding: 3px 8px; border: 1px solid var(--line);
  border-radius: 999px; color: var(--ink-3); background: var(--bg);
}
.rownote > summary::-webkit-details-marker { display: none; }
.rownote > summary:hover { border-color: var(--brand); color: var(--brand); background: var(--brand-tint); }
.rownote[open] { width: 100%; }
.rownote form { display: flex; gap: 6px; align-items: center; margin: 6px 0 0; }
.rownote input[type=text] {
  flex: 1 1 auto; min-width: 120px; margin: 0; padding: 7px 9px;
  /* 16px or iOS zooms the whole board in when the box is tapped, which on a
     sideline is somebody losing their place in the list. */
  font-size: 16px; border: 1px solid var(--line); border-radius: var(--radius);
}
@media (max-width: 380px) { .rowfix { margin-left: 5px; padding: 3px 7px; } }

.dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--live);
  display: inline-block; flex: none; animation: pulse 1.6s infinite;
}
.pill.live .dot { background: #fff; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: .3; } }
@media (prefers-reduced-motion: reduce) { .dot { animation: none; } }

.no-match { text-align: center; color: var(--ink-2); padding: 30px 0; }

/* ---------------------------------------------------------- empty / cta */
.empty, .cta-band {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 28px 22px; text-align: center;
  margin: 16px 0;
}
.empty h2, .cta-band h2 { margin: 0 0 8px; font-size: 19px; letter-spacing: -.3px; }
.empty p, .cta-band p { margin: 0 auto 16px; color: var(--ink-2); max-width: 44ch; font-size: 14.5px; }
/* The ask on an empty team page. It is the whole point of the page, so it gets
   to look like a note rather than more body copy. */
.empty-ask {
  background: var(--bg); border-left: 3px solid var(--brand);
  border-radius: 8px; padding: 12px 14px; text-align: left;
}
.empty-ask strong { color: var(--ink); display: block; margin-bottom: 4px; }
.empty .btn + .btn { margin-left: 8px; }
.empty-note { font-size: 13px !important; opacity: .78; margin-bottom: 0 !important; }
@media (max-width: 420px) {
  .empty .btn { display: block; margin: 0 auto 8px !important; }
}
.btn {
  display: inline-block; background: var(--brand); color: #fff;
  border-radius: 10px; padding: 10px 18px; font-weight: 650; font-size: 14.5px;
  border: 0; cursor: pointer; font-family: inherit;
}
.btn:hover { background: var(--brand-deep); text-decoration: none; }
.btn.big { padding: 13px 26px; font-size: 16px; }
.btn.ghost { background: transparent; border: 1px solid var(--line); color: var(--ink); }

/* ---------------------------------------------------------- ads
   An ad on a phone must never be the reason somebody can't see a score.
   Every slot is given an EXACT size here rather than letting Google choose,
   because "auto" on a 640px-tall screen picks a 600px-tall ad. The <ins> is
   sized, so the ad is delivered at that size — nothing is clipped, hidden or
   covered, which keeps it within AdSense's rules as well as ours.

   Phone budget: a 50px banner up top is about 8% of the screen. The old
   setup could take 90%. */
.ad { margin: 12px 0; text-align: center; }
.ad-label {
  display: block; font-size: 9.5px; letter-spacing: .8px; text-transform: uppercase;
  color: var(--ink-3); margin-bottom: 4px;
}
.ad .adsbygoogle { display: block; margin: 0 auto; }
.feed-ad { list-style: none; }

/* Mobile first: small banners only. The footer unit on team and game
   pages shares the in-feed shape. */
.ad-leaderboard .adsbygoogle { width: 320px; height: 50px; }
.ad-infeed      .adsbygoogle,
.ad-footer      .adsbygoogle { width: 300px; height: 100px; }

/* The sidebar unit is a desktop shape and has no business on a phone. */
.ad-sidebar { display: none; }

@media (min-width: 700px) {
  .ad-leaderboard .adsbygoogle { width: 728px; height: 90px; }
  .ad-infeed      .adsbygoogle,
  .ad-footer      .adsbygoogle { width: 728px; height: 90px; }
  .ad-sidebar { display: block; }
  .ad-sidebar .adsbygoogle { width: 300px; height: 250px; }
}

/* Belt and braces. If a unit ever comes back bigger than it was asked for —
   a mis-set slot in the AdSense console, say — this stops it eating the page
   before anybody tells us.

   :not(.ad-sold) IS THE WHOLE POINT OF THIS SELECTOR, NOT A REFINEMENT.

   This clamp was written to defend the board against Google. Unscoped, it was
   also cutting the bottom off the sponsors we sold ourselves: a card with a
   logo runs about 160px, so the business name and their one line got clipped
   away and only the logo showed. Nothing errored, the page returned 200, and
   the element was in the DOM with a real bounding box — it was simply painted
   inside a box with overflow:hidden. I spent a round assuming my screenshot
   had cropped badly.

   A guard against a stranger's markup must not fire on our own. The clamp
   still applies to every network unit; a card a customer paid for is measured
   by its own contents. */
.ad:not(.ad-sold) { max-height: 120px; overflow: hidden; }
@media (min-width: 700px) { .ad:not(.ad-sold) { max-height: 300px; } }

/* ---------------------------------------------------------- team page */
.team-head {
  background: var(--brand); color: #fff; border-radius: var(--radius);
  padding: 20px; margin: 18px 0 14px;
}
.team-head h1 { margin: 0 0 6px; font-size: 24px; letter-spacing: -.5px; }
.team-meta { color: #a9c4e0; font-size: 14px; }
.record { display: flex; gap: 20px; margin-top: 14px; }
.record div { line-height: 1.2; }
.record b { display: block; font-size: 22px; font-variant-numeric: tabular-nums; }
.record span { font-size: 11px; color: #a9c4e0; text-transform: uppercase; letter-spacing: .6px; }
/* Sits in the coloured hero, so it borrows the hero's muted text rather than
   the page's. Quiet on purpose — it explains a number, it isn't a warning. */
.rec-gap { margin: 10px 0 0; font-size: 12.5px; color: #a9c4e0; line-height: 1.55; }
.rec-gap a { color: #fff; text-decoration: underline; text-underline-offset: 2px; }
/* A record we can only put a floor under. Muted rather than red: it is an
   incomplete number, not an error, and the table is full of them early in a
   season. `cursor: help` is the affordance that there is a title to read. */
.partial { color: var(--ink-3); cursor: help; }

.sched-table { width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.sched-table td, .sched-table th { padding: 11px 12px; border-bottom: 1px solid var(--line); font-size: 14.5px; text-align: left; }
.sched-table th { font-size: 11px; text-transform: uppercase; letter-spacing: .6px; color: var(--ink-3); }
.sched-table tr:last-child td { border-bottom: 0; }
.res { font-weight: 750; font-variant-numeric: tabular-nums; }
.res.w { color: #1a7f37; }
.res.l { color: #b3261e; }
/* A game that is off. Not red — nothing has gone wrong and nobody lost; it is
   simply not happening, so it reads as quieter than the games around it rather
   than louder. Small caps for the same reason a result is bold: this column is
   scanned, not read. */
.res.ppd { color: var(--ink-3); font-weight: 650; font-size: 12.5px;
           letter-spacing: .04em; text-decoration: none; }

/* Reporting a score from the schedule.
   Somebody looking at a team page has already found the game — making them go
   back to the board and hunt for it is the difference between a score arriving
   and a score not arriving. So the Result column doubles as the way in.
   44px tall because it is tapped with a thumb, often in the dark. */
.add-score {
  display: inline-flex; align-items: center; gap: 5px;
  min-height: 34px; padding: 6px 11px;
  border: 1px solid var(--brand); border-radius: 999px;
  color: var(--brand); background: var(--brand-tint);
  font-size: 13px; font-weight: 700; white-space: nowrap;
  text-decoration: none;
}
.add-score:hover { background: var(--brand); color: #fff; text-decoration: none; }
.add-score .plus { font-size: 15px; line-height: 1; font-weight: 800; }
/* A game still to come. Reportable, because a date can be wrong or a game can
   be moved up — but quiet, so it doesn't read as a job that needs doing. */
.add-later {
  color: var(--ink-3); text-decoration: none; font-variant-numeric: tabular-nums;
  border-bottom: 1px dotted var(--line);
}
.add-later:hover { color: var(--brand); border-bottom-color: var(--brand); text-decoration: none; }
.sched-table td:last-child { text-align: right; }
.sched-hint {
  margin: 0 0 9px; font-size: 13px; color: var(--ink-2);
}

/* ---------------------------------------------------------- teams index */
.class-block { margin: 22px 0; }
.class-block > h2 { font-size: 17px; margin: 0 0 10px; letter-spacing: -.2px; }
.district { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 8px; }
.district h3 { margin: 0 0 6px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .7px; color: var(--ink-3); }
.district ul { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 6px 14px; }
.district a { font-size: 14.5px; }

/* ---------------------------------------------------------- forms */
.card {
  background: var(--card); border: 1px solid var(--line);
  border-radius: var(--radius); padding: 20px; margin: 16px 0;
}
.card h1 { margin: 0 0 6px; font-size: 22px; letter-spacing: -.4px; }
.lede { color: var(--ink-2); margin: 0 0 18px; font-size: 15px; }
.field { margin-bottom: 14px; }
.field label { display: block; font-size: 13px; font-weight: 650; margin-bottom: 5px; }
.field .hint { font-size: 12px; color: var(--ink-3); font-weight: 400; }
.field input, .field select, .field textarea {
  width: 100%; font: inherit; font-size: 16px; padding: 11px 12px;
  border: 1px solid var(--line); border-radius: 9px;
  background: var(--bg); color: var(--ink);
}
.field input:focus, .field select:focus { outline: 2px solid var(--accent); outline-offset: -1px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.score-pair { display: grid; grid-template-columns: 1fr 64px; gap: 10px; align-items: end; }
.notice { padding: 13px 15px; border-radius: 10px; margin-bottom: 16px; font-size: 14.5px; }
.notice.ok { background: #e7f6ec; color: #12572c; }
.notice.bad { background: #fdecea; color: #8b1a13; }
/* A THIRD STATE: IT WORKED, AND IT IS NOT FINISHED.
   A sheet that has been read and not yet saved was wearing the green of a
   finished job, and three of Lexington Christian's four box scores were lost to
   exactly that. Amber says "still your turn" in a way a sentence cannot. */
.notice.warn { background: #fdf3e0; color: #7a4a05; }
.notsaved { background: #fdf3e0; color: #7a4a05; border-left: 4px solid #c98a12;
  padding: 11px 14px; border-radius: 0 8px 8px 0; margin: -6px 0 16px; font-size: 14.5px; }
@media (prefers-color-scheme: dark) {
  .notice.ok { background: #10301c; color: #7fd6a0; }
  .notice.bad { background: #3a1512; color: #f3a49c; }
  .notice.warn { background: #3a2c10; color: #f0c674; }
  .notsaved { background: #3a2c10; color: #f0c674; border-left-color: #b98a2a; }
}

/* ---------------------------------------------------------- admin */
.admin-grid { display: grid; gap: 10px; }
.sub-card { background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px; }
.sub-card .who { font-size: 12px; color: var(--ink-3); margin-top: 4px; }
/* Which sport, and which night. A score card with neither is a number you
   cannot check: 12-0 is an ordinary football result, an unusual soccer one and
   an impossible basketball one. */
.sub-card .sub-sport {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .6px;
  color: var(--ink-3); font-weight: 700; margin-bottom: 3px;
}
.sub-actions { display: flex; gap: 8px; margin-top: 10px; }
/* Why a card refused to publish, on the card. Red rather than grey: this is the
   one thing in the queue that needs a decision rather than a press. */
.sub-why {
  margin-top: 9px; padding: 7px 10px; border-radius: 7px;
  background: #fdecea; color: #a5281b; font-size: 12.5px; line-height: 1.55;
}
/* The card the page just jumped to, so a run through sixteen of them has some
   sense of place. :target only matches the one named in the address bar. */
.sub-card:target { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-tint); }
/* Add the missing fixture and publish, in one press. The date sits inside the
   form rather than behind a link, because it is the one thing the site does not
   know and will not invent. */
.sub-add {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  margin-top: 10px; padding: 9px 11px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--brand-tint);
}
.sub-add label { font-size: 12.5px; color: var(--ink-2); font-weight: 600; }
.sub-add input[type="date"] {
  width: auto; padding: 5px 8px; font-size: 13px; min-height: 32px; margin: 0;
}
.sub-add .btn { padding: 6px 12px; font-size: 13px; min-height: 32px; }
.stat-row { display: flex; gap: 18px; margin: 14px 0; }
.stat-row div b { display: block; font-size: 22px; font-variant-numeric: tabular-nums; }
.stat-row div span { font-size: 11px; text-transform: uppercase; letter-spacing: .6px; color: var(--ink-3); }
.code-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.code-table td, .code-table th { padding: 7px 9px; border-bottom: 1px solid var(--line); text-align: left; }
.code { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 700; letter-spacing: 1px; }
.inline-form { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.inline-form input, .inline-form select {
  font: inherit; font-size: 14px; padding: 6px 8px; width: 62px;
  border: 1px solid var(--line); border-radius: 7px; background: var(--bg); color: var(--ink);
}
.inline-form select { width: auto; }

/* ---------------------------------------------------------- footer */
.site-footer { margin-top: 40px; padding: 26px 0 34px; border-top: 1px solid var(--line); }
.sponsor-strip { text-align: center; margin-bottom: 18px; }
.sponsor-label { display: block; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .8px; color: var(--ink-3); margin-bottom: 8px; }
.sponsor-logos { display: flex; flex-wrap: wrap; justify-content: center; gap: 8px; }
.sponsor { background: var(--card); border: 1px solid var(--line); border-radius: 8px;
  padding: 7px 14px; font-size: 13.5px; color: var(--ink); font-weight: 550; }
.foot-links { text-align: center; font-size: 13.5px; color: var(--ink-2); margin: 0 0 8px; }
.fine { text-align: center; font-size: 12px; color: var(--ink-3); max-width: 54ch;
  margin: 0 auto; line-height: 1.5; }

/* Admin score rows: keep one line on desktop, stack cleanly on a phone. */
.inline-form { flex-wrap: nowrap; }
.inline-form input { width: 54px; }
@media (max-width: 640px) {
  .code-table td, .code-table th { padding: 6px 4px; font-size: 13px; }
  .code-table tr { display: grid; }
  .inline-form { flex-wrap: wrap; }
  .card { padding: 15px; }
  .team-head { padding: 16px; }
  .record b { font-size: 19px; }
}

/* Uppercase the typed code without shouting in the placeholder. */
.field input.upper { text-transform: uppercase; }
.field input.upper::placeholder { text-transform: none; }

/* ---------------------------------------------------------- game page */
.crumb { font-size: 13px; margin: 16px 0 0; }
.state { text-decoration: none; }
.state:hover { text-decoration: none; }
.game:hover { border-color: var(--ink-3); }

.matchup {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 16px; margin: 12px 0 14px; display: grid; gap: 10px;
}
.matchup .side { display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 4px 12px; }
.mname { font-size: 18px; font-weight: 600; color: var(--ink); }
.side.win .mname { font-weight: 800; color: var(--win); }
.mmeta { grid-column: 1; font-size: 12px; color: var(--ink-3); }
.mscore {
  grid-row: 1 / span 2; grid-column: 2; font-size: 34px; font-weight: 800;
  color: var(--ink-2); font-variant-numeric: tabular-nums; line-height: 1;
}
.side.win .mscore { color: var(--ink); }
.mstate {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  border-top: 1px solid var(--line); border-bottom: 1px solid var(--line);
  padding: 8px 0; margin: 2px 0;
}
.mstate .sub { font-size: 12px; color: var(--ink-3); }

.sec {
  font-size: 13px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--ink-3); margin: 22px 0 8px; font-weight: 700;
}
.sec .hint { text-transform: none; letter-spacing: 0; font-weight: 400; font-size: 12px; }

.scroll-x { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.line-table {
  width: 100%; border-collapse: collapse; background: var(--card);
  border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden;
  font-variant-numeric: tabular-nums;
}
.line-table th, .line-table td {
  padding: 9px 10px; border-bottom: 1px solid var(--line);
  text-align: center; font-size: 14px; white-space: nowrap;
}
.line-table th {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em;
  color: var(--ink-3); font-weight: 700;
}
.line-table tr:last-child td { border-bottom: 0; }
.line-table .lname { text-align: left; font-weight: 600; width: 100%; white-space: normal; }
.line-table .tot { font-weight: 800; color: var(--ink); }
.stat-table .lname { font-weight: 500; }
.stat-table .lname .tclass { margin-left: 5px; font-weight: 600; }

.stand-table .lname {
  display: flex; align-items: center; gap: 8px;
  /* Let the table scroll sideways rather than stacking school names two-high. */
  white-space: nowrap;
}
.stand-table .rank {
  display: inline-grid; place-items: center; width: 20px; height: 20px; flex: none;
  border-radius: 5px; background: var(--bg); border: 1px solid var(--line);
  font-size: 11px; font-weight: 700; color: var(--ink-3);
}
.stand-table tbody tr:first-child .rank { background: var(--brand); border-color: var(--brand); color: #fff; }

/* ---------------------------------------------------------- box entry */
.line-table.entry td { padding: 6px; }
.line-table.entry input {
  width: 52px; text-align: center; font: inherit; font-size: 16px; padding: 7px 4px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.side-head {
  font-size: 15px; margin: 18px 0 8px; font-weight: 700; letter-spacing: -.2px;
}
.statline {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 11px; margin-bottom: 7px;
}
.statline-cat {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--ink-3); font-weight: 700; margin-bottom: 5px;
}
.statline > input[type="text"] {
  width: 100%; font: inherit; font-size: 16px; padding: 8px 10px; margin-bottom: 7px;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
}
.statline-nums { display: flex; gap: 6px; flex-wrap: wrap; }
.statline-nums label {
  display: flex; align-items: center; gap: 4px; font-size: 11px;
  color: var(--ink-3); font-weight: 600;
}
.statline-nums input {
  width: 54px; font: inherit; font-size: 16px; padding: 6px 4px; text-align: center;
  border: 1px solid var(--line); border-radius: 8px; background: var(--bg); color: var(--ink);
  font-variant-numeric: tabular-nums;
}

/* ---------------------------------------------------------- legal pages */
.legal { max-width: 680px; padding-bottom: 10px; }
.legal h1 { font-size: 27px; letter-spacing: -.6px; margin: 26px 0 4px; }
.legal .legal-date {
  font-size: 12px; color: var(--ink-3); margin: 0 0 20px;
  text-transform: uppercase; letter-spacing: .06em;
}
.legal .legal-lede {
  font-size: 16.5px; color: var(--ink); background: var(--card);
  border: 1px solid var(--line); border-left: 3px solid var(--brand);
  border-radius: 10px; padding: 14px 16px; margin: 0 0 26px; line-height: 1.55;
}
.legal h2 {
  font-size: 19px; letter-spacing: -.3px; margin: 30px 0 8px;
  padding-top: 18px; border-top: 1px solid var(--line);
}
.legal h3 { font-size: 15.5px; margin: 20px 0 6px; }
.legal p, .legal li { font-size: 15.5px; line-height: 1.6; color: var(--ink-2); }
.legal p { margin: 0 0 12px; }
.legal ul { margin: 0 0 14px; padding-left: 20px; }
.legal li { margin-bottom: 6px; }
.legal strong { color: var(--ink); }
.legal code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13.5px;
  background: var(--bg); border: 1px solid var(--line); border-radius: 4px; padding: 1px 5px;
}
.legal .legal-callout {
  background: var(--card); border: 1px solid var(--line);
  border-left: 3px solid var(--live); border-radius: 10px;
  padding: 12px 15px; color: var(--ink); font-weight: 550;
}
.legal .legal-foot {
  margin-top: 30px; padding-top: 16px; border-top: 1px solid var(--line);
  font-size: 14px;
}
.legal-links { font-size: 12.5px; color: var(--ink-3); margin-top: 6px; }

/* ---------------------------------------------------------- group pages */
.meta-link { color: #cfe0f2; text-decoration: underline; text-underline-offset: 2px; }
.meta-link:hover { color: #fff; }
.group-switch { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 13px; }
.group-switch a {
  background: rgba(255,255,255,.12); color: #fff; border: 1px solid rgba(255,255,255,.2);
  border-radius: 7px; padding: 5px 11px; font-size: 13px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.group-switch a:hover { background: rgba(255,255,255,.22); text-decoration: none; }
.group-switch a.on { background: #fff; color: var(--brand); border-color: #fff; }
.district h3 a { color: var(--ink-3); }
.district h3 a:hover { color: var(--accent); }

/* ---------------------------------------------------------- team identity */
/* Colour bars and chips carry the school's real colour. Text never does —
   half of these schools are gold or columbia blue, which is unreadable on
   white, and a scoreboard's whole job is being scannable. */
.tbar {
  width: 3px; align-self: stretch; min-height: 15px; border-radius: 2px;
  flex: none; margin-right: 1px;
}
.tchip {
  width: 9px; height: 9px; border-radius: 3px; flex: none;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.12);
}

.team-id { display: flex; align-items: center; gap: 13px; }
.team-mark {
  width: 64px; height: 64px; flex: none; border-radius: 12px;
  background: rgba(255,255,255,.14); padding: 4px; object-fit: contain;
}
.team-mascot {
  margin: 3px 0 0; font-size: 13px; color: rgba(255,255,255,.78);
  text-transform: uppercase; letter-spacing: .08em; font-weight: 600;
}
/* A school-coloured header gets a stripe of its secondary colour. */
.team-head[style*="background"] { position: relative; overflow: hidden; }
.team-head[style*="background"]::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 5px;
  background: var(--team2, #fff); opacity: .85;
}

.side-mark {
  width: 34px; height: 34px; flex: none; border-radius: 8px;
  object-fit: contain; margin-right: 2px;
}
.matchup .side { grid-template-columns: auto 1fr auto; }
.matchup .side .mname { grid-column: 2; }
.matchup .side .mmeta { grid-column: 2; }
.matchup .side .side-mark { grid-row: 1 / span 2; grid-column: 1; }
.matchup .side .mscore { grid-column: 3; }

@media (max-width: 480px) {
  .team-mark { width: 52px; height: 52px; }
}

/* A school colour used as text. The two custom properties are set inline per
   element by teamInk(); each has already been contrast-corrected for its
   theme, so neither can fall below 4.5:1. */
.inked { color: var(--team-ink); }
.row.win .inked, .side.win .inked { color: var(--team-ink); }
@media (prefers-color-scheme: dark) {
  .inked, .row.win .inked, .side.win .inked { color: var(--team-ink-dark); }
}


/* ------------------------------------------------- report-a-score fast path */
/* The button under the score on a game page. Full width on a phone because it
   is the only action on the page and a coach is using one thumb. */
.report-now { margin: 14px 0 4px; text-align: center; }
.report-now .btn { display: block; width: 100%; }
.report-now .hint { display: block; margin-top: 6px; font-size: 12px; color: var(--ink-3); }
@media (min-width: 560px) {
  .report-now .btn { display: inline-block; width: auto; min-width: 260px; }
}

/* The matchup, shown as a fact rather than two dropdowns, when the form was
   reached from a game page. */
.known-game {
  background: var(--brand-tint); border: 1px solid #cddaf2;
  border-radius: var(--radius); padding: 11px 13px; margin-bottom: 18px;
  display: flex; flex-direction: column; gap: 2px;
}
.known-game strong { font-size: 16px; }
.known-game span { font-size: 12.5px; color: var(--ink-2); }
.known-game a { font-size: 12.5px; margin-top: 3px; }

/* Optional fields, folded away. Three more boxes at half time is three more
   reasons to give up. */
.about-you { margin: 4px 0 16px; }
.about-you > summary {
  cursor: pointer; font-size: 13.5px; color: var(--brand);
  font-weight: 600; padding: 4px 0;
}

.remember {
  display: flex; align-items: flex-start; gap: 7px;
  margin-top: 9px; font-size: 13px; color: var(--ink-2); line-height: 1.45;
}
.remember input { width: auto; margin: 2px 0 0; flex: none; }

/* Box score links in a team's schedule.
   Deliberately quiet: the result is the thing being read, and this sits beside
   it as an offer rather than competing with it. The "+ Box" variant is the one
   that matters — every part of the box score feature existed for weeks and no
   game in Kentucky had one, because nothing on the site ever pointed at it. */
.boxlink {
  display: inline-block; margin-left: 8px; font-size: 11px; font-weight: 700;
  letter-spacing: .3px; text-transform: uppercase; text-decoration: none;
  border: 1px solid var(--line); border-radius: 4px; padding: 1px 6px;
  color: var(--ink-3); vertical-align: middle;
}
.boxlink:hover { border-color: var(--brand); color: var(--brand); }
.boxlink.add { color: var(--ink-3); opacity: .8; }
.boxlink.add:hover { opacity: 1; }


/* A tournament round with no opponent drawn yet: the event and the gym, in
   place of a link to a school that does not exist. Deliberately quieter than a
   team name, because it is not one. */
.tname.tba { color: var(--ink-2); font-style: italic; font-weight: 600; }

/* TONIGHT, THEN THE NEXT NIGHT.
   "however many games there are tonight, then they start the list of tomorrows
   games." Each block under its own date, so a Friday game is never mistaken for
   a Wednesday one. */
.sb-next {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  font-size: 15px; margin: 26px 0 8px; padding-bottom: 6px;
  border-bottom: 1px solid var(--line);
}
.sb-next a { color: var(--ink); text-decoration: none; }
.sb-next a:hover { text-decoration: underline; }
.sb-next span { font-size: 12.5px; color: var(--ink-3); font-weight: 400; }
.sb-none { font-size: 14px; color: var(--ink-2); margin: 0 0 14px; }

/* ------------------------------------------------------- sold sponsorships
   Businesses who paid this site directly, as opposed to the ad network. Held
   deliberately quieter than an AdSense unit: a sponsor is buying a scoreboard
   people trust, and the first thing that spends that trust is a logo competing
   with the scores. See lib/sponsors.js. */
.presented-by {
  background: var(--brand-tint);
  border-bottom: 1px solid var(--line);
  font-size: 13px;
  padding: 7px 0;
  color: var(--ink-2);
}
.presented-by .wrap {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
}
.presented-by b { color: var(--ink); font-weight: 700; }
.presented-by a { text-decoration: none; }
.presented-by a:hover b { text-decoration: underline; }
.presented-by em { font-style: normal; color: var(--ink-3); font-size: 12.5px; }

.ad-sold { display: flex; flex-direction: column; align-items: center; }
.sponsor-card {
  display: flex; flex-direction: column; align-items: center; gap: 6px;
  text-decoration: none; color: var(--ink); padding: 16px 18px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--card); width: 100%; max-width: 728px; text-align: center;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.sponsor-card:hover { border-color: var(--brand); box-shadow: 0 1px 6px rgba(23,48,122,.09); }
/* A LOGO IS THE POINT, SO GIVE IT ROOM AND A WHITE GROUND.
   Most business logos are drawn for white and a transparent PNG on a tinted
   card renders as dark-on-dark for half of them. The plate is not decoration,
   it is the thing that stops a sponsor's mark disappearing. */
.sponsor-logo {
  background: #fff; border-radius: 6px; padding: 10px 14px;
  display: flex; align-items: center; justify-content: center;
  max-width: 100%;
}
.sponsor-logo img { max-height: 58px; max-width: 100%; width: auto; display: block; }
.sponsor-name { font-size: 17px; font-weight: 700; letter-spacing: -.01em; line-height: 1.25; }
.sponsor-line { font-size: 13.5px; color: var(--ink-2); line-height: 1.4; }
/* With a logo the name is a caption, not the headline. */
.sponsor-card.has-logo .sponsor-name { font-size: 13.5px; font-weight: 600; color: var(--ink-2); }
