/* ============================================================================
   app.css - the site shell. Sidebar, app bar, disclosure cards, search.

   Loaded after msu-theme.css, which owns every colour and the type scale.
   Nothing here introduces a colour; if one is needed it goes in the token
   layer, which encodes the branding contrast rules.

   Layout is a three-column grid that degrades to one:
     >= 1200px   sidebar | article | on-this-page
     >= 900px    sidebar | article
     <  900px    article, with the sidebar as an off-canvas drawer

   HOW THE WIDTH IS SPENT - measured, 2026-08-21, because the page did not
   fill a desktop screen and the emptiness was all on one side.

   At 1920 the centre column was 1416px with a 565px article LEFT-ALIGNED in
   it: 803px of void, entirely on the right. 1443px at 2560. That reads as a
   broken layout rather than as margin, and the one-sidedness is the reason.

   Four separate levers, none of which is "make the line longer":

     1. THE RAILS GROW. At a fixed 17.5rem, five of the eleven page titles
        wrapped to two lines in the sidebar and every entry in this page's
        TOC wrapped. The rails were the part of the layout actually short of
        room, so they take width first - that is width spent on content.
     2. THE SHELL IS CAPPED AND CENTRED. Past --shell-max the extra width
        becomes a symmetric page margin instead of a one-sided hole, and the
        TOC stops drifting a metre away from the text it indexes.
     3. THE ARTICLE IS CENTRED IN ITS COLUMN. This is what fixes 1280 and
        1440, where the cap does not bind and the void was 163 / 323px.
     4. THE MEASURE WIDENS RESPONSIVELY. 68ch NAMED 68 characters and
        rendered 54-66, average 59, because `ch` is the width of a zero and
        this body face is narrower than that. The first desktop pass at 80ch
        rendered only 664px here: 46% of a 1440px viewport and 35% of 1920px,
        which still read as a phone column beside two rails. At the three-
        column breakpoint it now grows to 96ch (~797px / ~85 characters in
        this face), the upper end reserved for these short, scannable cards
        and lists. Below 1200px it remains 80ch, so phone/tablet geometry is
        unchanged. This one knob also widens .dims, .pass and .firetable;
        there is still no separate full-bleed lane - see below.
   ==========================================================================*/

:root {
  --appbar-h: 3.5rem;
  --sidebar-w: clamp(17.5rem, 17vw, 21rem);
  --toc-w: clamp(14rem, 13vw, 17rem);
  --prose: 80ch;
  --shell-max: 100rem;

  /* The inset that keeps a full-bleed bar aligned with the capped shell, so
     the brand sits over the sidebar's left edge instead of floating off in
     the page margin. It carries a PERCENTAGE, which is resolved where the
     token is USED, not here - so it is only correct on an element whose
     containing block is the viewport. That is true of .appbar (a sticky
     child of body) and .seqbar (position: fixed), and it is why this is not
     spelled `--gutter` and dropped into arbitrary components.

     --bar-pad is the floor, kept separate because presentation mode needs to
     drop back to exactly it. One value, three bars, no drift. */
  --bar-pad:   clamp(.75rem, 2vw, 1.25rem);
  --bar-inset: max(var(--bar-pad), (100% - var(--shell-max)) / 2);
}

/* A wider reading lane only where the TOC makes this a true desktop shell.
   At narrower breakpoints the existing 80ch cap and all drawer behaviour are
   byte-for-byte unchanged. */
@media (min-width: 75rem) { :root { --prose: 96ch; } }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* Anchored headings must clear the sticky app bar. */
[id] { scroll-margin-top: calc(var(--appbar-h) + 1rem); }

body.app {
  min-height: 100svh;
  /* The app surface is denser than a printed page. */
  font-size: 1rem;
  /* A long URL or part number must wrap rather than widen the document. */
  overflow-wrap: break-word;
}
main, .shell > * { min-width: 0; }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--accent); color: #fff; padding: .7rem 1rem;
  font-family: var(--f-display); font-weight: 600;
}
.skip:focus { left: 0; }

:where(a, button, summary, input, [tabindex]):focus-visible {
  outline: 2px solid var(--msu-liteblue);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ==========================================================================
   App bar
   ========================================================================*/
.appbar {
  position: sticky; top: 0; z-index: 40;
  height: var(--appbar-h);
  display: flex; align-items: center; gap: .75rem;
  padding: 0 var(--bar-inset);
  background: color-mix(in srgb, var(--paper) 90%, transparent);
  backdrop-filter: blur(10px);
  border-bottom: 1px solid var(--rule);
}
.appbar .brand {
  display: flex; align-items: center; gap: .6rem;
  font-family: var(--f-display); font-weight: 700;
  font-size: .95rem; letter-spacing: .04em;
  color: var(--accent); text-decoration: none; white-space: nowrap;
  flex: 0 1 auto; min-width: 0; overflow: hidden;
}
/* The official lockup, at bar height. Never recoloured - the reversed file is
   swapped in by display, same as everywhere else. */
.appbar .brandmark { height: 1.7rem; width: auto; display: block; }
.appbar .brand .sub {
  color: var(--ink); font-weight: 700; letter-spacing: .06em;
  padding-left: .6rem; border-left: 1px solid var(--rule);
}
@media (max-width: 34rem) {
  .appbar .brand .sub { display: none; }
  /* The logo is the one non-negotiable item in this compact bar: let search
     surrender width before clipping the official lockup. */
  .appbar .brand { flex: none; }
  .appbar .brandmark { height: 1.5rem; }
}

.iconbtn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 2.25rem; height: 2.25rem; flex: none;
  background: transparent; color: var(--ink);
  border: 1px solid transparent; border-radius: var(--radius-sm);
  cursor: pointer; font: inherit; line-height: 1;
}
.iconbtn:hover { background: var(--surface-2); border-color: var(--rule); }
.iconbtn svg { width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
.nav-toggle { display: none; }
@media (max-width: 56.25rem) { .nav-toggle { display: inline-flex; } }

/* ---- search ------------------------------------------------------------
   flex + min-width:0 is the whole fix for narrow screens. A fixed width here
   gave the app bar a minimum wider than a phone viewport, and because nothing
   clips, that widened the DOCUMENT - the article then scrolled sideways and
   clipped its own headings. Flex children need min-width:0 to be allowed to
   shrink below their content size. */
.search {
  position: relative; margin-left: auto;
  flex: 1 1 12rem; min-width: 0; max-width: 22rem;
}
.search input {
  width: 100%; height: 2.25rem;
  padding: 0 2rem 0 .7rem;
  font: inherit; font-size: .9rem; font-family: var(--f-display);
  color: var(--ink); background: var(--surface);
  border: 1px solid var(--rule); border-radius: var(--radius-sm);
}
.search input::placeholder { color: var(--ink-soft); }
.search .hint {
  position: absolute; right: .5rem; top: 50%; transform: translateY(-50%);
  font-family: var(--f-mono); font-size: .68rem; color: var(--ink-soft);
  pointer-events: none;
}
.search input:focus + .hint { display: none; }

.results {
  position: absolute; top: calc(100% + .4rem); right: 0;
  width: min(30rem, 92vw); max-height: 70svh; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: .35rem;
  box-shadow: 0 12px 32px rgb(0 0 0 / .16);
  display: none;
}
.results[data-open] { display: block; }
.results a {
  display: block; padding: .55rem .65rem; border-radius: var(--radius-sm);
  text-decoration: none; color: inherit;
}
.results a:hover, .results a[data-cur] { background: var(--surface-2); }
.results .rt { font-family: var(--f-display); font-weight: 700; font-size: .92rem; color: var(--accent); }
.results .rc { font-family: var(--f-display); font-size: .7rem; letter-spacing: .07em; text-transform: uppercase; color: var(--ink-soft); }
.results .rs { font-size: .85rem; color: var(--ink-soft); line-height: 1.4; }
.results mark { background: var(--flag-soft); color: inherit; border-bottom: 1.5px solid var(--flag); }
.results .empty { padding: .9rem .65rem; font-size: .9rem; color: var(--ink-soft); }

/* ==========================================================================
   Shell
   ========================================================================*/
.shell {
  display: grid;
  grid-template-columns: var(--sidebar-w) minmax(0, 1fr);
  align-items: start;
  /* Lever 2. Inert below 1600px, so every laptop width is untouched. */
  max-width: var(--shell-max);
  margin-inline: auto;
}
@media (min-width: 75rem) {
  .shell.has-toc { grid-template-columns: var(--sidebar-w) minmax(0, 1fr) var(--toc-w); }
}
@media (max-width: 56.25rem) { .shell { grid-template-columns: minmax(0, 1fr); } }

/* ---- catalog pages span the display ------------------------------------
   The shell cap exists so a prose page does not stretch its grid across a
   very wide display. A catalog page is the opposite case: index, practice
   and the track list are card grids whose whole job is to show as many
   cards at once as the screen allows, and .article.wide already removes
   the prose cap on them. Capping the shell around them left most of a wide
   monitor blank -- reported from the track index at 1600px on a display
   much wider than that.

   Overriding the VARIABLE rather than max-width matters: --bar-inset is
   derived from --shell-max, so the app bar and the seqbar re-align to the
   new width on their own. Setting `max-width: none` on .shell alone would
   have left the bar inset against flush content.

   100vw makes the (100% - --shell-max) / 2 term go negative, so --bar-inset
   falls back to its --bar-pad floor, which is exactly what a full-width
   page wants.                                                             */
html:has(.article.wide) { --shell-max: 100vw; }

/* ---- sidebar -----------------------------------------------------------*/
.sidebar {
  position: sticky; top: var(--appbar-h);
  height: calc(100svh - var(--appbar-h));
  overflow-y: auto; overscroll-behavior: contain;
  padding: 1.1rem .75rem 3rem;
  border-right: 1px solid var(--rule);
}
.sidebar h2 {
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft); border: 0; padding: 0 .55rem .5rem;
  font-weight: 700;
}

.navgroup { margin-bottom: .35rem; }
.navgroup > summary {
  display: flex; align-items: center; gap: .5rem;
  padding: .45rem .55rem; cursor: pointer; list-style: none;
  border-radius: var(--radius-sm);
  font-family: var(--f-display); font-weight: 700; font-size: .88rem;
  color: var(--ink);
}
.navgroup > summary::-webkit-details-marker { display: none; }
.navgroup > summary:hover { background: var(--surface-2); }
.navgroup > summary .chev {
  width: .7rem; height: .7rem; flex: none;
  border-right: 1.8px solid currentColor; border-bottom: 1.8px solid currentColor;
  transform: rotate(-45deg); transition: transform .16s ease;
  margin-left: -.1rem;
}
.navgroup[open] > summary .chev { transform: rotate(45deg); }
@media (prefers-reduced-motion: reduce) { .navgroup > summary .chev { transition: none; } }

.navgroup .navgroup > summary {
  font-weight: 600; font-size: .78rem; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-soft);
}
.navgroup .navgroup { margin-left: .6rem; }

