/* ==========================================================================
   Symbulator documentation
   Palette taken from the Symbulator Py interface, so the documentation and
   the software it documents read as one product. Amber survives from the
   TI-89 keypad as the single warm signal, reserved for warnings.
   ========================================================================== */

:root {
  /* Sampled from the Symbulator Py interface, so the documentation and the
     software it documents read as one product. Amber survives from the TI-89
     keypad as the single warm signal, reserved for warnings. */
  --navy:       #203864;   /* the header band                          */
  --accent:     #2f5fa8;   /* buttons, links, disclosure arrows        */
  --accent-bg:  #e9eff8;
  --accent-dk:  #24487e;
  --sky:        #8ec7f5;   /* the "Py" in the wordmark                 */
  --ink:        #1c2330;
  --ink-2:      #545d6d;
  --ink-3:      #858d9c;
  --paper:      #ffffff;
  --paper-2:    #f4f6fa;
  --rule:       #e2e5ea;
  --rule-2:     #eef1f6;
  --amber:      #b7791f;   /* warnings only: the one warm signal       */
  --amber-bg:   #fbf3e2;
  --gold:       #d9a521;   /* "new" -- see DESIGN.md; never a warning     */
  --gold-ink:   #1c2330;   /* fixed dark text on gold, in both themes     */
  --green:      #2f7d4f;
  --green-bg:   #ecf5ef;
  --danger:     #a13030;
  --danger-bg:  #fdf1f1;
  --answer:     #a13030;   /* a value the software returned            */
  --ui-card:    #24487e;   /* a card in the app (#357)                 */
  --ui-control: #0f6b5c;   /* a control inside a card (#357)           */
  --ui-button:  #c9614f;   /* a button you press (#361)               */
  --ui-tool:    #7c4d9a;   /* a tool you run (#369)                   */
  --lcd:        #f2f6fc;   /* the input panel, tinted toward the accent */
  --lcd-edge:   #dbe4f2;

  --sans: "IBM Plex Sans", system-ui, -apple-system, sans-serif;
  --serif: "IBM Plex Serif", Georgia, serif;
  --mono: "IBM Plex Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --measure: 34rem;
  --sidebar: 17rem;

  /* A surface that is always dark with always-light text, regardless of
     theme -- for the primary button and the skip-to-content link, which
     want a fixed dark chip rather than something that inverts with --ink.
     Equals --ink here, so light mode renders identically to before this
     token existed; dark mode (below) gives it its own value. */
  --inverse-bg: #1c2330;   /* equals --ink today; kept as a literal, like
                              every other token here, so tools/check_palette.py
                              can verify it against tokens.json the same way */
  --inverse-ink: #ffffff;
}

/* ---------------------------------------------------------------- dark --- */
/* Dark Mode -- the same switch as the Symbulator 9 interface's own
   #themeToggle: a data-theme="dark" attribute on <html>, applied before
   first paint from a saved preference (see the inline script in
   index.php's <head>) so there's no flash of the wrong theme on load.

   --navy and --sky are deliberately not overridden: the topbar stays navy
   in both themes, same as the interface's header, which is a fixed band
   rather than a themed one. */
html[data-theme="dark"] {
  --accent:     #5b96e0;
  --accent-bg:  #24344a;
  --accent-dk:  #7fb0e8;
  --ink:        #e4e8ee;
  --ink-2:      #97a3b6;
  --ink-3:      #6b7690;
  --paper:      #1b212c;
  --paper-2:    #12161d;
  --rule:       #2b3341;
  --rule-2:     #232a36;
  --amber:      #e3b567;
  --amber-bg:   #3a2f16;
  --gold:       #e8b93a;
  --gold-ink:   #1c2330;
  --green:      #7fd9a4;
  --green-bg:   #16301f;
  --danger:     #ff9c9c;
  --danger-bg:  #3a1f21;
  --answer:     #ff9c9c;
  --ui-card:    #7fb0e8;
  --ui-control: #5fd3bd;
  --ui-button:  #ffab91;
  --ui-tool:    #c9a3e8;
  --lcd:        #182534;
  --lcd-edge:   #2f5170;
  --inverse-bg:  #303c4e;
  --inverse-ink: #ffffff;
}

* { box-sizing: border-box; }

html { scroll-behavior: smooth; scroll-padding-top: 1.25rem; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation: none !important; transition: none !important; }
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--serif);
  font-size: 1.0625rem;
  line-height: 1.66;
  -webkit-font-smoothing: antialiased;
  transition: background-color .15s ease, color .15s ease;
}
@media (prefers-reduced-motion: reduce) { body { transition: none; } }

a { color: var(--ink); text-underline-offset: 0.18em; }
a:focus-visible, .vkey:focus-visible, .btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px;
}

.skip {
  position: absolute; left: -9999px;
  background: var(--inverse-bg); color: var(--inverse-ink); padding: 0.6rem 1rem;
}
.skip:focus { left: 1rem; top: 1rem; z-index: 100; }

/* ------------------------------------------------------------------ top -- */

/* The lockup -- navy band, logo, wordmark, second line, sky keyline, theme
   toggle -- lives in the shared banner.css (canonical in the app tree,
   Application/v9/repos/local/banner.css), one file shared with symbulator.com
   and the app. build.py copies it to assets/banner.css and index.php links it
   ahead of this file. Only this site's own controls are styled below: the two
   ribbon buttons and the version picker. Do not restate banner rules here --
   that duplication is exactly what the shared banner.css exists to end. */



/* version selector — a disclosure, so the options stay real links and the
   whole thing still works with JavaScript switched off */
.versions { position: relative; }

.versions summary {
  display: flex; align-items: center; gap: 0.45rem;
  padding: 0.42rem 0.7rem 0.46rem 0.85rem;
  list-style: none; cursor: pointer; white-space: nowrap; color: #fff;
  background: rgba(255, 255, 255, 0.1);
  border: 1px solid rgba(255, 255, 255, 0.28); border-radius: 8px;
  transition: background 0.12s ease;
}
.versions summary::-webkit-details-marker { display: none; }
.versions summary:hover { background: rgba(255, 255, 255, 0.2); }
.versions summary:focus-visible { outline: 2px solid var(--sky);
                                  outline-offset: 3px; }
.versions[open] summary { background: rgba(255, 255, 255, 0.2); }

