/* Components. Colours come only from theme.css tokens. */

*, *::before, *::after { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--c-ground);
  color: var(--c-ink);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.5;
  -webkit-text-size-adjust: 100%;
}

h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  line-height: 1.15;
  margin: 0 0 var(--space-4);
}

a { color: var(--c-accent); }

/* ---------- Icons ----------
   Sized in ems so an icon tracks the text it sits beside, and nudged down a
   little: a cap-height glyph next to lowercase text otherwise reads high. */
.icon {
  width: 1em;
  height: 1em;
  flex: none;
  vertical-align: -0.125em;
}
.icon--brand { width: 1.2em; height: 1.2em; }

/* ---------- App bar ----------
   Dark chrome over light content. Because the bar is dark and the page is
   not, every colour in here comes from the --c-chrome-* pair rather than
   --c-ink, which would be invisible. */
.appbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;              /* a long coach name must wrap, not overflow a 320px phone */
  gap: var(--space-3) var(--space-4);
  padding: var(--space-2) var(--space-4);
  background: var(--c-chrome);
  color: var(--c-chrome-ink);
}
/* Every tappable item in the bar clears --tap-min: this chrome is on every
   authenticated page, and coaches use it one-handed at the alley. */
.appbar__brand,
.appbar__user,
.appbar__link,
.appbar__context {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
}
.appbar__brand {
  font-weight: 700;
  font-size: var(--step-1);
  color: var(--c-chrome-ink);
  text-decoration: none;
}
/* height:auto overrides the height attribute on the tag, which is there to
   reserve the right box before the image loads rather than to size it.

   2.4rem for about 1.4rem of visible mark: only 58.6% of the logo PNG's
   height is ink, the rest being transparent padding above and below the
   wordmark, so a height set to what the mark should look like comes out
   two-fifths too small. Still inside the bar's --tap-min rhythm. */
.appbar__logo {
  display: block;
  height: 2.4rem;
  width: auto;
}
.appbar__context {
  color: var(--c-chrome-ink-dim);
  font-size: var(--step--1);
}
.appbar__context .badge {
  background: transparent;              /* the pill's light fill is invisible on dark chrome */
  color: var(--c-chrome-ink);
  border-color: var(--c-chrome-ink-dim);
}
/* ---------- League switcher ----------
   A <details> disclosure, because this app ships no JavaScript and the
   browser opens and closes one for free. The list is positioned over the page
   rather than pushing it down, which is the only part a menu really needs. */
.leagueswitch { position: relative; }
.leagueswitch__summary {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  border-radius: var(--radius);
  color: var(--c-chrome-ink);
  font-size: var(--step--1);
  cursor: pointer;
  list-style: none;          /* no default triangle; the caret below is ours */
}
.leagueswitch__summary::-webkit-details-marker { display: none; }
.leagueswitch__summary::after {
  content: '';
  width: 0.4em;
  height: 0.4em;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-0.1em, -0.1em);
}
.leagueswitch__summary .badge {
  background: transparent;              /* the pill's light fill is invisible on dark chrome */
  color: var(--c-chrome-ink);
  border-color: var(--c-chrome-ink-dim);
}
.leagueswitch__summary:hover { background: var(--c-chrome-2); }
.leagueswitch[open] > .leagueswitch__summary { background: var(--c-chrome-2); }

.leagueswitch__list {
  position: absolute;
  z-index: 20;
  top: calc(100% + var(--space-1));
  left: 0;
  min-width: 12rem;
  margin: 0;
  padding: var(--space-1) 0;
  list-style: none;
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  box-shadow: var(--shadow-menu);
}
.leagueswitch__link {
  display: flex;
  align-items: center;
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  color: var(--c-ink);
  text-decoration: none;
  font-size: var(--step--1);
}
.leagueswitch__link:hover { background: var(--c-surface-2); }
.leagueswitch__link.is-active { font-weight: 700; color: var(--c-accent); }

/* On a phone the bar has already wrapped; a floating panel would sit off the
   side of a 320px screen, so it opens in the flow instead. */
@media (max-width: 30rem) {
  .leagueswitch { width: 100%; }
  .leagueswitch__list { position: static; width: 100%; }
}