.navlist { display: flex; flex-direction: column; gap: 1px; padding: 0 0 .3rem; margin: 0; list-style: none; }
.navlist li { padding: 0; }
.navlist li::before { content: none; }
.navlist a {
  display: block; padding: .38rem .55rem .38rem 1.5rem;
  font-size: .875rem; line-height: 1.35;
  color: var(--ink-soft); text-decoration: none;
  border-left: 2px solid transparent; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.navlist a:hover { color: var(--ink); background: var(--surface-2); }
.navlist a[aria-current="page"] {
  color: var(--accent); font-weight: 700;
  border-left-color: var(--accent); background: var(--surface-2);
}
.navlist a .pending {
  font-family: var(--f-mono); font-size: .62rem; color: var(--ink-soft);
  margin-left: .35rem;
}

/* off-canvas drawer */
@media (max-width: 56.25rem) {
  .sidebar {
    position: fixed; inset: var(--appbar-h) auto 0 0; z-index: 45;
    width: min(var(--sidebar-w), 84vw); height: auto;
    background: var(--paper);
    transform: translateX(-102%); transition: transform .2s ease;
    box-shadow: 4px 0 24px rgb(0 0 0 / .18);
  }
  .sidebar[data-open] { transform: none; }
  @media (prefers-reduced-motion: reduce) { .sidebar { transition: none; } }
  .backdrop {
    position: fixed; inset: var(--appbar-h) 0 0 0; z-index: 44;
    background: rgb(0 0 0 / .42); display: none;
  }
  .backdrop[data-open] { display: block; }
}
@media (min-width: 56.3125rem) { .backdrop { display: none; } }

/* ---- a tree header is a link AND a toggle ------------------------------
   The <summary> toggles; the <a> inside it navigates to the section's own
   page. Colour is inherited from the summary so the header still reads as a
   header rather than as a link in a list - the underline on hover and the
   focus ring are what say it is clickable. */
.navgroup > summary .navself { color: inherit; text-decoration: none; }
.navgroup > summary a.navself:hover {
  text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: .18em;
}
.navgroup > summary .count { margin-left: auto; }

/* ---- sidebar: tracks block ---------------------------------------------
   Deliberately NOT part of the topic tree above. A track is a playlist over
   the topics, so it reads as a separate kind of thing - but it is rendered
   with the SAME <details> markup, because it has the same shape: a programme
   holds courses the way a collection holds groups. */
.navsplit { height: 1px; background: var(--rule); margin: 1.25rem .55rem .9rem; }
.navnote { padding: 0 .55rem .5rem; font-size: .78rem; color: var(--ink-soft); line-height: 1.4; }
.navlist.tracknav a, .navlist.navinc a { display: flex; align-items: center; gap: .5rem; padding-left: 1.5rem; }
.navlist.tracknav > li > a { padding-left: .55rem; }
.navlist .count {
  margin-left: auto; font-family: var(--f-mono); font-size: .68rem;
  color: var(--ink-soft); background: var(--surface-2);
  border-radius: 999px; padding: .05rem .4rem;
}

/* The track you are currently inside, marked in the up-chain. Deliberately the
   SAME marking the sidebar and the playlist already use for the current page -
   accent, bold, an accent rule down the left - because it answers the same
   question in the same place and a second visual language for "you are here"
   is one the reader has to learn twice. aria-current="true", not "page": it is
   the current track, and a screen reader announcing "current page" on a link
   that is not this page would be a lie. */
.navlist a[aria-current="true"] {
  color: var(--accent); font-weight: 700;
  border-left-color: var(--accent); background: var(--surface-2);
}
.nav-tracks .navgroup > summary:has(a.navself[aria-current="true"]) {
  color: var(--accent); background: var(--surface-2);
  box-shadow: inset 2px 0 0 var(--accent);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}

/* A track this one CARRIES: it is not a child, it is a track of its own that
   this one includes by reference. Marked the way an out-of-deck link is
   marked, and for the same reason - following it leaves where you are. */
.navlist.navinc a::before {
  content: "\2197"; font-size: .78em; color: var(--ink-soft); flex: none;
  margin-left: -.75rem; width: .75rem;
}
.navgroup .navlist.navinc { margin-left: .6rem; }

/* ==========================================================================
   Playlist mode — a sequence applied to the real pages
   ========================================================================*/

/* ---- the sidebar, while inside a playlist -----------------------------*/
.pl-nav-head { padding: .3rem .55rem 1rem; display: flex; flex-direction: column; gap: .15rem; }
.pl-nav-head .k {
  font-family: var(--f-display); font-weight: 700; font-size: .66rem;
  letter-spacing: .12em; text-transform: uppercase; color: var(--ink-soft);
}
.pl-nav-head .t { font-family: var(--f-display); font-weight: 700; font-size: 1.02rem; color: var(--accent); line-height: 1.2; }
.pl-nav-head .n { font-family: var(--f-mono); font-size: .72rem; color: var(--ink-soft); }

.pl-nav { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 1px; }
.pl-nav li { padding: 0; }
.pl-nav li::before { content: none; }
.pl-nav a {
  display: flex; gap: .6rem; align-items: baseline;
  padding: .42rem .55rem; font-size: .875rem; line-height: 1.35;
  color: var(--ink-soft); text-decoration: none;
  border-left: 2px solid transparent; border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
.pl-nav a:hover { color: var(--ink); background: var(--surface-2); }
.pl-nav a[aria-current="page"] {
  color: var(--accent); font-weight: 700;
  border-left-color: var(--accent); background: var(--surface-2);
}
.pl-nav .num { flex: none; font-family: var(--f-mono); font-size: .72rem; opacity: .75; }
.pl-nav-group {
  padding: .9rem .55rem .3rem;
  font-family: var(--f-display); font-weight: 700; font-size: .66rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
.navlist.escape a { padding-left: .55rem; font-weight: 600; }

/* ---- arriving in another class's material ------------------------------
   A banner on ARRIVAL rather than a confirm before the click. A confirm only
   fires for a click made on this site - it does nothing for a link a student
   was sent - and it is dismissed reflexively because it stands between the
   reader and the thing they just asked for. A banner catches both routes in,
   says where you are, and carries the way back.

   Gold flag tokens, so the text inside is navy on a light ground and the
   theme swap is already handled: gold is never set as type on white here. */
.xcourse {
  display: flex; align-items: baseline; gap: .6rem 1rem; flex-wrap: wrap;
  margin: 0 0 1.6rem; padding: .8rem 1rem;
  background: var(--flag-soft); border: 1px solid var(--flag);
  border-radius: var(--radius-sm); color: var(--flag-ink);
  font-size: .95rem; line-height: 1.45;
}
.xcourse .xc-body { flex: 1 1 18rem; min-width: 0; }
.xcourse .xc-t { font-family: var(--f-display); font-weight: 700; }
.xcourse a { color: inherit; font-weight: 600; text-underline-offset: .18em; }
.xcourse .xc-x {
  flex: none; background: transparent; border: 1px solid transparent;
  color: inherit; cursor: pointer; font-size: 1.1rem; line-height: 1;
  /* Sized for a thumb, not for the glyph: this is the control a reader on a
     phone reaches for first. */
  padding: .55rem .7rem; min-width: 2.5rem; border-radius: var(--radius-xs);
}
.xcourse .xc-x:hover { border-color: var(--flag); }
/* "DET 330 is my class" - the deliberate way out, as against the x, which is
   only this browsing session. Outlined rather than filled: it must not read as
   the primary action, because for most readers who see this banner it is the
   wrong one. */
.xcourse .xc-sw {
  flex: none; cursor: pointer; font: inherit;
  font-family: var(--f-display); font-weight: 700; font-size: .7rem;
  letter-spacing: .05em; text-transform: uppercase;
  padding: .4rem .7rem; border-radius: var(--radius-xs);
  background: transparent; color: inherit; border: 1px solid var(--flag);
}
.xcourse .xc-sw:hover { background: var(--flag); color: var(--flag-soft); }
.presenting .xcourse { margin: 0; border-radius: 0; border-width: 0 0 1px; }
@media print { .xcourse { display: none !important; } }

/* A sidebar link into another class. Dashed, the same way an out-of-deck link
   and a cross-course chip are dashed, and layout-neutral: .navlist a already
   carries a 2px transparent left border for the current-page marker, so this
   only colours what is already reserved. */
.navlist a.is-xcourse { border-left-style: dashed; border-left-color: var(--ink-soft); }

/* ---- next / previous ---------------------------------------------------*/
.seqbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 42;
  display: flex; align-items: stretch; gap: .5rem;
  padding: .5rem var(--bar-inset);
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  backdrop-filter: blur(10px);
  border-top: 1px solid var(--rule);
}
/* the bar is fixed, so the page needs room to scroll clear of it */
body.has-seqbar main { padding-bottom: 7rem; }

.seq {
  flex: 1 1 0; min-width: 0;
  display: flex; flex-direction: column; justify-content: center; gap: .1rem;
  padding: .5rem .8rem; border-radius: var(--radius-sm);
  border: 1px solid var(--rule); background: var(--surface);
  text-decoration: none; color: inherit;
}
.seq:hover { border-color: var(--accent); }
.seq.next { text-align: right; align-items: flex-end; }
.seq.disabled { opacity: .45; background: transparent; border-style: dashed; }
.seq .d {
  font-family: var(--f-display); font-weight: 700; font-size: .66rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft);
}
.seq .t {
  font-family: var(--f-display); font-weight: 600; font-size: .9rem; color: var(--accent);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 100%;
}
.seq.prev .d::before { content: "\2190  "; }
.seq.next .d::after  { content: "  \2192"; }

.seqmid {
  flex: 0 0 auto; display: flex; flex-direction: column; justify-content: center;
  align-items: center; gap: .1rem; padding: 0 .5rem; text-align: center;
}
.seqmid .pos { font-family: var(--f-mono); font-size: .8rem; color: var(--ink); }
.seqmid .pl {
  font-family: var(--f-display); font-size: .66rem; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink-soft);
  max-width: 12rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
@media (max-width: 34rem) {
  .seqmid .pl { display: none; }
  .seq .t { font-size: .82rem; }
}

/* ==========================================================================
   Presentation mode — the SAME page, shown as panels.

   No separate document, no second rendering, no per-page presentation code.
   Every card is already in the DOM with its listeners attached, so checklists,
   embedded video and anything added later keep working here.
   ========================================================================*/
body.presenting {
  scroll-snap-type: y mandatory;
  scroll-behavior: smooth;
}
@media (prefers-reduced-motion: reduce) { body.presenting { scroll-behavior: auto; } }

.presenting .sidebar,
.presenting .toc,
.presenting .cardtools,
.presenting .usedin,
.presenting .crumb,
.presenting .backdrop { display: none !important; }

/* A panel is read from across a room, so it takes the whole projector: the
   shell cap that gives a desktop READER balanced margins would just throw
   320px of slide away at 1920. --bar-inset drops back to its floor with it,
   or the next/previous bar would sit inset under a full-bleed panel. */
.presenting .shell, .presenting .shell.has-toc { display: block; max-width: none; }
body.presenting { --bar-inset: var(--bar-pad); }
.presenting main { padding: 0; }
.presenting .article { max-width: none; }

/* the page title becomes the opening panel */
.presenting .article > h1 {
  min-height: calc(100svh - var(--appbar-h) - 5rem);
  display: flex; flex-direction: column; justify-content: center;
  scroll-snap-align: start; scroll-snap-stop: always;
  padding: 0 clamp(1.5rem, 6vw, 5rem);
  font-size: clamp(2.4rem, 7vw, 4.5rem);
  max-width: 20ch;
}
.presenting .article > .lede { display: none; }

.presenting .cards { gap: 0; }
.presenting .xcard {
  min-height: calc(100svh - var(--appbar-h) - 5rem);
  scroll-snap-align: start; scroll-snap-stop: always;
  display: flex; flex-direction: column; justify-content: center;
  border: 0; border-radius: 0; background: transparent;
  border-top: 1px solid var(--rule);
  padding: 2rem clamp(1.5rem, 6vw, 5rem);
}
.presenting .xcard > summary {
  pointer-events: none;           /* a panel does not collapse */
  padding: 0 0 1rem; border: 0;
  font-size: clamp(1.5rem, 3.6vw, 2.6rem);
  border-bottom: 3px solid var(--ink);
}
.presenting .xcard > summary .chev { display: none; }
.presenting .xcard[open] > summary { border-bottom: 3px solid var(--ink); }
.presenting .xcard .body {
  padding: 1.5rem 0 0;
  font-size: clamp(1.05rem, 1.5vh + .6rem, 1.5rem);
  overflow-y: auto;
}
.presenting .xcard .body h3 { font-size: clamp(1.05rem, 1.7vh, 1.3rem); }
.presenting .xcard .body ul { gap: .7em; }

.present-exit {
  /* Fixed, so --bar-inset resolves against the viewport - the same value the
     seqbar it sits above is using, which is the point. */
  position: fixed; right: var(--bar-inset);
  bottom: calc(4.6rem + .5rem); z-index: 43;
  font-family: var(--f-display); font-size: .68rem;
  letter-spacing: .08em; text-transform: uppercase;
  padding: .4rem .7rem; border-radius: var(--radius-xs);
  background: var(--surface); color: var(--accent);
  border: 1px solid var(--rule); text-decoration: none;
}
.present-exit:hover { border-color: var(--accent); }

@media print {
  .seqbar, .present-exit { display: none !important; }
  body.presenting { scroll-snap-type: none; }
  .presenting .xcard { min-height: auto; break-inside: avoid; padding: 0 0 .4in; }
  .presenting .article > h1 { min-height: auto; display: block; }
}

/* ---- playlists ---------------------------------------------------------*/
.playlist {
  margin: 2rem 0; padding: 1.25rem 1.3rem;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius);
}
.pl-head { display: flex; gap: 1rem; align-items: flex-start; flex-wrap: wrap; }
.pl-meta { flex: 1 1 16rem; min-width: 0; }
.pl-meta h2 { border: 0; padding: 0; }
.pl-stat {
  margin-top: .4rem; font-family: var(--f-mono); font-size: .74rem; color: var(--ink-soft);
}
/* The sideways edges: the topic tracks this one carries, and the courses
   beside it. Both were unreachable from here. */
.pl-rel {
  margin-top: .7rem; display: flex; align-items: center; gap: .4rem; flex-wrap: wrap;
}
.pl-rel .k {
  font-family: var(--f-display); font-weight: 700; font-size: .66rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink-soft);
}
.pl-rel .k:not(:first-child) { margin-left: .6rem; }
.btn-play {
  flex: none; display: inline-flex; align-items: center; gap: .45rem;
  padding: .6rem 1rem; border-radius: var(--radius-sm);
  background: var(--accent); color: var(--paper);
  font-family: var(--f-display); font-weight: 700; font-size: .82rem;
  letter-spacing: .05em; text-transform: uppercase; text-decoration: none;
}
.btn-play::before { content: "\25B6"; font-size: .8em; }
.btn-play:hover { filter: brightness(1.12); }

