/* ══ THE TOOL RAIL ═════════════════════════════════════════════════════════
   The calculator and the scratchpad, sharing one tabbed rail (D2). Numbered 16
   so it lands after 15-bs-shared.css; the NN- prefix is the cascade.

   WAS a panel docked bottom-right, floating over the page. Mark's call is that
   an instrument belongs IN the margin beside the work, as a column of the page,
   with the two tools as tabs so only one is ever competing for that column.

   Two faces for the calculator, unchanged: a Desmos mount (the normal path) and
   the in-house keypad that takes over when Desmos cannot load. The keypad is
   built in the publication's own language — hard edges, the letterpress offset,
   ink-block keys, Plex Mono for the figures.

   TWO MODES, one breakpoint (1500px, matched by TOOL_RAIL_MIN_PX in
   js/11a-calculator.js — change both or neither):

     docked  >= 1500  the rail is a static block in .margin. Opening a tool
                      widens the rail column and spends the page's outer gutter;
                      the question column never moves.
     sheet    < 1500  no gutter left to spend, so the rail leaves the grid, pins
                      to the bottom of the window as a tab strip, and raises a
                      sheet when a tool is picked.

   WHY 1500 AND NOT 1440. The widened question (1040) plus the 34px gap plus a
   usable graphing rail (384) is 1458px of content. At a 1440 viewport there are
   1425 to give it, so the two cannot coexist — one of them has to yield. Mark
   asked for BOTH ("the question box should be bigger" and "one at a time so the
   question keeps its width"), and the only reading that honours both is: dock
   the rail where it genuinely fits, and raise a bottom sheet where it does not.
   The alternative — shrinking the stem the moment a student opens a calculator —
   is the thing he explicitly did not want.

   The cost, stated plainly: at 1440 the calculator is now a bottom sheet, so the
   docked plate he reviewed and liked needs a 1600 capture to show. qc_ui_walk.py
   shoots the two tool surfaces at 1600 for exactly that reason.
   ══════════════════════════════════════════════════════════════════════════ */

/* 52vh was sized for a pane the student OPENED deliberately. §2 makes the
   scratchpad open BY DEFAULT, and at that height it took ~40% of a 1440x900
   viewport on arrival — the question it exists to help with got the smaller
   half of the screen. The pad is still drag-resizable (the grip writes --pad-h
   over this), so a student who wants the big pad is one drag away; the default
   just stops assuming everyone does. */
.tool-rail { --tool-pane-h: min(300px, 32vh); }
.tool-rail[hidden] { display: none; }

/* ── the tab strip ── */
.tool-tabs {
  display: flex;
  align-items: stretch;
  gap: 0;
  background: var(--bs-ink);
}
.tool-tab {
  flex: 1 1 0;
  min-width: 0;
  background: transparent;
  border: 0;
  border-right: 1px solid color-mix(in oklab, var(--bs-stock) 26%, transparent);
  color: var(--bs-stock);
  font-family: var(--bs-mono);
  font-size: 9.5px;
  font-weight: 500;
  letter-spacing: .2em;
  text-transform: uppercase;
  padding: 7px 6px;
  cursor: pointer;
  transition: background .12s ease, color .12s ease;
}
.tool-tab:hover:not(:disabled) { background: color-mix(in oklab, var(--bs-stock) 14%, transparent); }
/* The selected tab is cut OUT of the ink bar — it becomes the sheet below it,
   which is what makes the pane read as belonging to the tab rather than to the
   rail generally. */
.tool-tab[aria-selected="true"] {
  background: var(--bs-sheet);
  color: var(--bs-ink);
  font-weight: 700;
}
/* Disabled is the exam's gate, not a broken control, so it stays legible and
   keeps its tooltip rather than fading to noise. Camel on ink is 4.77:1.

   NO `opacity` HERE. This rule used to carry `opacity: .62` directly under that
   ratio, and the two contradicted each other: 4.77:1 describes the declared
   pair, while the compositor multiplied the fade into it and painted 2.75:1 —
   under the 4.5:1 this rule's own comment is claiming. The colour is already
   two steps down from the enabled tab's --bs-stock, so "unavailable" survives
   the fade's removal; the cursor and the tooltip carry the rest.
   Pinned by tests/test_palette_contrast.py, which composites it rather than
   reading the declaration. */