.appbar__spacer { margin-left: auto; }
.appbar__link { color: var(--c-chrome-ink-dim); font-size: var(--step--1); }
.appbar__link:hover { color: var(--c-chrome-ink); }
.appbar__user {
  color: var(--c-chrome-ink-dim);
  font-size: var(--step--1);
  max-width: 14ch;              /* truncate rather than push the sign-out button off-screen */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* On a narrow screen the auto-margin spacer would right-align the first row
   while the wrapped row stayed left — drop it and let everything stack left. */
@media (max-width: 30rem) {
  .appbar__spacer { display: none; }
  .appbar__context { display: none; }   /* the page's own title already names the league */
}

/* ---------- Layout ---------- */
.shell { display: flex; align-items: stretch; min-height: calc(100vh - 60px); }
.app-main { flex: 1; min-width: 0; padding: var(--space-5) var(--space-4) var(--space-8); }
.page { max-width: 70rem; margin: 0 auto; }
.page--narrow { max-width: 32rem; }
.page__title { font-size: var(--step-3); }

/* ---------- Brand band ----------
   The two marks that own this programme, on white: Youth Bowl Canada, and
   Mid-Town Bowl where it bowls. White is required rather than chosen — the
   Mid-Town logo is black line art on transparency and would vanish on the
   app's black chrome.

   space-between rather than a gap, so the pair sits to the two edges of the
   band and reads as a letterhead instead of a pair of loose images. Below
   30rem it centres and stacks, because side by side at 320px leaves each
   mark too small to make out. */
.brandband {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-5);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  /* The programme's red along the top edge, the way youthbowl.ca rules its
     own header. A border rather than a filled band: the marks need white
     behind them, and this is the one line of brand colour that does not
     fight them for it. */
  border-top: 4px solid var(--c-accent);
}
.brandband__logo { display: block; width: auto; }
/* Sized so the two read as equals, which is not the same as sizing them
   alike. Three things pull against each other here.

   First, the padding: 58.6% of the YBC PNG's height is ink and the house mark
   is cropped to its own ink, so 5.5rem of YBC box is about 3.2rem of visible
   wordmark.

   Second, the shape: the YBC lockup is 4.85 times as wide as it is tall and
   the house mark is 1.72, and a badge needs more height than a wordmark
   before the two feel like equals.

   Third, and the reason the house is no longer the 4.5rem that suited the old
   Mid-Town badge: the MTB mark has a line of type under it. "BOWLING +
   ARCADE" is about an eighth of the mark's height, so at 4.5rem it sets at
   roughly 8px and is a grey smudge rather than words. 5.5rem is the point at
   which it becomes readable, and matching YBC's box turns out to balance them
   anyway — the house mark is all ink where YBC's is mostly padding. */
.brandband__logo--ybc   { height: 5.5rem; }
.brandband__logo--house { height: 5.5rem; }

@media (max-width: 30rem) {
  .brandband { justify-content: center; }
  .brandband__logo--ybc   { height: 4rem; }
  /* Same argument as above: below about 4rem the subline stops being words. */
  .brandband__logo--house { height: 4rem; }
}

/* ---------- League sidebar ----------
   The old app deleted this below 768px and took the navigation with it. Here
   it turns into a scrolling strip of chips instead, so the screens are still
   reachable from the phone they are most used on. */
.sidebar {
  display: flex;
  flex-direction: column;
  width: 13rem;
  flex: none;
  background: var(--c-chrome);
  color: var(--c-chrome-ink-dim);
}
.sidebar__list { list-style: none; margin: 0; padding: var(--space-2) 0; flex: 1; }
.sidebar__link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  padding: 0 var(--space-4);
  border-left: 3px solid transparent;
  color: var(--c-chrome-ink-dim);
  text-decoration: none;
  font-size: var(--step--1);
}
.sidebar__link:hover { background: var(--c-chrome-2); color: var(--c-chrome-ink); }
.sidebar__link.is-active {
  background: var(--c-chrome-2);
  border-left-color: var(--c-accent);
  color: var(--c-chrome-ink);
  font-weight: 600;
}
.sidebar__foot { padding: var(--space-3); }
.sidebar__foot .btn {
  gap: var(--space-2);
  background: transparent;
  border-color: var(--c-chrome-ink-dim);
  color: var(--c-chrome-ink);
}

@media (max-width: 48rem) {
  .shell { display: block; }
  .sidebar { width: auto; flex-direction: row; overflow-x: auto; }
  .sidebar__list { display: flex; padding: 0; }
  .sidebar__link {
    white-space: nowrap;
    border-left: 0;
    border-bottom: 3px solid transparent;
  }
  .sidebar__link.is-active { border-left: 0; border-bottom-color: var(--c-accent); }
  .sidebar__foot { display: none; }   /* the report is on the league's own page too */
}