.tracks {
  list-style: none; margin: 1.15rem 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 0;
  counter-reset: none;
}
.tracks li::before { content: none; }
.track {
  display: flex; align-items: baseline; gap: .8rem;
  padding: .55rem .3rem; border-top: 1px solid var(--rule);
}
.track .tn {
  flex: none; width: 1.4rem; text-align: right;
  font-family: var(--f-mono); font-size: .78rem; color: var(--ink-soft);
}
.track .tt { flex: 1 1 auto; min-width: 0; font-weight: 600; text-decoration: none; border-bottom: 1px solid transparent; }
.track .tt:hover { border-bottom-color: var(--accent); }
.track .tc { flex: none; font-family: var(--f-display); font-size: .7rem; letter-spacing: .06em; text-transform: uppercase; color: var(--ink-soft); }
.track .ts { flex: none; font-family: var(--f-mono); font-size: .72rem; color: var(--ink-soft); width: 4.5rem; text-align: right; }
@media (max-width: 34rem) {
  .track { flex-wrap: wrap; gap: .35rem .6rem; }
  .track .tt { flex: 1 1 100%; order: 1; padding-left: 2.2rem; }
  .track .tn { order: 0; }
  .track .tc { order: 0; }
  .track .ts { order: 2; width: auto; margin-left: auto; }
}
.track-group {
  padding: 1rem .3rem .3rem; margin-top: .3rem;
  font-family: var(--f-display); font-weight: 700; font-size: .7rem;
  letter-spacing: .1em; text-transform: uppercase; color: var(--accent);
}

/* ---- article -----------------------------------------------------------*/
main { min-width: 0; padding: clamp(1.5rem, 3.5vw, 3rem) clamp(1.1rem, 4vw, 3rem) 6rem; }

/* Lever 3. `margin-inline: auto` is the whole fix for the one-sided void, and
   it is the half that works at EVERY width - the shell cap only bites past
   1600px, while the article was left-aligned in a column wider than itself
   from about 1150px up.

   No bleed lane, and that is a decision rather than an omission. The obvious
   alternative was to widen .article past the measure and let .dims / .pass /
   .firetable break out of a centred text column. It was rejected because the
   width has to come from somewhere: either every card grows a one-sided inner
   gutter - the same complaint, moved inside the border - or only the cards
   holding a wide widget grow, and a column of cards with ragged edges reads
   as a bug. There is no <table> on this site; the three widgets that wanted
   room get it from --prose instead, and they all get it equally.

   max-width: none in presentation mode and in print both still win: this rule
   is (0,1,0) and earlier in the source than the print block, and .presenting
   .article is (0,2,0). Auto margins against no max-width are a no-op, so
   neither mode is touched. */
.article { max-width: var(--prose); margin-inline: auto; }
.article.wide { max-width: none; }

/* .wide is for card and tile grids -- the track index, the collection hubs --
   not for prose. A grid reflows to whatever width it is given, so the 72rem
   cap it used to carry was pure waste: on a wide monitor the shell centred at
   100rem and the grid then stopped 450px short inside it, blank on both sides.
   Reported from a 4K screen as "the window fixes to a certain size".
   The shell cap is what bounds these now, which is the rule that should own it. */

.crumb {
  display: flex; align-items: center; gap: .45rem; flex-wrap: wrap;
  font-family: var(--f-display); font-size: .72rem;
  letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft);
  margin-bottom: .8rem;
}
.crumb a { color: var(--ink-soft); text-decoration: none; }
.crumb a:hover { color: var(--accent); }
.crumb .sep { opacity: .5; }

.article > h1 { margin-bottom: .6rem; }
.article .lede { font-size: 1.08rem; margin-bottom: 1.6rem; }

.pill {
  display: inline-block; font-family: var(--f-display); font-weight: 700;
  font-size: .66rem; letter-spacing: .08em; text-transform: uppercase;
  padding: .18rem .45rem; border-radius: var(--radius-xs);
  border: 1px solid var(--flag); background: var(--flag-soft); color: var(--flag-ink);
  vertical-align: .18em;
}

/* ---- disclosure cards --------------------------------------------------
   Each authored <section> becomes one of these. <details> is used on purpose:
   it discloses without JavaScript, is keyboard and screen-reader native, and
   can be forced open for printing. JS only adds bulk controls and memory.   */
.cards { display: flex; flex-direction: column; gap: .85rem; }

.xcard {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  overflow: hidden;
}
.xcard > summary {
  display: flex; align-items: center; gap: .7rem;
  padding: .9rem 1.05rem; cursor: pointer; list-style: none;
  font-family: var(--f-display); font-weight: 600; font-size: 1.08rem;
  color: var(--ink);
}
.xcard > summary::-webkit-details-marker { display: none; }
.xcard > summary:hover { background: var(--surface-2); }
.xcard > summary .chev {
  width: .62rem; height: .62rem; flex: none; margin-left: auto;
  border-right: 2px solid var(--ink-soft); border-bottom: 2px solid var(--ink-soft);
  transform: rotate(45deg); transition: transform .16s ease;
}
.xcard[open] > summary .chev { transform: rotate(-135deg); }
@media (prefers-reduced-motion: reduce) { .xcard > summary .chev { transition: none; } }
.xcard[open] > summary { border-bottom: 1px solid var(--rule); }

.xcard .body {
  padding: 1.15rem 1.05rem 1.25rem;
  display: flex; flex-direction: column; gap: 1rem;
}
/* The authored h2 is the summary text, so the copy inside starts at h3. */
.xcard .body > h2 { display: none; }

/* ---- the `deeper` tier -------------------------------------------------
   A second disclosure INSIDE a card: the rule stays on the main path, the
   reason to believe it goes behind a click. It has to read as obviously
   openable, which plain <details> does not -- the instructor's report was
   that it looked like a stray line of text.

   Deliberately unlike .xcard > summary, because it is not a card header:
   accent-coloured rather than ink, smaller than body type, and its marker
   sits on the LEFT where a disclosure triangle is expected, while a card's
   chevron sits far right. Two different affordances, told apart at a glance.

   .xcard's summary rules all use the child combinator, so none of them
   reach in here. Keep it that way -- a single descendant selector would
   make every one of these render as a card header.                        */
.deeper {
  border: 1px solid var(--rule);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  margin: .35rem 0;
}
.deeper > summary {
  display: flex; align-items: center; gap: .55rem;
  padding: .6rem .85rem;
  cursor: pointer; list-style: none;
  font-family: var(--f-display); font-weight: 600; font-size: .93rem;
  color: var(--accent);
}
.deeper > summary::-webkit-details-marker { display: none; }

/* A solid triangle, not the card's outline chevron. currentColor ties it to
   the accent so the two never drift apart. */
.deeper > summary::before {
  content: ""; flex: none; width: 0; height: 0;
  border-left: .4rem solid currentColor;
  border-top: .3rem solid transparent;
  border-bottom: .3rem solid transparent;
  transition: transform .16s ease;
}
.deeper[open] > summary::before { transform: rotate(90deg); }
@media (prefers-reduced-motion: reduce) {
  .deeper > summary::before { transition: none; }
}
.deeper > summary:hover { text-decoration: underline; text-underline-offset: .18em; }
.deeper > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.deeper[open] > summary { border-bottom: 1px solid var(--rule); }

.deeper > :not(summary) { margin: .8rem .85rem; }

/* Site-page printing still force-opens every <details>. Aggregate printing
   extracts this tier into numbered addenda before this stylesheet is inlined. */
@media print {
  .deeper { break-inside: avoid; background: none; }
}

/* ---- card markers --------------------------------------------------------
   A coloured left edge says what KIND of content a collapsed card holds, with
   one meaning per colour site-wide. These colours are a semantic set of their
   own, deliberately NOT tied to the MSU palette in msu-theme.css (operator,
   2026-10-28): change the brand and the markers stay put, and the reverse.
   The build sets the flag from the card's content (build-site.ps1, $flag),
   precedence stop > watch > interactive > checklist. Interactive green passes 3:1 on both
   surfaces (4.0:1 light, 3.9:1 dark).                                      */
