/* ECE 343 -- the site shell.
 *
 * Everything the reading pages and the frame lessons hold in common: the
 * palette, the type scale, the HUD, its popovers, and the jump overlay. Split
 * out of frames.css when the shell went site-wide, so a reading page does not
 * carry the scroll-snap and present-mode machinery it will never use.
 *
 * frames.css layers the frame-only rules on top of this. Load order is
 * shell.css first.
 */

/* ---- tokens: complete light palette on bare :root ------------------- */
:root {
  --blue:        #0067b9;
  --blue-dark:   #0067b9;
  --red:         #b01e24;
  --ink:         #1a1a1a;
  --muted:       #5a5a5a;
  --ground:      #ffffff;
  --panel:       #ffffff;
  --rule:        rgba(0, 74, 133, 0.16);
  --rule-strong: rgba(0, 74, 133, 0.42);
  --soft:        rgba(0, 103, 185, 0.07);
  --soft-red:    rgba(176, 30, 36, 0.07);

  /* The course's practice accent, the amber the sidebar icons and the
     material cards already use for problem sets. Was a literal #8a5a00 in
     custom.css with a hand-written dark twin; a token instead, so anything
     that wants it gets both modes for free. */
  --accent-ink:  #8a5a00;
  --accent-rule: rgba(138, 90, 0, 0.35);
  --accent-soft: rgba(138, 90, 0, 0.09);
  --rail:        rgba(0, 74, 133, 0.13);
  --shadow:      0 1px 2px rgba(0, 30, 60, .06), 0 8px 24px rgba(0, 30, 60, .05);
  /* Landing cards and pills (Neil, 2026-10-24): flat white with a blue
     outline and gray titles in light mode; dark keeps the shell's look. */
  --card-rule:   #0067b9;
  --card-shadow: none;
  --card-title:  #707070;
  --card-foot:   #0067b9;
  /* The answer-key half of a practice-set pair (Neil, 2026-10-24): gray,
     in both modes. */
  --key-ink:     #707070;
  --key-rule:    rgba(112, 112, 112, 0.35);
  --key-soft:    rgba(112, 112, 112, 0.09);
  --fig-far:     #cddce9;
  --fig-line:    #5a5a5a;

  /* Table skin. custom.css styles the tables and used to hardcode these as
     light-mode literals -- including `color:#000 !important` on every cell,
     which went invisible the moment the palette flipped. Tokens instead. */
  --tbl-head:     #0067b9;
  --tbl-head-ink: #ffffff;
  --tbl-stripe:   #f5f9fc;
  --tbl-hover:    #f0f5fa;
  --tbl-rule:     #b9d2e5;

  --step--1: clamp(.82rem, .78rem + .18vw, .95rem);
  --step-0:  clamp(1rem, .93rem + .34vw, 1.24rem);
  --step-1:  clamp(1.2rem, 1.08rem + .6vw, 1.6rem);
  --step-2:  clamp(1.6rem, 1.32rem + 1.4vw, 2.5rem);
  --step-3:  clamp(2.1rem, 1.6rem + 2.5vw, 3.7rem);

  --sans: "Source Sans Pro", "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --math: "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* system dark, unless an explicit light choice is stamped */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --blue:        #58a7e5;
    --blue-dark:   #8cc4ee;
    --red:         #e4737a;
    --ink:         #e9eef4;
    --muted:       #93a3b5;
    --ground:      #0d1826;
    --panel:       #14243a;
    --rule:        rgba(140, 196, 238, 0.18);
    --rule-strong: rgba(140, 196, 238, 0.45);
    --soft:        rgba(88, 167, 229, 0.10);
    --soft-red:    rgba(228, 115, 122, 0.10);
    --rail:        rgba(140, 196, 238, 0.16);
    --shadow:      0 1px 2px rgba(0, 0, 0, .35), 0 10px 30px rgba(0, 0, 0, .30);
    --card-rule:   rgba(140, 196, 238, 0.18);
    --card-shadow: var(--shadow);
    --card-title:  #b4d4f0;
    --card-foot:   #1d7a4d;
    --key-ink:     #a9b4c0;
    --key-rule:    rgba(169, 180, 192, 0.35);
    --key-soft:    rgba(169, 180, 192, 0.10);
    --fig-far:     #1d3350;
    --fig-line:    #93a3b5;
    --tbl-head:     #17334f;
    --tbl-head-ink: #cfe4f6;
    --tbl-stripe:   rgba(140, 196, 238, 0.07);
    --tbl-hover:    rgba(140, 196, 238, 0.13);
    --tbl-rule:     rgba(140, 196, 238, 0.20);
    --accent-ink:  #d9a04a;
    --accent-rule: rgba(217, 160, 74, 0.38);
    --accent-soft: rgba(217, 160, 74, 0.13);
  }
}
/* explicit dark choice wins over a light OS */
:root[data-theme="dark"] {
  --blue:        #58a7e5;
  --blue-dark:   #8cc4ee;
  --red:         #e4737a;
  --ink:         #e9eef4;
  --muted:       #93a3b5;
  --ground:      #0d1826;
  --panel:       #14243a;
  --rule:        rgba(140, 196, 238, 0.18);
  --rule-strong: rgba(140, 196, 238, 0.45);
  --soft:        rgba(88, 167, 229, 0.10);
  --soft-red:    rgba(228, 115, 122, 0.10);
  --rail:        rgba(140, 196, 238, 0.16);
  --shadow:      0 1px 2px rgba(0, 0, 0, .35), 0 10px 30px rgba(0, 0, 0, .30);
  --card-rule:   rgba(140, 196, 238, 0.18);
  --card-shadow: var(--shadow);
  --card-title:  #b4d4f0;
  --card-foot:   #1d7a4d;
  --key-ink:     #a9b4c0;
  --key-rule:    rgba(169, 180, 192, 0.35);
  --key-soft:    rgba(169, 180, 192, 0.10);
  --fig-far:     #1d3350;
  --fig-line:    #93a3b5;
  --tbl-head:     #17334f;
  --tbl-head-ink: #cfe4f6;
  --tbl-stripe:   rgba(140, 196, 238, 0.07);
  --tbl-hover:    rgba(140, 196, 238, 0.13);
  --tbl-rule:     rgba(140, 196, 238, 0.20);
  --accent-ink:  #d9a04a;
  --accent-rule: rgba(217, 160, 74, 0.38);
  --accent-soft: rgba(217, 160, 74, 0.13);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Mobile browsers paint a gray rectangle over whatever was tapped, square
   corners and all, which looks broken on the pills and cards. Every control
   here already answers a tap with its own hover or active state, and keyboard
   focus keeps its :focus-visible ring, so the rectangle goes site-wide. */
a, button, summary, [role="button"] { -webkit-tap-highlight-color: transparent; }

/* The bd-article compatibility class on the shell containers brings
   custom.css's `body, .bd-article { font-family: 'Inter' }` back with it, and
   (0,1,0) beats a bare `body`. Reassert the shell's stack at matching
   specificity -- Inter is not shipped any more, so leaving it in place means a
   generic fallback. Goes when custom.css is rewritten. */
.bd-article { font-family: var(--sans); }

/* ---- type ----------------------------------------------------------- */
.eyebrow {
  font-size: var(--step--1);
  letter-spacing: .13em;
  text-transform: uppercase;
  font-weight: 700;
  color: var(--muted);
  margin: 0;
}
h1 {
  font-size: var(--step-3);
  line-height: 1.02;
  letter-spacing: -.02em;
  font-weight: 700;
  color: var(--blue-dark);
  margin: 0;
  text-wrap: balance;
}
h2 {
  font-size: var(--step-2);
  line-height: 1.08;
  letter-spacing: -.015em;
  font-weight: 700;
  color: var(--blue-dark);
  margin: 0 0 .2em;
  text-wrap: balance;
  padding-bottom: .3em;
  border-bottom: 2px solid var(--rule-strong);
}
p { margin: 0; max-width: 62ch; }
.lede { font-size: var(--step-1); line-height: 1.35; max-width: 34ch; }
strong { font-weight: 700; color: var(--blue-dark); }
em.term { font-style: italic; color: var(--ink); }

ul { margin: 0; padding-left: 1.1em; display: flex; flex-direction: column; gap: .55em; max-width: 62ch; }
li::marker { color: var(--blue); }


/* ---- callout -------------------------------------------------------- */
.callout {
  background: var(--soft);
  border-left: 4px solid var(--blue);
  border-radius: 3px;
  padding: clamp(.8rem, 2vh, 1.15rem) clamp(1rem, 2.4vw, 1.5rem);
  display: flex; flex-direction: column; gap: .6em;
}
.callout p { max-width: 58ch; }

/* ---- hand-set math -------------------------------------------------- */
.eq {
  font-family: var(--math);
  font-size: clamp(1.05rem, .9rem + .7vw, 1.5rem);
  line-height: 1.35;
  text-align: center;
  padding: clamp(.7rem, 2vh, 1.3rem) 0;
  overflow-x: auto;
  color: var(--ink);
}
.eq i, .var { font-style: italic; font-family: var(--math); }

/* A fraction is ONE inline-block with two block children. Both children then
   take the box width, so the rule spans the wider of numerator and
   denominator -- not just the denominator, which is what a border on the
   lower half gives you. Equal line-heights and mirrored padding put the rule
   exactly at the box's vertical center, which is what makes
   `vertical-align: middle` land it on the math axis, level with the `=`.
   No magic offset. */
.frac {
  display: inline-block;
  vertical-align: middle;
  text-align: center;
  margin: 0 .08em;
}
.frac > span { display: block; line-height: 1.2; padding-left: .22em; padding-right: .22em; }
.frac > span:first-child { padding-bottom: .1em; border-bottom: 1px solid currentColor; }
.frac > span:last-child  { padding-top: .1em; }

/* parens around a fraction: centerd on the same axis, and kept out of the
   line box so they do not stretch the row */
.paren {
  display: inline-block; vertical-align: middle;
  font-size: 2.05em; line-height: .62; font-family: var(--math);
  margin: 0 .02em;
}
sub, sup { font-size: .7em; line-height: 0; }
sub { vertical-align: -.28em; }
sup { vertical-align: .5em; }

/* ---- table ---------------------------------------------------------- */
.tablewrap { overflow-x: auto; }
table {
  border-collapse: collapse;
  width: 100%;
  font-size: clamp(.86rem, .8rem + .32vw, 1.06rem);
  font-variant-numeric: tabular-nums;
}
caption {
  text-align: left; color: var(--muted); font-size: var(--step--1);
  padding-bottom: .5em;
}
th {
  text-align: left; font-weight: 700; color: var(--blue-dark);
  border-bottom: 2px solid var(--rule-strong);
  padding: .45em .8em .45em 0;
  font-size: var(--step--1); letter-spacing: .05em; text-transform: uppercase;
}
/* The header's uppercase must not reach its math: MathJax draws glyphs as
   text, so text-transform turned eta_t into a capital Eta and lambda into
   Lambda in every header that carried a symbol. */
th mjx-container { text-transform: none; letter-spacing: normal; }
td { padding: .5em .8em .5em 0; border-bottom: 1px solid var(--rule); vertical-align: top; }
/* A phone column is ~343px: five columns of padding, tracking, and an
   uppercase header pushed the L15 taper table to 449px, its last column
   off screen in present mode. Tighten the gutters and drop the tracking. */
@media (max-width: 480px) {
  th { letter-spacing: 0; padding-right: .45em; }
  td { padding-right: .45em; }
}
tr:last-child td { border-bottom: none; }
td.res { font-weight: 700; color: var(--blue-dark); }
td .m { font-family: var(--math); font-style: italic; }

/* ---- figure --------------------------------------------------------- */
figure { margin: 0; display: flex; flex-direction: column; gap: .6em; }
figure svg { width: 100%; height: auto; max-height: 58vh; display: block; }
figcaption { color: var(--muted); font-size: var(--step--1); max-width: 62ch; }
.f-far   { fill: var(--fig-far); }
.f-line  { fill: var(--fig-line); }
.s-line  { stroke: var(--fig-line); }
.f-blue  { fill: var(--blue); }
.f-navy  { fill: var(--blue-dark); }
.s-navy  { stroke: var(--blue-dark); }
.f-red   { fill: var(--red); }
.s-red   { stroke: var(--red); }
.fig-t   { font-family: var(--sans); }

/* ---- document links -------------------------------------------------- */
/* Practice sets, solutions, lab sheets and handouts are downloads, not prose
   links. They shipped as raw <a> in the markdown with nothing styling them:
   on a reading page that inherited `.page a`, but on a frame lesson -- where
   most of them live -- nothing did, so they rendered in the user agent's own
   #0000EE with an underline. Against the dark ground (#0d1826) that is very
   nearly invisible, and it was never the site's look in either mode.

   The shell already has a vocabulary for "go get this": the title frame's
   CTA pills and the deck admonition's chips. A download link is the same
   gesture, so it gets the same pill. Tokens only -- the dark palette on
   :root then carries it with no second rule to keep in sync. */
.doc-link {
  /* A lined page for the problem set; `.doc-key` swaps in the checked one. */
  --doc-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 15'%3E%3Cpath d='M7 0H1.6A1.6 1.6 0 0 0 0 1.6v11.8A1.6 1.6 0 0 0 1.6 15h8.8a1.6 1.6 0 0 0 1.6-1.6V5H8a1 1 0 0 1-1-1V0zm1.2.35V3.8h3.45L8.2.35zM2.9 7.6h6.2v1.2H2.9V7.6zm0 2.8h6.2v1.2H2.9v-1.2z'/%3E%3C/svg%3E");
  display: inline-flex; align-items: center; gap: .5em;
  padding: .38em .95em; border-radius: 999px;
  font-size: var(--step--1); font-weight: 600; line-height: 1.35;
  text-decoration: none; white-space: nowrap;
  color: var(--blue-dark); background: var(--panel);
  border: 1px solid var(--rule);
  transition: color .12s ease, background .12s ease, border-color .12s ease;
}

/* The glyph is a mask rather than a background image so it inherits
   `currentColor` and follows the link through hover and the dark palette.
   A two-tone SVG would need a dark twin; this does not. */
.doc-link::before {
  content: ""; flex: none; width: .74em; height: .92em;
  background: currentColor;
  -webkit-mask: var(--doc-glyph) center / contain no-repeat;
          mask: var(--doc-glyph) center / contain no-repeat;
}
.doc-link:hover {
  color: var(--blue); background: var(--soft); border-color: var(--rule-strong);
}
.doc-link:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* The solutions half of the pair reads as the answer key, so it carries the
   key color rather than its blue: a quieter gray in both modes. */
.doc-link.doc-key {
  color: var(--key-ink); border-color: var(--key-rule);
  --doc-glyph: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 15'%3E%3Cpath d='M7 0H1.6A1.6 1.6 0 0 0 0 1.6v11.8A1.6 1.6 0 0 0 1.6 15h8.8a1.6 1.6 0 0 0 1.6-1.6V5H8a1 1 0 0 1-1-1V0zm1.2.35V3.8h3.45L8.2.35zM5.15 11.9 2.6 9.35l.88-.88 1.67 1.67 3.52-3.52.88.88-4.4 4.4z'/%3E%3C/svg%3E");
}
.doc-link.doc-key:hover {
  color: var(--key-ink); background: var(--key-soft);
  border-color: var(--key-ink);
}

/* A row of them, not a bulleted list. `:class: doc-links` on the frame (or
   on any container) turns the list it wraps into the row; the chips wrap to
   the next line on a phone rather than overflowing the frame. */
.doc-links ul, .doc-links ol {
  list-style: none; margin: .1em 0 0; padding: 0; max-width: none;
  /* `flex-direction` has to be said out loud: the bare `ul` rule above sets
     column, and overriding only `display` leaves that in place -- the chips
     then stack one per line with the row's gap between them, which looks
     deliberate enough that it is easy to miss. */
  display: flex; flex-flow: row wrap; align-items: center; gap: .55rem .6rem;
}
.doc-links li { margin: 0; }
.doc-links li > p { margin: 0; }

/* In a table cell the pill is the cell's whole content, so it drops the fill
   and the border and keeps only the glyph and the color -- 82 bordered pills
   down the materials page read as a form, not a list of downloads. The key
   variant has to be named here too: `.doc-link.doc-key` outranks a bare
   `td .doc-link`, and without it the solutions column keeps its border while
   the problem-set column loses one. */
td .doc-link, th .doc-link,
td .doc-link.doc-key, th .doc-link.doc-key {
  padding: .2em .6em; background: transparent; border-color: transparent;
}
td .doc-link:hover, th .doc-link:hover {
  background: var(--soft); border-color: var(--rule);
}
td .doc-link.doc-key:hover, th .doc-link.doc-key:hover {
  background: var(--key-soft); border-color: var(--key-rule);
}

/* ---- progress rail -------------------------------------------------- */
.rail {
  position: fixed; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--rail); z-index: 40; pointer-events: none;
}
.rail span {
  display: block; width: 100%; background: var(--blue);
  height: 0; transition: height .25s ease;
}
@media (prefers-reduced-motion: reduce) { .rail span { transition: none; } }