/* ---------- Buttons ---------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
  padding: 0 var(--space-5);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  background: var(--c-surface);
  color: var(--c-ink);
  font: inherit;
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
}
.btn:hover { background: var(--c-surface-2); }
.btn--primary { background: var(--c-accent); color: var(--c-accent-ink); border-color: transparent; }
.btn--primary:hover { background: var(--c-accent); filter: brightness(0.92); }
.btn--block   { width: 100%; }
.btn--danger  { color: var(--c-danger); }
/* Chrome is tight on a phone; a bar button that cleared 44px would push the
   sign-out control onto its own row on every page. These sit inside a bar
   that is itself a tap target, so 36px is the floor here. */
.btn--sm { min-height: 36px; padding: 0 var(--space-3); font-size: var(--step--1); }

/* Two or more controls side by side in one table cell. Each button is its own
   <form> — they post to different routes — and a form is a block, so without
   this they stack. Wraps rather than overflows: on a phone the pair drops to
   two rows instead of pushing the table sideways. */
.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: center;
}
.btn:focus-visible { outline: 3px solid var(--c-accent); outline-offset: 2px; }

/* ---------- Forms ---------- */
.form { display: grid; gap: var(--space-4); }
.form--inline {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  margin-bottom: var(--space-5);
}
.form--inline .field { margin: 0; }
.field { display: grid; gap: var(--space-2); }
.field__label { font-size: var(--step--1); color: var(--c-ink-dim); text-transform: uppercase; letter-spacing: 0.06em; }
.field__hint  { font-size: var(--step--1); color: var(--c-ink-dim); }

/* A .field that is a <fieldset> of radios rather than one input. The reset is
   the point: a bare fieldset carries a browser border and its own padding, so
   without this the permission question sits in a box no other field has. */
.field:where(fieldset) {
  border: 0;
  padding: 0;
  margin-inline: 0;
  min-width: 0;                    /* fieldset defaults to min-content, which will not shrink to 320px */
}

/* One radio and its wording, on a row that is a comfortable tap target. The
   label wraps the input, so the words are part of the hit area — the whole
   line is pressable, not just the dot. */
.choice {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--tap-min);
  font-size: var(--step-0);
  cursor: pointer;
}
.choice input { width: 1.15rem; height: 1.15rem; flex: none; }
.field__input, .field__select {
  min-height: var(--tap-min);
  padding: 0 var(--space-3);
  background: var(--c-surface-2);
  color: var(--c-ink);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  font: inherit;
  width: 100%;
}
.field__input:focus-visible, .field__select:focus-visible {
  outline: 3px solid var(--c-accent);
  outline-offset: 1px;
}
.checklist { display: grid; gap: var(--space-2); }
.checklist__item { display: flex; align-items: center; gap: var(--space-3); min-height: var(--tap-min); }

/* ---------- Auth ---------- */
.auth-shell { min-height: 100dvh; display: grid; place-items: center; padding: var(--space-4); }
.auth-card {
  width: min(26rem, 100%);
  padding: var(--space-6);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
}
.auth-eyebrow { margin: 0; color: var(--c-accent); font-size: var(--step--1); letter-spacing: 0.16em; text-transform: uppercase; }
.auth-title { font-size: var(--step-3); margin-top: var(--space-2); }
.auth-footnote { margin-top: var(--space-5); font-size: var(--step--1); }

/* ---------- Flash & notices ---------- */
.flash-list { list-style: none; margin: 0 0 var(--space-5); padding: 0; display: grid; gap: var(--space-2); }
.flash, .notice {
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius);
  border-left: 4px solid var(--c-line);
  background: var(--c-surface-2);
}
.flash--success { border-left-color: var(--c-success); }
.flash--error   { border-left-color: var(--c-danger); }
.flash--info    { border-left-color: var(--c-accent); }
.notice--warning { border-left-color: var(--c-warning); }
.notice--info    { border-left-color: var(--c-accent); }

/* Pick a Winner: the monthly Perfect Attendance draw. */
.wheel__prompt  { margin: var(--space-5) 0 0; text-align: center; font-size: var(--step-2); font-weight: 400; }
.wheel__stage   { position: relative; max-width: 760px; margin: var(--space-4) auto 0; }
.wheel__canvas  { display: block; margin: 0 auto; max-width: 100%; cursor: pointer; }
/* The spin button doubles as the pointer: the wheel stops with the winner
   under the tip of its pin. */