/* 0.875rem matches .subbar nav a in the shared banner.css exactly: the picker
   sits in the same row as the links and should read at the same size. It
   keeps its border and background because it is a menu, not a link -- that
   affordance is the one thing in this ribbon that should look different. */
.vkey-full {
  font-family: var(--sans); font-weight: 600; font-size: 0.875rem;
  line-height: 1.1; letter-spacing: -0.005em;
}
.vkey-num { display: none; font-family: var(--sans); font-weight: 700;
            font-size: 1rem; line-height: 1.1; }
.chev { width: 0.85em; height: 0.85em; color: var(--sky); flex: none;
        transition: transform 0.14s ease; }
.versions[open] .chev { transform: rotate(180deg); }

.version-list {
  /* Hangs from the picker's left edge: on the controls ribbon the picker sits
     at the left, under the wordmark, rather than at the right of the bar. */
  position: absolute; left: 0; top: calc(100% + 0.4rem); z-index: 60;
  min-width: 15rem; margin: 0; padding: 0.3rem; list-style: none;
  background: var(--paper);
  border: 1px solid var(--rule); border-radius: 10px;
  box-shadow: 0 10px 28px rgba(22, 24, 26, 0.13);
}
.version-list a {
  display: block; padding: 0.5rem 0.7rem; border-radius: 6px;
  text-decoration: none; font-family: var(--sans);
}
.version-list a:hover { background: var(--paper-2); }
.version-list a.is-current { background: var(--accent-bg); }
.version-list .v-name { display: block; font-weight: 600; font-size: 0.9rem; }
.version-list a.is-current .v-name { color: var(--accent); }
.version-list .v-plat { display: block; font-size: 0.74rem; color: var(--ink-3); }

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

/* The ribbon's links are styled in the shared banner.css, shared with
   symbulator.com and the app -- see the note there. The .ribbon-btn chips
   that used to live here were removed on 23 Aug 2026: they were the most
   visible thing making this site look unrelated to the other two. Nothing
   replaced them here, deliberately. If a control needs styling, ask first
   whether the other two properties want it as well.

   Still local, because only this site has them: .versions and its dropdown,
   below. */

/* Which link gives way on a phone (#257, Roberto, 4 Sep 2026). The shared
   banner.css caps the ribbon's nav at one line and clips whatever wraps,
   so the LAST link in flex order is the one that goes -- and here that was
   Split View, the link a phone reader is most likely to want, while
   Download as PDF, the least useful thing on a phone, stayed. The ribbon
   held all three down to about 400px of viewport and then dropped Split
   View at 375px.

   Reversing the DOM would fix the priority but also the tab order and
   what a screen reader announces first. Instead the nav lays out in
   row-reverse -- so flex order runs right to left across the screen --
   and each link is given an explicit order that reverses the DOM again:
   the first child sits at the far right of the nav's flex order and the
   last child at the far left. Visually the row still reads Download as
   PDF, Online App, Split View (or How it works, on 7 and 8), left to
   right; in flex order the PDF link is last, so it is the one that wraps
   and is clipped. Nothing about *when* a link hides changed -- that is
   still the shared one-line clip, measured against real widths, not a
   media query -- only *which* link.

   justify-content is flex-end because in row-reverse main-end is the
   left edge: without it the links would pack against the version picker.
   The orders are explicit rather than a step so a fourth link, arriving
   with order 0, is a visible mistake (it lands at the right, rather than
   silently taking the PDF link's place as the first to hide). Measured
   live with the rule injected, 4 Sep 2026: at 320 and 375px the PDF link
   is hidden and the other two show; at 430px and above all three show
   in the usual order. */
.subbar nav { flex-direction: row-reverse; justify-content: flex-end; }
.subbar nav > :nth-child(1) { order: 3; }
.subbar nav > :nth-child(2) { order: 2; }
.subbar nav > :nth-child(3) { order: 1; }

/* ---------------------------------------------------------------- shell -- */

.shell {
  display: grid;
  grid-template-columns: var(--sidebar) minmax(0, 1fr);
  gap: clamp(1.5rem, 4vw, 4rem);
  /* Same track as the banner, so the sidebar's left edge lines up with the
     logo above it. Both come from the shared banner.css. */
  width: min(var(--banner-w), 100%);
  margin: 0 auto;
  padding: 2.5rem var(--banner-pad) 4rem;
}
body.home .shell { grid-template-columns: minmax(0, 1fr); }
body.home .sidebar { display: none; }

/* -------------------------------------------------------------- sidebar -- */

.sidebar {
  position: sticky; top: 2rem;
  align-self: start;
  max-height: calc(100vh - 4rem);
  overflow-y: auto;
  font-family: var(--sans);
  font-size: 0.875rem;
}

/* Scrollbars, themed (#79).
   -------------------------------------------------------------------------
   Left alone, the contents list gets the browser's default scrollbar: a pale
   grey trough that looks fine on the light theme and glares on the dark one,
   because it is drawn by the browser and knows nothing about the palette.

   Two mechanisms, because no single one covers every browser. scrollbar-color
   is the standard property -- Firefox has had it for years, Chrome since 121 --
   and ::-webkit-scrollbar is the older WebKit and Blink way. Both are given
   the same two tokens, so they stay in step, and both invert with the theme
   for free because --rule and --ink-3 already do.

   Thin rather than full width, and the track is transparent rather than a
   painted trough: the bar should read as an edge to the column, not as a
   piece of furniture. */
.sidebar,
.code pre,
.chapter .table-wrap {
  scrollbar-width: thin;
  scrollbar-color: var(--rule) transparent;
}
.sidebar::-webkit-scrollbar,
.code pre::-webkit-scrollbar,
.chapter .table-wrap::-webkit-scrollbar {
  width: 9px; height: 9px;
}
.sidebar::-webkit-scrollbar-track,
.code pre::-webkit-scrollbar-track,
.chapter .table-wrap::-webkit-scrollbar-track {
  background: transparent;
}
.sidebar::-webkit-scrollbar-thumb,
.code pre::-webkit-scrollbar-thumb,
.chapter .table-wrap::-webkit-scrollbar-thumb {
  background: var(--rule);
  border-radius: 999px;
  /* A transparent border inset by the background keeps the thumb clear of
     the column edge without a second element to do it. */
  border: 2px solid transparent;
  background-clip: content-box;
}
.sidebar:hover, .code pre:hover, .chapter .table-wrap:hover {
  scrollbar-color: var(--ink-3) transparent;
}
.sidebar::-webkit-scrollbar-thumb:hover,
.code pre::-webkit-scrollbar-thumb:hover,
.chapter .table-wrap::-webkit-scrollbar-thumb:hover {
  background: var(--ink-3);
  background-clip: content-box;
}
/* Contents is a disclosure, so it collapses without JavaScript and the
   chapter list can be folded away on a narrow screen or a long read. The
   small script after the nav restores the reader's last choice; nothing
   here depends on it running. */