/* ---- HUD ------------------------------------------------------------ */
/* One pill, centerd in the window.

   Three separately-pinned groups looked symmetric and were not: with
   `space-between` the middle group is centerd between the other two, not in
   the window, so it sat off center by exactly half the difference between the
   outer groups' widths -- 16px on a 1280px desktop, and it moves whenever any
   label changes. Spread across a wide window they also stopped reading as one
   bar.

   Centering one pill is immune to both. `translateX(-50%)` puts it dead center
   at every width whatever it contains, so no label can ever knock it off, and
   the pill is only as wide as its contents -- there is no gap to judge. The
   groups survive as plain flex runs inside it, separated by a rule. */
.hud {
  position: fixed; z-index: 45;
  left: 50%; transform: translateX(-50%);
  bottom: clamp(.7rem, 2vh, 1.4rem);
  /* max-content is load-bearing. A fixed element with `left` and no `right`
     shrink-to-fits against what is left of the window -- here 50vw -- so the
     bar wrapped onto two rows at every phone width while its contents needed
     241px and 304px were available. */
  width: max-content; max-width: calc(100vw - 1.4rem);
  /* A grid, not a flex row, and this is the whole trick: `1fr auto 1fr` makes
     the two outer columns EQUAL by definition, whatever is in them. Under
     max-content sizing both settle at the wider one's width, so the middle
     run sits at the pill's true center -- and stays there when `1/27` becomes
     `12/27` or the site label changes. Flex can center the pill in the window
     but cannot center a run inside the pill without measuring. */
  display: grid; grid-template-columns: 1fr auto 1fr;
  align-items: center; column-gap: .55rem;
  font-family: var(--mono); font-size: .74rem; color: var(--muted);
  background: color-mix(in srgb, var(--panel) 88%, transparent);
  border: 1px solid var(--rule); border-radius: 999px;
  padding: .32rem .72rem; box-shadow: var(--shadow);
  backdrop-filter: blur(6px);
}
/* min-width:0 so a column can be narrower than its content rather than
   pushing the pill past max-width at 320px. */