.wheel__hub {
  position: absolute; top: 50%; left: 50%; z-index: 1;
  width: 15%; aspect-ratio: 168 / 280;
  transform: translate(-50%, -70%);
  padding: 0; border: 0; background: none; cursor: pointer;
  filter: drop-shadow(var(--shadow-menu));
}
.wheel__hub:active:not(:disabled) { transform: translate(-50%, -70%) scale(0.92); }
.wheel__hub:disabled { cursor: default; }
.wheel__hub:focus-visible { outline: 3px solid var(--c-accent); outline-offset: 4px; border-radius: 50%; }
.wheel__hub svg  { display: block; width: 100%; height: 100%; }
.wheel__hub-pin  { fill: var(--c-surface); stroke: var(--c-line); stroke-width: 3; }
.wheel__hub-text { fill: var(--c-accent); font: 800 44px Arial, Helvetica, sans-serif; letter-spacing: 1px; }
.wheel__result  { min-height: 2.5em; margin: var(--space-4) 0 0; text-align: center; font-size: var(--step-3); }
.wheel__result strong { color: var(--c-accent); }
.wheel__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-3); }
.wheel__start   { min-width: 12rem; font-size: var(--step-1); }

/* ---------- Cards ----------
   One component for every block of content on a page. The head is dark by
   default, with variants for the sections that want to announce themselves —
   this is what makes a screen read as finished rather than as a stack of
   headings. */
.card {
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  overflow: hidden;                /* so the head's corners follow the card's */
  margin-bottom: var(--space-5);
}
.card__head {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--c-chrome);
  color: var(--c-chrome-ink);
  font-weight: 700;
}
.card__head--accent  { background: var(--c-accent);  color: var(--c-accent-ink); }
.card__head--success { background: var(--c-success); color: var(--c-accent-ink); }
.card__head--warning { background: var(--c-warning); color: var(--c-accent-ink); }
.card__sub {
  font-weight: 400;
  font-size: var(--step--1);
  opacity: 0.75;
}
.card__body { padding: var(--space-4); }
/* A table fills its card edge to edge; padding around it would leave the
   header row floating in a white margin. */
.card__body--flush { padding: 0; }
.card__body--flush .table th:first-child,
.card__body--flush .table td:first-child { padding-left: var(--space-4); }
.card__body--flush .table th:last-child,
.card__body--flush .table td:last-child { padding-right: var(--space-4); }

/* ---------- Status pills ----------
   Never colour alone: the word stays in the pill, so the state survives a
   greyscale printout and a colour-blind reader.

   .badge is the same component under the name the views already use. Every
   status word in the app is already marked up as one, so styling it here
   turns them all into pills without forty view edits — and without a second
   spelling of the same idea for the next screen to choose between. */
.pill,
.badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--c-line);
  border-radius: 99px;
  background: var(--c-surface-2);
  color: var(--c-ink-dim);
  font-size: var(--step--1);
  font-weight: 600;
  white-space: nowrap;
}
.pill--success, .badge--ok      { background: var(--c-success-subtle); border-color: var(--c-success); color: var(--c-success); }
.pill--warning, .badge--pending { background: var(--c-warning-subtle); border-color: var(--c-warning); color: var(--c-warning); }
.pill--danger,  .badge--miss    { background: var(--c-danger-subtle);  border-color: var(--c-danger);  color: var(--c-danger); }
.pill--info                     { background: var(--c-info-subtle);    border-color: var(--c-info);    color: var(--c-info); }

/* ---------- Progress bars ----------
   Always beside a written figure. The bar is the glance; the number is the
   answer. */
.bar {
  display: block;
  width: 100%;
  min-width: 4rem;
  height: 8px;
  border-radius: 99px;
  background: var(--c-surface-2);
  overflow: hidden;
}
.bar__fill { display: block; height: 100%; background: var(--c-success); }
.bar__fill--warning { background: var(--c-warning); }
.bar__fill--accent  { background: var(--c-accent); }
.bar-cell { display: grid; gap: var(--space-1); min-width: 6rem; }
.bar-cell__label { font-size: var(--step--1); color: var(--c-ink-dim); }