.toc > summary.sidebar-title {
  display: flex; align-items: center; gap: 0.4rem;
  margin: 0 0 0.9rem;
  font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
  list-style: none; cursor: pointer;
  border-radius: 6px;
}
.toc > summary.sidebar-title::-webkit-details-marker { display: none; }
.toc > summary.sidebar-title:hover { color: var(--accent); }
.toc > summary.sidebar-title:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 4px;
}
.toc-chev {
  width: 0.9em; height: 0.9em; flex: none;
  transform: rotate(-90deg);
  transition: transform 0.14s ease;
}
.toc[open] > summary .toc-chev { transform: rotate(0deg); }
@media (prefers-reduced-motion: reduce) { .toc-chev { transition: none; } }

/* Folded away, Contents should not go on holding a 17rem column open. */
/* #396: scoped away from the pages that have no sidebar. `:has()` takes
   its argument's specificity, so this selector is (0,3,0) and outranked
   `body.home .shell` (0,2,1) -- the rule that collapses the layout to one
   column. On a home or shelf page `main` therefore landed in the
   `max-content` track and sized to the card grid's own 66rem, 62px wider
   than the shell's content box, and the page scrolled sideways by 19px.
   Only when the reader's Contents panel was closed, which is what this
   tests, which is why it was intermittent and long-lived. */
body:not(.home) .shell:has(.toc:not([open])) {
  grid-template-columns: max-content minmax(0, 1fr);
}

/* ---------------------------------------------------------- search (#87) --
   The one piece of furniture only this site has, so it is styled here and
   not in the shared banner.css, which the landing page and the app share.

   It sits at the top of the sidebar rather than in the banner for that
   reason, and it searches the version being read and nothing else: the
   index is written per version by build.py, and the page fetches the one
   for its own version. A version 8 reader is never shown a hit that only
   exists in 9. */
.docsearch { margin: 0 0 1rem; }
.docsearch label {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  clip-path: inset(50%); overflow: hidden; white-space: nowrap;
}
.docsearch input[type="search"] {
  width: 100%; box-sizing: border-box;
  padding: 0.42rem 0.6rem;
  font-family: var(--sans); font-size: 0.84rem;
  color: var(--ink);
  background: var(--lcd);
  border: 1px solid var(--lcd-edge);
  border-radius: 7px;
}
.docsearch input[type="search"]::placeholder { color: var(--ink-3); }
/* Focused, this has to read as one edge. With an offset the browser draws
   three: the 1px --lcd-edge border, a 1px ring of page background, then the
   2px accent outline -- and in dark mode the border and the accent are both
   blues, so the gap between them looks like a seam rather than a focus ring.
   Offset 0 closes the gap, and colouring the border to match turns the
   remaining 3px into a single band that follows the corner radius. */
.docsearch input[type="search"]:focus-visible {
  border-color: var(--accent);
  outline: 2px solid var(--accent); outline-offset: 0;
}
.docsearch-status {
  margin: 0.5rem 0 0;
  font-size: 0.76rem; color: var(--ink-3);
}
.docsearch-results { list-style: none; margin: 0.4rem 0 0; padding: 0; }
.docsearch-results li + li { border-top: 1px solid var(--rule-2); }
.docsearch-results a {
  display: block; padding: 0.45rem 0;
  color: inherit; text-decoration: none;
}
.docsearch-results a:hover .docsearch-what,
.docsearch-results a:focus-visible .docsearch-what { color: var(--accent); }
.docsearch-where {
  display: block;
  font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-3);
}
.docsearch-what { display: block; font-size: 0.84rem; color: var(--ink); }
.docsearch-snip { display: block; font-size: 0.78rem; color: var(--ink-2); }
.docsearch-snip mark {
  padding: 0 1px; border-radius: 2px;
  background: var(--gold); color: var(--gold-ink);
}

