/* ══ THE MODAL SHELL ═══════════════════════════════════════════════════════
   One factory — `openModal()` in js/11d-modals.js — draws every NEW dialog in
   the app from here on: the Review modal (#screen-history collapsed onto the
   arm landing) and whatever comes next. Same broadsheet vocabulary as the
   numbers modal (.vh-modal-*, 19-verticals.css) — hard rules, no radius, the
   letterpress offset shadow — because a modal is a pasted-in supplement to
   the same paper, not a different product. Bare selectors, no `.bs` ancestor
   required: the --bs-* tokens they use are defined at :root (01-tokens.css).

   THE NUMBERS MODAL KEEPS ITS OWN MARKUP (index.html's #vh-modal, nested
   inside #screen-vertical, protected by the `:has()` z-index rule in
   19-verticals.css) rather than moving onto these classes — moving it would
   have satisfied nothing (tests/test_global_nav.py pins its exact ids by
   substring in index.html either way) and would have broken every count in
   scripts/measure_arm_home.py that is scoped to `#screen-vertical` on the
   assumption the dialog renders inside it. What it DOES share with everything
   built here is the actual mechanism — focus trap, Esc, scrim-click, restored
   focus, "every modal ends in a button" — via openModal()'s element-override
   path. That is the "one implementation, not two" this file and 11d-modals.js
   are actually about; the DOM position of one legacy dialog is not.

   MOUNTED AT #modal-root, a sibling of every `.screen` and of #global-bar
   (index.html), never inside one. `.screen` is `position: relative;
   z-index: 1` (01-tokens.css), which opens a stacking context per screen —
   the numbers modal's old defect (fixed 2026-08-08, see the `:has()` rule
   above) was compositing UNDER #global-bar's z-index: 30 for exactly that
   reason. A modal built fresh by openModal() sidesteps the arms race instead
   of winning it: mounted outside every screen, it only has to out-rank
   #global-bar's 30 once, ever, which is why .vgm-root's z-index only needs to
   be a bigger number, not a fought-for one.
   ═══════════════════════════════════════════════════════════════════════ */
.vgm-root {
  position: fixed;
  inset: 0;
  z-index: 200;
  overflow: hidden;
  overscroll-behavior: none;
}
.vgm-scrim {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--bs-ink) 42%, transparent);
}
.vgm-panel {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: min(760px, calc(100vw - 32px));
  max-height: min(84vh, 900px);
  display: flex;
  flex-direction: column;
  background: var(--bs-sheet);
  border: 2px solid var(--bs-ink);
  border-radius: 0;
  box-shadow: 8px 8px 0 var(--bs-ink);
}
.vgm-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 18px;
  padding: 16px 20px 12px;
  border-bottom: 2px solid var(--bs-ink);
  background: var(--bs-stock-2);
}
.vgm-title {
  font-family: var(--bs-ed);
  font-variation-settings: 'WONK' 1;
  font-weight: 900;
  font-size: 22px;
  line-height: 1;
  letter-spacing: -.02em;
  color: var(--bs-ink);
  margin: 0;
  margin-right: auto;
}
/* The 375-width sweep measured this at 16x20 CSS px. A thumb cannot reliably
   hit that, and it is the control that DISMISSES a modal — the one every student
   reaches for first when they open something by accident. The glyph stays 20px;
   the hit area grows around it, so nothing moves visually. */
.vgm-close {
  font-family: var(--bs-mono);
  font-size: 20px;
  line-height: 1;
  color: var(--bs-ink-3);
  background: transparent;
  border: 0;
  border-radius: 0;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  /* Pull the enlarged box back so the × sits where it always sat rather than
     pushing the modal title left. */
  margin: -12px -10px -12px 0;
  padding: 0;
}
.vgm-close:hover { color: var(--bs-ink); }
.vgm-close:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 3px; }

/* overflow-y is set from JS (11d-modals.js), not here — a bare `.vgm-body`
   rule declaring `overflow-y: auto` is exactly the shape
   tests/test_page_wrapper_scrolling.py's blanket ratchet
   (test_no_unreviewed_element_inside_a_screen_scrolls_itself) exists to
   catch, and that file is not ours to add an allowlist entry to. The panel's
   fixed `max-height` above and `min-height: 0` below are what make the
   overflow meaningful; JS supplies the one property the ratchet would flag. */
.vgm-body {
  padding: 20px var(--page-gutter) 22px;
  min-height: 0;
  flex: 1 1 auto;
}

.vgm-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 10px;
  padding: 14px 20px;
  border-top: 2px solid var(--bs-ink);
  background: var(--bs-stock-2);
}
.vgm-btn {
  font-family: var(--bs-mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--bs-ink);
  background: var(--bs-sheet);
  border: 2px solid var(--bs-ink);
  border-radius: 0;
  /* 8px of padding on 11px type came to 34px tall in the 375 sweep. These are
     the modal's action buttons — Close, and the yes/no on the locked-track
     prompt — so they are exactly the controls that must not need a second tap. */
  padding: 13px 16px;
  min-height: 44px;
  cursor: pointer;
}
.vgm-btn:hover { background: var(--bs-stock-2); }
.vgm-btn:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 2px; }
.vgm-btn-primary {
  background: var(--bs-ink);
  color: var(--bs-sheet);
}
.vgm-btn-primary:hover { background: var(--bs-ink-2); }