.hud-group {
  display: flex; align-items: center; justify-content: center;
  gap: .55rem; min-width: 0;
}
/* Both rules belong to the MIDDLE run, one on each side. Hanging them off the
   groups that follow a group put the second rule at the left edge of the
   right-hand column instead of the right edge of the middle one -- the pill
   was then symmetric in its columns and visibly not in its rules. Owned by the
   middle run they cannot drift, and they disappear with it. */
.hud-mid::before, .hud-mid::after {
  content: ""; flex: none; width: 1px; height: .95em;
  background: var(--rule-strong);
}
/* A reading page has nothing to put in the middle run, so the run IS the rule:
   one line, dead center, with the site on its left and the contents on its
   right. Two rules around a zero-width run would read as `ECE 343 || 34%`.
   The template must emit that div with no whitespace inside it -- a text node
   is enough to make `:empty` fail and both rules come back. */
.hud-mid:empty::after { content: none; }
.hud b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }

/* The counter doubles as the contents button, so it has to read as a value
   and behave as a control: the number keeps the ink weight it had as plain
   text, and the affordance shows up on hover and focus. */
.hud button.counter { font: inherit; letter-spacing: 0; }
.hud button.counter b { color: var(--ink); font-weight: 700; font-variant-numeric: tabular-nums; }
.hud button.counter:hover b { color: var(--blue-dark); }
.hud button.counter[aria-pressed="true"] { color: var(--blue-dark); background: var(--soft); }
.hud button.counter[aria-pressed="true"] b { color: var(--blue-dark); }
/* The site button carries the site's name, so it keeps the mark's voice --
   the same mono, the same wide tracking, the same blue. It is the only
   colored thing in the bar, which is what makes the bar readable as "you are
   here, and here is everything else". */
