  /* ══════════════════════════════════════════════════════════════════════════
     BROADSHEET — the practice + feedback surfaces, as printed paper.
     ══════════════════════════════════════════════════════════════════════════
     Why this is an ADDITIVE OVERRIDE LAYER rather than a rewrite: the app carries
     ~250 colour decisions outside the token system (167 hardcoded hex, 60 rgba,
     22 inline styles), all tuned for dark. Rewriting them would touch every
     screen at once. Instead #screen-practice and #screen-feedback carry class
     "bs", every rule below is scoped ".bs …" (specificity 0,2,0, which beats the
     base 0,1,0 rules regardless of source order), and the rest of the app stays
     dark and untouched. Deleting this block plus the two classes reverts cleanly.

     Why light at all: every figure in this app is black line art on white, and
     the dark theme has to punch a white rectangle behind each one — so on dark,
     every diagram reads as a glaring slab. With the AP Biology bank landing
     (776 images) that becomes the dominant visual. On paper, figures integrate.

     A light document inside dark chrome is a deliberate mode shift, not a
     half-finished migration: the shell stays dark, the worksheet is paper.
     ────────────────────────────────────────────────────────────────────────── */
  /* The palette and type roles used to be declared HERE, and were promoted to
     :root when the conversion went app-wide (see the note on that block). This
     copy was left behind, and because .bs beats :root it kept winning on every
     converted screen — silently reverting one value that had since been
     corrected at the root: --bs-ink-3 was still the original #7c8475, which
     sits at 3.16:1 on --bs-stock. That is below AA for body text, and it was
     the colour of nearly every muted label in the publication — folios, decks,
     index subtitles, agate meta. The root's #616858 measures 4.71:1.
     Only the three declarations that are genuinely about .bs remain. */
  .bs {
    /* Transparent, not --bs-stock: the paper tooth is now the #paper-tooth
       canvas behind every screen, and an opaque ground here would hide it. */
    background: transparent;
    color: var(--bs-ink);
    font-family: var(--bs-ui);
  }
  .bs .num, .bs [class*="-num"] { font-variant-numeric: tabular-nums; }


  /* ─── MASTHEAD + scantron progress rail ─── */
/* ════════════════════════════════════════════════════════════════════════
   VANGUARD BROADSHEET — masthead (.topbar) + scantron progress rail (.q-rail)
   Scope: #screen-practice.bs and #screen-feedback.bs only. Every selector is
   led by ".bs " so it lands at 0,2,0+ and beats the dark 0,1,0 rules on
   source order alone — no !important anywhere below.

   Two `display:contents` promotions do the structural work instead of new
   wrapper markup: `.stats` (both screens) and `.q-rail-head` (practice only)
   dissolve so their real children become direct flex items of `.topbar` /
   `.q-rail`, letting one flex line do a full masthead row and letting
   order:1/2/3 put the scantron label, bubbles and count in mockup order
   even though the count is nested one level deeper in the DOM than the
   bubbles track.
   ════════════════════════════════════════════════════════════════════════ */


/* ── fallback: old pip markup (streak-pips reused by #calib-pips too) ──
   currentColor so it reads correctly whether it lands on the ink masthead
   or on a lighter surface owned by another pass. */
.bs .streak-pips { display: flex; gap: 4px; align-items: center; }
.bs .pip {
  width: 6px; height: 6px; border-radius: 50%;
  background: transparent; border: 1px solid currentColor; opacity: .4;
  transition: background .15s ease, opacity .15s ease;
}
.bs .pip.lit { background: currentColor; opacity: 1; }

/* ── masthead shell ── */
/* A broadsheet masthead is the paper's name PRINTED IN INK on the paper —
   not white knocked out of a black bar. The reversed bar was a dark-UI chrome
   habit that survived the conversion, and it read as a foreign slab sitting on
   top of the newsprint. Ink on stock, closed by the classic thick-then-thin
   double rule, which is what actually says "newspaper". */
.bs .topbar {
  --mh-line: color-mix(in srgb, var(--bs-ink) 22%, transparent);
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: flex-start;
  gap: 0 20px;
  background: transparent;
  color: var(--bs-ink);
  padding: 15px 22px 0;
  /* The thin half of the double rule; the thick half is the ::after below,
     because one element cannot carry two borders on the same edge. */
  border-bottom: 1px solid var(--bs-ink);
  position: relative;
}
.bs .topbar::after {
  content: '';
  position: absolute;
  left: 0; right: 0; bottom: -4px;
  height: 3px;
  background: var(--bs-ink);
}
.bs .logo {
  font-family: var(--bs-ed);
  font-variation-settings: 'SOFT' 0, 'WONK' 1;
  font-weight: 900;
  /* The wordmark is the largest thing on the page after the question — a
     masthead earns its scale. */
  font-size: 30px;
  line-height: .9;
  letter-spacing: -.035em;
  color: var(--bs-ink);
  text-transform: none;
  flex-shrink: 0;
}
/* Flattens .stats so the 4 meters (practice) / 2 meters (feedback) become
   real siblings of .logo — the only way a single flex line can put the
   wordmark and the right-aligned meters on one true masthead row. */