.tool-tab:disabled {
  color: var(--bs-camel-on-ink);
  cursor: not-allowed;
  /* No line-through. A struck-out label reads as DELETED or as an error — on a
     tab whose only crime is that this exam forbids a calculator, that is the
     wrong signal at the wrong volume, and it is the first thing the eye lands on
     in the rail. The muted colour, the cursor and the tooltip (which states the
     exam's actual rule) carry "unavailable" without shouting. */
}
.tool-tab:focus-visible { outline: 2px solid var(--bs-camel); outline-offset: -3px; }

.tool-close {
  flex: 0 0 auto;
  background: transparent;
  border: 0;
  color: var(--bs-stock);
  font-family: var(--bs-mono);
  font-size: 11px;
  line-height: 1;
  padding: 2px 9px;
  cursor: pointer;
  /* No tool open means nothing to close, so the ✕ is not offered. */
  display: none;
}
.tool-rail.open .tool-close { display: block; }
.tool-close:hover { color: var(--bs-camel-on-ink); }
.tool-close:focus-visible { outline: 2px solid var(--bs-camel); outline-offset: 2px; }

/* Camel, not bronze: this is a mark on a dark ground, where camel is 4.76:1 and
   reads as the gold it is. See the accent note in 01-tokens.css. */
.tool-rule {
  display: block;
  font-family: var(--bs-mono);
  font-size: 8.5px;
  font-weight: 500;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  padding: 5px 1px 0;
}

/* ── panes ── */
.tool-body { display: none; }
.tool-rail.open .tool-body { display: block; }
.tool-pane { display: none; }
.tool-pane.is-active { display: block; }

#calc-host {
  background: var(--bs-sheet);
  border: 1px solid var(--bs-ink);
  border-top: none;
  /* Same letterpress language as the primary action, one step quieter. */
  box-shadow: 3px 3px 0 var(--bs-ink);
  padding: 10px;
}

/* ── the scratchpad ──
   A typing surface, deliberately not a canvas: the figure face, and nothing
   else. Mark asked for somewhere to work for students without paper, not for
   a drawing tool. WAS ruled (a repeating-linear-gradient on the 26px
   line-height) — Mark asked for the lines gone, so the surface is plain now. */
.pad-input {
  display: block;
  width: 100%;
  /* --pad-h, not --tool-pane-h directly: the grip below writes --pad-h on the
     rail, and --tool-pane-h also sizes the Desmos pane, which measures its
     container on mount and would need an explicit resize() on every drag. The
     scratchpad resizes; the calculator keeps the rail's default. */
  height: var(--pad-h, var(--tool-pane-h));
  resize: none;
  background: var(--bs-sheet);
  border: 1px solid var(--bs-ink);
  border-top: none;
  box-shadow: 3px 3px 0 var(--bs-ink);
  border-radius: 0;
  padding: 12px 13px;
  font-family: var(--bs-mono);
  font-size: 13px;
  line-height: 26px;
  color: var(--bs-ink);
}
.pad-input:focus { outline: none; border-color: var(--bs-blue); }
.pad-input::placeholder { color: var(--bs-ink-3); font-style: italic; }

/* ── the resize grip (§6: "draggable up/down to resize") ──
   Full width rather than a corner handle: this is a docked panel in a narrow
   rail, and a 16px native textarea grip is not a target a student finds. The
   three rules are the same printer's furniture the rest of the rail uses. */
