/* ═══════════════════════════════════════════════════════════════════════
   set-work.css — the Set work sheet (MRB-335), Design's chrome, our DOM.

   ⚠️ WHY THIS FILE EXISTS AT ALL, AND WHY THE SHEET LEFT THE COMPILED
   RUNTIME. The v1 sheet was Design's compiled node 581, mounted inside
   `#mrb-teacher`, and it scroll-jumped on every selection. The cause is
   `shared/student-runtime.js`:

       496   var keepScroll = window.scrollY;
       497   host.textContent = "";
       498   host.appendChild(frag);
       501   if (window.scrollY !== keepScroll) { window.scrollTo(0, keepScroll); }

   `draw()` empties the WHOLE mount host and rebuilds it from the template on
   every `setState`, and the only scroll it preserves is the DOCUMENT's.
   Design's sheet is `max-height:86vh; overflow:auto` — an INNER scroller — so
   the node holding the teacher's scroll position was destroyed and replaced
   by a fresh one at scrollTop 0 every time they tapped a topic.

   So the fix is structural rather than cosmetic: this sheet is a sibling of
   the mount host, appended to `<body>`, and `draw()` can never reach it.
   Nothing here is rebuilt on a selection — see set-work.js.

   ⚠️ EVERY COLOUR, RADIUS AND FONT IS A `--st-*` TOKEN read out of
   `shared/teacher-ds.css`, and every one of them is Design's own value from
   the Set work sheet in `Teacher Dashboard.dc.html` (nodes 581-627). Nothing
   here is a new design. The two shapes she did not draw — a sticky header and
   a tier/subject/paper chip rail — are drawn in her vocabulary: the header is
   her footer's button pair over her panel's bottom rule, and the chip rails
   are her `swCounts` row.

   ⚠️ NO SIDEWAYS SCROLL AT 390px. Every flex child that can hold long text
   carries `min-width:0`, every stem carries `overflow-wrap:anywhere`, and
   every date/time input pair wraps rather than pushing. RISKS A8/E11.
   ═══════════════════════════════════════════════════════════════════════ */

/* ── keyboard focus — one ring for the whole sheet ─────────────────────────
   ⊕ Experience run, 24 Sep 2026 (stream G). `focus_audit.py` found every
   control in this sheet already showing a visible change on Tab — nothing
   here does `all:unset` — so this is a floor, not a fix for a measured
   defect, and it is its OWN rule rather than an assumed inheritance from
   `shared/teacher-ds.css`'s `[data-mode="ks3"] :focus-visible`: that rule
   lives on the element carrying `data-mode="ks3"` inside `#mrb-teacher`,
   and `.sw-overlay` is a SIBLING of that mount, appended straight to
   `<body>` (see the file banner above — that is the whole reason this sheet
   left the compiled runtime), so the ks3 ring never reaches in here at all.
   `:focus-visible` only, so a mouse click never rings. The one existing
   focus rule below (the download checkbox's `:has()` ring) is unaffected —
   equal-or-narrower selectors, later in the cascade, both still apply. */
.sw-overlay :focus-visible {
  outline: 2px solid var(--st-accent);
  outline-offset: 2px;
  border-radius: 6px;
}

/* ── the overlay ─────────────────────────────────────────────────────────
   Design's node 581: `position:fixed;inset:0;z-index:60;background:
   rgba(26,23,20,.45)`. The padding drops to 0 under 480px so the sheet can
   use the whole width of a phone rather than 350px of it. */
.sw-overlay {
  position: fixed;
  inset: 0;
  z-index: 60;
  background: rgba(26, 23, 20, .45);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 40px 20px;
}
.sw-overlay[hidden] { display: none; }

/* ── the sheet — AND THE SCROLLER ────────────────────────────────────────
   Design's exact frame. `overflow:auto` makes this the scroll container the
   sticky header sticks inside; NOTHING between it and the header may carry a
   transform, a filter or `overflow:hidden`, because each of those makes a
   `position:sticky` descendant behave differently on iOS ≤16 (RISKS A2). */
.sw-sheet {
  width: 720px;
  max-width: 100%;
  max-height: 86vh;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--st-paper);
  border: 1px solid var(--st-edge);
  border-radius: 14px;
  box-shadow: var(--st-shadow-frame);
  display: flex;
  flex-direction: column;
  min-width: 0;
}