/* Everything but the line-height: it inherits the bar's, because the site
   button is alone in its group and a shorter button makes a shorter pill --
   three pills of different heights read worse than the one lopsided pill this
   replaced. */
.hud button.site {
  font-family: var(--mono); font-weight: 700; font-size: .74rem;
  letter-spacing: .14em; color: var(--blue);
}
.hud button.site:hover { color: var(--blue-dark); background: var(--soft); }
.hud button.site[aria-expanded="true"] { color: var(--blue-dark); background: var(--soft); }
.hud button {
  font: inherit; color: var(--muted); background: none; border: none;
  cursor: pointer; padding: .1rem .3rem; border-radius: 4px; letter-spacing: .04em;
}
.hud button:hover { color: var(--ink); background: var(--soft); }
.hud button:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.hud button[aria-pressed="true"] { color: var(--red); background: var(--soft-red); font-weight: 700; }

/* ---- HUD nav links --------------------------------------------------- */
/* These are real links, so they arrive underlined and blue. Make them read
   as part of the HUD. */
/* ---- HUD popovers ---------------------------------------------------- */
/* Children of .hud, which is fixed and so a containing block. Each panel is
   placed under the button that opened it; shell.js sets `left` on open, and
   the value here is only what a panel would show for the frame before that
   runs. */