/* Kept for the plain <p class="sidebar-title"> the print stylesheet uses. */
.sidebar-title {
  margin: 0 0 0.9rem;
  font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--ink-3);
}
.chapters, .sections { list-style: none; margin: 0; padding: 0; }
.chapters > li { border-left: 2px solid var(--rule-2); padding: 0.32rem 0 0.32rem 0.85rem; }
.chapters > li.is-current { border-left-color: var(--accent); }
.chapters a { text-decoration: none; display: block; }
.chapters a:hover .ch-title { text-decoration: underline; }
.ch-eyebrow {
  display: block; font-size: 0.64rem; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
.ch-title { display: block; line-height: 1.35; }
.chapters > li.is-current > a .ch-title { font-weight: 600; }
.chapters > li.is-absent .ch-title { color: var(--ink-3); }
.chapters > li.is-absent .ch-title::after {
  content: "not in this version";
  display: block; font-size: 0.64rem; color: var(--amber);
}
.sections { margin: 0.45rem 0 0.2rem; }
.sections li { padding: 0.16rem 0; }
.sections a { color: var(--ink-2); font-size: 0.82rem; }
.sections .secno { color: var(--ink-3); margin-right: 0.4rem; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------- prose --- */

.chapter { max-width: var(--measure); }
.chapter-head { margin-bottom: 2.25rem; }
.eyebrow {
  margin: 0 0 0.35rem;
  font-family: var(--sans); font-size: 0.7rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--accent);
}
.chapter h1 {
  margin: 0;
  font-family: var(--sans); font-weight: 700;
  font-size: clamp(1.9rem, 4vw, 2.5rem); line-height: 1.1;
  letter-spacing: -0.025em;
}
.lede {                       /* standfirst: the "what to expect" paragraph */
  margin: 1.1rem 0 0;
  font-family: var(--sans); font-weight: 400;
  font-size: clamp(1.15rem, 1.9vw, 1.3rem); color: var(--ink-2);
  line-height: 1.45; letter-spacing: -0.011em;
  max-width: 30rem;
}
.updated {
  margin: 1rem 0 0;
  font-family: var(--sans); font-size: 0.72rem; color: var(--ink-3);
  letter-spacing: 0.04em;
}

.chapter h2 {
  margin: 3rem 0 0.6rem;
  font-family: var(--sans); font-weight: 700; font-size: 1.45rem;
  letter-spacing: -0.015em; line-height: 1.25;
  padding-top: 1.2rem; border-top: 1px solid var(--rule);
}
.chapter h2 .secno {
  color: var(--accent); margin-right: 0.55rem; font-variant-numeric: tabular-nums;
}
/* A subsection heading must not be smaller than the text it introduces.
   At 1.02rem it measured 16.32px against a 17px paragraph -- smaller, and
   in --ink-2 lighter as well, which together read as a caption rather than
   a heading. 1.15rem puts it a clear step above body text and still well
   below h2 at 23.2px. Reported 24 Aug 2026. */
.chapter h3 {
  margin: 2rem 0 0.4rem;
  font-family: var(--sans); font-weight: 600; font-size: 1.15rem;
  color: var(--ink);
}
.chapter p { margin: 0 0 1.05rem; }

/* ---------------------------------------------------------------- table -- */
/* Quiet: one rule under the header and a hairline between rows. A grid of
   boxes would out-shout the prose it sits in, and every table in this book
   is a short key against a line of text -- the textbook codes in the
   credits, the answer tables in the lessons. */
.chapter .table-wrap { overflow-x: auto; margin: 0 0 1.3rem; }
.chapter table {
  border-collapse: collapse;
  width: 100%;
  font-family: var(--sans); font-size: 0.92rem;
  line-height: 1.45;
}
.chapter th {
  text-align: left; vertical-align: bottom;
  padding: 0 0.9rem 0.45rem 0;
  font-size: 0.7rem; letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--ink-3); font-weight: 600;
  border-bottom: 1px solid var(--rule);
  white-space: nowrap;
}
.chapter td {
  vertical-align: baseline;
  padding: 0.5rem 0.9rem 0.5rem 0;
  border-bottom: 1px solid var(--rule-2);
}
.chapter tr > *:last-child { padding-right: 0; }
/* The key column names the thing; it should not wrap mid-code. */
.chapter td:first-child { white-space: nowrap; }

/* ------------------------------------------------------------ quotation -- */
/* Set in the serif, a size up from the prose and in the softer ink: a
   quotation is someone else speaking, so it should look like a different
   voice rather than an indented paragraph. */
blockquote {
  margin: 1.5rem 0;
  padding-left: 1.1rem;
  border-left: 3px solid var(--rule);
  font-family: var(--serif);
  color: var(--ink-2);
}
blockquote p { margin: 0 0 0.5rem; }
/* The attribution. Smaller and quieter than the words, with the name the
   one thing in it set in full ink -- so the eye finds who said it without
   the affiliation competing. */
blockquote .quote-by {
  margin: 0;
  font-family: var(--sans); font-size: 0.82rem;
  color: var(--ink-3);
}
blockquote .quote-by strong {
  color: var(--ink); font-weight: 600;
}
.chapter ul, .chapter ol { margin: 0 0 1.05rem; padding-left: 1.25rem; }
.chapter li { margin-bottom: 0.35rem; }

code {
  /* 0.92em, not 0.86 (#275, Roberto, 6 Sep 2026): the mono read small
     beside the prose, in the field panels most of all. Same step in the
     panels below and in the PDF class. */
  font-family: var(--mono); font-size: 0.92em;
  background: var(--paper-2); border: 1px solid var(--rule-2);
  border-radius: 4px; padding: 0.08em 0.32em;
  word-break: break-word;
}
/* a value the software returned, as distinct from one you typed */
.ans { color: var(--answer); font-weight: 600; font-family: var(--sans);
       font-size: 0.95em; }
/* an answer that is a symbolic expression: same answer colour, but set as
   mathematics (KaTeX) rather than bold text -- build.py's answer_math
   decides which form each {{o:...}} gets */
.ans-math { font-weight: 400; }
/* A problem's own variable -- {{var:I_s}} (#261): the family's SemiBold
   Italic, 600, chosen by Roberto from a strip of five weights (#271). Not
   <strong><em>: that asked for weight 700 italic, a cut the page never
   loaded, so browsers slanted the semibold roman -- the "chubby" mark he
   saw. index.php now loads ital,wght 1,600, so this is the real cut. */
em.var { font-weight: 600; font-style: italic; }
/* #357: the app's own vocabulary, in two tiers -- the card the reader is
   sent to, and the control inside it. Same weight the plain **bold** had,
   so only the colour changes; the body face is kept, unlike .ans, because
   these are words in the sentence rather than a value standing apart. */
.ui-card { color: var(--ui-card); font-weight: 600; }
.ui-ctl  { color: var(--ui-control); font-weight: 600; }
/* #361: a button you press, as against a field you set. Warm on purpose
   -- the blue and teal tiers are places and settings, this one is an
   action. Salmon is Roberto's pick of 9 Sep 2026 from a rendered strip,
   made knowing the risk: in dark mode --answer is #ff9c9c and this is
   #ffab91, so a returned value and a button are close there. Kept apart
   by weight and by company -- an answer is set in the sans face, a button
   is in the body text -- and by never sitting inside one another. If it
   ever does read as one thing, this token is the single place to move. */
.ui-btn  { color: var(--ui-button); font-weight: 600; }
/* #369: an analysis or a little tool -- dc, ac, pr, th, er. The book
   set these bold, which is also what a node name and an element name
   wear, so `run **dc** on **r1**` said the two were the same kind of
   word. Violet: the fourth hue, clear of the blue places, the teal
   settings, the salmon buttons and the red answers. */
.ui-tool { color: var(--ui-tool); font-weight: 600; }
.ans-math .katex { font-size: 1.06em; }

.xref { color: var(--accent); text-decoration-color: rgba(15, 107, 92, 0.4); }
.ix { scroll-margin-top: 6rem; }

/* ---------------------------------------------------------------- code --- */