/* 24px minimum, and this is the one where it matters most: a DRAG target is
   the hardest kind to hit and a miss here does something else instead — it
   scrolls the page, which is exactly why this rule already carries
   `touch-action: none`. Measured 366x14 at 390px and 281x14 at 320px on
   2026-09-05. The two grip bars are drawn by ::before/::after and stay
   centred, so the handle looks unchanged and only the grab area grows; the
   pad gives up 10px it can spare out of a 213px rail. */
.pad-grip {
  min-height: 24px;
  margin: 0 0 2px;
  cursor: ns-resize;
  /* The pointer events are handled in JS; without this a drag on a touch
     screen scrolls the page instead of moving the grip. */
  touch-action: none;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: transparent;
  border: none;
}
.pad-grip::before,
.pad-grip::after {
  content: '';
  width: 18px;
  height: 1px;
  background: var(--bs-ink-3);
}
.pad-grip:hover::before,
.pad-grip:hover::after,
.pad-grip.is-dragging::before,
.pad-grip.is-dragging::after { background: var(--bs-ink); }
.pad-grip:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 1px; }

.pad-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 1px 0;
}
.pad-hint {
  font-family: var(--bs-mono);
  font-size: 8.5px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}
/* 24px minimum, WCAG 2.2 SC 2.5.8 — measured 53x19 in a browser at 320px on
   2026-09-05. A 9px label needs the box to do the work. */
.pad-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 24px;
  background: transparent;
  border: 1px solid var(--bs-rule);
  border-radius: 0;
  font-family: var(--bs-mono);
  font-size: 9px;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--bs-ink-2);
  padding: 3px 9px;
  cursor: pointer;
}
.pad-clear:hover { border-color: var(--bs-ink); color: var(--bs-ink); }
.pad-clear:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 1px; }

/* ── the formula sheet ──
   Same sheet-on-ink letterpress as #calc-host and .pad-input, so the three tabs
   read as three faces of one instrument. It SCROLLS inside --tool-pane-h rather
   than growing the rail: eleven cells plus three facts do not fit a 32vh pane at
   every width, and a rail that changes height per tab makes the page jump. */
.ref-sheet {
  background: var(--bs-sheet);
  border: 1px solid var(--bs-ink);
  border-top: none;
  box-shadow: 3px 3px 0 var(--bs-ink);
  height: var(--tool-pane-h);
  overflow-y: auto;
  padding: 10px;
}
.ref-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* auto-fit, not a fixed count: the rail is 230px shut and 384px open, and the
     sheet is also the foot-pinned drawer at narrow widths. */
  grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
  gap: 1px;
  /* Sheet, NOT the rule colour. 11 cells never divide evenly into the 3 columns
     the docked rail fits, so the last row always has an empty slot — and with
     the rule colour behind the grid that slot painted as a solid grey block
     (visible in the first practice_ref-1600 plate). The rules are drawn by the
     cells' own outlines instead, which overlap across the 1px gap into one
     continuous hairline and leave an empty slot looking like paper. */
  background: var(--bs-sheet);
  border: 1px solid var(--bs-rule);
}
.ref-cell {
  background: var(--bs-sheet);
  outline: 1px solid var(--bs-rule);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 7px 5px 8px;
  text-align: center;
}
.ref-fig {
  width: 100%;
  max-width: 48px;
  height: 30px;
  /* currentColor so one rule serves both themes and no asset is shipped. */
  stroke: currentColor;
  fill: none;
  stroke-width: 1.4;
  color: var(--bs-ink-2);
}
.ref-name {
  font-family: var(--bs-mono);
  font-size: 8px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
}
.ref-eq {
  font-family: var(--bs-ed);
  font-size: 12px;
  font-style: italic;
  line-height: 1.45;
  color: var(--bs-ink);
}
.ref-frac { font-style: normal; }