:root {
  --mark-stop:        #FF4500;   /* a hard stop: do not proceed           */
  --mark-watch:       #B8860B;   /* a key rule or trap to check            */
  --mark-checklist:   #9466D1;
  --mark-interactive: #3A8C3F;   /* something to work with: a live module  */
}
.xcard[data-card-edge] { border-left: 3px solid var(--card-marker); }
.xcard > summary {
  position: relative; flex-wrap: wrap; padding-right: 2.7rem;
  overflow-wrap: anywhere;
}
.xcard > summary .chev { position: absolute; right: 1.05rem; top: 1.2rem; }
.card-tags { display: inline-flex; flex-wrap: wrap; gap: .3rem .65rem; max-width: 100%; }
.card-tag { display: inline-flex; align-items: center; gap: .3rem; color: var(--ink); font: 600 .75rem/1.5 var(--f-body); }
.card-tag[style]::before, .marker-sample {
  content: ''; display: inline-block; width: 3px; height: 1em;
  flex: none; background: var(--card-marker);
}
.marker-separator { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
.card-legend { margin: 1.5rem 0; padding-block: .75rem; border-block: 1px solid var(--rule); color: var(--ink); }
.card-legend h2 { margin: 0 0 .5rem; font-size: 1rem; border: 0; padding: 0; }
.card-legend ul { list-style: none; padding: 0; margin: 0; font-size: .85rem; }
.card-legend li::before { content: none; }
.marker-sample { margin-right: .5rem; vertical-align: middle; }
/* a label-only category keeps the sample's width so its row lines up, but shows no colour */
.marker-sample.is-label-only { background: none; }
.presenting .card-tags { display: none; }
@media print {
  main figure, [data-interactive] { break-inside: avoid; }
  .card-tag { font-size: 8pt; }
}

/* ---- phase spine -------------------------------------------------------
   BEFORE / DURING / AFTER, set sideways down the left edge of the three
   cards on the equipment operating procedure.

   Built for the CLOSED state first. Cards ship collapsed, so what a reader
   arrives at is three shut headings with no sequence in them at all; three
   rails make the phase legible before anything is opened. Open, the rail
   becomes the header band of the same mark.

   THE HOOK IS AUTHORED, NOT DERIVED - and that is the whole point.
   The generator stamps data-card="<slug>" on every card, so these three are
   addressable from here with no generator change. It was not used: the slug
   comes from the <h2>, and a reworded heading would detach the styling in
   silence. These three headings were reworded the day this was written.

   Instead the page authors an empty <span class="phase" data-phase="Before">
   as the first thing inside its <h2>. build-site.ps1 copies the h2's inner
   HTML verbatim into the <summary>, so the span arrives in the summary of
   the site page AND of the docs\print\ aggregate. Everything else derived
   from that heading - the card slug and #anchor, the on-this-page label, the
   search-index title - runs through ConvertTo-Plain, which replaces a tag
   with a space and collapses runs of whitespace, so an EMPTY span changes
   none of them. Verified: the three slugs are unchanged.

   Reword a heading and the rail follows it. DELETE the span and the rail is
   gone - which is a visible loss on the page you are editing, not a silent
   one three files away.

   The word lives in the markup rather than in this file for the same reason
   it is not a per-slug `content` string: it is content. `aria-hidden` on the
   span, because the summary already says "Before operating any equipment"
   and Chrome folds ::before content into the accessible name.

   COLOUR CARRIES ONE CLAIM: these three cards are one procedure, in order.
   Deliberately NOT a hue per phase. This collection already teaches a
   three-hue ramp - lite blue proceed, gold caution, red stop - on the
   equipment category page, and a second unrelated three-hue scale beside it
   would be read as that one. Nor is there an honest three-level scale here
   to encode: before/during/after is a sequence, not an escalation, and
   colouring AFTER as "low" would quietly undercut the cleanup rules that are
   the point of the third card.

   --accent on --paper is the pair .pass-steps already uses for its selected
   badge, and for the same reason: it is the one that survives both skins.
   Measured on the built page - navy #002144 on off-white #F2F1EE is 14.29:1
   light; near-black #16181A on Lite Blue #00A4E3 is 6.29:1 dark. Print
   drops the fill entirely, see below.
   ========================================================================*/
.phase {
  flex: none; align-self: stretch;
  display: grid; place-items: center;
  writing-mode: vertical-rl; transform: rotate(180deg);
  width: var(--phase-w, 1.95rem);
  /* One height for all three rails. The words differ in set width, so
     without this the closed stack is 89 / 87 / 77px - three sizes where the
     reader is being asked to see one ladder. A longer word still grows it. */
  min-height: var(--phase-min, 5.5rem);
  padding: .7rem 0; margin-right: .35rem;
  background: var(--accent); color: var(--paper);
  font-family: var(--f-display); font-weight: 700;
  font-size: var(--phase-fs, .78rem);
  letter-spacing: .16em; text-transform: uppercase; line-height: 1;
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
.phase::before { content: attr(data-phase); }

/* The rail bleeds to the card edge; the summary's own left and block padding
   is the only thing that would inset it. The .35rem margin above puts the
   heading back at the 1.05rem it sat at before, via the summary's own gap.

   :has() rather than a per-slug selector. If it ever fails to match, the
   rail simply sits inset by the summary's padding - still a legible label,
   not a broken layout. */
.xcard > summary:has(> .phase) { padding-block: 0; padding-left: 0; }

/* Open, the rail covers only the header, so the card edge carries the mark
   on down the body. Invisible when closed: same colour, and the rail sits on
   top of it. :not() keeps the danger edge - a card that is both a phase and
   a hard stop shows 3px of red beside the rail rather than losing it, which
   is two facts instead of one overwriting the other. */
.xcard:not([data-card-edge]):has(> summary > .phase) {
  border-left: 3px solid var(--accent);
}

/* Presentation - a panel is read from across a room, so the rail scales with
   the 2.6rem heading and drops its minimum. The card edge goes, exactly as
   it does in print: the panel is full-bleed and a 3px line hard against the
   projector edge reads as a rendering artefact. Both overrides outrank the
   base on specificity rather than on source order. */
.presenting .xcard > summary:has(> .phase) { padding: 0 0 1rem; }
.presenting .xcard.xcard:not([data-card-edge]):has(> summary > .phase) { border-left: 0; }
.presenting .phase { --phase-w: 2.6rem; --phase-fs: 1.05rem; --phase-min: 0; margin-right: .8rem; }

@media print {
  .xcard > summary:has(> .phase) { padding: 0; }
  /* On paper the heading underline does the separating - the print sheet
     drops every card border, and the rail goes with them. */
  .xcard.xcard:not([data-card-edge]):has(> summary > .phase) { border-left: 0; }
  /* NO FILL ON PAPER, and the doubled class rather than source order,
     because this is a legibility invariant and not a preference: a printer
     or a user setting that drops backgrounds would leave reversed type on a
     vanished ground, i.e. nothing at all. Navy type on white is 16.14:1 and
     cannot fail that way. Same reasoning as .pass-steps .k. */
  .phase.phase {
    --phase-w: 1.5rem; --phase-fs: .66rem; --phase-min: 4.3rem;
    padding: .35rem 0; margin-right: .6rem;
    background: transparent; color: var(--accent);
    border-right: 1px solid var(--rule);
  }
}

/* ---- authored images ---------------------------------------------------
   [[img:file.png|Alt text]]. On a site page this is a real file; in a
   docs\print\ aggregate the identical markup carries a data: URI, because
   that file is handed out on its own. Nothing here may assume which.        */
.content-img {
  display: block; max-width: 100%; height: auto;
  border-radius: var(--radius-sm);
}
@media print { .content-img { break-inside: avoid; max-width: 100%; } }

/* Cap authored artwork in both dimensions, retaining its aspect ratio.
   Direct figure SVGs cover inline artwork without reaching module SVGs. */
@media screen {
  body:not(.presenting) main :is(img.content-img, figure > svg):not([data-interactive] *, .ghs-pictogram *, .diagram-viewer *) {
    max-height: 85vh; width: auto; height: auto; max-width: 100%;
  }
}
@media print {
  body:not(.presenting) main :is(img.content-img, figure > svg):not([data-interactive] *, .ghs-pictogram *, .diagram-viewer *) {
    max-height: 8.5in; width: auto; height: auto; max-width: 100%;
    break-inside: avoid;
  }
  body:not(.presenting) main :is(h2, h3):not([data-interactive] *) { break-after: avoid; }
}

/* The viewer restores natural size for artwork reduced by either cap. */
.diagram-hint, .diagram-viewer { display: none; }
@media screen {
  .diagram-expandable { cursor: zoom-in; }
  .diagram-hint:not([hidden]) { display: block; font-size: .8rem; color: var(--ink-soft); }
  html.diagram-viewer-open, .diagram-viewer-open body { overflow: hidden; }
  .diagram-viewer[open] {
    display: flex; flex-direction: column; position: fixed; inset: 0;
    width: 100%; height: 100dvh; max-width: none; max-height: none;
    box-sizing: border-box; margin: 0; padding: 0; border: 0;
    background: var(--surface); color: var(--ink);
  }
  .diagram-viewer :focus-visible {
    outline: 2px solid var(--ink);
    outline-offset: -3px;
  }
  .diagram-toolbar {
    display: flex; gap: .5rem; flex: none; padding: .75rem;
    border-bottom: 1px solid var(--rule);
  }
  .diagram-toolbar button {
    min-height: 44px; padding: .5rem 1rem;
    background: var(--surface); color: var(--ink); border: 1px solid var(--rule);
    border-radius: var(--radius-sm); cursor: pointer;
  }
  .diagram-toolbar button[aria-pressed="true"] { border-color: var(--accent); background: var(--surface-2); }
  /* Reserve a surface-colored border so focus never sits on the artwork. */
  .diagram-scroll { border: 3px solid var(--surface); box-sizing: border-box; flex: 1; min-height: 0; overflow: auto; overscroll-behavior: contain; }
  .diagram-scroll img { display: block; max-width: none; height: auto; margin: auto; }
}

/* ---- figures and captions ----------------------------------------------
   The UA default for <figure> is `margin: 1em 40px`. Measured at a 375px
   viewport that insets the figure to 222px against 302px of prose -- a
   graphic rendering 26% narrower than its own caption. Zero the horizontal
   inset and own the vertical rhythm instead.

   .graphic-slot is a figure whose artwork has not been drawn yet. It is
   deliberately visible rather than hidden: an empty slot that renders as a
   bare floating caption reads as a bug, and a slot nobody can see is a slot
   nobody fills. Filling one replaces the comment with [[img:...|alt]] and
   the placeholder rule stops matching on its own.                          */
figure { margin: 1.5rem 0; }

figure > figcaption {
  margin-top: .5rem;
  color: var(--ink-soft);
  font-size: .9rem;
  line-height: 1.45;
}

/* An image with no width set renders at its intrinsic size, capped by the
   column -- so a near-square illustration at the full prose measure comes out
   taller than the paragraph it supports, and inside a card that ships
   collapsed the picture becomes the card. Cap the FIGURE rather than the
   image, so the caption wraps to the picture it belongs to instead of running
   the full column away from it. Below the cap this is inert, which is what
   keeps it safe at 375px. */
figure.fig-narrow { max-width: 27rem; }

figure.graphic-slot:not(:has(img, svg))::before {
  content: "Artwork pending";
  display: block;
  padding: 1.75rem .75rem;
  text-align: center;
  font-family: var(--f-mono); font-size: .78rem;
  letter-spacing: .06em; text-transform: uppercase;
  color: var(--ink-soft);
  border: 2px dashed var(--rule);
  border-radius: var(--radius-sm);
}

/* ---- electrical conductor sequence -------------------------------------
   The gap is the lesson. The three panels stay complete without colour or
   motion: panel labels, exposed blades, the falling chip path, and the flash
   carry the sequence for print, reduced-motion, and assistive descriptions. */
.electrical-plug-sequence { margin: 1.75rem 0; }
.plug-sequence {
  list-style: none; margin: 0; padding: 0;
  display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: .8rem;
}
.plug-sequence > li { min-width: 0; padding: 0; }
.plug-sequence > li::before { content: none; }
.plug-panel-art { display: block; width: 100%; height: auto; }
.plug-sequence .plug-panel-box { fill: var(--surface); stroke: var(--rule); stroke-width: 2; }
.plug-sequence .plug-panel-number { fill: var(--accent); stroke: var(--accent); }
.plug-sequence .plug-panel-number-danger { fill: var(--cat3); stroke: var(--cat3); }
.plug-sequence .plug-panel-number-text {
  fill: var(--paper); font-family: var(--f-display); font-size: 22px; font-weight: 700;
  text-anchor: middle;
}
.plug-sequence .plug-panel-title,
.plug-sequence .plug-note {
  fill: var(--ink); font-family: var(--f-display); font-weight: 700; letter-spacing: .8px;
}
.plug-sequence .plug-panel-title { font-size: 26px; }
.plug-sequence .plug-note { font-size: 13px; }
.plug-sequence .plug-wall { fill: var(--surface-2); stroke: var(--ink-soft); stroke-width: 2; }
.plug-sequence .plug-receptacle { fill: var(--paper); stroke: var(--ink); stroke-width: 2; }
.plug-sequence .plug-socket { fill: var(--ink); }
.plug-sequence .plug-body { fill: var(--accent); stroke: var(--accent); }
.plug-sequence .plug-grip { fill: none; stroke: var(--paper); stroke-width: 3; opacity: .75; }
.plug-sequence .plug-cord { fill: none; stroke: var(--ink-soft); stroke-width: 13; stroke-linecap: round; }
.plug-sequence .plug-cord-line { fill: none; stroke: var(--ink); stroke-width: 2; stroke-linecap: round; opacity: .4; }
.plug-sequence .plug-blade { fill: var(--surface-2); stroke: var(--ink); stroke-width: 2; }
.plug-sequence .plug-seated-mark { fill: none; stroke: var(--accent); stroke-width: 6; stroke-linecap: round; stroke-linejoin: round; }
.plug-sequence .plug-gap-mark { fill: none; stroke: var(--ink); stroke-width: 2; stroke-dasharray: 4 3; }
.plug-sequence .plug-fall { fill: none; stroke: var(--ink-soft); stroke-width: 2; stroke-dasharray: 5 5; }
.plug-sequence .plug-chip { fill: var(--ink-soft); stroke: var(--ink); stroke-width: 2; }
.plug-sequence .plug-flash { fill: var(--cat3-soft); stroke: var(--cat3); stroke-width: 3; stroke-linejoin: round; }

@media (max-width: 36rem) {
  .plug-sequence { grid-template-columns: minmax(0, 1fr); gap: .7rem; }
}

@media (prefers-reduced-motion: no-preference) {
  .plug-sequence .plug-chip {
    transform-box: fill-box; transform-origin: center;
    animation: plug-chip-drop 4s cubic-bezier(.32, .72, .32, 1) infinite;
  }
  .plug-sequence .plug-flash { animation: plug-contact-flash 4s ease-out infinite; }
}
@keyframes plug-chip-drop {
  0%, 42% { opacity: 0; transform: translateY(-42px); }
  50% { opacity: 1; transform: translateY(-42px); }
  66%, 100% { opacity: 1; transform: translateY(0); }
}
@keyframes plug-contact-flash {
  0%, 62% { opacity: .35; }
  68%, 76% { opacity: 1; }
  84%, 100% { opacity: .7; }
}
@media print {
  .plug-sequence { break-inside: avoid; gap: .45rem; }
  .plug-sequence .plug-panel-box { fill: #fff; stroke: #000; }
  .plug-sequence .plug-panel-number,
  .plug-sequence .plug-panel-number-danger,
  .plug-sequence .plug-body { fill: #fff; stroke: #000; }
  .plug-sequence .plug-panel-number-text,
  .plug-sequence .plug-panel-title,
  .plug-sequence .plug-note { fill: #000; }
  .plug-sequence .plug-grip { stroke: #000; }
  .plug-sequence .plug-seated-mark,
  .plug-sequence .plug-flash { stroke: #000; }
  .plug-sequence .plug-flash { fill: #fff; }
}

/* ---- GHS pictograms ----------------------------------------------------
   These are the standard regulatory artwork, not an illustration in the site
   palette. Their white field, black symbol, and red frame stay fixed in every
   screen theme. */
.ghs-pictograms { margin: 1.25rem 0; }
.ghs-pictograms > figcaption {
  margin: 0 0 .65rem;
  font-family: var(--f-display); font-weight: 700; font-size: .76rem;
  letter-spacing: .07em; text-transform: uppercase; color: var(--ink-soft);
}
.ghs-pictogram-list {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(5.25rem, 1fr));
  gap: .8rem .75rem; margin: 0; padding: 0; list-style: none;
}
.ghs-pictogram-list li {
  display: grid; justify-items: center; align-content: start; gap: .35rem;
  min-width: 0; padding: 0; text-align: center;
}
.ghs-pictogram-list li::before { content: none; }
.ghs-pictogram { display: block; width: min(100%, 5.25rem); }
.ghs-pictogram .content-img {
  display: block; width: 100%; height: auto; border-radius: 0;
}
.ghs-pictogram-list li > span[aria-hidden="true"] {
  min-height: 2.4em;
  font-family: var(--f-display); font-weight: 700; font-size: .68rem;
  line-height: 1.2; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ink);
}
@media print {
  /* Turn the regulatory red frame into a firm black outline for handouts;
     the white field and black symbol remain unchanged. */
  .ghs-pictogram .content-img {
    print-color-adjust: exact; -webkit-print-color-adjust: exact;
    filter: grayscale(1) contrast(3);
  }
}

/* Both of these are build-error states that reach the page. The build already
   fails on them; these make the failure visible in the artifact too, because
   a generated surface that looks fine is how the last three of these got out.
   Neither should ever be seen on the live site. */
.img-missing {
  display: inline-block; padding: .2rem .5rem;
  font-family: var(--f-mono); font-size: .82rem;
  color: var(--cat-ink); background: var(--cat3-soft);
  border: 1.5px dashed var(--cat3); border-radius: var(--radius-xs);
}
.img-missing::before { content: "missing image: "; }
img[data-alt-missing] { outline: 3px dashed var(--cat3); outline-offset: 2px; }

.cardtools {
  display: flex; gap: .5rem; align-items: center;
  margin-bottom: .9rem;
}
.cardtools button {
  font-family: var(--f-display); font-size: .72rem;
  letter-spacing: .07em; text-transform: uppercase;
  padding: .35rem .6rem; cursor: pointer;
  background: transparent; color: var(--ink-soft);
  border: 1px solid var(--rule); border-radius: var(--radius-xs);
}
.cardtools button:hover { color: var(--accent); border-color: var(--accent); }

/* ---- interactive checklist --------------------------------------------*/
.boxes.live li { padding-left: 0; }
.boxes.live li::before { content: none; }
.boxes.live label {
  display: flex; gap: .65rem; align-items: flex-start;
  padding: .4rem .5rem; border-radius: var(--radius-sm); cursor: pointer;
}
.boxes.live label:hover { background: var(--surface-2); }
.boxes.live input {
  appearance: none; -webkit-appearance: none;
  width: 1.05rem; height: 1.05rem; flex: none; margin-top: .28em;
  border: 1.5px solid var(--accent); border-radius: 3px;
  background: transparent; cursor: pointer; position: relative;
}
.boxes.live input:checked { background: var(--accent); }
.boxes.live input:checked::after {
  content: ""; position: absolute; left: .3rem; top: .1rem;
  width: .28rem; height: .55rem;
  border-right: 2px solid var(--paper); border-bottom: 2px solid var(--paper);
  transform: rotate(45deg);
}
.boxes.live input:checked ~ span { color: var(--ink-soft); text-decoration: line-through; }
.ck-reset {
  align-self: flex-start; margin-top: .3rem;
  font-family: var(--f-display); font-size: .68rem;
  letter-spacing: .07em; text-transform: uppercase;
  background: none; border: 0; padding: .2rem 0; cursor: pointer;
  color: var(--ink-soft); border-bottom: 1px solid var(--rule);
}
.ck-reset:hover { color: var(--accent); }

/* ---- on this page ------------------------------------------------------*/
.toc {
  position: sticky; top: var(--appbar-h);
  max-height: calc(100svh - var(--appbar-h));
  overflow-y: auto;
  padding: clamp(1.5rem, 3.5vw, 3rem) 1rem 3rem 0;
  font-size: .82rem;
}
@media (max-width: 74.9375rem) { .toc { display: none; } }
.toc .k {
  font-family: var(--f-display); font-weight: 700;
  font-size: .68rem; letter-spacing: .1em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: .6rem;
}
.toc ul { gap: 0; }
.toc li { padding: 0; }
.toc li::before { content: none; }
.toc a {
  display: block; padding: .3rem .6rem; line-height: 1.35;
  color: var(--ink-soft); text-decoration: none;
  border-left: 2px solid var(--rule);
}
.toc a:hover { color: var(--ink); }
.toc a[data-active] { color: var(--accent); border-left-color: var(--accent); font-weight: 600; }

/* ---- cards grid (landing + section index) ------------------------------*/
.grid {
  display: grid; gap: .9rem;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}
.tile {
  display: flex; flex-direction: column; gap: .4rem;
  padding: 1.1rem 1.15rem;
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius); text-decoration: none; color: inherit;
  transition: border-color .14s ease, transform .14s ease;
}
.tile:hover { border-color: var(--accent); transform: translateY(-1px); }
@media (prefers-reduced-motion: reduce) { .tile { transition: none; } .tile:hover { transform: none; } }
.tile .t { font-family: var(--f-display); font-weight: 700; font-size: 1.02rem; color: var(--accent); }
.tile .s { font-size: .9rem; color: var(--ink-soft); line-height: 1.45; }
.tile .n { margin-top: auto; padding-top: .7rem; font-family: var(--f-mono); font-size: .72rem; color: var(--ink-soft); }
.tile.pending { border-style: dashed; }
.tile.pending .t { color: var(--ink-soft); }
.tile.big { border-left: 3px solid var(--accent); }
.tile.big .t { font-size: 1.2rem; }

.section-head { margin: 2.25rem 0 1rem; }
.section-head:first-of-type { margin-top: 0; }

/* ---- used-in footer ----------------------------------------------------*/
.usedin {
  display: flex; flex-direction: column; gap: .6rem;
  margin-top: 2.5rem; padding-top: 1.2rem; border-top: 1px solid var(--rule);
  font-size: .9rem; color: var(--ink-soft);
}
.usedin .k { font-family: var(--f-display); font-weight: 700; font-size: .7rem; letter-spacing: .09em; text-transform: uppercase; }
.chips { display: flex; gap: .5rem; flex-wrap: wrap; }
.chip {
  font-family: var(--f-display); font-size: .78rem;
  padding: .28rem .6rem; border: 1px solid var(--rule);
  border-radius: var(--radius-xs); text-decoration: none; color: var(--accent);
}
.chip:hover { border-color: var(--accent); }

/* A link into ANOTHER CLASS. Marked before the click, the same way an
   out-of-deck link is marked, so arriving in another course's material is
   never a surprise. The arrival banner is the other half of this. */
.chip.is-xcourse { border-style: dashed; }
.chip.is-xcourse::after {
  content: " \2197"; font-size: .82em; vertical-align: .12em; color: var(--ink-soft);
}
.navlist a.is-xcourse { border-left-color: var(--rule); border-left-style: dashed; }
.navlist a.is-xcourse::after {
  content: "\2197"; font-size: .78em; color: var(--ink-soft); margin-left: .2rem;
}
.navgroup > summary a.navself.is-xcourse::after {
  content: "\2197"; font-size: .78em; color: var(--ink-soft); margin-left: .2rem;
}

/* ==========================================================================
   Print - the page is a handout again. Chrome goes, every card opens.
   ========================================================================*/
@media print {
  /* The paper palette is NOT redefined here. It lives in the token layer, in
     one place, enforced with !important - because the version that lived here
     was quietly out-specified by the dark skin. See msu-theme.css, "Paper is
     white". Adding a second copy is how the two would drift. */
  .appbar, .sidebar, .backdrop, .toc, .cardtools, .usedin, .skip, .ck-reset { display: none !important; }
  /* max-width: none is currently inert - --shell-max is 1600px and a Letter
     content box is ~720px - and it is here anyway, because the day someone
     lowers --shell-max to something a page box can reach, the printed sheet
     would silently narrow and nothing on screen would say so. */
  .shell, .shell.has-toc { display: block; max-width: none; }
  main { padding: 0; }
  .article { max-width: none; }
  body.app { font-size: 11.2pt; }
  /* A collapsed card must still print - now that cards SHIP collapsed this is
     the difference between a handout and a list of headings. Two mechanisms,
     because the one that was here was inert:
       * app.js forces every <details> open on beforeprint and restores after.
         Measured: 36 printed pages of the General Safety track with it, 12
         without.
       * ::details-content is the CSS half, for a renderer that never fires
         the event. The rule below it - display on the light-DOM children -
         CANNOT work in Chrome: a closed <details> hides its content through
         the UA shadow slot, and no light-DOM rule reaches inside that. It was
         measured at identical PDF bytes with the rule present and with the
         rule deleted. ::details-content addresses the slot itself: 31 pages
         with JavaScript off, against 12. The old rule is kept because it is
         free and does work on engines that hide the children directly.
     "Printing never loses a card" is an invariant, so it is enforced as one -
     the same reason the logo swap carries !important.

     Scoped to `details`, NOT to `.xcard`: the JS half already forces every
     <details> open, and a second disclosure tier nested inside a card is
     planned. A rule naming one class would have to be remembered and extended
     when that lands; a rule naming the element covers the tier that does not
     exist yet. The sidebar's own <details> are display:none in print, so there
     is nothing here to protect them from. */
  details::details-content { content-visibility: visible !important; }
  details:not([open]) > *:not(summary) { display: revert !important; }
  .xcard { break-inside: avoid; border: 0; border-radius: 0; }
  .xcard > summary { padding: 0; border-bottom: 2px solid var(--ink); font-size: 14pt; }
  .xcard > summary .chev { display: none; }
  .xcard .body { padding: .6rem 0 0; }
  .xcard[open] > summary { border-bottom: 2px solid var(--ink); }
  .gate, .stop, .panel, .cat { print-color-adjust: exact; -webkit-print-color-adjust: exact; }
}

/* ---- playlist entry points --------------------------------------------*/
.pl-actions { flex: none; display: flex; flex-direction: column; gap: .45rem; }
.btn-alt {
  display: inline-flex; align-items: center; justify-content: center;
  padding: .5rem 1rem; border-radius: var(--radius-sm);
  border: 1px solid var(--rule); background: transparent; color: var(--accent);
  font-family: var(--f-display); font-weight: 700; font-size: .74rem;
  letter-spacing: .05em; text-transform: uppercase; text-decoration: none;
}
.btn-alt:hover { border-color: var(--accent); }
/* On a phone the three entry points lie in a row - but `flex: none` above
   means the row is sized by its contents and never shrinks, so it ran 466px
   wide inside a 375px viewport and pushed the whole page sideways by 130.
   `flex-wrap` on the row cannot help while the row itself is free to be as
   wide as it likes: it has to be given a width first. `1 1 100%` makes it take
   a full line of .pl-head, which already wraps, and the buttons then wrap
   inside that line. Found at 375px, measured per element; the document-level
   check saw it because the offender is outside a .xcard. */
@media (max-width: 34rem) {
  .pl-actions { flex: 1 1 100%; flex-direction: row; flex-wrap: wrap; }
}

/* ==========================================================================
   Collection intro — hero, hazard stripes, category ladder

   The source slide for this was a third-party commercial safety poster with
   another company's logo and phone number on it. The quote is a common shop
   slogan and is kept; the artwork is rebuilt here in the MSU palette rather
   than republished. Hazard stripes are a gradient, so they scale, theme, and
   animate without an image asset - the thing a slide deck could not do.
   ========================================================================*/
.hero {
  position: relative;
  margin: 2rem 0 2.5rem;
  background: var(--band);
  border-radius: var(--radius);
  overflow: hidden;
}
.hazard {
  height: .85rem;
  background-image: repeating-linear-gradient(
    -45deg,
    var(--msu-gold) 0 1rem,
    #1a1a1a 1rem 2rem
  );
  background-size: 2.83rem 100%;
}
@media (prefers-reduced-motion: no-preference) {
  .hazard { animation: hazard-slide 2.4s linear infinite; }
  .hazard-bottom { animation-direction: reverse; }
}
@keyframes hazard-slide { to { background-position: 2.83rem 0; } }

.hero-inner { padding: clamp(2rem, 6vw, 3.75rem) clamp(1.25rem, 5vw, 3rem); }
.hero-eyebrow {
  font-family: var(--f-display); font-weight: 700;
  font-size: .7rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--msu-gold); margin-bottom: 1.1rem;
}
.hero-quote {
  margin: 0; border: 0; padding: 0;
  font-family: var(--f-display); font-weight: 700;
  text-transform: uppercase; letter-spacing: -.015em; line-height: .98;
  color: #fff;
  display: flex; flex-direction: column; gap: .1em;
}
.hero-quote .l { font-size: clamp(1.9rem, 6.2vw, 4rem); }
.hero-quote .l2, .hero-quote .l4 { color: var(--msu-gold); }

.hero-but {
  display: flex; align-items: center; gap: .8rem;
  margin: .45em 0; color: #fff;
}
.hero-but .rule { flex: 1 1 auto; height: .32rem; background: #fff; }
.hero-but .but {
  flex: none; font-size: clamp(.85rem, 1.8vw, 1.15rem);
  letter-spacing: .22em; font-weight: 600;
}

/* staged reveal — motion is opt-in, and the text is fully present without it */
@media (prefers-reduced-motion: no-preference) {
  .hero-quote .l, .hero-but { opacity: 0; transform: translateY(.5em); animation: rise .55s ease forwards; }
  .hero-quote .l1 { animation-delay: .05s; }
  .hero-quote .l2 { animation-delay: .18s; }
  .hero-but       { animation-delay: .34s; }
  .hero-quote .l3 { animation-delay: .48s; }
  .hero-quote .l4 { animation-delay: .60s; }
}
@keyframes rise { to { opacity: 1; transform: none; } }

.intro-block { margin: 2.5rem 0; }
.intro-block > h2 { margin-bottom: .9rem; }
.intro-block > .lede { margin-bottom: 1.25rem; }

/* ---- category ladder ---------------------------------------------------*/
.ladder {
  border: 1px solid var(--rule); border-radius: var(--radius);
  background: var(--surface); overflow: hidden;
}
.ladder-tabs { display: flex; flex-wrap: wrap; gap: .5rem; padding: .9rem; border-bottom: 1px solid var(--rule); }
.ladder-tabs .cat { cursor: pointer; font: inherit; font-family: var(--f-display);
  font-weight: 700; font-size: .82rem; letter-spacing: .06em; text-transform: uppercase; }
.ladder-tabs .cat[aria-selected="false"] { background: transparent; color: var(--ink-soft); border-color: var(--rule); }
.ladder-tabs .cat[aria-selected="true"] { box-shadow: 0 0 0 2px var(--accent) inset; }

.ladder-body { padding: 1.4rem 1.15rem; }
.who { display: flex; gap: clamp(1rem, 5vw, 3rem); justify-content: center; margin-bottom: 1.2rem; }
.fig { display: flex; flex-direction: column; align-items: center; gap: .5rem; opacity: .28; transition: opacity .25s ease; }
.fig.is-on { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .fig { transition: none; } }
/* a person, drawn in two shapes - no icon font, no image */
.fig .body { position: relative; width: 2.6rem; height: 3.2rem; display: block; }
.fig .body::before {
  content: ""; position: absolute; left: 50%; top: 0; transform: translateX(-50%);
  width: 1.05rem; height: 1.05rem; border-radius: 50%;
  background: currentColor;
}
.fig .body::after {
  content: ""; position: absolute; left: 50%; bottom: 0; transform: translateX(-50%);
  width: 2.1rem; height: 1.85rem; background: currentColor;
  border-radius: 1.05rem 1.05rem .25rem .25rem;
}
.fig-you   { color: var(--cat1); }
.fig-peer  { color: var(--cat2); }
.fig-instr { color: var(--cat3); }
.fig .label {
  font-family: var(--f-display); font-weight: 700; font-size: .68rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--ink);
}
.ladder-text { text-align: center; font-size: 1.05rem; max-width: 34rem; margin: 0 auto; }

/* ---- emergency band ----------------------------------------------------*/
.emergency-band {
  display: grid; gap: 1px; background: var(--rule);
  border: 1px solid var(--rule); border-radius: var(--radius); overflow: hidden;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.emergency-band > div {
  background: var(--cat3-soft); padding: 1rem 1.1rem;
  display: flex; flex-direction: column; gap: .2rem;
}
.emergency-band .k {
  font-family: var(--f-display); font-weight: 700; font-size: .68rem;
  letter-spacing: .09em; text-transform: uppercase; color: var(--cat-ink); opacity: .75;
}
.emergency-band .v { font-size: 1.1rem; color: var(--cat-ink); }
.emergency-band a { color: inherit; }

.pl-stat .kind {
  display: inline-block; font-family: var(--f-display); font-weight: 700;
  font-size: .62rem; letter-spacing: .1em; text-transform: uppercase;
  padding: .1rem .4rem; margin-right: .3rem; border-radius: var(--radius-xs);
  background: var(--surface-2); color: var(--accent);
}

/* ==========================================================================
   Fire safety graphics — PASS stepper and the class/agent matrix

   Both replace a flat image from the source deck. The point is not decoration:
   an image cannot show you that the SAME extinguisher is correct for one fire
   and dangerous on another, and that is the thing that has to land.
   ========================================================================*/
/* The drawing is aria-hidden support; the steps are the content, and the
   letter badges now claim part of their column. 15rem of a 68ch article left
   the descriptions wrapping four lines deep, so the split favours the text. */
.pass {
  display: grid; gap: 1.25rem; align-items: center;
  grid-template-columns: minmax(0, 11rem) minmax(0, 1fr);
  background: var(--surface); border: 1px solid var(--rule);
  border-radius: var(--radius); padding: 1.25rem;
}
@media (max-width: 40rem) { .pass { grid-template-columns: minmax(0, 1fr); } }

.pass-svg { width: 100%; height: auto; display: block; }
.pass-svg .ext-body   { fill: var(--cat3); }
.pass-svg .ext-band   { fill: var(--paper); opacity: .35; }
.pass-svg .ext-neck,
.pass-svg .ext-lever  { fill: var(--ink-soft); }
.pass-svg .ext-nozzle { fill: var(--ink-soft); }
.pass-svg .ext-hose   { stroke: var(--ink-soft); }
.pass-svg .ext-pin    { stroke: var(--msu-gold); opacity: .35; transition: opacity .2s ease; }
.pass-svg .ext-spray  { stroke: var(--msu-liteblue); opacity: 0; transition: opacity .2s ease; }
.pass-svg .ext-base   { fill: var(--msu-gold); opacity: 0; transition: opacity .2s ease; }
.pass-svg .ext-sweep  { stroke: var(--msu-gold); opacity: 0; transition: opacity .2s ease; }
@media (prefers-reduced-motion: reduce) { .pass-svg * { transition: none; } }

/* one highlight per step — the drawing is the same, the emphasis moves */
.pass[data-active="0"] .ext-pin   { opacity: 1; }
.pass[data-active="1"] .ext-base  { opacity: 1; }
.pass[data-active="2"] .ext-spray { opacity: 1; }
.pass[data-active="3"] .ext-spray { opacity: 1; }
.pass[data-active="3"] .ext-base  { opacity: 1; }
.pass[data-active="3"] .ext-sweep { opacity: 1; }

/* ---- PASS stepper -------------------------------------------------------
   The acronym is the thing being taught, so the LETTER is the dominant
   element: P-A-S-S has to read straight down the left edge before any word
   does. Strict hierarchy at every width - letter, then word, then
   description - which also satisfies the type-scale rule that nothing heads
   something larger than itself.

   The letter is a BADGE rather than coloured type, and that is a contrast
   requirement, not a style choice. Red Orange fails as text on a light
   ground; here it is a border and a fill with --cat-ink inside it - navy on
   light, near-white on dark - the same treatment .cat, .stop and .gate get
   from the token layer. Before this it was 1.5rem of #FF4500 type on
   #FBFAF8, which only cleared AA at all because it happened to be large.

   The two colour declarations carry !important for the reason written at the
   bottom of msu-theme.css: "the letter is never red-orange type" is an
   invariant, and a specificity fix only outranks the rules that existed when
   it was written. Three rules in this project have already expired that way.
   One knob, --pass-k, sizes the badge, its type and the grid column together,
   so narrow and print variants are a single line each and cannot drift.     */
.pass-steps {
  --pass-k: 3rem;
  list-style: none; margin: 0; padding: 0;
  display: flex; flex-direction: column; gap: .45rem;
}
.pass-steps li { padding: 0; }
.pass-steps li::before { content: none; }
.pass-steps button {
  width: 100%; text-align: left; cursor: pointer; font: inherit;
  display: grid; grid-template-columns: var(--pass-k) minmax(0, 1fr);
  gap: .1rem .8rem;
  padding: .55rem .7rem; border-radius: var(--radius-sm);
  background: transparent; border: 1px solid transparent; color: inherit;
}
.pass-steps button:hover { background: var(--surface-2); }
.pass-steps button[aria-pressed="true"] { border-color: var(--accent); background: var(--surface-2); }

.pass-steps .k {
  grid-row: span 2; align-self: center;
  display: grid; place-items: center;
  width: var(--pass-k); height: var(--pass-k);
  font-family: var(--f-display); font-weight: 700; text-transform: uppercase;
  font-size: calc(var(--pass-k) * .68); line-height: 1;
  color: var(--cat-ink) !important;
  background: var(--cat3-soft);
  border: 2px solid var(--cat3);
  border-radius: var(--radius-xs);
  print-color-adjust: exact; -webkit-print-color-adjust: exact;
}
/* The step being shown inverts - same letter, same size, same place, so the
   acronym still reads. --accent is navy on light and Lite Blue on dark, and
   --paper is the ground of each, so this pair is legible in both. Filling
   with --cat3 would not be: near-white on Red Orange is 2.9:1. */
.pass-steps button[aria-pressed="true"] .k {
  background: var(--accent); border-color: var(--accent);
  color: var(--paper) !important;
}
.pass-steps .w {
  align-self: end;
  font-family: var(--f-display); font-weight: 700;
  font-size: 1.05rem; letter-spacing: .05em; text-transform: uppercase;
  line-height: 1.2;
}
.pass-steps .d { align-self: start; font-size: .88rem; color: var(--ink-soft); line-height: 1.4; }

@media (max-width: 30rem) { .pass-steps { --pass-k: 2.5rem; } }
/* A panel is read from across a room. */
.presenting .pass-steps { --pass-k: 3.75rem; }
@media print {
  .pass-steps { --pass-k: 2.6rem; }
  .pass-steps li { break-inside: avoid; }
  /* On paper no step is "current", and an inverted one would read as an
     instruction to start there. Later in source and equally specific, so it
     takes the !important pair above. */
  .pass-steps button[aria-pressed="true"] { border-color: transparent; background: transparent; }
  .pass-steps button[aria-pressed="true"] .k {
    background: var(--cat3-soft); border-color: var(--cat3); color: var(--cat-ink) !important;
  }
}

/* ---- fire class matrix -------------------------------------------------*/
.firetable { border: 1px solid var(--rule); border-radius: var(--radius); background: var(--surface); overflow: hidden; }
.fc-tabs { display: flex; flex-wrap: wrap; gap: .4rem; padding: .85rem; border-bottom: 1px solid var(--rule); }
.fc-tabs button {
  cursor: pointer; font: inherit; font-family: var(--f-display); font-weight: 700;
  font-size: .8rem; letter-spacing: .05em; text-transform: uppercase;
  padding: .4rem .7rem; border-radius: var(--radius-xs);
  border: 1.5px solid var(--rule); background: transparent; color: var(--ink-soft);
}
.fc-tabs button[aria-selected="true"] { border-color: var(--accent); color: var(--accent); background: var(--surface-2); }
.fc-body { padding: 1.2rem 1.15rem; }
.fc-what { margin-bottom: 1rem; }
.fc-agents { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.fc-agents li { padding: .55rem .7rem; border-radius: var(--radius-sm); display: flex; gap: .7rem; align-items: baseline; }
.fc-agents li::before { content: none; }
.fc-agents .n { font-family: var(--f-display); font-weight: 700; font-size: .92rem; flex: 0 0 11rem; }
.fc-agents .r { font-size: .92rem; }
@media (max-width: 34rem) { .fc-agents li { flex-wrap: wrap; } .fc-agents .n { flex: 1 1 100%; } }
.fc-agents .ok   { background: color-mix(in srgb, var(--cat1) 12%, transparent); }
.fc-agents .no   { background: var(--surface-2); color: var(--ink-soft); }
.fc-agents .bad  { background: var(--cat3-soft); }
.fc-agents .ok   .n { color: var(--accent); }
.fc-agents .bad  .n { color: var(--cat-ink); }
.fc-agents .bad  .r { color: var(--cat-ink); font-weight: 600; }

/* ===========================================================================
   DET 403 Unit 1 — interactive work surfaces

   These are deliberately tools, not a second dashboard. Each one occupies a
   single continuous surface inside a lesson: inputs establish the situation,
   a quiet results area gives the current consequence, and any table stays a
   table. The content remains meaningful when the JavaScript is unavailable.
   ======================================================================== */
.conversion-equation {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  align-items: center;
  margin: 1rem 0;
  padding: .8rem .95rem;
  border-inline-start: .25rem solid var(--accent);
  background: var(--surface-2);
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .98rem;
  font-weight: 700;
  line-height: 1.45;
  overflow-wrap: anywhere;
}
.conversion-factor {
  display: inline-grid;
  grid-template-rows: auto auto;
  vertical-align: middle;
  min-width: max-content;
  text-align: center;
  line-height: 1.15;
}
.conversion-factor > span {
  display: block;
  padding-inline: .18em;
}
.conversion-factor > span:first-child { border-bottom: 1px solid currentColor; padding-bottom: .14em; }
.conversion-factor > span:last-child { padding-top: .14em; }
.unit-cancel {
  position: relative;
  display: inline-block;
  color: var(--ink-soft);
}
.unit-cancel::after {
  content: "";
  position: absolute;
  z-index: 1;
  inset-inline: -.08em;
  top: 52%;
  height: 2px;
  background: var(--cat3);
  transform: rotate(-8deg);
  transform-origin: center;
}

/* The original Unit Systems slide used a map. On the web, the more useful
   comparison is the same physical feature carrying two different labels. */
.unit-systems-figure,
.prefix-ladder-figure {
  margin: 1.5rem 0;
  padding: clamp(1rem, 3vw, 1.5rem);
  border: 1px solid var(--rule);
  background: var(--surface-2);
  color: var(--ink);
}
.unit-systems-visual {
  display: grid;
  grid-template-columns: minmax(8rem, .8fr) minmax(13rem, 1.4fr) minmax(8rem, .8fr);
  gap: clamp(.75rem, 2.5vw, 1.5rem);
  align-items: center;
}
.unit-system-readout {
  display: grid;
  gap: .2rem;
  padding-top: .7rem;
  border-top: .28rem solid var(--accent);
  font-family: var(--f-display);
}
.unit-system-us { border-color: var(--cat2); text-align: end; }
.unit-system-readout .k {
  color: var(--ink-soft);
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
}
.unit-system-readout strong {
  color: var(--ink);
  font-size: clamp(1.35rem, 3vw, 2.25rem);
  line-height: 1;
  white-space: nowrap;
}
.unit-system-readout > span:last-child { color: var(--ink-soft); font-size: .82rem; }
.unit-systems-part { display: block; width: 100%; height: auto; overflow: visible; }
.unit-systems-shadow { fill: color-mix(in srgb, var(--ink) 16%, transparent); }
.unit-systems-plate { fill: var(--paper); stroke: var(--ink); stroke-width: 4; stroke-linejoin: round; }
.unit-systems-hole { fill: var(--surface-2); stroke: var(--ink); stroke-width: 4; }
.unit-systems-extension { fill: none; stroke: var(--ink-soft); stroke-width: 2; }
.unit-systems-dimension { fill: none; stroke: var(--accent); stroke-width: 3; }
.unit-systems-arrowhead { fill: var(--accent); }
.unit-systems-figure figcaption,
.prefix-ladder-figure figcaption {
  margin-top: .9rem;
  color: var(--ink-soft);
  font-size: .9rem;
  line-height: 1.5;
  text-align: center;
}

.prefix-ladder-message {
  display: flex;
  gap: .45rem 1rem;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 1rem;
  font-family: var(--f-display);
}
.prefix-ladder-message span { color: var(--ink-soft); font-size: .86rem; }
.prefix-ladder {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: .7rem;
  margin: 0;
  padding: 0;
  list-style: none;
  counter-reset: none;
}
.prefix-ladder li {
  position: relative;
  display: grid;
  gap: .15rem;
  min-width: 0;
  padding: .75rem .45rem;
  border-top: .32rem solid var(--rule);
  text-align: center;
}
.prefix-ladder li:not(:last-child)::after {
  content: "\2192";
  position: absolute;
  z-index: 1;
  inset-inline-end: -.68rem;
  top: 50%;
  width: .7rem;
  color: var(--accent);
  font-family: var(--f-display);
  font-weight: 800;
  text-align: center;
  transform: translateY(-50%);
}
.prefix-ladder li:nth-child(n+3) { border-color: var(--accent); }
.prefix-ladder .prefix-ladder-base { border-color: var(--cat2); background: var(--paper); }
.prefix-ladder-name {
  color: var(--ink-soft);
  font-family: var(--f-display);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
}
.prefix-ladder strong { font-family: var(--f-display); font-size: 1.45rem; line-height: 1.1; }
.prefix-ladder li > span:last-child { color: var(--ink-soft); font-size: .78rem; white-space: nowrap; }
.prefix-equivalence {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem .65rem;
  justify-content: center;
  align-items: center;
  margin: 1.1rem 0 0;
  font-family: var(--f-display);
  font-weight: 700;
}
.prefix-equivalence span {
  padding: .35rem .5rem;
  border-bottom: 2px solid var(--accent);
  background: var(--paper);
}
.prefix-equivalence b { color: var(--ink-soft); }

.prefix-route {
  margin: 1.15rem 0;
  padding: .85rem .9rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
}
.prefix-route > p {
  margin: 0 0 .6rem;
  color: var(--ink-soft);
  font-size: .88rem;
  line-height: 1.45;
}
.prefix-route > p strong { color: var(--ink); }
.prefix-route-track {
  position: relative;
  min-height: 6.1rem;
  overflow: hidden;
}
.prefix-route-track::before {
  content: "";
  position: absolute;
  inset: 3rem 4% auto;
  height: 2px;
  background: var(--ink-soft);
}
.prefix-route-track::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: 50%;
  top: 2.55rem;
  width: 2px;
  height: .95rem;
  background: var(--ink-soft);
}
.prefix-route-band {
  position: absolute;
  z-index: 1;
  left: 40.8%;
  top: 2.83rem;
  width: 4.6%;
  min-width: 3px;
  height: .36rem;
  background: var(--accent);
}
.prefix-route-marker {
  position: absolute;
  z-index: 2;
  left: var(--prefix-position);
  display: block;
  width: max-content;
  max-width: 45%;
  padding: .3rem .45rem;
  border: 2px solid var(--accent);
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .7rem;
  font-weight: 700;
  line-height: 1.2;
  text-align: center;
  transform: translateX(-50%);
}
.prefix-route-from { --prefix-position: 40.8%; top: .1rem; }
.prefix-route-to { --prefix-position: 45.4%; bottom: .1rem; border-color: var(--cat2); }
.prefix-route-marker::after {
  content: "";
  position: absolute;
  left: 50%;
  width: 2px;
  height: 1.2rem;
  background: currentColor;
  transform: translateX(-50%);
}
.prefix-route-from::after { top: 100%; }
.prefix-route-to::after { bottom: 100%; }
.prefix-route-marker[data-prefix-edge="low"] { transform: translateX(0); }
.prefix-route-marker[data-prefix-edge="high"] { transform: translateX(-100%); }
.prefix-route-marker[data-prefix-edge="low"]::after { left: .45rem; transform: none; }
.prefix-route-marker[data-prefix-edge="high"]::after { right: .45rem; left: auto; transform: none; }
.prefix-route-axis {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: .5rem;
  color: var(--ink-soft);
  font-family: var(--f-display);
  font-size: .64rem;
  font-weight: 700;
  line-height: 1.25;
  text-align: center;
}
.prefix-route-axis span:first-child { text-align: start; }
.prefix-route-axis span:last-child { text-align: end; }

.unit-lab,
[data-prefix-explorer],
[data-unit-converter],
[data-path-lab],
[data-acceleration-lab],
[data-cad-lab] {
  margin: 1.5rem 0;
  padding: clamp(1rem, 3vw, 1.5rem);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  min-width: 0;
}
.unit-lab > :first-child,
[data-prefix-explorer] > :first-child,
[data-unit-converter] > :first-child,
[data-path-lab] > :first-child,
[data-acceleration-lab] > :first-child,
[data-cad-lab] > :first-child { margin-top: 0; }
.unit-lab > :last-child,
[data-prefix-explorer] > :last-child,
[data-unit-converter] > :last-child,
[data-path-lab] > :last-child,
[data-acceleration-lab] > :last-child,
[data-cad-lab] > :last-child { margin-bottom: 0; }
.unit-lab :is(h3, h4),
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(h3, h4) { margin-top: 0; }

.unit-controls,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: .9rem;
  align-items: end;
  min-width: 0;
}
.unit-controls > *, .unit-controls label, .unit-controls fieldset,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset > * { min-width: 0; }
.unit-controls > p,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset > p {
  display: grid;
  gap: .35rem;
  margin: 0;
}
.unit-controls label,
.unit-controls legend,
.unit-control-label,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset :is(label, legend) {
  display: grid;
  gap: .35rem;
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .045em;
  line-height: 1.25;
  text-transform: uppercase;
}
.unit-controls fieldset,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset {
  margin: 0;
  padding: 0;
  border: 0;
}
.unit-controls legend,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset legend { padding: 0; }
.unit-controls :is(input, select, button),
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(input, select, button) {
  box-sizing: border-box;
  width: 100%;
  min-height: 2.75rem;
  margin: 0;
  padding: .55rem .65rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  line-height: 1.2;
}
.unit-controls :is(input, select):hover,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(input, select):hover { border-color: var(--ink-soft); }
.unit-controls button,
.practice-check,
[data-practice-check] {
  cursor: pointer;
  border-color: var(--accent);
  background: var(--accent);
  color: var(--paper);
  font-family: var(--f-display);
  font-weight: 700;
  letter-spacing: .02em;
}
.unit-controls button:hover,
.practice-check:hover,
[data-practice-check]:hover { filter: brightness(1.08); }
.unit-lab :is(input, select, button):focus-visible,
:is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab], [data-practice]) :is(input, select, button):focus-visible {
  outline: 3px solid var(--cat2);
  outline-offset: 2px;
}

.unit-results {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 12rem), 1fr));
  gap: .75rem;
  margin: 1rem 0 0;
  min-width: 0;
}
.unit-result {
  min-width: 0;
  padding: .8rem .9rem;
  border-inline-start: .25rem solid var(--accent);
  background: var(--surface-2);
}
.unit-result > :first-child { margin-top: 0; }
.unit-result > :last-child { margin-bottom: 0; }
.unit-result .k,
.unit-result dt,
.unit-result-label {
  display: block;
  margin-bottom: .2rem;
  color: var(--ink-soft);
  font-family: var(--f-display);
  font-size: .7rem;
  font-weight: 700;
  letter-spacing: .08em;
  line-height: 1.25;
  text-transform: uppercase;
}
.unit-result .v,
.unit-result dd,
[data-prefix-result],
[data-unit-result],
[data-convert-result],
[data-path-output],
[data-acceleration-output],
[data-cad-output] {
  margin: 0;
  color: var(--ink);
  font-family: var(--f-display);
  font-size: 1.12rem;
  font-weight: 700;
  line-height: 1.3;
  overflow-wrap: anywhere;
}
.unit-equation,
[data-prefix-work],
[data-unit-work],
[data-convert-work] {
  display: block;
  margin: 1rem 0 0;
  padding: .75rem .9rem;
  border-inline-start: 2px solid var(--rule);
  background: var(--surface-2);
  color: var(--ink-soft);
  font-size: .94rem;
  line-height: 1.55;
  overflow-wrap: anywhere;
}
.unit-lab[data-state="error"] .unit-result,
.unit-lab[data-state="error"] .unit-equation,
.unit-lab[data-state="error"] [data-prefix-result],
.unit-lab[data-state="error"] [data-unit-result] {
  border-color: var(--cat3);
}
[data-prefix-explorer][data-state="error"] [data-prefix-result],
[data-unit-converter][data-state="error"] [data-convert-result],
[data-unit-converter][data-state="error"] [data-unit-result],
[data-path-lab][data-state="error"] .dims,
[data-acceleration-lab][data-state="error"] .dims,
[data-cad-lab][data-state="error"] .dims { border-color: var(--cat3); }