/* Radii converged toward the app's softer treatment (DESIGN.md §6.4),
   scaled a step down for a document page: 6px on panels, 8px on cards. */
.code {
  position: relative;
  margin: 0 0 1.1rem;
  background: var(--lcd);
  border: 1px solid var(--lcd-edge);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  padding: 0.85rem 0.9rem 0.75rem;
}
.code.out { border-left-color: var(--ink-3); background: var(--paper-2);
            border-color: var(--rule); }
/* A listing that is neither typed nor returned -- the contents of a file,
   say. Quiet, and unlabelled, because there is nothing true to call it:
   "type" would be a lie and "returns" says Symbulator produced it. */
.code.plain { border-left-color: var(--rule); background: var(--paper-2);
              border-color: var(--rule); }
/* An address to share (#259, Roberto, 4 Sep 2026): a URL on a line of its
   own, centred, on the input panel's tint -- the ::: address directive.
   The same panel as a typed fence, without the accent rule down its left,
   because nothing here is typed: it is handed over. overflow-wrap so a
   long query string breaks inside the panel on a phone rather than
   pushing the column wide. */
.address {
  margin: 0 0 1.1rem; padding: 0.7rem 0.9rem;
  background: var(--lcd); border: 1px solid var(--lcd-edge);
  border-radius: 6px; text-align: center;
  font-family: var(--mono); font-size: 0.95rem; line-height: 1.55;
  overflow-wrap: anywhere;
}
.address a { color: var(--accent); text-decoration: none; }
.address a:hover { text-decoration: underline; }
.code-label {
  position: absolute; top: 0; right: 0.5rem;
  transform: translateY(-52%);
  background: var(--paper); padding: 0 0.4rem;
  font-family: var(--sans); font-size: 0.6rem; font-weight: 600;
  letter-spacing: 0.13em; text-transform: uppercase; color: var(--ink-3);
}
.code pre { margin: 0; overflow-x: auto; }

/* A field of the Symbulator 9 interface, and what the reader types into it.
   -------------------------------------------------------------------------
   Version 9 has no command line. Everything is entered into a named box, so
   the documentation shows the box: the label is the field's real name as it
   appears on screen, sitting above the panel the way the interface puts it
   above its input, rather than floating on the rule like TYPE and RETURNS.

   Deliberately not styled as a code block. A reader who has the app open
   should be able to glance between page and screen and see the same object.
   The tokens are the documentation's own -- matching the interface exactly
   would mean importing its palette, and these are within a shade of it. */
.code.field {
  background: var(--paper);
  border: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
  border-radius: 8px;
  padding: 1.5rem 0.9rem 0.7rem;
}
.code.field .code-label {
  top: 0.5rem; left: 0.85rem; right: auto;
  transform: none; background: none; padding: 0;
  text-transform: none; letter-spacing: 0.01em;
  font-size: 0.72rem; font-weight: 400;   /* plain, like the result panel (Roberto, 6 Sep) */ color: var(--ink-2);
}
/* One element per line is the point, so lines must not be re-wrapped: a
   wrapped element would read as two. */
.code.field code { white-space: pre; }
/* #313 (Roberto, 5 Sep 2026): copy what the box shows, in one click, for
   pasting into the app's field. Sits where the type/returns label sits on
   the other panels, in the field's own label row. */
.code.field .copy {
  position: absolute; top: 0.38rem; right: 0.6rem;
  font-family: var(--sans); font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.04em; line-height: 1.4;
  padding: 0.05rem 0.6rem; border-radius: 999px;
  border: 1px solid var(--rule); background: var(--paper-2);
  color: var(--accent); cursor: pointer;
}
.code.field .copy:hover, .code.field .copy:focus-visible {
  background: var(--accent-bg); border-color: var(--accent);
}
.code.field .copy.done { color: var(--ink-2); background: var(--paper); }
@media print { .code.field .copy { display: none; } }

/* A card answer as the app shows it (#276, Roberto, 6 Sep 2026): the
   label the Results card uses -- current through, resistance seen -- over
   the typeset answer, on the returned-output panel. The label sits inside
   at the top left, the way a field's name does; the panel's colours are
   .code.out's, so it follows the page's mode like every other panel. */
.code.result {
  background: var(--paper-2); border: 1px solid var(--rule);
  border-left: 3px solid var(--ink-3); border-radius: 6px;
  padding: 1.5rem 0.9rem 0.4rem;
}
.code.result .code-label {
  top: 0.5rem; left: 0.85rem; right: auto;
  transform: none; background: none; padding: 0;
  text-transform: none; letter-spacing: 0.01em;
  font-size: 0.72rem; font-weight: 400; color: var(--ink-2);   /* plain, not bold (Roberto, 6 Sep) */
}
.code.result .mathblock { margin: 0.2rem 0 0.6rem; }


.code code {
  background: none; border: 0; padding: 0;
  font-size: 0.95rem; line-height: 1.55; white-space: pre-wrap;
  word-break: break-word;
}

/* ------------------------------------------------------------ callouts --- */
/* Icon plus word, so the four kinds survive greyscale printing and don't rely
   on colour alone to be told apart. */

.callout {
  margin: 0 0 1.2rem;
  padding: 0.85rem 1.1rem 0.55rem;
  border-left: 3px solid var(--ink-3);
  background: var(--paper-2);
  border-radius: 0 6px 6px 0;
  /* Named for the callout, not `--accent`: setting `--accent` here shadowed
     the page's blue inside every callout, which is why `note` could not
     simply ask for it (#188). */
  --callout-accent: var(--ink-3);
}
.callout p:last-child { margin-bottom: 0.4rem; }

.callout-kind {
  display: flex; align-items: center; gap: 0.4rem;
  margin: 0 0 0.35rem !important;
  color: var(--callout-accent);
  font-family: var(--sans); font-weight: 600; font-size: 0.66rem;
  letter-spacing: 0.13em; text-transform: uppercase;
}
.callout-icon { width: 1em; height: 1em; flex: none; font-size: 1.15rem; }

.callout-title {
  margin: 0 0 0.4rem !important;
  font-family: var(--sans); font-weight: 600; font-size: 0.98rem;
  color: var(--ink);
}

/* `note` is the plainest of the four and used to have no rule at all, so it
   fell through to the neutral default above and read grey. It is the same
   blue as the links and the section numbers now (#188, Roberto). */