.hud .pop {
  position: absolute; bottom: calc(100% + .5rem); left: 0;
  display: flex; flex-direction: column; gap: .1rem;
  min-width: max-content; max-width: min(22rem, calc(100vw - 2rem));
  padding: .3rem; border-radius: 12px;
  background: color-mix(in srgb, var(--panel) 94%, transparent);
  border: 1px solid var(--rule); box-shadow: var(--shadow);
  backdrop-filter: blur(6px);
}
/* Load-bearing: the class above sets `display: flex`, which beats the UA
   stylesheet's `[hidden] { display: none }` on specificity. Without this the
   panels never close. */
.hud .pop[hidden] { display: none; }

/* The current site is a link to where you already are. It stays in the list --
   dropping it would make the panel a list of elsewhere with no "here" -- but
   it is lit rather than offered. */
.hud .pop a[aria-current="page"] {
  color: var(--blue-dark); background: var(--soft); font-weight: 700;
}

/* (0,2,1), because plain `.hud button` is (0,1,1) and would otherwise win --
   the same trap that made .index-close span the whole viewport. */
.hud .pop button, .hud .pop a {
  font: inherit; color: var(--muted); background: none; border: none;
  cursor: pointer; text-decoration: none; text-align: left; white-space: nowrap;
  display: flex; align-items: center; justify-content: space-between; gap: 1.4rem;
  padding: .4rem .55rem; border-radius: 7px; letter-spacing: .04em;
}
.hud .pop button:hover, .hud .pop a:hover { color: var(--ink); background: var(--soft); }
.hud .pop button:focus-visible, .hud .pop a:focus-visible {
  outline: 2px solid var(--blue); outline-offset: -2px;
}
.hud .pop button[aria-pressed="true"] {
  color: var(--red); background: var(--soft-red); font-weight: 700;
}
.hud .pop-empty { padding: .4rem .55rem; color: var(--muted); opacity: .7; }
/* A popover row that is a CHOICE, not an action: it stacks a short note under
   the label and lights whichever one is in force. Without the lit state a
   panel of options reads as a panel of buttons that do nothing. */