.unit-table-wrap {
  max-width: 100%;
  margin: 1rem 0;
  overflow-x: auto;
  overscroll-behavior-inline: contain;
  border: 1px solid var(--rule);
}
.unit-table {
  width: 100%;
  min-width: 32rem;
  margin: 0;
  border: 0;
  border-collapse: collapse;
  background: var(--surface);
}
.unit-table :is(th, td) {
  padding: .65rem .75rem;
  border: 0;
  border-bottom: 1px solid var(--rule);
  color: var(--ink);
  text-align: start;
  vertical-align: top;
}
.unit-table th {
  background: var(--surface-2);
  font-family: var(--f-display);
  font-size: .78rem;
  letter-spacing: .05em;
  text-transform: uppercase;
}
.unit-table tr:last-child :is(th, td) { border-bottom: 0; }

/* app.js derives the scale from the entered positions. The visible endpoint
   labels and the track's accessible label always name that same scale. */
.path-track,
.path-lab-track,
[data-path-track] {
  position: relative;
  min-height: 6rem;
  margin: 1rem 0 0;
  padding: .75rem .75rem 1.8rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
  overflow: hidden;
}
[data-path-lab][data-path-label-rows="3"] :is(.path-track, .path-lab-track, [data-path-track]) { min-height: 7.5rem; }
[data-path-lab][data-path-label-rows="3"] :is(.path-track, .path-lab-track, [data-path-track]) [data-path-marker]::after {
  height: calc(3.8rem - var(--path-label-row, 0) * 1.3rem);
}
.path-track::before,
.path-lab-track::before,
[data-path-track]::before {
  content: "";
  position: absolute;
  inset: auto .75rem 1.3rem;
  height: 2px;
  background: var(--ink-soft);
}
.path-axis {
  position: absolute;
  z-index: 1;
  inset: auto .75rem .2rem;
  display: flex;
  justify-content: space-between;
  color: var(--ink-soft);
  font-family: var(--f-display);
  font-size: .66rem;
  font-weight: 700;
  letter-spacing: .025em;
  pointer-events: none;
}
.path-axis > span { min-width: 0; overflow-wrap: anywhere; }
.path-axis > span:last-child { text-align: end; }
.path-lab-track [data-path-marker="start"] { --path-position: 16.6667%; --path-label-row: 0; }
.path-lab-track [data-path-marker="turn"] { --path-position: 83.3333%; --path-label-row: 0; }
.path-lab-track [data-path-marker="end"] { --path-position: 16.6667%; --path-label-row: 1; }
.path-lab-track [data-path-marker] {
  top: calc(.45rem + var(--path-label-row, 0) * 1.3rem);
}
.path-lab-track [data-path-marker][data-path-edge="low"] { transform: translateX(0); }
.path-lab-track [data-path-marker][data-path-edge="high"] { transform: translateX(-100%); }
.path-lab-track [data-path-marker][data-path-edge="low"]::after { left: .45rem; }
.path-lab-track [data-path-marker][data-path-edge="high"]::after { right: .45rem; }
.path-lab-track [data-path-marker]:not([data-path-edge])::after { left: 50%; transform: translateX(-50%); }
.path-lab-track [data-path-marker][data-path-edge="low"]::after,
.path-lab-track [data-path-marker][data-path-edge="high"]::after {
  transform: none;
}
.path-track [data-path-marker],
.path-track [data-path-start-marker],
.path-track [data-path-turn-marker],
.path-track [data-path-end-marker],
.path-lab-track [data-path-marker],
.path-lab-track [data-path-start-marker],
.path-lab-track [data-path-turn-marker],
.path-lab-track [data-path-end-marker],
[data-path-track] [data-path-marker],
[data-path-track] [data-path-start-marker],
[data-path-track] [data-path-turn-marker],
[data-path-track] [data-path-end-marker] {
  position: absolute;
  z-index: 1;
  top: calc(.45rem + var(--path-label-row, 0) * 1.3rem);
  left: var(--path-position, 50%);
  display: block;
  width: max-content;
  max-width: calc(100% - 1rem);
  min-height: 1.8rem;
  padding: .3rem .45rem;
  transform: translateX(-50%);
  border: 2px solid var(--accent);
  border-radius: 50%;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .68rem;
  font-weight: 700;
  line-height: 1;
}
.path-track [data-path-marker]::after,
.path-track [data-path-start-marker]::after,
.path-track [data-path-turn-marker]::after,
.path-track [data-path-end-marker]::after,
.path-lab-track [data-path-marker]::after,
.path-lab-track [data-path-start-marker]::after,
.path-lab-track [data-path-turn-marker]::after,
.path-lab-track [data-path-end-marker]::after,
[data-path-track] [data-path-marker]::after,
[data-path-track] [data-path-start-marker]::after,
[data-path-track] [data-path-turn-marker]::after,
[data-path-track] [data-path-end-marker]::after {
  content: "";
  position: absolute;
  top: 100%;
  width: 2px;
  height: calc(2.3rem - var(--path-label-row, 0) * 1.3rem);
  background: var(--accent);
}