.bs .stats { display: contents; }
/* First meter absorbs all leftover row-1 space, pushing the meter cluster
   to the right; same rule works for feedback's 2-stat row unchanged. */
.bs .stats > .stat:nth-of-type(1) { margin-left: auto; }

/* Required new spacer (see notes) forces tier 2 onto its own full-width
   line — a flex-basis:100% item is the only reliable line-break in a
   flattened flex row without reparenting existing markup. */
.bs .bs-mast-break {
  flex-basis: 100%;
  height: 0;
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--mh-line);
}

/* ── tier-1 meters ── */
.bs .stat {
  position: relative;
  display: flex;
  align-items: baseline;
  gap: 6px;
  padding-top: 13px;
  color: var(--bs-ink);
}
.bs .stat::before {
  position: absolute;
  top: 0; right: 0;
  /* The label recedes; the value carries the weight. Recede with a chosen
     tone, NOT with alpha: opacity composites toward the ground, so .55 on an
     already-tertiary ink measured 2.13:1 on newsprint (the ink itself is
     4.71:1). Same defect as the :disabled opacity trap. */
  font-family: var(--bs-mono);
  font-size: 8px;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  white-space: nowrap;
}
.bs .stats > .stat:nth-of-type(1)::before { content: 'Streak'; }
.bs .stats > .stat:nth-of-type(2)::before { content: 'Rating'; }
.bs .stats > .stat:nth-of-type(3)::before { content: 'Score'; }
.bs .stats > .stat:nth-of-type(4)::before { content: 'Elapsed'; }
/* icon glyphs (§ ★ ⏱) and the bare "/" separator — every span in a .stat
   that isn't the value itself */
.bs .stat > span:not(.val) { font-size: 12px; opacity: .7; line-height: 1; }
.bs .stat .val {
  font-family: var(--bs-ui);
  font-variation-settings: 'wdth' 112;
  font-weight: 700;
  font-size: 17px;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--bs-ink-3);
}
.bs .stat.num-pop .val { display: inline-block; animation: numPop .44s var(--ease-snap) both; }

/* ── tier-2: departments / edition selector / quit ──
   All three toggle groups (family/mode/diff) share this segmented look;
   family (#tb-family-toggle) additionally reads as the "Edition" selector
   via its own ::before label since it's the only group with no adjacent
   text caption in the existing markup. */
.bs .topbar-filters {
  align-self: center;
  font-family: var(--bs-mono);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  row-gap: 8px;
}
.bs .topbar-toggle {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--mh-line);
}
.bs .topbar-toggle-btn {
  background: transparent;
  border: none;
  border-right: 1px solid var(--mh-line);
  border-radius: 0;
  font-family: var(--bs-ui);
  font-variation-settings: 'wdth' 82;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bs-ink);
  opacity: .72;
  padding: 5px 10px;
  cursor: pointer;
  transition: opacity .12s ease, background .12s ease, color .12s ease;
}
.bs .topbar-toggle-btn:last-child { border-right: none; }
.bs .topbar-toggle-btn:hover { opacity: .9; }
.bs .topbar-toggle-btn.active,
.bs .topbar-toggle-btn.active-teal,
.bs .topbar-toggle-btn.active-purple {
  /* Inverted from the old dark masthead: the selection is now an INK block
     with a paper label, which reads as a stamp rather than a highlight. */
  background: var(--bs-ink);
  color: var(--bs-stock);
  opacity: 1;
}
.bs .topbar-toggle-btn:focus-visible {
  outline: 2px solid var(--bs-blue);
  outline-offset: 2px;
}
.bs #tb-family-toggle::before {
  content: 'Edition';
  order: -1;
  display: flex;
  align-items: center;
  padding: 0 8px;
  border-right: 1px solid var(--mh-line);
  font-family: var(--bs-mono);
  font-size: 8px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  /* No opacity multiplier — see the note on .bs .stat::before. */
  color: var(--bs-ink-3);
}

/* Icon-only dashboard/history buttons become labelled department links by
   collapsing the emoji glyph and surfacing the existing title attribute —
   no markup change needed, the text was already sitting there as a tooltip. */
