/* ============================================================
   rxdx study site — site.css
   Shared component + layout rules. Lifts from rxdx-ds/components
   and past/s537 layout. tokens.css supplies all color/type/space.
   ============================================================ */

*{box-sizing:border-box}
/* SITEWIDE PAGE-LOAD CONTRACT (owner: nav must be defined once, not patched per page). The site is an MPA —
   every link is a full document load — so the ONE thing that makes navigation feel seamless is that the
   document is the paper `--bg` from the FIRST paint: html itself is tan, so the browser never flashes white
   between pages (the old jerk). Everything else follows from this: the shell (rail + top bar) paints
   synchronously and identically on every page; served-in-HTML blocks (#ecglab, #about) stay hidden until
   reparented (no FOUC); and the boot cover only ever masks the FIRST auth/gate check of a session — after
   that it's skipped, and because html is already tan, the coverless loads look identical (tan → content). */
html{background:var(--bg);-webkit-text-size-adjust:100%;text-size-adjust:100%;
  /* Always reserve the scrollbar gutter. Without it a page that scrolls (ECG Lab) and one that does not
     (home) render at different viewport widths, so navigating between them shifts the top bar, the centered
     title and the content column sideways — and the view transition then MORPHS across that shift. Reserving
     it makes every page the same width, so this whole class of jump cannot happen again. */
  scrollbar-gutter:stable}  /* iOS: don't inflate text on rotate */
html,body{margin:0;padding:0}
body{
  background:var(--bg);color:var(--ink);
  font-family:var(--body);font-size:var(--t-body);
  -webkit-font-smoothing:antialiased;
  -webkit-tap-highlight-color:rgba(194,65,12,.10); /* subtle branded tap flash, not the default gray */
  accent-color:var(--accent);                      /* native checkbox/radio/search controls in brand rust */
  color-scheme:light;                              /* keep native controls/scrollbars light on a dark-OS device */
  min-height:100vh;min-height:100dvh;              /* dvh: don't overshoot behind the iOS URL bar (100vh is taller than the visible area → phantom scroll) */
}
/* touch-action:manipulation on every tappable control — kills the iOS double-tap-to-zoom (tapping chips
   fast during selection read as a double-tap and zoomed the page) + the 300ms tap delay. Pinch-zoom on
   the page/images is untouched. */
button,a,summary,label,.chip,.opt,.hub-card{touch-action:manipulation}
button{font-family:inherit;font-size:inherit;cursor:pointer;border:none;background:none;color:inherit}
input{font-family:inherit;font-size:inherit}
a{color:var(--accent);text-decoration:none}
svg{display:block}
:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:var(--r-1)}

/* PERSISTENT-SHELL NAVIGATION (owner: the side menu should STAY, transitions should be smoother, not
   leave-and-come-back). The site is an MPA, but cross-document View Transitions make a link feel like an
   app: the rail + top bar carry their OWN transition names so they hold in place (no fade), while only the
   content (the unnamed "root" snapshot) cross-fades between pages. Progressive enhancement — a browser
   without View Transitions just navigates instantly, exactly as before (no harm). */
@view-transition{navigation:auto}
.sidebar{view-transition-name:rail}
.topbar{view-transition-name:topbar}
/* The named rail/top-bar are their OWN transition groups, so ONLY the content (the unnamed root snapshot)
   cross-fades between pages. The shell must not animate at all: its old/new snapshots are pinned, because
   even when the geometry matches, the old and new CONTENTS still cross-fade by default — that is what made
   a ghost top bar dissolve over the home hub (home/search hide .topbar, so the named group exists on only
   one side of those navigations) and made ECG Lab's view tabs ghost over the next page's bar. Suppressing
   old/new here is the fix, not a workaround: a persistent shell should have nothing to fade. */
::view-transition-old(root),::view-transition-new(root){animation-duration:var(--dur-2)}
::view-transition-group(rail),::view-transition-group(topbar){animation-duration:0s}
::view-transition-old(rail),::view-transition-old(topbar){animation:none;opacity:0}
::view-transition-new(rail),::view-transition-new(topbar){animation:none;opacity:1}
@media (prefers-reduced-motion:reduce){::view-transition-group(*),::view-transition-old(*),::view-transition-new(*){animation:none!important}}

/* ============================================================
   APP SHELL — sticky top, push sidebar, sticky bottom
   ============================================================ */
/* content sits to the RIGHT of a persistent var(--rail) icon rail; the rail is fixed and, when
   expanded, floats OVER the content and both sticky bars (never pushes). */
.app{display:flex;flex-direction:column;min-height:100vh;min-height:100dvh;padding-left:var(--rail)}

/* ---- sticky TOP bar = wayfinding: [ham][back?] · context title · [actions] ---- */
.topbar{
  position:sticky;top:0;z-index:50;flex:none;
  /* the bar's surface extends UP into the notch (safe-area) and its content is padded below it, so
     on a notched iPhone the status bar sits on the bar's own background, not on top of the burger/
     title. env=0 with no notch → identical to before on desktop/iPad. (This is the "top-top".) */
  height:calc(var(--topbar-h) + var(--sa-top));
  padding-top:var(--sa-top);padding-left:var(--sa-left);padding-right:var(--sa-right);
  background:var(--bg);border-bottom:1px solid var(--line);
  /* NO drop-shadow: the rail header (.side-top) meets this bar at the top-left corner and carries only a 1px
     hairline; a shadow here made the bar's bottom edge read ~1px lower/heavier than the rail's, so the corner
     looked misaligned. One clean continuous hairline across rail + bar instead (owner: "off a pixel"). */
  display:grid;grid-template-columns:auto 1fr auto;align-items:center;
}
/* SEARCH (/search) = the old Google-style home: no wayfinding title, hidden top bar, one centered search
   box with the wordmark floating above it. The heavy (half-built) drug/disease search moved OFF the front
   door to its own page; the launcher hub is home now. The left rail + its home mark stay on both. */
body[data-page="search"] .topbar{display:none}
body[data-page="search"] .main{display:flex;flex-direction:column;padding:0}
body[data-page="search"] .hero{position:relative;display:block;flex:1;min-height:0;padding:0;gap:0}
/* OPTICAL center, not mathematical: the search box is the heavy element, so pinning it to a literal
   50% reads low (big empty void below). Seat it a few % above the line — the classic search-hero feel. */
body[data-page="search"] .searchfield{
  position:absolute;top:46%;left:50%;transform:translate(-50%,-50%);
  width:min(560px, calc(100% - 2 * var(--s-4)));max-width:none;margin:0;
}
/* wordmark sits above the bar: its bottom edge = bar top (bar half-height 26px) minus a gap */
body[data-page="search"] .hero-logo{
  position:absolute;left:50%;bottom:calc(54% + 26px + var(--s-5));transform:translateX(-50%);margin:0;
}

/* HOME = the launcher hub (owner): the rxdx wordmark above App-Store-style app tiles, in TWO groups (apps ·
   courses) split by a divider. No top bar on home (the wordmark IS the brand here); grid optically centered. */
body[data-page="home"] .topbar{display:none}
/* HOME centers on the FULL viewport, not the area after the rail (owner: "it's not centered, look at rxdx vs
   everything"). Elsewhere .app reserves the rail via padding-left so content sits beside it; on the launcher hub
   that offset pushes the whole block ~half-a-rail right of screen center. Zero it here — the fixed rail overlays
   the empty left gutter and the hub (wordmark + tiles) is truly screen-centered. */
body[data-page="home"] .app{padding-left:0}
/* OPTICAL CENTER. ⛔ THE LIFT IS A TRANSFORM, NEVER BOTTOM PADDING. 🔬 2026-09-07, 👤: "you sure this is
   optically centered? i feel its kinda low." Measured at 1440x900: the token declares 6vh = 54px and the
   page delivered 27px — EXACTLY HALF. Inside a `justify-content:center` flex, extra bottom padding
   shrinks the content box, and centering in the shrunk box moves content up by padding/2. So the
   declared value and the delivered value silently disagreed by 2x, while `.authov` (line ~1320) used a
   TRANSFORM with the same token and got the full 6vh — two "optically centered" screens at two heights.
   A transform shifts by exactly what it says. check_style.js now fails if the padding trick returns. */
body[data-page="home"] .main{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--s-5) var(--s-4);transform:translateY(calc(-1 * var(--optical-lift)))}
/* app-launcher: keep tiles a compact ICON size (~150px squares), not big empty cards. Capping each tile
   (minmax) rather than stretching to the column keeps them tidy on any width; the grid centers itself. */
.hub{width:100%;max-width:688px;display:flex;flex-direction:column;align-items:center;gap:var(--s-5)}
.hub-mark{font-family:var(--display);font-weight:700;font-size:var(--t-hero-sm);color:var(--accent);letter-spacing:-.02em;line-height:1}
/* minmax(0,1fr) — WITHOUT the 0 floor, each column can't shrink below the tile's min-content (~171px), so the
   3 columns (537px) overflowed the 504px hub to the RIGHT, pushing the tiles ~17px right of the wordmark (owner:
   "rxdx isn't centered vs the chips"). The 0 floor lets the columns share the container equally → tiles centered
   under the wordmark at any width. */
/* DESKTOP: 4 across (owner) — with the current course set that's a clean 4 + 3 (never a lonely 3+1). The wider
   hub (688px) keeps each tile ~the same size as the old 3-up; a mid-width band drops to 3, phone to 2. */
/* ⛳ WHATEVER THEY PICK IS CENTERED — 👤, 2026-09-13: *"the shortcut option is available now in the users
   profile. so whatever they have, make sure it automatically gets centered optically."* 🔬 A fixed 4-column
   grid fills left-to-right, so a student who pins TWO got two tiles in columns 1–2 and a hole where 3–4
   would be — the wordmark centered above a left-aligned block. ⛳ Flex-wrap centers every row INCLUDING a
   partial one, so 2, 5 or 9 tiles all sit under the mark. The basis reproduces the old 4-up width exactly,
   so the default hub is pixel-identical to what it was. */
.hub-grid{width:100%;display:flex;flex-wrap:wrap;justify-content:center;gap:var(--s-3)}
.hub-grid > *{flex:0 1 calc((100% - 3 * var(--s-3)) / 4);min-width:0}
/* ⛳ THE `+` IS A TILE, NOT A LINK IN A CORNER — 👤: *"on the home page, there should be a plus button
   somewhere so a user can select the courses they want there that takes them to their account page."*
   ⛳ It sits LAST in the same grid, so it reads as "and one more thing you can do here" rather than as
   chrome, and it is quieter than a real destination because it is a way to change the page, not a place. */
/* ⚠️ `.hub-card.hub-add`, NOT `.hub-add` — this block sits ABOVE `.hub-card` in the file, so at equal
   specificity the plain rule won on source order and the tile rendered identically to a real destination.
   🔬 Caught in a screenshot, not by the style gate: both rules are tokenized and both are legal. */
.hub-card.hub-add{border-style:dashed;background:none;color:var(--ink-3)}
.hub-card.hub-add .hub-ic{font-size:var(--t-h1);line-height:1;background:none}
.hub-card.hub-add:hover{border-color:var(--accent);color:var(--accent)}
/* tablet / small laptop: 4 tiles get cramped below ~700px, so fall back to 3 (and the narrower hub re-centers).
   ⛔ THESE TWO BREAKPOINTS DID NOTHING UNTIL 2026-09-19. They set `grid-template-columns` on `.hub-grid`,
   which is `display:flex` — a grid property on a flex container is ignored, so a phone kept rendering FOUR
   tiles per row at ~87px each. 🔬 👤's screenshot: "Endocrinology" wider than the tile it sits in.
   ⚖️ The column count here lives in the flex BASIS, the same place the 4-up default sets it. */
@media (max-width:700px){.hub{max-width:504px}
  .hub-grid > *{flex:0 1 calc((100% - 2 * var(--s-3)) / 3)}}
/* TILE SHAPE (owner 2026-08-10): DESKTOP tiles are LANDSCAPE rectangles — a square read "elongated / empty"
   on a wide screen; a gentle 3:2 landscape card fills the row and feels like a desktop app shelf. PHONE keeps
   the SQUARE (2-up) — a square grid is the right icon-launcher feel on a narrow screen. So the shape flexes
   with width (3:2 desktop → 1:1 phone), set per-breakpoint below. */
.hub-card{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s-2);aspect-ratio:3/2;
  /* ⛳ ONE HEIGHT FOR EVERY TILE. A flex row is as tall as its tallest item, so a single two-line label
     ("Drug Bank", "Study Guides") made its entire row taller than the row above — 🔬 measured 134px vs
     114px, with the icons 10px out of line. The reserve is icon + gap + two lines of label + padding. */
  min-height:calc(52px + var(--s-2) + 2 * 1.3em + 2 * var(--s-4));padding:var(--s-4);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);color:var(--ink-2);text-decoration:none;text-align:center;transition:border-color var(--dur-1),transform var(--dur-1),color var(--dur-1)}
.hub-card:hover{border-color:var(--line-2);color:var(--ink);transform:translateY(-2px)}
/* the icon sits in a rounded-square accent-tinted "app icon" badge (App Store tile feel). */
.hub-ic{display:flex;align-items:center;justify-content:center;width:52px;height:52px;border-radius:var(--r-2);background:var(--accent-bg);color:var(--accent)}
.hub-card .hub-ic svg{width:26px;height:26px;display:block}
/* ⛔ `Endocrinology` has no space to break at, so without this it overflowed the tile rather than wrapping.
   A label is allowed two lines; the tile's min-height reserves the room so one wrapping label cannot make
   its whole row taller than the row above it (which is what put the icons at two different heights). */
.hub-title{font-family:var(--body);font-size:var(--t-body);font-weight:400;overflow-wrap:anywhere;
  max-width:100%;line-height:1.3}
/* coming-soon course tile: flat/muted, non-clickable, a "Coming soon" line under the label. */
.hub-card.soon{cursor:default;color:var(--ink-3);background:var(--surface-2)}
.hub-card.soon:hover{border-color:var(--line);color:var(--ink-3);transform:none}
.hub-card.soon .hub-ic{background:var(--surface);color:var(--ink-3)}
/* phone: 2-up SQUARE tiles (app-launcher feel, owner) + more side breathing room. Override the desktop 3:2
   back to a square here — a square grid reads best on a narrow screen; the home main gets --s-5 side padding. */
/* ⛳ PHONE IS ITS OWN LAYOUT, NOT THE DESKTOP SHRUNK. 👤, 2026-09-19: *"you just shrunk it and don't make
   it mobile."* Correct — two big cards per row pushed a ten-destination launcher onto five rows and made
   the home page SCROLL to see the last one. ⚖️ A launcher's whole job is that everything is visible at
   once, the way a phone's own home screen is. So the phone gets a COMPACT SPRINGBOARD: three per row, a
   smaller icon, the label at meta size so a real course name still sits on one line in a ~106px tile, and
   the 3:2 desktop proportion dropped for a shape that packs. 🔬 All ten fit above the fold at 390×844. */
@media (max-width:560px){
  .hub-grid{gap:var(--s-2)}
  .hub-grid > *{flex:0 1 calc((100% - 2 * var(--s-2)) / 3)}
  .hub-card{aspect-ratio:auto;min-height:0;gap:var(--s-2);padding:var(--s-3) var(--s-2)}
  .hub-ic{width:40px;height:40px;border-radius:var(--r-1)}
  .hub-card .hub-ic svg{width:20px;height:20px}
  .hub-title{font-size:var(--t-meta);line-height:1.25}
  .hub-card.hub-add .hub-ic{font-size:var(--t-h3)}
  /* ⛳ AND IT SITS NEAR THE TOP, not floating in the middle. Centering a launcher vertically is right on a
     wide screen, where the block is short against a lot of height; on a phone it pushed the wordmark a
     quarter of the way down and left a dead band under the last tile. 🔬 390x844: content now starts under
     the burger and the whole set is above the fold. */
  /* ⛳ HOME GETS THE SAME HEADER BAND AS EVERY OTHER PAGE. 🔬 2026-09-19, 👤: *"the menu is there on the
     top left"* — and the wordmark sat 20px below it, centered against nothing: the burger's optical center
     was y28 and the wordmark's was y48, so a 40px control and a 48px title shared a band while agreeing on
     no line at all. ⚖️ Every other page pairs that burger with a title in a --topbar-h band; home is not a
     special case, it just has a wordmark where the title goes. Same start, same height, same center — and
     then ONE clear gap before the grid, wider than the gaps inside it so the title reads as a header and
     not as the first row. */
  body[data-page="home"] .main{justify-content:flex-start;
    padding:calc(var(--sa-top) + var(--s-2)) var(--s-4) var(--s-5);transform:none}
  .hub{gap:var(--s-5)}
  .hub-mark{font-size:var(--t-h1);height:var(--ctl-lg, 40px);line-height:var(--ctl-lg, 40px)}
  /* ⛳ 2026-09-27 FIX — 👤: *"why does this look terrible? all scrunched to the top… all the empty space
     at the bottom."* 🔬 MEASURED: even at the 10-shortcut count this springboard was built for, the grid
     left 313px empty below it on a 775px-tall viewport (40%) — the "fits above the fold" comment above only
     ever promised no SCROLLING, never no DEAD SPACE, and with fewer than 10 shortcuts (this account: 7 + the
     Add tile) the gap is worse. ⛳ THE FIX keeps the header pinned exactly where the 09-19 ruling put it
     (same burger band, same start) — `.hub` now fills the remaining height below it, and the GRID (not the
     header) centers inside THAT space with `margin:auto` absorbing the leftover evenly above/below, `gap`
     still the floor so it never crowds the wordmark. A short list now sits in the middle of its own room
     instead of stranded under the header with the whole page's slack dumped below it. */
  .hub{flex:1 1 auto;min-height:0}
  .hub-grid{margin-top:auto;margin-bottom:auto}}
/* STUDY GUIDES landing (owner 2026-09-04, "Reader / Contents") — a two-pane READER: LEFT = the index
   (Study Guides → course → week), RIGHT = the open guide's contents so you see what's inside before you
   commit. "In this guide" is filled from the guide's own real <h2> sections (the guide is the one source). */
/* Study Guides is a full-height APP (one page: left = courses, right = overview↔reader), so it goes edge-to-edge
   with no page padding and fills the viewport below the top bar. */
body[data-page="study-guides"] .main{max-width:none;padding:0}
/* Study Guides v2 (owner 2026-09-06 — two clean pages, mobile-first): MAIN = pick a course · READER = the guide
   full-width, top-bar is "back". No left list, no breadcrumb — the module owns the in-course nav. */
/* ⛳ THE `.sg2` RULE THAT LIVED HERE IS DELETED, ⛔ not corrected — it set height/background/overflow and
   the `.sg2` block further down this file sets all three AGAIN plus the flex layout, so the later one won
   every time and this was dead weight that still had to be kept in sync. §12: a duplicate is a future
   contradiction, and this one had already become one — 👤 found the study-guide pane on the wrong ground
   (2026-09-14) and there were TWO places to fix it. One home now: search `.sg2{` — there is exactly one. */
/* ⛔ A RULE BODY WITH NO SELECTOR LIVED HERE, and its stray `}` closed a block that was never
   opened. 🔬 Found by A24 the hour it learned to count braces. The `.sg2` topic-card rules it
   belonged to are DUPLICATED in full further down this file (the `.sg2` block near the study-guide
   section), so this fragment styled nothing and only unbalanced the file. ⛔ Deleted, not patched —
   an orphan is not a rule. */
.sg2.reading{padding:0}
.sg2-frame{width:100%;height:100%;border:0;display:block;background:var(--surface)}
/* ⚠️ `.sg` IS THE v1 STUDY-GUIDE SHELL AND NOTHING RENDERS IT — app.js writes `sg2` only (7 call sites, 0
   for `sg`). ⛔ Left in place rather than deleted on my own read: it carries a whole column layout below and
   a dead-code removal is its own change with its own verification. ⛳ But it is put on the SAME ground as
   `.sg2` so it cannot re-ship the defect above the day something does render it. Flagged for a sweep. */
.sg{display:flex;align-items:stretch;overflow:hidden;background:var(--rail-bg);height:calc(100vh - var(--topbar-h) - var(--sa-top))}
/* LEFT — the index */
.sg-nav{flex:0 0 300px;width:300px;background:var(--bg);border-right:1px solid var(--line);padding:var(--s-4) var(--s-3);overflow:auto}
.sg-nav-h{font-family:var(--sans);font-size:var(--t-h3);font-weight:600;color:var(--ink);margin-bottom:var(--s-3)}
.sg-course{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2);border-radius:var(--r-1)}
.sg-course.open{background:var(--accent-bg)}
.sg-course.soon{opacity:.65}
.sg-course-ic{display:inline-flex;width:16px;height:16px;color:var(--accent)}
.sg-course.soon .sg-course-ic{color:var(--ink-3)}
.sg-course-ic svg{width:100%;height:100%}
.sg-course-nm{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--ink)}
.sg-course.open .sg-course-nm{color:var(--accent)}
.sg-course.soon .sg-course-nm{font-weight:500;color:var(--ink-2)}
.sg-course-tag{margin-left:auto;font-family:var(--mono);font-size:var(--t-micro);letter-spacing:.06em;color:var(--accent)}
.sg-course-tag.soon{color:var(--ink-3)}
.sg-weeks{padding:var(--s-1) 0 var(--s-2) var(--s-4);display:flex;flex-direction:column;gap:var(--s-1)}
.sg-week{display:flex;align-items:center;gap:var(--s-2);width:100%;min-height:var(--tap);padding:var(--s-2);border:none;border-left:2px solid var(--accent);border-radius:var(--r-1);background:var(--surface);color:var(--ink);cursor:pointer;text-align:left;font-family:var(--sans)}
.sg-week:hover{background:var(--surface-2)}
.sg-week-nm{font-size:var(--t-body);font-weight:600}
.sg-week-tag{margin-left:auto;font-family:var(--mono);font-size:var(--t-micro);color:var(--accent)}
.sg-week-more{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);padding:var(--s-1) var(--s-2)}
/* RIGHT — the reader */
.sg-reader{flex:1;min-width:0;display:flex;flex-direction:column;overflow:hidden}
.sg-scroll{flex:1;overflow:auto}
/* READING state — the guide in-place: a back strip + the guide iframe filling the pane */
.sg-back{display:flex;align-items:center;gap:var(--s-3);min-height:var(--tap);padding:0 var(--s-5);border-bottom:1px solid var(--line);flex:none}
.sg-back-btn{display:inline-flex;align-items:center;gap:var(--s-1);background:none;border:0;padding:0;font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--accent);cursor:pointer}
.sg-back-crumb{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3)}
.sg-frame{flex:1;width:100%;border:0;display:block;background:var(--surface)}
.sg-crumb{display:flex;align-items:center;gap:var(--s-2);min-height:var(--tap);padding:0 var(--s-5);border-bottom:1px solid var(--line);font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3)}
.sg-crumb-sep{color:var(--line-2)}
.sg-crumb-on{color:var(--ink);font-weight:500}
.sg-read-body{padding:var(--s-6) var(--s-5);max-width:44rem}
.sg-eyebrow{font-family:var(--mono);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--accent);margin-bottom:var(--s-3)}
.sg-read-h1{font-family:var(--sans);font-size:var(--t-hero);font-weight:700;line-height:1.05;letter-spacing:-.015em;color:var(--ink);margin:0 0 var(--s-3)}
.sg-read-lede{font-family:var(--body);font-size:var(--t-h3);line-height:1.55;color:var(--ink-2);margin:0 0 var(--s-5)}
.sg-toc{margin-bottom:var(--s-5)}
.sg-toc-h{font-family:var(--mono);font-size:var(--t-micro);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--s-3)}
.sg-toc-list{display:flex;flex-direction:column}
.sg-toc-row{display:flex;align-items:center;gap:var(--s-3);width:100%;min-height:var(--tap);padding:var(--s-2) 0;border:0;border-top:1px solid var(--line);background:none;text-align:left;font:inherit;color:var(--ink);text-decoration:none;cursor:pointer}
.sg-toc-row:last-child{border-bottom:1px solid var(--line)}
.sg-toc-row:hover .sg-toc-nm{color:var(--accent)}
.sg-toc-row:hover svg{color:var(--accent)}
.sg-toc-n{font-family:var(--mono);font-size:var(--t-meta);color:var(--accent)}
.sg-toc-nm{font-family:var(--sans);font-size:var(--t-body);font-weight:500}
.sg-toc-row svg{margin-left:auto;color:var(--line-2)}
.sg-start{display:inline-flex;align-items:center;gap:var(--s-2);min-height:var(--tap);padding:0 var(--s-4);background:var(--accent);color:var(--surface);border:0;border-radius:var(--r-1);font-family:var(--sans);font-size:var(--t-body);font-weight:600;text-decoration:none;cursor:pointer}
.sg-start:hover{background:var(--accent-press)}
.sg-empty{padding:var(--s-6) var(--s-5);max-width:36rem}
.sg-empty-h{font-family:var(--sans);font-size:var(--t-h2);font-weight:600;color:var(--ink);margin-bottom:var(--s-2)}
.sg-empty-p{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);line-height:1.5;margin:0}
@media (max-width:680px){
  .sg{flex-direction:column}
  .sg-nav{flex:none;width:auto;border-right:none;border-bottom:1px solid var(--line)}
  .sg-read-body,.sg-empty{padding:var(--s-5) var(--s-4)}
  /* Reading on a phone: hand the whole screen to the guide — the courses list hides, "Contents" brings it back. */
  .sg.sg-reading .sg-nav{display:none}
}

/* ─── RECALL app (owner + writer-3, 2026-09-03) — its own subtle identity: the whole page re-themes its
   accent to the recall teal, so the rail mark, top-bar title and buttons read as one distinct app. ─── */
/* Recall keeps its teal ACCENT (the per-app skin contract, §C3) but ⛔ NOT its own background: the two
   radial washes made this page the only surface in the app that isn't the standard ground, which read as
   clunky rather than distinct (owner 2026-09-06: "get rid of the background, doesn't work, just go with what
   we have"). An app is distinguished by its ACCENT, ⛔ never by re-painting the floor. */
body.recallapp{--accent:var(--recall);--accent-press:var(--recall-press);--accent-bg:var(--recall-tint);
  background:var(--bg)}
/* RECALL SETUP = THE QUIZ WIZARD, same frame (.sview / .wiz-card / .wiz-foot). Top-anchored so the title
   holds one spot on every step, content flows directly below it, Back/Next sit below the content. */
body.recallapp .main{display:flex;flex-direction:column;align-items:center;justify-content:flex-start;
  padding-top:clamp(20px,5vh,64px);padding-bottom:4vh}
/* ⛳ ON A PHONE RECALL USES THE SAME GAP AS EVERY OTHER PAGE. 🔬 2026-09-20, 👤: *"some elements are from
   another place… something scale."* He is describing this: `clamp(20px,5vh,64px)` is the only top padding
   in the app that SCALES WITH THE SCREEN, so on an 844px phone recall started its content 42.2px down
   while the drug bank started at 24 and a course page at 0. A fractional number no token declares, on one
   page out of eighteen. ⛔ A viewport-scaled gap is right for a wide screen's vertical rhythm and wrong on
   a phone, where every page should meet the top bar the same way. */
@media (max-width:560px){ body.recallapp .main{padding-top:var(--s-5);padding-bottom:var(--s-5)} }
.rc{width:100%;max-width:620px;margin:0 auto;padding:0}
/* ⛳ THE RUN IS THE QUIZ'S COLUMN, NOT RECALL'S. The 620px cap above is the WIZARD's — a setup form wants a
   narrow column. During a run it made the card 620px inside a 1040px `.main` while the bottom bar's inner
   is 820px centered in the shell: 🔬 card left edge 353, Back button left edge 269. ⛔ Two axes on one
   screen is 👤's "things are all off balance", and it is arithmetic, not taste. `.narrow` + this = the
   quiz's exact geometry, so the card's left edge IS the Back button's. */
.rc.running{max-width:none}
/* Step title — the quiz wizard's .wiz-q, so the two read as one product. */
.rc-q{flex:none;font-family:var(--sans);font-weight:400;letter-spacing:.01em;line-height:1.25;color:var(--ink);
  font-size:var(--t-h1);text-align:center;margin:0 0 var(--s-5)}
/* ONE RHYTHM down the step (👤 2026-09-07: "improve spacing"). Three sizes, by relationship, not by
   taste: a heading owns --s-2 to the thing it labels · related blocks sit --s-5 apart · the footer takes
   --s-5 of its own padding. ⛔ No other vertical gap on this step. */
.rc-wiz-body > * + *{margin-top:var(--s-5)}
.rc-empty,.rc-load{text-align:center;color:var(--ink-2);padding:var(--s-6) var(--s-4);font-size:var(--t-body)}
/* start screen */
.rc-start{text-align:center;padding:var(--s-4) var(--s-3)}
.rc-kick{font-family:var(--sans);font-size:var(--t-meta);font-weight:var(--w-cap-strong);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--recall)}
.rc-h1{font-family:var(--sans);font-weight:700;font-size:var(--t-h1);color:var(--ink);letter-spacing:-.01em;margin:var(--s-2) 0 var(--s-3)}
.rc-mstrip{font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--ink-2);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-pill);padding:var(--s-2) var(--s-4);margin:0 auto var(--s-5);display:inline-block}
.rc-mstrip.due{color:var(--recall);background:var(--recall-tint);border-color:var(--recall-tint)}
.rc-mstrip b{font-weight:700}
.rc-filter{display:flex;flex-wrap:wrap;gap:var(--s-2);justify-content:center;margin-bottom:var(--s-5)}
.rc-chip{font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--ink-2);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-1);padding:var(--s-2) var(--s-3);cursor:pointer;transition:border-color var(--dur-1),color var(--dur-1)}
.rc-chip:hover{border-color:var(--recall);color:var(--recall)}
.rc-chip.on{background:var(--recall);border-color:var(--recall);color:var(--on-accent)}
/* length + timer pickers (start screen) */
.rc-sets{display:flex;gap:var(--s-5);justify-content:center;flex-wrap:wrap;margin-bottom:var(--s-4)}
.rc-set{display:inline-flex;align-items:center;gap:var(--s-2)}
.rc-set-lb{font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3)}
.rc-pool{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin-top:var(--s-2)}
/* drug picker (custom deck: "test me on just these drugs") */
.rc-drugs{margin-bottom:var(--s-5)}
.rc-dtoggle{display:inline-flex;align-items:center;gap:var(--s-2);font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--recall);background:none;border:0;cursor:pointer;padding:var(--s-1)}
.rc-dcaret{font-size:var(--t-eye)}
.rc-dpanel{max-width:400px;margin:var(--s-3) auto 0;text-align:left;border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-3);background:var(--surface)}
/* ⛳ `--ctl`, THE SITE'S CONTROL HEIGHT — ⛔ not `--ctl-2l` (48px). 🔬 It stood 12px taller than the
   identical search box on Guidances, which is the kind of difference you cannot name but can always see. */
.rc-dsearch{width:100%;height:var(--ctl);font-family:var(--body);font-size:var(--t-body);color:var(--ink);background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);padding:0 var(--s-4)}
.rc-dsearch:disabled{background:var(--surface-2);color:var(--ink-3);cursor:not-allowed}
.rc-dsearch:focus{outline:none;border-color:var(--recall)}
.rc-dresults{display:flex;flex-direction:column;max-height:200px;overflow-y:auto;margin-top:var(--s-2)}
.rc-dresult{font-family:var(--body);font-size:var(--t-body);text-align:left;color:var(--ink);background:none;border:0;border-radius:var(--r-1);padding:var(--s-2) var(--s-3);cursor:pointer}
.rc-dresult:hover{background:var(--recall-tint);color:var(--recall)}
.rc-dnohit{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);padding:var(--s-2) var(--s-3)}
.rc-dchips{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);min-height:var(--ctl)}
.rc-dchip{display:inline-flex;align-items:center;gap:var(--s-2);font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--recall);background:var(--recall-tint);border:1px solid var(--recall-tint);border-radius:var(--r-1);padding:0 var(--s-3);height:var(--ctl-sm);cursor:pointer}
/* ⛳ SQUARE, LIKE EVERY OTHER CHIP. 👤, 2026-09-17: *"why are your chips circular vs square?"* 🔬 This was
   the ONLY `--r-pill` control in the view — the suggestion chips under it are `--r-1`, so one selection sat
   round among a grid of square ones. ⛔ `--r-pill` is for toggles and status ovals (tokens.css says so); a
   chip you can click and dismiss is a chip. ⛳ And it now carries a real control height instead of padding
   alone, so it lines up with the search box above it rather than floating at its own size. */
.rc-dchip:hover{border-color:var(--recall)}
.rc-x{font-size:var(--t-body);line-height:1;color:var(--recall)}
.rc-tbtn{font-family:var(--mono);font-size:var(--t-meta);color:var(--ink-2);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-1);padding:var(--s-1) var(--s-3);cursor:pointer;transition:border-color var(--dur-1),color var(--dur-1)}
.rc-tbtn:hover{border-color:var(--recall);color:var(--recall)}
.rc-tbtn.on{background:var(--recall);border-color:var(--recall);color:var(--on-accent)}
/* in-drill clock + countdown */
/* ⛔ `.rc-clock` / `.rc-cd` deleted 2026-09-13 with the timer itself — the course quiz shows no clock and
   no per-card countdown, and rules left behind for markup nobody writes are how a stylesheet rots. */
.rc-go{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--on-accent);background:var(--recall);border:1px solid var(--recall);border-radius:var(--r-1);padding:var(--s-3) var(--s-5);cursor:pointer;transition:background var(--dur-1)}
.rc-go:hover{background:var(--recall-press)}
.rc-go:disabled{opacity:.5;cursor:default;box-shadow:none}
/* run */
.rc-run{padding:var(--s-3) 0}
/* The revealed chips row IS a `.qmeta` — the badges inside it are the quiz's `.qtag`. ⛔ `.rc-tag` and
   `.rc-tag.key` are deleted: a second badge treatment sitting one line below the first. It is empty until
   the card is graded, so it must not reserve its own margin while there is nothing in it. */
.rc-meta:empty{display:none}
/* ⛳ RECALL RENDERS THE QUIZ'S COMPONENTS — ⛔ AND KEEPS NO COPY OF THEM (§12, finished 2026-09-13).
   `.qwrap` `.qmeta` `.qtag` `.qprog` `.qcard` `.qtype` `.stem` `.opts` `.opt` `.key` `.lbl` `.opt-mute`
   `.picked` `.muted` `.correct` `.wrong` `.mk`, and the action bar's `.btn`/`.qb-*` — every one of them is
   the QUIZ'S rule, reached by the quiz's own selector.
   🔬 What the half-measure cost: `.rc-idrow` meant `.qmeta .qtag` could not reach the question id, so it
   rendered as plain 14px text instead of a mono badge; `.rc-opt.muted` faded a ruled-out option to
   `opacity:.5` where the quiz tints it `--surface-2`; `.rc-opt .lbl` was missing the quiz's font-family and
   line-height. Three silent drifts in the ONE direction a duplicate always drifts.
   ⛳ Only genuinely recall-only chrome stays below: the timer, the score, Exit, the wizard, the ring. */
/* PICKED but not yet graded — the site's ONE selection treatment (accent tint + accent outline), which
   inside recall resolves to the teal via body.recallapp. Mirrors .opt.picked in the concept quiz: pick,
   then Submit. ⛔ Not a verdict color — green/red belong to .is-right/.is-wrong and only after grading. */
/* ⛔ `.rc-idrow` / `.rc-qprog` / `.rc-submit` are GONE (2026-09-13). The meta row is the quiz's `.qmeta`
   and the actions are the quiz's `.btn` in `setBottomBar`. 🔬 `.rc-idrow` is exactly why this mattered:
   every `.qtag` rule is scoped `.qmeta .qtag`, so recall's question id — which carried `class="qtag qid"` —
   matched NOTHING and rendered as plain 14px body text where the quiz shows a bordered mono badge. Measured
   in the browser, not inferred. Rung A14 now probes this row so it cannot happen a third time. */
/* The reveal slot IS an .explain box (the `.fc-*` rules are all scoped under it); empty until graded, so
   it must not paint a box while there is nothing in it. */
/* ⛳ `.explain` ON THE RECALL REVEAL IS A SCOPING HOOK, NOT A CONTAINER. 🔬 2026-09-13, 👤: *"its a
   container within a container within a container and this weird line that forms."* Measured, and he
   counted right — THREE nested boxes, each with its own 1px border, 12px radius and 16px padding:
   `.qcard` (#fbf7ef) → `.explain.rc-fact` (#efe8da) → `.fc-stack` (#fbf7ef). The middle one exists ONLY
   so the `.explain .fc-…` rules can reach the fact card (that is the A14 fix from 2026-09-08) — ⛔ it was
   never meant to be seen, and two borders sitting a hair apart is the "weird line".
   ⛳ Stripped to nothing visual. The scope survives; the box does not. ⛔ NOT fixed by un-scoping the
   `.fc-*` rules — that would be a second copy of them (§12) and would re-open the defect A14 exists for. */
/* ⚠️ `.qcard .rc-fact` (0,2,0), NOT `.rc-fact` (0,1,0) — `.explain` is declared 1,600 lines LATER at equal
   specificity and wins outright. 🔬 The first version of this fix changed nothing on screen for exactly
   that reason; the box was still there when I re-measured. Same trap as the study guide's `margin:<gap> 0`
   shorthands, three hours apart. ⛳ Measure after the edit, always — the edit is not the fix. */
.qcard .rc-fact{background:none;border:0;padding:0;margin:0}   /* no radius needed — there is no box left to round */
.qcard .rc-fact:empty{display:none}
.qcard .rc-fact:not(:empty){margin-top:var(--s-3)}
/* The verdict now sits INSIDE the card, above the fact card — where the quiz speaks from. */
.rc-verdict{font-family:var(--sans);font-size:var(--t-body);font-weight:600}
.rc-verdict:empty{display:none}
.rc-verdict:not(:empty){margin:var(--s-3) 0 0}
.rc-verdict.ok{color:var(--good)}
.rc-verdict.no{color:var(--bad)}
/* done */
.rc-done{text-align:center;padding:var(--s-6) var(--s-3)}
.rc-ring{width:124px;height:124px;margin:0 auto var(--s-4);position:relative}
.rc-ring-n{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center}
.rc-ring-n b{font-family:var(--sans);font-size:var(--t-h1);font-weight:700;line-height:1;color:var(--ink)}
.rc-ring-n span{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3);margin-top:2px}
.rc-done-h{font-family:var(--sans);font-weight:600;font-size:var(--t-h2);color:var(--ink);margin:0 0 var(--s-2)}
.rc-done-p{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);margin:0 auto var(--s-5);max-width:40ch}
.rc-done-btns{display:flex;gap:var(--s-3);justify-content:center;flex-wrap:wrap;max-width:420px;margin:0 auto}
/* The two footer buttons are flex:1 in the wizard; here they share one row and must not stretch the page. */
.rc-done-btns > .rc-w-next,.rc-done-btns > .rc-w-back{flex:1 1 0;padding:0 var(--s-4)}
.rc-ghost{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--recall);background:transparent;border:1px solid var(--recall);border-radius:var(--r-1);padding:var(--s-3) var(--s-5);cursor:pointer}
.rc-ghost:hover{background:var(--recall-tint)}
/* ── the setup WIZARD (3 steps → the existing drill): just the choices + Next ──
   ⛔ .rc-wiz / .rc-wiz-body are declared ONCE, further down with the step-card sizing. A second block
   here set min-height:100vh, which beats the height:30rem there (min-height always wins), so the card
   stretched to the viewport: the step title clipped above the top bar and Next fell to the page floor.
   🔬 That is what 👤 was looking at, live, on 2026-09-07. §12: one home. */
.rc-lede{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin:0 0 var(--s-3)}
.rc-w-chip{font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--ink-2);background:var(--bg);border:1px solid var(--line);border-radius:var(--r-1);padding:var(--s-2) var(--s-3);cursor:pointer;transition:border-color var(--dur-1)}
.rc-w-chip:hover{border-color:var(--line-2)}
.rc-w-chip.on{color:var(--ink);background:var(--surface-2);border-color:var(--line-2);font-weight:600}
.rc-w-acc{display:flex;flex-direction:column;gap:var(--s-2)}
.rc-w-famrow{border:1px solid var(--line);border-radius:var(--r-2);overflow:hidden;background:var(--surface)}
.rc-w-fam{width:100%;display:flex;align-items:center;gap:var(--s-3);font-family:var(--body);background:transparent;border:0;padding:var(--s-3) var(--s-4);cursor:pointer}
.rc-w-famname{font-size:var(--t-body);font-weight:600;color:var(--ink)}
.rc-w-famn{margin-left:auto;font-family:var(--mono);font-size:var(--t-meta);color:var(--ink-3)}
.rc-w-fcar{font-size:var(--t-eye);color:var(--ink-3)}
.rc-w-facets{display:flex;flex-wrap:wrap;gap:var(--s-2);padding:0 var(--s-4) var(--s-3)}
/* ⛔ 2026-09-08, 👤 on this picker: *"when selecting something, it looks like its grayed out, maybe an
   outline of the box shows its selected."* He was reading a REAL defect. This pill had invented its OWN
   selected state — a GRAY FILL (--surface-2) — and a gray fill is the universal DISABLED affordance, so
   selecting a facet made it look switched off. ⛳ It now uses the site's ONE selection treatment (accent
   tint + accent OUTLINE + accent text, grouped with .chip.on below), which inside recall resolves to the
   teal via body.recallapp — so a selected facet matches the Next button and the header, and the border is
   the signal 👤 asked for. Unselected sits at --ink-2, not --ink-3: available, not dimmed. */
.rc-w-fpill{font-family:var(--sans);font-size:var(--t-meta);font-weight:500;color:var(--ink-2);background:var(--bg);border:1px solid var(--line);border-radius:var(--r-1);padding:var(--s-1) var(--s-3);cursor:pointer;
  transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out),color var(--dur-1) var(--ease-out)}
.rc-w-fpill:hover{border-color:var(--line-2);color:var(--ink)}
.rc-w-how{display:flex;flex-direction:column;gap:var(--s-4)}
.rc-w-hrow{display:flex;align-items:center;gap:var(--s-4);flex-wrap:wrap}
.rc-w-hl{font-family:var(--sans);font-weight:var(--w-cap-strong);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3);min-width:110px}
.rc-w-seg{display:inline-flex;border:1px solid var(--line);border-radius:var(--r-1);overflow:hidden}
.rc-w-sopt{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);background:var(--surface);border:0;border-right:1px solid var(--line);padding:var(--s-2) var(--s-4);cursor:pointer}
.rc-w-sopt:last-child{border-right:0}
.rc-w-sopt.on{color:var(--on-accent);background:var(--recall);font-weight:600}
.rc-w-crow{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.rc-w-toggle{display:inline-flex;align-items:center;gap:var(--s-2);font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);background:transparent;border:0;cursor:pointer;padding:0}
.rc-w-knob{width:36px;height:20px;border-radius:var(--r-pill);background:var(--line);position:relative;transition:background var(--dur-1)}
.rc-w-knob::after{content:"";position:absolute;top:2px;left:2px;width:16px;height:16px;background:var(--surface);border-radius:var(--r-pill);transition:left var(--dur-1)}
.rc-w-toggle.on .rc-w-knob{background:var(--recall)}
.rc-w-toggle.on .rc-w-knob::after{left:18px}
/* Back + Next, EQUAL size, below the content — .wiz-foot / .wiz-nav-btn, not a second button family. */
.rc-w-foot{flex:none;display:flex;gap:var(--s-3);width:100%;padding-top:var(--s-5)}
/* Step 1 has no Back, but Next must not stretch to the full card — it holds the same half it occupies on
   every later step, so the control never changes size as you move through the wizard. */
.rc-w-gap{flex:1 1 0}
.rc-w-back,.rc-w-next{flex:1 1 0;min-width:0;height:var(--ctl);border-radius:var(--r-1);
  border:1px solid transparent;font-family:var(--sans);font-weight:600;font-size:var(--t-body);
  cursor:pointer;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-2)}
.rc-w-back{background:var(--surface);border-color:var(--line);color:var(--ink-2)}
.rc-w-back:hover{border-color:var(--ink-3)}
.rc-w-next{background:var(--recall);color:var(--on-accent)}
.rc-w-next:hover{background:var(--recall-press)}
.rc-w-next:disabled{background:var(--surface-2);color:var(--ink-3);border-color:var(--line);cursor:default}
/* course failed-to-load state (§11 — never a silent empty course) */
.course-err{max-width:440px;margin:0 auto;text-align:center;padding:var(--s-6) var(--s-4)}
.course-err-h{font-family:var(--sans);font-weight:600;font-size:var(--t-h2);color:var(--ink);margin-bottom:var(--s-3)}
.course-err-p{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);line-height:1.55;margin:0 auto var(--s-5);max-width:40ch}
.course-err-btn{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--on-accent);background:var(--accent);border:1px solid var(--accent);border-radius:var(--r-1);padding:var(--s-3) var(--s-5);cursor:pointer}
.course-err-btn:hover{background:var(--accent-press)}
:root{--rc-accent:var(--recall);--rc-track:var(--surface-2)}
/* iOS never auto-zooms a focused field at ≥16px — floor every input on phones so tapping search / the email
   box doesn't jump-zoom the page (the sign-in input was 15px). --t-body IS 16px inside this ≤720 block. */
@media (max-width:720px){input,textarea,select{font-size:var(--t-body)}}
.topleft{display:flex;align-items:center;min-width:var(--s-2);padding-left:var(--s-4)}
/* back = LEFT. A plain TEXT pill ("Home" / "Drugs"), no leading arrow — same bordered-surface
   treatment as the right-cluster controls (densebtn/glossbtn family) so the whole top menu reads
   as one set of pills. */
/* Back pill (drug CARD → drug list). Styled like the neutral top-bar controls (glossbtn/densebtn
   family): bordered surface + INK text, NOT accent/orange — so the whole top menu reads as one set
   of calm pills and the accent stays reserved for primary actions. */
.topback{
  height:var(--ctl);min-height:var(--ctl);cursor:pointer;
  display:inline-flex;align-items:center;gap:var(--s-1);padding:0 var(--s-3);
  border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink-2);white-space:nowrap;
  transition:border-color var(--dur-1),color var(--dur-1);
}
/* ⛔ NO leading arrow. 👤 2026-09-07: "do we need the word Exit and a back arrow? isn't exit good enough?"
   The word already says where it goes — the arrow was a second copy of the same instruction. */
/* quiet bar-control hover — one notch darker, no movement (same theory as the chips) */
.topback:hover{color:var(--ink);border-color:var(--ink-3)}
.topback:active{background:var(--surface-2)}
.topback-lb{font-family:var(--body);font-size:var(--t-meta);font-weight:600;line-height:1}
/* context title is ABSOLUTELY centered across the whole top bar. Because .app has a var(--rail)
   left pad the top bar spans the content region exactly, so left:0/right:0 centers the title
   on the SAME axis as the centered content column (not the shrunken 1fr grid cell). The generous
   line-height + vertical padding keep the display serif's descenders (g / y) from being clipped;
   the side padding reserves room for the left back pill and the right action cluster. */
.topctx{
  /* anchored to the topbar-h region BELOW the safe-area (top:--sa-top, height:--topbar-h) and
     vertically centered by line-height — so the title clears the notch exactly like the grid-placed
     burger/actions do, and stays a single ellipsizing line. env=0 off-notch → visually unchanged. */
  position:absolute;left:0;right:0;top:var(--sa-top);height:var(--topbar-h);margin:0 auto;
  text-align:center;pointer-events:none;padding:0 120px;
  font-family:var(--body);font-weight:700;font-size:var(--t-h2);letter-spacing:-.01em;
  color:var(--accent);line-height:var(--topbar-h);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
/* RIGHT cluster — flush to the right edge with an even gap between every control (Back + tools). */
.topact{display:flex;align-items:center;gap:var(--s-2);justify-self:end;padding-right:var(--s-4);min-width:var(--tap)}
.topactions{display:flex;align-items:center;gap:var(--s-2)}
/* ⛔ `.ecg-viewtabs` DELETED 2026-09-07 — dead CSS. It styled the ECG lab's 4-tab row after app.js
   lifted it into the top bar; the row no longer exists (Manual retired, Learn moved to the Study
   Guides, Drug/Target became the tool's on-page step 1). §12: CSS for markup that is gone is not
   harmless — it is the next session's false lead. */

/* the middle band just holds the page content; the sidebar is a FIXED overlay, not in flow */
.band{flex:1;display:flex;align-items:stretch;min-height:0}

/* ── ⛳ ONE WAIT. ⛔ THERE IS NO SECOND WAY FOR THIS SITE TO SAY "NOT YET".
   👤, 2026-09-17: *"check the loading animation for all places that need it."*
   🔬 MEASURED before this rule existed — THREE treatments for one state, and the good one was dead:
     ① `.loading` + `.spinner` — a 48px ring, centered in the viewport. ⛔ Referenced by ZERO call sites
        in `app.js` and zero pages. A component nobody used, still carrying the only animation.
     ② `.empty` + the WORD "Loading…" — flat gray text, no motion, on the Guidances landing, the Guidances
        tree, and the Reference bank. Motionless text cannot be told from a page that has given up.
     ③ `.sgnote` "Loading…" inside a study-guide wrapper — a third font and a third box for the same idea.
   ⚖️ That is the §12 shape exactly: one concept, three homes, drifting. This is the one home. Every wait
   on the site is built by `waitHTML()` in `app.js`, so a new one CANNOT be invented by accident.
   ⛳ IT SCALES WITH ITS JOB, it does not multiply: the same ring and the same label, inline by default
   (a tree loading inside a card), and `.wait-page` when the wait OWNS the screen. ⛔ Not two components —
   one, with a size, because "a small spinner" and "a big spinner" are not different ideas. */
.wait{display:flex;align-items:center;justify-content:center;gap:var(--s-3);
  color:var(--ink-3);font-size:var(--t-body);padding:var(--s-6) 0;text-align:center}
/* ⛳ the wait's WORDS. It inherits color and size from `.wait`, so it looked right and had no rule —
   which is precisely the seam A5 exists for: a class the app writes that the stylesheet never mentions is
   one rename away from rendering as raw text, and no other rung can see it. Declared, with the one thing
   it genuinely needs: ⛔ never let a long label ("Loading the reference bank…") wrap under the ring. */
.wait-lb{min-width:0}
.wait-ring{width:var(--wait-d,18px);height:var(--wait-d,18px);flex:none;border-radius:50%;
  border:2px solid var(--line);border-top-color:var(--accent);
  animation:spin .8s linear infinite}
/* the wait owns the screen: same parts, stacked and larger, centered in the real content area
   (viewport − top bar − main's own padding) so it sits at the TRUE middle, not the upper third. */
.wait-page{flex-direction:column;gap:var(--s-4);--wait-d:44px;
  min-height:calc(100vh - var(--topbar-h) - var(--sa-top) - 2 * var(--s-5))}
.wait-page .wait-ring{border-width:4px}
@keyframes spin{to{transform:rotate(360deg)}}
/* ⛳ A student who asked the OS for less motion still gets an answer — the ring holds, quietly, and the
   label carries the meaning. ⛔ Never a frozen ring with no words. */
@media (prefers-reduced-motion:reduce){.wait-ring{animation:none;opacity:.5}}

/* ── Build-Quiz "filing cabinet" build animation — the load state after "Build Quiz". Three REAL phases
   (Pull = fetch the pool · Sort = filter to your picks · Assemble = quiz ready), bound to the pool-ready
   promise: a fast/warm load compresses to the beat floor (never a flash), a slow load holds on Assemble
   until it settles. Reduced-motion + headless short-circuit (see buildQuizAnim). */
.buildq{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:clamp(32px,5vh,46px);text-align:center;
  /* the gap clears the PAYOFF bloom: the shockwave + halos scale out past the 140px medallion (~34px beyond),
     so the medallion→quip gap must exceed that or a ripple grazes the text (owner). */
  /* Optically center the WHOLE block (cabinet + quip + status): center it, then bias UP a touch with extra
     bottom padding so it sits at the optical center, not a literal one that reads low (owner). */
  padding-bottom:clamp(40px,8vh,96px);
  min-height:calc(100vh - var(--topbar-h) - var(--sa-top) - 2 * var(--s-5))}
/* THE GRADE MEDALLION — building a quiz grades you UP: a progress RING sweeps while the pool loads, and inside
   it a grade DISC turns F → D → C → B → A, the color climbing crimson → amber → rust → green. It lands on the
   A with the ring completing green, a spring on the mark, and a soft halo ripple (owner: make it premium). */
.bq-medal{position:relative;flex:none;width:140px;height:140px;perspective:800px}
/* ORBITAL SYSTEM — an outer + inner dashed ring spinning at different speeds/directions behind the arc, for
   depth (owner: make it grander). */
.bq-ring2{position:absolute;border-radius:50%;pointer-events:none;transition:border-color var(--dur-2) ease}
.bq-ring2-outer{inset:0;border:2px dashed var(--line-2);animation:bqSpin2 9s linear infinite}
.bq-ring2-inner{inset:22px;border:1px dashed var(--line);opacity:.75;animation:bqSpin2 6s linear infinite reverse}
@keyframes bqSpin2{to{transform:rotate(360deg)}}
/* orbiting SPARKS — three accent dots circling the core while the quiz assembles; they converge at the payoff. */
.bq-orbit{position:absolute;inset:0;animation:bqSpin2 2.4s linear infinite}
.bq-orbit i{position:absolute;top:50%;left:50%;width:7px;height:7px;margin:-3.5px;border-radius:50%;background:var(--accent);
  transform:rotate(var(--a)) translateX(66px);box-shadow:0 0 6px color-mix(in srgb,var(--accent) 55%,transparent)}
/* progress ARC — a faint track + a sweeping accent arc, on a calm loop while it builds. */
.bq-ring{position:absolute;inset:0;width:100%;height:100%;transform-origin:center;animation:bqSweep 1.15s linear infinite}
@keyframes bqSweep{to{transform:rotate(360deg)}}
.bq-ring-track{fill:none;stroke:var(--line);stroke-width:4}
.bq-ring-arc{fill:none;stroke:var(--accent);stroke-width:4;stroke-linecap:round;stroke-dasharray:88 352}
/* SHOCKWAVE — a dashed ring (echoing the orbital rings) that bursts outward once at the payoff, then fades. */
.bq-rays{position:absolute;inset:-4px;border-radius:50%;border:2px dashed var(--good);opacity:0;pointer-events:none}
/* HALOS — soft rings that ripple outward when the A lands (two, staggered, for a bigger bloom). */
.bq-halo{position:absolute;inset:0;border-radius:50%;border:2px solid var(--good);opacity:0;pointer-events:none}
/* central DISC — carries the climbing grade; one 3D turn per re-grade; a shine sweeps across it at the payoff. */
.bq-disc{position:absolute;inset:16px;border-radius:50%;background:var(--surface);border:1px solid var(--line-2);
  display:flex;align-items:center;justify-content:center;transform-style:preserve-3d;overflow:hidden;
  box-shadow:0 2px 14px rgba(60,46,24,.10);transition:background-color var(--dur-3) ease,border-color var(--dur-3) ease}
.bq-disc.spin{animation:bqSpin .6s var(--ease-1)}
@keyframes bqSpin{0%{transform:rotateY(0)}100%{transform:rotateY(360deg)}}
.bq-disc::after{content:"";position:absolute;inset:0;border-radius:50%;opacity:0;pointer-events:none;
  background:linear-gradient(115deg,transparent 30%,rgba(255,255,255,.7) 50%,transparent 70%);transform:translateX(-120%)}
.bq-medal.is-a .bq-disc::after{animation:bqShine .7s var(--ease-out) .15s}
@keyframes bqShine{0%{opacity:0;transform:translateX(-120%)}30%{opacity:1}100%{opacity:0;transform:translateX(120%)}}
/* the mark — a big bold grade whose COLOR climbs crimson → amber → rust → green as it turns up. */
.bq-grade{font-family:var(--body);font-weight:700;font-size:var(--t-hero-sm);line-height:1;color:var(--ink);position:relative;z-index:1;transition:color var(--dur-2) ease}
.bq-grade.g-F,.bq-grade.g-D{color:var(--bad)}
.bq-grade.g-C{color:var(--warn-ink)}
.bq-grade.g-B{color:var(--accent)}
.bq-grade.g-A{color:var(--good)}
/* THE A — the grand payoff: rings ignite green + still, the sparks converge to the core, the arc completes, the
   mark springs, a ray burst blooms, a shine sweeps the disc, and two halos ripple out. */
.bq-medal.is-a .bq-ring2{border-color:var(--good);animation-play-state:paused;opacity:.9}
.bq-medal.is-a .bq-ring{animation:none;transform:rotate(-90deg)}
.bq-medal.is-a .bq-ring-arc{stroke:var(--good);stroke-dasharray:352 352;transition:stroke-dasharray .55s var(--ease-out),stroke var(--dur-2) ease}
.bq-medal.is-a .bq-orbit{animation:none}
.bq-medal.is-a .bq-orbit i{background:var(--good);animation:bqConverge .5s var(--ease-out) .05s forwards}
@keyframes bqConverge{to{transform:rotate(var(--a)) translateX(0);opacity:0}}
.bq-medal.is-a .bq-disc{background:var(--good-bg);border-color:var(--good)}
.bq-medal.is-a .bq-grade{animation:bqPopA .55s var(--ease-out)}
@keyframes bqPopA{0%{transform:scale(.82)}55%{transform:scale(1.18)}100%{transform:scale(1)}}
.bq-medal.is-a .bq-rays{animation:bqRays .8s var(--ease-out)}
@keyframes bqRays{0%{opacity:.7;transform:scale(.66) rotate(0)}100%{opacity:0;transform:scale(1.42) rotate(28deg)}}
.bq-medal.is-a .bq-halo{animation:bqHalo .8s var(--ease-out)}
.bq-medal.is-a .bq-halo2{animation:bqHalo .8s var(--ease-out) .16s}
@keyframes bqHalo{0%{opacity:.5;transform:scale(.92)}100%{opacity:0;transform:scale(1.38)}}
/* the cocky one-liner headlines the build (owner: show off). Thin display weight. Short quips sit on ONE line;
   a long/two-sentence one wraps to TWO BALANCED lines (text-wrap:balance) rather than one cramped line — and
   the block re-centers itself either way (the flex column is optically centered, so height doesn't matter). */
/* quip + steps are now direct children of .buildq, so the ONE flex gap (--s-5) sets an EVEN rhythm:
   cabinet · quip · steps are equally spaced — no bespoke margins making the space above/below the quip
   uneven (owner: the spacing around the cocky line was off). */
.buildq-quip{font-family:var(--body);font-weight:400;font-size:var(--t-h2);color:var(--ink);margin:0 auto;line-height:1.32;max-width:46ch;text-wrap:balance}
@media (prefers-reduced-motion:reduce){
  .bq-ring,.bq-ring2,.bq-orbit,.bq-disc.spin,.bq-medal.is-a .bq-grade,.bq-medal.is-a .bq-halo,.bq-medal.is-a .bq-halo2,.bq-medal.is-a .bq-rays,.bq-medal.is-a .bq-disc::after{animation:none}
  .bq-ring-arc{stroke:var(--good);stroke-dasharray:352 352}
  .bq-ring2{border-color:var(--good)}
  .bq-orbit i{opacity:0}
}

/* SIDEBAR = fixed left drawer. Collapsed = var(--rail) icon rail (RX/537/535/300), always visible,
   sitting over the far-left gutter. Open = var(--drawer) drawer that slides OVER the content and both
   sticky bars (top:0 → bottom:0, z above them). Never pushes — content stays put behind a scrim. */
.sidebar{
  position:fixed;top:0;left:0;bottom:0;width:var(--rail);overflow:hidden;
  /* pad the drawer clear of the notch (top) + home-indicator (bottom); the rail-bg still fills those
     areas since it's on the fixed full-height element. env=0 off-notch → no change on desktop/iPad. */
  padding-top:var(--sa-top);padding-bottom:var(--sa-bottom);padding-left:var(--sa-left);
  background:var(--rail-bg);border-right:1px solid var(--line);
  z-index:100;
}
/* animate width for LIVE hover only — enabled after first paint (JS adds .rail-anim), so a pinned/open rail
   never animates in on page load (owner: no bigger-then-smaller flash when navigating). */
.app.rail-anim .sidebar{transition:width var(--dur-3) var(--ease-out)}
/* DESKTOP: the top bar sits ABOVE the hover-drawer so its Back button + actions stay clickable even while the
   drawer is expanded over the content (owner: "on the family card I can't go back" — the open drawer was
   covering the Back button). Desktop-only: on mobile the drawer is a modal with a scrim and must stay on top. */
@media (min-width:721px){ .topbar{z-index:101} }
/* OPEN drawer sits ABOVE the top bar (z 102 > topbar 101) so its right-edge line runs UNBROKEN from the very
   top down — the header (rxdx) + its edge no longer get clipped by the bar (owner: "to the right of rxdx the
   line should extend upward"). Safe now that Back lives in the right cluster: the ≤248px drawer never reaches
   the bar's title/actions, only its empty left gutter. */
.app.nav-open .sidebar{width:var(--drawer);box-shadow:2px 0 20px rgba(60,46,24,.20);z-index:102}
/* full-height flex column: brand (flush top) · nav (scrolls) · toggle (pinned bottom) */
.sidebar-inner{width:var(--drawer);height:100%;display:flex;flex-direction:column}

/* RAIL TOP — a topbar-height cell (matching bottom border → one clean seam with the top bar) that
   now holds a small thin HOME icon. The icon sits in the same var(--rail) column the course marks occupy,
   so Home reads as the FIRST mark in the rail's icon stack, on the same vertical pitch. */
.side-top{
  flex:none;width:var(--rail);height:var(--topbar-h);border-bottom:1px solid var(--line);
  display:flex;align-items:center;
}
.app.nav-open .side-top{width:var(--drawer)}
/* the rail BRAND = home. Collapsed rail = a compact accent logo icon (triangle placeholder), centered
   in the 60px column like the course marks below it. Open drawer = the full "rxdx" + pulse wordmark,
   left-aligned. Click → home. */
.side-brand{
  flex:none;width:100%;height:100%;
  display:flex;align-items:center;justify-content:center;
  color:var(--accent);text-decoration:none;overflow:hidden;white-space:nowrap;
}
/* Keep the "rxdx" wordmark INSIDE the rail column (≤ --rail) when open: the drawer header sits at top-bar
   height and the top bar paints above the drawer past the rail edge (z-index, so the open drawer can't cover
   the Back button) — so a wordmark spilling past --rail gets its last glyph sliced. Snug left padding keeps
   it clear of that seam (owner: "the logo is cut off in the menu bar left corner"). */
.app.nav-open .side-brand{justify-content:flex-start;padding-left:var(--s-2)}
.side-brand-icon{font-size:var(--t-h2);line-height:1;color:var(--accent);flex:none}
.side-brand:hover .side-brand-icon{opacity:.7}
.side-brand-full{display:none;align-items:center;gap:var(--s-2)}
.side-brand-full .brand-mark{color:var(--accent)}
.app.nav-open .side-brand-icon{display:none}
.app.nav-open .side-brand-full{display:inline-flex}

/* SIDE-MENU SPACING — ONE rule for the whole rail (owner: it was sloppy/inconsistent). Every row (nav +
   floor + toggle) is a --ctl-tall .navlink that TOUCHES its neighbor (no inter-row gap) — so About↔Sign-out
   reads exactly like Reference↔Cardiology. Groups are separated by a hairline `--line` divider with a
   uniform --s-2 of breathing. Both the nav region and the floor carry the same --s-2 padding, so the icon
   column lines up top to bottom. */
.sidebar-nav{flex:1;min-height:0;overflow-y:auto;overscroll-behavior:contain;padding:var(--s-2);display:flex;flex-direction:column;gap:0}
/* group divider inside the rail (courses | non-course apps) — uniform --s-2 breathing, like the floor divider */
.nav-div{flex:none;height:1px;background:var(--line);margin:var(--s-2) 0}

/* Rail floor: About + Sign-out, pinned to the bottom above the collapse toggle, ONE divider above. Rows
   touch (gap 0) like the nav; --s-2 padding gives even breathing under the divider and above the toggle,
   and lines the icon column up with the nav. Padding collapses to 0 in the icon-rail. */
.rail-floor{flex:none;display:flex;flex-direction:column;gap:0;padding:var(--s-2);border-top:1px solid var(--line)}
.app:not(.nav-open) .rail-floor{padding:var(--s-2) 0}

/* bottom collapse toggle (Cloudflare-style) — pinned to the very bottom, with its OWN hairline above it so
   it reads as the separate collapse control below the About/Sign-out floor (line shows open + collapsed). */
.rail-toggle{
  flex:none;display:flex;align-items:center;gap:var(--s-3);
  height:var(--tap);padding:0;border-top:1px solid var(--line);
  color:var(--ink-3);font-family:var(--body);font-size:var(--t-meta);font-weight:400;
  cursor:pointer;background:none;
}
.rail-toggle:hover{color:var(--ink-2)}
/* PINNED (manual, locked-open) — the toggle goes accent so it reads as active, like a pressed control */
.rail-toggle.on{color:var(--accent)}
.rail-toggle.on:hover{color:var(--accent-press)}
.rail-toggle .rt-ic{width:var(--rail);text-align:center;flex:none;line-height:1;transition:transform var(--dur-2) ease}
.rail-toggle .rt-ic svg{width:16px;height:16px;display:block;margin:0 auto}   /* same 16px stroke mark as every nav icon */
.rail-toggle .rt-lbl{display:none}
/* open drawer: line the chevron + label up with the nav/floor icon column; the chevron flips to point LEFT
   (collapse) when open, RIGHT (expand) when collapsed — the icon carries the open/closed state. */
.app.nav-open .rail-toggle{padding:0 var(--s-2) 0 var(--s-4)}
.app.nav-open .rail-toggle .rt-ic{width:24px;transform:scaleX(-1)}
.app.nav-open .rail-toggle .rt-lbl{display:inline}

/* footer links moved OFF the pages into the rail floor (above the collapse toggle). Small muted
   links; only shown when the drawer is open (the var(--rail) rail is too narrow for them). */
.side-foot{flex:none;display:flex;flex-direction:column;gap:2px;padding:var(--s-3) var(--s-3) var(--s-2)}
.app:not(.nav-open) .side-foot{display:none}
.side-foot a,.side-foot span{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3);line-height:1.5;text-decoration:none;white-space:nowrap}
.side-foot a{cursor:pointer}
.side-foot a:hover{color:var(--ink-2)}

/* scrim — covers everything (content + both bars) when the drawer is open; tap to close */
/* Scrim = the MOBILE drawer's modal backdrop only. On desktop the rail floats open on hover and must NOT
   dim the page (a hover that darkens everything reads as broken), so the scrim is suppressed ≥721px. */
.scrim{display:none}
@media(max-width:720px){.app.nav-open .scrim{display:block;position:fixed;inset:0;z-index:99;background:var(--scrim)}}

/* mobile menu button — the drawer opener on phones. Hidden on desktop (the rail is always visible
   there); the mobile @media below reveals it. See the @media(max-width:720px) block for its layout. */
.navburger{display:none}
.drawer-close{display:none}
.navgroup > .glabel{
  font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);margin:0 0 var(--s-1);padding:0 var(--s-2);
}
/* nav labels use a cleaner, SMALLER face than body text (13px vs 15px) and even 36px rows */
.navlink{
  display:flex;align-items:center;gap:var(--s-3);
  height:var(--ctl);padding:0 var(--s-2);border-radius:var(--r-1);
  color:var(--ink-2);font-family:var(--body);font-size:var(--t-meta);font-weight:400;white-space:nowrap;
  transition:color var(--dur-1);
}
/* course marks (Rx / 537 / 535 / 300) are treated as ICONS, not text: one uniform mono glyph — same
   size, weight, tabular width and centered alignment — so they read as an even column like the Home
   mark above them. Tabular numerals keep 2- and 3-char marks optically identical in the icon cell. */
.navlink .nl-ic{font-family:var(--mono);font-size:var(--t-eye);font-weight:600;letter-spacing:-.01em;
  font-variant-numeric:tabular-nums;line-height:1;color:var(--ink-2);width:24px;text-align:center;flex:none}
/* ⛳ THE LABEL HALF OF THE ROW. It got a class (2026-09-14) so `relabelNav()` can find it again when the
   governed course name lands — and A5 immediately reported it as a class with no rule, which is correct:
   a hook with no styling is a hook that will drift. ⛔ NOT parked in HOOKS_ONLY to get to green; the span
   needs these three properties on their own merits. A flex child does not shrink below its content unless
   `min-width:0`, so a long name would push the row wider than the rail and be sliced mid-glyph by the
   rail's `overflow:hidden` — "Principles of Drug Action" is the long one (153px in a 232px rail today, so
   it fits, ⛳ but it fits by luck and not by rule). Ellipsis degrades it honestly instead. */
.navlink .nl-lb{min-width:0;overflow:hidden;text-overflow:ellipsis}
/* subtle hover — a faint text-color shift, no filled block */
.navlink:hover{color:var(--ink)}
/* instant press feedback: the tint flashes on tap so a rail click feels responsive during the page load */
.navlink:active{background:var(--accent-bg);color:var(--accent)}
.navlink:active .nl-ic{color:var(--accent)}
/* selected row reads ORANGE — the vivid accent (not the pressed/burnt tone) on both label and mark. */
.navlink.active{background:var(--accent-bg);color:var(--accent);font-weight:600}
.navlink.active .nl-ic{color:var(--accent)}
/* coming-soon course in the rail: grayed + inert (no href, no hover/press tint), a small "Soon" tag after
   the label. The label (with the tag) hides in the collapsed icon-rail, leaving just the muted mark. */
.navlink.soon,.navlink.soon:hover,.navlink.soon:active{color:var(--ink-3);cursor:default;background:none}
.navlink.soon .nl-ic{color:var(--ink-3)}
/* ECG Lab rail mark — an ECG pulse (one beat). It inherits the row's tint via currentColor exactly
   like the text marks: muted at rest, accent only when the row is selected. Sized to match the mono
   marks' optical height so every mark in the column reads the same size. */
.navlink .nl-ic svg{width:16px;height:16px;display:block;margin:0 auto}
.navlink[href="/ecg-lab.html"] .nl-ic svg{width:15px;height:15px}

/* ---- collapsed RAIL: only the icons (RX/537/535/300) show, centered in the var(--rail) rail and
   EVENLY spaced. Group headers collapse to zero height so every icon sits on one uniform pitch. ---- */
.app:not(.nav-open) .navgroup > .glabel{height:0;margin:0;overflow:hidden;opacity:0;pointer-events:none}
.app:not(.nav-open) .sidebar-nav{gap:0;padding:var(--s-2) 0}
.app:not(.nav-open) .navlink > span:last-child{display:none}
.app:not(.nav-open) .navlink{gap:0;padding:0}
.app:not(.nav-open) .navlink .nl-ic{width:var(--rail)}

/* ============================================================
   MOBILE (<=720px) — the left rail is NOT a persistent gutter here.
   The desktop keeps the Cloudflare-style always-on 60px rail; on
   phones that rail steals horizontal space and squishes the page, so
   it becomes an off-canvas drawer opened by the .navburger button and
   the content spans the full width.
   ============================================================ */
@media(max-width:720px){
  /* ⛳ THE DRAWER IS WIDER ON A PHONE, because on a phone it is the whole navigation and not a strip beside
     the content. 🔬 2026-09-19, 👤's screenshot: "Principles of D…" clipped to an ellipsis while the drawer
     sat at the desktop's 248px with 300+px of unused screen to its right. ⛔ A label truncated next to empty
     space is not a space problem, it is a width that was never re-decided for the device.
     ⚖️ Overridden on the TOKEN, so `.sidebar`, `.sidebar-inner` and `.side-top` all follow one number
     instead of three rules drifting apart. 86vw keeps the scrim tappable on a small phone; the 320px cap
     stops it swallowing the screen on a large one. */
  :root{--drawer:min(90vw, 320px)}
  /* ⛔ AND A DESTINATION'S NAME IS NEVER TRUNCATED ON A PHONE. Even at 320px the widest drawer cannot hold
     "Principles of Drug Action" on one line, and "Principles of D…" is not a navigation label — it is a
     guess the student has to make. The row grows to two lines instead; every row still clears the tap
     target, and nothing else on the screen moves. */
  .navlink .nl-lb{white-space:normal;overflow-wrap:anywhere;text-overflow:clip;line-height:1.25}
  .navlink{min-height:var(--tap);height:auto;align-items:center}
  /* content reclaims the rail's 60px gutter */
  .app{padding-left:0}
  /* rail → off-canvas drawer: always full drawer width, slid out of view until opened. Smoother, longer
     slide with a soft landing (owner: "it's not smooth") — surfaces cover, so --dur-3 + ease-out. */
  .sidebar{width:var(--drawer);transform:translateX(-100%);transition:transform var(--dur-3) var(--ease-out)}
  .app.nav-open .sidebar{transform:translateX(0)}
  /* reduced-motion: the drawer snaps open/closed instead of sliding */
  @media (prefers-reduced-motion:reduce){.sidebar{transition:none}}
  /* fixed top-left menu button — above the top bar (z-50), below the scrim (z-99) and drawer (z-100) */
  .navburger{
    display:flex;flex-direction:column;justify-content:center;gap:5px;
    position:fixed;top:calc(var(--sa-top) + 8px);left:calc(var(--sa-left) + var(--s-3));z-index:60;
    width:40px;height:40px;padding:0 9px;cursor:pointer;
    background:var(--rail-bg);border:1px solid var(--line);border-radius:var(--r-1);box-shadow:var(--shadow-1);
  }
  .navburger span{display:block;height:2px;width:100%;background:var(--accent);border-radius:2px}
  /* On pages with a real top bar (everything but home) the burger is the SAME icon-button
     component as the bar's tools (.searchbtn/.sortbtn-m/.collapsebtn — STYLE_GUIDE §3): a --ctl
     square, --line-2 border, --r-1 radius, muted icon, vertically centered in the bar. It must
     match its siblings — not a bespoke shape. Home has no bar, so it keeps the floating button. */
  body:not([data-page="home"]):not([data-page="search"]) .navburger{
    top:calc(var(--sa-top) + (var(--topbar-h) - var(--tap)) / 2);left:calc(var(--sa-left) + var(--s-3));
    width:var(--tap);height:var(--tap);padding:0;gap:5px;align-items:center;justify-content:center;
    background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);box-shadow:none;
  }
  body:not([data-page="home"]):not([data-page="search"]) .navburger span{width:22px;height:2.5px;background:var(--ink-2)}
  /* while the drawer is open the scrim + its own controls close it — hide the button so it doesn't
     ghost through the scrim */
  .app.nav-open .navburger{opacity:0;pointer-events:none}
  /* "Collapse" is a desktop concept (shrink the drawer back to the 60px icon rail). On mobile there
     is no icon rail — the drawer is just open/closed — so it's redundant with the × below. Hide it. */
  .rail-toggle{display:none}
  /* explicit × close, pinned to the right of the drawer header */
  .side-top{position:relative}
  .drawer-close{
    display:flex;align-items:center;justify-content:center;
    position:absolute;top:50%;right:var(--s-3);transform:translateY(-50%);
    width:var(--ctl);height:var(--ctl);padding:0;cursor:pointer;
    background:none;border:none;color:var(--ink-2);font-size:var(--t-h1);line-height:1;
  }
  .drawer-close:hover{color:var(--ink)}
  /* Mobile drawer breathes: the desktop rail's 36px rows / 12px gaps read tiny on a full-width phone
     drawer, so give the rows tap height, more air between them, and a body-sized label + icon. */
  .sidebar-nav{gap:var(--s-2);padding:var(--s-3)}
  /* Bigger, easier menu rows on a full-width phone drawer (owner: "the options in the menu are small"):
     taller rows + an h3 label so each is a comfortable, legible tap target. */
  .navlink{height:52px;gap:var(--s-4);padding:0 var(--s-4);font-size:var(--t-h3);font-weight:500}
  .navlink .nl-ic{width:28px;font-size:var(--t-h3)}
  .navlink .nl-ic svg{width:20px;height:20px}
  .navlink[href="/ecg-lab.html"] .nl-ic svg{width:19px;height:19px}
  /* keep the centered page title clear of the fixed button and the right action cluster */
  /* mobile: title sits LEFT, right next to the burger (burger ends at --s-4+--ctl = 52px, + --s-3 gap),
     left-aligned; right edge clears the action cluster so a long title ellipsizes instead of colliding. */
  /* ⛳ THE TITLE TAKES WHAT IS ACTUALLY LEFT, not a guess. 🔬 2026-09-19: the old rule reserved
     `--ctl * 3 + --s-6` = 156px on the right for an action cluster that is EMPTY on most pages, so every
     title was squeezed into 154px and "Assessing kidney function" ellipsized at 267px needed vs 154 given
     — with 156px of nothing beside it. ⛔ A constant cannot track a variable cluster.
     ⚖️ So the title stops being absolutely positioned on a phone and becomes the middle cell of the bar's
     own grid: the left column reserves the fixed burger, the right column is the real actions, and the
     title gets the remainder — exactly, on every page, with no number to keep in sync. */
  .topbar{grid-template-columns:calc(var(--s-4) + var(--ctl) + var(--s-2)) minmax(0,1fr) auto}
  .topctx{
    position:static;left:auto;right:auto;top:auto;height:auto;margin:0;padding:0;
    text-align:left;min-width:0;overflow:hidden;text-overflow:ellipsis;
    /* the display size is a desktop measure; on a phone it costs more than it buys */
    font-size:var(--t-h3);
  }
  /* ⚖️ AND WHEN THE BAR STILL CANNOT HOLD BOTH, THE PAGE YOU ARE ON WINS. 🔬 "Assessing kidney function"
     needs 229px and the labeled back pill ("Study Guides", 117px) left it 181. ⛳ The back control keeps
     its arrow and loses its word — the destination is one tap away and its name is still announced to a
     screen reader, while a truncated TITLE names nothing at all. This is the iOS pattern, and it is the
     same ruling already written into the guide title itself: drop a part, never slice a word. */
  .topback .topback-lb{
    position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;
  }
  /* ⛔ …AND IT MUST KEEP ITS ARROW. 🔬 🎨 2026-09-29: the arrow was removed for desktop (👤 09-07 — word OR
     arrow, not both) while this rule still hid the word, so on a phone Back was an EMPTY pill. */
  .topback::before{content:"\2039";font-size:var(--t-h2);line-height:1;color:var(--ink-2)}
  /* ⛳ THE CALCULATOR IS IN THE BAR ON EVERY PAGE (👤 2026-09-29), which costs 44px. The title gives it back by
     WRAPPING, never by being cut: two lines of a long title ("Assessing kidney / function") beat "Assessing
     kidney …". */
  .topctx{white-space:normal;text-overflow:clip;line-height:1.15;overflow-wrap:normal}
  /* …and on the two bank LISTS, whose bar already carries search · sort · filter, the name steps aside (still
     announced): 🔬 "Drug Bank" had 39px beside five controls, and a clipped name names nothing. */
  .topbar:has(.dtools) .topctx{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
    clip-path:inset(50%);white-space:nowrap}
}
/* ⛳ 320px IS STILL A PHONE IN USE, and the bar is where it bites first: at that width the burger, a
   four-icon action cluster and a title do not all fit at desktop sizes. 🔬 C17 found "Drug Bank" cut to
   36px and "Assessing kidney function" 27px short. The actions give first — an icon reads at 36px as well
   as at 44 — and the title drops one step on the type scale rather than losing words. */
@media (max-width:360px){
  .topact{gap:var(--s-1)}
  /* ⛳ THE TITLE IS NOT NEGOTIABLE; THE ACTION ROW SCROLLS. At 320px a burger, four 44px actions and a
     title do not fit, and both of my first two answers were wrong: shrinking the buttons broke the tap
     target, and letting the title ellipsize hid the name of the page you are on. ⚖️ So the cluster is
     capped and swipes — every control keeps its full size and stays reachable, and the title always has
     what it needs. The scrollbar is hidden because a 4px bar inside a 44px row is noise, not information. */
  .topact{max-width:52vw;overflow-x:auto;scrollbar-width:none;-ms-overflow-style:none}
  .topact::-webkit-scrollbar{display:none}
  /* the bank lists' title has stepped aside (above), so their controls may take the whole row — 🔬 capped,
     the calculator at the end of it was scrolled off-screen at 320px */
  .topbar:has(.dtools) .topact{max-width:none;overflow-x:visible;padding-right:var(--s-3)}
  .topbar:has(.dtools) .dtools{gap:var(--s-1)}   /* 4px between 44px targets — the targets themselves stay whole */
  .topctx{font-size:var(--t-body)}
  /* ⛳ AND THE BURGER'S COLUMN IS TRIMMED TO THE BURGER. It reserved 60px for a 40px button sitting 12px
     from the edge — 8px of reserve nothing uses. ⚖️ Taking it back beats the alternative I nearly shipped,
     which was shrinking the action buttons below the 44px tap target to buy the same pixels. ⛔ A title
     that fits is not worth a control you cannot hit. */
  /* ⚠️ TWO WRONG CUTS BEFORE THIS ONE, both measured: `max-width:52%` on the ITEM left the title at 48px
     (a percentage resolved against an indefinite track behaves as auto during track sizing), and capping
     the TRACK with `minmax(0,52%)` took the full 52% on every page — a growth limit is filled from free
     space before `1fr` sees any, so pages with one small action lost 166px to nothing.
     ⛳ An `auto` track plus a VIEWPORT-relative cap on the item: definite at sizing time, so the track is
     the cluster's real width, clamped only when the cluster is genuinely too wide. */
  .topbar{grid-template-columns:calc(var(--s-3) + 40px + var(--s-1)) minmax(0,1fr) auto}
}

/* content column: holds the page main + a per-page sticky bottom bar */
.content{flex:1;min-width:0;display:flex;flex-direction:column}
.main{flex:1;width:100%;max-width:1040px;margin:0 auto;
  /* base horizontal pad + the landscape safe-area (a notch on the left/right in landscape) so body
     copy never hides behind the notch; env=0 in portrait/off-notch → unchanged. */
  padding:var(--s-5) calc(var(--s-4) + var(--sa-right)) var(--s-5) calc(var(--s-4) + var(--sa-left))}
.main.narrow{max-width:820px}
/* LEARN module: a self-contained reference framed inside the shell (rail + top bar stay). Full-bleed, its own
   internal max-width centers the content; the iframe fills the area below the top bar. */
body[data-page="learn"] .main{max-width:none;padding:0}
.learn-frame{display:block;width:100%;height:calc(100dvh - var(--topbar-h) - var(--sa-top));border:0;background:var(--surface)}

/* ---- sticky BOTTOM bar (per page) ---- */
/* ⛔ THE BAR IS CHROME, SO IT IS THE CHROME GROUND — NOT A LIGHTER BAND. 👤, 2026-09-14: *"the bottom menu
   bar, how it's a different color."* 🔬 Sampled from the render: rail (244,239,228) · page canvas
   (244,239,228) · **bottom bar (251,247,239)**. It was the ONLY surface in the app chrome on a different
   tone, and it sits flush against the rail — so the mismatch showed as a seam exactly where they meet.
   ⚖️ THE RULE WAS ALREADY WRITTEN, in `--rail-bg`'s own comment: *"rail matches the page so the top-left
   isn't 3 mismatched color bands (rail/bar/page)."* The rail was moved to the page tone and **the bottom
   bar was never brought along** — the rule was stated once and applied to one of the two bars.
   ⛳ So it takes `--rail-bg` — the SAME token, not a second copy of the same value (§12). If the chrome
   ground ever moves, both bars move together and this cannot drift apart again.
   ⛳ Separation is the JOB OF THE LINE, not of a tint: the 1px top border + the lift shadow still say
   "content scrolls under this", which is what a sticky bar has to say. */
.bottombar{
  position:sticky;bottom:0;z-index:40;flex:none;
  background:var(--rail-bg);border-top:1px solid var(--line);
  box-shadow:0 -1px 3px rgba(60,46,24,.08);
}
.bottombar-inner{
  max-width:1040px;margin:0 auto;
  /* Height locked to the rail's bottom cell (--tap, minus the bar's 1px top border) so the bar's top line
     meets the rail's bottom (») divider — ONE continuous horizontal line across the whole bottom of the app,
     independent of the control-size token (which differs desktop vs tablet). Controls center inside it. */
  min-height:calc(var(--tap) - 1px);
  padding:0 var(--s-4);
  padding-bottom:env(safe-area-inset-bottom,0px);
  display:flex;align-items:center;gap:var(--s-3);
}
/* The running quiz bar is part of the quiz app — keep its controls compact (36px, not the 44px tap floor)
   so the bar doesn't tower over the question. Scoped to .bottombar so global .btn sizing is untouched. */
.bottombar .btn{min-height:var(--ctl);height:var(--ctl);padding:0 var(--s-4)}
.bottombar-inner.narrow{max-width:820px}
.bb-count{font-size:var(--t-meta);color:var(--ink-2)}
.bb-count b{color:var(--ink);font-weight:600}
.bb-spacer{flex:1}
/* NARROW window: the quiz bar's Back/Skip/Next crowd when the viewport gets small (owner: "made my browser
   small and the buttons don't size down the right way"). Tighten the gap + padding, drop the reserved
   min-widths, and let the primary button shrink (its label ellipsizes) so the row always fits cleanly. */
@media (max-width:600px){
  /* ⛳ ON A PHONE THE ACTION ROW IS ONE SIZE, like every other control row in the app. 🔬 2026-09-19,
     👤's screenshot of an endo question: Back 82px, Skip 64px, Submit 138px — three widths scattered
     across the bar, each sized to its own label. ⚖️ 👤's rule for the wizard's footer, already in this
     file, is "Back + Next, EQUAL size"; the running quiz had its own second family that never got it.
     ⛔ And they were --ctl (36px) here: a 36px control is under the 44px tap floor on the one surface
     that is only ever used with a thumb. */
  .bottombar-inner{display:grid;grid-auto-flow:column;grid-auto-columns:1fr;
    gap:var(--s-2);padding:var(--s-1) var(--s-3) calc(var(--s-1) + env(safe-area-inset-bottom,0px))}
  .bottombar .btn{height:var(--tap);min-height:var(--tap);width:100%;padding:0 var(--s-2);
    overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
  .bb-spacer{display:none}          /* a spacer would eat a whole column of the grid */
  .qb-back,.qb-skip,.qb-go{min-width:0}
  /* ⛳ AND THE CARD SITS IN THE MIDDLE OF WHAT IS LEFT. A four-option question is ~550px tall on an 844px
     screen, so top-aligning it left a 160px hole above the bar and read as a broken page rather than a
     short question. Centered, the space falls either side and the card looks placed. Long questions are
     unaffected — `auto` margins collapse the moment the content fills the column. */
  body[data-page="course"] .main{display:flex;flex-direction:column}
  body[data-page="course"] .qwrap{margin-top:auto;margin-bottom:auto}
}

/* ============================================================
   BUTTONS  (from components/button.html)
   ============================================================ */
.btn{
  font-family:var(--body);font-weight:600;font-size:var(--t-body);
  height:var(--ctl);min-height:var(--ctl);padding:0 var(--s-5);
  border-radius:var(--r-1);border:1px solid transparent;
  display:inline-flex;align-items:center;justify-content:center;line-height:1;gap:var(--s-2);
}
.btn{transition:border-color var(--dur-1),color var(--dur-1),background-color var(--dur-1)}
.btn--primary{background:var(--accent);color:var(--on-accent);border-color:var(--accent)}
.btn--primary:hover:not(:disabled){background:var(--accent-press);border-color:var(--accent-press)}
.btn--primary:active{background:var(--accent-press);border-color:var(--accent-press)}
.btn--ghost{background:transparent;color:var(--accent);border-color:var(--line-2)}
/* ghost hover = the quiet bar-control notch (border darkens; text keeps its accent identity) */
.btn--ghost:hover:not(:disabled){border-color:var(--ink-3)}
.btn--ghost:active{background:var(--accent-bg)}
.btn--disabled,.btn:disabled{background:var(--surface-2);color:var(--ink-3);border-color:var(--line);cursor:not-allowed}

/* ============================================================
   CHIPS  (from components/chip.html)
   ============================================================ */
.chip{
  font-family:var(--body);font-weight:400;font-size:var(--t-meta);
  height:var(--ctl);padding:0 var(--s-4);border-radius:var(--r-1);
  display:inline-flex;align-items:center;gap:var(--s-2);line-height:1;
  background:var(--surface);color:var(--ink-2);border:1px solid var(--line-2);
  transition:border-color var(--dur-1),color var(--dur-1);
}
/* Quiet, movement-free hover — the ONE hover for every selectable chip in the family (.chip · .facet-pill ·
   .sortopt): border + text darken ONE notch (rest --line-2 → --ink-3), NO lift. Chips PICK, tiles OPEN.
   Skips the selected/disabled/empty states, which own their own look. Single source, like the .on block. */
.chip:not(.on):not(:disabled):not(.chip-empty):hover,
.facet-pill:not(.on):hover,
.sortopt:not(.on):hover{border-color:var(--ink-3);color:var(--ink)}
.chip .n{font-size:var(--t-eye);color:var(--ink-2)}  /* solid --ink-2, not opacity-dimmed: AA-legible count (2026-07-25) */
/* small superscript status badge on a chip label (e.g. Recall "Beta", Cases "New") */
.chip-badge{font-family:var(--sans);font-size:var(--t-micro);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);text-transform:uppercase;vertical-align:super;margin-left:4px;line-height:0}
.chip-badge-beta{color:var(--ink-3)}
.chip-badge-new{color:var(--accent)}
.chip.on .chip-badge-beta{color:var(--accent-press)}
/* .chip.on fill/border/text = the single SELECTION-STATE source below (.chip.on,.sortopt.on,.facet-pill.on) */
.chip.on .n{color:var(--accent-press)}
.chip:disabled,.chip.chip-empty{opacity:.4;border-style:dashed;cursor:default;color:var(--ink-3)}

/* ============================================================
   eyebrow / section headings
   ============================================================ */
.eyebrow{font-family:var(--sans);font-weight:var(--w-cap-strong);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);margin:0 0 var(--s-3)}

/* ============================================================
   HOME — Google-style centered search hero
   ============================================================ */
.hero{
  flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;
  text-align:center;padding:var(--s-6) var(--s-4);gap:var(--s-5);min-height:60vh;
}
.hero-logo{font-family:var(--display);font-weight:700;font-size:var(--t-hero);line-height:1;color:var(--accent);letter-spacing:-.02em;display:inline-flex;align-items:center;gap:var(--s-3)}
.hero-logo b{color:var(--accent)}
.searchbox{
  display:flex;align-items:center;gap:var(--s-3);width:100%;max-width:560px;
  height:52px;padding:0 var(--s-5);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-2);
  box-shadow:var(--shadow-1);
}
.searchbox:focus-within{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)}
.searchbox .si-ic{color:var(--ink-3);font-size:var(--t-h3)}
.searchbox input{flex:1;border:none;background:none;outline:none;color:var(--ink);font-size:var(--t-h3)}
.searchbox input::placeholder{color:var(--ink-3)}

/* search results list (home + drug bank) */
.results{width:100%;max-width:560px;margin:0 auto;display:flex;flex-direction:column;gap:var(--s-2);text-align:left}
.result{
  display:flex;align-items:center;gap:var(--s-3);
  padding:var(--s-3) var(--s-4);border-radius:var(--r-1);
  background:var(--surface);border:1px solid var(--line);
}
.result:hover{border-color:var(--line-2)}
.result .r-generic{font-family:var(--body);font-weight:600;font-size:var(--t-h3);color:var(--ink)}
.result .r-brand{font-size:var(--t-meta);color:var(--ink-2)}
.result .r-class{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--accent);margin-left:auto;text-align:right}

/* ============================================================
   SHARED SEARCH COMBOBOX  (home hero + drug-bank field)
   .searchfield / .searchwrap are the position:relative hosts;
   .sugg is the floating ranked dropdown built by attachDrugSearch.
   ============================================================ */
.searchfield{position:relative;width:100%;max-width:560px;margin:0 auto}
.sugg{
  position:absolute;left:0;right:0;top:calc(100% + var(--s-2));z-index:40;
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-2);
  box-shadow:var(--shadow-2);
  padding:var(--s-1);max-height:min(60vh,420px);overflow-y:auto;overscroll-behavior:contain;
  animation:suggIn var(--dur-1) ease-out;
}
@keyframes suggIn{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){.sugg{animation:none}}
.sugg[hidden]{display:none}
.sugg-row{
  display:flex;align-items:center;gap:var(--s-3);cursor:pointer;
  padding:var(--s-3) var(--s-4);border-radius:var(--r-1);
}
.sugg-row.on{background:var(--accent-bg)}
/* Rx/Dx type badge on the unified home search (drug vs disease) */
.sugg-kind{flex:0 0 auto;font-family:var(--sans);font-size:var(--t-micro);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);text-transform:uppercase;padding:2px var(--s-2);border-radius:var(--r-1);line-height:1.5}
.sugg-kind--rx{color:var(--accent);background:var(--accent-bg)}
.sugg-kind--dx{color:var(--ink-2);background:var(--surface-2)}
.sugg-kind--tool{color:var(--walk-accent);background:var(--walk-tint)}
.sugg-kind--fam{color:var(--ink-2);background:var(--surface-2)}
.sugg-main{display:flex;align-items:baseline;gap:var(--s-3);min-width:0;flex:1}
.sugg-generic{font-family:var(--body);font-weight:600;font-size:var(--t-h3);color:var(--ink);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.sugg-brand{font-size:var(--t-meta);color:var(--ink-2);white-space:nowrap}
.sugg-class{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--accent);margin-left:auto;text-align:right;white-space:nowrap}
.sugg-hl{background:transparent;color:var(--accent-press);font-weight:700;padding:0}
.sugg-row.on .sugg-hl{color:var(--accent-press)}
.sugg-empty{color:var(--ink-3);font-size:var(--t-meta);text-align:center;padding:var(--s-5) var(--s-4)}

/* ============================================================
   DRUG BANK — LIST  (verbatim port of 537's drugsView / renderDrugList
   CSS: .dview column, search field, Organize strip, "Drugs N" title,
   grouped list with letter headers + counts + "›" chevron rows.
   Colors re-pointed to rxdx rust/cream tokens; heights 38→34 to sit
   inside rxdx's 44px top bar.)
   ============================================================ */
.dview{display:flex;flex-direction:column}
/* search field — opens BELOW the bar when the magnifier is toggled (537 .searchwrap/.search) */
/* ⛳ ONE TYPE SIZE ACROSS THE PANEL. 🔬 The search read `--t-body` (14px) while the three buttons directly
   beneath it read `--t-meta` (13px) — two sizes in five stacked controls, which is exactly what 👤 sees as
   "all different". ⛔ A search field is not more important than the buttons under it. */
.search{width:100%;height:var(--ctl);background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-1);padding:0 var(--s-4);font-size:var(--t-meta);color:var(--ink);margin-bottom:var(--s-3)}
.search:focus{outline:none;border-color:var(--accent)}
.search:focus-visible{box-shadow:0 0 0 3px var(--accent-bg)}  /* keyboard focus ring (border tint alone isn't enough) */
.searchwrap{margin:0 0 var(--s-3);position:relative}
.searchwrap .search{margin-bottom:0}
.search::placeholder{color:var(--ink-3)}

/* top-bar list tools (icon-only): collapse-all / Organize / Search (537 .dtools) */
.dtools{display:inline-flex;gap:var(--s-2);align-items:center;flex-wrap:nowrap}
.searchbtn,.sortbtn-m,.collapsebtn,.filterbtn{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;position:relative;
  width:var(--ctl);height:var(--ctl);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink-2);cursor:pointer;transition:border-color var(--dur-1),color var(--dur-1)}
/* quiet bar-control hover (same theory as the chips): border + icon darken one notch, no movement; the accent .open state owns its own look */
.searchbtn:not(.open):hover,.sortbtn-m:not(.open):hover,.collapsebtn:not(.open):hover,.filterbtn:not(.open):hover{border-color:var(--ink-3);color:var(--ink)}
.searchbtn:active,.sortbtn-m:active,.collapsebtn:active,.filterbtn:active{background:var(--surface-2)}
/* all toggles share ONE "on" look — accent border + icon + tint (matches 537) */
.collapsebtn.open,.sortbtn-m.open,.searchbtn.open,.filterbtn.open{border-color:var(--accent);color:var(--accent);background:var(--accent-bg)}
.searchbtn-ic,.collapsebtn-ic,.sortbtn-ic{display:inline-flex;align-items:center;justify-content:center}
.ic{width:20px;height:20px;display:block}
.searchbtn .ic,.sortbtn-m .ic,.collapsebtn .ic,.filterbtn .ic{width:18px;height:18px}
/* a small dot on the filter button signals filters are active even when the panel is closed */
.filterbtn-dot{position:absolute;top:5px;right:5px;width:6px;height:6px;border-radius:var(--r-pill);background:var(--accent)}

/* ── REFERENCE FILTER PANEL (architect's browse.filters → the owner's "by settings" browse) ─────────────
   One collapsible group per facet; OR-within chips; active picks summarized above the list with a count + clear. */
.filterpanel{display:block;padding:var(--s-2) 0 var(--s-3);border-bottom:1px solid var(--line);margin-bottom:var(--s-3)}
.ffacet{border-top:1px solid var(--line);}
.ffacet:first-of-type{border-top:none}
.ffhead{display:flex;align-items:center;gap:var(--s-2);width:100%;padding:var(--s-2) 0;background:none;border:none;
  cursor:pointer;color:var(--ink-2);font-family:var(--sans);font-size:var(--t-body);text-align:left}
.ffhead:hover{color:var(--ink)}
.ffcaret{flex:0 0 auto;color:var(--ink-3);font-size:var(--t-meta)}
.ffname{flex:1 1 auto}
.ffsel{flex:0 0 auto;min-width:18px;height:18px;padding:0 var(--s-1);display:inline-flex;align-items:center;justify-content:center;
  border-radius:var(--r-pill);background:var(--accent);color:var(--surface);font-size:var(--t-eye)}
.ffopts{display:flex;flex-wrap:wrap;gap:var(--s-2);padding:0 0 var(--s-3) var(--s-4)}
.ffopt{display:inline-flex;align-items:center;gap:var(--s-1);padding:0 var(--s-3);height:var(--ctl-sm);
  border:1px solid var(--line-2);border-radius:var(--r-1);background:var(--surface);color:var(--ink-2);cursor:pointer;
  font-family:var(--sans);font-size:var(--t-meta);transition:border-color var(--dur-1),color var(--dur-1),background var(--dur-1)}
.ffopt:not(.on):hover{border-color:var(--ink-3);color:var(--ink)}
/* ⛳ "+N more" is a DOOR, not a value — dashed so it cannot be mistaken for something you can filter by. */
.ffopt-more{border-style:dashed;color:var(--ink-3)}
.ffopt.on{border-color:var(--accent);color:var(--accent);background:var(--accent-bg)}
.ffopt-n{color:var(--ink-3);font-size:var(--t-eye)}
.ffopt.on .ffopt-n{color:var(--accent)}
/* active-filter summary strip */
.activefilters{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-2);padding:var(--s-2) 0 var(--s-3)}
.af-n{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin-right:var(--s-1)}
.afchip{display:inline-flex;align-items:center;gap:var(--s-1);padding:0 var(--s-2) 0 var(--s-3);height:var(--ctl-sm);
  border:1px solid var(--accent);border-radius:var(--r-1);background:var(--accent-bg);color:var(--accent);cursor:pointer;
  font-family:var(--sans);font-size:var(--t-meta)}
.afchip .afk{color:var(--ink-3);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps)}
.afchip .afx{color:var(--accent);font-size:var(--t-eye)}
.afchip:hover{background:var(--surface-2)}
/* back to the card a tag was clicked from — same control shape as the chips beside it */
.af-back{display:inline-flex;align-items:center;height:var(--ctl-sm);padding:0 var(--s-3);
  border:1px solid var(--line-2);border-radius:var(--r-1);background:var(--surface);color:var(--ink-2);
  font-family:var(--sans);font-size:var(--t-meta);cursor:pointer}
.af-back:hover{border-color:var(--accent);color:var(--accent)}
/* The family SCOPE chip is the same component as a filter chip — ⛔ deliberately not a second pill. It is the
   one chip the reader did not pick from this page (they arrived on it from a disease card), so it leads the
   row and carries no other styling: same shape, same removal, same place. */
.af-fam{order:-1}
.af-clear{margin-left:auto;padding:0 var(--s-2);height:var(--ctl-sm);background:none;border:none;cursor:pointer;
  color:var(--ink-3);font-family:var(--sans);font-size:var(--t-meta);text-decoration:underline}
.af-clear:hover{color:var(--ink)}
.collapsebtn-ic.right{transform:rotate(-90deg)}  /* caret points down when expanded, right when all-collapsed */

/* Organize strip — the four sort options, 2×2 on narrow, one row of 4 ≥560px (537 .sortpanel) */
.sortpanel{display:block;background:transparent;border:none;padding:var(--s-2) 0 var(--s-3);margin-bottom:0}
.sortpanel.closed{display:none}
/* ⛳ same convention as `.sortpanel` — the class hides it on narrow; the ≥1000px block shows it again. */
.filterpanel.closed{display:none}
/* The Type + Sort options FLOW (flex-wrap, content-width) instead of a rigid 3-col grid — so 4/5/6 chips
   never orphan a lonely cell; each row is labeled (.sortpanel-cap "Show" / "Sort") so its JOB is obvious. */
.sortpanel-grid{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.sortpanel-grid .sortopt{flex:0 1 auto;padding:0 var(--s-4);white-space:nowrap}
/* SHOW (the Type filter) is the primary control — an EQUAL-width segmented control: every chip is the same
   width (equal grid columns), filling the container on desktop and wrapping to equal columns on a phone.
   (flex:1 1 auto sized chips to their label — "Disease States" wider than "All" — so a grid is used instead.)
   SORT stays compact (content-width, flex) below it. */
.sortpanel.banktype .sortpanel-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(116px,1fr))}
/* Course FILTER row — same option-button look as the sort tabs, its own labeled grid of 4. */
/* small muted caption separating Sort from Course within the Organize panel */
.sortpanel-cap{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;
  letter-spacing:var(--track-caps);color:var(--ink-2);margin:var(--s-2) 0 var(--s-1)}
.sortpanel-cap:first-child{margin-top:0}
/* --surface (not --bg): every chip in the family rests on the raised card cream over the page tan — the
   sort toggles are chips too, so they match .chip/.facet-pill instead of blending into the page. */
.sortopt{height:var(--ctl);padding:0 var(--s-2);background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-1);font-size:var(--t-meta);color:var(--ink-2);text-align:center;
  display:flex;align-items:center;justify-content:center;line-height:1.2;cursor:pointer;
  transition:border-color var(--dur-1),color var(--dur-1)}
/* .sortopt.on = the single SELECTION-STATE source below (.chip.on,.sortopt.on,.facet-pill.on) */
.sortopt:active{opacity:.7}

/* "Drugs N · by class" title row (537 .dtitle) */
.dtitle{display:flex;align-items:baseline;gap:var(--s-3);padding:var(--s-2) 0 var(--s-3);margin-bottom:var(--s-2)}
.dtitle-meta{font-size:var(--t-meta);color:var(--ink-2);font-variant-numeric:tabular-nums}

/* grouped list — section header (letter/class/topic/exam) + count, foldable (537 .dgroup) */
.dgroup{display:flex;align-items:baseline;gap:var(--s-2);font-family:var(--sans);font-size:var(--t-meta);
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);padding:var(--s-4) 0 var(--s-2);
  margin-top:var(--s-2);border-bottom:1px solid var(--line-2);
  cursor:pointer;-webkit-user-select:none;user-select:none;transition:color var(--dur-1)}
.dgroup:first-child{margin-top:0;padding-top:0}
.dgroup:active{opacity:.6}
.dgroup-caret{font-size:var(--t-meta);color:var(--ink-3);opacity:.7;width:1em;display:inline-block}
.dgroup.collapsed{color:var(--ink-2)}
.dgroup-l{font-size:var(--t-h3);font-weight:700;color:var(--ink);letter-spacing:.02em}
.dgroup-n{font-size:var(--t-eye);color:var(--ink-3);font-variant-numeric:tabular-nums}
/* rows: generic (bold) + class subtitle stacked, "›" chevron right (537 .drow) */
.dlist{display:flex;flex-direction:column}
.drow{display:flex;align-items:center;gap:var(--s-3);padding:var(--s-2) 0;
  border-bottom:1px solid var(--line);text-align:left;width:100%;background:none;cursor:pointer;transition:color var(--dur-1)}
.drow:active{background:var(--surface-2)}
.drow:hover .nm{color:var(--accent)}
/* NOTE: this inner column is `.drow-main`, NOT `.main` — the layout `.main` (the centered content
   column) carries 24px vertical padding, and the old class collision inflated every row ~48px. */
.drow .drow-main{flex:1;min-width:0;display:flex;flex-direction:column;gap:var(--s-1)}
.drow .nm{font-weight:600;font-size:var(--t-body);line-height:1.2;color:var(--ink)}
.drow .cl{font-size:var(--t-meta);color:var(--ink-2);line-height:1.2;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ⛳ A BANK ROW'S CLASS LINE GETS A SECOND LINE ON A PHONE. One line is right on a wide row; at 390px
   "Calcitonin Gene-related Peptide (CGRP) receptor antagonist" missed by 10px and ellipsized. Two lines
   cost 16px of row height and lose nothing; still capped, so a very long class cannot run away.
   ⚠️ IT LIVES HERE, NOT IN THE PHONE BLOCK 700 LINES ABOVE — same specificity, and the base rule below it
   would have won on source order. That is the `.hub-add` trap documented at the top of this file, and I
   walked into it once on this very change. */
@media (max-width:560px){
  .drow .cl{white-space:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical}
}
.drow .chev{color:var(--ink-3);font-size:var(--t-body);flex:0 0 auto}
/* Rx/Dx type tag — shown on rows only in the mixed "All" view of the unified bank, so a drug and a
   disease are told apart at a glance. Rx = accent tint (the drug side), Dx = neutral surface. */
.drow .drow-tag{flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;
  min-width:2rem;height:1.25rem;padding:0 var(--s-1);border-radius:var(--r-1);
  font-family:var(--sans);font-size:var(--t-micro);font-weight:600;letter-spacing:.06em}
.drow .drow-tag-rx{background:var(--accent-bg);color:var(--accent)}
.drow .drow-tag-dx{background:var(--surface-2);color:var(--ink-2)}
/* Tl = clinical Tool (the 3rd reference type). A calm petrol/teal tint (the walk palette's cool accent)
   so it reads as its OWN category, distinct from Rx (rust) and Dx (neutral gray). */
.drow .drow-tag-tool{background:var(--walk-tint);color:var(--walk-accent)}
/* Fm = ICD Family Card (the hierarchy/structure type). Neutral like Dx (a family IS a group of diseases);
   the "Fm" letters carry the distinction. */
.drow .drow-tag-fam{background:var(--surface-2);color:var(--ink-2)}
/* TYPE FILTER — [All · Drugs · Diseases]. Reuses the .sortopt button look so it matches the Sort row; it
   is its OWN .sortpanel so it collapses TOGETHER with the Sort row under the one Organize toggle. Just a
   small gap to the Sort row below — no divider line. */
.sortpanel.banktype{padding-bottom:0;margin-bottom:0}
/* (it carries .sortpanel, so the existing .sortpanel.closed rule hides it with the Sort row) */
.empty{text-align:center;color:var(--ink-3);padding:var(--s-6) 0;font-size:var(--t-body)}

/* ---- amber "!" call-out note (537 clarnote) — drug card + explanation ---- */
.clarnote{
  display:flex;gap:var(--s-3);align-items:flex-start;
  margin-top:var(--s-3);padding:var(--s-3) var(--s-4);
  background:var(--warn-bg);border:1px solid var(--warn-border);border-radius:var(--r-1);
}
.card .clarnote{margin-top:var(--s-4)}
.explain .clarnote,.exp-why .clarnote,.ebody .clarnote{margin:var(--s-3) 0 0}
.clarnote-ic{
  flex:none;width:20px;height:20px;border-radius:50%;
  background:var(--warn);color:var(--on-accent);font-weight:700;font-size:var(--t-eye);
  display:flex;align-items:center;justify-content:center;line-height:1;margin-top:1px;
}
.clarnote-tx{font-size:var(--t-meta);line-height:1.55;color:var(--ink)}
.clarnote-tx b{color:var(--warn-ink)}

/* ============================================================
   DRUG BANK — CARD  (verbatim port of 537's drugDetailView: .card
   identity table + .subtab strip + bordered .subpanel per tab.)
   ============================================================ */
.dcard{width:100%}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-5)}
/* TABULAR FIGURES on every reference value + dose chip — in a drug reference digits must line up so a dose or
   half-life can't be misread (a documented clinical-safety practice; medical UIs use tabular numerals for exactly
   this). Our body face is already a screen-designed sans (Inter), per the same guidance.
   ⛳ It said "DM Sans" until 2026-09-17 — stale since the switch to Inter, and a stale comment naming a
   font the system no longer ships is how `font-family:'DM Sans'` stayed hardcoded in ecg-lab and looked
   sanctioned. C11 found it; the comment is corrected so the next reader is not told the old answer. */
.dcard .dd-tbl td,.dcard .tag{font-variant-numeric:tabular-nums}
/* EVERY card name (drug · disease · family · tool) uses the BODY sans — the Fraunces/--display serif is the
   BRANDING face ONLY (the rxdx wordmark). One rule, so it changes on every card at once (owner: "if you change
   the template it should change every one of them"; "times new roman is only for branding"). */
.dd-name{font-family:var(--body);font-weight:600;font-size:var(--t-h1);line-height:1.05;margin-bottom:var(--s-2)}
/* "Hear it" pronunciation button — a small inline speaker beside the generic name (Web Speech API) */
.saybtn{display:inline-flex;align-items:center;justify-content:center;vertical-align:middle;margin-left:var(--s-2);width:var(--ctl-sm);height:var(--ctl-sm);padding:0;border:1px solid var(--line);border-radius:var(--r-pill);background:var(--surface-2);color:var(--ink-2);cursor:pointer;transition:color var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out)}
.saybtn:hover{color:var(--accent);border-color:var(--accent)}
.saybtn .ic{width:1em;height:1em}
/* one-liner slot of the universal card top (CARD_TEMPLATE_MASTER) — a quiet subtitle under the name; empty→omitted */
.dd-oneliner{font-family:var(--body);font-size:var(--t-body);line-height:1.4;color:var(--ink-2);margin-bottom:var(--s-3)}
/* identity + field tables are real <table>s (537 .dd-tbl) */
.dd-tbl{width:100%;border-collapse:collapse}
/* One row height and one label column, whatever the value is. A pill value used to make its row 8px
   taller than a plain-text one, and a percentage label column landed on a different x in each card. */
.dd-tbl td{height:var(--ctl-2l);padding:var(--s-2) 0;border-bottom:1px solid var(--line);
  vertical-align:middle;font-size:var(--t-body);color:var(--ink)}
/* ⛳ THE RULE IS **ONE LABEL COLUMN PER CARD, SIZED TO THAT CARD'S LONGEST LABEL** — and the two cards
   reach it differently BECAUSE THEY ARE SHAPED DIFFERENTLY, which is the honest way to apply one rule.
   🔬 I tried `width:1%` (the table spelling of `max-content`) here first and MEASURED the result: the drug
   card went back to THREE content edges — 451 · 510 · 532 — because it is built from SEVERAL tables and
   each one then sized to its own longest label. ⚖️ `max-content` equalises labels WITHIN a table; it
   cannot equalise across three of them, and a card whose columns disagree with themselves is the defect
   👤 reported in the first place.
   ⛳ So: the guidance card (ONE list) uses `max-content` and gets a tight 70px column. The drug card
   (SEVERAL tables) pins `--kv-k`, which is sized for its own longest label, "FDA PHARMACOLOGIC CLASS".
   ⛔ Not two rules — one rule, and the cap is the number the wider card needs. */
.dd-tbl td:first-child{color:var(--ink-2);width:var(--kv-k);font-family:var(--sans);font-size:var(--t-eye);  /* --ink-2 for AA contrast (Governance A, 2026-07-25) */
  text-transform:uppercase;letter-spacing:var(--track-caps);padding-right:var(--s-3);vertical-align:middle}
@media (max-width:720px){.dd-tbl td:first-child{width:9rem}}
/* ⛳ ON A PHONE THE ROW STACKS: label above, value below, both on the left margin. 👤, 2026-09-19:
   *"on mobile you want to left justify that text there? its sloppy."* 🔬 He is describing the two-column
   table at 390px — a 144px label column against a 164px value column, so a short label sat in a wide
   empty cell while "Angiotensin Converting Enzyme Inhibitor" wrapped to three lines beside it. ⚖️ Neither
   column is wrong; a TABLE is wrong at that width. Stacked, the value gets the full 358px, every label and
   every value start on the same x, and nothing wraps that did not have to. */
@media (max-width:560px){
  .dd-tbl,.dd-tbl tbody,.dd-tbl tr{display:block;width:100%}
  .dd-tbl td{display:block;width:auto;height:auto;border-bottom:none;padding:0}
  .dd-tbl td:first-child{width:auto;padding:0 0 var(--s-1)}
  .dd-tbl tr{padding:var(--s-2) 0;border-bottom:1px solid var(--line)}
  .dd-tbl tr:last-child{border-bottom:none}
}
.dd-tbl tr:last-child td{border-bottom:none}
/* ⛔ THE ADVANCED DRAWER'S RULES ARE GONE with the drawer itself (👤, 2026-09-19: "remove all
   advanced"). A5 would have flagged them as styling a class nothing writes. */
.dd-dash{color:var(--ink-3)}
/* DB-tag pills: each governed value renders as a .tag (537 .tag/.tagrow) */
.tagrow{display:flex;flex-wrap:wrap;gap:var(--s-1)}
/* ⛳ A VALUE READS LEFT. 👤, 2026-09-19: *"on mobile you want to left justify that text there? its
   sloppy."* 🔬 A chip inherits the card's text-align, so a long value that wrapped to three lines —
   "Angiotensin Converting Enzyme Inhibitor" at 390px — came out centered, three ragged lines in a box.
   ⛔ Centering is for a label on a control, never for prose in a cell. */
.tag{display:inline-block;padding:2px var(--s-2);background:var(--surface-2);border:1px solid var(--line-2);
  border-radius:var(--r-1);font-size:var(--t-meta);line-height:1.5;color:var(--ink-2);text-align:left}
/* TRAVERSAL nav chip (architect #555): a governed value that is a SHARED node → clickable, opens a facet view
   (every entity that shares it). Reads like a .tag but signals interactivity — a diagnosis-blue tint, a hover
   lift, and a small › cue. A singleton value stays a plain .tag (a label, not a view) per the nav rule. */
.tag-nav{cursor:pointer;font:inherit;font-size:var(--t-meta);line-height:1.5;color:var(--dx);
  padding:2px var(--s-1) 2px var(--s-2);background:var(--surface-2);border:1px solid var(--line-2);
  border-radius:var(--r-1);display:inline-flex;align-items:center;gap:2px;
  transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out)}
.tag-nav:hover,.tag-nav:focus-visible{background:var(--accent-bg);border-color:var(--dx);outline:none}
.tag-nav .tag-nav-ic{color:var(--dx);opacity:.5;font-size:var(--t-eye)}
.tag-nav:hover .tag-nav-ic,.tag-nav:focus-visible .tag-nav-ic{opacity:1}
/* FACET banner atop a filtered list: which node opened this view + a one-tap Clear back to the full list. */
.facet-banner{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);
  margin-bottom:var(--s-3);padding:var(--s-2) var(--s-3);background:var(--accent-bg);
  border:1px solid var(--line-2);border-left:3px solid var(--dx);border-radius:var(--r-1)}
.facet-txt{display:flex;align-items:baseline;gap:var(--s-2);flex-wrap:wrap;min-width:0}
.facet-lead{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2)}
.facet-val{font-size:var(--t-body);font-weight:600;color:var(--ink)}
.facet-n{font-size:var(--t-meta);color:var(--ink-3);font-variant-numeric:tabular-nums}
.facet-clear{cursor:pointer;font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-2);
  background:none;border:1px solid var(--line-2);border-radius:var(--r-1);padding:2px var(--s-2);flex:none}
.facet-clear:hover{color:var(--ink);border-color:var(--ink-3)}
/* ⛳ A SECTION IS THE SAME TWO COLUMNS AS A ROW. 👤, 2026-09-17: *"for this drug card why is indications
   not the same way?"*
   🔬 MEASURED on lisinopril · Clinical — THREE different content left edges on ONE card:
     15 table rows   label x=308 → content x=**564**
     Notes           label x=308 → content x=**323**
     Indications     label x=308 → content x=**308**  (flush under its own label)
   ⚖️ Indications and Notes are not table rows — Indications is a composite panel (headline + parts +
   limitation + FDA label) that 👤 asked for in the Dosing shape, so it genuinely cannot be a `<td>`.
   ⛔ But "different structure" was never a reason to sit on a different axis. The eye tracks ONE left
   edge down a card; Treats and Adverse effects are chips at 564 and Indications is chips at 308, so the
   card reads as two cards spliced together.
   ⛳ A section now puts its label in the label column and its content in the value column — the same
   `--kv-k` split the rows use — so every chip on the card, in a row or in a panel, starts at one x. */
.dd-section{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-2);margin-bottom:var(--s-3)}
/* ⚠️ NO `gap` — THE SPACING LIVES INSIDE THE LABEL COLUMN, exactly as it does in the table.
   🔬 With a 12px gap the section body landed at 544 while the table rows sat at 532: the table puts its
   `padding-right` INSIDE the 14rem cell, so adding a gap on top of a 14rem flex basis pushes 12px further.
   ⛳ Same construction as `.dd-tbl td:first-child` → same x, provably. */
.dd-secwrap{display:flex;align-items:flex-start;padding:var(--kv-pad) 0;
  border-bottom:1px solid var(--line)}
.dd-secwrap:last-child{border-bottom:0}
.dd-secwrap > .dd-section{flex:0 0 var(--kv-k);margin-bottom:0;padding-top:2px;padding-right:var(--kv-gap)}
.dd-secwrap > .dd-secbody{flex:1 1 auto;min-width:0}
/* ⛳ AND THIS ONE STACKS TOO. It is the same two-column shape as `.dd-tbl` built out of flex instead of a
   table — Indications was the last row on the card still pushing its values into a narrow right column at
   390px while its label sat in 144px of empty space. One rule for the card means both spellings stack. */
@media (max-width:560px){
  .dd-secwrap{flex-direction:column}
  .dd-secwrap > .dd-section{flex:0 0 auto;padding-right:0;padding-bottom:var(--s-1)}
}
.dd-subsec{margin-bottom:var(--s-3)}
.dd-bullets{margin:0 0 var(--s-4);padding-left:1.1em;list-style:disc}
.dd-bullets li{font-size:var(--t-body);color:var(--ink);line-height:1.5;margin-bottom:var(--s-2)}
.dd-bullets li:last-child{margin-bottom:0}
.dd-empty{color:var(--ink-3);font-size:var(--t-meta);padding:var(--s-1) 0}
/* setup-wizard empty state (e.g. recall not produced for this course yet) — a plain, centered, muted note */
.setup-empty{color:var(--ink-2);font-size:var(--t-body);text-align:center;max-width:44ch;margin:0 auto var(--s-4);padding:var(--s-3) var(--s-4);
  background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-1)}
/* MERGED OVERVIEW (governor spec A): Overview (+ Pathophysiology) fold INTO the top identity card, below the
   dd-tbl — one card at the top. A hairline rule separates it from the identity table; each section carries a
   quiet eyebrow heading in the same voice as .dd-section. */
/* the merged Overview LEADS the card (right under the name); a slim Family/Guideline table, if any, follows it
   with a hairline above that table. So the overview itself carries no top rule. */
.dd-overview{margin-top:var(--s-3)}
.dd-overview + .dd-tbl{margin-top:var(--s-4);border-top:1px solid var(--line)}
.dd-overview + .dd-tbl td{padding-top:var(--s-4)}
/* WALK LEAD (governor spec D): the interactive treatment walk leads the Treatment panel; a little breathing
   room below it when static content follows. */
.dz-walk-lead{margin-bottom:var(--s-4)}
.dz-walk-lead:last-child{margin-bottom:0}
/* Contraindication pills (Clinical): condition term + an ABSOLUTE/relative badge. Absolute carries a
   red border + red badge; the split is governed (FDA) and itself testable, so it's shown, not hidden. */
.tag.contra-absolute{border-color:var(--danger)}
.tag.contra-relative{border-color:var(--line-2)}
.contra-lvl{margin-left:var(--s-1);font-family:var(--sans);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.tag.contra-absolute .contra-lvl{color:var(--danger)}
/* Lipid / ECG panels (Pharma): a compact key→direction mini-grid (LDL/HDL/TG/VLDL, PR/QRS/QT). */
.lipidgrid{display:grid;grid-template-columns:auto 1fr;gap:var(--s-1) var(--s-3);align-items:baseline}
.lipidgrid .lp-k{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);color:var(--ink-3);text-transform:uppercase}
.lipidgrid .lp-v{font-size:var(--t-meta);color:var(--ink);font-variant-numeric:tabular-nums}
.lipidgrid .lp-none{font-size:var(--t-meta);color:var(--ink-3);font-style:italic}
/* Dose "By scenario" matrix (Dosing): scenario groups, each a scenario pill + per-slot dose lines. */
.dose-matrix-h{margin-top:var(--s-3)}
.dose-scenarios{display:flex;flex-direction:column;gap:var(--s-3)}
.dose-scenario{display:flex;flex-direction:column;gap:var(--s-2)}
.dose-scenario-key{display:flex}
.dose-scenario-body{display:flex;flex-direction:column;gap:var(--s-2);padding-left:var(--s-3)}
.dose-line{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.dose-pills{display:flex;gap:var(--s-1);flex-wrap:wrap;align-items:center}
.dose-slot-pill{min-width:78px;text-align:center}
/* Indication panel (Clinical) — the same shape as the dose matrix, because it is the same idea: a fact
   broken into governed parts and reassembled on screen. One ROW per indication; the CONDITION leads and
   links to its disease card when one exists; the qualifiers (intent · severity · population · line ·
   combination) follow as quiet parts. A row with no qualifiers is just its headline — no empty slots. */
.ind-h{margin-top:var(--s-3)}
.ind-rows{display:flex;flex-direction:column;gap:var(--s-3)}
.ind-row{display:flex;flex-direction:column;gap:var(--s-2)}
.ind-head{display:flex;flex-wrap:wrap;gap:var(--s-1)}
/* the condition itself carries the weight — ink, not the muted chip gray the qualifiers use */
.tag-ind-head{color:var(--ink);font-weight:600}
a.tag-ind-head.ind-link{color:var(--dx);text-decoration:none;display:inline-flex;align-items:center;gap:2px;
  padding:2px var(--s-1) 2px var(--s-2);
  transition:background var(--dur-1) var(--ease-out),border-color var(--dur-1) var(--ease-out)}
a.tag-ind-head.ind-link:hover,a.tag-ind-head.ind-link:focus-visible{background:var(--accent-bg);border-color:var(--dx);outline:none}
a.tag-ind-head.ind-link .tag-nav-ic{color:var(--dx);opacity:.5;font-size:var(--t-eye)}
a.tag-ind-head.ind-link:hover .tag-nav-ic,a.tag-ind-head.ind-link:focus-visible .tag-nav-ic{opacity:1}
/* a row whose "condition" is a package-insert sentence renders as PROSE, not as a 300-char chip.
   ⛔ Shown, never hidden — the render stays honest and the extraction defect stays visible. */
.ind-prose{font-size:var(--t-meta);color:var(--ink-2);line-height:1.5;max-width:62ch}
/* A governed value that turned out to be a package-insert SENTENCE. It renders as prose wherever it
   appears — never inside a chip border, which it would blow apart. Shown in full, never truncated. */
.tag-prose{display:block;font-size:var(--t-meta);color:var(--ink-2);line-height:1.5;max-width:62ch;
  margin:2px 0}
.ind-parts{display:flex;flex-wrap:wrap;gap:var(--s-1);padding-left:var(--s-3)}
.tag-ind-part{font-size:var(--t-eye);color:var(--ink-3);background:transparent}
/* a limitation is PROSE, not an atom — a line under the chips, never a chip (professor's contract) */
.ind-limit{padding-left:var(--s-3);font-size:var(--t-meta);color:var(--ink-3);font-style:italic}
/* the verbatim label sentence, and ONLY when the row is provenance-sourced. 0 of 739 rows today. */
.ind-fda{padding-left:var(--s-3);font-size:var(--t-meta);color:var(--ink-3)}
.ind-fda-k{display:inline-block;margin-right:var(--s-1);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-3)}
/* composite dose bubble = amount + unit + frequency fused into one fact; heavier border flags the fusion */
.tag-composite{border-width:2px;border-color:var(--ink-3);color:var(--ink);font-variant-numeric:tabular-nums}
.gap{height:var(--s-3)}
/* Disease subcards UN-HIDDEN 2026-08-01 (owner): the disease DB was rebuilt to a clean identity-only
   skeleton (fabricated/unsourced subcard content deleted), so the Overview→Decisions tabs now show again —
   empty under hide-empty (dimmed), ready for the provenance-first rebuild from slide-sourced atoms. */
/* subcards: segmented tab strip (mobile) → each panel its own bordered card (537 .subtab/.subpanel) */
.subtab-strip{display:flex;flex-wrap:wrap;background:var(--surface-2);border-radius:var(--r-1);padding:var(--s-1);
  gap:var(--s-1);margin-bottom:var(--s-3)}
/* flex:1 1 auto + wrap: each tab is at least as wide as its own label and grows to fill the row; when the
   full set won't fit (6 disease tabs on a phone) they wrap onto a second row instead of truncating. */
.subtab{flex:1 1 auto;min-width:4.5rem;height:var(--ctl);border:none;background:transparent;border-radius:var(--r-1);
  padding:0 var(--s-2);color:var(--ink-2);font-family:var(--body);font-size:var(--t-body);font-weight:400;cursor:pointer;
  white-space:nowrap;transition:background var(--dur-1),color var(--dur-1),box-shadow var(--dur-1)}
.subtab.on{background:var(--surface);color:var(--accent);font-weight:600;box-shadow:0 1px 2px rgba(42,32,24,.10)}
.subtab-dim{opacity:.4;cursor:default;font-style:italic;box-shadow:none}
.subtab-dim:active{background:transparent}
/* ⛳ ON A PHONE THE TABS ARE ONE SIZE. 👤, 2026-09-19: *"on mobile, it needs to be equal size buttons for
   it to look good."* 🔬 They were 74 · 90 · 89 · 85 · 183 · 163 — each sized to its own label, so two rows
   of ragged blocks. ⚖️ A tab strip is a segmented control, and a segmented control's cells are equal by
   definition; the label is what varies, not the target. Three per row, every cell the same width. */
@media (max-width:560px){
  .subtab-strip{display:grid;grid-template-columns:repeat(3,minmax(0,1fr))}
  .subtab{min-width:0;white-space:normal;line-height:1.15;padding:0 var(--s-1);
    display:flex;align-items:center;justify-content:center;text-align:center}
}
/* same padding + radius as .card, so a field label sits on the same x in both */
.subpanel{display:none;background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-2);padding:var(--s-5)}
.subpanel.on{display:block}
.subpanel-h{display:none}   /* the tab strip already labels it */
/* disease card: labeled atomic sections inside each subpanel — SAME two-column rhythm as the drug card's
   .dd-tbl rows (owner: disease cards must read like drug cards, one style system). Label LEFT (34%, caps
   eyebrow), value RIGHT — aligned rows, hairline dividers, even breathing room. */
.dz-field{display:grid;grid-template-columns:34% 1fr;gap:var(--s-4);align-items:start;padding:var(--s-3) 0;border-bottom:1px solid var(--line)}
.dz-field:last-child{border-bottom:none}
.dz-lbl{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);margin-bottom:0;padding-top:2px}
.dz-val{font-size:var(--t-body);color:var(--ink);line-height:1.5}
/* full-width variant: the Treatment Pathway tree spans the whole panel (like the drug card's NOTES block),
   label on its own line above the branches — a 2-col squeeze would crush the if-then rows. */
.dz-field-full{display:block}
.dz-field-full .dz-lbl{margin-bottom:var(--s-3)}
.dz-val p{margin:0}
.dz-list{margin:0;padding-left:1.1em}
.dz-list li{margin:.2em 0}
/* grouped disease atoms: values grouped under their element (element = a --meta caption, values joined) */
.grp{margin:0 0 var(--s-3)}
.grp:last-child{margin-bottom:0}
/* keyed-atom bullets (classification / criteria / therapies / targets / tools): bold lead reads as one phrase */
.dz-key{font-weight:600;color:var(--ink)}
/* Connections: causal edges (concept-map propositions). Each edge = a target + its plain why/how
   (self-explanation). Target is the proposition; why/how are muted, constrained elaborations. */
.dz-edges{list-style:none;margin:0;padding:0}
.dz-edge{padding:var(--s-2) 0;border-bottom:1px dashed var(--line)}
.dz-edge:last-child{border-bottom:none}
.dz-edge-to{display:block;font-weight:600;color:var(--ink)}
.dz-edge-q{display:block;font-size:var(--t-meta);color:var(--ink-2);line-height:1.45;margin-top:2px}
.dz-edge-q em{font-style:normal;text-transform:uppercase;letter-spacing:var(--track-caps);font-size:.85em;color:var(--ink-3,var(--ink-2));margin-right:.35em}
/* Decision pathway — each fork is one clinical question; its branches read as clean WHEN → THEN rules.
   One tidy row per branch (condition muted, action in ink, arrow the accent), wrapping naturally — no
   far-right push / big gap. Consistent with the card's calm type system. */
/* ── DECISION TREE (Treatment Pathway) — its own clean WHITE panel (owner 2026-08-10: "make that background
   white so the colors look better") set apart from the warm paper card, styled in the cool ALT palette
   (the walk's petrol/teal) + a thin/light type treatment, so the reasoning reads as a calm knowledge space
   that matches the interactive walk. Emphasis is by COLOR, not weight — nothing heavy. */
.dz-tree{background:var(--walk-surface);border:1px solid var(--walk-line-2);border-radius:var(--r-2);padding:var(--s-4)}
.dz-fork{padding:var(--s-1) 0 var(--s-2)}
.dz-fork + .dz-fork{border-top:1px solid var(--walk-line);margin-top:var(--s-2);padding-top:var(--s-3)}
.dz-fork-q{font-family:var(--sans);font-weight:400;color:var(--ink);margin-bottom:var(--s-2)}
.dz-fork-ax{font-weight:var(--w-cap);font-size:var(--t-meta);color:var(--walk-accent);text-transform:uppercase;letter-spacing:var(--track-caps);margin-left:.5em}
.dz-branches{list-style:none;margin:0;padding:0}
.dz-branch{padding:var(--s-2) 0;line-height:1.6;color:var(--ink)}
.dz-branch + .dz-branch{border-top:1px solid var(--walk-line)}
.dz-when{color:var(--ink-2)}
.dz-arrow{color:var(--walk-accent);font-weight:400;margin:0 .35em}
.dz-then{color:var(--ink)}
/* decision-tree branch extras: the resolved dose (secondary) + the branch role badge (override/default/alternative) */
.dz-dose{font-weight:400;font-size:var(--t-meta);color:var(--ink-3);margin-left:.35em}
.dz-role{display:inline-block;font-size:var(--t-eye);font-weight:var(--w-cap-strong);text-transform:uppercase;letter-spacing:var(--track-caps);line-height:1.6;padding:0 .5em;margin-left:.4em;border-radius:var(--r-1);border:1px solid var(--walk-line-2);color:var(--ink-3);background:var(--walk-surface);vertical-align:baseline}
.dz-role-override{color:var(--walk-accent);border-color:var(--walk-accent);background:var(--walk-tint)}
.dz-role-alternative{background:transparent}
/* "THE WHY" — the reasoning behind a branch/fork, hidden until clicked (native <details>, works on mobile).
   A quiet teal "Why" toggle that opens a calm pastel callout in the alt palette — the walk's reasoning voice.
   "compact picture, depth on demand" (professor). Thin/light, no quiz colors. */
.dz-branch-line{display:block}
.dz-why{margin:var(--s-1) 0}
.dz-why-sum{display:inline-flex;align-items:center;gap:.35em;cursor:pointer;list-style:none;
  padding:var(--s-1) 0;font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--walk-accent)}
.dz-why-sum::-webkit-details-marker{display:none}
.dz-why-sum::before{content:"+";font-weight:400;font-size:var(--t-meta);line-height:1;width:1em;text-align:center}
.dz-why[open] .dz-why-sum::before{content:"\2212"}
.dz-why-sum:hover{color:var(--walk-accent-press)}
.dz-why-body{margin-top:var(--s-2);padding:var(--s-2) var(--s-3);border-left:2px solid var(--walk-accent);
  background:var(--walk-tint);border-radius:var(--r-1);
  font-family:var(--body);font-size:var(--t-meta);line-height:1.55;color:var(--ink)}
.dz-guide{color:var(--accent)}
.dz-druglink{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
.dz-druglink:hover{border-bottom-color:currentColor}
/* disease-hierarchy links (Part of / Types) mirror the drug link; nickname line tucks under the name */
.dz-dzlink{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
.dz-dzlink:hover{border-bottom-color:currentColor}
/* tool links (the clinical instruments listed on a disease card → their own tool card) mirror the same
   quiet underline-on-hover link treatment as drug/disease links. */
.dz-toollink{color:var(--accent);text-decoration:none;border-bottom:1px solid transparent}
.dz-toollink:hover{border-bottom-color:currentColor}
/* a computable tool's chip is a <button> (opens the slide-up calculator, writer-2 2026-09-28) — strip the
   browser's default button chrome so it reads exactly like the <a> it replaces */
.dz-toolcalc{background:none;border:0;padding:0;margin:0;font:inherit;cursor:pointer;
  display:inline-flex;align-items:center;gap:var(--s-1);vertical-align:middle}
.dz-toolcalc .ic{width:14px;height:14px;flex:none}
/* the calculator sheet's own "go learn more" link, sitting under the compute rows */
.calcsheet-more{color:var(--accent);text-decoration:none;font-family:var(--sans);font-size:var(--t-meta);font-weight:600}
.calcsheet-more:hover{text-decoration:underline}

/* ── GOVERNED CARD-TYPE CHIP (governor 2026-08-10) — a quiet eyebrow chip above every reference card's name
   naming its governed type (Drug · Disease · Clinical tool · Umbrella). The SAME governed set is the list's
   Type filter. Colors echo the list/search kind tags: drug=accent · tool=cool · disease & umbrella=neutral
   (an umbrella IS a disease-family — the label + sub-states block carry the distinction, not a 4th color). */
.rtype-ey{display:flex;align-items:center;gap:var(--s-2);margin-bottom:var(--s-2)}
/* MARKET-STATUS badge (drug lifecycle): sits at the right of the eyebrow row. Withdrawn/Discontinued read RED
   (a safety flag); Limited·REMS amber; Investigational neutral. "On market" renders no badge (silent default). */
.mkt-badge{margin-left:auto;font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);
  text-transform:uppercase;line-height:1.6;padding:0 var(--s-2);border-radius:var(--r-1);border:1px solid var(--line-2);white-space:nowrap}
.mkt-bad{color:var(--bad);border-color:var(--bad);background:color-mix(in srgb,var(--bad) 9%,transparent)}
.mkt-warn{color:var(--warn-ink);border-color:var(--warn-ink);background:color-mix(in srgb,var(--warn-ink) 10%,transparent)}
.mkt-info{color:var(--ink-2);border-color:var(--line-2)}
.rtype-chip{display:inline-flex;align-items:center;gap:var(--s-1);font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);
  text-transform:uppercase;letter-spacing:var(--track-caps);line-height:1.6;padding:0 var(--s-2);border-radius:var(--r-1)}
.rtype-chip .ic{width:13px;height:13px;flex:none}   /* type glyph inherits the chip's role color */
.rtype-chip-drug{color:var(--accent);background:var(--accent-bg)}
.rtype-chip-disease-state{color:var(--ink-2);background:var(--surface-2)}
.rtype-chip-hierarchy{color:var(--ink-2);background:var(--surface-2)}
.rtype-chip-clinical-tool{color:var(--walk-accent);background:var(--walk-tint)}

/* ── CLINICAL-TOOL CARD (3rd reference type) — a small, single-panel card: identity table + "what it
   does". The type pill is a quiet outlined chip in the cool tool palette so a tool reads as its own kind. */
.tool-type{display:inline-block;font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);
  text-transform:uppercase;letter-spacing:var(--track-caps);line-height:1.6;padding:0 var(--s-2);
  border-radius:var(--r-1);color:var(--walk-accent);background:var(--walk-tint)}
.tool-what-body{font-family:var(--body);font-size:var(--t-body);line-height:1.55;color:var(--ink)}

/* ── THE CALCULATOR — 2026-09-27 (✍️-2's formula export + 👤's ask). One row per variable, a segmented
   control when the formula needs a sex-conditioned constant, the live result, the equation shown above it
   so the student sees the math and not just the answer. */
.calc{display:flex;flex-direction:column;gap:var(--s-4)}
/* ⛳ 👤, 2026-09-28: "the equation should look like an equation... clean it up, fix the spacing" — was a
   cramped left-aligned monospace dump. Centered like a textbook display equation, more breathing room,
   and `formatExpr()` (app.js) now raises a written `^exponent` to a real <sup> instead of a caret. */
.calc-expr{font-family:var(--mono);font-size:var(--t-body);color:var(--ink);line-height:2;text-align:center;
  background:var(--surface-2);border-radius:var(--r-1);padding:var(--s-4) var(--s-4);overflow-wrap:anywhere}
.calc-expr sup{font-size:.72em;line-height:0}
.calc-row{display:flex;align-items:center;gap:var(--s-3);flex-wrap:wrap}
.calc-lb{font-family:var(--body);font-size:var(--t-body);color:var(--ink);flex:1 1 auto;min-width:120px}
.calc-unit{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3)}
.calc-in{width:120px;height:var(--ctl);font-family:var(--body);font-size:var(--t-body);color:var(--ink);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);padding:0 var(--s-3)}
.calc-in:focus{outline:none;border-color:var(--walk-accent);box-shadow:0 0 0 3px var(--walk-tint)}
.calc-in{-moz-appearance:textfield;appearance:textfield}
.calc-in::-webkit-inner-spin-button,.calc-in::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.calc-segctl{display:inline-flex;gap:var(--s-2)}
.calc-segbtn{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);background:var(--surface);
  border:1px solid var(--line-2);border-radius:var(--r-1);height:var(--ctl);padding:0 var(--s-4);cursor:pointer}
.calc-segbtn:hover{border-color:var(--walk-accent)}
.calc-segbtn.on{color:var(--on-accent);background:var(--walk-accent);border-color:var(--walk-accent)}
.calc-result{margin-top:var(--s-1);padding-top:var(--s-3);border-top:1px solid var(--line)}
.calc-result .calc-lb{font-weight:600;color:var(--ink)}
.calc-out{font-family:var(--body);font-size:var(--t-h2);font-weight:700;color:var(--walk-accent)}
/* ⛳ THE CALCULATORS SHEET — 🎨 2026-09-28, 👤: *"the user clicks on the calculator we need to use. Like this
   one is eGFR, what about ClCr."* A picker of every computable tool (name + what it outputs), the chosen
   tool's own "How to use" line under its name, the calculator, and ONE action: Insert. (Replaces the
   `.calc-place` "puts this patient at" button, which clicked the arm for the student — the walk now
   preselects and waits for Next, so there is one contract for every path into a decision.) */
/* an even 4-up grid on a wide sheet (a free wrap left MVO2 alone on a third row); one scrolling row on a phone */
.calcpick{flex:0 0 auto;display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:var(--s-2);
  padding:0 var(--s-5) var(--s-4);border-bottom:1px solid var(--line)}
.calcpick-it{display:flex;flex-direction:column;align-items:flex-start;justify-content:flex-start;gap:2px;min-width:0;
  min-height:var(--ctl-2l);padding:var(--s-2) var(--s-3);background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-1);text-align:left;cursor:pointer;transition:border-color var(--dur-1),background-color var(--dur-1)}
.calcpick-nm{font-family:var(--body);font-size:var(--t-body);font-weight:600;color:var(--ink);white-space:nowrap}
.calcpick-out{font-family:var(--body);font-size:var(--t-meta);line-height:1.3;color:var(--ink-3)}
.calcpick-it:hover{border-color:var(--walk-accent)}
.calcpick-it.on{border-color:var(--walk-accent);background:var(--walk-tint);box-shadow:inset 0 0 0 1px var(--walk-accent)}
.calcpick-it.on .calcpick-nm{color:var(--walk-accent-press)}
/* a little wider than the glossary sheet so the 4-up picker reads each tool's output on one or two lines */
#toolcalc-host .sheet-inner{max-width:780px}
.calcsheet{padding-top:var(--s-4)}
.calcsheet-hd{display:flex;flex-direction:column;gap:var(--s-1);margin-bottom:var(--s-4)}
.calcsheet-nm{font-family:var(--body);font-size:var(--t-h3);font-weight:600;color:var(--ink)}
.calcsheet-use{font-family:var(--body);font-size:var(--t-meta);line-height:1.5;color:var(--ink-2)}
.calcsheet-ft{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);flex-wrap:wrap;
  margin-top:var(--s-5);padding-top:var(--s-4);border-top:1px solid var(--line)}
.calc-insert{order:2;height:var(--ctl);padding:0 var(--s-5);font-family:var(--sans);font-size:var(--t-body);font-weight:600;
  color:var(--on-accent);background:var(--walk-accent);border:1px solid var(--walk-accent);border-radius:var(--r-1);cursor:pointer}
.calc-insert:hover:not(:disabled){background:var(--walk-accent-press);border-color:var(--walk-accent-press)}
.calc-insert:disabled{color:var(--ink-3);background:var(--surface-2);border-color:var(--line-2);cursor:default}
@media (max-width:720px){
  .calcpick{display:flex;overflow-x:auto;padding:0 var(--s-4) var(--s-3);scrollbar-width:none}
  .calcpick-it{flex:0 0 auto}
  .calcpick-out{white-space:nowrap}
  .calcpick::-webkit-scrollbar{display:none}
  .calc-insert{order:0;width:100%}
}
.calc-note{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.5;
  background:var(--warn-bg);border-radius:var(--r-1);padding:var(--s-3)}
.calc-worked{font-family:var(--body);font-size:var(--t-meta)}
.calc-worked summary{color:var(--walk-accent);cursor:pointer}
.calc-worked-body{margin-top:var(--s-2);color:var(--ink-2);line-height:1.5}
.calc-src{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3)}

/* ── THE RISK GRID — kdigo_cga's own "why_a_grid" is the spec: two axes read TOGETHER, so it draws as a
   real table with a risk tint per cell, not a flattened list. 4-tier gradient reuses the app's existing
   severity scale (good → warn → accent → bad) rather than inventing a 5th color family. */
.gridwrap{display:flex;flex-direction:column;gap:var(--s-3)}
.grid-axes{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.grid-tbl{width:100%;border-collapse:collapse;table-layout:fixed}
.grid-tbl th,.grid-tbl td{padding:var(--s-2);text-align:center;font-family:var(--body);font-size:var(--t-meta)}
.grid-tbl thead th{color:var(--ink-3);font-weight:600;border-bottom:1px solid var(--line)}
.grid-tbl tbody th{text-align:left;color:var(--ink-2);font-weight:600;white-space:nowrap}
.grid-cell{border-radius:var(--r-1);line-height:1.3}
.grid-risk-lb{display:block;font-weight:600;text-transform:capitalize}
.grid-monitor{display:block;font-size:var(--t-eye);opacity:.85}
.grid-risk-low{background:var(--good-bg);color:var(--good)}
.grid-risk-moderate{background:var(--warn-bg);color:var(--warn)}
.grid-risk-high{background:var(--accent-bg);color:var(--accent-press)}
.grid-risk-very-high{background:var(--bad-bg);color:var(--bad)}
.grid-monitor-lb{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3)}
.grid-legend{display:flex;flex-wrap:wrap;gap:var(--s-3)}
.grid-leg-item{display:flex;align-items:center;gap:var(--s-2);font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2)}
.grid-leg-sw{display:inline-block;width:12px;height:12px;border-radius:var(--r-pill)}
.grid-why{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);line-height:1.5;font-style:italic}

/* ── PARENT (umbrella) disease — the distinct look for an is_parent card: a small eyebrow over the name,
   and a "Sub-states" panel that LEADS with the child disease-states as tappable rows. This is what makes
   a parent read as a family index, not a leaf card. */
/* (the parent's type is now shown by the unified .rtype-chip "Umbrella" eyebrow above the name) */
.dz-parent-lede{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);
  margin:calc(-1 * var(--s-1)) 0 var(--s-3)}
.dz-state-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.dz-state{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);
  padding:var(--s-3) var(--s-4);border:1px solid var(--line);border-radius:var(--r-1);
  background:var(--surface-2);font-size:var(--t-h3);font-weight:600}
.dz-state:hover{border-color:var(--ink-3)}
.dz-state .dz-dzlink{color:var(--ink);border-bottom:none;flex:1 1 auto}
.dz-state .dz-dzlink:hover{color:var(--accent)}
.dz-state-chev{flex:0 0 auto;color:var(--ink-3);font-size:var(--t-h2);line-height:1}
/* FAMILY CARD (a directory, not a content card) — a plain-name headline in the BODY sans (Fraunces/--display is
   the BRANDING face only — owner: "times new roman can't be used here, that's only for branding"), a small ICD
   subtitle, and the member conditions as a wrapping row of tappable CHIPS (owner: "it was a chip, more conditions
   will be added" — chips flow as the family grows). */
/* member conditions are the SAME compact reference chip as everywhere else — a reference database, not big buttons. */
.fam-why{font-family:var(--body);font-size:var(--t-body);line-height:1.55;color:var(--ink);margin-bottom:var(--s-4)}
.fam-chips{display:flex;flex-wrap:wrap;gap:var(--s-1)}
/* Professor card (dz.card) — overview one-liner, treatment scenarios (drug · class · shared dose). */
.dz-oneliner{font-size:var(--t-body);margin-bottom:var(--s-3)}
.dz-scenario{margin:0 0 var(--s-3)}
.dz-scenario-h{font-weight:600;margin-bottom:var(--s-1)}
/* Treatment reference row label (left column of the two-column .dz-field) — a readable Title-case bold, NOT the
   uppercase eyebrow, so scenario names ("Widespread pain") read naturally next to their drug/list (owner). */
.dz-txrow-h{font-family:var(--body);font-weight:400;font-size:var(--t-body);color:var(--ink-2);line-height:1.35;padding-top:1px}
.dz-txdrugs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--s-2)}
.dz-txdrug{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-1)}
/* the scenario is its own chip (the row's key), the drug a LINKED accent chip, the class a quiet chip — every value
   a .tag pulled from the DB (owner). Base .tag carries size/shape; these only re-point color. */
.tag-scen{white-space:normal;color:var(--ink)}
.tag-class{color:var(--ink-3)}
/* a NON-PHARM treatment atom (a procedure, e.g. "Electrical cardioversion") — the real treatment at a
   drug-less endpoint. Not a link (it's a measure, not a drug card); ink-dark so it reads as the answer. */
.tag-nonpharm{white-space:normal;color:var(--ink)}
/* ── THE LINKED-REFERENCE CHIP — the ONE way one card points to another (owner: "it's a reference database,
   it should feel like one"). EVERY card→card connection renders as this same chip: a drug, a disease, a
   family, a clinical tool — in an identity row, an attribute, or a directory. Accent = tappable/navigable;
   hover fills. A non-navigable reference (no card yet) is `.tag-ref.is-plain`, quiet and inert. This retires
   the three earlier renderings (bare dz-dzlink/dz-druglink links · fam-chip · tag-drug). */
a.tag-ref,a.tag-drug{color:var(--accent-press);text-decoration:none;cursor:pointer;transition:border-color var(--dur-1),background-color var(--dur-1)}
a.tag-ref:hover,a.tag-drug:hover{border-color:var(--accent);background:var(--accent-bg)}
.tag-ref.is-plain{color:var(--ink-3)}
.refchips{display:inline-flex;flex-wrap:wrap;gap:var(--s-1)}   /* reference chips inside an identity-row value */
/* atomic dose pulled into the Treatment scenario — the SAME dose · route · frequency chips the drug card's Dosing
   tab shows, joined by scenario (owner). */
/* Reference bank load state — the feed reads Airtable live; show a calm line (not a blank page) until the
   first data arrives. Replaced wholesale when the index renders. */
/* ⛔ THE BANK'S OWN DOT-WAVE LOADER IS GONE (👤, 2026-09-19). It was a third loading treatment in a file
   whose rule is that there is ONE (`.wait`), and a student saw the boot cover end and these dots carry on
   — "the load time for the references ends while the entire bank is still loading". The bank now shows the
   same `.wait` as guidances, recall and the study guides. */
.dview > .wait{grid-column:1/-1;min-height:58vh}
/* ============================================================
   WALK — the INTERACTIVE diagnosis/treatment tree (ADR-0041, professor's card model). One decision per
   screen: prompt → pick a prose OPTION (NOT a chip — picks run to full sentences, so they're full-width
   wrapping buttons like the quiz .opt) → reveal FEEDBACK (green=correct, red=refutation) → Continue. A
   terminal node shows the DIAGNOSIS badge + hands off to Treatment. Built length-agnostic: every text slot
   wraps (overflow-wrap:anywhere) and the flow scrolls, so a 300-char feedback or a 97-char pick never breaks.
   ============================================================ */
/* LIGHT + pastel knowledge walk (owner: thin/light · white cards · pastel · NO quiz colors · sans, the
   serif is for titles/branding only). Sits on the page — no heavy panel; near-white cards, one soft teal
   accent, a pastel teal tint for the chosen path + the teaching note. No green/red, no ✓/✗ (that's the quiz). */
.walk{color:var(--ink)}
.walk-title{font-family:var(--sans);font-weight:600;font-size:var(--t-h3);color:var(--ink);line-height:1.3;margin-bottom:var(--s-5)}
.walk-step{margin-bottom:var(--s-5);padding-bottom:var(--s-5);border-bottom:1px solid var(--walk-line)}
.walk-step:last-child{border-bottom:none;margin-bottom:0;padding-bottom:0}
.walk-eyebrow{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap;margin-bottom:var(--s-3)}
.walk-step-n{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--walk-accent);font-weight:var(--w-cap-strong)}
.walk-step-label{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.walk-prompt{font-family:var(--body);font-size:var(--t-body);color:var(--ink);line-height:1.5;margin-bottom:var(--s-4)}
.walk-opts{display:flex;flex-direction:column;gap:var(--s-3)}
/* OPTION = a prose answer button (not a chip): full-width, left-aligned, wraps any length. */
.walk-opt{display:flex;flex-direction:column;gap:2px;width:100%;text-align:left;
  min-height:var(--tap);padding:var(--s-3) var(--s-4);border:1px solid var(--walk-line-2);border-radius:var(--r-1);
  background:var(--walk-surface);color:var(--ink);cursor:pointer;transition:border-color var(--dur-1),background-color var(--dur-1)}
.walk-opt:not(:disabled):hover{border-color:var(--walk-accent)}
.walk-opt-pick{font-family:var(--body);font-size:var(--t-body);font-weight:600;line-height:1.35;color:var(--ink);overflow-wrap:anywhere}
.walk-opt-detail{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.45;overflow-wrap:anywhere}
.walk-opt.locked{opacity:.45;cursor:default}
/* the CHOSEN path — a calm pastel-teal highlight (NOT a green/red quiz verdict) */
.walk-opt.picked{opacity:1;border-color:var(--walk-accent);background:var(--walk-tint)}
.walk-opt.picked .walk-opt-pick{color:var(--walk-accent-press)}
/* TEACHING NOTE = the reasoning revealed after the pick — a soft pastel callout, no ✓/✗ verdict. */
.walk-note{margin-top:var(--s-3);padding:var(--s-3) var(--s-4);border-left:2px solid var(--walk-accent);
  background:var(--walk-tint);border-radius:var(--r-1);
  font-family:var(--body);font-size:var(--t-meta);color:var(--ink);line-height:1.55;overflow-wrap:anywhere}
/* OUTCOME = the diagnosis at a terminal node (pastel teal, sans — not the serif). */
.walk-outcome{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap;margin-top:var(--s-4);padding:var(--s-3) var(--s-4);
  background:var(--walk-tint);border:1px solid var(--walk-line-2);border-radius:var(--r-1)}
.walk-oc-lbl{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--walk-accent)}
.walk-oc-val{font-family:var(--sans);font-weight:600;font-size:var(--t-h3);color:var(--walk-accent-press);overflow-wrap:anywhere;min-width:0}
.walk-continue{display:inline-flex;align-items:center;justify-content:center;margin-top:var(--s-4);
  height:var(--ctl);padding:0 var(--s-5);border:1px solid var(--walk-accent);border-radius:var(--r-1);
  background:transparent;color:var(--walk-accent-press);font-family:var(--body);font-weight:600;font-size:var(--t-body);cursor:pointer;
  transition:background-color var(--dur-1)}
.walk-continue:hover{background:var(--walk-tint)}
/* ============================================================
   CLINICAL-ALGORITHM FLOWCHART (.flowtree) — the interactive branching decision tree (FLOWCHART_STANDARD §6,
   render = designer's lane). Top-to-bottom spine of boxes joined by connector lines; a decision shows its
   whole fork (all branch values), you click one to walk the chosen path down to the green answer. §3 element
   semantics carried as a CALM left-accent + eyebrow color, not loud diamonds/hexagons (owner: minimal): a
   decision reads red, a clinical tool purple, an outcome green. Every box's "why" is the shared .dz-why toggle.
   ============================================================ */
.flowtree{width:100%}
/* ONE layout at every width (owner 2026-08-10: "terrible spacing... the action chips open below it and close").
   A single vertical column of nodes that FILLS the card; the WHY of the step you click drops in directly BELOW
   that node and closes again — no floating side-column, so no permanent dead space beside the tree. Same behavior
   the owner approved on mobile, now everywhere. */
.ft-flow{display:flex;flex-direction:column;max-width:none;margin:0;padding-bottom:var(--s-4)}
.ft-root{display:flex;flex-direction:column}
.ft-row{display:block}
.ft-why-side{display:none}
.ft-row.why-open .ft-why-side{display:block;margin-top:var(--s-2)}
/* scope = the disease name (the tree's frame, not a step) — a quiet eyebrow */
.ft-scope{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--s-3)}
.ft-scope-lbl{font-weight:600;color:var(--ink-2)}
/* thin connector — a light hairline centered under the node column */
.ft-conn{display:flex;justify-content:center;height:var(--s-5)}
.ft-conn-line{width:2px;height:100%;background:var(--line-2);border-radius:2px}
/* click cue — a step whose why can open reads as tappable: pointer + a small "why" hint at the row's right that
   flips to "close" while open. */
.ft-clicky{cursor:pointer}
/* WHY reads as a real tappable control — a small outlined pill, not tiny gray text (owner: "make why and change
   more actionable, it looks hard to do"). */
/* first line, not centred — 🔬 on a two-line question it floated to the middle while the step disc and CHANGE
   sit on line one (🎨 2026-09-29) */
.ft-clicky::after{content:"why";margin-left:auto;flex:none;align-self:flex-start;margin-top:1px;
  font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);font-weight:var(--w-cap-strong);color:var(--ink-2);
  border:1px solid var(--line-2);border-radius:var(--r-1);padding:3px 10px;line-height:1;transition:border-color var(--dur-1),color var(--dur-1),background-color var(--dur-1)}
.ft-clicky:hover::after{color:var(--ink);border-color:var(--ink-3);background:var(--surface-2)}
.ft-clicky.on::after{content:"close";color:var(--ink);border-color:var(--ink-3);background:var(--surface-2)}
/* the WHY card — compact: tight padding, small text, a quiet surface. Same on desktop (right cell) and mobile
   (below the node). */
/* the why card gets GENEROUS room — it's the reasoning, so give it space + readable body text, not a cramped
   footnote next to the tree (owner: "the why is tiny compared to the left side"). */
.ft-why-card{padding:var(--s-5);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-2);
  font-family:var(--body);font-size:var(--t-body);line-height:1.65;color:var(--ink-2)}
/* the box — lighter: tighter padding, a type-colored left rule instead of a heavy eyebrow */
/* ⛳ ONE INSET FOR EVERY BOX IN THE WALK — 🎨 2026-09-29, 👤: *"check spacing everywhere."* 🔬 MEASURED: three
   insets in one column — the patient's values 16/24, an answered step 12/16, an open step 24 — so the text
   edge jumped left and right as you read down. Now every box (open, answered, values, outcome) is 16/24 on a
   wide screen and 16 on a phone. ⛔ No per-type padding override below this line. */
.ft-node{background:var(--surface);border:1px solid var(--line);border-left:3px solid var(--line-2);border-radius:var(--r-2);padding:var(--s-4) var(--s-5)}
@media (max-width:720px){ .ft-node{padding:var(--s-4)} }
/* ONE rhythm rule owns the gap between a node's blocks (question · choices · chosen · drug · plan · treatment).
   The node — not each child — sets the spacing, so ADDING or REMOVING a block can never leave a collapsed or
   doubled gap. (This replaced four different per-child margins; removing the "Select one" line had silently
   taken the question→choices gap with it.) */
.ft-node > * + *{margin-top:var(--s-4)}
/* ⛳ 2026-09-28 (🎨) — 👤: "looks off... you follow no style guide." 🔬 ROOT CAUSE: STYLE_GUIDE.md §1 WALK
   is explicit — "NO quiz colors here — no --good/--bad green/red... ONE soft pastel-teal accent
   (--walk-accent) for the chosen path + the teaching-note callout + the diagnosis badge + the CTA." This
   block used `--danger` (absolute-contraindication RED) as the default border on every ordinary question
   and `--good` (quiz-correct GREEN) on the treatment box — the walk is a reference surface with no
   right/wrong, so a plain question bordered in alarm-red next to a "correct-answer" green treatment box
   reads exactly like the graded quiz this component is explicitly NOT supposed to resemble. A plain
   decision step gets no accent (neutral, same line the base .ft-node already draws — it earns color only
   once it's chosen); tool/outcome/dx — the walk's own "diagnosis badge"/"chosen path" — share the ONE
   teal accent the guide names, so they read as one coherent system instead of a five-color taxonomy. */
.ft-decision{border-left-color:var(--line-2)}
.ft-tool{border-left-color:var(--walk-accent)}
.ft-outcome{border-left-color:var(--walk-accent)}   /* TREATMENT — the walk's one accent, not quiz-green */
.ft-dx{border-left-color:var(--walk-accent)}        /* DIAGNOSIS — the guide's own "diagnosis badge" use of the one accent */
.ft-ruledout{border-left-color:var(--ink-3)}        /* RULED-OUT — neutral gray, never a quiz color */
.ft-ruledout .ft-answer{color:var(--ink-2)}         /* a ruled-out branch reads quieter than a live diagnosis */
.ft-head{display:flex;align-items:baseline;gap:var(--s-3)}
/* ONE box skeleton: a role eyebrow in the shared slot, then the body stacked beneath it — same rhythm on
   every box (decision · clinical-tool · diagnosis · ruled-out). Treatment's eyebrow is the teal .ft-treat-h. */
.ft-headbody{display:flex;flex-direction:column;gap:var(--s-1);flex:1;min-width:0}
.ft-role{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;font-weight:var(--w-cap-strong);line-height:1}
.ft-role-dx{color:var(--walk-accent-press)}           /* only the two ENDPOINT kinds carry a text tag — matches the border's one accent, not the separate --dx blue */
.ft-role-ruledout{color:var(--ink-3)}
/* Align every box's BODY to ONE left edge — past the step-number gutter the decisions use — so the decision
   question, the diagnosis label and the treatment atoms all begin on the same vertical line (owner: "not
   aligned"). The gutter = the 22px step disc + the .ft-head gap. */
.ft-dx .ft-headbody,.ft-ruledout .ft-headbody,.ft-outcome .ft-treat{padding-left:calc(22px + var(--s-3))}
/* step marker — a calm 22px numbered disc, sized to sit with the question line (was a cramped 16px). */
.ft-step{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;flex:none;align-self:flex-start;margin-top:1px;
  border-radius:50%;background:var(--surface-2);color:var(--ink-2);font-family:var(--sans);font-size:var(--t-meta);font-weight:600;font-variant-numeric:tabular-nums}
/* Decision-tree text is ONE size (t-body) + ONE weight (400) everywhere — question, choice, chosen, outcome — so
   nothing reads bold/heavy and the sizes stop varying (owner: "the top part is still bold and ugly... font sizes
   vary too much"). Structure comes from the numbered disc + the colored left rule, not weight. */
.ft-var{flex:1;font-family:var(--body);font-size:var(--t-body);font-weight:400;color:var(--ink);line-height:1.4;overflow-wrap:anywhere}
.ft-toollink{font-weight:400;font-size:var(--t-meta)}
/* A step's own authority (only set when it differs from the tree's guideline — see app.js). Same scale as
   the tool link beside it; muted rather than accent, since it names a source, not a destination to click. */
.ft-authlink{font-weight:400;font-size:var(--t-meta);color:var(--ink-3)}
.ft-answer{flex:1;font-family:var(--body);font-size:var(--t-body);font-weight:400;color:var(--ink);line-height:1.3;overflow-wrap:anywhere}
.ft-drug{font-size:var(--t-meta)}
.ft-dose{display:block;margin-top:2px;font-size:var(--t-meta);color:var(--ink-2)}
/* NEW MODEL: a diagnosis leaf's inline treatment plan (professor) — a short calm line under the outcome.
   ⛳ 2026-09-17 — this rule already existed and was CORRECT; what was missing is that `app.js` never emitted
   the element. 🔬 35 of 303 outcomes state their answer ONLY as `treatment.plan` prose (every acne endpoint
   among them) and rendered as a bare label. ⛔ I nearly added a SECOND `.ft-plan` here before finding this
   one — §12, and the later rule would have silently won the cascade. Only `margin-top` is added, so the
   prose does not butt against the green TREATMENT eyebrow now that it actually renders. */
.ft-plan{margin-top:var(--s-2);font-family:var(--body);font-size:var(--t-meta);line-height:1.5;color:var(--ink-2)}
/* brief highlight when the diagnosis handoff jumps to the matching Treatment scenario (leaf.treatment.scenario). */
.dz-scenario-flash{animation:dzScenFlash 1.6s ease}
@keyframes dzScenFlash{0%,15%{background:var(--accent-bg)}100%{background:transparent}}
@media (prefers-reduced-motion:reduce){.dz-scenario-flash{animation:none}}
/* choices — EVEN: full-width buttons, all the same width, stacked (owner: "choices are not even") */
.ft-branches{display:flex;flex-direction:column;gap:var(--s-3)}
.ft-branch{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);width:100%;min-height:var(--tap);
  padding:var(--s-3);border:1px solid var(--line);border-radius:var(--r-1);background:var(--surface);color:var(--ink);
  font-family:var(--body);font-size:var(--t-body);font-weight:400;text-align:left;cursor:pointer;transition:border-color var(--dur-1),background-color var(--dur-1)}
.ft-branch:hover{border-color:var(--walk-accent);background:var(--walk-tint)}
.ft-branch-go{color:var(--ink-3);font-weight:400;flex:none}
/* RESOLVED — the decision collapses to a slim one-liner: the chosen value + a quiet "change". No ask, no
   unchosen clutter, less bulk. */
.ft-decision.resolved .ft-branches{display:none}
/* ⛳ writer-2 + 🎨, 2026-09-28 — the number a step turns on is asked BY that step (👤: *"just put all inputs
   as a decision node or whatever"*), so there is no panel above the walk any more: one blank inside the step
   box, beside the arms it decides between, collapsing with the rest of the step once resolved.
   ⛳ TOKENS ARE 🎨's, AND HE HAD TO TELL ME TWICE OVER: my first pass used `--t-small` and `--rule`, which
   🔬 DO NOT EXIST anywhere in this file — the browser silently fell back to its own defaults, which is why
   it read as visually broken rather than merely plain. ⛔ An undefined custom property fails silently; it is
   the §11 shape in CSS. Real ones here: `--t-meta` · `--t-eye` · `--sans` · `--ctl` · `--bg` · `--track-caps`
   · `--w-cap-strong`. Label ABOVE the field, like every other labelled value on this site. */
/* ⛳ writer-2, 2026-09-28 — 👤: *"why isn't CKD asking me for the patient data before? maybe thats a box
   above or starts before the decision tree?"* The case starts with the patient, so the first box in the walk
   collects the values the tree decides on. ⛔ Unnumbered: it precedes the decisions rather than being one. */
/* ⛳ writer-2, 2026-09-28 — 👤: *"those decisions will be PRESELECTED, the user needs to still understand and
   hit next."* The arm the patient's data selects is marked and explained; the step stays open until the
   student presses Next. ⛔ The walk never advances on its own — reading the question is the point. */
/* ⛳ 🎨 2026-09-28 — 👤: *"put yourself in the position of someone doing this."* Walked as a student, three
   things read wrong: the preselected arm was PEACH (`--accent-bg`, the brand tint — the walk's one accent is
   teal, STYLE_GUIDE §1 WALK); the number that chose it sat in faint grey ~110px below the arm; and the
   patient's fields used `--bg`, which is this site's DISABLED field (`.gdc-off .gdc-in`). Now: teal tint, the
   value as a tag ON the arm, Next alone on the right, and the fields look like every other live field. */
.ft-branch.ft-pre-on{border-color:var(--walk-accent);background:var(--walk-tint);box-shadow:inset 0 0 0 1px var(--walk-accent)}
.ft-branch-val{flex:1 1 auto;min-width:0}
.ft-pre-tag{flex:none;font-family:var(--sans);font-size:var(--t-meta);font-weight:600;line-height:1.2;
  color:var(--walk-accent-press);background:var(--surface);border:1px solid var(--walk-accent);
  border-radius:var(--r-pill);padding:var(--s-1) var(--s-2);white-space:nowrap}
.ft-pre{display:flex;justify-content:flex-end;margin-top:var(--s-3)}
.ft-pre:empty{display:none}
.ft-next{flex:none;font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--on-accent);
  background:var(--walk-accent);border:1px solid var(--walk-accent);border-radius:var(--r-1);
  height:var(--ctl);padding:0 var(--s-5);cursor:pointer}
.ft-next::after{content:"\2192";margin-left:var(--s-2)}
.ft-next:hover{background:var(--walk-accent-press);border-color:var(--walk-accent-press)}
.ft-node.resolved .ft-pre{display:none}
/* THE PATIENT'S VALUES — one aligned table of fields: label · field · unit · calculator, the fields in ONE
   column so the eye runs straight down them (a grid whose rows are `display:contents`, so every row shares
   the same tracks). On a phone the label goes above its field, like every other labelled field here. */
.ft-data{display:grid;grid-template-columns:minmax(0,max-content) auto auto 1fr;align-items:center;
  column-gap:var(--s-3);row-gap:var(--s-3)}
.ft-data-h{grid-column:1/-1;font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;font-weight:var(--w-cap-strong);color:var(--walk-accent-press);margin-bottom:var(--s-1)}
.ft-data .ft-ask,.ft-data .ft-ask-row{display:contents}
.ft-data .ft-ask-lb{font-family:var(--body);font-size:var(--t-body);font-weight:400;letter-spacing:0;color:var(--ink);
  margin-right:var(--s-3)}
.ft-data .ft-ask-in{grid-column:2}
.ft-data .ft-ask-u{grid-column:3}
.ft-data-calc{grid-column:4;justify-self:start;display:inline-flex;align-items:center;gap:var(--s-1);
  height:var(--ctl);padding:0 var(--s-3);font-family:var(--sans);font-size:var(--t-meta);font-weight:600;
  color:var(--walk-accent-press);background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);
  cursor:pointer;white-space:nowrap;transition:border-color var(--dur-1),background-color var(--dur-1)}
.ft-data-calc .ic{width:15px;height:15px;flex:none}
.ft-data-calc:hover{border-color:var(--walk-accent);background:var(--walk-tint)}
@media (max-width:720px){
  .ft-data{display:block}
  .ft-data-h{margin-bottom:var(--s-3)}
  .ft-data .ft-ask{display:flex;flex-direction:column;gap:var(--s-2);margin:0 0 var(--s-4)}
  .ft-data .ft-ask:last-child{margin-bottom:0}
  .ft-data .ft-ask-row{display:flex;align-items:center;flex-wrap:wrap;gap:var(--s-2)}
  .ft-branch.ft-pre-on{flex-wrap:wrap;row-gap:var(--s-2)}
  .ft-branch.ft-pre-on .ft-branch-val{flex-basis:calc(100% - 2em)}
  .ft-pre-tag{order:3}
  .ft-next{width:100%}
}
.ft-ask{display:flex;flex-direction:column;gap:var(--s-1);margin:var(--s-2) 0 var(--s-3)}
/* ⛔ NOT uppercased, unlike the other eyebrows on this site: a metric name is a clinical term whose case is
   part of it. `text-transform:uppercase` turned eGFR into EGFR, and mEq/L, mOsm/kg and Kt/V lose the same
   way. Same size, weight and colour as the eyebrow family, so it still reads as one — just not re-cased. */
.ft-ask-lb{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  font-weight:var(--w-cap-strong);color:var(--ink-3)}
.ft-ask-row{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
/* a LIVE field — `--surface` + `--line-2`, the same as the disease/guidance pickers (`.gdc-in`). ⛔ Not `--bg`:
   that is the site's disabled field. No spinner arrows: a lab value is typed, never nudged by 1. */
.ft-ask-in{width:7em;height:var(--ctl);font-family:var(--body);font-size:var(--t-body);color:var(--ink);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);padding:0 var(--s-3);
  -moz-appearance:textfield;appearance:textfield}
.ft-ask-in::-webkit-inner-spin-button,.ft-ask-in::-webkit-outer-spin-button{-webkit-appearance:none;margin:0}
.ft-ask-in:focus{outline:none;border-color:var(--walk-accent);box-shadow:0 0 0 3px var(--walk-tint)}
.ft-ask-u{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3)}
.ft-node.resolved .ft-ask{display:none}
/* the resolved line keeps the number that chose it — the SAME tag the arm wore, so it reads as the same fact */
/* ⛳ It sits INSIDE the answer's text, after it — 🔬 as a third flex item beside the answer and Change it
   squeezed the answer to a 26px column on a phone, one word per line. Inline, the answer wraps normally. */
.ft-auto{display:inline-block;margin-left:var(--s-2);vertical-align:1px;font-family:var(--sans);font-size:var(--t-meta);
  font-weight:600;line-height:1.2;color:var(--walk-accent-press);border:1px solid var(--walk-accent);
  border-radius:var(--r-pill);padding:2px var(--s-2);white-space:nowrap}
.ft-chosen{display:none}
/* the resolved chosen line ALIGNS with the question above it (indented past the step marker) — the value on the
   left, a tappable "change" pill on the right; both start where the question starts (owner: "start at the same place"). */
.ft-decision.resolved .ft-chosen{display:flex;align-items:baseline;gap:var(--s-3);padding-left:calc(22px + var(--s-3))}
.ft-chosen-val{flex:1;min-width:0;font-family:var(--body);font-size:var(--t-body);font-weight:400;color:var(--ink);line-height:1.4;overflow-wrap:anywhere}
.ft-chosen-val::before{content:"\2192";margin-right:.45em;color:var(--ink-3);font-weight:400}
/* CHANGE — a real tappable pill, matching the WHY control (owner: more actionable) */
.ft-change{flex:none;align-self:flex-start;border:1px solid var(--line-2);background:none;cursor:pointer;border-radius:var(--r-1);
  padding:3px 10px;line-height:1;font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);font-weight:var(--w-cap-strong);color:var(--ink-2);transition:border-color var(--dur-1),color var(--dur-1),background-color var(--dur-1)}
.ft-change:hover{color:var(--ink);border-color:var(--ink-3);background:var(--surface-2)}
/* INLINE TREATMENT — the diagnosis leaf carries its treatment RIGHT INSIDE the green outcome box (owner: "delete
   the Treatment tab — the treatment appears at the end of diagnosis"). A quiet divider + a small green eyebrow,
   then the SAME two-column reference rows (.dz-txrow) used nowhere else — one treatment layout on the whole site. */
/* The treatment box is treatment-ONLY (no diagnosis label above it), so no divider — the green eyebrow sits
   at the box's top padding, atoms tight beneath it, same rhythm as the diagnosis box (eyebrow + body). */
.ft-treat{padding-top:0;border-top:0}
.ft-treat-h{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;font-weight:var(--w-cap-strong);color:var(--walk-accent-press);margin-bottom:var(--s-3)}
/* At a leaf there is no scenario label, so collapse the reference's 34%/1fr two-column grid to a single
   LEFT-aligned column — otherwise the empty label column shoves the atom chip to center. */
.ft-treat .dz-txrow{display:block;padding:0;border-bottom:0}
.ft-treat .dz-txrow-h:empty{display:none}
.ft-treat .dz-txrow + .dz-txrow{margin-top:var(--s-2)}
/* traps note — a caution appendix to the whole tree (algorithm.do_not_use), not a walk step */
/* ⛳ a clinical PITFALL note is a "watch out," not an absolute contraindication — --danger names that exact
   narrower meaning (tokens.css: "absolute-contraindication red"), so this reuses the site's own amber
   call-out palette (STYLE_GUIDE §3 `.clarnote`) instead, the same "!" family used everywhere else on the
   site for a clarification, not a quiz color the walk explicitly excludes. */
.ft-traps{margin-top:var(--s-4);padding:var(--s-3) var(--s-4);border:1px solid var(--warn-border);border-left:3px solid var(--warn);
  border-radius:var(--r-2);background:var(--warn-bg)}
.ft-traps-h{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;font-weight:var(--w-cap-strong);color:var(--warn-ink);margin-bottom:var(--s-2)}
.ft-traps-body{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.5}
/* ============================================================
   537-PORTED TOP/BOTTOM BAR CHROME  (markup + CSS lifted from
   past/s537 bar()/navArrows()/quizExitBtn()/glossaryBtn() and its
   .barlogo/.brand-mark/.pulse-mark/.densebtn/.glossbtn/.backbtn/
   .navarrows/.contactfoot rules; colors re-pointed to rust tokens,
   heights nudged 38→34 to sit inside rxdx's 44px top bar.)
   ============================================================ */
/* LEFT section mark — 537 .barlogo + .brand-mark + .pulse-mark */
.brand-mark{font-family:var(--display);font-weight:600;font-size:var(--t-brand);line-height:1;color:var(--accent);letter-spacing:-.02em}
.brand-mark b{color:var(--accent);font-weight:700}

/* RIGHT cluster — bordered pill controls (537 .backbtn/.densebtn/.glossbtn family, shared surface+border) */
.densebtn,.glossbtn,.backbtn,.clearbtn{
  flex:0 0 auto;display:inline-flex;align-items:center;justify-content:center;gap:var(--s-1);
  height:var(--ctl);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink-2);white-space:nowrap;
  font-family:var(--body);font-size:var(--t-meta);font-weight:600;cursor:pointer;
  transition:border-color var(--dur-1),color var(--dur-1);
}
/* quiet bar-control hover — border + text darken one notch, no movement (same theory as the chips). The
   .on/.arm accent states own their own look; .backbtn keeps its accent TEXT (border-only hover). */
.densebtn:not(.on):hover,.glossbtn:hover,.clearbtn:hover{border-color:var(--ink-3);color:var(--ink)}
.backbtn:hover{border-color:var(--ink-3)}
.densebtn:active,.glossbtn:active,.backbtn:active,.clearbtn:active{opacity:.65}
/* Clear-all-selections — course setup sticky menu (restored from the old 537 "× Clear"). Muted text
   pill; shows only once a filter is engaged. The "×" glyph precedes the label. */
/* Hide-empty toggle — 537 .densebtn (eye icon + label; accent when on) */
.densebtn{padding:0 var(--s-3)}
.densebtn.on{background:var(--accent-bg);border-color:var(--accent);color:var(--accent-press)}
.densebtn-ic{display:inline-flex;align-items:center}
.densebtn-ic .ic{width:15px;height:15px}
.densebtn.on .ic{stroke:var(--accent-press)}
/* glossary/book button — 537 .glossbtn (square icon) */
.glossbtn{width:var(--ctl);padding:0}
.glossbtn .ic{width:17px;height:17px}
/* ← Drugs back button — 537 .backbtn (bordered, accent text) */
.backbtn{padding:0 var(--s-3);color:var(--accent)}
.backbtn-ic{font-size:var(--t-h3);line-height:1}
.backbtn-lb{font-size:var(--t-meta);font-weight:600;line-height:1}
/* quiz Exit — 537 .quizexit arm-to-confirm: the armed ("Confirm (n)") state fills accent-tint */
.quizexit.arm{background:var(--accent-bg);border-color:var(--accent);color:var(--accent-press)}

/* joined [← | Drugs →] nav pill — 537 .navarrows/.navarrow */

/* (contact footer .contactfoot/.footlink/.footsig removed — those links now live in .side-foot.) */

/* ============================================================
   REFERENCE SHEET — swipe-up bottom sheet (ported from 537's
   glossary sheet: scrim + slide-up panel + grip + sticky A–Z jump).
   Single "Abbreviations" page for now; see _notes/REFERENCE_SHEET_TODO.md.
   ============================================================ */
body.sheet-open{overflow:hidden}
#glossary-host{position:relative;z-index:200}
.sheet-scrim{position:fixed;inset:0;z-index:200;background:var(--scrim);animation:scrimIn var(--dur-2) ease}
@keyframes scrimIn{from{opacity:0}to{opacity:1}}
.sheet{position:fixed;left:0;right:0;bottom:0;z-index:201;max-height:82vh;display:flex;flex-direction:column;
  background:var(--bg);border-top-left-radius:var(--r-2);border-top-right-radius:var(--r-2);
  box-shadow:0 -8px 30px rgba(42,32,24,.22);animation:sheetUp var(--dur-2) var(--ease-out);
  transition:transform var(--dur-2) ease;touch-action:none}
@keyframes sheetUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
@media (prefers-reduced-motion:reduce){.sheet{animation:none}.sheet-scrim{animation:none}}
.sheet-grip{flex:0 0 auto;width:38px;height:4px;border-radius:var(--r-pill);background:var(--line-2);margin:var(--s-2) auto var(--s-1);cursor:grab}
/* constrain the inner column to the page width (640px) so rows don't run edge-to-edge on desktop */
.sheet-inner{flex:1 1 auto;min-height:0;width:100%;max-width:640px;margin:0 auto;display:flex;flex-direction:column}
/* a PDF needs the room a calculator/glossary sheet doesn't — near-fullscreen, and wide enough on desktop
   that a letter-size page doesn't render illegibly small inside the 640px content column. */
.sheet-full{max-height:96vh}
.sheet-full .sheet-inner{max-width:960px}
.sheet-full .sheet-body{padding:0 var(--s-3) var(--s-3);display:flex;flex-direction:column;min-height:0;flex:1 1 auto}
.pdfsheet-frame{flex:1 1 auto;min-height:60vh;width:100%;border:1px solid var(--line);border-radius:var(--r-1);background:var(--surface-2)}
.pdfsheet-dl{flex:0 0 auto;display:block;text-align:center;margin-top:var(--s-3);padding:var(--s-2);
  font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--accent-press);
  background:var(--accent-bg);border-radius:var(--r-1);text-decoration:none}
.pdfsheet-dl:hover{text-decoration:underline}
.sheet-head{flex:0 0 auto;display:flex;align-items:baseline;gap:var(--s-3);padding:0 var(--s-5) var(--s-3)}
.sheet-title{font-family:var(--body);font-weight:600;font-size:var(--t-h2);color:var(--ink)}
.sheet-sub{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2)}
.sheet-body{flex:1 1 auto;overflow-y:auto;-webkit-overflow-scrolling:touch;padding:0 var(--s-5) var(--s-5)}
/* sticky A–Z jump bar over the abbreviation list */
.gl-az{position:sticky;top:0;z-index:3;display:flex;gap:1px;background:var(--bg);
  padding:var(--s-2) 0;margin-bottom:var(--s-3);border-bottom:1px solid var(--line)}
.gl-azk{flex:1 1 0;min-width:0;text-align:center;font-family:var(--mono);font-size:var(--t-eye);
  font-weight:600;color:var(--accent);padding:4px 0;line-height:1;background:none;border:0;border-radius:var(--r-1);cursor:pointer}
.gl-azk:hover{background:var(--accent-bg)}
.gl-group{margin-bottom:var(--s-5)}
.gl-group:last-child{margin-bottom:var(--s-2)}
.gl-letter{scroll-margin-top:var(--s-2)}
.gl-gtitle{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);margin:0 0 var(--s-2)}
/* row = one entry: a bold lead term, an abbreviation chip on the right, a plain meaning below */
.gl-arow{padding:var(--s-2) 0}
.gl-arow + .gl-arow{border-top:1px solid var(--line)}
.gl-atop{display:flex;gap:var(--s-3);align-items:baseline}
/* the ABBREVIATION is the lead — it's what people look up — with the full term as its expansion beside it */
.gl-aabbr{flex:0 0 auto;font-family:var(--mono);font-size:var(--t-body);font-weight:600;color:var(--accent-press);letter-spacing:.3px}
.gl-aword{flex:1 1 auto;min-width:0;font-family:var(--body);font-weight:400;color:var(--ink);font-size:var(--t-meta);line-height:1.3}
.gl-ameaning{color:var(--ink-2);font-size:var(--t-meta);line-height:1.4;margin-top:3px}

/* ---- REFERENCE SHEET TABS: Abbreviations / Understanding Classes (King Atomic build ask, 2026-09-27,
   owner: "the classes, it's confusing still … a tab that explains each class"). ---- */
.gl-tabs{flex:0 0 auto;display:flex;gap:var(--s-2);padding:0 var(--s-5) var(--s-3);border-bottom:1px solid var(--line)}
.gl-tab{flex:1 1 0;font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--ink-2);
  background:none;border:0;border-bottom:2px solid transparent;padding:var(--s-2) 0;cursor:pointer}
.gl-tab.on{color:var(--accent-press);border-bottom-color:var(--accent)}
.gl-pane[hidden]{display:none}
.gl-empty{color:var(--ink-2);font-size:var(--t-meta);padding:var(--s-4) 0}
.gl-cls-intro{color:var(--ink-2);font-size:var(--t-meta);line-height:1.5;margin-bottom:var(--s-4)}
.gl-cls-list{display:flex;flex-direction:column;gap:var(--s-4)}
.gl-cls-card{scroll-margin-top:var(--s-2);padding-bottom:var(--s-4)}
.gl-cls-card + .gl-cls-card{border-top:1px solid var(--line);padding-top:var(--s-4)}
.gl-cls-top{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3)}
.gl-cls-name{font-family:var(--body);font-weight:600;font-size:var(--t-body);color:var(--ink)}
.gl-cls-count{flex:0 0 auto;font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-3)}
.gl-cls-q{font-family:var(--sans);font-style:italic;color:var(--ink-2);font-size:var(--t-meta);margin-top:2px}
.gl-cls-exp{color:var(--ink);font-size:var(--t-meta);line-height:1.5;margin-top:var(--s-2)}
.gl-cls-exs{display:flex;flex-wrap:wrap;gap:var(--s-1);margin-top:var(--s-2)}
.gl-cls-ex{display:inline-block;padding:2px var(--s-2);background:var(--surface-2);border:1px solid var(--line-2);
  border-radius:var(--r-1);font-size:var(--t-eye);line-height:1.5;color:var(--ink-2)}
/* the drug card's "?" beside the Class system pill (Classification tab) — opens this sheet at that system */
.klass-axis-info{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;
  margin-left:2px;font-family:var(--sans);font-size:var(--t-eye);font-weight:700;line-height:1;color:var(--ink-3);
  background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r-pill);cursor:pointer;
  vertical-align:middle}
.klass-axis-info:hover{color:var(--accent-press);border-color:var(--accent)}

/* ---- UPDATE NOTICE: a low-key dark, slightly-transparent bar that slides up when a new deploy is
   detected, offering a reload. Sits above everything (incl. the quiz sticky bar); content page-width. ---- */
/* starts at the RAIL edge (not the viewport edge) so its box matches the .app content column exactly —
   the rail stays visible, and the inner lines up with the page's cards instead of floating off-center. */
/* A skinny notice that slides DOWN from the top, sized exactly like the top bar (--topbar-h + the notch
   strip) so it reads as chrome, not a floating card. SOLID near-black (fully opaque — a translucent bar
   let the top-bar title bleed through as a smudge). Overlays the sticky top bar (z above its 50). */
.update-toast{position:fixed;left:0;right:0;top:0;z-index:300;transform:translateY(-110%);   /* FULL width — spans the whole top, over the rail too */
  display:flex;align-items:center;                     /* vertically center the inner in the top-bar-h region */
  min-height:calc(var(--topbar-h) + var(--sa-top));padding-top:var(--sa-top);   /* content sits below the notch, like the top bar */
  transition:transform var(--dur-3) var(--ease-out);
  background:color-mix(in srgb, black 92%, var(--ink));   /* solid near-black, a hair of warmth to match the palette */
  border-bottom:1px solid color-mix(in srgb, var(--on-accent) 14%, transparent)}
.update-toast.in{transform:translateY(0)}
/* Span the bar EDGE-TO-EDGE like the top bar's own row — message hard-left (--s-5 indent), Reload+× hard-
   right in the corner (--s-4 gutter, matching the top-bar tools). ut-msg flex:1 pushes the controls right. */
.update-toast .ut-inner{flex:1 1 auto;width:100%;display:flex;align-items:center;gap:var(--s-2);
  padding:0 calc(var(--s-4) + var(--sa-right)) 0 calc(var(--s-5) + var(--sa-left))}   /* no vertical pad — the bar height is the top-bar's */
.update-toast .ut-msg{flex:1;min-width:0;font-family:var(--body);font-size:var(--t-meta);color:var(--bg)}
.update-toast .ut-reload{flex:none;font-family:var(--body);font-size:var(--t-meta);font-weight:600;color:var(--bg);
  background:transparent;border:1px solid color-mix(in srgb, var(--bg) 34%, transparent);border-radius:var(--r-1);
  height:var(--ctl-sm);padding:0 var(--s-4);cursor:pointer;transition:background var(--dur-1)}
.update-toast .ut-reload:hover{background:color-mix(in srgb, var(--bg) 14%, transparent)}
.update-toast .ut-x{flex:none;width:var(--ctl-sm);height:var(--ctl-sm);border:0;background:transparent;
  color:color-mix(in srgb, var(--bg) 55%, transparent);font-size:var(--t-h3);line-height:1;cursor:pointer;border-radius:var(--r-1)}
.update-toast .ut-x:hover{color:var(--bg)}
/* Mobile: tighter side gutters (the bar is already full-width). Reload/× stay --ctl-sm to fit the 56px bar. */
@media (max-width:720px){
  .update-toast .ut-inner{padding:0 var(--s-3)}
}
@media (prefers-reduced-motion:reduce){.update-toast{transition:none}}

/* ============================================================
   QUIZ RUNNER — question card (from components/question-card.html)
   ============================================================ */
.qwrap{max-width:820px;margin:0 auto}
.qmeta{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap;margin-bottom:var(--s-3)}
.qmeta .qtag{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);background:var(--surface-2);border:1px solid var(--line-2);border-radius:var(--r-1);padding:2px var(--s-2)}
/* qid + progress use --ink-2 (not --ink-3): muted metadata that still meets WCAG AA contrast at
   --t-eye/--t-meta on the bar surfaces. --ink-3 was ~2.5:1 (fails 4.5:1). Governance ADR-0009 ruling
   2026-07-25 (option A) — token-usage change, palette values stay locked. */
.qmeta .qtag.qid{font-family:var(--mono);letter-spacing:.02em;text-transform:none;color:var(--ink-2)}
/* recall vs case type badge (ADR-0012) — token-tinted so the two read apart at a glance */
.qmeta .qtag.qkind-recall{color:var(--accent);background:var(--accent-bg);border-color:var(--accent-bg)}
.qmeta .qtag.qkind-case{color:var(--warn-ink);background:var(--warn-bg);border-color:var(--warn-border)}
/* ⛔ NO `.qlo` RULE, BECAUSE NOTHING RENDERS A `.qlo` ANY MORE — see the note beside the deleted loTag()
   in app.js. The objective was in the question header (another lane, 09-18), then in the reveal (me,
   09-19), and 👤 rejected both: the header spoils the answer, the reveal belongs to the explanation. */
.qprog{font-size:var(--t-meta);color:var(--ink-2);margin-left:auto}

.qcard{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-4);animation:qcardIn var(--dur-2) ease both}
/* Pure cross-FADE on each new question (no movement — a translateY nudge read as a jerk on the big card).
   Plays on question change only; does NOT replay on submit (that reveals in place, no rebuild). */
@keyframes qcardIn{from{opacity:0}to{opacity:1}}
@media (prefers-reduced-motion:reduce){.qcard{animation:none}}
.qcard .qtype{font-family:var(--sans);font-weight:var(--w-cap);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--accent);margin:0 0 var(--s-2)}
/* 👤, 2026-09-13: "the spacing between sentences is very compact on the course and recall quizzes."
   🔬 The stem sat at 1.4 and the explanation at 1.5, while MY OWN reading sheet (`learn/guide.css`)
   uses `--lh-body: 1.62` for exactly this kind of prose. ⚖️ I had two standards for one job. These now
   match the reading sheet — ⛔ not a number I invented, the one already proven on the study guides. */
.qcard .stem{font-family:var(--body);font-weight:500;font-size:var(--t-h3);line-height:1.5;margin:0 0 var(--s-3)}
/* med-chem STRUCTURE renderer (ported from 535). A single molecule caps at 300px; a row of analogs
   spreads full width. Cream inset frames the diagram; serif A/B/C labels match the source. */
.medstruct{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-1);padding:var(--s-3);margin:0 0 var(--s-4);text-align:center}
.medstruct>svg{display:block;margin:0 auto;width:100%;height:auto;max-width:min(100%,28rem)}
.molrow{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin:0}
.molcell{flex:1 1 160px;min-width:140px;max-width:210px;text-align:center}
.mollab{font-family:var(--body);font-weight:600;color:var(--ink-2);font-size:var(--t-h3);margin-bottom:var(--s-1)}
.molcell svg{max-width:100%;height:auto}
.exp-concept .medstruct,.exp-why .medstruct{margin-top:var(--s-4)}
/* zoomable stem figure — a button reset back to the framed .medstruct look, plus a "tap to zoom" hint */
.medstruct--zoom{display:block;width:100%;font:inherit;color:inherit;cursor:zoom-in;position:relative;-webkit-tap-highlight-color:transparent;transition:border-color var(--dur-1) ease,box-shadow var(--dur-1) ease}
.medstruct--zoom:hover,.medstruct--zoom:focus-visible{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg);outline:none}
.medstruct--zoom .medstruct-fig{display:block}
.medstruct--zoom .medstruct-fig{display:block}
.medstruct--zoom .medstruct-fig>svg{display:block;margin:0 auto;width:100%;height:auto;max-width:min(100%,28rem)}
.medstruct-zoomhint{display:inline-flex;align-items:center;gap:4px;margin-top:var(--s-2);font-family:var(--sans);font-size:var(--t-meta);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.medstruct-zoomhint .ic{width:14px;height:14px}
/* structure lightbox */
/* ── REPORT AN ISSUE — a quiet FLAG icon in the question's meta row (owner: an icon up top, not a bottom
   link) that opens the report sheet. Same design language as the lightbox: scrim + surface box, accent primary. */
.qflag{background:none;border:0;padding:var(--s-1);margin-left:var(--s-1);color:var(--ink-3);cursor:pointer;line-height:0;border-radius:var(--r-1);flex:none}
.qflag svg{width:16px;height:16px;display:block}
.qflag:hover{color:var(--accent)}
/* the FLAG carries the report state, so sending needs no dialog or confirmation screen (owner): it waves
   while the POST is in flight, then settles filled + accent once the report has actually landed. */
.qflag-sending svg{animation:qflagWave .9s ease-in-out infinite;transform-origin:5px 20px}
.qflag-sent{color:var(--accent)}
.qflag-sent svg path:nth-child(2){fill:currentColor}
@keyframes qflagWave{0%,100%{transform:rotate(0)}25%{transform:rotate(-9deg)}60%{transform:rotate(7deg)}}
@media (prefers-reduced-motion:reduce){.qflag-sending svg{animation:none;opacity:.55}}
/* quiet failure line — the ONLY time a report says anything, and it never blocks the question */
.rpt-toast{position:fixed;left:50%;bottom:calc(var(--s-5) + env(safe-area-inset-bottom,0px));transform:translate(-50%,8px);
  z-index:1001;max-width:min(92vw,420px);padding:var(--s-3) var(--s-4);border-radius:var(--r-1);
  display:flex;align-items:flex-start;gap:var(--s-3);
  background:var(--surface);border:1px solid var(--line);box-shadow:var(--shadow-1);
  font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);
  opacity:0;pointer-events:none;transition:opacity var(--dur-2) ease,transform var(--dur-2) ease}
.rpt-toast.on{opacity:1;transform:translate(-50%,0);pointer-events:auto}
.rpt-toast.ok{color:var(--ink);border-color:var(--line-2)}
.rpt-toast-body{flex:1 1 auto;min-width:0}
/* ✕ to close it sooner — same quiet weight as the report window's close, never louder than the message */
.rpt-toast-x{flex:0 0 auto;background:none;border:0;color:var(--ink-3);font-size:var(--t-body);line-height:1;cursor:pointer;padding:0;margin:-1px calc(-1 * var(--s-1)) 0 0}
.rpt-toast-x:hover{color:var(--ink)}
/* two-tier "already tagged" — the report window's hierarchy, dialled down: a quiet lead + a lighter subline */
.rpt-toast-t{font-weight:600;color:var(--ink)}
.rpt-toast-s{font-size:var(--t-eye);color:var(--ink-3);margin-top:2px}
@media (prefers-reduced-motion:reduce){.rpt-toast{transition:none}}
.rptov{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:var(--s-4);opacity:0;pointer-events:none;transition:opacity var(--dur-2) ease}
.rptov.open{opacity:1;pointer-events:auto}
.rptov-scrim{position:absolute;inset:0;background:var(--scrim)}
.rptov-box{position:relative;width:min(92vw,460px);max-height:92vh;overflow:auto;background:var(--surface);border:1px solid var(--line);
  border-radius:var(--r-2);box-shadow:var(--shadow-3);padding:var(--s-4);transform:translateY(8px);transition:transform var(--dur-2) ease}
.rptov.open .rptov-box{transform:none}
.rptov-close{position:absolute;top:var(--s-2);right:var(--s-2);background:none;border:0;font-size:var(--t-body);color:var(--ink-3);cursor:pointer;line-height:1;padding:var(--s-1)}
.rptov-h{font-family:var(--body);font-weight:600;font-size:var(--t-h3);color:var(--ink)}
.rptov-qid{font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-3);margin-bottom:var(--s-3)}
.rptov-choices{display:flex;flex-direction:column;gap:var(--s-1);margin-bottom:var(--s-3)}
/* choices use the SAME light proportions as the site chips — compact, --t-meta, one control radius */
.rptchip{width:100%;text-align:left;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-1);
  padding:0 var(--s-3);height:var(--ctl);font-family:var(--body);font-size:var(--t-meta);color:var(--ink);cursor:pointer}
.rptchip:hover{border-color:var(--line-2)}
.rptchip.on{background:var(--accent-bg);border-color:var(--accent);color:var(--accent-press)}
.rptov-note{width:100%;box-sizing:border-box;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-1);
  padding:var(--s-2) var(--s-3);font-family:var(--body);font-size:var(--t-meta);color:var(--ink);resize:vertical;margin-bottom:var(--s-3)}
.rptov-foot{display:flex;gap:var(--s-2);justify-content:flex-end}
.rptov-send{background:var(--accent);border:1px solid var(--accent);border-radius:var(--r-1);padding:0 var(--s-4);height:var(--ctl);
  font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--on-accent);cursor:pointer}
.rptov-send:disabled{background:var(--surface-2);border-color:var(--line);color:var(--ink-3);cursor:default}
/* AUTH — rail sign-in control (styled as a nav row; the label hides at the 60px collapsed width via the
   shared .navlink rules) + the magic-link modal (mirrors the report modal's overlay). Identity only; the
   site stays public. */
/* NB: no `font:inherit` here — that shorthand would reset font-size to the inherited BODY size and beat
   .navlink's --t-meta (it's declared later), making Sign-out render bigger than About. .navlink already
   sets the family + size; the button just needs its chrome reset. */
.rail-auth{width:100%;background:none;border:0;cursor:pointer;text-align:left}
.rail-auth.is-in .nl-ic{color:var(--accent)}
.rail-auth .rail-auth-lb,.rail-user .rail-user-lb{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* identity row sits right above Sign out (they go hand in hand). It carries `.soon`, so its icon + text
   inherit the SAME grayed `--ink-3` treatment as every coming-soon nav item — no bespoke color here, so the
   grayed sidebar items can never drift apart. Ungrays when the account page ships. */
/* full-bleed sign-in screen — its OWN surface (the site's tan paper), not a modal over the page. Minimal,
   centered, mirrors the home hero (wordmark + one line). Accent reserved for the button (owner: just minimal). */
/* Served-page ship containers (#ecglab, #about) start HIDDEN so the raw block never flashes at the body
   root before app.js reparents it into the shell column — the jerk was worst for ECG Lab (its block is
   the biggest). visibility (not display) keeps layout so the ECG canvases still measure a real width while
   hidden. app.js adds .rdy the instant it's placed. */
#ecglab:not(.rdy),#about:not(.rdy){visibility:hidden}

/* Boot cover — a plain paper sheet painted from the FIRST JS frame so the app never flashes before the
   auth/gate check resolves. (Was: the app painted, then the sign-in wall dropped on top = the launch
   flash.) Sits just under the auth wall (z 900 < 1000). Deliberately BARE — no wordmark, no transition:
   on the signed-in / open path it's removed the instant me() resolves (the app is already painted, so it
   just appears — zero motion, no splash blink on every refresh). Only the gate path holds it a beat, until
   the sign-in wall has faded opaque over it. */
.boot-cover{position:fixed;inset:0;z-index:900;background:var(--bg)}

/* Sign-in / gate wall = a full-bleed paper surface, OPTICALLY centered (a touch above the mathematical
   middle, like the home hero + About) so the top-heavy wordmark block doesn't read low. */
/* ⛔ The lift is a TRANSFORM here too — caught by check_style, not by me: I fixed home + about by hand and
   walked straight past this one, so the sign-in wall would have kept its half-lift. That is the whole
   argument for the gate over the fix (§9). ⚠️ transform + position:fixed is fine here: `.authov` IS the
   fixed layer, and it has no fixed descendants of its own. */
.authov{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:var(--s-5);background:var(--bg);opacity:0;pointer-events:none;transition:opacity var(--dur-2) ease}
.authov > *{transform:translateY(calc(-1 * var(--optical-lift)))}
.authov.open{opacity:1;pointer-events:auto}
.authov-close{position:absolute;top:var(--s-4);right:var(--s-4);background:none;border:0;font-size:var(--t-h3);color:var(--ink-3);cursor:pointer;line-height:1;padding:var(--s-2)}
.authov-close:hover{color:var(--ink)}
.authov-box{width:min(90vw,380px);text-align:center;transform:translateY(8px);transition:transform var(--dur-2) var(--ease-out)}
.authov.open .authov-box{transform:none}
.authov-mark{font-family:var(--display);font-weight:700;font-size:var(--t-hero-sm);color:var(--accent);letter-spacing:-.02em;line-height:1;margin-bottom:var(--s-5)}
.authov-note{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);line-height:1.55;margin:0 0 var(--s-2)}
/* the username leads (the site is Chapman-gated so it's the meaningful part); the full email sits under it, muted */
.authov-user{font-family:var(--body);font-size:var(--t-h2);font-weight:600;color:var(--ink);line-height:1.2;margin-bottom:var(--s-1)}
.authov-email{font-family:var(--mono);font-size:var(--t-meta);color:var(--ink-3);margin-bottom:var(--s-4);word-break:break-all}
.authov-warn{font-family:var(--body);font-size:var(--t-meta);color:var(--warn-ink);background:var(--warn-bg);border:1px solid var(--warn-border);border-radius:var(--r-1);padding:var(--s-2) var(--s-3);margin-bottom:var(--s-3);line-height:1.5}
.authov-in{width:100%;box-sizing:border-box;height:var(--ctl);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-1);padding:0 var(--s-3);font-family:var(--body);font-size:var(--t-body);color:var(--ink);text-align:center;margin-bottom:var(--s-3)}
.authov-in:focus{outline:none;border-color:var(--accent)}
.authov-err{font-family:var(--body);font-size:var(--t-meta);color:var(--bad);margin-bottom:var(--s-2)}
.authov-send{width:100%;background:var(--accent);border:1px solid var(--accent);border-radius:var(--r-1);height:var(--ctl);color:var(--on-accent);font-family:var(--body);font-size:var(--t-body);font-weight:600;cursor:pointer}
.authov-send:hover:not(:disabled){background:var(--accent-press);border-color:var(--accent-press)}
.authov-send:disabled{background:var(--surface-2);border-color:var(--line);color:var(--ink-3);cursor:default}
.authov-out{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-1);padding:0 var(--s-4);height:var(--ctl);color:var(--ink);font-family:var(--body);font-size:var(--t-meta);cursor:pointer}
.authov-out:hover{border-color:var(--line-2)}
/* About — its own page: the wordmark + minimal plain copy, centered in the content column */
/* About = one calm centered column. Optically centered (a touch above the mathematical middle, like the
   home hero) by seating the flex-center and letting extra bottom padding lift the block up. */
body[data-page="about"] .main{display:flex;flex-direction:column;align-items:center;justify-content:center;transform:translateY(calc(-1 * var(--optical-lift)))}
/* 📱 About top-anchors on a phone, exactly like home and every ordinary page (owner 2026-09-20: "some
   elements are from another place… something scale"). Optical centering is a DESKTOP idea — it needs slack
   above and below the block to be doing anything. On a 390×844 phone the centered flex dropped About's copy
   277px down the screen (every sibling page starts its content 24px below the top bar) and THEN yanked it up
   by `--optical-lift`, which is `6vh` — a VIEWPORT-SCALING transform, so the page literally sat in a
   different place on a 667px SE than on an 844px Pro. Same species as the `5vh` recall padding killed above:
   ⚖️ nothing about optical centering depends on how tall the phone is. Top-anchored + `transform:none` puts
   About on the one start line the rest of the app already shares. */
@media (max-width:560px){
  body[data-page="about"] .main{justify-content:flex-start;padding-top:var(--s-5);transform:none}
}
.about-page{max-width:440px;margin:0;padding:0 var(--s-4);text-align:center}
.about-p{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);line-height:1.55;margin:0 0 var(--s-2)}
.about-dis{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);line-height:1.55;margin:0 0 var(--s-4)}
.about-made{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3);margin:0}
/* ACCOUNT — the student's own page: who you are, then sections, then the way out (§13: only things ABOUT
   ONE STUDENT). ⛳ A settings page, not a dashboard: an eyebrow label + a hairline opens each section, rows
   carry the content, and nothing explains itself in prose. ⛔ Every size and color is a token — this page
   adds no scale of its own. */
/* ── GUIDANCES ──────────────────────────────────────────────────────────────────────────────────────
   👤, 2026-09-13: *"it's more like the recall — they type in the disease state and then select the guidance,
   2 steps… like 2 drop down menus, but search drop down."* ⛳ TWO STEPS, ONE CONTROL: `.gdc` is built once in
   `initGuidances` and rendered twice, so step ② teaches nothing new. It reuses `.chips`/`.chip` for the
   shortcuts and `.card.dd-tree` + `.flowtree` for the tree — ⛔ this block adds ONLY the combobox and the
   page frame, because a new app is a new PAGE ON THE EXISTING SHELL, never a new design system (charter §5b,
   after a third one shipped on this site in a week). Every size, color and radius here is a token. */
/* ⛳ 620, THE SAME COLUMN AS THE QUIZ AND RECALL. 👤, 2026-09-17: *"our recall app and guidances app look
   like you don't give a shit and you keep lying to me about keeping elements the same."* ⚖️ He is right and
   I was wrong to say they matched — I had measured the TITLE, found it identical, and generalised that into
   "they match" without measuring anything else. 🔬 The real diff: input 48 vs 36 · column 620 vs 688 · label
   --ink-2 vs --ink-3 · a step title on one and not the other. ⛔ FIVE differences, asserted as zero. */
/* ⛔ NO HORIZONTAL PADDING OF ITS OWN. 🔬 With `--s-4` each side inside the 620 cap, the content measured
   588 against Recall's 620 — the last of the five differences, and the one that would have kept the two
   columns visibly unequal after everything else matched. ⛳ The page gutter comes from `.main`, which is
   where every other view gets it; a second gutter here was a second answer to the same question. */
.gd{width:100%;max-width:620px;margin:0 auto;padding:var(--s-5) 0 var(--s-7)}
/* ⛳ THE LANDING IS OPTICALLY CENTERED — 👤: "i think the home page should be optically center." The block
   sits `--optical-lift` ABOVE the mathematical middle, the same lift the home hero and the sign-in surface
   use, because a block centered by arithmetic reads low. ⚠️ It fills the space between the top bar and the
   viewport floor; ⛔ `dvh` not `vh`, so a phone's collapsing address bar does not push it off screen.
   ⚠️ HORIZONTAL: this column centers inside the CONTENT AREA, which starts after the rail — so it sits
   `--rail`/2 right of true window center, exactly like every other page on the site. That is the shell's
   convention, not this page's choice; moving it here alone would make Guidances the one page that does not
   line up with the others. */
/* ⛔ THE LIFT IS A TRANSFORM, NOT PADDING. 🔬 A1 caught this the moment it landed: padding inside a
   `justify-content:center` flex splits the space, so the block moves HALF the declared amount — the exact
   2026-09-07 defect the rung was written for. `translateY` shifts by precisely what it says. */
/* ⛔ THE CENTERING IS GONE, AND THE JUMP IS WHY. 👤, 2026-09-17: *"your UX is not smooth for recall and
   guidance, it starts in the middle and then jumps up."*
   🔬 MEASURED: the first control sat at y=375 on the landing and y=92 the instant a disease was picked — a
   **283px leap, a third of the screen, under the cursor that just clicked.** ⚖️ Centering an EMPTY page is
   right; centering a page that is ABOUT TO GROW is not, because the anchor has to change the moment it does,
   and the user pays for it with the one frame they are looking at.
   ⛳ THE FIX IS ONE ANCHOR, ALWAYS — and it is the anchor Recall already uses, so the two pickers finally
   behave the same way (🔬 Recall's card holds y=134 through typing AND adding: 0px of movement). ⛔ Not a
   transition on the jump: animating a layout change still moves the target, it just takes longer about it.
   ⚠️ This supersedes the landing-centering 👤 asked for on the Guidances page earlier — he is overruling it
   with the reason, and a page that never moves beats a page that is prettily placed for one frame. */
/* ⛳ THE TOP OFFSET LIVES ON `.gd`, WHICH IS ALWAYS THERE. ⛔ I first put it on `.gd-hero` and that class is
   REMOVED the moment a disease is picked — so the padding vanished with it and the page still moved 8px.
   A rule that only applies in one of the two states cannot hold an anchor steady across the change between
   them; the anchor has to sit on the element that survives it. */
.gd{padding-top:var(--s-6)}
/* ⛳ `.gd-lede` DELETED 2026-09-17 with the sentence it styled — the two labeled fields are the
   instruction, and a third copy of a label is commentary (👤's standing rule: a label, or nothing). */
.gd-steps{margin:0 0 var(--s-5)}
.gdc + .gdc{margin-top:var(--s-4)}
/* ⛳ THE SAME LABEL AS EVERY OTHER CONTROL GROUP ON THE SITE — `.group > .label`: `--ink-2`, `.12em`
   tracking, `--s-3` beneath. ⛔ It was `--ink-3` at `.06em` with `--s-2`: three values off, in the one
   element whose whole job is to look like its counterparts. */
.gdc-lb{display:block;font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-2);margin:0 0 var(--s-3)}
/* The field is the site's `.search` box with a clear button inside it — ⛔ NOT a second input style. */
.gdc-field{position:relative}
.gdc-in{width:100%;height:var(--ctl);background:var(--surface);border:1px solid var(--line-2);
  border-radius:var(--r-1);padding:0 calc(var(--s-5) + var(--s-3)) 0 var(--s-4);
  font-family:var(--body);font-size:var(--t-body);color:var(--ink)}
.gdc-in::placeholder{color:var(--ink-3)}
/* ⛳ THE STEP THAT IS NOT READY YET. It holds its full size and position — ⛔ the whole point is that the
   layout does not move when step ① is answered — and reads as clearly inert: no clear button, no caret,
   and the placeholder says what unlocks it rather than what to type. ⛔ `opacity` is NOT used: fading a
   whole control also fades its label and reads as "broken", where a quieter GROUND reads as "not yet". */
.gdc-off .gdc-in{background:var(--bg);color:var(--ink-3);cursor:default}
.gdc-off .gdc-in::placeholder{color:var(--ink-3)}
.gdc-off .gdc-clear{display:none}
.gdc-in:focus{outline:none;border-color:var(--accent)}
.gdc-in:focus-visible{box-shadow:0 0 0 3px var(--accent-bg)}
.gdc-clear{position:absolute;right:0;top:0;width:calc(var(--s-5) + var(--s-3));height:var(--ctl);
  border:0;background:none;color:var(--ink-3);font-size:var(--t-h3);line-height:1;cursor:pointer}
.gdc-clear:hover{color:var(--ink)}
/* ⛳ THE MENU IS THE SECOND HALF OF THE CONTROL — 👤: *"the search, and the drop down appears."* It overlays
   rather than pushes, so picking a disease does not make step ② jump out from under the cursor. */
.gdc-menu{position:absolute;left:0;right:0;top:calc(100% + var(--s-1));z-index:20;max-height:min(46vh,320px);
  overflow-y:auto;background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);
  box-shadow:var(--shadow-1)}
/* ⛔ `align-items:baseline` ON A ROW WITH A MIN-HEIGHT PINS THE TEXT TO THE TOP. 👤, 2026-09-14: *"your
   drop down menus, the text is never centered in the row, it's always top justified everywhere you have a
   dropdown menu and it's many places."* 🔬 Measured: the row is `--tap` (44px) tall, its padding takes 16,
   and the text line is ~20 — so 8px is left over. With `baseline` the flex line is placed at the START of
   the cross axis and ALL of that slack falls below the text, which is exactly the top-justified look.
   ⛳ `center` splits it. ⚖️ Baseline was not a silly choice — it aligns the main label with the smaller
   right-hand cell — but it is the wrong trade here: those two agree to the pixel at `center` too, because
   they are one line each, and only `center` also honours the row's own height.
   ⛳ ONE RULE, EVERY MENU: `combo()` is the only dropdown component on the site, so both pickers on
   Guidances and every future one are fixed by this line — which is why it was wrong in "many places". */
.gdc-opt{display:flex;width:100%;align-items:center;gap:var(--s-3);min-height:var(--tap);
  padding:var(--s-2) var(--s-4);border:0;background:none;text-align:left;cursor:pointer}
.gdc-opt + .gdc-opt{border-top:1px solid var(--line)}
.gdc-opt:hover,.gdc-opt.on{background:var(--accent-bg)}
.gdc-opt-main{flex:1;min-width:0;font-family:var(--body);font-size:var(--t-body);color:var(--ink)}
/* ⛳ The right cell says what the BANK HOLDS for that row ("No guidance yet", "ACC/AHA 2017 · no tree yet").
   ⛔ It is a state label, not commentary — and dimming a dead-end row is the honest signal that picking it
   will not reach a tree, shown BEFORE the click instead of as an apology after it. */
.gdc-opt-sub{flex:none;max-width:50%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  font-family:var(--sans);font-size:var(--t-eye);color:var(--ink-3)}
.gdc-opt.dim .gdc-opt-main{color:var(--ink-2)}
.gdc-none{padding:var(--s-3) var(--s-4);font-size:var(--t-meta);color:var(--ink-3)}
.gd-quick{margin:var(--s-4) 0 0}
.gd-quick-lb{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);margin:0 0 var(--s-2)}
/* An empty state that NAMES what is missing (§11) — never a blank panel where a tree belongs. */
.gd-note{margin:var(--s-4) 0 0;font-size:var(--t-meta);color:var(--ink-2)}
.gd-note .lk{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
/* ⛳ THE VARIATION CARD — 👤: *"disease state name, family, guideline, and that decision tree below it…
   that's all."* ⛔ FOUR THINGS. The header is a name and one meta line; everything else on this card is the
   tree. Any new row here is a drift back toward the Reference disease card, which is a different card. */
/* ⛳ TWO SUBCARDS, EVENLY SPACED. 👤, 2026-09-17: *"look at the spacing on this site, are all the boxes
   evenly spaced out? why doesn't ADHD have a subcard with the basics on a 2nd subcard for the decision
   tree?… the identity subcard, space it out well… it all feels crushed."*
   ⛔ `.gd-card` is no longer a panel itself — it is the STACK, and the two `.gd-sub` panels inside it are
   what the student sees. One margin rule sets the gap between them, so "evenly spaced" is structural rather
   than something to keep re-checking by eye. */
.gd-card{margin-top:var(--s-5);background:none;border:0;padding:0}
.gd-sub{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-5)}
/* on a phone the walk's boxes sit INSIDE this panel — 24 + 16 of inset each side left a question 155px wide */
@media (max-width:720px){ .gd-sub{padding:var(--s-4)} }
.gd-sub + .gd-sub{margin-top:var(--s-4)}
.gd-card-nm{margin:0;font-family:var(--sans);font-size:var(--t-h2);font-weight:600;color:var(--ink)}
/* ⛳ THE BASICS ARE LABELED ROWS, ⛔ not a wrapped line of four different things. Same grammar as the
   settings page: a fixed label column so every value starts on the same axis, and a hairline closing each
   row so the eye has a rest between facts. ⚖️ That is the "style guide governing this" 👤 says is missing —
   it exists, it just had not been applied here. */
/* ⛳ ONE LABEL/VALUE ROW FOR THE WHOLE SITE. 👤, 2026-09-17: *"you need to find common elements between
   the guidance and drug cards and make sure they follow similar rules."*
   🔬 MEASURED at 1400px before this — the SAME idea, built twice, agreeing on nothing:
     drug card `.dd-tbl`   label x=308 → value x=**564**  (label column **16rem**) · label `--ink-2`
     guidance `.gd-fact`   label x=438 → value x=**542**  (label column **92px**)  · label `--ink-3`
     row heights 48 (drug) vs 33 and 24 (guidance — and those two are each other's neighbours)
   ⚖️ §12: one concept, two homes, drifting. ⛳ The numbers now live in ONE place — the `--kv-*` tokens
   below — and both implementations read them, so the two cards cannot disagree again without someone
   editing the shared line on purpose.
   ⛳ THE LABEL COLUMN IS PROPORTIONAL, ⛔ not a fixed pixel count, because the two cards are different
   widths: a 16rem column that reads right on a 980px drug card is half a 620px guidance card. `clamp()`
   gives them the same RULE sized to their own measure — which is what "similar rules" actually means. */
:root{
  /* ⚠️ A FIXED LENGTH, ⛔ NOT A PERCENTAGE. 🔬 I first wrote `clamp(6rem, 24%, 16rem)` and measured the
     result: a `%` width on a `<td>` resolves against ITS OWN table, and the drug card has two tables of
     different widths — so the value column landed at **664 on one and 451 on the other**, i.e. I turned
     one misaligned card into a worse one. ⛳ 14rem clears the longest label on either card
     ("FDA PHARMACOLOGIC CLASS" ≈ 161px at `--t-eye`) and leaves the 620px guidance card a real value
     column. One number, both cards, no context-dependent arithmetic. */
  --kv-k:14rem;                     /* the label column */
  --kv-gap:var(--s-3);              /* label → value */
  --kv-pad:var(--s-2);              /* vertical breathing inside a row */
}
/* ⛳ THE LABEL COLUMN IS ITS OWN LONGEST LABEL, capped at `--kv-k`. ⛔ Not a flat 14rem on every card.
   🔬 👤's screenshot, after my first pass: on the guidance card "GUIDELINE" is a nine-letter word and the
   value sat **277px** away, because I had given both cards one fixed column so they would "follow similar
   rules". ⚖️ That is the rule stated at the wrong level — the rule is *labels share a column and values
   start on one axis*, ⛔ not *every card spends 224px on it*. `max-content` gives the drug card its wide
   column (it has "FDA PHARMACOLOGIC CLASS") and the guidance card a tight one, from ONE declaration.
   ⚠️ `display:contents` on the row means the row box disappears, so the hairline moves onto the two cells —
   ⛔ otherwise the border vanishes and I would have traded a gap for a missing rule. */
/* ⛳ 🎨 2026-09-29 — 👤: *"even where it says guideline and family, its all off."* 🔬 MEASURED at 1400px:
   ① the hairline was BROKEN — each cell drew its own border and `column-gap` left a 12px hole between them;
   ② the rows were 35 and 32px (an icon row vs a text row); ③ values were 13px against 14px everywhere else
   on the page — while the drug card's identical label/value row (`.dd-tbl`) is 48px tall at 14px.
   Now: the gap is the label cell's own padding (so the rule runs unbroken), every row is `--ctl-2l` with its
   text centred, and values are `--t-body` — the same row as the drug card, not a smaller cousin of it. */
.gd-facts{display:grid;grid-template-columns:minmax(0,max-content) minmax(0,1fr);
  margin-top:var(--s-4);font-family:var(--sans);font-size:var(--t-body)}
.gd-fact{display:contents}
.gd-fact-k,.gd-fact-v{display:flex;align-items:center;min-height:var(--ctl-2l);padding:var(--kv-pad) 0;
  border-bottom:1px solid var(--line);line-height:1.4}
.gd-fact:last-child .gd-fact-k,.gd-fact:last-child .gd-fact-v{border-bottom:0}
.gd-fact-k{max-width:var(--kv-k);padding-right:var(--kv-gap);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-2);white-space:nowrap}
.gd-fact-v{min-width:0;color:var(--ink);flex-wrap:wrap}
/* ⛔ THE TOP-PADDING OVERRIDE IS DELETED, AND IT WAS THE "not spaced correctly". 👤, 2026-09-17, on the
   identity subcard. 🔬 MEASURED: `padding: 16px 24px 24px` — **top 16, sides and bottom 24**. I wrote that
   deliberately, arguing the title "supplies its own optical weight"; on screen it just reads as the card
   being pushed up inside its own border while its twin below it sits on a uniform 24. ⚖️ A panel is a box;
   a box has one inset. If a title needs air it gets a margin, ⛔ not a hole cut in one side of the padding.
   ⛳ `.gd-sub-id` keeps its hook for the family-card rules below, so A5 still has a rule to find. */
.gd-sub-id{position:relative}
/* ⛳ THE FAMILY CARD — 👤, 2026-09-17: *"it's not a search feature, it's to show students how everything
   connects."* ⛔ So it carries no search box, no filter chip and no "Clear all": it is a DESTINATION, which
   is what he asked for originally (*"a family card which is not searchable"*).
   ⛳ It reuses `.gd-sub` / `.gd-fact` — the same two-panel card and labeled rows the guidance card uses —
   because it is the same shape of thing: a short list of facts about one object, then its contents. §12. */
.fam-card{max-width:880px;margin:0 auto}
.fam-code{font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-3)}
.fam-also{color:var(--ink-2)}
.fam-cap{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);margin:0 0 var(--s-3)}
.fam-list{list-style:none;margin:0;padding:0}
/* Each condition is a row on the same rhythm as every other list on the site — one hairline between,
   none trailing, so the panel closes on its own edge rather than on a stray rule. */
.fam-dz{display:flex;align-items:baseline;gap:var(--s-3);flex-wrap:wrap;
  padding:var(--s-2) 0;border-bottom:1px solid var(--line)}
.fam-dz:last-child{border-bottom:0;padding-bottom:0}
.fam-dz-a{color:var(--accent);text-decoration:underline;text-underline-offset:2px;font-size:var(--t-body)}
.fam-dz-a:hover{color:var(--accent-press)}
.fam-dz-c{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
/* ⛳ A CHILD IS INDENTED UNDER ITS PARENT, and the indent is the whole statement — ⛔ no connector art for a
   relationship that exists on 2 of 80 diseases. When the spine fills in, this row already holds it. */
.fam-kids{list-style:none;margin:var(--s-2) 0 0;padding:0 0 0 var(--s-5);flex:1 1 100%;
  border-left:1px solid var(--line)}
.fam-kids li{padding:var(--s-1) 0}
/* The family is a link you can only ARRIVE at (👤: "not searchable"), so it has to read as a link — a plain
   word here would look like a category and nobody would press it. */
.gd-fam,.gd-gl{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
.gd-fam:hover,.gd-gl:hover{color:var(--accent-press)}
/* ⛳ 2026-09-28 — `.gd-gl-pdf` is a <button> (opens our own viewer sheet, never a new tab), so it needs the
   plain-anchor reset a `.gd-gl` <a> gets for free: no button chrome, same font/color/underline as the link
   it replaces when a citation has a `pdf_key`. The doc icon is the only visual tell it behaves differently. */
/* ⛔ NOT `all:unset` — 🔬 that shipped and stripped the very color + underline `.gd-gl` sets, so the one
   clickable fact on the card read as plain text. Reset only what a <button> adds. */
.gd-gl-pdf{background:none;border:0;padding:0;margin:0;font:inherit;cursor:pointer;text-align:left;
  display:inline-flex;align-items:center;gap:var(--s-1)}
.gd-gl-pdf .ic{width:14px;height:14px;flex:none}
.gd-gl-pdf:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:2px}
/* ⛔ A guideline with no URL is NOT underlined — an underline that leads nowhere is a promise the card
   cannot keep (§11: say what is missing rather than dress it up as present). */
/* ⛳ NO URL MEANS NO UNDERLINE — ⛔ it does NOT mean "quieter than everything else on the card". 🔬 In the
   labeled-row layout the guideline is the NAME of the thing the student picked, and at `--ink-3` it was the
   faintest text in the panel, below its own label's weight. The underline is what promises a destination;
   the COLOR just says how important the fact is, and this fact is the most important one there. */
.gd-gl-flat{color:var(--ink);text-decoration:none}
/* ⛳ 2026-09-24/26 — a tree that cites MORE THAN ONE guideline stacks them, one per line, inside the same
   "Guideline(s)" value cell (`.gd-fact-v` has no flex of its own — a single `.gd-gl`/`.gd-gl-flat` still
   renders bare, unwrapped, exactly as before). 0 of 71 trees trigger this today; the row is silent until one does. */
.gd-gl-row{display:block}
.gd-gl-row + .gd-gl-row{margin-top:var(--s-1)}
/* `use_when` — 👤: "sometimes hospitals use 2 of them for various situations." It is the ONLY thing that
   separates two guidances for one disease, so it sits on the card, not just in the menu. */
.gd-when{color:var(--ink-3)}
/* ⛳ THE ATTRIBUTION FLAG — 🏗️: "do not let the page imply a certainty it does not have." 🔬 5 of 65 pairs
   are registry-matched. ⛔ Quiet, not alarming: this is a provenance note, not an error. */
.gd-flag{margin:0 0 var(--s-4);padding:var(--s-3);border-radius:var(--r-1);background:var(--surface);
  border:1px solid var(--line);font-size:var(--t-meta);color:var(--ink-2)}

/* ⛳ IT NEEDS ROOM TO BREATHE — 👤, 2026-09-13: *"this page is too compact, too much commentary, not
   intuitive, needs to flow better."* 🔬 Everything was packed into a 620px column at the top-left of a
   1400px screen with `--s-5` (24px) between sections, so four unrelated things read as one block. ⛳ 720px
   and a full step more air between sections: the same content, with the SEAMS visible. */
.acct{width:100%;max-width:720px;margin:0 auto;padding:var(--s-6) var(--s-4) var(--s-7)}
/* Identity is TEXT, not a card. ⛔ It was a bordered panel holding two lines and a button, which made the
   emptiest block on the page also the loudest. */
.acct-id{margin:0 0 var(--s-7)}
.acct-who{font-family:var(--sans);font-weight:700;font-size:var(--t-h1);color:var(--ink);letter-spacing:-.01em;line-height:1.15;word-break:break-word}
.acct-mail{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);margin-top:2px;word-break:break-all}
/* ONE section shape: label left, state right, hairline under. */
.acct-sec{margin:0 0 var(--s-7)}
.acct-sech{display:flex;align-items:center;justify-content:space-between;gap:var(--s-3);flex-wrap:wrap;
  padding-bottom:var(--s-3);border-bottom:1px solid var(--line);margin-bottom:var(--s-4)}
.acct-sect{font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2)}
.acct-secm{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3)}
.acct-empty{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);margin:0;padding:var(--s-3) 0}
.acct-empty a{color:var(--accent);text-decoration:none;border-bottom:1px solid var(--accent-bg)}
.acct-empty a:hover{border-bottom-color:var(--accent)}
/* Rows are separated by hairlines, ⛔ not by boxes — a list of six bordered cards is a stack of containers,
   which is what made this read as a dashboard instead of an account. */
.acct-rows{display:flex;flex-direction:column}
.acct-row{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3);padding:var(--s-3) 0;border-bottom:1px solid var(--line)}
.acct-row:last-child{border-bottom:0}
.acct-row-main{flex:1 1 200px;min-width:0}
.acct-row-t{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--ink);line-height:1.3;word-break:break-word}
.acct-n{min-width:20px;height:18px;margin-left:var(--s-2);display:inline-flex;align-items:center;justify-content:center;vertical-align:1px;padding:0 5px;
  border-radius:var(--r-1);background:var(--surface-2);color:var(--ink-2);font-size:var(--t-eye);font-weight:600}
.acct-row-p{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);line-height:1.45;margin-top:2px;
  overflow:hidden;text-overflow:ellipsis;display:-webkit-box;-webkit-line-clamp:1;-webkit-box-orient:vertical}
.acct-row-say{-webkit-line-clamp:none;display:block}
.acct-row-w{font-family:var(--body);font-size:var(--t-eye);color:var(--ink-3);margin-top:2px}
/* An enrolment the resolver could not match. ⛳ Marked, never hidden — a student whose course silently
   resolves to nothing gets an empty bank and no idea why. */
.acct-warn{margin-left:var(--s-2);padding:1px 6px;border-radius:var(--r-1);vertical-align:1px;
  background:var(--warn-bg);color:var(--warn-ink);font-family:var(--sans);font-size:var(--t-eye);font-weight:600}
.acct-row-acts{flex:0 0 auto;display:flex;gap:var(--s-3);margin-left:auto}
/* Row actions are quiet TEXT, ⛔ not three bordered buttons per row — six rows of those is eighteen boxes. */
.acct-act{background:none;border:0;padding:0;font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);
  text-decoration:none;cursor:pointer;white-space:nowrap}
.acct-act:hover{color:var(--ink)}
/* ⛳ THE AUTOSAVE'S RECEIPT. It stands where the Save button used to be on the School row, so the eye finds
   the answer in the place it was about to press — ⛔ not a toast somewhere else on the page. Same size and
   color as the quiet actions beside it, because it is a status, not a control. */
.acct-said{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);white-space:nowrap}
.acct-act-go{color:var(--accent);font-weight:600}
.acct-act-go:hover{color:var(--accent-press)}
.acct-act-del:hover{color:var(--bad)}
.acct-busy{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);margin:var(--s-3) 0 0}
/* ⛳ WHERE A VALUE CAME FROM, ON THE VALUE — 🗄️: "a default that looks like a choice is one nobody can find
   the source of and nobody can undo." ⛔ A tag, not a sentence. */
.acct-from{margin-left:var(--s-2);font-family:var(--sans);font-size:var(--t-eye);font-weight:400;
  color:var(--ink-3);text-transform:none;letter-spacing:0}
/* ⛳ ONE CARD PER COURSE — 🗄️'s ②, and it REPLACES both the old Courses list and the "Your Bank" rollup
   that restated the same teachers and exams one section lower. Same hairline spine as every other section. */
.acct-course{padding:var(--s-3) 0;border-bottom:1px solid var(--line)}
.acct-course:last-of-type{border-bottom:0}
.acct-course-h{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3)}
.acct-course-t{font-family:var(--sans);font-size:var(--t-body);font-weight:600;color:var(--ink);line-height:1.3}
.acct-course-w{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);margin-top:2px;line-height:1.45}
.acct-course-f{display:flex;align-items:baseline;justify-content:space-between;gap:var(--s-3);margin-top:var(--s-3)}
.acct-course-f .acct-from{margin-left:0}
/* The exam row wraps rather than scrolls — four exams fit a laptop and stack on a phone. */
.acct-exams{display:flex;flex-wrap:wrap;gap:var(--s-2) var(--s-5);margin-top:var(--s-3)}
.acct-exam{display:inline-flex;align-items:baseline;gap:var(--s-2)}
.acct-exam-n{font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-2)}
/* ⛔ "week of Sep 23" is not a date and must not be dressed as one — it renders in the same quiet face as
   a real date because the WORDS carry the difference, and a second color would imply a fault. */
.acct-exam-d{font-family:var(--body);font-size:var(--t-meta);color:var(--ink);font-variant-numeric:tabular-nums}
/* ⛳ THE ADD FORM SITS IN THE SAME RHYTHM AS A ROW, ⛔ it does not open a hole. 👤, 2026-09-13: *"no
   spacing, no standard, no format."* 🔬 Measured on the live page: an open picker left ~100px between the
   Add control and the next section head while a course ROW sits on 12px — so opening it made the page look
   like something had broken. ⛳ One vertical step (`--s-4`) above and below, the select and the actions on
   ONE line at desktop so the form is a row rather than a stack, and it collapses to a stack only where the
   width forces it (§6.2 — the editor opens INSIDE the section, it does not restructure it). */
.acct-add{margin:var(--s-4) 0 0;padding:0}
.acct-add-open{display:flex;flex-wrap:wrap;align-items:center;gap:var(--s-3)}
.acct-add-open .acct-syl-lb{flex:0 0 auto}
.acct-add-open .acct-syl-acts{margin:0}
.acct-add-s{width:100%;max-width:320px;flex:1 1 200px;height:var(--ctl);padding:0 var(--s-3);border:1px solid var(--line-2);
  border-radius:var(--r-1);background:var(--surface);color:var(--ink);font:inherit;font-size:var(--t-body)}
.acct-add-s:focus{outline:none;border-color:var(--accent)}
/* ⛔ PASTE, NOT UPLOAD — `POST /api/syllabus` takes text and the deploy has no R2 binding, so a file
   picker here would be a promise the backend cannot keep (A19). The label says paste. */
.acct-syl{margin-top:var(--s-3);display:flex;flex-direction:column;gap:var(--s-2)}
.acct-syl-lb{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3)}
.acct-syl-t{width:100%;padding:var(--s-3);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink);font:inherit;font-size:var(--t-meta);line-height:1.5;resize:vertical}
.acct-syl-t:focus{outline:none;border-color:var(--accent)}
/* ⛳ THE COMMIT ROW, AND IT IS THE SAME ROW EVERYWHERE. 👤, 2026-09-14: *"make a line or something at the
   bottom and the save button aligned with everything, you place it in the corner and it's just weird, it's
   not consistent and the user is getting confused."*
   🔬 He is describing a real inconsistency, not a preference. Every other row on this page is
   `[label] [value] [action]` closed by a 1px `--line` (see `.acct-fact`), with the ACTION on the right edge
   — the course row's `›` sits at the content edge. The Save row alone had no rule under it and sat hard
   against the LEFT edge, so it read as loose furniture rather than the end of the block it commits.
   ⛳ So: `border-top` CLOSES the editing block the way `.acct-fact`'s bottom border closes a row, and the
   button lands on the SAME right-hand axis as every other action on the page. Same height (`--ctl`) and
   same vertical padding as a fact row, so the rhythm does not break at the bottom of a section. */
.acct-syl-acts{display:flex;align-items:center;justify-content:flex-end;gap:var(--s-4);
  min-height:var(--ctl);margin-top:var(--s-3);padding:var(--s-2) 0;border-top:1px solid var(--line)}
@media (max-width:520px){
  /* On touch the actions get their own row under the name, and each one gets a REAL tap target — quiet
     text is right for a pointer, but an 18px-tall word is not something you can reliably hit with a thumb. */
  .acct-row-acts{margin-left:0;width:100%;gap:var(--s-5)}
  .acct-act{min-height:var(--tap);display:inline-flex;align-items:center}
}
.zoomov{position:fixed;inset:0;z-index:1000;display:flex;align-items:center;justify-content:center;padding:var(--s-4);opacity:0;transition:opacity var(--dur-2) ease}
.zoomov.open{opacity:1}
.zoomov-scrim{position:absolute;inset:0;background:var(--scrim-strong);cursor:zoom-out}
.zoomov-box{position:relative;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-5);width:min(96vw,1100px);max-width:96vw;max-height:92vh;overflow:hidden;box-shadow:var(--shadow-3);transform:scale(.96);transition:transform var(--dur-2) ease;display:flex;align-items:center;justify-content:center}
.zoomov.open .zoomov-box{transform:scale(1)}
.zoomov-close{position:absolute;top:var(--s-2);right:var(--s-2);width:var(--ctl-sm);height:var(--ctl-sm);border:1px solid var(--line);border-radius:50%;background:var(--surface);color:var(--ink-2);font-size:var(--t-body);line-height:1;cursor:pointer;display:flex;align-items:center;justify-content:center}
.zoomov-close:hover{border-color:var(--accent);color:var(--accent)}
/* the figure fills the enlarged box and is the pan/zoom target (JS drives transform); centered both ways */
.zoomov-fig{text-align:center;touch-action:none;transform-origin:center center;will-change:transform;max-width:100%;max-height:100%;display:flex;flex-direction:column;align-items:center;justify-content:center;cursor:zoom-in}
.zoomov-fig svg{width:100%;height:auto;display:block;margin:0 auto}
.zoomov-fig>svg{max-width:min(90vw,1000px);max-height:84vh}
.zoomov-fig .molrow{gap:24px;justify-content:center;flex-wrap:wrap}
.zoomov-fig .molcell{flex:1 1 40%;min-width:280px;max-width:none}
.zoomov-fig .mollab{font-size:var(--t-h2)}
.zoomov-hint{position:absolute;left:50%;bottom:var(--s-3);transform:translateX(-50%);font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);pointer-events:none;white-space:nowrap}
.opts{display:flex;flex-direction:column;gap:var(--s-2);margin:0 0 var(--s-3)}
/* ⛳ THE ROWS WERE A PIXEL APART, AND 👤 SAW IT. 2026-09-14: *"look at the lines, are they off a pixel?"*
   ⛔ MY FIRST READ WAS WRONG — I said the borders were antialiased onto a half pixel. They are not: sampled
   from the render, every border is ONE pixel at full color. What differs is the ROW HEIGHT.
   🔬 `--t-body` is 14px and `line-height` was `1.45` → a **20.3px line box**, so an option computed to
   38.3px and the four rows rendered **38 · 38 · 38 · 39** — the last one rounds up, and the stack drifts.
   ⛳ `1.5` makes the line box an INTEGER at both sizes the scale ships (14 → 21, 16 → 24): every row is
   39px, gaps are a flat 8px, and the drift is impossible rather than merely absent today.

   ⚠️ ALSO MEASURED, ⛔ NOT CHANGED — the resting fill of an option is `--surface`, which is EXACTLY the
   fill of the `.qcard` it sits on: card, option and the gap between options all sample (251,247,239), so an
   answer is separated from its panel by a 1px border and nothing else. ⛳ Every control in the system does
   this (`.acct-in`, `.nument-in`, `.acct-plus`), so it is a SYSTEM decision, not a bug in this rule — and
   changing it here alone would put two answer controls in one card on two different grounds. Raised with
   👤; ⛔ left as-is until he rules.
   ⛔ NOT fixed with a hard `height:` — an option must still wrap to two lines when the text is long. */
/* ⛳ THE LETTER SITS WITH THE LINE IT LABELS. 🔬 2026-09-18: `align-items:center` centered A/B/C/D against
   the WHOLE option, so on a wrapped answer the letter drifted down the row — measured 115px below the top
   of a 249px option on desktop, and **503px down a 1,026px option on a phone**, half a screen from the text
   it names. 🔬 13.8% of the bank's 24,424 options wrap on desktop and 47.2% wrap at 390px, so this is the
   common case, not the edge one.
   ⚖️ `baseline` was the obvious fix and I MEASURED IT OUT: the mono key's metrics grew the flex line to
   21.5px, so every row became 39.5px — the exact half-pixel drift the note above this rule exists to
   prevent. `flex-start` PLUS a key whose line box IS the label's (see `.opt .key`) puts the letter on the
   first line and keeps the row an integer. ⚠️ A one-line option does not move at all: its content already
   exceeds --ctl, so there is no free space for any alignment value to distribute.
   🔬 After: the key is 9px from the top of the row on a 39px option and on a 1,266px one alike; rows are
   39px desktop / 44px phone; the gap between rows is 8px at every length. */
.opt{
  display:flex;align-items:flex-start;gap:var(--s-3);min-height:var(--ctl);
  padding:var(--s-2) var(--s-3);border-radius:var(--r-1);
  border:1px solid var(--line-2);background:var(--surface);
  /* option text reads as PROSE — body reading font, comfortable size + line-height. */
  font-family:var(--body);font-size:var(--t-body);line-height:1.5;color:var(--ink);text-align:left;width:100%;
  transition:border-color var(--dur-1) ease, background-color var(--dur-1) ease;
}
/* the option KEY (A/B/C/D or rank) stays mono — it's a label, not prose. */
.opt .key{font-family:var(--mono);font-size:var(--t-meta);color:var(--ink-3);flex:none;
  /* the key's line BOX is the label's line box, so the letter lands ON the first line and the row height
     stays the integer the .opts note above depends on (14 × 1.5 = 21, never a half pixel). */
  line-height:calc(var(--t-body) * 1.5)}
.opt .lbl{flex:1;min-width:0;overflow-wrap:anywhere;font-family:var(--body);line-height:1.5}
.opt:not(:disabled):hover{border-color:var(--accent)}
.opt:not(:disabled):active{border-color:var(--accent);background:var(--accent-bg)}  /* immediate tap feedback on phones */
.opt.picked{border-color:var(--accent);background:var(--accent-bg)}
/* THE REVEAL SPEAKS IN TWO CHANNELS, so the feedback card below never has to restate a choice in words
   (Vijay 2026-09-13). COLOR = the key: green is an answer, red is wrong. FILL = whose it is: a filled row
   is the row YOU picked, an outlined row is one you didn't. Hence: filled green = right, and yours ·
   filled red = yours, and wrong · outlined green = an answer you missed. That last state is what lets a
   select-all item show which correct options the student actually got. */
.opt.correct{background:var(--good-bg);border-color:var(--good);color:var(--good)}
.opt.correct .key{color:var(--good)}
.opt.correct.missed{background:none}
.opt.wrong{background:var(--bad-bg);border-color:var(--bad);color:var(--bad)}
.opt.wrong .key{color:var(--bad)}
.opt:disabled{cursor:default}
.opt .mk{margin-left:auto;font-family:var(--mono);font-size:var(--t-meta);font-weight:600}
/* RULE-OUT ("mute") — process-of-elimination on MCQ/SATA (owner). The eye on the right closes ("eyelashes")
   when an option is ruled out; the option dims + strikes and can't be picked. Same row tokens; the eye's tap
   area stretches the full row height (negative margins eat the row padding) so it's a comfortable touch target
   on phones. Ephemeral — hidden once the answer reveals. */
/* ⛳ AND THE RULE-OUT EYE FOLLOWS THE LETTER. Same defect, same measurement: stretched down a wrapped
   option it ended up hundreds of pixels from the text it rules out. `max-height` clamps the stretch, which
   parks it at the TOP on a tall option while a short row still stretches to a full-height tap target. */
.opt-mute{flex:none;align-self:stretch;max-height:var(--tap);display:inline-flex;align-items:center;justify-content:center;
  margin:calc(-1 * var(--s-2)) calc(-1 * var(--s-3)) calc(-1 * var(--s-2)) var(--s-1);padding:0 var(--s-3);
  color:var(--ink-3);opacity:.4;cursor:pointer;transition:opacity var(--dur-1) ease,color var(--dur-1) ease}
.opt-mute .ic{width:16px;height:16px}
.opt-mute:hover{opacity:.85}
.opt.muted{background:var(--surface-2)}
.opt.muted .lbl{text-decoration:line-through;color:var(--ink-3)}
.opt.muted .key{color:var(--ink-3);opacity:.55}
.opt.muted .opt-mute{opacity:1;color:var(--ink-2)}
.opt:disabled .opt-mute{display:none}   /* muting is over once the answer reveals */
/* KEYBOARD FOCUS — the option the keys point at. A calm accent ring so ↑/↓ can travel without the row
   reading as "picked" (picked owns the filled tint). Sits under .correct/.wrong, which recolour fully. */
.opt.kbfocus{border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-bg)}

/* ============================================================
   KEYBOARD LEGEND (desktop) — a quiet floating pill, centered above the action bar, naming the quiz keys
   with raised keycap glyphs so a keyboard-first student never guesses. Rendered inside the quiz DOM (auto-
   clears with the question); hidden where there's no physical keyboard; never intercepts a tap.
   ============================================================ */
.kbhud.off{display:none}   /* graded — see kbHudShow(): it would float over the reveal */
.kbhud{
  position:fixed;left:50%;bottom:96px;z-index:35;transform:translateX(-50%);
  display:flex;align-items:center;gap:var(--s-4);
  padding:6px var(--s-4);border-radius:var(--r-pill);   /* flat, with breathing room so keys don't touch the edge */
  background:var(--surface);                            /* fallback for no color-mix */
  background:color-mix(in srgb, var(--surface) 42%, transparent);   /* ~40% frosted, per owner */
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  border:1px solid color-mix(in srgb, var(--line) 55%, transparent);
  font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-2);
  pointer-events:none;white-space:nowrap;
  animation:kbhudIn var(--dur-2) var(--ease-out) both;
}
.kbhud span{display:inline-flex;align-items:center;gap:6px}
.kbhud kbd{
  display:inline-flex;align-items:center;justify-content:center;
  min-width:18px;height:18px;padding:0 5px;
  font-family:var(--mono);font-size:var(--t-micro);line-height:1;color:var(--ink-2);
  background:color-mix(in srgb, var(--bg) 65%, transparent);
  border:1px solid color-mix(in srgb, var(--line-2) 70%, transparent);border-radius:var(--r-1);
}
@keyframes kbhudIn{from{opacity:0;transform:translate(-50%,6px)}to{opacity:1;transform:translate(-50%,0)}}

/* Long questions: extra bottom scroll room so the last option clears the fixed pill. Desktop only. */
@media (min-width:861px){.qwrap{padding-bottom:76px}}
/* No physical keyboard (touch / narrow) → no legend. NOT gated on (pointer:coarse): touchscreen laptops
   report a coarse primary pointer yet have a keyboard, which was hiding the pill on real desktops. */
@media (max-width:860px){.kbhud{display:none}}

/* concept "why" card (from components/explanation-concept.html) */
.exp-concept{margin-top:var(--s-3);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);overflow:hidden}
.exp-concept .status{display:flex;align-items:center;gap:var(--s-2);padding:var(--s-2) var(--s-4);border-bottom:1px solid var(--line)}
/* A simple concept can have NO explanation ("it's okay if there isn't one"): then the verdict header IS the
   whole card, so its divider would dangle over empty space. Drop it — the tinted verdict fills the rounded
   card on its own, no half-drawn line under it. */
.exp-concept .status:last-child{border-bottom:none}
.exp-concept .dot{width:8px;height:8px;border-radius:var(--r-1);flex:none}
.exp-concept .verdict{font-family:var(--body);font-weight:600;font-size:var(--t-body);margin:0}
.exp-concept .status--good{background:var(--good-bg)}
.exp-concept .status--good .dot{background:var(--good)}
.exp-concept .status--good .verdict{color:var(--good)}
.exp-concept .status--bad{background:var(--bad-bg)}
.exp-concept .status--bad .dot{background:var(--bad)}
.exp-concept .status--bad .verdict{color:var(--bad)}
.exp-concept .ebody{padding:var(--s-3) var(--s-4)}
/* the "WHY" eyebrow is a LABEL → stays mono (537 keeps its section labels mono). */
.exp-concept .ee{font-family:var(--sans);font-weight:var(--w-cap-strong);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--accent);margin:0 0 var(--s-2)}
.exp-concept .rationale{font-family:var(--body);font-size:var(--t-body);line-height:1.62;color:var(--ink);margin:0}
/* Paragraphs within one explanation: space them apart. The first keeps margin:0 so the block still
   sits tight under the "Why" eyebrow; each later paragraph gets a gap. */
.exp-concept .rationale + .rationale{margin-top:1em}
/* ⛳ AUTHORED BULLETS RENDER AS BULLETS (see escLines). 🔬 42 explanations, 291 bullet lines. The list
   takes the rationale's type exactly — same family, size and line-height — so a bulleted explanation and
   a prose one are the same voice. It sits TIGHT under its intro line (--s-2), because "the four options
   and their niches:" and the four options are one thought; a following PARAGRAPH gets the full 1em. */
.exp-concept .rationale-list{font-family:var(--body);font-size:var(--t-body);line-height:1.62;color:var(--ink);
  margin:var(--s-2) 0 0;padding-left:var(--s-4)}
.exp-concept .rationale-list li{margin:0}
.exp-concept .rationale-list li + li{margin-top:var(--s-2)}
.exp-concept .rationale-list + .rationale,.exp-concept .rationale-list + .rationale-list{margin-top:1em}
/* ⛔ THIS RULE WAS DEAD: it targeted `b`, and `mdBold()` has only ever emitted `<strong>` — so the accent
   it asked for never rendered on any of the 1,027 explanations that use `**bold**`. ⚖️ Pointed at what is
   actually emitted, and the accent DROPPED on the measurement: bold is a median 19% of an explanation's
   text, and a fifth of a paragraph in accent is not a highlight, it is noise. Weight carries it. */
.exp-concept .rationale strong,.exp-concept .rationale-list strong{font-weight:600}
/* End-of-case wrap-up (caseSynthesis) — sits below the last part's Why, set apart with an accent rule so
   it reads as the case tie-off, not another rationale. Shares the .ee/.rationale type. */
.exp-concept .case-synth{margin:0 var(--s-5) var(--s-5);padding-top:var(--s-4);border-top:1px solid var(--line)}

/* recall COMPACT fact-card (from components/explanation-recall.html) */
.explain{margin-top:var(--s-4);padding:var(--s-4);border-radius:var(--r-2);font-size:var(--t-body);line-height:1.55;
  background:var(--surface-2);border:1px solid var(--line);border-left:2px solid var(--ink-3)}
.explain.ok{background:var(--good-bg);border-color:var(--line);border-left-color:var(--good)}
.explain.no{background:var(--bad-bg);border-color:var(--line);border-left-color:var(--bad)}
.explain .h{display:flex;align-items:baseline;gap:var(--s-2);font-family:var(--sans);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;margin-bottom:var(--s-3)}
.explain.ok .h{color:var(--good)} .explain.no .h{color:var(--bad)}
/* RECALL REVEAL — the reveal is the ANSWER, separate from the verdict, so the recall panel is UN-TINTED (no
   pink/green fill behind it). The fact-card is ONE calm surface: sub-cards are hairline-separated SECTIONS, not
   boxes-in-a-box (owner: kill the nested borders). Values are plain text; only a multi-value field becomes
   ATOMIC PILLS (a lone box around a single word is over-styled). The tested sub-card leads with an accent eyebrow. */
/* higher specificity than .explain.ok/.no so the tint is truly removed behind the cards */
.explain.explain--recall{background:none;border:0;padding:0;margin-top:var(--s-3)}
/* VERDICT — the panel is un-tinted, so the correct/incorrect signal can't ride a background wash any more.
   It lives in a compact TINTED verdict chip (good/bad fill + color + left bar) sitting ABOVE the calm cards,
   so the verdict stays unmistakable while no tint bleeds behind the fact-card (owner). */
.explain.explain--recall .h{display:inline-flex;align-items:baseline;gap:var(--s-2);margin-bottom:var(--s-3);
  padding:var(--s-2) var(--s-3);border-radius:var(--r-1);border-left:3px solid currentColor}
.explain.explain--recall.ok .h{background:var(--good-bg)}
.explain.explain--recall.no .h{background:var(--bad-bg)}
/* stack = the one surface card; standalone .fc (disease/bare reveal) keeps its own box */
.explain .fc-stack{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-3) var(--s-4);margin-top:var(--s-2)}
.explain .fc{margin:var(--s-2) 0 0;background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-3) var(--s-4)}
.explain .fc-stack .fc{margin:0;background:none;border:0;padding:0}
.explain .fc-stack .fc + .fc{border-top:1px solid var(--line);margin-top:var(--s-3);padding-top:var(--s-3)}
.explain .fc-sc{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3);margin-bottom:var(--s-2)}
.explain .fc-lead .fc-sc{color:var(--accent)}
.explain .fc-name{font-family:var(--body);font-size:var(--t-h3);font-weight:600;color:var(--ink);margin-bottom:var(--s-2)}
.explain .fc-rows{display:grid;grid-template-columns:minmax(0,124px) minmax(0,1fr);column-gap:var(--s-3);row-gap:var(--s-2);align-items:start}
.explain .fc-row{display:contents}
.explain .fc-k{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3);padding-top:4px}
.explain .fc-v{display:flex;flex-wrap:wrap;gap:var(--s-1);align-items:center;min-width:0}
.explain .fc-one{font-size:var(--t-meta);color:var(--ink);line-height:1.5;padding-top:2px}
.explain .fc-atom{display:inline-block;background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-1);padding:0 var(--s-2);font-size:var(--t-meta);color:var(--ink-2);line-height:1.7}
.explain .fc-link{display:inline-block;margin-top:var(--s-3);font-size:var(--t-meta);color:var(--accent);text-decoration:none;border-bottom:1px solid currentColor;padding-bottom:1px}
.explain .fc-link:hover{color:var(--accent-press)}
/* bare recall = verdict only (no fact-card body): compact, evenly-padded box, header hugs its single line */
.explain--bare{padding:var(--s-3) var(--s-4)}
.explain--bare .h{margin-bottom:0}

/* NOTE: the old '.exp-why' cases box (accent-tinted, no correctness state) was RETIRED 2026-07-13 — cases now
   use '.exp-concept' good/bad like concepts. Do not reintroduce an accent-colored feedback box: --accent (rust)
   reads as the error red. See STYLE_GUIDE §1 (accent≈bad guard) + tools/render_lint.py FEEDBACK_ACCENT. */
/* ---- MULTI-PART CASE vignette banner (ported from 537, rust/cream tokens) ----
   Header (Case X of N + point value), part-track bar, shared vignette body, labs table. Shown once
   above each part; the "Part X of N" line + the part lead-in follow below it in the question card. */
.case-vignette{background:var(--surface-2);border:1px solid var(--line);border-left:2px solid var(--accent);border-radius:var(--r-1);overflow:hidden;margin-bottom:var(--s-4)}
.case-vhead{display:flex;align-items:center;justify-content:space-between;gap:var(--s-2);padding:var(--s-3) var(--s-4) 0}
.case-tag{font-family:var(--sans);font-weight:var(--w-cap-strong);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--accent)}
.case-pts{font-size:var(--t-eye);color:var(--accent);background:var(--surface);border:1px solid var(--accent-bg);border-radius:var(--r-pill);padding:3px 12px;white-space:nowrap}
.case-track{display:flex;gap:4px;padding:var(--s-3) var(--s-4) 0}
.ctrk{flex:1 1 0;height:6px;border-radius:var(--r-pill);background:var(--line);cursor:pointer;transition:background var(--dur-1) ease}
.ctrk.now{background:var(--accent)}
.ctrk.ok{background:var(--good)}
.ctrk.miss{background:var(--bad)}
.ctrk.done{background:var(--ink-3)}  /* at-end: part answered, correctness withheld until review */
.case-body{padding:var(--s-3) var(--s-4) var(--s-4);font-size:var(--t-body);line-height:1.6;color:var(--ink-2)}
.case-body .case-narr{margin:0 0 var(--s-3)}
.case-body .case-dl{margin:0 0 var(--s-1);line-height:1.5}
.case-body .case-dl:last-child{margin-bottom:0}
.case-body .case-dt{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
/* the labs read as a plain table INSIDE the vignette, not a bordered box-in-a-box: a top rule separates
   them from the narrative, header + row hairlines give structure, no outer border/fill. */
.case-labs{margin:0 var(--s-4) var(--s-4);padding-top:var(--s-2);border-top:1px solid var(--line);font-size:var(--t-meta)}
.case-labs .lab-head,.case-labs .lab-row{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);column-gap:var(--s-4);align-items:center;padding:var(--s-2) var(--s-4)}
.case-labs .lab-head{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);border-bottom:1px solid var(--line)}
/* header labels sit directly over their column's data: Lab left, Value centered, Reference right-aligned */
.case-labs .lab-head span:nth-child(2){text-align:center}
.case-labs .lab-head span:nth-child(3){text-align:right}
.case-labs .lab-row+.lab-row,.case-labs .lab-head+.lab-row{border-top:1px solid var(--line)}
.case-labs .lab-name{color:var(--ink-2)}
.case-labs .lab-val{text-align:center;color:var(--ink)}
.case-labs .lab-ref{text-align:right;color:var(--ink-3)}
/* Reveal-only abnormal flag (dataRole cases): shown once the case is answered — H/L/crit badge next to the
   value. Uses the governed correctness/warn tokens; neutral 'inert' never carries one. */
.case-labs .lab-flag{font-family:var(--sans);font-size:var(--t-eye);font-weight:600;letter-spacing:.3px;
  padding:0 5px;border-radius:var(--r-pill);background:var(--warn-bg,color-mix(in srgb,var(--bad) 12%,var(--surface)));color:var(--bad);vertical-align:middle}
.case-partline{display:flex;align-items:center;margin:0 0 var(--s-3)}
.case-part{font-family:var(--sans);font-weight:600;font-size:var(--t-meta);letter-spacing:.02em;color:var(--ink-2)}
/* per-case results breakdown (each case's parts-correct as a labeled bar) */
.breakdown{margin-top:var(--s-5);display:flex;flex-direction:column;gap:var(--s-3)}
.brow{display:grid;grid-template-columns:minmax(72px,auto) 1fr auto;gap:var(--s-3);align-items:center}
.brow .name{font-family:var(--sans);font-size:var(--t-meta);letter-spacing:.02em;color:var(--ink-2)}
.brow .bar{height:8px;border-radius:var(--r-pill);background:var(--surface-2);overflow:hidden}
.brow .bar i{display:block;height:100%;background:var(--accent);border-radius:var(--r-pill)}
.brow .v{font-size:var(--t-meta);color:var(--ink-3);white-space:nowrap}

/* ---- quiz sticky-bar controls: Back (left) · Skip + Submit/Next (right) ----
   The "Q n / N" progress counter lives ONLY on the question card (.qprog); the
   bottom bar deliberately omits it to avoid a duplicate indicator. */
.qb-back{flex:0 0 auto;min-width:92px;padding:0 var(--s-4)}
.qb-skip{flex:0 0 auto;min-width:64px}
.qb-go{flex:0 0 auto;min-width:110px}
@media(max-width:560px){
  .qb-back{min-width:0;padding:0 var(--s-3)}
  .qb-go{flex:1 1 auto;min-width:0}
}

/* ============================================================
   RESULTS SCREEN — 537 result card: eyebrow · big % + phrase ·
   the ECG "rhythm strip" completion effect · actions · review list.
   ============================================================ */
/* results container — its OWN class (not `.card .result`, which collides with the search-result row). */
.results-card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-5)}
/* eyebrow = the ONE uppercase treatment (STYLE_GUIDE §3): --sans · --t-eye · .12em tracking · --ink-3 */
.res-eyebrow{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--s-3)}
/* score + phrase as a balanced unit: the number leads at --t-h1 (was an oversized --t-hero that dominated
   once the page calmed down — owner), the phrase sits beside it, both --ink. Baseline-aligned. */
.res-score{display:flex;align-items:baseline;gap:var(--s-3)}
.res-score .pct{font-family:var(--body);font-weight:700;font-size:var(--t-h1);line-height:1;color:var(--ink);font-variant-numeric:tabular-nums;letter-spacing:-.02em}
.res-score .pct small{font-size:var(--t-h3);color:var(--ink-2);font-weight:600}
.res-phrase{display:flex;flex-direction:column}
.res-phrase b{font-family:var(--body);font-weight:600;font-size:var(--t-h3);color:var(--ink);line-height:1.15}
.res-phrase i{font-style:normal;font-size:var(--t-body);color:var(--ink-2);font-variant-numeric:tabular-nums}

/* the rhythm strip (completion effect) — draws left→right with a glowing sweep line */
.strip{background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-2);padding:var(--s-4) var(--s-3) var(--s-3);margin-top:var(--s-4);box-shadow:var(--shadow-1)}
.strip-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:var(--s-3);
  font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.strip-lead{font-variant-numeric:tabular-nums;text-transform:none;letter-spacing:0}
.strip-trace{position:relative;background:var(--bg);border-radius:var(--r-1);padding:8px 6px;overflow:hidden}
.strip-grid{width:100%;height:auto;display:block;position:relative;z-index:1}
.strip-grid line{stroke:var(--line);stroke-width:1}
.strip-svg{width:calc(100% - 12px);height:auto;display:block;position:absolute;top:8px;left:6px;z-index:2;
  animation:drawStrip 1.5s var(--ease-1) var(--dur-2) backwards}
.strip-sweep{position:absolute;top:6px;bottom:6px;width:2px;z-index:3;
  background:linear-gradient(var(--accent),transparent);box-shadow:0 0 8px 1px rgba(194,65,12,.45);
  animation:sweep 1.5s var(--ease-1) var(--dur-2) backwards}
@keyframes drawStrip{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
@keyframes sweep{0%{left:0;opacity:0}8%{opacity:1}92%{opacity:1}100%{left:100%;opacity:0}}
@media (prefers-reduced-motion:reduce){.strip-svg,.strip-sweep{animation:none}.strip-sweep{display:none}}
.strip-legend{display:flex;align-items:center;gap:var(--s-5);margin-top:var(--s-3);padding-top:11px;border-top:1px solid var(--line)}
.strip-legend .lg{display:flex;align-items:center;gap:7px;font-size:var(--t-meta);color:var(--ink-2)}
.strip-legend .lg svg{display:inline-block}
.strip-legend .lg b{color:var(--ink);font-variant-numeric:tabular-nums}
.strip-legend .lg b.miss{color:var(--bad)}

.results-actions{display:flex;gap:var(--s-3);margin-top:var(--s-4);flex-wrap:wrap}
.results-actions .btn{flex:1 1 auto}
/* REVIEW list — a calm, clickable, hairline-separated list (tap a row to review that answer), NOT bold
   bordered cards (owner: got too bold/big). */
.res-review-hd{font-family:var(--sans);font-size:var(--t-eye);text-transform:uppercase;letter-spacing:var(--track-caps);color:var(--ink-3);margin:var(--s-4) 0 var(--s-2)}
.res-review{max-width:100%;display:flex;flex-direction:column}
.revrow{display:flex;align-items:center;gap:var(--s-3);width:100%;text-align:left;background:none;border:0;border-top:1px solid var(--line);padding:var(--s-3) var(--s-2);cursor:pointer;font:inherit;border-radius:var(--r-1)}
.revrow:first-child{border-top:0}
.revrow:hover{background:var(--surface-2)}
.rev-mark{flex:none;width:16px;text-align:center;font-size:var(--t-meta);font-weight:700}
.rev-mark.ok{color:var(--good)} .rev-mark.no{color:var(--bad)} .rev-mark.sk{color:var(--ink-3)}
.rev-q{flex:none;width:28px;font-family:var(--sans);font-size:var(--t-eye);font-weight:600;letter-spacing:.02em;color:var(--ink-3)}
.rev-stem{flex:1;min-width:0;font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.rev-go{flex:none;color:var(--ink-3);font-size:var(--t-body)}
.revrow:hover .rev-go{color:var(--accent)}

/* small helpers */
.hide{display:none!important}
.muted{color:var(--ink-3)}

/* ============================================================
   QUIZ SETUP  (faithful port of the 537 build screen)
   Labeled chip GROUPS in a vertical stack; crisp rounded-rect
   chips laid out in equal columns; per-chip live counts; empty
   chips grayed/dashed; whole rows grayed when "not applicable".
   ============================================================ */
.sview{display:flex;flex-direction:column}
.group{margin-bottom:var(--s-5)}
/* Two compact binary toggles (Feedback + Expand abbreviations) sharing one line. */
.group-pair{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-6);margin-bottom:var(--s-5)}
@media (max-width:560px){.group-pair{grid-template-columns:1fr;gap:var(--s-5)}}
.group-pair > .group{margin-bottom:0}
.group > .label{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);margin:0 0 var(--s-3)}
/* ONE chip-layout system — replaces BOTH the old per-group col1–6 math AND the Phase-2 flex experiment.
   Every selection group is a GRID of auto-fit tracks:
     · a group that fits one row SPANS the full width (empty tracks collapse, 1fr fills) — Exam, Lens, Teacher…
     · a group that wraps forms an ALIGNED matrix — tracks are grid-wide, so a partial last row lines up
       under the row above instead of the ragged, different-width rows a flex-wrap produced — Topic.
   The min-track (9rem) is the ONLY knob: desktop yields ~5–6 columns, a phone naturally yields 2, both
   aligned and centered. No per-group column count, no per-breakpoint patch — one rule holds every step. */
/* ⛳ `auto-fill`, ⛔ NOT `auto-fit`. 🔬 2026-09-13, 👤: *"nephro quiz options show up for exam and teacher
   load weird"* — measured on `neph`: the Exam row rendered `149px 149px 149px 149px` and the Teacher row
   directly beneath it rendered `306px 306px 0px 0px`. **`auto-fit` COLLAPSES the empty tracks and stretches
   what is left**, so a row with fewer chips than columns gets giant chips, and two rows in the same step sit
   on two different grids. Cardio never showed it because every one of its rows has 4+ items.
   ⛳ `auto-fill` KEEPS the tracks, so 2 chips occupy 2 of 4 at the same 149px and line up with the row
   above. ⚖️ 👤: *"everything is symmetrical… everything follows a style guide so its consistent."* One chip
   width per step is what that means, and it cannot hold while the grid is elastic. */
.chips{display:grid;grid-template-columns:repeat(auto-fill,minmax(9rem,1fr));gap:var(--s-2)}
/* Mode tiles are the ONE intentional non-chip layout: a fixed 3-up grid of big tiles (1-up on mobile, below). */
/* Concepts + Cases — 2 tiles now Recall left (2026-09-06). Balanced + centered, capped so they don't stretch
   on wide screens; stack on a phone. */
.chips.mode-tiles{grid-template-columns:repeat(2,minmax(0,1fr));max-width:620px;margin-left:auto;margin-right:auto}
@media (max-width:560px){.chips.mode-tiles{grid-template-columns:1fr;max-width:360px}}
.chips + .chips{margin-top:var(--s-2)}
/* ⛳ EVERY CHIP IN A GRID IS THE SAME HEIGHT — one line or two (👤 2026-09-07).
   ⛔ This rule used to say `height:auto; min-height:--ctl` — "grow TALLER when a long label wraps" — which
   made the box size a byproduct of WHICH LABEL happened to be long. 🔬 Endo's focus step rendered THREE
   heights at once: lens 36, topic rows 1–2 at 49 (one wrapping label stretched the whole grid row), topic
   row 3 at 36. And it silently contradicted `CHIP_SIZING_MAP.md`, which calls itself the authority and says
   height is uniform. One token now, tall enough for two lines, so nothing clips and nothing shrinks. */
.chips .chip{width:auto;justify-content:center;text-align:center;cursor:pointer;
  height:var(--ctl-2l);min-height:var(--ctl-2l);padding:0 var(--s-4);line-height:1.2}
/* PER-GRID HEIGHT (👤 2026-09-07: "the size of the box should follow a per page rule… on this page you
   can go back to the thinner box height, bc when you go big and equal in length to the bottom navigation
   buttons, it looks weird").
   ⚖️ This does NOT reopen the ragged-chip defect. The rule is still ONE height per grid — what changes is
   WHICH of the two declared heights a grid takes, and it is MEASURED, never chosen by hand: after render,
   a grid in which NO label wraps is marked `.one-line` and drops to --ctl. A grid where even one label
   wraps stays at --ctl-2l, so every chip beside it still matches. Two tokens, one height per grid.
   ⛔ Mode tiles opt out — they are illustrated tiles with their own min-height, not text chips. */
.chips.one-line:not(.mode-tiles) .chip{height:var(--ctl);min-height:var(--ctl)}
.chips .chip span.lbl{white-space:normal;overflow-wrap:break-word}
.chip.chip-empty .n{display:none}
/* Abbreviations TOGGLE SWITCH — one switch, not two chips (owner). On = exam-style abbreviations; Off = full
   terms + first-use hint. Same design language as the chips: --surface-2 off / --accent on, accent knob flip. */
/* height matches the chip row (--ctl) — NOT --tap — so the toggle + ON sit on the SAME center line as the
   REVEAL chips beside it in the pair (owner: it read a few px low against the chip to its left). */
.abbr-tog{display:inline-flex;align-items:center;gap:var(--s-3);background:none;border:0;padding:0;cursor:pointer;font:inherit;height:var(--ctl)}
.group-abbr .abbr-tog{display:flex;width:100%;justify-content:flex-start}   /* LEFT-aligned under its ABBREVIATIONS label (owner: the toggle didn't need centering), tidy in its cell */
.abbr-tog-track{position:relative;width:38px;height:22px;border-radius:var(--r-pill);background:var(--surface-2);border:1px solid var(--line);transition:background var(--dur-2) var(--ease-1),border-color var(--dur-2) ease;flex:none}
/* knob glides via TRANSFORM (compositor-smooth, no left-repaint jerk) */
.abbr-tog-knob{position:absolute;top:50%;left:0;width:16px;height:16px;border-radius:var(--r-pill);background:var(--surface);box-shadow:var(--shadow-1);transform:translate(3px,-50%);transition:transform var(--dur-2) var(--ease-1),background var(--dur-2) ease;will-change:transform}
/* ON = the SELECTED-chip language (soft --accent-bg tint + accent border + a small accent knob), NOT a bold
   accent fill — the loud orange is reserved for Build Quiz + the course title. */
.abbr-tog[aria-checked="true"] .abbr-tog-track{background:var(--accent-bg);border-color:var(--accent)}
.abbr-tog[aria-checked="true"] .abbr-tog-knob{transform:translate(19px,-50%);background:var(--accent)}
/* state text IS the eyebrow label type (--sans uppercase, matches ABBREVIATIONS/FEEDBACK) — not a new font */
.abbr-tog-state{font-family:var(--sans);font-weight:var(--w-cap);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-2);min-width:2.4em}
/* A whole-group disable (Recall/Cases gray out the Teacher/Lens rows) renders each chip with the
   EXACT same disabled look as an individually-emptied chip — those chips are already :disabled, so
   they inherit `.chip:disabled` (opacity .4, dashed, --ink-3, --surface bg) above. No separate
   background and no compounding group opacity (which stacked .5×.4 and read as a second, darker
   gray). Only the group LABEL dims, matched to the chips' .4 so the row reads as one flat gray. */
.group-disabled .label{opacity:.4}
/* NOT-APPLICABLE tuck: an axis that doesn't apply to the chosen TYPE (Lens/Teacher in Recall/Cases)
   collapses to its label + one muted line instead of a dead gray chip row — the page reads as adapting
   to the type, not breaking. Label dims to match the .4 disabled family so it doesn't shout. */
.group-na .label{opacity:.4}
.na-note{font-size:var(--t-meta);color:var(--ink-3);padding:var(--s-1) 0 var(--s-2)}
.cat-grid .chip{font-size:var(--t-meta)}

/* SUB-TOPIC accordion — subcard-family rows (Identity/Med-chem/…) that expand to facet pills.
   Full-width rows mirror the drug card's subcards; the pills are their sub-subtopic children. */
/* SUB-TOPIC accordion — SEPARATE stacked cards (537 parity), each = wide row + separate chevron box. */
/* Sub-topic rows share the setup's box system: same between-box gap (--s-2), same box height (--ctl) and
   horizontal padding (--s-3) and radius (--r-1) as every .chip — so the section reads as one family. */
.subtopic-acc{display:flex;flex-direction:column;gap:var(--s-2);background:transparent}
/* domain header inside the picker (Drugs · Conditions) — only shown when a pool spans both domains, so the
   disease sub-cards read as their own cluster, distinct from the drug sub-cards (FACET_VOCAB domain-first). */
.subtopic-dom-h{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);margin:var(--s-2) 0 0;padding:0 var(--s-1)}
.subtopic-dom-h:first-child{margin-top:0}
.famrow{display:flex;gap:var(--s-2);align-items:stretch}
.subcard-row{flex:1 1 auto;display:flex;align-items:center;gap:var(--s-3);
  height:var(--ctl);padding:0 var(--s-3);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);cursor:pointer;text-align:left}
.subcard-row:hover{background:var(--bg)}
.subcard-row .dir{flex:0 0 auto;width:18px;font-size:var(--t-meta);color:var(--ink-3);line-height:1;text-align:center}
.subcard-row .scname{flex:1 1 auto;font-weight:400;font-size:var(--t-meta);color:var(--ink-2)}
.subcard-row.open .scname,.subcard-row.has-sel .scname{color:var(--accent-press)}
.subcard-row .scn{flex:0 0 auto;font-size:var(--t-eye);color:var(--ink-2);min-width:24px;text-align:right}
.subcard-row.open,.subcard-row.has-sel{border-color:var(--accent)}
.subcard-row.has-sel .scname,.subcard-row.has-sel .dir{color:var(--accent)}
/* separate square chevron box on the right (537 .fam-car). Height is NOT hard-coded: the .famrow is
   align-items:stretch, so the chevron STRETCHES to whatever height .subcard-row is — the row is the single
   source of truth for the pair's height. This makes the two boxes structurally impossible to mismatch
   (the old bug: both hard-coded 44px independently and drifted when one changed). Width stays 44 = square. */
.fam-car{flex:0 0 auto;align-self:stretch;display:inline-flex;align-items:center;justify-content:center;
  width:var(--ctl);min-height:var(--ctl);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink-3);font-size:var(--t-h3);line-height:1;cursor:pointer}
.fam-car:hover{background:var(--bg)}
.fam-car.open{border-color:var(--accent);color:var(--accent)}
.fam-car-dis{opacity:.35;cursor:default}
/* facet chips (sub-sub-topics) under a left accent bar — same box DNA as every .chip: --ctl tall, crisp
   --r-1 radius, --t-meta body text. NOT rounded pills (that broke the site's boxes-and-sizes protocol). */
.facet-pills{display:flex;flex-wrap:wrap;gap:var(--s-2);
  margin:calc(-1 * var(--s-1)) var(--s-3) 0 var(--s-3);padding:var(--s-2) 0 var(--s-2) var(--s-4);
  border-left:2px solid var(--line-2);background:transparent}
.facet-pill{display:inline-flex;align-items:center;gap:var(--s-2);
  font-family:var(--body);font-weight:400;font-size:var(--t-meta);line-height:1;
  height:var(--ctl);padding:0 var(--s-4);border-radius:var(--r-1);cursor:pointer;
  background:var(--surface);color:var(--ink-2);border:1px solid var(--line-2);
  transition:border-color var(--dur-1),color var(--dur-1)}
.facet-pill .n{font-size:var(--t-eye);color:var(--ink-2)}  /* solid --ink-2, AA-legible count (2026-07-25) */
/* .facet-pill.on = the single SELECTION-STATE source below (.chip.on,.sortopt.on,.facet-pill.on) */
.facet-pill.on .n{color:var(--accent-press)}

.rule{border:none;border-top:1px solid var(--line);margin:var(--s-2) 0 var(--s-4)}

/* status line + Start, inside the sticky bottom bar */
/* Progressive-disclosure setup (governor load-flow spec): steps reveal as you choose; no count column, so the
   bottom-bar hint centers over the full-width Build Quiz button. */
/* FIXED WIZARD CARD (owner rethink) — a consistent frame so nothing jumps between steps: a fixed TITLE at the
   top (text swaps per step), the options in the middle (one step visible, slides in; tall steps scroll inside),
   the footer at the bottom (Back + primary, EQUAL size). The card is a set size + position, optically centered. */
/* Fill the space BETWEEN the sticky top bar and the sticky bottom bar — subtract BOTH bars, the safe-area,
   and .main's own vertical padding, or the view runs a hair past the viewport and the page shows a phantom
   "ghost" scroll with nothing actually to scroll (owner). dvh so a mobile URL bar can't reintroduce it. */
.sview{display:flex;flex-direction:column;justify-content:flex-start;align-items:center;min-height:calc(100dvh - var(--topbar-h) - var(--sa-top) - var(--tap) - 2 * var(--s-5));padding:clamp(20px,5vh,64px) 0 4vh}
/* 📱 On a phone the quiz builder starts on the SAME line as every other page (owner 2026-09-20: "some
   elements are from another place… something scale"). `clamp(20px,5vh,64px) 0 4vh` is viewport-SCALING
   padding — 42.2/33.8px on an 844px Pro, 33.4/26.7px on a 667px SE — so the builder sat somewhere different
   on every device AND on a different line from the 24px (`--s-5`) every ordinary page starts at. This is the
   SAME expression `body.recallapp .main` carried (§12 — one concept, two homes), so both are pinned to the
   same token here. Desktop keeps the optical clamp: there the slack is real and the lift does its job. */
@media (max-width:560px){ .sview{padding:var(--s-5) 0} }
/* Card TOP-ANCHORED so the TITLE sits in the SAME spot on every step (owner's #1 ask), with the options
   flowing DIRECTLY below it as one cohesive block — never a title pinned far above a floating content void.
   The card is content-sized and capped to the viewport; a tall step (the focus list) is fully shown, and only
   if it genuinely exceeds the viewport does .wiz-stage scroll (no clipped fixed 452px box). */
/* The card GROWS with its content — no viewport cap, so a tall step (the whole facet picker expanded) is shown
   in full and the PAGE scrolls as one (owner: the inner scroll "covers part of it, you can't see it all"). The
   title stays top-anchored (same spot every step), the options flow below it, and Back/Next sit BELOW the full
   content instead of pinned over a clipped box. overflow-x:clip contains the step slide-animation without turning
   the card into a vertical scroll box (clip is compatible with a visible y-axis). */
.wiz-card{width:100%;max-width:620px;height:auto;display:flex;flex-direction:column;overflow-x:clip}
/* the ONE title — thin modern sans, centered, fixed at the top of the card (same place every step) */
.wiz-q{flex:none;font-family:var(--sans);font-weight:400;letter-spacing:.01em;line-height:1.25;color:var(--ink);font-size:var(--t-h1);text-align:center;margin:0 0 var(--s-5)}
/* No inner scroll — the stage flows at its natural height and the page carries any overflow, so nothing is
   clipped and no inner scrollbar overlaps the chips. */
.wiz-stage{flex:1 1 auto;min-height:0;display:flex;align-items:flex-start;justify-content:center}
.wiz-track{width:100%}
.wiz-tier{display:none}
.wiz-tier.on{display:block}
/* only the current tier animates, sliding in from the right (Next) / left (Back) — no touching steps, no jitter */
/* Slower, gentler glide between steps (owner: the old .3s felt fast/jerky). ~0.5s with a soft ease-out
   settle + a smaller travel so it reads as one calm slide, not a snap. */
.wiz-tier.on.wiz-fwd{animation:wizSlideNext .5s var(--ease-out) both}
.wiz-tier.on.wiz-rev{animation:wizSlidePrev .5s var(--ease-out) both}
@keyframes wizSlideNext{from{opacity:0;transform:translateX(26px)}to{opacity:1;transform:none}}
@keyframes wizSlidePrev{from{opacity:0;transform:translateX(-26px)}to{opacity:1;transform:none}}
/* MODE tiles — illustrated tiles (icon over label), even, sized to carry the step */
.mode-tiles .chip{flex-direction:column;gap:var(--s-3);min-height:150px;justify-content:center;padding:var(--s-4)}
.chip-ic{display:flex;align-items:center;justify-content:center;color:var(--accent)}
.chip-ic svg{width:38px;height:38px}
.mode-tiles .chip.on .chip-ic{color:var(--accent-press)}
/* FOOTER — Back + primary, EQUAL size (flex:1 each), pinned at the card bottom */
.wiz-foot{flex:none;display:flex;gap:var(--s-3);padding-top:var(--s-5)}
.wiz-nav-btn{flex:1 1 0;min-width:0;height:var(--ctl);border-radius:var(--r-1);border:1px solid transparent;font-family:var(--sans);font-weight:600;font-size:var(--t-body);cursor:pointer;display:inline-flex;align-items:center;justify-content:center}
.wiz-back{background:var(--surface);border-color:var(--line);color:var(--ink-2)}
.wiz-back:hover{border-color:var(--ink-3)}
.wiz-next{background:var(--accent);color:var(--on-accent)}
.wiz-next:hover{background:var(--accent-press)}
.wiz-next.is-off{background:var(--surface-2);color:var(--ink-3);border-color:var(--line);cursor:default}
@media (prefers-reduced-motion:reduce){.wiz-tier.on.wiz-fwd,.wiz-tier.on.wiz-rev{animation:none}}
/* MOBILE — its own layout: the card fills the screen, title at TOP, options scroll, footer pinned at BOTTOM;
   mode = a stacked vertical list (icon left), exam = 2-up. Not a shrunk desktop. */
@media(max-width:600px){
  /* the setup page zeroes .main's VERTICAL padding on mobile so the wizard owns the full height from the top
     bar to the screen bottom — footer flush (no gap under Back/Next), no phantom scroll. The card fills exactly
     topbar→bottom; the title's own top margin + the footer's safe-area padding do the breathing. Horizontal
     main padding stays, so the chips keep their side breathing room. */
  body[data-page="course"] .main{padding-top:0;padding-bottom:0}
  .sview{justify-content:flex-start;align-items:stretch;padding:0;min-height:calc(100dvh - var(--topbar-h) - var(--sa-top))}
  .wiz-card{max-width:none;height:calc(100dvh - var(--topbar-h) - var(--sa-top));max-height:none}
  .wiz-q{margin:var(--s-5) 0 var(--s-4)}
  /* ⛳ THE STAGE IS THE SCROLLER, AND THE FOOTER IS OPAQUE. 🔬 2026-09-19, walking endo on a 390px phone:
     the card is a fixed-height flex column (stage + Back/Next), the stage had `flex:1;min-height:0` but NO
     `overflow-y`, so a long topic list did not scroll INSIDE it — it overflowed, and the footer, drawn with
     a transparent background, sat ON TOP of the chips. "‹ Back" printed over "Menopause" and "Next ›" over
     "Menstrual Disorders", and the button could not be pressed because the chip underneath took the tap.
     ⛔ A wizard you cannot advance on a phone is the whole app, not a cosmetic defect. */
  .wiz-stage{padding:0 0 var(--s-4);overflow-y:auto;overscroll-behavior:contain;-webkit-overflow-scrolling:touch}
  .wiz-foot{padding:var(--s-4) 0 calc(var(--s-3) + var(--sa-bottom));border-top:1px solid var(--line);
    background:var(--bg)}
  /* ─── MOBILE CHIP THEORY (owner: "make it consistent, with a theory, for the user") ───
     On a phone every selection chip is the SAME: 2 equal columns · full --t-meta label (never shrunk) ·
     --ctl (44px) tall · fills its column. The desktop balances 2/3/4 columns PER GROUP by item-count to
     avoid an orphan last chip — smart on a wide screen, but on a phone it makes EXAM (2-up) and TEACHER
     (3-up) different widths, and dense grids shrink text to --t-eye. That reads as "no two rows match."
     User-first call: a phone is thumb-driven and narrow, so one big uniform tap target beats clever
     packing. Mode tiles are the one intentional exception — full-width rows, because they're the
     top-level "what am I doing" choice, not a grid of peers. */
  .wiz-tier .chips.mode-tiles{grid-template-columns:1fr;gap:var(--s-3)}
  .wiz-tier .mode-tiles .chip{flex-direction:row;justify-content:flex-start;text-align:left;gap:var(--s-4);min-height:var(--tap);padding:var(--s-3) var(--s-4)}
  .wiz-tier .mode-tiles .chip .chip-ic{flex:none}
  .wiz-tier .mode-tiles .chip .chip-ic svg{width:26px;height:26px}
  /* every other selection row is flex-wrap content-width chips (base rule) — no per-row column overrides needed. */
  /* phones are too narrow for a long quip on one line — let it wrap here so it never overflows the viewport */
  .buildq-quip{white-space:normal}
}

@media(max-width:520px){
  .hero-logo{font-size:var(--t-hero-sm)}
}

/* ===== TOP 300 · quiz.  Setup uses the SHARED .group/.chips/.chip setup components (identical to
   537/535 — PAGE_FORMAT_STANDARD), so nothing bespoke is needed here. Only the two quiz-specific
   bits below: the hint highlight on the stem, and the At-end review detail. ===== */
/* Hint suffix-stem highlight on the question subject (DRUGBANK_HINT_RULES §1). A YELLOW HIGHLIGHTER over
   the stem — NOT a padded pill. Horizontal padding is 0 so the highlight sits flush against the rest of the
   word (e.g. "Labetalol" stays ONE word, not "Labet alol"); text color/weight inherit so the marked
   letters read as continuous text with a marker over them. box-decoration-break keeps it clean across wraps. */
mark.hl{background:var(--hl);color:inherit;font-weight:inherit;border-radius:2px;padding:0;
  -webkit-box-decoration-break:clone;box-decoration-break:clone}
.fc-row.fc-hint .fc-v{color:var(--ink-2)}
/* At-end results review — per-question your-answer / correct-answer / hint lines. */

/* ============================================================
   CANONICAL TYPE ROLES — enforced. Placed LAST so it wins by source
   order. ONE definition per role; components must not fork these.
   A genuinely new role is raised before it's added. See
   docs/TEXT_STYLE_MAP.md for the full role table + location map.
   ============================================================ */
/* SECTION LABEL ("eyebrow") — the single small uppercase label style.
   Regular weight (NOT bold), 11px, .12em tracking, uppercase. Each element
   keeps its own color (ink-3 default, accent where it's an emphasis label)
   and its own margins — only the type shape is normalized here. */
.eyebrow,.navgroup>.glabel,.group>.label,.dd-section,.dd-tbl td:first-child,.dz-lbl,
.sortpanel-cap,.dgroup,.contra-lvl,.lipidgrid .lp-k,.sheet-sub,.gl-gtitle,.res-eyebrow,
.strip-head,.qmeta .qtag,.qcard .qtype,.medstruct-zoomhint,.hero-tag,.result .r-class,
.sugg-class,.explain .h,.explain .fc-k,.case-tag,.case-body .case-dt,.case-labs .lab-head,
.exp-concept .ee,.exp-why h4{
  font-family:var(--sans);font-weight:var(--w-cap);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;
}

/* SELECTION STATE — the ONE "selected/active" treatment (accent tint + accent
   border + accent text + 600). Grouped so there is a single source of truth;
   .navlink.active and .densebtn.on are intentional near-variants (nav = tint only;
   dense = accent-press) and keep their own rules. */
.chip.on,.sortopt.on,.facet-pill.on,.rc-w-fpill.on{
  /* selected-state text = --accent-press (the "active" accent, #9a330a ~6.1:1 on --accent-bg) not
     --accent (~4.3:1, fails WCAG AA on the tint). Fill + border stay --accent, so the treatment is
     unchanged; only the text darkens a hair. 2026-07-25. */
  /* selected = accent fill + border + text; NO weight bump (owner: "debold the main chips, keep it clean") —
     the color already carries the selected state, the extra 600 just read heavy. */
  background:var(--accent-bg);border-color:var(--accent);color:var(--accent-press);
}

/* NUMBERS (counts · scores · progress · lab/dose values) = BODY font with tabular alignment.
   MONO is reserved for coded IDs & code-keys ONLY (question IDs, rail course tags Rx/537/535/300,
   option keys A/B/C/D, the A–Z glossary index, abbreviation chips). Enforced here, source-order-last. */
.bb-count,.bb-count b,.chip .n,.facet-pill .n,.subcard-row .scn,.dgroup-n,
.dtitle-meta,.qprog,.case-pts,.case-labs .lab-val,.case-labs .lab-ref,.brow .v,
.startrow .count,.startrow .count b,.strip-legend .lg b{
  font-family:var(--body);font-variant-numeric:tabular-nums;
}

/* TIER-3 JUMP — lives in the shell top bar while a guide is open (owner 2026-09-06). A native <select> on
   purpose: it is one tap on a phone, gets the OS picker for free (scrollable, searchable by keypress), and
   needs no custom popover that would become a fifth navigation. */
.sgjump{display:inline-flex;align-items:center;gap:var(--s-2);min-width:0}
.sgjump-lb{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);flex:none}
.sgjump-sel{appearance:none;-webkit-appearance:none;font-family:var(--sans);font-size:var(--t-meta);font-weight:500;
  color:var(--ink);background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1);
  height:var(--ctl-sm);max-width:15rem;padding:0 calc(var(--s-5) + var(--s-1)) 0 var(--s-3);cursor:pointer;
  text-overflow:ellipsis;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236a5c48' stroke-width='2'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat:no-repeat;background-position:right var(--s-2) center;background-size:15px}
.sgjump-sel:hover{border-color:var(--accent)}
.sgjump-sel:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
@media (max-width:560px){ .sgjump-lb{display:none} .sgjump-sel{max-width:11rem} }

/* THE FILTER FINDER — replaces the 787-chip wall (owner 2026-09-06). Search across every facet at once; a
   result names its category so "Oral" reads as a Route and not a class. */
/* ⛳ THE SAME BOX AS EVERY OTHER CONTROL IN THIS PANEL. 👤, 2026-09-17: *"spacing is off on the left… the
   size of the boxes and selects are all different?"* 🔬 MEASURED at 1400px, the left column top to bottom:
     Search drugs   287 × **36**   14px
     A → Z / Class / Topic  287 × **36**   13px
     Filter by any property 287 × **44**   14px   ⛔ eight pixels taller than everything above it
   ⚖️ The widths and the left edge were already right (all 99 → 386); what broke the rhythm was ONE control
   on `--tap` while its neighbours sit on `--ctl`, and it is the bottom-most box so the error reads as the
   panel getting looser as it goes down. ⛳ `--ctl` is the panel's control height; ⛔ `--tap` is the minimum
   TOUCH target, which is a phone rule, not a desktop box size. */
.ffind{width:100%;height:var(--ctl);margin:0 0 var(--s-3);padding:0 var(--s-4);
  font-family:var(--sans);font-size:var(--t-meta);color:var(--ink);
  background:var(--surface);border:1px solid var(--line-2);border-radius:var(--r-1)}
.ffind::placeholder{color:var(--ink-3)}
.ffind:focus-visible{outline:2px solid var(--accent);outline-offset:1px;border-color:var(--accent)}
.ffopt{display:flex;align-items:center;gap:var(--s-2);width:100%;min-height:var(--ctl);
  padding:var(--s-2) var(--s-3);border:1px solid var(--line);border-radius:var(--r-pill);
  background:var(--surface);font-family:var(--sans);font-size:var(--t-meta);color:var(--ink);
  text-align:left;cursor:pointer}
.ffopt:hover{border-color:var(--accent);background:var(--accent-bg)}
.ffopt.on{border-color:var(--accent);background:var(--accent);color:var(--on-accent)}
.ffopt-lb{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* the category label — what makes a cross-facet search legible */
.ffopt-f{flex:none;font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.ffopt.on .ffopt-f{color:var(--on-accent);opacity:.75}
.ffopt-n{flex:none;margin-left:auto;font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-2)}
.ffopt.on .ffopt-n{color:var(--on-accent)}
.ffopts{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-bottom:var(--s-3)}
.ffmore{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-2);margin:var(--s-2) 0 0}
.ffacet{margin-bottom:var(--s-4)}
.ffacet .ffname{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);margin-bottom:var(--s-2)}
/* A FACET IS A CLOSED ROW UNTIL YOU OPEN IT (👤: "do you need to list every property on the side or is there
   a cleaner more simple way?"). The name, how many drugs it can filter, and a caret — ⛔ no wall of chips.
   It is a real button: full width, its own tap target, and it reuses the `.ffname` type so the panel still
   reads as one control and not two. */
.ffacet .ffname-btn{display:flex;align-items:center;gap:var(--s-2);width:100%;min-height:var(--ctl-sm);
  padding:var(--s-1) 0;background:none;border:0;cursor:pointer;text-align:left}
.ffacet .ffname-lb{flex:1 1 auto}
.ffacet .ffname-n{font-family:var(--mono);font-size:var(--t-meta);color:var(--ink-3);letter-spacing:0}
.ffacet .ffname-caret{color:var(--ink-3);font-size:var(--t-meta)}
.ffacet .ffname-btn:hover .ffname-lb{color:var(--ink-2)}
.ffacet + .ffacet{border-top:1px solid var(--line)}

/* ── REFERENCE INDEX ON DESKTOP — controls left, results right ──────────────────────────────────────────
   Before: an 820px column inside a 1440px window, with search + Show + Sort + Filter stacked ABOVE a
   34,000px-tall list, and every control hidden behind a top-bar icon. Now the controls are a sticky column
   that is always visible where there is room, and the list starts at the top of the viewport.
   ⛔ Mobile keeps the stacked column and the icon toggles — a sidebar needs width to be an improvement. */
@media (min-width:1000px){
  body[data-page="drug-bank"] .main,
  body[data-page="disease-bank"] .main{max-width:1280px}
  .dview:not(.dcard){display:grid;grid-template-columns:clamp(230px,23%,290px) minmax(0,1fr);
    gap:var(--s-6);align-items:start}
  .dview.dcard{display:flex;flex-direction:column;gap:var(--s-4);max-width:880px;margin:0 auto}
  .dside{position:sticky;top:var(--s-3);display:flex;flex-direction:column;gap:var(--s-5);
    max-height:calc(100vh - var(--topbar-h) - var(--s-5));overflow-y:auto;padding-bottom:var(--s-4)}
  .dmain{min-width:0}
  /* With a column of their own the controls are simply present — ⛔ no reason to hide them behind a toggle. */
  .dside .sortpanel.closed{display:block}
  /* ⛔ …AND THEREFORE THE TOGGLES THEMSELVES ARE DEAD CONTROLS HERE. 👤, 2026-09-17: *"the entire search
     thing on the drug bank is bad… have you even seen how confusing it is?"* 🔬 I looked: four unlabelled
     icons in the top bar, and two of them — Organize and Search — only toggle `.closed` on panels that the
     rule directly above FORCES VISIBLE at this width. So on desktop you press them and NOTHING HAPPENS.
     ⚖️ A control that cannot fail is a defect; a control that cannot DO anything is worse, because the
     student assumes they missed something and presses it again. ⛳ They stay on narrow, where the panels
     really do collapse and the toggle is the only way back to them. */
  /* ⛳ AND THE FILTER IS THE THIRD ONE — 👤, 2026-09-17: *"isn't the show/no show features at the top only
     a mobile feature? and why isn't there a show/no show for the search?"*
     ⚖️ HE HAS IT EXACTLY BACKWARDS-PROOFED: the SEARCH has no toggle because on desktop it simply lives in
     the column — which is right — and the question is why the FILTER did not get the same treatment.
     🔬 MEASURED at 1400px before this: of the four icons, `bkSortTog` and `bkSearchTog` were hidden and
     `bkFilterTog` and `bkCollapse` were not. So two behaved one way and two the other, in one four-icon
     row, and the only thing separating them was which day each was written.
     ⛳ The filter panel is now simply PRESENT in the column like the search and the group-by, and its
     toggle goes with the other two. ⛔ One rule: on desktop the column shows its controls; nothing in it
     is hidden behind an unlabelled icon. */
  .dside .filterpanel.closed{display:block}
  /* ⛳ AND THE COLLAPSE ARROW, which 👤 chose directly when asked: *"Remove it on desktop."*
     ⚖️ His reasoning, and it is the rule for this whole bar: expand/collapse is a PHONE concern. On a
     desktop the list stays open and you scroll or search; on a phone the groups really do need folding
     and all four icons stay. ⛳ That leaves the desktop top bar with NOTHING in it but the page's name —
     ⛔ no unlabelled icon doing something the left column does not. */
  .dtools #bkSortTog,
  .dtools #bkFilterTog,
  .dtools #bkCollapse,
  .dtools #bkSearchTog{display:none}
  .dside .searchwrap{margin:0}
  .dside .sortpanel-grid,
  .dside .sortpanel.banktype .sortpanel-grid{display:flex;flex-direction:column;align-items:stretch;gap:var(--s-2)}
  .dside .sortopt{width:100%;text-align:left;justify-content:flex-start}
  .dside .filterpanel{margin:0}
}


/* ── STUDY GUIDES — ONE ROW, THREE TIERS ────────────────────────────────────────────────────────────────
   Course · Topic · Section are the SAME control at three depths (owner: "make the 3 tiers look the same").
   Column view: drill left→right on desktop, one column at a time on a phone. Optically centered — the block
   is centered horizontally and sits slightly ABOVE true center, which reads as centered to the eye. */
/* OPTICALLY CENTERED: the column block is centered horizontally AND vertically, then lifted slightly above
   true center — the eye reads a block sitting a little high as "centered", and one sitting exactly centered as
   slightly low. Columns are top-aligned to each other (they are lists), the BLOCK is what centers. */
/* ⛔ THE PICKER SHELL IS THE PAGE, SO IT IS THE PAGE'S GROUND. 👤, 2026-09-14: *"make sure the colors of
   the side and top menu bars are all the same color… i think even on study guides there is an issue."*
   🔬 He was right and it was the biggest one: `.sg2` filled everything below the top bar at `--surface`
   (251,247,239) while the rail, the top bar and the bottom bar are all (244,239,228) — so the entire
   content area was a lighter panel with a hard seam running down the left edge against the rail, and
   another under the top bar. ⛳ Every other page in the app puts its content on `--bg` and floats CARDS on
   `--surface`; this page had no cards, so the pane itself had quietly become the card.
   ⚖️ THE READING STATE IS UNCHANGED AND DELIBERATE: `.sg2.reading` is filled edge-to-edge by the guide
   iframe, whose own `--paper` IS `--surface` — *"the guide reads as a clean page"* (learn/guide.css §217).
   ⛔ That is a READING surface and it stays paper; this is the SHELL around a picker, which is not.
   ⛳ `--rail-bg` is the token, so the shell moves with the chrome rather than carrying its own copy. */
.sg2{height:calc(100vh - var(--topbar-h) - var(--sa-top));background:var(--rail-bg);overflow:hidden;
  display:flex;justify-content:center;align-items:stretch}
/* ⛔ THE WINDOW IS FIXED; THE CONTENT SCROLLS INSIDE IT. Previously the block was vertically centered and its
   height changed as columns filled, so the whole layout JUMPED on every click (owner 2026-09-06: "when
   clicking on various things it adjusted how its centered"). A stable frame with internal scroll means the
   columns can differ in length — 6 courses beside 27 sub-topics — and nothing moves. */
.sgwrap{width:100%;height:100%;display:flex;gap:var(--s-5);padding:var(--s-5);
  overflow:hidden;align-items:stretch}
.sgwrap-3{max-width:70rem}
.sgwrap-1{max-width:34rem;flex-direction:column;gap:var(--s-4)}
/* Each column is a LANE: separated by a rule, with its own settled head. Without the divider the three
   lists float in one field and read as unrelated (owner 2026-09-07: "put some lines between the lanes"). */
.sgcol{flex:1 1 0;min-width:0;display:flex;flex-direction:column;gap:0;overflow:hidden;height:100%;
  padding-right:var(--s-5);border-right:1px solid var(--line)}
.sgcol:last-child{border-right:0;padding-right:0}
.sgcol-h{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);padding:0 var(--s-3) var(--s-3);border-bottom:1px solid var(--line);margin-bottom:var(--s-3)}
/* What a column says while it waits for the step before it. ⛔ Better than a ruled, labeled void. */
.sgcol-b{display:flex;flex-direction:column;gap:2px;overflow-y:auto;min-height:0;padding-right:var(--s-1)}

/* THE row — identical at every tier */
.sgrow{display:flex;flex:none;align-items:center;gap:var(--s-3);width:100%;height:var(--tap);
  padding:var(--s-2) var(--s-3);border:1px solid transparent;border-radius:var(--r-1);
  background:none;font:inherit;text-align:left;color:var(--ink);cursor:pointer;
  transition:background var(--dur-1) var(--ease-1),border-color var(--dur-1) var(--ease-1)}
.sgrow:hover{background:var(--surface-2)}
.sgrow.on{background:var(--accent-bg);border-color:var(--accent);color:var(--accent)}
.sgrow:focus-visible{outline:2px solid var(--accent);outline-offset:1px}
.sgrow.dim{color:var(--ink-3);opacity:.55;cursor:default}
.sgrow.dim:hover{background:none}
.sgrow.dim .sgrow-go{display:none}

.sgrow.dim .sgrow-ic{opacity:.45}
.sgrow-ic{flex:none;display:inline-flex;width:18px;height:18px;color:currentColor}
.sgrow-ic svg{width:18px;height:18px}
/* ONE LINE at every tier — a stacked meta made the Topic row taller than Course/Section and broke
   the 'three tiers look the same' rule. Label and meta sit on one baseline instead. */
.sgrow-tx{min-width:0;display:flex;align-items:baseline;gap:var(--s-2)}
.sgrow-lb{font-size:var(--t-body);font-weight:500;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* ⛳ A GUIDE'S NAME WRAPS ON A PHONE RATHER THAN TRUNCATING — 🔬 "Principles of Drug Action" missed by 5px
   at 320. It is the row's whole identity; one more line costs nothing in a list you scroll anyway. */
@media (max-width:560px){.sgrow-lb{white-space:normal;overflow:visible;text-overflow:clip}}
.sgrow-mt{flex:none;white-space:nowrap;font-size:var(--t-eye);color:var(--ink-3)}
.sgrow.on .sgrow-mt{color:var(--accent)}
.sgrow-tag{flex:none;margin-left:auto;font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.sgrow-go{flex:none;margin-left:auto;color:var(--line-2)}
.sgrow.on .sgrow-go{color:var(--accent)}
.sgrow:hover .sgrow-go{color:var(--ink-2)}
.sgnote{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);padding:var(--s-2) var(--s-3);margin:0}

/* mobile drill-down breadcrumb — the columns' headers, walked backwards */
.sgcrumb{display:flex;align-items:center;gap:var(--s-2);flex-wrap:wrap}
.sgcrumb-b{background:none;border:0;padding:var(--s-1) 0;font-family:var(--sans);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);cursor:pointer}
.sgcrumb-b.on{color:var(--accent)}
.sgcrumb-s{color:var(--line-2);font-size:var(--t-eye)}
.sg2-frame{width:100%;height:100%;border:0}
.sg2.reading{display:block}

/* MOBILE (single column): ⛔ do NOT vertically center here. `align-items:center` on the parent shrink-wraps
   the wrap, and a `flex:1 1 0` column inside a COLUMN flex parent then resolves to ZERO height — the rows
   rendered outside their own container's box and every tap hit the background instead of the button.
   🔬 Measured 2026-09-06: wrap 87px tall, column 0px, first course button unreachable. Top-align and scroll. */
@media (max-width:899px){
  .sg2{align-items:stretch}
  .sgwrap-1{height:100%;max-height:100%;margin-bottom:0}
  .sgwrap-1 .sgcol{flex:1 1 auto;min-height:0;max-height:none}
}

/* THE POT — the union of all three axes, always visible so "additive" is legible rather than implied. */

/* Reserve the tag row: adding or clearing a tag must not move the footer. */
/* SELECTION ROW — chips block on top, actions on one aligned row beneath. 🔬 2026-09-13: the actions were
   inline peers of the chip block with TWO competing `margin-left:auto`s, so "Save list" (14px tall) and
   "Clear" (36px) landed on different lines at different heights. One container, one baseline, one rule. */
.rc-pot{flex:none;display:flex;flex-direction:column;gap:var(--s-2);margin-top:var(--s-4)}
/* ⛳ THE SELECTION'S COMMIT ROW — same shape as the settings page's: the count holds the left, the quiet
   actions sit next to the primary, and the primary lands on the right edge. ⛔ `--ctl` tall, not `--ctl-sm`,
   because it now holds a real button rather than three lines of micro-text. */
.rc-pot-acts{display:flex;align-items:center;gap:var(--s-3);min-height:var(--ctl);
  margin-top:var(--s-3);padding-top:var(--s-3);border-top:1px solid var(--line)}
.rc-pot-n{font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-3);margin-right:auto}
.rc-pot-act{flex:none;background:none;border:0;padding:0;cursor:pointer;font-family:var(--sans);
  font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3);white-space:nowrap;
  line-height:1.5}
.rc-pot-act:hover{color:var(--accent)}
/* A drug that a picked CLASS already covers. Quieted, not removed: the pick is harmless (the pool is a
   union, counted once) and the student may well want it listed — it just should not read as extra weight. */
.rc-dchip-dup{opacity:.72}
.rc-dup{margin-left:var(--s-1);font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-3)}
/* ⛔ A PICKER MAY NOT PUSH THE PAGE. 🔬 21 drugs grew this to 196px and kept going; the student's own
   selection was shoving Next toward the fold. Past ~3 rows it scrolls and the wizard keeps its shape. */
.rc-dchips{max-height:calc(3.2 * var(--ctl));overflow-y:auto;overscroll-behavior:contain}
/* Clear is just a .rc-pot-act now — ⛔ its old `margin-left:auto` fought the actions row and split it. */
/* SAVE / UPDATE a drug list — quiet text actions sitting with Clear, so the row reads as one set of things
   you can do to the current selection rather than a new control bar. */
/* A saved list reads as a chip like everything else here; .on marks the one currently loaded, through the
   site's single selection treatment. The × deletes and asks first. */
.rc-saved{margin-top:var(--s-3)}
.rc-saved-note{margin-left:var(--s-2);font-family:var(--sans);font-size:var(--t-eye);letter-spacing:.04em;
  text-transform:none;color:var(--ink-3);font-weight:400}
.rc-listchip .n{margin-left:var(--s-1);font-family:var(--mono);font-size:var(--t-eye);color:var(--ink-3)}
.rc-listdel{margin-left:var(--s-1);color:var(--ink-3);font-size:var(--t-meta);line-height:1}
.rc-listdel:hover{color:var(--bad)}
.rc-pot-none{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);
  border-top:1px solid var(--line);margin:var(--s-4) 0 0;padding-top:var(--s-4)}

/* ── THE ABBREVIATION HINT BOX (👤 2026-09-07, replacing ADR-0024's inline expansion) ───────────────
   Sits between the stem and the options. A quiet INSET panel — --surface-2 on --surface — so it reads as
   "set aside" without drawing a second frame inside the question card.
   BREATHING ROOM is the layout: ONE entry per row, the short forms in their own aligned column, --s-2
   between rows, --s-4 of padding. The eye scans a single column of short forms and stops at the one it
   needs. ⛔ It never rewrites the question, so an option's LENGTH is never altered by it — which is the
   defect the old inline render could inject (option length is an answer cue). */
/* THE ABBREVIATION KEY IS A FOOTNOTE TO THE QUESTION, ⛔ NOT A PANEL ABOVE IT (👤, 2026-09-13: "the hints
   section is also very overwhelming"). 🔬 It was a FILLED tan card sitting above the stem — on the worst
   item in the bank (6 terms) it outweighed the question it was explaining, so the eye landed on the
   glossary and the stem read as secondary. ⚖️ A reading aid that competes with the thing it helps you read
   has stopped being an aid. Now: no fill, a hairline under it, quieter keys — it sits out of the way until
   you need it, and the question is the loudest thing on the card again. ⛳ ONE component: the quiz and
   recall both render it, so this is one rule for both (the payoff of unifying the two cards). */
.abbox{background:none;padding:0 0 var(--s-3);margin:var(--s-2) 0 var(--s-3);border-bottom:1px solid var(--line)}
.abrow{display:grid;grid-template-columns:5.5rem 1fr;gap:var(--s-3);align-items:baseline}
.abrow + .abrow{margin-top:var(--s-1)}
.abk{font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--ink-2);letter-spacing:.01em}
.abv{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);line-height:1.45}
/* Five or more: two columns of rows on a wide screen, one on a phone. 🔬 The worst item in the bank is 12. */
@media (min-width:620px){
  .abbox.two{display:grid;grid-template-columns:1fr 1fr;gap:var(--s-2) var(--s-6)}
  .abbox.two .abrow + .abrow{margin-top:0}
}
@media (max-width:420px){ .abrow{grid-template-columns:4.5rem 1fr;gap:var(--s-2)} }

/* ── STUDY-GUIDE READER — one node of a governed spine (2026-09-07) ─────────────────────────────────
   The reading pane used to be an <iframe> onto a hand-written HTML module; it now renders 🎓's governed
   content directly. ⛔ Prose holds --measure so a line stays readable; TABLES break out of it, because an
   ECG interval table is a grid to scan, not a paragraph to read. */
.sgread{padding:var(--s-4) var(--s-4) var(--s-6);overflow-y:auto;height:100%}
.sgread > *{max-width:var(--measure);margin-left:auto;margin-right:auto}
.sgread-h{font-family:var(--sans);font-size:var(--t-h1);font-weight:600;color:var(--ink);margin:0 0 var(--s-2)}
.sgread-lede{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);margin:0 0 var(--s-4);line-height:1.6}
.sgread-p{font-family:var(--body);font-size:var(--t-body);color:var(--ink-2);line-height:1.7;margin:0 0 var(--s-3)}
.sgread-note{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin:0 0 var(--s-3)}
.sgread-pearl{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.6;margin:var(--s-2) 0 0;
  padding-left:var(--s-3);border-left:2px solid var(--accent)}
.sgblk{margin:0 0 var(--s-4)}
.sgblk-h{font-family:var(--sans);font-size:var(--t-h3);font-weight:600;color:var(--ink);margin:0 0 var(--s-2)}
.sgb{margin:0 0 var(--s-2);padding-left:var(--s-4)}
.sgb li{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.6;margin-bottom:2px}
.sgscen{font-family:var(--sans);font-size:var(--t-meta);font-weight:600;color:var(--ink);margin-bottom:var(--s-2)}
.sgpill{display:inline-flex;align-items:baseline;gap:6px;font-family:var(--sans);font-size:var(--t-meta);
  color:var(--ink);background:var(--surface-2);border:1px solid var(--line);border-radius:var(--r-pill);
  padding:4px 10px;margin:0 6px 6px 0}
.sgpill i{font-style:normal;font-size:var(--t-micro);color:var(--ink-3)}
.sgivals{display:flex;flex-wrap:wrap;gap:var(--s-2);margin:0 0 var(--s-2)}
.sgival{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-2)}
.sgival b{font-weight:600;color:var(--ink-3);font-size:var(--t-micro);letter-spacing:.06em;margin-right:3px}
/* A wide table scrolls INSIDE its own box — the page body never scrolls sideways. */
.sgtw{max-width:100%;overflow-x:auto;-webkit-overflow-scrolling:touch;margin:0 0 var(--s-4)}
.sgt{border-collapse:collapse;width:100%;min-width:520px}
.sgt th{font-family:var(--sans);font-size:var(--t-micro);letter-spacing:var(--track-caps);text-transform:uppercase;
  color:var(--ink-3);font-weight:var(--w-cap-strong);text-align:left;padding:0 var(--s-3) var(--s-2) 0;border-bottom:1px solid var(--line)}
.sgt td{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);padding:var(--s-2) var(--s-3) var(--s-2) 0;
  border-bottom:1px solid var(--line);vertical-align:top}
.sgt .sgt-k{font-family:var(--sans);color:var(--ink);font-weight:600;white-space:nowrap}
.sgread-ref{display:inline-block;font-family:var(--sans);font-size:var(--t-meta);color:var(--accent);
  text-decoration:none;margin-top:var(--s-3);padding:var(--s-2) 0}
.sgread-ref:hover{text-decoration:underline}
@media (max-width:720px){.sgread{padding:var(--s-3) var(--s-3) var(--s-6)}.sgt{min-width:440px}}
/* A row whose name is PROVISIONAL — its slug is not a governed topic, so the label is a stand-in until the
   real name lands. Muted + a dotted underline: readable and usable, but visibly not final. ⛔ The row still
   works; this marks the NAME, not the content. */
.sgrow.prov .sgrow-lb{color:var(--ink-2);text-decoration:underline dotted var(--line-2);text-underline-offset:3px}
.sgrow.prov .sgrow-mt{font-style:italic}
.rc-hits{display:flex;flex-direction:column;gap:var(--s-1);min-height:0}
.rc-hit{display:flex;align-items:center;gap:var(--s-3);width:100%;min-height:var(--ctl);
  padding:0 var(--s-3);background:var(--surface);border:1px solid var(--line);border-radius:var(--r-1);
  cursor:pointer;text-align:left;font-family:var(--sans);font-size:var(--t-meta);color:var(--ink)}
.rc-hit:hover{border-color:var(--recall);background:var(--recall-tint)}
.rc-hit-l{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.rc-hit-k{flex:0 0 auto;font-size:var(--t-micro);color:var(--ink-3)}
.rc-note{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin:0 0 var(--s-3)}
/* Result-group heading (Disease states / Drug classes / Drugs) — a label, not a control. */
.rc-hit-g{font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-3);margin:var(--s-3) 0 var(--s-1)}
.rc-hit-g:first-child{margin-top:0}
.rc-none{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);margin:var(--s-4) 0 0;text-align:center}
/* EMPTY STATE — the biggest starting points, as SITE chips (.chips/.chip), so the first screen is usable
   before typing and uses the quiz wizard's own component. Only the group label is local. */
.rc-qg + .rc-qg{margin-top:var(--s-5)}
/* ⛳ THE SUGGESTION CHIPS SIZE TO THEIR WORDS. 👤: *"why are the boxes so large?"* 🔬 They were in the quiz's
   `.chips` GRID — `repeat(auto-fill, minmax(9rem, 1fr))` — where every track stretches to fill the column,
   so "Gout" rendered 236px wide to match "Coronary Artery Disease". ⚖️ That grid is right for the quiz,
   where a row of filters SHOULD form an aligned matrix; it is wrong here, where these are a handful of
   shortcuts and their length is information. ⛔ Same `.chip` component, just not forced onto a shared track. */
.rc-qg .chips{display:flex;flex-wrap:wrap;gap:var(--s-2)}
.rc-qg .chips .chip{width:auto}
.rc-qg-h{font-family:var(--sans);font-size:var(--t-eye);font-weight:var(--w-cap-strong);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-3);margin-bottom:var(--s-2)}
/* ⛔ DELETED 2026-09-19, found by the new dead-rule guard: `.rc-qg .chips` is `display:flex` (right above),
   so this `grid-template-columns` never applied — it has been inert since the day the row was moved off the
   quiz's grid. ⚖️ And it argued the OPPOSITE of the live rule: it forced 11.5rem tracks, while the comment
   above deliberately let these chips size to their content because "their length is information". The
   shipped behavior is the one that was argued for; the dead line was the leftover. */

/* The step card GROWS with its content and the page carries any overflow — .wiz-card's rule, for the same
   reason: an inner scroll box clips the thing you are choosing from. */
.rc-wiz{display:flex;flex-direction:column;width:100%;height:auto;overflow-x:clip}
.rc-wiz > .rc-q,.rc-wiz-body > .rc-dsearch{flex:none}
.rc-wiz-body{display:flex;flex-direction:column}

/* ⛔ A TREE THAT STOPS DEAD SAYS SO. 🔬 65 of 65 governed trees rendered their root and then nothing, because
   the renderer read the wrong chaining key — silently. A visible stop is how the next one gets found in one
   look instead of by reading JSON (§11: never swallow an error). */
.ft-broken{padding:var(--s-3);border:1px dashed var(--line-2);border-radius:var(--r-1);
  font-size:var(--t-meta);color:var(--ink-2)}

/* ⛔ AN UNAVAILABLE FILTER SAYS WHY — 🗄️, 2026-09-13: "for a course whose exam_source is none, the filter
   must be shown unavailable with the reason, not shown empty." Quiet, inside the step, in the reading face —
   this is a sentence a student acts on, not an error. */
.wiz-note{margin:var(--s-3) 0 0;font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);line-height:1.45}

/* ⛳ ONE BLOCK PER EXAM — 🗄️'s SPEC DELTA: the exam line says WHEN, the topics under it say WHAT. Stacked,
   not a wrapping row: neph exam 1 carries 12 topics and they would wrap into an unreadable ribbon. */
.acct-exlist{display:flex;flex-direction:column;gap:var(--s-3);margin-top:var(--s-3)}
.acct-ex{min-width:0}
.acct-exhead{display:flex;align-items:baseline;gap:var(--s-2)}
.acct-extop{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.45;margin-top:2px}
/* The lens line sits one tone quieter than the topics — it narrows them, it does not name them. */
.acct-exlens{font-family:var(--sans);font-size:var(--t-eye);color:var(--ink-3);margin-top:2px}

/* ⛔ A CLASS IS NOT A DRUG — 👑, 2026-09-13: "`classes` must read visibly quieter than a drug: the guidance
   named a class and ⛔ we must not dress that up as a specific drug recommendation." A drug beside it is a
   LINK to its card; this is unlinked, quieter, and carries its own label so the difference is stated, not
   implied by styling alone. */
.ft-class{margin-top:var(--s-2);display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2)}
.ft-class-lb{font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.ft-class-v{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2)}
/* Non-pharm renders as written — 👑 resolved the atom ids to their text, because `np_avoid_nephrotoxins`
   on a student's screen is worse than nothing.
   ⛳ 2026-09-28 (🎨) — 👤: "fonts are different sizes, spacing changes, you follow no style guide." 🔬 ROOT
   CAUSE, found by screenshotting a real treatment card: `.ft-plan` (the authored PROSE sentence) was
   declared TWICE — here AND at its real, original home (app.js:1886, whose own comment explicitly warns
   "I nearly added a SECOND `.ft-plan` here… the later rule would have silently won the cascade" — exactly
   what then happened anyway). Deleted the duplicate; app.js:1886 is the one authoritative rule (§12).
   Separately: `.ft-np` was ALSO the class for the non-pharm MEASURES LIST in `leafPlanHTML` — a set of
   discrete short items, not a sentence — styled byte-identical to the prose plan right above it, which
   read on screen as the same paragraph printed twice. That list now renders through the disease card's
   OWN existing `.tag.tag-nonpharm` chip (treatmentRowsHTML, above) instead — the same component, not a
   third visual language for one concept. `.ft-np` stays for its one real prose use, a dose's authored label. */
.ft-np{margin-top:var(--s-2);font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2);line-height:1.45}

/* ⛔ FOUR DRUGS MUST NOT READ AS ONE WORD. 🔬 The leaf first rendered "aspirinticagrelorprasugrel…" — joined
   with nothing at all. Each is its own item, separated by a middot the CSS supplies so the markup stays a
   list rather than a pre-joined string. */
.ft-drug{display:flex;flex-wrap:wrap;align-items:baseline}
.ft-drug-i + .ft-drug-i::before{content:'·';margin:0 var(--s-2);color:var(--ink-3)}

/* The shortcuts a student can still ADD — the site's own chips, wrapping. A non-pinnable one is
   `.chip-empty` (dashed + dimmed, the site's disabled chip) and keeps its reason in a title. */
.acct-scadd{display:flex;flex-wrap:wrap;gap:var(--s-2);margin-top:var(--s-3)}

/* ⛳ THE LOADING SCREEN NAMES ITS APP — 👤: "the loading screen should say loading drug bank, the icon
   should be a pill." Icon and label both come from the rail's own NAV entry, so they can never disagree.
   ⛔ Quiet, not a splash: it sits where the content will, in the app's own ink, and vanishes the instant
   the page is ready — a fade would be the jerk 👤 asked me to remove. */
.boot-id{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%) translateY(calc(-1 * var(--optical-lift)));
  display:flex;flex-direction:column;align-items:center;gap:var(--s-3);color:var(--ink-3)}
.boot-ic svg{width:32px;height:32px;display:block}
.boot-lb{font-family:var(--sans);font-size:var(--t-meta);letter-spacing:.02em}

/* ⛳ THE ACCOUNT PAGE IS A CONTROL PANEL, NOT A REPORT — 👤: "i cant edit anything here, no way for me to
   add a course, a short-cut, like no plus button, im confused, so many words, its sloopy." Every rule below
   exists to replace a sentence with an affordance. */
/* The identity line: one line, everything in it, one Edit. ⛔ No sub-labels under each value — the value
   reads as itself — and ⛔ no "your school" badge: a default needs no label. */
.acct-idline{display:flex;flex-wrap:wrap;align-items:baseline;gap:var(--s-2) var(--s-3);margin-top:var(--s-2);
  font-family:var(--body);font-size:var(--t-meta);color:var(--ink-2)}
/* ⛳ The one thing only the student knows, and the only thing on this line we can't fill in for them. */
.acct-ask{color:var(--accent);text-decoration:underline;text-underline-offset:2px}
.acct-idform{display:flex;flex-direction:column;gap:var(--s-3);margin-top:var(--s-3);max-width:420px}
.acct-inlb{display:flex;flex-direction:column;gap:var(--s-1);font-family:var(--sans);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.acct-inrow{display:flex;gap:var(--s-2)}
.acct-in{height:var(--ctl);padding:0 var(--s-3);border:1px solid var(--line-2);border-radius:var(--r-1);
  background:var(--surface);color:var(--ink);font:inherit;font-size:var(--t-body);text-transform:none;letter-spacing:0}
.acct-in:focus{outline:none;border-color:var(--accent)}
/* The `+` on a section head — same place on every section, so it never needs a caption. */
.acct-plus{margin-left:auto;width:var(--ctl-sm);height:var(--ctl-sm);border:1px solid var(--line-2);
  border-radius:var(--r-1);background:var(--surface);color:var(--ink-2);font-size:var(--t-h3);line-height:1;cursor:pointer}
.acct-plus:hover{border-color:var(--accent);color:var(--accent)}
/* ⛳ A COURSE IS ONE LINE UNTIL ASKED — 🗄️: "the topics are for the FILTER, not for reading down a page." */
.acct-cx{flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--s-3);padding:0;border:0;
  background:none;cursor:pointer;text-align:left;font:inherit}
.acct-course-sub{font-family:var(--body);font-size:var(--t-meta);color:var(--ink-3);white-space:nowrap;
  overflow:hidden;text-overflow:ellipsis}
.acct-cx-ic{margin-left:auto;color:var(--ink-3);transition:transform var(--dur-1)}
.acct-course.on .acct-cx-ic{transform:rotate(90deg)}
.acct-course-body{margin-top:var(--s-3)}
/* ⛔ "PHA…" IS NOT A COURSE CODE. 🔬 At 390px the sub-line — code · term · N teachers · N exams — was
   ellipsed to three characters, so the row told a phone reader nothing at all. ⛳ It wraps to its own line
   instead: `order` puts the chevron back on line 1 beside the title, where a disclosure control belongs. */
@media(max-width:560px){
  .acct-cx{flex-wrap:wrap;align-items:baseline}
  .acct-cx-ic{order:1}
  .acct-course-sub{order:2;flex:1 1 100%;white-space:normal;overflow:visible;text-overflow:clip;margin-top:2px}
}
/* ⛳ THE SHORTCUT GRID — 🗄️'s own sketch: ◉ pinned · ○ available · ⊘ not open yet, SHOWN not hidden. */
.acct-scgrid{display:flex;flex-wrap:wrap;gap:var(--s-2);margin:0}
.acct-sc{display:inline-flex;align-items:center;height:var(--ctl-sm);border:1px solid var(--line-2);
  border-radius:var(--r-1);background:var(--surface);overflow:hidden}
.acct-sc.on{border-color:var(--accent);background:var(--accent-bg)}
.acct-sc.off{opacity:.5;border-style:dashed}
.acct-sc-t{display:inline-flex;align-items:center;gap:var(--s-2);height:100%;padding:0 var(--s-3);border:0;
  background:none;font:inherit;font-size:var(--t-meta);color:var(--ink-2);cursor:pointer}
.acct-sc.on .acct-sc-t{color:var(--accent-press)}
.acct-sc.off .acct-sc-t{cursor:default}
.acct-sc-m{font-size:var(--t-eye)}
.acct-sc-up{height:100%;padding:0 var(--s-2);border:0;border-left:1px solid var(--line-2);background:none;
  color:var(--ink-3);font-size:var(--t-eye);cursor:pointer}
.acct-sc-up:hover{color:var(--accent)}

/* ⛳ COURSES AND APPS ARE TWO KINDS — 👤: "why is this order so backwards and confusing?" They were one wrap
   of eleven chips, so six courses read like a second enrolment list sitting under the real one. A quiet
   label per group is all it takes for the two to stop looking like peers. */
.acct-scgrp{display:flex;align-items:flex-start;gap:var(--s-3);margin:var(--s-3) 0}
.acct-scgrp-lb{flex:0 0 56px;padding-top:var(--s-2);font-family:var(--sans);font-size:var(--t-eye);
  letter-spacing:var(--track-caps);text-transform:uppercase;color:var(--ink-3)}
.acct-scgrp .acct-scgrid{flex:1;margin:0}
@media(max-width:560px){
  /* On a phone the 56px gutter costs more than the alignment buys — the label sits above its own row. */
  .acct-scgrp{flex-direction:column;gap:var(--s-1)}
  .acct-scgrp-lb{flex:none;padding-top:0}
}

/* ── NUMERIC ENTRY — the student computes and TYPES the number (👤: "if you can make those numbers we have
   to input, it would make it 1000 times better"). ⛳ The unit is PRINTED beside the box, never typed: a
   string compare on "2.7 L" vs "2.7L" vs "2,700 mL" fails three of four correct answers.
   ⛳ Same control height, radius, border and focus color as `.opt` and `.acct-in` — a new question format
   is a new ROW IN THE EXISTING SYSTEM, ⛔ never a new look (charter §5b). */
.nument{margin:var(--s-4) 0 0}
.nument-row{display:flex;align-items:center;gap:var(--s-3)}
.nument-in{flex:0 1 220px;min-width:0;height:var(--ctl);padding:0 var(--s-3);
  border:1px solid var(--line-2);border-radius:var(--r-1);background:var(--surface);
  font-family:var(--mono);font-size:var(--t-body);color:var(--ink);text-align:right;
  font-variant-numeric:tabular-nums}
.nument-in:focus{outline:none;border-color:var(--accent)}
.nument-in:disabled{opacity:1}                      /* graded, not grayed — the answer stays readable */
.nument-in.ok{border-color:var(--good);color:var(--good)}
.nument-in.no{border-color:var(--bad);color:var(--bad)}
/* The unit is a LABEL, not an input: quieter than the number, and it never takes focus. */
.nument-u{font-family:var(--sans);font-size:var(--t-meta);color:var(--ink-3);white-space:nowrap}
.nument-say{margin-top:var(--s-2);font-family:var(--body);font-size:var(--t-meta);line-height:1.45;min-height:1px}
.nument-say.ok{color:var(--good)}
.nument-say.no{color:var(--bad)}
@media(max-width:560px){ .nument-in{flex:1 1 auto} }

/* ⛳ A COMMIT ON THIS PAGE IS A QUIET CONTROL, ⛔ NOT A FILLED ACCENT BLOCK. 👤, 2026-09-13:
   *"you have a bright orange button in the middle, its annoying."* He is right, and the reason is
   structural rather than taste: `.btn--primary` is the RUNNER's control — it ends a quiz, it is the one
   thing on that screen — so a full accent block mid-page here reads as the most important thing on the
   account page, which "Add" and "Save" are not. ⛳ Same height, radius and border as `.acct-in` and the
   chips, accent INK on the page's own surface: it reads as the commit without shouting over the sections.
   ⛔ It is still unmistakably a button — bordered, accent-colored, and it darkens on hover. */
.acct-go{height:var(--ctl);padding:0 var(--s-4);border:1px solid var(--accent);border-radius:var(--r-1);
  background:var(--surface);font-family:var(--sans);font-size:var(--t-meta);font-weight:600;
  color:var(--accent);cursor:pointer;white-space:nowrap}
.acct-go:hover{background:var(--accent-bg);color:var(--accent-press);border-color:var(--accent-press)}
.acct-go:disabled{opacity:.5;cursor:default}

/* ── THE IDENTITY HEADER — two facts, two rows, one column each ────────────────────────────────────
   👤, 2026-09-13: *"we just know their email address, and their school... thats it"* · *"look up account
   pages, why is ours so ugly."*
   ⚖️ The thing every settings page does that this did not: facts live in LABELED ROWS OF EQUAL HEIGHT —
   label left at a fixed width, value in the same column on every row, the row's action on the row. What
   was here was three stacked lines at three sizes ending in a sentence with "Edit" jammed on it, and an
   action inline in running text has no column, so nothing lined up with anything.
   ⛳ The rows share the hairline rhythm of `.acct-sec` above them, so the header reads as part of the same
   page rather than a different component bolted to the top. */
.acct-facts{margin-top:var(--s-4);border-top:1px solid var(--line)}
.acct-fact{display:flex;align-items:center;gap:var(--s-3);min-height:var(--ctl);
  padding:var(--s-2) 0;border-bottom:1px solid var(--line)}
.acct-fact-k{flex:0 0 92px;font-family:var(--sans);font-size:var(--t-eye);letter-spacing:var(--track-caps);
  text-transform:uppercase;color:var(--ink-3)}
.acct-fact-v{flex:1 1 auto;min-width:0;font-family:var(--body);font-size:var(--t-body);color:var(--ink);
  word-break:break-word}
.acct-fact-a{flex:0 0 auto;display:flex;align-items:center;gap:var(--s-3);margin-left:auto}
.acct-fact-in{width:100%;max-width:320px}
/* On a phone the 92px label gutter costs more than the alignment buys — the label sits above its value,
   and the action drops to its own line rather than squeezing the field. */
@media(max-width:560px){
  .acct-fact{flex-wrap:wrap;gap:var(--s-1) var(--s-3);padding:var(--s-3) 0}
  .acct-fact-k{flex:1 1 100%}
  .acct-fact-v{flex:1 1 100%}
  .acct-fact-a{margin-left:0;flex:1 1 100%;padding-top:var(--s-2)}
}

/* ── ⛳ THE ACCOUNT PAGE HAS THREE TYPE ROLES. ⛔ NOT EIGHT. ────────────────────────────────────────
   👤, 2026-09-13: *"why is everything different sizes? why is there no consideration for spacing? do you
   do anything with diligence?"*
   🔬 MEASURED on the live page before this — every leaf text node, computed, not guessed. EIGHT distinct
   styles on one short page: 11/400, 11/600, 13/400, 13/600, 14/400, 14/600, 15/400, 22/700. Each arrived
   defensibly on its own day; together they are the "different sizes" he is looking at. (Vertical rhythm
   measured CLEAN at the same time — 48px between every section — so the sizes were the whole defect.)
   ⛳ THE ROLES, and every element on this page is exactly one of them:
     ① EYEBROW  `--t-eye` 600 uppercase  — section labels, field labels. It names a thing.
     ② VALUE    `--t-body` 400 (600 when it is a row's own title) — the fact itself.
     ③ META     `--t-meta` 400          — secondary detail and actions, which sit BELOW a value in weight
                                          because an action is not the content.
   ⛔ A fourth role is a new pattern: raise it, don't inline it. */
.acct-fact-k{font-weight:600}                 /* ① was 400 — the only eyebrow on the page that was not 600 */
.acct-plus{font-size:var(--t-body)}           /* was a stray 15px, the only one on the page */
.acct-secm{font-size:var(--t-meta)}           /* ③ section state reads as meta, matching every other detail */
.acct-row-w{font-size:var(--t-meta)}          /* "today" / "yesterday" — same role, same size */
/* The shortcut group label is the SAME role as a section label and a field label — it names a group — so
   it takes the same eyebrow weight. It was the last 11px/400 piece of TEXT on the page; what remains at
   400 are the ◉ ○ ⊘ ↑ GLYPHS, which are marks rather than words. */
.acct-scgrp-lb{font-weight:600}

/* ── ⛳ THE SYLLABUS RENDERS AS ATOMS, USING THE COMPONENT THAT ALREADY SHIPS ──────────────────────
   👤, 2026-09-13: *"they are essentially atomic tags, kinda like drug atomic values, and just put
   teachers, courses, exams."*
   ⛔ I had just written `.acct-tag` — a THIRD tag style on this site, beside the drug card's `.tag`/
   `.tagrow` and the shortcut chips. That is the §12 defect I spend my days reporting in other people's
   stores, committed in mine within the hour. ⛳ Deleted: the syllabus atoms use `.tag`/`.tagrow`, so a
   governed value looks the same whether it is on a drug card or a course row.
   ⛳ The ROW is the header's row — eyebrow label naming the kind, atoms beside it — so the page has ONE
   row pattern rather than a different arrangement per section. */
.acct-fact-atoms{align-items:flex-start;border-bottom:0;padding:var(--s-1) 0;min-height:0}
.acct-fact-atoms .acct-fact-k{padding-top:3px}
.tag-q{border-style:dashed;color:var(--ink-3)}

/* ── ⛳ THE SHORTCUT GRID — one box size, one rhythm, one left axis ────────────────────────────────
   👤, 2026-09-13: *"the spacing between your various lines is off, your size of the boxes for the courses
   and apps is off, the spacing between clear all and use courses is off."* All three, measured:
   ① THE BOXES were `--ctl` (36px) — the height of a text INPUT — while the exam tags beside them are 22px
     and the page's other controls are 28px. A toggle is not a field; it is now `--ctl-sm`, so the page has
     ONE control height instead of a 36/28/22 ladder.
   ② THE ROWS carried their own `margin: 8px 0 16px` INSIDE a group that already set `margin: 12px 0`, so
     the gap between Courses and Apps was 12+16 on one side and 12+8 on the other — uneven by construction.
     The grid now owns no vertical margin at all; the GROUP owns the rhythm, one step, both sides.
   ③ CLEAR ALL / USE MY COURSES sat at the section's left edge while every chip starts 56px in, after the
     group-label gutter — so the two actions lined up with nothing on the page. They are now indented onto
     the same axis as the chips they act on, and separated by one step rather than `--s-4` guessed. */
.acct-scgrp{margin:var(--s-4) 0}
.acct-scgrp + .acct-scgrp{margin-top:var(--s-3)}
.acct-sc-t{padding:0 var(--s-3);gap:6px}
/* ⛳ `.acct-sc-m`'s SIZE IS SET ONCE, ABOVE, FROM THE TOKEN. A hardcoded `font-size:9px` lived here and,
   being later in the file, silently won. ⛔ 9px is not on the type scale at all — the smallest step is
   `--t-micro` (10px) — which is why A1 has been red on this single line; and two rules for one class is the
   same §12 duplicate that gave 👤's study-guide report two places to fix. ⛳ Deleted, ⛔ not "corrected to
   10px": the rule above already says `var(--t-eye)`, so this line only ever existed to push it off-scale. */
/* the section's own actions ride the chip axis, not the section edge */
/* ⛳ The shortcuts commit row used to be INDENTED to the chip axis. ⛔ Dropped: it now sits on the same
   right-hand axis as every other action on the page, and a left indent on a right-aligned row is a rule that
   silently does nothing — which is worse than no rule, because the next person keeps it in sync for free. */
.acct-sec .acct-scgrp ~ .acct-syl-acts{gap:var(--s-5)}
@media(max-width:560px){
}