/* ── the sticky header: Back · step · primary ────────────────────────────
   RISKS A3 — v1's primary action lived in a footer that scrolled away with
   the content, so on a long topic list "Next" was off-screen from the moment
   the sheet opened. It is up here now and it does not move.

   `top:0` inside `.sw-sheet`'s own scroller. Opaque background is not
   decoration: a transparent sticky header shows the list sliding under it. */
.sw-head {
  position: sticky;
  top: 0;
  z-index: 2;
  flex: none;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px;
  background: var(--st-paper);
  border-bottom: 1px solid var(--st-rule-soft);
  border-radius: 14px 14px 0 0;
}
.sw-step {
  flex: 1 1 auto;
  min-width: 0;
  text-align: center;
  font: 500 13px/1.2 var(--st-mono);
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--st-caption);
  /* ⊕ MRB-352 run 2 (batch 3 landing): the step word is ONE word and must
     never break inside itself — `overflow-wrap:anywhere` put "DETAI / L" on
     two lines at 360px, where Download joins the header. It stays whole;
     the ≤520px block below trims the header so it fits. */
  overflow-wrap: normal;
  word-break: keep-all;
  white-space: nowrap;
}

/* ── iOS FALLBACK, WIRED AND OFF ─────────────────────────────────────────
   RISKS A2's ruling: if `position:sticky` inside the scroller fails on real
   iOS Safari, add `sw-fixed-header` to `.sw-sheet` and the header becomes
   `position:fixed` with the body padded down to clear it. It is coded here
   rather than left as a note so that turning it on is one class and no
   rebuild. It is OFF: nothing sets this class today. */
/* ⊕ MRB-335 — SCOPED TO THE NARROW BREAKPOINT, where the sheet is the whole
   viewport. Unscoped, `position:fixed` on the header pinned it to the
   VIEWPORT rather than to the 720px sheet, so at 1280px — where the sheet is
   a centred card with 40px of scrim above it — turning the fallback on would
   have stretched the header edge to edge across the page and left the card's
   own top empty. The bug it exists for is an iOS Safari one, and iOS Safari
   is inside this media query. See the rules at the foot of this file. */

/* ── buttons ─────────────────────────────────────────────────────────────
   Design's footer pair, node 626/627, unchanged. */
.sw-btn {
  flex: none;
  height: 38px;
  padding: 0 15px;
  font: 600 16.5px/1.2 var(--st-ui);
  color: var(--st-ink);
  background: var(--st-paper);
  border: 1px solid var(--st-btn-border);
  border-radius: 9px;
  cursor: pointer;
}
.sw-btn-primary {
  padding: 0 18px;
  color: var(--st-paper);
  background: var(--st-accent-text);
  border: none;
}
.sw-btn-primary:hover { background: var(--st-accent-hover); }

/* ⚠️ A DISABLED PRIMARY IS THE WHOLE VALIDATION LANGUAGE (RISKS A9). There
   is no sentence anywhere in this sheet explaining why a step will not
   advance; the button is off and the field that is wrong is outlined. So the
   disabled state has to be legible rather than merely different — 0.45 alpha
   on a filled button reads as "off" at a glance and still passes contrast
   against the paper it sits on. */
.sw-btn[disabled] { opacity: .45; cursor: default; }
/* ⊕ MRB-351 theme pass, 27 Sep 2026 — FOUND LIVE by contrast_audit.py once
   it first drove the sheet's flashcards branch far enough to see a
   disabled Next/Save (nothing in this file's own coverage had reached
   that state before). `.sw-btn-primary` pairs TWO colours that flip in
   OPPOSITE directions across the theme, `color:var(--st-paper)` on
   `background:var(--st-accent-text)` — 6.48:1 light, 8.65:1 dark, both
   fine at full opacity. The generic `.sw-btn[disabled]` rule above dims
   the WHOLE element toward whatever is behind it, and both colours are
   warm and light-on-the-page in their own theme, so they converge into
   two near-identical washed tints of the same hue: 1.45:1 light, 1.64:1
   dark, both under the 3:1 disabled floor. Opacity is fine for `.sw-btn`'s
   plain outline variant (one ink colour fading against its own paper);
   it cannot work for a two-tone fill+text pair, in either theme — so the
   primary variant gets its own flat disabled treatment instead of a
   fade: `--st-muted` on `--st-num-well`, un-dimmed, 4.69:1 light / 6.15:1
   dark. */
.sw-btn-primary[disabled] {
  opacity: 1;
  background: var(--st-num-well);
  color: var(--st-muted);
}
.sw-btn-primary[disabled]:hover { background: var(--st-num-well); }