.bs .btn-ghost {
  align-self: center;
  display: inline-flex;
  align-items: center;
  min-height: 36px;
  background: transparent;
  border: none;
  border-radius: 0;
  font-family: var(--bs-ui);
  font-variation-settings: 'wdth' 80;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink);
  opacity: .72;
  padding: 5px 12px;
  cursor: pointer;
  transition: opacity .12s ease, color .12s ease;
}
.bs .btn-ghost:hover { opacity: 1; background: transparent; color: var(--bs-blue); }
.bs .btn-ghost:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }
.bs #dashboard-btn { margin-left: auto; font-size: 0; border-right: 1px solid var(--mh-line); }
.bs #history-btn { font-size: 0; }
.bs #dashboard-btn::before,
.bs #history-btn::before {
  content: attr(title);
  font-size: 10.5px;
}
.bs #sprint-timer-wrap { align-self: center; }
.bs #end-btn { align-self: center; }


/* ── scantron progress rail — sits on paper, not on the ink chrome ── */
.bs .q-rail {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: 16px;
  padding: 12px 20px 13px;
  background: var(--bs-stock);
  border-bottom: 3px solid var(--bs-ink);
}
.bs .q-rail-head { display: contents; }
.bs .q-rail-focus {
  order: 1;
  margin-right: auto;
  min-width: 0;
  flex-shrink: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-family: var(--bs-ui);
  font-variation-settings: 'wdth' 78;
  font-weight: 700;
  font-size: 11.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-2);
}
.bs .q-rail-track {
  order: 2;
  flex: 0 1 auto;
  min-width: 0;
  margin-right: auto;   /* keep the bubbles beside the focus label, not mid-page */
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 2px 1px;
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: none;
}
.bs .q-rail-track::-webkit-scrollbar { display: none; }
.bs .q-rail-count {
  order: 3;
  flex-shrink: 0;
  font-family: var(--bs-ui);
  font-variation-settings: 'wdth' 100;
  font-weight: 700;
  font-size: 12px;
  letter-spacing: .04em;
  font-variant-numeric: tabular-nums;
  color: var(--bs-ink-2);
}

.bs .q-rail-seg {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  min-width: 15px;
  border-radius: 50%;
  position: relative;
  background: transparent;
  border: 1.5px solid var(--bs-rule);
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.bs .q-rail-seg.done.correct {
  background: var(--bs-graphite);
  border-color: var(--bs-graphite);
}
.bs .q-rail-seg.done.missed { border-color: var(--bs-miss); }
.bs .q-rail-seg.done.missed::after {
  content: '';
  position: absolute;
  left: 1px; right: 1px; top: 6px;
  height: 1.5px;
  background: var(--bs-miss);
  transform: rotate(-42deg);
}
.bs .q-rail-seg.current {
  border-color: var(--bs-blue);
  border-width: 2.5px;
  box-shadow: 0 0 0 2.5px color-mix(in srgb, var(--bs-blue) 16%, transparent);
  transform: none; /* cancels the un-scoped .q-rail-seg.current{transform:scaleY(2.2)} bar animation */
}
.bs .q-rail-seg.seg-start { margin-left: 9px; }
.bs .q-rail-seg.seg-start:first-child { margin-left: 0; }

/* ── responsive ── */
@media (max-width: 700px) {
  .bs .logo { font-size: 18px; }
}
@media (max-width: 560px) {
  .bs .topbar { padding: 10px 14px; }
  .bs .logo { font-size: 16px; }
  .bs .stat .val { font-size: 15px; }
  .bs .btn-ghost { min-height: 44px; }
  .bs .q-rail { padding: 8px 14px 9px; gap: 10px; }
  .bs .q-rail-focus { font-size: 10.5px; }
}

@media (prefers-reduced-motion: reduce) {
  .bs .stat.num-pop .val,
  .bs .pip,
  .bs .topbar-toggle-btn,
  .bs .btn-ghost,
  .bs .q-rail-seg {
    transition-duration: .001s;
    animation-duration: .001s;
  }
}

/* Inverted along with the masthead. These carried the dark-masthead
   relationship — pale label on ink, ink label on the pale active pill — and
   kept winning on specificity after the shell went to paper, which left the
   inactive tracks pale-on-paper and the active one ink-on-ink. Both invisible. */
  .bs .tb-tint-math,
.bs .tb-tint-reading,
.bs .tb-tint-comp { color: var(--bs-ink); }
.bs .topbar-toggle-btn.tb-tint-math.active,
.bs .topbar-toggle-btn.tb-tint-reading.active,
.bs .topbar-toggle-btn.tb-tint-comp.active { color: var(--bs-stock); }

