/**
 * Leaderboards — tool styles
 *
 * Cards, tables, buttons and empty states all come from frikwork and app.css
 * untouched. What is here is the standings grid, the two colours this page
 * invents, and the CSS-only view switch.
 *
 * THE VIEW SWITCH, AND WHY THE INPUTS ARE CLIPPED RATHER THAN HIDDEN
 * -----------------------------------------------------------------
 * All three views are rendered server-side and the switch is three radios plus
 * `:checked ~` sibling selectors, so this page ships no JavaScript.
 *
 * `display: none` (or `visibility: hidden`) on a radio removes it from the tab
 * order AND from the arrow-key walk a radio group gets for free — the control
 * would still LOOK operable, because the <label> stays clickable, and would be
 * unreachable from a keyboard. So the inputs are clipped off-screen instead:
 * still rendered, still focusable, still a native radio group, and the focus
 * lands visibly on the label because the rules below key a ring off
 * `:focus-visible` on the input and paint its label.
 *
 * The markup is a <fieldset> for two reasons that happen to agree: the <legend>
 * gives the group a name a screen reader can announce, and a fieldset is the
 * only wrapper that leaves the inputs, the visible switch and EVERY pane as
 * siblings — which is what `~` needs. A control nested inside .lb-switch could
 * not reach the panes.
 *
 * Each period is named in three places — an input id here, a modifier class on
 * its tab and one on its pane — and all three are written out per period rather
 * than generated, because `:checked ~` needs a literal selector per pair. That
 * is the whole reason index.php calls its pane function twice by name instead
 * of looping: a tab with no rule behind it would look live and do nothing.
 *
 * WHY THE ACTIVE TAB IS NOT MARKED BY COLOUR ALONE
 * ------------------------------------------------
 * frikwork's own .fw-tabs--segmented marks its active tab with a brand text
 * colour and a 10% brand tint. Measured, that tint is #f4faec on a #ffffff
 * track in the light palette — about 1.1:1, which is to say invisible. So in
 * light the active state would rest on text colour alone, and that is exactly
 * what WCAG 1.4.1 is about. No tint over white can reach 3:1 without becoming a
 * solid green block, so the cue here is a SHAPE: a 2px inset underline, plus a
 * weight change. Both survive greyscale and colour blindness; the underline
 * measures 5.05:1 (light) and 9.72:1 (dark) against the track, clearing the 3:1
 * graphic floor. The look is still frikwork's segmented control.
 *
 * WHY THERE IS NO THEME-CONDITIONAL BLOCK IN THIS FILE
 * ----------------------------------------------------
 * tools/wordle/style.css needs one and its comments are worth reading first.
 * frikwork ships dark on bare :root and declares light TWICE — once from
 * [data-fw-theme="light"], again from
 * `@media (prefers-color-scheme: light) { :root:not([data-fw-theme]) }` — so a
 * variable defined once at :root does not necessarily follow the theme. That
 * bit Wordle, whose ink was --fw-on-primary: defined once, near-black in every
 * state.
 *
 * Nothing here has that problem. Both colours below are color-mix() against
 * --fw-text-muted / --fw-brand-text and --fw-text, and frikwork re-declares all
 * three in BOTH light blocks (diffed: the two blocks are identical for every
 * token this page touches). The mix is Wordle's trick — --fw-text is by
 * definition the pole furthest from the surface, so one rule deepens a
 * dark-theme colour toward white and a light-theme colour toward black and
 * raises contrast either way. An empty two-selector block here would be cargo
 * cult; what matters is that all four states were MEASURED.
 *
 * Measured IN THE BROWSER — Chrome reading its own resolution of every
 * color-mix() and compositing each backdrop — in all four states (explicit
 * light, explicit dark, attribute-absent + OS light, attribute-absent + OS
 * dark); figures quoted in the task report. Nineteen colour pairs, and every
 * text one clears 4.5:1 in all four: worst is the active tab label at 4.74:1
 * (light), then .lb-sub__title at 4.84:1 (light). Worst graphic in use is the
 * focus ring at 5.05:1 (light).
 *
 * The Monthly tab added no NEW colour pair — it is the same two declarations as
 * Daily and Weekly — and was re-measured in all four states anyway rather than
 * assumed: 4.71:1 active (light), 8.07:1 active (dark), 6.42:1 inactive
 * (light), 8.21:1 inactive (dark). The 4.71 against the 4.74 above is the
 * 8-bit quantisation of two harnesses reading the same pair, not a change.
 *
 * Measuring in the browser rather than from the token table was not belt-and-
 * braces: .fw-card is --fw-glass-bg, an ALPHA, so a card is #181818 in dark and
 * #fbfaf9 in light rather than the #1f1f1f / #ffffff --fw-bg-card would predict.
 * Every ratio here is against the composited value.
 *
 * Two raw tokens were measured and REJECTED for text on a card: --fw-text-muted
 * at 3.75:1 and --fw-brand-text at 4.11:1, both in the light palette. Hence
 * --lb-muted and --lb-accent, and hence .lb-note / .lb-meta rather than
 * .fw-help.
 *
 * A third raw token was rejected for a different job: frikwork's global
 * `:focus-visible { outline: 2px solid var(--fw-brand) }` is --fw-brand, which
 * is NOT re-declared in either light block and measures 1.80:1 against the light
 * page background. A focus ring nobody can see is the same defect as no focus
 * ring, so the switch overrides it with --lb-accent (4.59:1 on the page, 5.05:1
 * on the track).
 *
 * WHERE THAT LEAVES TEXT THIS FILE DOES NOT AUTHOR
 * ------------------------------------------------
 * Four things on this page are painted by shared code — frikwork's
 * `.fw-table th`, app.css's `.app-panel__lead`, `.app-empty p` and
 * `.app-empty i` — and every one is raw --fw-text-muted at 3.75:1 on a light
 * card. That is a shared-CSS problem affecting four other tools, and fixing it
 * in app.css would restyle tools this task has no business touching. So each is
 * out-specified HERE, scoped to this page, with a note saying which shared rule
 * it beats. The requirement is every text the PAGE paints, not every text this
 * file styles.
 *
 * --fw-bg-2 is used for the switch track, which is legitimate only because the
 * track also has a border: bg-2 resolves to the same #ffffff as --fw-bg-card in
 * the light palette, so it cannot separate two adjacent surfaces on its own.
 * The track's own border is frikwork's --fw-border and measures 1.28:1 against
 * the light page — kept deliberately, byte-for-byte with .fw-tabs--segmented, so
 * this control matches the games' mode switches. It is a decorative container
 * edge: what identifies the component and its state is the two text labels and
 * the underline above, all of which clear their thresholds.
 */