.hud .pop button.choice {
  flex-direction: column; align-items: flex-start; gap: .1rem;
}
.hud .pop-note {
  font-size: .92em; opacity: .7; letter-spacing: 0; text-transform: none;
  font-weight: 400; color: var(--muted);
}
.hud .pop button[aria-pressed="true"] {
  color: var(--blue-dark); background: var(--soft); font-weight: 700;
}
.hud .pop button[aria-pressed="true"] .pop-note { color: var(--blue-dark); }
.hud kbd {
  font-family: var(--mono); font-size: .82em; color: var(--muted);
  border: 1px solid var(--rule); border-radius: 3px; padding: 0 .3em;
}
/* A laser left running is invisible once the panel closes, so light the button
   that opens it. */
.hud button[data-active="true"] {
  color: var(--red); background: var(--soft-red); font-weight: 700;
}

/* ---- HUD on a phone -------------------------------------------------- */
/* Nothing is dropped -- the panels carry everything that does not fit. The
   groups tighten and square off; they stay pinned left, center and right,
   which is the whole point of splitting them. */
@media (max-width: 46rem) {
  .hud {
    max-width: calc(100vw - 1rem);
    column-gap: .3rem; padding: .34rem .4rem;
  }
  .hud-group { gap: .35rem; }
}