.velocity-bars {
  display: grid;
  gap: .75rem;
  margin: 1rem 0 0;
}
.velocity-bars > * {
  display: grid;
  grid-template-columns: minmax(5rem, .45fr) minmax(0, 1fr);
  gap: .65rem;
  align-items: center;
  min-width: 0;
}
.velocity-bars .velocity-track,
.velocity-bars [data-velocity-track] {
  position: relative;
  min-width: 0;
  height: 1rem;
  border: 1px solid var(--rule);
  background: var(--surface-2);
}
.velocity-bars [data-acceleration-initial-bar],
.velocity-bars [data-acceleration-final-bar],
.velocity-bars [data-acceleration-bar] {
  display: block;
  width: var(--velocity-width, 0%);
  max-width: 100%;
  min-width: 0;
  height: 100%;
  background: var(--accent);
}
.velocity-bars [data-acceleration-final-bar],
.velocity-bars [data-acceleration-bar="final"] { background: var(--cat2); }

[data-practice] {
  display: grid;
  gap: .75rem;
  align-items: end;
}
[data-practice] label {
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .045em;
  line-height: 1.25;
  text-transform: uppercase;
}
[data-practice] input {
  box-sizing: border-box;
  width: 100%;
  min-height: 2.75rem;
  margin: 0;
  padding: .55rem .65rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  line-height: 1.2;
}
[data-practice] .practice-check,
[data-practice] [data-practice-check] { min-height: 2.75rem; }
[data-practice-feedback],
[data-practice-result] {
  min-height: 1.5em;
  margin: 0;
  padding: .7rem .8rem;
  border-inline-start: .25rem solid var(--rule);
  background: var(--surface-2);
  color: var(--ink);
  line-height: 1.45;
}
[data-practice][data-state="correct"] :is([data-practice-feedback], [data-practice-result]) { border-color: var(--cat1); }
[data-practice][data-state="incorrect"] :is([data-practice-feedback], [data-practice-result]) { border-color: var(--cat2); }
[data-practice][data-state="error"] :is([data-practice-feedback], [data-practice-result]) { border-color: var(--cat3); }
[data-practice][data-state="correct"] :is(.practice-check, [data-practice-check])::before { content: "\2713 "; }
[data-practice][data-state="incorrect"] :is(.practice-check, [data-practice-check])::before { content: "\21bb "; }
[data-practice][data-state="error"] :is(.practice-check, [data-practice-check])::before { content: "! "; }