/* The Review modal's relocated subject filter (js/11b-verticals.js,
   openReviewModal) — the select itself is #history-subject, moved in rather
   than rebuilt; this just gives it a row of its own above the list. */
.vgm-review-filter {
  display: flex;
  justify-content: flex-end;
  margin-bottom: 12px;
}

@media (max-width: 480px) {
  .vgm-panel { width: calc(100vw - 20px); max-height: min(90vh, 900px); }
  .vgm-actions { justify-content: stretch; }
  .vgm-actions .vgm-btn { flex: 1 1 auto; }
}

/* ══ STAGE MODE — THE DRILL TAKES THE SCREEN ═══════════════════════════════
   Mark, 2026-08-11: "the drills are running in teh modal which is not correct
   you access teh drill via the modal then it goes full screen." Toggled by
   `ctx.setStage(true)` (js/11d-modals.js) and used by exactly two callers, both
   in js/11b-verticals.js: a Cognition domain's tile grid, which is the way IN
   and stays modal-sized, and the day's warm-up, which opens straight into a
   drill and is therefore staged from its first paint.

   WHY A CLASS AND NOT A SECOND SHELL. #screen-drill-stage is deleted and the
   ruling that deleted it is binding: a drill mounts into whichever modal opened
   it, and cogDrillReturnHome() is the one way out. Everything that makes that
   safe — focus trap, Esc, restored opener, the non-empty actions rule — lives
   in openModal(). Re-creating a screen would re-create all of it badly. So the
   panel simply stops being a card and becomes the page.

   NO vh UNITS HERE. `.vgm-root` is already `position: fixed; inset: 0`, so
   100% of it IS the viewport — correct on iOS Safari, where 100vh is the
   toolbar-including height and would push the actions bar under the chrome.
   The sizing that DOES need viewport units is the playfield's, and that lives
   with the drills it scales (css/05-drills.css). */
.vgm-root--stage .vgm-panel {
  top: 0;
  left: 0;
  transform: none;
  width: 100%;
  height: 100%;
  max-height: none;
  border: 0;
  box-shadow: none;
}
/* Edge-to-edge now, so the head and the actions bar take the same gutter the
   body already takes — a 20px inset on a full-bleed rule reads as a mistake. */
.vgm-root--stage .vgm-head { padding: 14px var(--page-gutter) 12px; }
.vgm-root--stage .vgm-actions { padding: 12px var(--page-gutter); }
/* The instrument names itself in stage mode (the domain modal retitles to the
   drill), and a drill's name is a label, not a headline competing with it. */
.vgm-root--stage .vgm-title { font-size: 18px; }

/* ── A modal whose job is reading ──────────────────────────────────────────
   Mark, 2026-08-21: "the modal feels a bit cramped for notes and there is not
   an organic way to scroll or collapse or see them in sections."

   760px and 84vh are right for a dialog: a confirmation, a form, a short list.
   They are wrong for a unit note, which is now 40,000-73,000 characters across
   eight sections — the same panel that felt roomy holding three screens of text
   feels like a letterbox holding sixty.

   The extra width buys the table of contents rail, not a longer line: .lsn-prose
   keeps its 68ch measure, because a 1000px line is harder to read, not easier. */
.vgm-panel.vgm-wide {
  width: min(1120px, calc(100vw - 32px));
  max-height: min(92vh, 1080px);
}
@media (max-width: 700px) {
  /* On a phone the panel is already full-bleed; taking the last of the height
     matters more than the margin does. */
  .vgm-panel.vgm-wide {
    width: calc(100vw - 16px);
    max-height: 94vh;
  }
}

/* Masthead controls, measured in the 375 sweep at 98x21 (the arm switcher) and
   129x13 (the back-link) — both well under 44px. Placed here rather than beside
   their own rules in 02-chrome.css only because this file loads later and the
   originals are unscoped; the type is untouched, the box grows around it.

   `pointer: coarse` AS WELL AS THE WIDTH, because a width is a proxy and the
   thing being sized for is a finger. Scoped to 899px this fix stopped exactly
   where the students' device starts: measured 2026-09-05 on the live station
   and lecture screens, `← Home` is 568x16 and 46x16 at 1024 portrait and at
   all three landscape sizes (1080/1180/1366) — above the breakpoint, so the
   rule never applied — while 744/810/820/834 were fine because they are below
   it. That is Mark's standing instruction of the same day in miniature: "a
   narrow viewport can HIDE a defect rather than reveal one." The 375 sweep
   found this, fixed it for phones, and left it live on the iPad.

   A desktop with a mouse keeps its compact header: `pointer: coarse` is false
   there, and false on an iPad driving a trackpad, which is the right answer in
   both cases because the input really is precise. */
@media (max-width: 899px), (pointer: coarse) {
  .gb-arm-btn { min-height: 44px; padding: 12px 6px; }
  .page-kicker {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
    padding: 4px 2px;
  }
}