/* The intro's module cards and deliverable cards were authored for a scrolling
   page. Stacked in a frame on a phone they run past the screen -- 761px into a
   709px budget -- so they tighten up inside one. */
@media (max-width: 44rem) {
  .frame .block-card, .frame .deliverable-card { padding: .6rem .85rem; }
  .frame .block-card { padding: .6rem 1.2rem; border-radius: 22px; }
  .frame .block-card h3, .frame .deliverable-card h3 { font-size: .98rem; }
  .frame .block-card p, .frame .deliverable-card p { font-size: .84rem; line-height: 1.4; }
  .frame .block-grid, .frame .deliverable-cards { gap: .45rem; }
}

/* ---- help card and first-visit hint ---------------------------------- */
/* One screen naming each control in the bar, in the bar's own order. Opened
   by `?`, by Help in the site menu or tools, and by the hint. */
.help {
  position: fixed; inset: 0; z-index: 80;
  display: flex; align-items: center; justify-content: center;
  padding: 1rem max(1rem, env(safe-area-inset-right, 0px)) 1rem max(1rem, env(safe-area-inset-left, 0px));
  background: color-mix(in srgb, var(--ground) 80%, transparent);
  backdrop-filter: blur(6px); overflow-y: auto;
}
.help[hidden] { display: none; }
.help-card {
  position: relative; width: min(34rem, 100%); max-height: calc(100vh - 2rem); overflow-y: auto;
  background: var(--panel); color: var(--ink);
  border: 1px solid var(--card-rule); border-radius: 22px; box-shadow: var(--shadow);
  padding: 1.4rem 1.6rem 1.2rem;
  font-family: var(--sans); font-size: 1rem; line-height: 1.45;
}
.help-card h2 { margin: 0 2.2rem .2rem 0; padding: 0; border: 0; font-size: 1.35rem; color: var(--blue-dark); }
.help-card h3 { margin: 1rem 0 .2rem; font-size: .8rem; letter-spacing: .1em; text-transform: uppercase; color: var(--muted); }
.help-card p { margin: 0; }
.help-lead { color: var(--muted); margin-bottom: .8rem !important; }
.help-list {
  display: grid; grid-template-columns: max-content minmax(0, 1fr);
  gap: .65rem .9rem; margin: 0; align-items: baseline;
}
.help-list dt { margin: 0; }
.help-list dd { margin: 0; }
.help-chip {
  display: inline-block; font: .74rem var(--mono); color: var(--blue-dark);
  border: 1px solid var(--rule); border-radius: 999px; padding: .15rem .6rem;
  background: var(--panel); white-space: nowrap;
}
.help-card kbd, .help-hint kbd {
  font: .78em var(--mono); color: var(--muted);
  border: 1px solid var(--rule); border-radius: 4px; padding: 0 .3em;
}
.help-foot { margin-top: 1rem !important; font-size: .85rem; color: var(--muted); }
.help-close {
  position: absolute; top: .8rem; right: .8rem;
  width: 2.1rem; height: 2.1rem; padding: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font: 400 1.4rem/1 var(--sans); cursor: pointer;
  background: var(--soft); border: 1px solid var(--rule); color: var(--muted);
}
.help-close:hover { color: var(--ink); }
.help-close:focus-visible, .help-hint button:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
@media (max-width: 30rem) {
  .help-card { padding: 1.1rem 1.1rem 1rem; }
  .help-list { grid-template-columns: minmax(0, 1fr); gap: .15rem; }
  .help-list dd { margin-bottom: .55rem; }
}

/* A phone has no keyboard: drop the keys, and say where the menu item is. */
.help-touch { display: none; }
@media (hover: none) and (pointer: coarse) {
  .help-keys { display: none; }
  .help-touch { display: inline; }
}

/* The hint sits just above the bar and leaves with it. */
.help-hint {
  position: fixed; z-index: 44; left: 50%; transform: translateX(-50%);
  bottom: calc(clamp(.7rem, 2vh, 1.4rem) + 2.6rem);
  display: flex; align-items: center; gap: .2rem; max-width: calc(100vw - 2rem);
  background: var(--panel); border: 1px solid var(--card-rule); border-radius: 999px;
  box-shadow: var(--shadow); padding: .2rem .25rem .2rem .9rem; white-space: nowrap;
  font: .85rem var(--sans); color: var(--ink);
}
.help-hint[hidden], html[data-mode="present"] .help-hint, body.chrome-away .help-hint { display: none; }
.help-hint button {
  font: inherit; color: var(--blue-dark); background: none; border: 0; cursor: pointer;
  padding: .25rem .2rem; text-align: left;
}
.help-hint button:hover { text-decoration: underline; }
.help-hint button.help-hint-close {
  color: var(--muted); font-size: 1.15rem; line-height: 1; width: 1.9rem; height: 1.9rem;
  border-radius: 50%; text-align: center; padding: 0;
}
.help-hint button.help-hint-close:hover { background: var(--soft); text-decoration: none; }