:root {
  /* Muted body text and accent text, both deepened past the raw tokens — see
     the rejected measurements in the header. */
  --lb-muted: color-mix(in srgb, var(--fw-text-muted) 66%, var(--fw-text));
  --lb-accent: color-mix(in srgb, var(--fw-brand-text) 86%, var(--fw-text));
}

/* --- Notes -----------------------------------------------------------------
   Replacements for .fw-help, which measures 3.75:1 on a card in the light
   palette. Same role, same size, a colour that clears AA. */

.lb-note,
.lb-meta {
  color: var(--lb-muted);
  font-size: 0.85rem;
  line-height: 1.6;
}
.lb-note strong,
.lb-meta strong { color: var(--fw-text); }

/* `i, svg` on every icon rule in this file, and it is not belt-and-braces: the
   Font Awesome kit is the JS build, which REPLACES each <i> with an <svg> in
   place and copies its classes across. A rule that only names `i` therefore
   matches in the markup and not in the rendered page — measured, by looking for
   `.app-empty i` in a loaded document and not finding it. The svg inherits
   `fill: currentColor`, so setting `color` is still the right lever. */
.lb-meta i,
.lb-meta svg { color: var(--lb-accent); }

.lb-note { margin: 0 0 var(--fw-space-md); }
.lb-note--foot { margin: var(--fw-space-md) 0 0; }

/* The one note per pane sits on the PAGE background rather than on a card, so
   it is measured against --fw-bg as well (5.84:1 light, 8.97:1 dark). */
.lb-note--pane { margin: 0 0 var(--fw-space-lg); max-width: 70ch; }

.lb-meta {
  margin: var(--fw-space-md) 0 0;
  padding-top: var(--fw-space-md);
  border-top: 1px solid var(--fw-border);
  font-size: 0.8rem;
}

/* --- The league ------------------------------------------------------------
   Visual weight above the per-game boards: a tinted header and a bigger points
   column. The tint is decorative — the panel is already identified by its
   heading — so it is not held to a contrast floor.

   --fw-brand rather than --fw-brand-text on purpose, and worth knowing why:
   --fw-brand is the ONE brand token frikwork does not re-declare in either
   light block, so it is #94c83e in all four states. That makes it wrong for
   text (see the focus-ring note in the header) and exactly right for a low-
   percentage tint, because the surface it is mixed into follows the theme while
   the hue stays put — which is what frikwork's own .fw-tab.is-active does with
   the same token at the same 10%. Sixteen other tools use it the same way. */