/* ── the AP Biology sheet ──
   A formula sheet, not a shape sheet: no figures, longer expressions, and two
   real tables. So it lays out as stacked SECTIONS rather than the geometry
   grid, and reuses .ref-sheet's paper and scroll so both read as one
   instrument. `hidden` on the wrapper is what switches sheets — one tab, one
   sheet per exam (11a-calculator.js). */
.ref-sec { margin: 0 0 12px; }
.ref-sec:last-child { margin-bottom: 2px; }
.ref-sec-head {
  font-family: var(--bs-mono);
  font-size: 8.5px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  margin: 0 0 5px;
  padding-bottom: 3px;
  border-bottom: 1px solid var(--bs-rule);
}
.ref-row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  padding: 3px 0;
}
.ref-lbl {
  font-family: var(--bs-mono);
  font-size: 8px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--bs-ink-3);
  flex: 0 0 auto;
}
/* The equation is the thing being read, so it wins the space and the label
   gives way — a wrapped label is legible, a wrapped formula is not. */
.ref-row .ref-eq { text-align: right; flex: 1 1 auto; }
.ref-note {
  font-family: var(--bs-ed);
  font-size: 10.5px;
  line-height: 1.4;
  color: var(--bs-ink-2);
  margin: 4px 0 0;
}
/* A radical needs a bar over its content or `s = √Σ(x−x̄)²/(n−1)` is ambiguous
   about where the root ends. An overline is the whole job and costs no glyph. */
.ref-rad {
  border-top: 1px solid currentColor;
  padding: 0 2px;
  margin-left: -1px;
}
.ref-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--bs-ed);
  font-size: 10.5px;
  color: var(--bs-ink);
}
.ref-table th, .ref-table td {
  border: 1px solid var(--bs-rule);
  padding: 2px 3px;
  text-align: center;
}
.ref-table thead th { font-weight: 700; }
.ref-table tbody th { font-weight: 700; font-style: italic; }
.ref-table--prefix { font-size: 10px; }
.ref-table--prefix td { text-align: left; padding: 3px 5px; }
.ref-facts {
  margin: 9px 0 0;
  padding: 0 0 0 15px;
  font-size: 11px;
  line-height: 1.5;
  color: var(--bs-ink-2);
}
.ref-facts li + li { margin-top: 4px; }
.ref-facts b { color: var(--bs-ink); }

/* ── sheet, pinned to the foot of the window, at EVERY width ──
   The rail used to dock into .margin above 1500px wide, as a column of the
   page. Mark reversed that on 2026-08-20, driving the live site: the calculator
   "is showing in the right margin instead of below where i had asked". He is on
   a display wide enough to have hit the docked register (a 14" MBP is 1512
   logical), so the docked face was the only one he ever saw.

   Withdrawn with it: the media block that re-ordered .margin and widened the
   rail track to 384px, and TOOL_RAIL_MIN_PX in js/11a-calculator.js. Those two
   were required to agree, so they had to go together.

   The stem no longer has to yield anything for this, because the sheet is
   `position: fixed` over a column that already reserves room for it — hit-tested
   with elementFromPoint at six viewport sizes on 08-12, where every choice and
   every Submit returned itself.

   The cost, stated rather than hidden: the docked plate reviewed in the 08-17 UI
   walk no longer exists at any width, so qc_ui_walk.py's 1600 captures of the
   tool surfaces now show the sheet. The reversal is pinned by
   test_the_rail_is_a_bottom_sheet_at_every_width. */
.tool-rail {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 40;               /* above the sheet, below the track menu */
  width: 100%;
  /* The strip is chrome the student did not ask for, so it stays the height of
     one line of type and nothing more until a tool is picked. */
  box-shadow: 0 -2px 0 var(--bs-ink);
}
.tool-rail .tool-tabs { border-top: 1px solid var(--bs-ink); }
/* The rule would be a third line of chrome above the tabs down here. It is
   carried by the disabled tab's tooltip instead. */