.callout.note    { --callout-accent: var(--accent);
                   border-left-color: var(--accent);
                   background: var(--accent-bg); }
.callout.tip     { --callout-accent: var(--green);  border-left-color: var(--green);
                   background: var(--green-bg); }
.callout.warning { --callout-accent: var(--amber);  border-left-color: var(--amber);
                   background: var(--amber-bg); }
.callout.danger  { --callout-accent: var(--danger); border-left-color: var(--danger);
                   background: var(--danger-bg); }
.callout.absent  { --callout-accent: var(--amber);  border-left-color: var(--amber);
                   background: var(--amber-bg); }

/* In print the tints wash out, so lean on the rule and the label instead. */
@media print {
  .callout { background: none; border-left-width: 2px; }
}

/* ------------------------------------------------- problems and figures -- */

.problem {
  margin: 1.6rem 0;
  padding: 1.2rem 1.3rem 0.6rem;
  border: 1px solid var(--rule);
  border-radius: 8px;
  /* the app's card shadow — problems are the one card-like thing here */
  box-shadow: 0 1px 2px rgba(16, 24, 40, 0.04), 0 3px 10px rgba(16, 24, 40, 0.05);
}
.problem-title {
  margin: 0 0 0.7rem !important;
  /* #368: a point and a half taller than body text, not a heading --
     the title names the example and the reader scans for it. */
  font-family: var(--sans); font-weight: 600; font-size: 1.125rem;
}
/* #224: the links that open a worked problem in the app. Sized and
   coloured as a caption rather than as body text -- a problem carries up
   to four of these, and at body weight they would read as part of the
   question. Directly under the title, which is where the reader decides
   whether to run this one. */
.problem-links {
  margin: -0.3rem 0 0.9rem !important;
  display: flex; flex-wrap: wrap; gap: 0.35rem 1rem;
  font-family: var(--sans); font-size: 0.78rem; line-height: 1.5;
}
.applink-row { display: inline-flex; align-items: baseline; gap: 0.55rem; }
.applink-tag {
  font-size: 0.68rem; font-weight: 600; letter-spacing: 0.1em;
  text-transform: uppercase; color: var(--ink-3);
}
.problem-links a { color: var(--accent); text-decoration: none; }
.problem-links a:hover, .problem-links a:focus-visible { text-decoration: underline; }
.problem-links .splitlink { color: var(--ink-3); }

/* Embedded as the left pane of /split/. The banner and the sidebar are
   navigation for a page that is being read on its own; in a pane they
   are chrome around chrome, and the column they cost is the reason
   someone opened the split view. "Open in split view" goes too -- it
   points at where the reader already is. */
html.embedded .topbar,
html.embedded .subbar,
html.embedded .sidebar,
html.embedded .splitlink { display: none; }
html.embedded .shell { grid-template-columns: minmax(0, 1fr); }
html.embedded #main { padding-top: 1.2rem; }
/* The pane scrolls to a problem on request, and a heading flush with
   the top edge reads as cut off. */
html.embedded .problem { scroll-margin-top: 0.8rem; }

.answer { margin-top: 1.1rem; padding-top: 0.9rem; border-top: 1px dashed var(--rule); }
.answer-title {
  margin: 0 0 0.6rem !important;
  font-family: var(--sans); font-weight: 600; font-size: 0.68rem;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
}
.practice { margin-top: 2rem; }
.practice-title {
  margin: 0 0 1rem;
  font-family: var(--sans); font-weight: 600; font-size: 1.05rem;
}

figure { margin: 1.4rem 0; }
figure img { max-width: 100%; height: auto; display: block; margin: 0 auto; }
/* The circuit diagrams are line art on a transparent background, drawn to
   be read on paper -- black ink on the dark page would nearly disappear.
   Rather than recolour the SVGs, dark mode frames each one in a small
   white panel, the way a printed figure would look pasted onto a dark
   page. Light mode is untouched. */
html[data-theme="dark"] figure img {
  background: #fff; border-radius: 8px; padding: 0.75rem;
}
/* #256: on a phone a problem card's 1.3rem side padding is a fifth of the
   column, so a figure that wants the width may take it -- out to the card's
   inner edge, not past its border. The image is still capped at 100% of
   the figure box by the rule above. */
@media (max-width: 40rem) {
  .problem figure { margin-left: -1.3rem; margin-right: -1.3rem; }
}
figcaption {
  margin-top: 0.55rem; text-align: center;
  font-family: var(--sans); font-size: 0.78rem; color: var(--ink-3);
}
/* ::: photos -- a set of photographs the reader flips through, one at a
   time (Roberto, 16 Sep 2026). The PDFs print the first alone, so this is
   web-only furniture. The stage holds every photo stacked, one shown; it
   keeps the height of the tallest so the page does not jump on a flip.
   A photograph is not line art, so it takes none of the dark-mode white
   framing the circuit figures get above. */
figure.photos .photo-stage {
  display: grid; justify-items: center;
}
/* Bounded in BOTH directions, because the set mixes orientations: the
   1999 photograph is a portrait and the 2004 one a landscape, and a
   width cap alone would make the portrait tower over it. Each photo
   scales to fit inside the box on whichever side binds first, so the
   two carry the same visual weight and the stage barely changes height
   on a flip. */
figure.photos .photo-stage img {
  grid-area: 1 / 1; border-radius: 8px; align-self: center;
  max-width: min(100%, 24rem); max-height: 20rem;
  width: auto; height: auto;
}
/* `figure img { display: block }` above is an AUTHOR rule, and an author
   display beats the browser's own `[hidden] { display: none }` -- so
   setting `img.hidden` in the script would leave every photo stacked on
   screen. That is #249, which bit the app three times; the guard is
   explicit here rather than trusted. */
figure.photos .photo-stage img[hidden],
figure.photos .photo-cap[hidden] { display: none !important; }
html[data-theme="dark"] figure.photos img { background: none; padding: 0; }
figure.photos .photo-nav {
  display: inline-flex; align-items: center; gap: 0.3rem;
  margin-left: 0.6rem; vertical-align: middle;
}
figure.photos .photo-step,
figure.photos .photo-dot {
  border: 0; background: none; padding: 0; cursor: pointer;
  color: var(--accent); font-family: var(--sans);
}
figure.photos .photo-step {
  font-size: 1.15rem; line-height: 1; padding: 0 0.15rem;
}
figure.photos .photo-step:hover { color: var(--accent-dk); }
figure.photos .photo-dot {
  width: 7px; height: 7px; border-radius: 50%;
  background: var(--rule); transition: background 0.15s;
}
figure.photos .photo-dot.on { background: var(--accent); }
figure.photos .photo-dot:hover { background: var(--accent-dk); }
figure.photos .photo-step:focus-visible,
figure.photos .photo-dot:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