.lb-league { margin-bottom: var(--fw-space-lg); }
.lb-league .app-panel__head {
  background: color-mix(in srgb, var(--fw-brand) 8%, transparent);
}
.lb-league .lb-table__points {
  font-size: 1.05rem;
  font-weight: 700;
  color: var(--fw-text);
}

/* --- The view switch ------------------------------------------------------- */

.lb-views {
  /* A fieldset ships a border, padding and `min-inline-size: min-content` — the
     last of which would stop the grids inside it from ever shrinking. */
  margin: 0;
  padding: 0;
  border: 0;
  min-inline-size: 0;
}

.lb-views__legend {
  padding: 0;
  margin-bottom: var(--fw-space-sm);
  color: var(--lb-muted);
  font-size: 0.72rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

/* Clipped, NOT display:none — see the header. Keeps the radio group focusable
   and arrow-key navigable. */
.lb-views__input {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* frikwork's .fw-tabs--segmented, reproduced: an enclosed inline-flex track. */
.lb-switch {
  display: inline-flex;
  gap: var(--fw-space-xs);
  max-width: 100%;
  margin-bottom: var(--fw-space-lg);
  padding: var(--fw-space-xs);
  background: var(--fw-bg-2);
  border: 1px solid var(--fw-border);
  border-radius: var(--fw-radius-md);
}

.lb-switch__tab {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--fw-space-sm);
  padding: var(--fw-space-sm) var(--fw-space-md);
  border-radius: var(--fw-radius-sm);
  color: var(--lb-muted);
  font-size: 0.875rem;
  font-weight: 500;
  white-space: nowrap;
  cursor: pointer;
  transition: background var(--fw-dur) ease, color var(--fw-dur) ease;
}
.lb-switch__tab:hover {
  background: var(--fw-bg-hover);
  color: var(--fw-text);
}

/* Active: accent text, a brand tint, a weight change AND an inset underline.
   The last two are the non-colour cues — see the header. */
#lb-view-daily:checked ~ .lb-switch .lb-switch__tab--daily,
#lb-view-weekly:checked ~ .lb-switch .lb-switch__tab--weekly,
#lb-view-monthly:checked ~ .lb-switch .lb-switch__tab--monthly {
  color: var(--lb-accent);
  background: color-mix(in srgb, var(--fw-brand) 10%, transparent);
  box-shadow: inset 0 -2px 0 0 var(--lb-accent);
  font-weight: 700;
}

/* frikwork's global :focus-visible ring is --fw-brand, which measures 1.80:1 on
   the light page background. Overridden with the deepened accent, and keyed off
   the INPUT so the ring lands on the label the user is actually driving. */
#lb-view-daily:focus-visible ~ .lb-switch .lb-switch__tab--daily,
#lb-view-weekly:focus-visible ~ .lb-switch .lb-switch__tab--weekly,
#lb-view-monthly:focus-visible ~ .lb-switch .lb-switch__tab--monthly {
  outline: 2px solid var(--lb-accent);
  outline-offset: 2px;
}

/* The whole mechanism. All three panes are rendered into the document; one is
   shown, which is why switching needs no request and no script. */
.lb-pane { display: none; }
#lb-view-daily:checked ~ .lb-pane--daily,
#lb-view-weekly:checked ~ .lb-pane--weekly,
#lb-view-monthly:checked ~ .lb-pane--monthly { display: block; }

/* --- The board grid -------------------------------------------------------- */

.lb-grid {
  display: grid;
  /* min(100%, …) rather than a bare rem floor: on a narrow phone a 20rem track
     would overflow the viewport rather than collapse. 20rem rather than 22 so
     the games sit comfortably on one row at desktop width. (Until Sudoku's
     daily became a single board, it minted four and took the full-width row
     above the others — hence the 20/22 choice, which is kept because the
     resulting layout is still the one that fits.) */
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 20rem), 1fr));
  gap: var(--fw-space-lg);
  align-items: start;
}

/* app.css stacks consecutive panels with a top margin, which is right in flow
   and wrong in a grid — every card but the first would sit low in its row. */
.lb-grid .app-panel + .app-panel { margin-top: 0; }

/* DEAD as of the one-daily-Sudoku change, and kept deliberately.

   Sudoku used to post four boards a day where every other game posted one, so
   in the Daily view it took a row of its own rather than making every card in
   its row four times as tall. Its daily is now a single board, so nothing sets
   this class and nothing reaches .lb-subgrid below — lb_page_daily_boards()
   still returns a map because "how many boards does this game have today" is
   still a per-game question, it is just that every answer is now one.

   Pinned as unreachable by an assertion in tests/dashboard_page_test.php rather
   than left as an untested landmine. Delete both rules if that stays true. */