.tool-rail .tool-rule { display: none; }
/* Shorter than the docked pane. At the docked height the sheet stood 531px
   tall in a 900px window and covered the ballot — a working surface that hides
   the question you are working on. Capped so the stem and the choices stay
   above it. */
.tool-rail { --tool-pane-h: min(300px, 36vh); }
.tool-rail .tool-body {
  background: var(--bs-stock);
  max-height: min(44vh, 360px);
  overflow-y: auto;
  padding: 10px 12px 14px;
}
#calc-host, .pad-input { box-shadow: 2px 2px 0 var(--bs-ink); border-top: 1px solid var(--bs-ink); }
/* The question must stay reachable with a sheet up, so the scroller gets the
   strip's height back as padding. */
#screen-practice.bs .practice-body { padding-bottom: 52px; }
/* This rule used to set `padding-bottom: 0` — it gave the padding back at
   exactly the moment the rail was TALLEST, which is upside down. It was
   survivable while the pad opened only on request: a student who opened it
   could close it again. It stopped being survivable when the scratchpad
   became open-by-default (walk dictation §2, "The scratchpad should be OPEN
   BY DEFAULT"), because now every student arrives with the sheet already up.

   MEASURED with elementFromPoint at the Submit button's own centre, 1440
   wide: at 900 and 1000 tall the button is hit; at 800 and 700 the hit test
   returns TEXTAREA.pad-input. The scratchpad was sitting on top of the
   primary action on any window shorter than ~850px — which is most laptops
   once browser chrome is taken off — and the student could not submit an
   answer without first minimising the pad they never opened.

   The bound is the rail's own ceiling, not its current height: `.tool-body`
   carries `max-height: min(44vh, 360px)` and scrolls internally past it, so
   the rail cannot exceed the tab strip (52) plus that cap plus the body's
   own 24px of vertical padding — true even after a student drags the grip,
   which changes --pad-h but never the cap. Padding a scroller is the right
   shape of fix here: it costs some empty travel at the foot of a long
   question and guarantees every control clears the sheet.

   THAT PADDING WAS NOT ENOUGH, and Mark saw why: it reserves room in the
   scroller's CONTENT, but `.practice-body` is itself the scroller
   (`overflow-y: auto`) and its BOX still runs behind a `position: fixed`
   rail. Content therefore renders underneath the sheet at any scroll offset
   that is not the very bottom. Measured at 1440x900 with the pad open: rail
   top 555, Submit bottom 564, question card bottom 633 — the card ran 78px
   under the sheet and the primary action overlapped it by 9.
   So the SCROLLER ends above the rail now (margin, not padding), using the
   rail's measured height published on the root by _syncRailHeight(), plus a
   12px gap so the two surfaces read as separate rather than touching. */
/* SELECTED FROM THE ROOT, not from the rail's ancestry. The previous selector
   was `#screen-practice.bs:has(.tool-rail.open) .practice-body`, which only
   matched while the rail was a DESCENDANT of the practice screen — and being a
   descendant is what made `show('screen-feedback')` hide it (2026-09-03; see
   _homeToolRail). `data-rail-open` is set on <html> by _syncRailHeight, so the
   rail can live outside every screen and both scrollers still reserve room. */
:root[data-rail-open] #screen-practice.bs .practice-body,
:root[data-rail-open] #screen-feedback.bs .feedback-body {
  margin-bottom: calc(var(--rail-h, 76px) + 12px);
  padding-bottom: 16px;
}
/* THE DAILY SURFACES RESERVE DIFFERENTLY, because the PAGE is their scroller
   rather than an inner box — a margin on `.coach-body` shortens nothing. They
   are not made inner scrollers on purpose: `test_page_wrapper_scrolling.py`
   exists because that trap has cost eleven screens, and `.practice-body` sits
   on its allowlist as "a known, not a cleared, defect" (254px invisible at
   320x568). So these buy SCROLL ROOM instead, and keyed on `data-rail-visible`
   rather than `-open` because the 28px tab strip alone clipped a choice by
   43px at 320x568. */