/* The site menu's Help item reads as a button among links; a rule sets it off. */
.hud .pop button.pop-help { border-top: 1px solid var(--rule); border-radius: 0 0 7px 7px; margin-top: .2rem; padding-top: .5rem; }

/* ---- jump overlay: the shell's one navigational surface -------------- */
/* The panel chrome is shared. The frame-numbered list inside it lives in
   frames.css; the reading page's page-local TOC lives in page.css. */
.index {
  position: fixed; inset: 0; z-index: 70; display: none;
  background: color-mix(in srgb, var(--ground) 94%, transparent);
  backdrop-filter: blur(10px); overflow-y: auto;
  padding: clamp(2rem, 6vh, 4rem) clamp(1.2rem, 5vw, 4rem);
}
.index.on { display: block; }
/* `.index button` above is (0,1,1) and sets width:100% and display:flex, so
   this needs the type selector too or the close button spans the viewport and
   the glyph lands in the top-left corner. */
.index button.index-close {
  position: fixed; top: clamp(.6rem, 2vh, 1.2rem); right: clamp(.6rem, 3vw, 1.4rem);
  z-index: 1; font: 400 1.5rem/1 var(--sans); cursor: pointer;
  width: 2.2rem; height: 2.2rem; padding: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--soft); border: 1px solid var(--rule); color: var(--muted);
}
.index button.index-close:hover { color: var(--ink); background: var(--soft); }
.index button.index-close:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.index h2 { margin-bottom: .8em; }

/* ---- jump overlay: this lesson + the whole course -------------------- */
.index-cols {
  display: grid; gap: clamp(1.2rem, 4vw, 3rem);
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
  align-items: start;
  /* Flex item in .wrap: without this its automatic minimum is min-content,
     which no amount of column sizing below can claw back. */
  min-width: 0;
}
.index-cols > div { min-width: 0; }

/* Side by side, the course goes on the left and this page on the right --
   the whole course first, then where you are inside it.
   The DOM keeps "this page" first, because that is the order a phone wants
   when the two stack, and it is the order a screen reader should hear:
   immediate context before the whole catalogue. So the swap is visual, and
   only at the width where there are two columns to swap. */
/* grid-row is load-bearing: with an explicit column and an automatic row,
   auto-placement never backs up -- it put "this page" at row 1 column 2 and
   then dropped the course to row 2 column 1, one under the other. */
.index-cols > .index-here { grid-column: 2; grid-row: 1; }
.index-cols > .index-toc  { grid-column: 1; grid-row: 1; }
@media (max-width: 52rem) {
  .index-cols { grid-template-columns: 1fr; }
  /* One column: back to DOM order, this page on top. Row must go automatic
     again or both would be pinned to the same row and overlap. */
  .index-cols > .index-here, .index-cols > .index-toc {
    grid-column: 1; grid-row: auto;
  }
}

.index-toc ul { list-style: none; margin: 0; padding: 0; display: block; max-width: none; }
.index-toc ul ul { padding-left: .9rem; margin: .15em 0 .5em; }
.index-toc li { margin: 0; }
.index-toc a {
  display: block; padding: .3rem .5rem; border-radius: 4px;
  color: var(--ink); text-decoration: none;
  font-size: calc(var(--step-0) * .92); line-height: 1.3;
}
.index-toc a:hover { background: var(--soft); color: var(--blue-dark); }
.index-toc a:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.index-toc li.current > a, .index-toc a.current {
  color: var(--blue-dark); font-weight: 700; background: var(--soft);
}
.index-toc .caption, .index-toc p.caption {
  font-size: var(--step--1); letter-spacing: .1em; text-transform: uppercase;
  font-weight: 700; color: var(--muted); margin: .9em 0 .2em; padding-left: .5rem;
}