/* ── body ───────────────────────────────────────────────────────────────── */
.sw-body { padding: 18px 16px 22px; min-width: 0; }
.sw-panel[hidden] { display: none; }

/* Design's section label, node 588: her `swCounts` eyebrow exactly. */
.sw-label {
  font: 500 13px/1.2 var(--st-mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--st-caption);
  margin-top: 20px;
}
.sw-label:first-child { margin-top: 0; }
.sw-label[hidden] { display: none; }

/* ── a value that cannot be changed ⊕ MRB-336 ────────────────────────────
   The tier and the topic of a set that has already been released. It is a
   VALUE, in the sheet's own body type, under one of the sheet's own labels
   — not a disabled chip rail. A greyed control invites a press and then
   refuses it, and the sentence explaining the refusal is the thing this
   sheet does not write. */
.sw-ro {
  margin-top: 9px;
  font: 600 17px/1.35 var(--st-ui);
  color: var(--st-ink);
  overflow-wrap: anywhere;
}
.sw-ro[hidden] { display: none; }

/* Swap changes WHICH questions a set holds, and a released set's questions
   are the ones children have. The control goes rather than greying: see
   `.sw-ro` above. */
.sw-qlist.is-ro .sw-swap { display: none; }

/* ── chip rails ──────────────────────────────────────────────────────────
   Design's `swCounts` row (node 590): equal-width mono chips, tinted when
   chosen. `flex-wrap` is the 390px answer — four count chips at 14px padding
   do not fit a 358px sheet body, and wrapping is the only alternative to a
   sideways scroll. */
.sw-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 12px;
}
.sw-chips[hidden] { display: none; }
.sw-chip {
  flex: 1 1 auto;
  min-width: 72px;
  padding: 13px 10px;
  font: 500 16px/1.2 var(--st-mono);
  letter-spacing: .06em;
  border-radius: 10px;
  cursor: pointer;
  color: var(--st-ink);
  background: var(--st-paper);
  border: 1px solid var(--st-btn-border);
  overflow-wrap: anywhere;
}
.sw-chip.is-on {
  color: var(--st-accent-text);
  background: var(--st-chip-tint);
  border-color: var(--st-chip-tint-border);
}
.sw-chip[disabled] { opacity: .4; cursor: default; }

/* ── the tree ────────────────────────────────────────────────────────────
   Design's topic row (nodes 596-600): name in UI 600/18, eyebrow in mono, tag
   right-aligned. v2 adds the chevron and the subtopic level; the count takes
   the tag's mono treatment because it IS the tag's slot. */