/* ---------- Dashboard tiles ---------- */
.tile-grid {
  display: grid;
  gap: var(--space-4);
  grid-template-columns: repeat(auto-fill, minmax(15rem, 1fr));
}
.tile {
  display: block;
  padding: var(--space-5);
  min-height: 8rem;
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-top: 4px solid var(--tile-colour, var(--c-accent));
  border-radius: var(--radius);
  color: var(--c-ink);
  text-decoration: none;
}
.tile:hover, .tile:focus-visible { border-color: var(--c-accent); border-top-color: var(--tile-colour, var(--c-accent)); }
.tile__icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.5rem;
  margin-bottom: var(--space-3);
  border-radius: var(--radius);
  background: var(--c-surface-2);
  color: var(--tile-colour, var(--c-accent));
  font-size: var(--step-2);
}
.tile__title { display: block; font-weight: 700; font-size: var(--step-1); }
.tile__desc  { display: block; color: var(--c-ink-dim); font-size: var(--step--1); margin-top: var(--space-2); }

/* ---------- Tables ---------- */
.table { width: 100%; border-collapse: collapse; }
.table th, .table td { padding: var(--space-3); border-bottom: 1px solid var(--c-line); text-align: left; }
.table th {
  background: var(--c-surface-2);
  font-size: var(--step--1);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--c-ink-dim);
}
.table tbody tr:last-child td { border-bottom: 0; }

/* A table that is not already inside a card gets the card's surface anyway.
   Doing it here rather than by editing every view keeps one definition of
   what a block of content looks like — and a screen that later wants a
   titled head only has to add the .card wrapper around what is already
   there. */
.page > .table-scroll {
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  margin-bottom: var(--space-5);
}
.page > .table-scroll > .table th:first-child,
.page > .table-scroll > .table td:first-child { padding-left: var(--space-4); }
.page > .table-scroll > .table th:last-child,
.page > .table-scroll > .table td:last-child { padding-right: var(--space-4); }
/* First place, as the old app marked it. */
.table .row--first td { background: var(--c-warning-subtle); font-weight: 700; }
.table-scroll { overflow-x: auto; }

/* ---------- Empty states ---------- */
.empty {
  padding: var(--space-7) var(--space-5);
  text-align: center;
  border: 1px dashed var(--c-line);
  border-radius: var(--radius);
  color: var(--c-ink-dim);
}

/* ---------- Error pages ---------- */
/* Error pages are the most numeral-forward moment in the app — lean into the
   scoreboard direction rather than leaving a default-sized heading. */
.error-code {
  font-family: var(--font-display);
  font-size: var(--step-5);
  line-height: 1;
  color: var(--c-accent);
  margin: 0;
}
.error-message { font-size: var(--step-2); margin: var(--space-3) 0; }
.error-detail  { color: var(--c-ink-dim); }

@media print {
  .appbar, .sidebar, .btn, .flash-list { display: none !important; }
}

/* ---------- Public landing ----------
   Laid out in blocks the way youthbowl.ca is: a black bar, a red hero, a cyan
   stat band, then white content. The colour does the work here because this is
   the one page in the app a stranger sees, and the version before it was a pale
   grey admin page that happened to have red links.

   Every rule below that recolours a SHARED component is scoped to
   .layout-public. .statband and .stat also build the bowler profile inside the
   app, and repainting those globally would have turned a coach's profile screen
   cyan. */
.pubbar {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--c-chrome);
  color: var(--c-chrome-ink);
}
.pubbar__brand { display: block; }
/* 2.6rem of box for about 1.5rem of visible mark — 58.6% of the PNG's height
   is ink. Same arithmetic as .appbar__logo. */
.pubbar__logo { display: block; height: 2.6rem; width: auto; }
/* The button sits on black, so it cannot take .btn's light surface. */
.pubbar .btn {
  background: transparent;
  border-color: var(--c-chrome-ink-dim);
  color: var(--c-chrome-ink);
}
.pubbar .btn:hover { background: var(--c-chrome-2); }

.hero {
  padding: var(--space-8) var(--space-4);
  background: var(--c-accent);
  color: var(--c-accent-ink);
}
/* White on YBC red measures 4.7:1, which carries normal-size text as well as
   the display line — so the eyebrow and the meta can be white too, dimmed with
   opacity rather than a lighter colour. A tint would need a literal, and this
   file is not allowed one. */
.hero__eyebrow {
  margin: 0; color: var(--c-accent-ink); font-size: var(--step--1);
  letter-spacing: 0.18em; text-transform: uppercase;
  opacity: 0.85;
}
.hero__title {
  font-size: var(--step-5); margin: var(--space-3) 0;
  letter-spacing: -0.01em;
  color: var(--c-accent-ink);
}
.hero__meta { margin: 0; color: var(--c-accent-ink); opacity: 0.85; }