.mathblock { margin: 1.2rem 0; overflow-x: auto; }

/* ---------------------------------------------------------------- home --- */

.chapter-cards {
  list-style: none; margin: 0 auto; padding: 0; max-width: 66rem;
  display: grid; gap: 1px;
  /* Six columns, and a card spans two, three or six (#381). Six divides
     by both three and two, which is what lets the last row always fill:
     a lone card spans six, a pair spans three each. `auto-fill` left the
     remainder of a partial row as empty track, and since the grid paints
     `--rule` behind the 1px gaps, an empty cell showed as a grey block. */
  grid-template-columns: repeat(6, 1fr);
  background: var(--rule); border: 1px solid var(--rule);
}
/* One up, the narrow default. */
.chapter-cards > li { grid-column: span 6; }
/* Two up. A range, not a min-width: the orphan rule below must not
   survive into the three-up layout, where fifteen cards end on an odd
   child and would have stretched a row that was already full. */
@media (min-width: 34rem) and (max-width: 50.99rem) {
  .chapter-cards > li { grid-column: span 3; }
  .chapter-cards > li:last-child:nth-child(2n + 1) { grid-column: span 6; }
}
/* Three up. One left over spans the row; two left over split it. */
@media (min-width: 51rem) {
  .chapter-cards > li { grid-column: span 2; }
  .chapter-cards > li:last-child:nth-child(3n + 1) { grid-column: span 6; }
  .chapter-cards > li:nth-last-child(2):nth-child(3n + 1),
  .chapter-cards > li:last-child:nth-child(3n + 2) { grid-column: span 3; }
}
.chapter-cards li { background: var(--paper); }
.chapter-cards a {
  display: block; height: 100%; padding: 1.4rem 1.4rem 1.6rem;
  text-decoration: none;
}
.chapter-cards a:hover { background: var(--paper-2); }
.card-eyebrow {
  margin: 0 0 0.4rem;
  font-family: var(--sans); font-size: 0.66rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--accent);
}
.chapter-cards h2 {
  margin: 0 0 0.5rem;
  font-family: var(--sans); font-size: 1.1rem; font-weight: 600;
  letter-spacing: -0.01em;
}
.card-summary { margin: 0; font-size: 0.92rem; color: var(--ink-2); line-height: 1.5; }
.chapter-cards li.is-absent .card-eyebrow { color: var(--ink-3); }

/* ---------------------------------------------------------- #395 ----
   The three books, on version 9's home page. Each is a paragraph of its
   own and a cover beneath it; the chapters live on the shelf pages.

   The covers are one object repeated, not three designs -- same shape,
   type and spacing, differing only in the stripe and the name. Three
   different-looking cards would read as three different kinds of thing,
   when the reader's job here is to compare three of the same thing. */
.shelves {
  max-width: 66rem; margin: 0.6rem auto 0;
  display: grid; gap: 2.6rem 2.2rem;
  /* #456: four books, two by two -- three across left the fourth alone
     on a row of its own. `min(24rem, 100%)`: a phone in portrait is
     narrower than 24rem, and a bare 24rem minimum pushed every cover
     past the screen's edge (Roberto, 16 Sep 2026). */
  grid-template-columns: repeat(auto-fit, minmax(min(24rem, 100%), 1fr));
}
.shelf { display: flex; flex-direction: column; }
.shelf-lede {
  font-family: var(--sans); font-weight: 600; font-size: 1rem;
  color: var(--ink); margin: 0 0 0.45rem;
}
.shelf-body { margin: 0 0 1.1rem; color: var(--ink-2); flex: 1 1 auto; }

.cover {
  display: flex; flex-direction: column; justify-content: flex-end;
  min-height: 13rem; padding: 1.5rem 1.4rem 1.3rem;
  background: var(--paper-2);
  border: 1px solid var(--rule); border-top: 4px solid var(--accent);
  text-decoration: none; color: inherit;
  transition: background .12s ease, border-color .12s ease;
}
.cover:hover { background: var(--paper); border-color: var(--accent); }
.cover:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.cover-the {
  font-family: var(--sans); font-size: 0.72rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-2);
}
.cover-name {
  font-family: var(--serif); font-size: 1.9rem; line-height: 1.12;
  /* #398: --ink, not --navy. `--navy` is deliberately the same in both
     themes (it is the topbar's fixed band), so it was 1.56:1 on the dark
     cover ground -- unreadable. --ink is 14.74:1 there and is what the
     chapter-card headings already use; the stripe above the cover carries
     the colour that tells the three books apart. */
  color: var(--ink); margin: 0.15rem 0 0.7rem; text-wrap: balance;
}
.cover-meta {
  /* #398: --ink-3 measured 3.99:1 in dark and 3.09:1 in light, which
     fails for text this size in either theme. */
  font-family: var(--sans); font-size: 0.78rem; color: var(--ink-2);
  border-top: 1px solid var(--rule); padding-top: 0.6rem;
}
/* The stripe is the only thing that differs, and it is the one place the
   three are told apart at a glance. */
.cover-manual { border-top-color: var(--ui-tool); }
.cover-notes  { border-top-color: var(--ink-3); }
.cover-samplers { border-top-color: var(--ui-control); }

/* ---------------------------------------------------------- #390 ----
   The chooser: two books on one site, at the top of the version 9 home
   page. Deliberately not cards -- border, fill and radius each say
   "separate object", and these are a question and its two answers, not
   two more chapters. In cards they would read as the first two entries
   of the grid below them. */
.chooser {
  max-width: 66rem; margin: 0.4rem auto 0; padding-bottom: 0.4rem;
  display: grid; gap: 1.4rem 2.5rem;
  grid-template-columns: repeat(auto-fit, minmax(19rem, 1fr));
}
.chooser p { margin: 0 0 0.5rem; }
.chooser-q {
  font-family: var(--sans); font-weight: 600; color: var(--ink);
}