/* Learner-built practice track. /practice.html server-renders the catalog;
   JavaScript only selects and filters those authored problem cards. */
.practice-cart-status {
  display: inline-block;
  margin-inline-start: .6rem;
  color: var(--ink-muted);
  font-size: .86rem;
  line-height: 1.35;
}
[data-practice-add][aria-pressed="true"] {
  border-color: var(--cat1);
  background: var(--surface-2);
  color: var(--ink);
}
.practice-session-status {
  margin: 0 0 1rem;
  color: var(--ink-muted);
}
.practice-session-tools {
  margin: 1.25rem 0;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--surface);
}
.practice-session-tools label {
  display: grid;
  gap: .4rem;
  min-width: 0;
  max-width: 34rem;
  color: var(--ink);
  font-family: var(--f-display);
  font-size: .78rem;
  font-weight: 700;
  letter-spacing: .045em;
  text-transform: uppercase;
}
.practice-session-tools select {
  box-sizing: border-box;
  width: 100%;
  min-width: 0;
  max-width: 100%;
  min-height: 2.75rem;
  padding: .55rem .65rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--ink);
  font: inherit;
  font-size: 1rem;
  letter-spacing: 0;
  text-transform: none;
}
.practice-session-head {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 1rem;
  padding-bottom: 1rem;
  border-bottom: 2px solid var(--ink);
}
.practice-session-head :is(h2, p) { margin-bottom: 0; }
.practice-session-actions { display: flex; flex-wrap: wrap; gap: .55rem; }
:is([data-practice-run], [data-practice-clear]) {
  min-height: 2.75rem;
  padding: .55rem .85rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius-xs);
  background: var(--paper);
  color: var(--accent);
  font-family: var(--f-display);
  font-weight: 700;
}
[data-practice-run] { border-color: var(--accent); background: var(--accent); color: var(--paper); }
.practice-entry {
  display: grid;
  gap: .35rem;
  min-width: 0;
  margin: 0 0 1.25rem;
  break-inside: avoid;
}
.practice-source {
  margin: 0;
  color: var(--ink-muted);
  font-size: .86rem;
}
.practice-catalog-select {
  display: flex !important;
  align-items: center;
  gap: .55rem;
  min-width: 0;
  padding: .65rem .75rem;
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  cursor: pointer;
  overflow-wrap: anywhere;
}
.sr-only {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  overflow: hidden !important;
  clip: rect(0, 0, 0, 0) !important;
  white-space: nowrap !important;
  border: 0 !important;
}
[data-practice-session] { min-width: 0; }
[data-practice] .practice-catalog-select [data-practice-select] {
  flex: 0 0 auto;
  width: 1.15rem;
  height: 1.15rem;
  min-height: 1.15rem;
  margin: 0;
  padding: 0;
  accent-color: var(--accent);
}
[data-practice-session] .practice-entry[hidden] { display: none; }
:is([data-practice-add], [data-practice-run], [data-practice-clear]):focus-visible {
  outline: 3px solid var(--focus, var(--accent));
  outline-offset: 3px;
}