/* auto-fit, not a fixed three: the bowler profile shows four stats, and a
   hard 3-column grid dropped the fourth onto a row of its own. */
.statband {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  border-bottom: 1px solid var(--c-line);
}
.stat {
  padding: var(--space-6) var(--space-4);
  text-align: center;
  border-right: 1px solid var(--c-line);
}
.stat:last-child { border-right: 0; }
.stat__value {
  display: block; font-family: var(--font-display);
  font-size: var(--step-4); line-height: 1; color: var(--c-accent);
  font-variant-numeric: tabular-nums;
}
.stat__label {
  display: block; margin-top: var(--space-2);
  font-size: var(--step--1); color: var(--c-ink-dim);
  text-transform: uppercase; letter-spacing: 0.1em;
}

.pubfooter {
  padding: var(--space-6) var(--space-4);
  color: var(--c-ink-dim);
  border-top: 1px solid var(--c-line);
  text-align: center;
}
.pubfooter__line { margin: 0; font-size: var(--step--1); }
/* The footer band holds the two marks at the width of the content above it,
   not the full window. */
.pubfooter .brandband { max-width: 70rem; margin: 0 auto var(--space-4); }


.layout-public section.page > h2 {
  color: var(--c-brand-deep);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: var(--step-2);
}

.page + .page { margin-top: var(--space-6); }
section.page { padding: var(--space-6) var(--space-4); }

@media (max-width: 30rem) {
  .statband { grid-template-columns: 1fr; }
  .stat { border-right: 0; border-bottom: 1px solid var(--c-line); }
  .layout-public .stat { border-bottom-color: var(--c-surface); }
}

/* ── League Management ─────────────────────────────────────── */

.page__sub {
  color: var(--c-ink-dim);
  margin: calc(-1 * var(--space-3)) 0 var(--space-5);
}

/* .badge and its variants are defined with .pill, above: they are the same
   component, and two definitions of it would drift. */

.row--muted td {
  color: var(--c-ink-dim);
  font-style: italic;
}

/* ── Weekly scorecard ──────────────────────────────────────────
   Used on a phone, at the alley, while children are bowling.
   One column at 320px, no horizontal scroll, 44px targets. */

.scorecard {
  list-style: none;
  margin: 0 0 var(--space-5);
  padding: 0;
  display: grid;
  gap: var(--space-3);
}

.scorecard__row {
  display: grid;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
}

.scorecard__who {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  align-items: baseline;
}

.scorecard__name { font-weight: 600; }
.scorecard__team { color: var(--c-ink-dim); font-size: var(--step--1); }

.scorecard__games {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
}

.scorecard__game,
.scorecard__status { display: grid; gap: var(--space-1); }

/* The reason for an excused absence appears only when that is the status
   chosen. :has() over the select's :checked option tracks the coach's choice
   live, with no script; a browser without :has() ignores the rule and shows
   the field always, which is untidy rather than broken. Deliberately not the
   other way round — a field hidden by default in a browser that cannot reveal
   it would be a field nobody could ever fill in. */
.scorecard__reason { display: grid; gap: var(--space-1); margin-top: var(--space-2); }

@supports selector(:has(*)) {
  .scorecard__status:not(:has(option[value="absent_approved"]:checked)) .scorecard__reason {
    display: none;
  }
}

.scorecard__gamelabel {
  font-size: var(--step--1);
  color: var(--c-ink-dim);
}

.scorecard__input {
  min-height: var(--tap-min);
  width: 100%;
  box-sizing: border-box;
  padding: var(--space-2);
  font-size: var(--step-1);
  background: var(--c-surface-2);
  color: var(--c-ink);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
}

.scorecard__input:focus-visible {
  outline: 2px solid var(--c-accent);
  outline-offset: 2px;
}

/* From a tablet up, the whole row fits on one line. */
@media (min-width: 40rem) {
  .scorecard__row {
    grid-template-columns: 1fr auto auto;
    align-items: end;
  }
  .scorecard__games { grid-template-columns: repeat(3, 5.5rem); }
  .scorecard__status { min-width: 12rem; }
}

/* ── Swag board ────────────────────────────────────────────── */

.swag-grid th,
.swag-grid td { text-align: center; white-space: nowrap; }
.swag-grid td:first-child,
.swag-grid th:first-child { text-align: left; }