.sw-tree { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.sw-node { min-width: 0; }
.sw-node[hidden] { display: none; }

/* The topic row and its chevron. Two controls, one frame's worth of space:
   expanding a topic must not be the same press as choosing it. */
.sw-node-head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.sw-node-head > .sw-row { flex: 1 1 auto; }

.sw-row {
  width: 100%;
  display: flex;
  align-items: center;
  gap: 10px;
  text-align: left;
  padding: 13px 14px;
  border-radius: 10px;
  cursor: pointer;
  background: var(--st-paper);
  border: 1px solid var(--st-btn-border);
  min-width: 0;
}
.sw-row.is-on {
  background: var(--st-chip-tint);
  border-color: var(--st-chip-tint-border);
}
/* A zero-count node is shown, with its `0`, and cannot be picked (RISKS A5).
   Shown rather than hidden because "this topic has nothing at Higher" is a
   fact a teacher planning a term needs; hiding it would answer the question
   with a silence. */
.sw-row[aria-disabled="true"] { opacity: .5; cursor: default; }

.sw-row-main { flex: 1 1 auto; min-width: 0; }
.sw-row-name {
  display: block;
  font: 600 17px/1.25 var(--st-ui);
  color: var(--st-ink);
  overflow-wrap: anywhere;
}
.sw-row-tag {
  display: block;
  margin-top: 5px;
  font: 400 12.5px/1.2 var(--st-mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--st-caption);
  overflow-wrap: anywhere;
}
.sw-row.is-on .sw-row-tag { color: var(--st-accent-text); }
.sw-row-tag[hidden] { display: none; }

/* DM Mono, right-aligned, per the ticket. `tabular-nums` so a column of
   counts lines up rather than shimmering as the tier changes. */
.sw-count {
  flex: none;
  font: 500 15px/1.2 var(--st-mono);
  font-variant-numeric: tabular-nums;
  color: var(--st-ghost);
  min-width: 2.2em;
  text-align: right;
}
.sw-row.is-on .sw-count { color: var(--st-accent-text); }

/* The chevron is its own button so that expanding a topic is not the same
   press as selecting it. */
.sw-chev {
  flex: none;
  width: 30px;
  height: 30px;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  background: transparent;
  border: 1px solid var(--st-btn-border);
  border-radius: 9px;
  cursor: pointer;
  color: var(--st-muted);
}
.sw-chev svg { transition: transform .12s ease; }
.sw-chev[aria-expanded="true"] svg { transform: rotate(90deg); }
.sw-chev[hidden] { display: none; }

.sw-kids {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 6px 0 0 14px;
  padding-left: 10px;
  border-left: 1px solid var(--st-rule-soft);
}
.sw-kids[hidden] { display: none; }
.sw-kids .sw-row { padding: 10px 12px; }
.sw-kids .sw-row-name { font-size: 15.5px; font-weight: 500; }

/* ── the question list ───────────────────────────────────────────────────
   Collapsed: the stem on ONE line, ellipsised. Expanded: the full stem and
   the four options with the correct one marked. A teacher is accountable for
   work thirty children will sit, so the questions are readable before it is
   set — MRB-331's ruled divergence 2, kept. */
.sw-qlist { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.sw-q {
  border: 1px solid var(--st-btn-border);
  border-radius: 10px;
  background: var(--st-paper);
  min-width: 0;
}
.sw-q-head { display: flex; align-items: center; gap: 8px; padding: 11px 12px; min-width: 0; }
.sw-q-n {
  flex: none;
  font: 500 13px/1.2 var(--st-mono);
  color: var(--st-ghost);
  font-variant-numeric: tabular-nums;
  min-width: 1.6em;
}
/* The collapsed stem: one line, ellipsised. `min-width:0` is what lets
   `text-overflow` work at all inside a flex row — without it the flex item
   refuses to shrink below its content and the row overflows sideways, which
   is RISKS A8 exactly. */
.sw-q-stem {
  flex: 1 1 auto;
  min-width: 0;
  font: 400 15px/1.35 var(--st-ui);
  color: var(--st-body);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background: transparent;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
}
.sw-q.is-open .sw-q-stem {
  white-space: normal;
  overflow: visible;
  overflow-wrap: anywhere;
}
.sw-swap {
  flex: none;
  height: 30px;
  padding: 0 11px;
  font: 500 13.5px/1.2 var(--st-mono);
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--st-accent-text);
  background: var(--st-paper);
  border: 1px solid var(--st-btn-border);
  border-radius: 9px;
  cursor: pointer;
}
.sw-swap[disabled] { color: var(--st-ghost); opacity: .5; cursor: default; }

.sw-q-body { padding: 0 12px 12px 12px; min-width: 0; }
.sw-q-body[hidden] { display: none; }
/* ⊕ MRB-352 — a question's figure, above its options. The figure paints
   itself (a cream card of its own, in light and dark alike); this only sizes
   it to the column, never wider than it was drawn (set-work.js sets the
   max-width from the manifest), and lets a wide one scroll sideways at 360px
   rather than widen the sheet. */
.sw-q-fig { margin: 4px 0 10px 0; overflow-x: auto; min-width: 0; }
.sw-q-fig svg { display: block; width: 100%; height: auto; margin: 0 auto; }
.sw-opt {
  display: flex;
  gap: 8px;
  padding: 7px 0;
  font: 400 14.5px/1.4 var(--st-ui);
  color: var(--st-body);
  border-top: 1px solid var(--st-rule-fact);
  overflow-wrap: anywhere;
  min-width: 0;
}
.sw-opt-k {
  flex: none;
  font: 500 13px/1.4 var(--st-mono);
  color: var(--st-ghost);
}
/* The correct option is marked by a WEIGHT and a COLOUR and a tick, never by
   colour alone — the same rule the KS3 figures follow. */
.sw-opt.is-right { color: var(--st-accent-text); font-weight: 600; }
.sw-opt.is-right .sw-opt-k { color: var(--st-accent-text); }
.sw-tick { flex: none; }
.sw-tick[hidden] { display: none; }

/* ── fields ──────────────────────────────────────────────────────────────
   `min-width:0` and `flex-wrap` on every pair: a `<input type=date>` and an
   `<input type=time>` side by side are ~300px of intrinsic width on iOS, and
   at 390px the sheet body is 358px. They wrap. */
.sw-fields { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 12px; }
.sw-fields[hidden] { display: none; }
.sw-input {
  flex: 1 1 150px;
  min-width: 0;
  width: 100%;
  padding: 11px 12px;
  font: 400 15.5px/1.2 var(--st-ui);
  color: var(--st-ink);
  background: var(--st-paper);
  border: 1px solid var(--st-btn-border);
  border-radius: 10px;
}
.sw-input[type="date"], .sw-input[type="time"] {
  font-family: var(--st-mono);
  font-size: 15px;
}
/* ⚠️ THE WHOLE OF VALIDATION'S VOCABULARY, WITH THE DISABLED PRIMARY. A
   field that is wrong is outlined in the accent and nothing is written about
   it. `--st-accent-text` rather than `--st-accent`: the darker of the two is
   the one that reads against paper at 1px. */
.sw-input.sw-bad, .sw-chips.sw-bad .sw-chip, .sw-tree.sw-bad {
  border-color: var(--st-accent-text);
}
.sw-tree.sw-bad { border: 1px solid var(--st-accent-text); border-radius: 10px; padding: 6px; }

/* ── ⊕ MRB-342.2 · THE NOTE FIELD — shared by the download menu's own note
   (contract §2.1) and the Set-work sheet's assignment note (§3). One field,
   built once (`buildNoteField` in set-work.js), so its two placements can
   never drift. A `<textarea>` gets `.sw-input`'s own look; `resize:vertical`
   is the one addition a single-line input never needed. */
.sw-note-field { margin-top: 12px; }
.sw-note-input {
  resize: vertical;
  min-height: 44px;
  font-family: var(--st-ui);
}
.sw-note-count {
  margin-top: 5px;
  text-align: right;
  font: 400 12.5px/1.2 var(--st-mono);
  color: var(--st-caption);
  font-variant-numeric: tabular-nums;
}
/* Never a block — the server is the one true 300-character bound (`bad_note`
   refuses it), so this is a courtesy colour change, not a control that locks
   the teacher out of typing further. */
.sw-note-count.sw-note-over { color: var(--st-accent-text); }

/* ── ⊕ MRB-342.2 · THE COUNT FIELD — beside the quick-pick chips, contract
   §1. `flex-wrap` matches `.sw-fields`' own answer to the same 390px
   problem: a number input and (on a clamped scope) its note both need room
   a 358px sheet body does not spare a chip rail for. */
.sw-count-custom { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 8px; }
.sw-count-input {
  flex: 0 1 96px;
  min-width: 72px;
  font-family: var(--st-mono);
}
/* The clamp note reuses `.sw-row-tag`'s look (the tree and the topic panel's
   own status line) — one word or one ruled sentence, never a paragraph. */
[data-sw="count-note"] { margin-top: 8px; }
[data-sw="count-note"][hidden] { display: none; }

/* A used-up Swap's settled line, same idiom, sitting between the question
   row's head and its (still-collapsed) options. */
[data-sw="swap-note"] { margin: 0 12px 10px; }
[data-sw="swap-note"][hidden] { display: none; }

/* ⊕ MRB-336 — `.sw-hold` WAS HERE. The sheet emits no such node any more:
   the school's open date governs automatic composition only, so there is
   nothing for the sheet to disclose. Deleted rather than left unused, so a
   future node cannot inherit a style for a rule that no longer exists. */

/* ── the toast ───────────────────────────────────────────────────────────
   Design's `tin` keyframes, redeclared here under our own name because this
   file must stand up on a page that has not loaded her inline `<style>`. */
.sw-toast {
  position: fixed;
  left: 50%;
  bottom: 26px;
  transform: translateX(-50%);
  z-index: 70;
  max-width: calc(100vw - 32px);
  padding: 12px 18px;
  font: 500 15px/1.35 var(--st-ui);
  color: var(--st-cream);
  background: var(--st-ink);
  border-radius: 11px;
  box-shadow: var(--st-shadow-pop);
  animation: sw-tin .18s ease-out;
  overflow-wrap: anywhere;
}
.sw-toast[hidden] { display: none; }
@keyframes sw-tin {
  from { opacity: 0; transform: translate(-50%, 8px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

/* ⊕ D7 (theme-run audit, 27 Sep 2026). This file "must stand up on a page
   that has not loaded [Design's] inline <style>" (the comment above) — and
   it equally never had its own dark handling at all, which is the bug: the
   toast is drawn INK, on purpose, so it reads as a page-level announcement
   rather than another card ("Set for 10h/Sc1", "not set", "not saved") —
   the same "inverted ink card" idiom `shared/leaderboard-ds.css`'s week
   chip and every KS3 ink panel share. `--st-ink` flips light for dark-theme
   body text (`shared/teacher-ds.css`'s own dark block, which this file
   depends on already being loaded), so the toast's card flips from a dark
   pill to a light one, and `--st-cream` (correctly NOT flipped generally —
   its other job is "cream on a plate that is always dark") stays light on
   it: 1.05:1, measured. `.sw-toast` is a real class, so — unlike the
   leaderboard week chip, which is inline-templated — this can be a normal
   class override, no attribute-selector trick needed, and `--st-cream` is
   used nowhere else in this file, so scoping the fix to `.sw-toast` alone
   cannot affect anything else. Wrapped in `@media screen` per rule 7
   (D8): the toast is a transient screen announcement, never meant to be
   part of a print output, and any future dark rule added to this file
   should be added inside this same guard rather than a new bare one.
   15.75:1 in dark (#F3EBDD bg / #16120D text — the same ink
   `--pg-on-accent-text` and the leaderboard week-chip fix both use for the
   identical reason). */
@media screen {
html[data-theme="dark"] .sw-toast { color: #16120D; }
}

/* ── 390px ───────────────────────────────────────────────────────────────
   The sheet takes the whole phone. `max-height:100%` with the overlay padding
   at 0 gives the scroller the full height, which matters because the sticky
   header costs 66px of it. */
@media (max-width: 520px) {
  .sw-overlay { padding: 0; align-items: stretch; }
  .sw-sheet {
    max-height: 100%;
    height: 100%;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }
  .sw-head { border-radius: 0; padding: 12px 14px; }
  .sw-body { padding: 16px 14px 26px; }
  .sw-step { letter-spacing: .12em; font-size: 12px; }
  .sw-btn { padding: 0 12px; font-size: 15.5px; }
  .sw-btn-primary { padding: 0 14px; }
  .sw-kids { margin-left: 8px; padding-left: 8px; }

  /* The iOS sticky fallback, off unless something adds `sw-fixed-header`
     to the sheet. Declared HERE, inside the narrow breakpoint, for the
     reason given where the class is introduced above. */
  .sw-sheet.sw-fixed-header { position: relative; }
  .sw-sheet.sw-fixed-header .sw-head {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    border-radius: 0;
  }
  .sw-sheet.sw-fixed-header .sw-body { padding-top: 78px; }
}

/* ── ⊕ Set from class (M), 27 Sep 2026 — the type choice ─────────────────
   Mide's ruling: Questions and Flashcards are the first thing on the sheet,
   two EQUAL, full-size options — the Tier chips' own rail, each chip taking
   half. Ruled off from the panel below it so it reads as the choice that
   decides what the rest of the sheet is. */
.sw-type {
  margin-bottom: 20px;
  padding-bottom: 18px;
  border-bottom: 1px solid var(--st-rule-soft);
}
.sw-type[hidden] { display: none; }
.sw-type .sw-chip { flex: 1 1 0; }

/* ── ⊕ Set from class (M), 27 Sep 2026 — the WIDE sheet ──────────────────
   Mide's ruling: on a desktop 720px was a thin column in the middle of the
   screen. From 1100px up the sheet takes 88% of the viewport, to 1400px so
   it does not sprawl on a very wide monitor; the height is unchanged.
   Below 1100px — tablets and phones — nothing here applies and the sheet is
   exactly what it was.

   The width is used, not just spent: the class list and the topic tree go
   two columns (`align-items:start`, so a topic opened in one column does
   not stretch its neighbour), and every chip rail stays on one line. The
   questions on the Detail step stay one column — a stem is read, not
   scanned. */
@media (min-width: 1100px) {
  .sw-sheet { width: min(88vw, 1400px); }
  .sw-body { padding: 22px 28px 28px; }
  .sw-head { padding: 14px 28px; }
  .sw-classes,
  .sw-panel[data-sw="panel-topic"] .sw-tree {
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 8px 14px;
    align-items: start;
  }
  .sw-chips { flex-wrap: nowrap; }
}

@media (prefers-reduced-motion: reduce) {
  .sw-toast { animation: none; }
  .sw-chev svg { transition: none; }
}

/* ── ⊕ MRB-342 · SEVERAL SCOPES, AS SECTIONS ─────────────────────────────
   One topic keeps the layout it had: the Questions eyebrow, a chip rail and
   a list. A second topic adds a second section under the same eyebrow, with
   a name on each so the rows are attributable. The rule above the section is
   the only new ink — Design's `--st-rule-soft`, the same hairline the sticky
   header sits on. */
.sw-scopes { display: flex; flex-direction: column; }
/* ⊕ MRB-351 — a flashcard set has no question list; the host is hidden and
   `display:flex` above would otherwise beat the `hidden` attribute. */
.sw-scopes[hidden], [data-sw="fc-top"][hidden] { display: none; }
.sw-scope { min-width: 0; }
.sw-scope + .sw-scope {
  margin-top: 18px;
  padding-top: 16px;
  border-top: 1px solid var(--st-rule-soft);
}
/* ⚠️ A TOPIC NAME IS DATA AND NOT CHROME, so it is NOT `.sw-label` — that
   class is swept against the sheet's allowed words and a topic name is not
   one of them. Same treatment as `.sw-row-name` in the tree, which is the
   other place the same string is drawn. */
.sw-scope-name {
  margin-top: 14px;
  font: 600 16px/1.3 var(--st-ui);
  color: var(--st-ink);
  overflow-wrap: anywhere;
}
.sw-scope-name[hidden] { display: none; }

/* Back to the Topic step for a further scope. Full width so it reads as an
   action ON the list above it rather than as a third header button, and
   `.sw-btn`'s own disabled alpha when the sheet already holds MAX_SCOPES
   topics (⊕ first-week fixes (22 Sep 2026) — it used to be "when the set is already at twenty",
   which was the whole-set question ceiling; a topic's count is now bounded by
   its own pool and the number of TOPICS is what bounds a set). */
.sw-add {
  width: 100%;
  margin-top: 16px;
  color: var(--st-accent-text);
}
.sw-add[hidden] { display: none; }

/* ⊕ first-week fixes (22 Sep 2026) — THE TOPIC STEP'S STATUS AND ITS RETRY.
   `Next` now enables the moment a class is ticked rather than waiting for
   `/scope`, so a teacher can stand on the Topic step before the tree exists.
   The note is the same `.sw-row-tag` the tree's own refusals wear — one word,
   in the tag slot — and it sits ABOVE the Retry rather than beside it so the
   two read as a statement and an action, not as a sentence.
   `.sw-add` carries the Retry's shape: it is the same kind of full-width
   action on the list above it that `Add topic` is. */
[data-sw="tree-note"] { margin-top: 12px; }
[data-sw="tree-note"][hidden], [data-sw="tree-retry"][hidden] { display: none; }
[data-sw="tree-retry"] { margin-top: 10px; }

/* ── ⊕ MRB-342 · THE DOWNLOAD MENU ───────────────────────────────────────
   One control, three places: the sheet's header, the class table's
   assignment row, the marking screen. `position:relative` on the wrapper is
   what the menu hangs off, and the menu is `absolute` so opening it moves
   nothing around it — a menu that pushed the rows down would take the row
   the teacher pressed out from under their thumb.

   ⚠️ RIGHT-ALIGNED. It opens under the last control in a header whose right
   edge is the sheet's, so anchoring it left would put it off the screen at
   390px. */
.sw-dl { position: relative; flex: none; }
.sw-dl[hidden] { display: none; }
.sw-dl-menu {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 3;
  min-width: 168px;
  padding: 8px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  background: var(--st-paper);
  border: 1px solid var(--st-edge);
  border-radius: 11px;
  box-shadow: var(--st-shadow-pop);
}
.sw-dl-menu[hidden] { display: none; }
.sw-dl-title {
  padding: 4px 8px 8px;
  font: 500 12px/1.2 var(--st-mono);
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--st-caption);
}
.sw-dl-opt {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 10px 10px;
  text-align: left;
  font: 500 15px/1.2 var(--st-ui);
  color: var(--st-ink);
  background: transparent;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
}
.sw-dl-opt:hover { background: var(--st-note-bg); }

/* ⊕ MRB-342.1 — THE TWO TOGGLES ARE REAL CHECKBOXES.
   They used to be buttons wearing an SVG tick. The rule that separated them
   from the two things that actually START a download is kept — it is the
   reason the panel reads as "two choices, then two actions" — but it now sits
   under the PAIR rather than under the single Answers row.

   ⚠️ NOTHING HERE REPLACES THE BOX'S OWN APPEARANCE. There is no
   `appearance: none`, so what a teacher sees is their own platform's
   checkbox, already correct in both colour schemes, already the right size
   for a finger on iPadOS, and already showing the indeterminate and disabled
   states this panel does not use but a browser may. `accent-color` tints the
   checked fill to the sheet's orange and is the whole of the styling. */
.sw-dl-checks {
  display: flex;
  flex-direction: column;
  gap: 2px;
  border-bottom: 1px solid var(--st-rule-soft);
  padding-bottom: 4px;
  margin-bottom: 4px;
}
.sw-dl-check { cursor: pointer; }
.sw-dl-box {
  flex: none;
  width: 16px;
  height: 16px;
  margin: 0;
  accent-color: var(--st-accent);
  cursor: pointer;
}
.sw-dl-word { flex: 1 1 auto; min-width: 0; }

/* ⚠️ `:focus-within` ON THE ROW, because the ring belongs around the LABEL
   and the focus is on the input inside it. A native focus ring on a 16px box
   tucked against the panel's edge is easy to miss; drawing it around the
   whole row is the same information where a teacher is already looking.
   `:focus-visible` on the input itself, so a mouse press does not ring. */
.sw-dl-check:has(.sw-dl-box:focus-visible) {
  outline: 2px solid var(--st-accent);
  outline-offset: -2px;
  background: var(--st-note-bg);
}
/* The fallback for a browser without `:has()`. It rings on any focus rather
   than on keyboard focus alone, which is the safe direction: a ring nobody
   needed is a smaller failure than a control that never shows focus. */
@supports not selector(:has(*)) {
  .sw-dl-box:focus { outline: 2px solid var(--st-accent); outline-offset: 2px; }
}

/* ── ⊕ MRB-342 · 390px ───────────────────────────────────────────────────
   Four controls in a 362px header: Cancel, the step word, Download and the
   primary. The step word is `flex:1 1 auto; min-width:0` and yields first;
   these trims buy it back enough room to stay legible. */
@media (max-width: 520px) {
  /* ⊕ MRB-352 run 2 (batch 3 landing): at 360px the Detail step's header
     left the step word 44px — too narrow for "DETAIL" — so it broke inside
     the word. A tighter gap and tracking buy the word back its width. */
  .sw-head { gap: 8px; }
  .sw-step { letter-spacing: .06em; }
  .sw-dl-btn { padding: 0 11px; }
  .sw-dl-menu { min-width: 156px; }
  .sw-scope + .sw-scope { margin-top: 16px; padding-top: 14px; }
}

/* ── ⊕ Catch-up (14 Oct 2026) ────────────────────────────────────────────
   The catch-up type shows its own blocks (`sw-cuonly`) and hides the ones a
   flat lesson search makes redundant (`sw-nocu`: subject, paper, the tree).
   !important because `.sw-chips`/`.sw-tree` set display themselves and these
   must win whatever order the sheet's rules load in. */
.sw-cuonly { display: none !important; }
.sw-overlay[data-sw-type="catchup"] .sw-cuonly:not([hidden]) { display: block !important; }
.sw-overlay[data-sw-type="catchup"] .sw-tree.sw-cuonly:not([hidden]) { display: flex !important; }
.sw-overlay[data-sw-type="catchup"] .sw-chips.sw-cuonly:not([hidden]) { display: flex !important; }
.sw-overlay[data-sw-type="catchup"] .sw-nocu { display: none !important; }
.sw-tree[hidden], .sw-row[hidden] { display: none !important; }
[data-sw="lesson-search"] { margin-top: 12px; }

/* ── ⊕ Unit C (21 Oct 2026) — the weekly challenge sheet ─────────────────
   Reuses the shell above; these are only the differences. A challenge row is
   a div with its own buttons, not one big button, so it takes no pointer
   cursor and no pressed look. The title is a sentence, not the mono step
   word, so it is allowed to wrap and loses the tracking. */
.sw-ch-row { cursor: default; }
.sw-ch-act[hidden], .sw-ch-row .sw-row-tag[hidden], .sw-btn[hidden] { display: none; }
.sw-ch-title {
  text-align: left;
  letter-spacing: 0;
  text-transform: none;
  white-space: normal;
  font: 600 16.5px/1.25 var(--st-ui);
  color: var(--st-ink);
}
.sw-ch-due { margin: 0 0 14px; }
.sw-ch-note { margin-top: 12px; }
.sw-ch-row[data-sw-state="error"] .sw-row-tag { color: var(--danger, #B3261E); }