/* A presenter gets slightly larger controls without ever constraining the
   tool's height; focus rings and all controls remain available in this mode. */
.presenting :is(.unit-lab, [data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) { font-size: 1.08rem; }
.presenting .xcard.deck-wiki-only { display: none !important; }
.presenting :is(.unit-controls, [data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(input, select, button) { min-height: 3rem; }
.presenting .conversion-equation { font-size: 1.12em; padding: 1rem 1.1rem; }
.presenting :is(.unit-systems-figure, .prefix-ladder-figure) { padding: 1.6rem; }
.presenting .unit-system-readout .k,
.presenting .prefix-ladder-name { font-size: .82rem; }
.presenting .unit-system-readout > span:last-child,
.presenting .prefix-ladder li > span:last-child { font-size: .94rem; }
.presenting .unit-system-readout strong { font-size: clamp(2rem, 4vw, 3.25rem); }
.presenting .prefix-ladder strong { font-size: 1.85rem; }
.presenting .prefix-ladder-message span,
.presenting :is(.unit-systems-figure, .prefix-ladder-figure) figcaption,
.presenting .prefix-route > p { font-size: 1rem; }
.presenting .prefix-route-marker { font-size: .82rem; }
.presenting .prefix-route-axis { font-size: .74rem; }

@media (max-width: 36rem) {
  .unit-systems-visual { grid-template-columns: minmax(0, 1fr); }
  .practice-cart-status { display: block; margin: .35rem 0 0; }
  .practice-session-head { align-items: stretch; flex-direction: column; }
  .practice-session-actions > * { flex: 1 1 10rem; }
  .unit-system-readout { text-align: center; }
  .unit-systems-part { width: min(100%, 19rem); margin-inline: auto; }
  .prefix-ladder-message { display: grid; }
  .prefix-ladder { grid-template-columns: minmax(0, 1fr); gap: .45rem; }
  .prefix-ladder li { grid-template-columns: 1fr auto 1fr; align-items: center; text-align: start; }
  .prefix-ladder li > span:last-child { text-align: end; }
  .prefix-ladder li:not(:last-child)::after {
    content: "\2193";
    inset: auto 50% -.66rem auto;
    top: auto;
    transform: translateX(50%);
  }
  .prefix-route { padding-inline: .65rem; }
  .prefix-route-marker { max-width: 47%; font-size: .62rem; }
  .prefix-route-axis { font-size: .56rem; }
  .unit-lab,
  [data-prefix-explorer],
  [data-unit-converter],
  [data-path-lab],
  [data-acceleration-lab],
  [data-cad-lab] { padding: 1rem; overflow: hidden; }
  .unit-controls,
  :is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) > fieldset,
  .unit-results,
  [data-practice] { grid-template-columns: minmax(0, 1fr); }
  .unit-controls > *,
  .unit-results > * { min-width: 0; }
  .unit-controls :is(input, select, button),
  :is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(input, select, button) { max-width: 100%; }
  .unit-table { min-width: 28rem; }
  .velocity-bars > * { grid-template-columns: minmax(0, 1fr); gap: .35rem; }
  .conversion-equation { gap: .3rem; padding: .7rem .75rem; font-size: .9rem; }
  .path-axis { font-size: .58rem; letter-spacing: 0; }
}

@media print {
  .practice-cart-status,
  [data-practice-run],
  [data-practice-clear] { display: none !important; }
  .unit-systems-figure,
  .prefix-ladder-figure,
  .prefix-route {
    break-inside: avoid;
    background: #FFF !important;
    border-color: #555 !important;
    color: #111 !important;
  }
  .unit-system-readout,
  .prefix-ladder li,
  .prefix-equivalence span,
  .prefix-route-marker { border-color: #111 !important; background: #FFF !important; color: #111 !important; }
  .unit-systems-shadow { fill: #DDD !important; }
  .unit-systems-plate { fill: #FFF !important; stroke: #111 !important; }
  .unit-systems-hole { fill: #FFF !important; stroke: #111 !important; }
  .unit-systems-extension,
  .unit-systems-dimension { stroke: #111 !important; }
  .unit-systems-arrowhead { fill: #111 !important; }
  .prefix-route-track::before,
  .prefix-route-track::after,
  .prefix-route-band,
  .prefix-route-marker::after { background: #111 !important; }
  .unit-lab,
  [data-prefix-explorer],
  [data-unit-converter],
  [data-path-lab],
  [data-acceleration-lab],
  [data-cad-lab] {
    break-inside: avoid;
    background: #FFF !important;
    border-color: #777 !important;
    color: #111 !important;
  }
  .unit-lab :is(input, select),
  :is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab]) :is(input, select) {
    min-height: 0;
    padding: .2rem .3rem;
    border-color: #777;
    background: #FFF;
    color: #111;
  }
  .unit-lab :is(button, .practice-check, [data-practice-check]):not([data-print-keep]),
  :is([data-prefix-explorer], [data-unit-converter], [data-path-lab], [data-acceleration-lab], [data-cad-lab], [data-practice]) :is(button, .practice-check, [data-practice-check]):not([data-print-keep]) { display: none; }
  .unit-result,
  .unit-equation,
  [data-practice-feedback],
  [data-practice-result],
  .path-track,
  .path-lab-track,
  [data-path-track],
  .unit-table,
  .unit-table th { background: #FFF !important; color: #111 !important; }
  .conversion-equation {
    break-inside: avoid;
    background: #FFF !important;
    border-color: #111 !important;
    color: #111 !important;
  }
  .conversion-equation .unit-cancel { color: #111 !important; }
  .conversion-equation .unit-cancel::after { background: #111 !important; }
  .unit-result,
  .unit-equation,
  [data-practice-feedback],
  [data-practice-result] { border-color: #555 !important; }
  .path-track::before,
  .path-lab-track::before,
  [data-path-track]::before,
  .path-track [data-path-marker]::after,
  .path-track [data-path-start-marker]::after,
  .path-track [data-path-turn-marker]::after,
  .path-track [data-path-end-marker]::after,
  .path-lab-track [data-path-marker]::after,
  .path-lab-track [data-path-start-marker]::after,
  .path-lab-track [data-path-turn-marker]::after,
  .path-lab-track [data-path-end-marker]::after,
  [data-path-track] [data-path-marker]::after,
  [data-path-track] [data-path-start-marker]::after,
  [data-path-track] [data-path-turn-marker]::after,
  [data-path-track] [data-path-end-marker]::after { background: #111 !important; }
  .path-track [data-path-marker],
  .path-track [data-path-start-marker],
  .path-track [data-path-turn-marker],
  .path-track [data-path-end-marker],
  .path-lab-track [data-path-marker],
  .path-lab-track [data-path-start-marker],
  .path-lab-track [data-path-turn-marker],
  .path-lab-track [data-path-end-marker],
  [data-path-track] [data-path-marker],
  [data-path-track] [data-path-start-marker],
  [data-path-track] [data-path-turn-marker],
  [data-path-track] [data-path-end-marker] {
    border-color: #111 !important;
    background: #FFF !important;
    color: #111 !important;
  }
  .velocity-bars [data-acceleration-initial-bar],
  .velocity-bars [data-acceleration-final-bar],
  .velocity-bars [data-acceleration-bar] { background: #333 !important; }
}

/* Standalone activities keep their own document, styles, workers and controls. */
.document-activity { min-width: 0; }
.document-activity iframe { display: block; width: 100%; height: 80vh; min-height: 34rem; border: 1px solid var(--line); border-radius: .5rem; background: var(--paper); }
.activity-links { display: flex; flex-wrap: wrap; align-items: center; gap: .65rem; }
.document-activity + p { margin-top: 1rem; }
[class^="activity-notes-"] { overflow-wrap: anywhere; }
[class^="activity-notes-"] table { display: block; max-width: 100%; overflow-x: auto; }
@media (max-width: 40rem) {
  .document-activity iframe { min-height: 32rem; height: 75vh; }
}
@media print {
  .document-activity iframe { display: none !important; }
  .activity-links { display: block; }
}