.swag-cell__empty { color: var(--c-ink-dim); }

/* Owed is the cell that wants a coach's attention, so it carries the
   success token the page's own caption promises. Mixed toward transparent
   rather than laid on flat: the button sits on top of it and still needs
   to read against --c-ink. */
.swag-cell--owed  { background: color-mix(in srgb, var(--c-success) 18%, transparent); }
.swag-cell--given { opacity: 0.55; }

/* ── Award rows (Bowler of the Week / Month) ───────────────── */

.award-row {
  padding: var(--space-4);
  margin-bottom: var(--space-4);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
}

.award-row__title {
  margin: 0 0 var(--space-2);
  font-family: var(--font-display);
  font-size: var(--step-1);
}

/* ── Coaches report ────────────────────────────────────────── */

/* Every printable overview — the coaches report, chocolate standings and draw
   lists, the Program Director's pages — is built from these blocks. Styling
   the block as a card here gives all of them the titled-card look at once,
   and a screen that wants an icon in the head only has to add .card__head to
   its own heading. */
.report__block {
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: var(--space-5);
}
.report__block > h2,
.report__block > h3 {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin: 0;
  padding: var(--space-3) var(--space-4);
  background: var(--c-chrome);
  color: var(--c-chrome-ink);
  font-size: var(--step-1);
}
/* h3 marks a sub-section (a tournament inside the awards block); it should
   read as a divider, not as a second title bar. */
.report__block > h3 {
  background: var(--c-surface-2);
  color: var(--c-ink);
  font-size: var(--step-0);
  border-top: 1px solid var(--c-line);
}
.report__block > p,
.report__block > .empty { margin: var(--space-4); }
/* A link in a block's black title bar, pushed to the right-hand end. */
.report__action {
  margin-left: auto;
  color: var(--c-chrome-ink);
  font-size: var(--step-0);
  font-weight: 700;
  white-space: nowrap;
}
.report__action:hover { color: var(--c-accent); }
.report__note {
  margin-left: auto;
  color: var(--c-chrome-ink-dim);
  font-size: var(--step--1);
  font-weight: 400;
  white-space: nowrap;
}
/* A chocolate draw's saved winner, above the list of who is left in it. */
.draw-winner {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-3);
  margin: 0; padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--c-line);
  font-size: var(--step-1);
}
.draw-winner p { margin: 0; }
.draw-winner strong { color: var(--c-accent); }
.report__block > .table-scroll > .table th:first-child,
.report__block > .table-scroll > .table td:first-child { padding-left: var(--space-4); }
.report__block > .table-scroll > .table th:last-child,
.report__block > .table-scroll > .table td:last-child { padding-right: var(--space-4); }

@media print {
  .no-print,
  .appbar,
  .sidebar,
  .flash-list { display: none !important; }

  .report__block { page-break-inside: avoid; }

  /* On screen a card head is a dark bar. On paper that is a band of solid
     ink across every section of a report a volunteer prints on their own
     printer — so the head keeps its weight and gives up its fill. */
  .card,
  .report__block {
    border: 0;
    background: transparent;
  }
  .card__head,
  .report__block > h2,
  .report__block > h3 {
    background: transparent;
    color: var(--c-ink);
    padding-left: 0;
    padding-right: 0;
    border-bottom: 1px solid var(--c-line);
  }
  .card__body { padding-left: 0; padding-right: 0; }
  .page > .table-scroll { border: 0; }

  /* The page ground is a light grey; paper is not. */
  body { background: var(--c-surface); }
}

/* ── Series chart ──────────────────────────────────────────────
   One series, so no legend: the card's own head names it. The line carries
   the accent; every other mark is recessive, and no number is written in the
   series colour — text wears ink. */