.lb-board--wide { grid-column: 1 / -1; }

.lb-subgrid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
  gap: var(--fw-space-lg);
  align-items: start;
}

.lb-sub__title {
  margin: 0 0 var(--fw-space-sm);
  color: var(--lb-accent);
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}

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

/* The value columns hug the right edge at a fixed width so the numbers line up
   with their heading and the initials column absorbs the slack. */
.lb-table__rank { width: 3rem; }
.lb-table__num { width: 7rem; font-variant-numeric: tabular-nums; }
.lb-table__who { font-weight: 700; letter-spacing: 0.06em; }

/* A full-width league table at 1600px would strand its numbers a screen away
   from the names they belong to. */
.lb-league .fw-table-wrap { max-width: 44rem; }

/* Every COLOUR rule on a cell is qualified with .fw-table on purpose rather
   than trusting stylesheet order. `.fw-table th` and `.fw-table td` are (0,1,1),
   so a bare `.lb-table th` at (0,1,1) would win only by load order, and a bare
   `.lb-table__rank` or `.lb-table__who` at (0,1,0) would LOSE outright — which
   is how a rank column ends up rendering at raw --fw-text-muted with a
   stylesheet three lines above plainly saying otherwise. This was not
   theoretical: `.lb-table__who { color: var(--fw-text) }` was measured in a
   loaded page still resolving to --fw-text-2, i.e. the declaration was dead.
   Measured, not guessed; see the header.

   text-align has the same problem and it is the more visible one: `.fw-table th`
   sets `text-align: left`, so an unqualified `.lb-table__num` right-aligns every
   VALUE and leaves every value HEADING on the left — a numeric column whose
   label sits nowhere near it. Seen in a browser, fixed here. */
.fw-table.lb-table th { color: var(--lb-muted); }
.fw-table.lb-table th.lb-table__num { text-align: right; }
.fw-table.lb-table td.lb-table__num { text-align: right; }
.fw-table.lb-table .lb-table__rank { color: var(--lb-muted); }
.fw-table.lb-table .lb-table__who { color: var(--fw-text); }

/* On a phone the rank column plus two fixed value columns no longer fit inside
   a card, and .fw-table-wrap answers that with a horizontal scroll the reader
   has no reason to go looking for — the league's "Appearances" column ends up
   off the right edge of its own card. Give the fixed widths up instead; the
   columns still line up, they just stop reserving desktop room. */
@media (max-width: 32rem) {
  .lb-table__rank { width: 2.25rem; }
  .lb-table__num { width: auto; }
  .lb-league .fw-table-wrap { max-width: none; }
  /* Measured at 390px: giving up the fixed widths still left the league table
     331px wide inside a 308px card, because "Appearances" is one unbreakable
     word needing 123px of it. .fw-table's 1rem side padding spends 128px on
     gutters across four columns — the heading needs that room more than the
     gutters do, and halving it brings the table to 267px. */
  .fw-table.lb-table th,
  .fw-table.lb-table td {
    padding-left: var(--fw-space-sm);
    padding-right: var(--fw-space-sm);
  }
}

/* --- Text this page renders but does not author ---------------------------- */

/* app.css's .app-panel__lead is raw --fw-text-muted (3.75:1 on a light card).
   (0,2,0) beats its (0,1,0). */
.lb-league .app-panel__lead,
.lb-board .app-panel__lead { color: var(--lb-muted); }

/* ccm_empty()'s copy and icon, from app.css: `.app-empty p` and `.app-empty i`,
   both raw --fw-text-muted. This is the default rendering of every board in a
   fresh install and of every game's card each morning before anyone has played,
   so it is the first thing most visitors read. (0,2,1) beats app.css's (0,1,1).
   frikwork's .fw-empty (0,1,0) is beaten too. `svg` for the reason given above
   the .lb-meta icon rule. */
.lb-league .app-empty p,
.lb-board .app-empty p,
.lb-league .app-empty i,
.lb-board .app-empty i,
.lb-league .app-empty svg,
.lb-board .app-empty svg { color: var(--lb-muted); }

/* --- Motion ---------------------------------------------------------------- */

/* The one transition this file adds, switched off to match what frikwork
   already does for its own. */
@media (prefers-reduced-motion: reduce) {
  .lb-switch__tab { transition: none; }
}