:root[data-rail-visible] #screen-daily .coach-body,
:root[data-rail-visible] #screen-general-daily .coach-body {
  padding-bottom: calc(var(--rail-h, 28px) + 24px);
}

/* ── readout ── */
/* The result sits ABOVE the entry, like a printing calculator's tape: what you
   are typing stays at the bottom under your hands. */
.calc-out {
  min-height: 22px;
  text-align: right;
  font-family: var(--bs-mono);
  font-size: 17px;
  font-weight: 600;
  color: var(--bs-ink);
  padding: 2px 6px 4px;
  overflow-x: auto;
  white-space: nowrap;
}
.calc-out.is-error {
  font-size: 11px;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--bs-pen);
}

.calc-input {
  width: 100%;
  background: var(--bs-stock);
  /* The same ruled writing line the answer field uses — this is where you write. */
  border: 1px solid var(--bs-rule);
  border-bottom: 2px solid var(--bs-ink);
  border-radius: 0;
  padding: 8px 10px;
  font-family: var(--bs-mono);
  font-size: 15px;
  color: var(--bs-ink);
  text-align: right;
}
.calc-input:focus { outline: none; border-bottom-color: var(--bs-blue); }

/* ── keypad ── */
.calc-pad {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 5px;
  margin-top: 8px;
}
.calc-actions {
  display: grid;
  grid-template-columns: 1fr 1fr 2fr;
  gap: 5px;
  margin-top: 5px;
}

.calc-key {
  background: var(--bs-sheet);
  border: 1px solid var(--bs-rule);
  border-radius: 0;
  padding: 9px 0;
  font-family: var(--bs-ui);
  font-size: 12px;
  font-weight: 600;
  color: var(--bs-ink);
  cursor: pointer;
  transition: background .1s ease, color .1s ease, border-color .1s ease;
}
/* Digits are the thing you hit most, so they get the figure face and more ink. */
.calc-key.is-num {
  font-family: var(--bs-mono);
  font-size: 15px;
  font-weight: 600;
  border-color: var(--bs-ink-3);
}
/* Hover/active are the designed states — the style lives in the interaction. */
.calc-key:hover { background: var(--bs-stock-2); border-color: var(--bs-ink); }
.calc-key:active { background: var(--bs-ink); color: var(--bs-stock); border-color: var(--bs-ink); }
.calc-key:focus-visible { outline: 2px solid var(--bs-blue); outline-offset: 1px; }

.calc-key.is-util {
  font-family: var(--bs-mono);
  font-size: 9px;
  letter-spacing: .14em;
  color: var(--bs-ink-2);
}
.calc-key.is-equals {
  background: var(--bs-blue);
  border-color: var(--bs-ink);
  color: var(--bs-sheet);
  font-size: 15px;
  font-weight: 700;
}
.calc-key.is-equals:hover { background: var(--bs-blue); border-color: var(--bs-ink); filter: brightness(0.92); }
.calc-key.is-equals:active { background: var(--bs-ink); color: var(--bs-stock); }

/* The toggle lives in the masthead alongside Dashboard / Question History and
   borrows their label mechanism (font-size:0 + content:attr(title)), so it
   reads as a word rather than a three-letter glyph. */
.bs #calc-btn { font-size: 0; }
.bs #calc-btn::before {
  content: attr(title);
  font-size: 10.5px;
}
#calc-btn[aria-expanded="true"] { opacity: 1; }

/* Desmos mount. It brings its own chrome, so it only needs a box with a height —
   the rail's tabs, border and letterpress offset still frame it. It shares the
   pad's height so switching tabs does not resize the rail under the student's
   hand; the old rule set its own 460px and the two panes disagreed by 40px. */
.calc-desmos { height: var(--tool-pane-h); }
#calc-wrap.open #calc-host:has(.calc-desmos) { padding: 0; }