.spark {
  display: block;
  width: 100%;
  max-width: 44rem;
  height: auto;
}
.spark__line {
  fill: none;
  stroke: var(--c-accent);
  stroke-width: 2;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.spark__dot {
  fill: var(--c-accent);
  /* A ring in the surface colour so points that land on the line, or on each
     other, stay countable. */
  stroke: var(--c-surface);
  stroke-width: 2;
}
.spark__ref   { stroke: var(--c-ink-dim); stroke-width: 1; }
.spark__value { fill: var(--c-ink-dim); font-size: 11px; font-weight: 600; }
.spark__caption {
  margin: var(--space-3) 0 0;
  font-size: var(--step--1);
  color: var(--c-ink-dim);
}

/* ── Press copy ────────────────────────────────────────────── */

.press-copy {
  padding: var(--space-4);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
  white-space: pre-wrap;
  overflow-x: auto;
}

/* ── User Guide ────────────────────────────────────────────
   One long page a coach can print and keep beside the computer, so it is set
   as a document rather than as a grid of cards: a measure narrow enough to
   read, and headings that survive a black-and-white printer. */

.guide { max-width: 52rem; }
.guide p { max-width: 42rem; }

.guide__toc {
  padding: var(--space-4) var(--space-5);
  margin-bottom: var(--space-7);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  border-radius: var(--radius);
}
.guide__toc-title {
  margin: 0 0 var(--space-3);
  font-size: var(--step-0);
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--c-ink-dim);
}
.guide__toc-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: var(--space-2) var(--space-5);
  margin: 0;
  padding: 0;
  list-style: none;
}
.guide__toc-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--tap-min);
}

/* The rule above each section is what separates one module from the next on
   paper, where the anchors and the hover states are gone. */
.guide__section {
  padding-top: var(--space-6);
  margin-top: var(--space-6);
  border-top: 2px solid var(--c-line);
}
.guide__section:first-of-type { border-top: 0; margin-top: 0; padding-top: 0; }

.guide__heading {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-3);
  font-size: var(--step-2);
}
.icon--lg { width: 1.3em; height: 1.3em; color: var(--c-accent); }

.guide__intro { color: var(--c-ink-dim); }

.guide__topic { margin-top: var(--space-5); }
.guide__topic-title { font-size: var(--step-1); margin-bottom: var(--space-2); }

/* The addresses a topic covers. Monospace because they are things to type or
   recognise in the browser bar, not prose. */
.guide__paths {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-2);
}
.guide__path {
  padding: .1em .45em;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--step--1);
  background: var(--c-surface-2);
  border-radius: 4px;
  word-break: break-all;
}
a.guide__path { text-decoration: none; }
a.guide__path:hover { text-decoration: underline; }

.guide__steps { max-width: 42rem; padding-left: var(--space-5); }
.guide__steps li { margin-bottom: var(--space-2); }

.guide__foot {
  padding-top: var(--space-5);
  margin-top: var(--space-7);
  border-top: 1px solid var(--c-line);
  color: var(--c-ink-dim);
}

/* Printing this page is a supported way to use it: drop the chrome the paper
   cannot follow and let every section break where it likes. */
@media print {
  .guide__toc { display: none; }
  .guide__section { break-inside: auto; }
  .guide__topic { break-inside: avoid; }
}

/* ── Landing page highlights ───────────────────────────────
   One card per league, holding the last night's news. Cards rather than a
   table: there are two to five facts per league and half of them are absent on
   any given night, and a table of mostly empty cells reads as a league that
   had a bad evening rather than one whose coach has not typed the scores yet.

   auto-fit rather than a fixed column count, because the programme has run
   four leagues and has run nine, and neither number should be written here. */

.highlight-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(17rem, 1fr));
  gap: var(--space-4);
  /* Cards size to what is in them. Stretched to the tallest in the row, a
     league with one highlight gets a card two thirds empty, which reads as
     something missing rather than as a quiet night. */
  align-items: start;
}

.highlight {
  padding: var(--space-5);
  background: var(--c-surface);
  border: 1px solid var(--c-line);
  /* The programme's red down the edge, the same signature the Bowler of the
     Week panel carried before this page was rebuilt around it. */
  border-left: 4px solid var(--c-accent);
  border-radius: var(--radius);
}

.highlight__league {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
  font-size: var(--step-1);
}

.highlight__list { margin: 0; }
.highlight__row {
  padding-top: var(--space-3);
  margin-top: var(--space-3);
  border-top: 1px solid var(--c-line);
}
.highlight__row:first-child { padding-top: 0; margin-top: 0; border-top: 0; }

.highlight__row dt {
  font-size: var(--step--1);
  color: var(--c-ink-dim);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.highlight__row dd {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-3);
  margin: var(--space-1) 0 0;
}

/* The number is the news, so it is the thing sized like news. */
.highlight__score {
  font-family: var(--font-display);
  font-size: var(--step-2);
  font-weight: 700;
  line-height: 1;
}
.highlight__who { min-width: 0; }

.highlight__leagues {
  margin: var(--space-3) 0 0;
  padding-left: var(--space-5);
  text-align: left;
}