/* ---------------------------------------------------------- #380 ----
   Technical notes: optional, reference-shaped chapters, grouped under
   a rule below the lessons rather than sitting in the run of them.
   Splitting them is also what closes the lessons' rectangle -- with a
   note among them the grid ran to sixteen cards at three columns and
   left the credits alone on a line of their own. */
.tn-divider {
  max-width: 66rem; margin: 3.2rem auto 0;
  border-top: 1px solid var(--rule);
}
.tn-heading {
  margin: 1.4rem 0 1rem;
  font-family: var(--sans); font-size: 0.78rem; font-weight: 600;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink-3);
}
.tn-cards { margin-top: 0; }
/* The partial-row rules that used to live here were special cases of
   what `.chapter-cards` now does for every grid, so they are gone (#381)
   rather than kept as a second way of saying the same thing. */
/* The lessons carry the same label as the notes, so the two sections are
   named alike. No rule above this one: the nav band's border already
   closes the space, and a second line under it read as a double rule. */
.tn-divider.tn-first { border-top: 0; margin-top: 0.4rem; }
/* The same grouping in the sidebar's Contents list, so a note is not
   wedged between two lessons there either. */
.tn-sep {
  list-style: none; margin: 1.1rem 0 0.4rem; padding: 0.7rem 0 0;
  border-top: 1px solid var(--rule);
  font-family: var(--sans); font-size: 0.68rem; font-weight: 600;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3);
}
.chapter-cards li.is-absent h2 { color: var(--ink-3); }

/* --------------------------------------------------------------- index --- */

.book-index { list-style: none; margin: 0; padding: 0; columns: 2; column-gap: 2.5rem; }
.book-index li {
  break-inside: avoid; margin-bottom: 0.4rem;
  font-family: var(--sans); font-size: 0.92rem;
}
.ix-term { margin-right: 0.4rem; }
.book-index a { color: var(--accent); margin-right: 0.3rem; }
/* #422: which book a number opens, when the version has more than one. */
.ix-book {
  font-size: 0.66rem; font-weight: 600; letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--ink-3);
  margin: 0 0.25rem 0 0.35rem; white-space: nowrap;
}
.ix-term + .ix-book { margin-left: 0; }

/* --------------------------------------------------------------- pager --- */

.pager {
  display: flex; justify-content: space-between; gap: 1rem;
  max-width: var(--measure);
  margin: 3.5rem 0 0; padding-top: 1.2rem; border-top: 1px solid var(--rule);
  font-family: var(--sans);
}
.pager a { text-decoration: none; font-weight: 600; font-size: 0.95rem; }
.pager-next { margin-left: auto; text-align: right; }
.pager span {
  display: block; font-size: 0.66rem; font-weight: 600; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--ink-3);
}
.pager a:hover { color: var(--accent); }

.foot {
  border-top: 1px solid var(--rule);
  padding: 1.8rem var(--banner-pad);
  font-family: var(--sans); font-size: 0.8rem; color: var(--ink-3);
  text-align: center;
}
.foot p { margin: 0.2rem 0; }

/* -------------------------------------------------------------- mobile --- */

@media (max-width: 62rem) {
  .shell { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: static; max-height: none; overflow: visible;
    padding-bottom: 1.5rem; margin-bottom: 1.5rem;
    border-bottom: 1px solid var(--rule);
  }
  /* One column here regardless: the max-content track above is a desktop
     idea, and :has() outranks the plain .shell rule on specificity.
     #428: this must carry `body:not(.home)` too. A media query adds no
     specificity, so the desktop rule above -- (0,4,1) once #396 scoped it
     away from the home page -- outranked this one at (0,3,0) and won on a
     phone whenever Contents was folded, which is its default there. The
     sidebar then took a max-content track and the chapter was left 141px
     of a 385px screen. Keep the two selectors the same shape. */
  body:not(.home) .shell:has(.toc:not([open])) {
    grid-template-columns: minmax(0, 1fr);
  }
  .sidebar:has(.toc:not([open])) { padding-bottom: 0.9rem; margin-bottom: 1.1rem; }
  .toc:not([open]) > summary.sidebar-title { margin-bottom: 0; }
  .chapter { max-width: none; }
  .book-index { columns: 1; }
}
/* the full label stops fitting alongside the wordmark; keep the numeral */
@media (max-width: 40rem) {
  .vkey-full { display: none; }
  .vkey-num { display: block; }
  .versions summary { padding: 0.38rem 0.5rem 0.42rem 0.6rem; }
  /* On a phone the ribbon wraps, and where the picker lands in the wrapped
     row moves with the viewport and its own label width -- so no edge of
     the summary is safe to hang a 15rem menu from. Hung left, it ran
     263..503 on a 375px viewport (measured 27 Aug 2026); flipped to hang
     right, it ran off the LEFT edge instead on a Galaxy S22 at 360px,
     where the picker sits at the row's left (Roberto's screenshot, 28 Aug
     2026: every option an unreadable sliver). Stop hanging it from the
     summary at all: un-position .versions so the menu's containing block
     becomes .subbar itself (position: relative in the shared banner.css),
     and span the band's full content width just under the keyline. It
     cannot leave the screen from there, wherever the picker wrapped to. */
  .versions { position: static; }
  .version-list {
    left: var(--banner-pad); right: var(--banner-pad);
    top: calc(100% + 0.4rem);
    min-width: 0;
  }
}

/* #420: the Technical Notes mark, long where there is room.

   The mark carries both spellings and this shows one. Measured on the
   live page at real viewport widths (a sweep that fakes the width by
   setting documentElement.style.width measures nothing -- media queries
   key off the viewport): the long form is 165px wide with 430px of
   clearance at 1200 and 24px at 700, and below 700 that gap stops closing
   because the wordmark starts giving up room instead. So the top band
   takes the short form under 700px. The slot form sits below the wordmark
   on phones, where 121.9px of text has a 155px slot to itself, and keeps
   the long name.

   Only the Technical Notes emit the two spans; Course and Manual are one
   word and render as plain text. Lives here rather than in the shared
   banner.css because no other property has this mark. */
.property-mark .mark-short { display: none; }
@media (max-width: 700px) {
  .property-mark-top .mark-long  { display: none; }
  .property-mark-top .mark-short { display: inline; }
}
