/* =========================================================================
   Silicon From Scratch — alu.css
   Page-specific components for the ALU lesson pages. Built entirely on
   the design tokens defined in main.css (loaded first): colors, fonts, spacing
   scale, radii, shadows, --ease. Keep this file to things the home page never
   needed — figures, data tables, code blocks, the doc hero, disclosures.
   ========================================================================= */

/* ------------------------------------------------------------------ *
   DOC HERO — a calmer, left-aligned hero for a content page
 * ------------------------------------------------------------------ */
.doc-hero {
  /* Extra top padding nudges the whole hero (diagram + text together) down a
     little so it clears the top bar with more breathing room. */
  padding-block: clamp(var(--space-6), 10vw, var(--space-7))
                 clamp(var(--space-5), 7vw, var(--space-6));
  /* A touch wider than the standard 72rem container so the diagram has room to
     grow once the text column is pinned to the title's width. */
  max-width: 80rem;
  display: grid;
  /* Left column hugs the title's width (~36rem) so the intro paragraph is no
     wider than the title; the diagram takes all the remaining width on the
     right, so it renders larger. */
  grid-template-columns: minmax(0, 36rem) minmax(0, 1fr);
  gap: clamp(var(--space-3), 4vw, var(--space-5));
  align-items: center;
}
.doc-hero__text { max-width: 36rem; }

/* On wide screens, nudge the whole hero — text and diagram together — a little
   left of the centered container so it doesn't crowd the right edge. Gated above
   the 80rem hero width + the shift, so it only applies when there's gutter to
   spare and nothing ever clips off the left. */
@media (min-width: 1344px) {
  .doc-hero { transform: translateX(-2rem); }
}

/* Landing height. On shorter viewports the lesson hero fills the screen, so the
   first content section — its heading included — stays below the fold on landing
   and only fades in on the first scroll (its scroll-reveal trigger sits off-screen
   at load). On taller viewports the hero keeps its natural height, so the first
   section sits within the initial view and fades in on load. Scoped to lesson
   pages (.page-logic). The topbar is fixed, so 100svh maps to the whole viewport. */
@media (max-height: 900px) {
  .page-logic .doc-hero { min-height: 100vh; min-height: 100svh; }
}

/* Balanced variant: the standard side-by-side hero (text + diagram), sized so
   the pair sits as a tight unit centered in the middle of the screen. Unlike the
   ALU overview hero it tightens the image column and centers the two columns as
   a group, and drops the wide-screen leftward nudge. Used on the Logic Gates
   lesson. */
.doc-hero--balanced { margin-inline: auto; max-width: 88rem; }
@media (min-width: 769px) {
  .doc-hero--balanced {
    grid-template-columns: minmax(0, 46rem) minmax(0, 44rem);
    justify-content: center;
  }
  /* Let the title paragraph fill the (now wider) text column. */
  .doc-hero--balanced .doc-hero__text { max-width: none; }
}
@media (min-width: 1344px) {
  .doc-hero--balanced { transform: none; }
}

/* The ALU block symbol sits beside the intro, on a paper card so the white
   drawing reads as a deliberate panel (matching the diagram figures below). */
.doc-hero__art {
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: clamp(var(--space-3), 4vw, var(--space-4));
  box-shadow: var(--shadow-soft);
}
.doc-hero__art img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
/* Logo variant (e.g. the Icarus emblem on the setup page): a small image, so
   center it and don't upscale it to fill the card. */
.doc-hero__art--logo { display: grid; place-items: center; }
.doc-hero__art--logo img { width: auto; max-width: 220px; max-height: 280px; }

/* Numbered steps (setup / how-to instructions) */
.steps {
  margin: var(--space-4) 0 0;
  padding-left: 1.5rem;
  max-width: var(--maxw-prose);
}
.steps li {
  margin-bottom: var(--space-2);
  padding-left: 0.3rem;
  color: var(--soft-ink);
  line-height: 1.6;
}
.steps li::marker {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 500;
}
/* A call-to-action button sitting inside a step, on its own line. */
.step__cta { display: block; margin-top: var(--space-2); }

/* Troubleshooting issues — each gets an accent left rule so the long list of
   problems stays scannable. */
.issue {
  margin-top: var(--space-4);
  padding: 0 0 0 var(--space-3);
  border-left: 2px solid var(--hairline);
  max-width: var(--maxw-prose);
}
.issue h3 { margin-bottom: var(--space-2); }
.issue p { color: var(--soft-ink); margin: 0 0 var(--space-2); }
.issue p:last-child { margin-bottom: 0; }
.issue strong { color: var(--ink); }
.issue .code { margin: var(--space-2) 0; }

/* Checklist — square markers, monospace ticks feel */
.checklist {
  list-style: none;
  margin: var(--space-4) 0 0;
  padding: 0;
  max-width: var(--maxw-prose);
}
.checklist li {
  position: relative;
  padding-left: 2rem;
  margin-bottom: var(--space-2);
  color: var(--soft-ink);
  line-height: 1.6;
}
.checklist li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.35em;
  width: 14px;
  height: 14px;
  border: 2px solid var(--accent);
  border-radius: 4px;
}
.checklist strong { color: var(--ink); }
.backlink {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  color: var(--soft-ink);
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  margin-bottom: var(--space-3);
}
.backlink:hover { color: var(--accent-deep); }

.doc-hero__tag {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--accent);
  margin: 0 0 var(--space-2);
}
.doc-hero h1 { margin-bottom: var(--space-3); }
/* Eyebrow-style page title — matches the "MEET THE PROCESSOR" / "YOUR PATH"
   section labels: a short purple tick followed by soft-gray, tracked, uppercase
   mono text (see main.css .kicker + .buildpath__title). Scoped to the class so
   other doc-hero titles keep the big display face. */
.doc-hero h1.title-eyebrow {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: clamp(1.15rem, 1.6vw, 1.35rem);
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--soft-ink);
  line-height: 1.35;
}
.doc-hero h1.title-eyebrow::before {
  content: "";
  flex: none;
  width: 1.5rem;
  height: 1px;
  background: var(--accent);
}
/* The title text lives in a wrapper so it can wrap independently of the tick.
   It's split into two "keep-together" groups; each never breaks internally, so
   the title either sits on one line or breaks cleanly between the groups
   ("Logic Gates &" / "the 1-bit ALU") — never mid-phrase. */
.title-eyebrow__group { white-space: nowrap; }
.title-eyebrow__text { min-width: 0; text-align: center; }
/* A long lesson title (e.g. "The Full Adder and Ripple Carry Adder") tuned to
   sit on one line on desktop: keep the whole text together and tighten the
   tracking a touch so it fits the hero column. Mobile keeps the default 0.22em
   tracking and the clean two-line break between the groups. */
@media (min-width: 769px) {
  .title-eyebrow--long { letter-spacing: 0.16em; }
  .title-eyebrow--long .title-eyebrow__text { white-space: nowrap; }
}
.doc-hero__lead {
  font-size: 1.25rem;
  color: var(--soft-ink);
  margin-bottom: var(--space-4);
}
/* Bold terms keep the muted prose color so emphasis reads the same grayish
   bold as the rest of the page (see .prose strong / .checklist--quiet strong). */
.doc-hero__lead strong { color: var(--soft-ink); }
.doc-hero__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}
/* Lead-in text sitting beside a hero button. */
.doc-hero__prompt { color: var(--soft-ink); }
/* On the balanced (Logic Gates) hero, center the title (tick + text), the lead
   paragraph's text within its box, and the prompt + skip button under it. */
.doc-hero--balanced h1.title-eyebrow { justify-content: center; }
.doc-hero--balanced .doc-hero__lead { text-align: center; }
.doc-hero--balanced .doc-hero__actions { justify-content: center; }
/* Text-shift variant: everything stays centered per the style guide (title, lead,
   and actions inherit the .doc-hero--balanced centring above); we only nudge the
   whole text column a little toward the outside (left) so the one-line title has
   breathing room from the picture instead of crowding it. Desktop only — mobile
   still stacks and centers per the base balanced rules. */
@media (min-width: 769px) {
  .doc-hero--textshift .doc-hero__text { transform: translateX(-3.75rem); }
}

/* Sub-headings inside long sections (e.g. "The control vector") */
.subhead {
  margin-top: var(--space-5);
  margin-bottom: var(--space-3);
}

/* Small muted note paragraph (e.g. the `make` requirement) */
/* Full-measure prose: let a block span the whole content width instead of the
   44rem reading column (e.g. a section's lead paragraph set across the page). */
.prose--full { max-width: none; }
.prose--note {
  font-family: var(--font-mono);
  font-size: 0.8rem;
  line-height: 1.7;
  color: var(--soft-ink);
  margin-top: var(--space-2);
}

/* ------------------------------------------------------------------ *
   OVERVIEW BAND — a full-bleed 50/50 split. The die shot fills the entire
   right half of the viewport edge-to-edge (object-fit: cover, so no page
   background shows through), and the text sits on its own dark panel on
   the left. Stacks vertically on narrow screens.
 * ------------------------------------------------------------------ */
.overview-band {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
  /* No border or own background: the page's continuous theme-dark glow shows
     through the left panel so this band reads as part of one unbroken surface
     (the die shot still covers the right half edge-to-edge). */
  background: transparent;
}

/* Left half — text sits directly on the shared band background. */
.overview-band__panel {
  display: flex;
  align-items: center;
  /* Center the text within the left half (between the screen edge and the
     picture) rather than hugging the image. */
  justify-content: center;
  padding: clamp(var(--space-4), 5vw, var(--space-7));
}
.overview-band__panel-inner {
  max-width: 40rem;
}
.overview-band__panel .kicker {
  color: #b794f6;
}
.overview-band__panel h2 {
  color: #fdfdfb;
}
.overview-band__panel p {
  color: rgba(253, 253, 251, 0.74);
  margin-bottom: 0;
}
/* Extra breathing room between the two overview paragraphs. */
.overview-band__panel p + p {
  margin-top: 2.5em;
}
.overview-band__panel strong {
  color: #fdfdfb;
  font-weight: 600;
}

/* Right half — the whole image stays visible (contain) over the shared
   band background. Extra padding on the right nudges the image leftward so
   a strip of the background shows along its right edge. */
.overview-band__art {
  margin: 0;
  height: 100%;
  padding-right: clamp(var(--space-3), 4vw, var(--space-6));
  /* Image hugs the left so the shared background shows along its right. */
  display: flex;
  align-items: center;
  justify-content: flex-start;
}
/* The crop window: matches the die-shot's aspect ratio, sits at a fixed size
   and position, and clips whatever spills past it. The zoom happens *inside*
   this frame, so the window itself never moves or resizes. */
.overview-band__crop {
  position: relative;
  width: 100%;
  aspect-ratio: 7849 / 6737;
  max-width: 100%;
  max-height: 100%;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.14);
  border-radius: var(--radius-sm);
}
/* The zoom layer holds the image + highlight so they scale/pan together.
   scroll.js scrubs scale()/x on this element. */
.overview-band__zoom {
  position: absolute;
  inset: 0;
  /* Zoom focal point — column 1, row 3 core. Tweak these two %s to re-aim
     the zoom (x across, y down). */
  transform-origin: 4% 53%;
}
.overview-band__zoom img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Highlight over the integer-execution block of the zoomed core. Positioned
   as a %-of-image rectangle so it rides along with the zoom. The numbers are
   derived from the labeled floorplan (ccd-dieshot-bottom-left-detail.jpg);
   nudge left/top/width/height to re-aim. Hidden until scroll.js reveals it. */
.overview-band__hl {
  position: absolute;
  left: 12.95%;
  top: 47.45%;
  width: 6.45%;
  height: 5.99%;
  background: rgba(205, 180, 242, 0.45); /* translucent light-purple fill */
  opacity: 0;
  pointer-events: none;
  /* Body = lower rectangle + square bump on the right of its top edge; a skinny
     rectangle hanging below the bottom (left edge out to ~2/3 across); plus a
     box jutting right from the bottom-right corner. Box-local %s. */
  clip-path: polygon(
    0% 39.2%, 44.2% 39.2%, 44.2% 0%, 78.3% 0%,
    78.3% 73.3%, 100% 73.3%, 100% 88.3%, 53% 88.3%,
    53% 100%, 0% 100%
  );
}
/* Label centered in the main rectangle of the highlight. Sized in image-space
   px (tiny) so the zoom magnifies it to a legible size. */
.overview-band__hl-text {
  position: absolute;
  left: 0;
  right: 0;
  top: 63%;
  transform: translate(-5px, -50%);
  text-align: center;
  font-family: var(--font-mono);
  font-weight: 500;
  font-size: 3.2px;
  line-height: 1.1;
  letter-spacing: 0.01em;
  color: #fdfdfb;
  text-shadow: 0 0 1.5px rgba(20, 20, 26, 0.7);
}

@media (max-width: 760px) {
  .overview-band {
    grid-template-columns: 1fr;
    min-height: 0;
  }
  .overview-band__art {
    padding: var(--space-3);
    justify-content: center;
  }
  .overview-band__crop {
    width: 100%;
    height: auto;
  }
}

/* ------------------------------------------------------------------ *
   FIGURES — the hand-drawn schematics sit on a paper card so the white
   drawing background blends into a deliberate panel rather than floating.
 * ------------------------------------------------------------------ */
.figure {
  margin: var(--space-5) 0 0;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: clamp(var(--space-2), 3vw, var(--space-4));
  box-shadow: var(--shadow-soft);
}
.figure img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
  margin-inline: auto;
}
/* Final Upgrade: the slice diagram on the basic image figure, centered, with two
   "See the code on GitHub" cards below it — one under each slice. */
/* The slice diagram on the basic image figure, with the two GitHub cards a sliver
   below the frame — one under each slice. */
.slice-figure { max-width: 66rem; margin-inline: auto; }
.slice-links {
  --slice-gap: clamp(var(--space-2), 3vw, var(--space-3));
  max-width: 66rem;
  margin: var(--slice-gap) auto 0;        /* same gap below the image as between the cards */
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--slice-gap);
}
.slice-links .code-card { margin-top: 0; }

/* "Not less than" (≮): a < with a diagonal slash drawn on top via a pseudo-element,
   so it renders in any font — the precomposed glyph and the combining-slash overlay
   don't in our type stack, especially inside a monospace <code>. */
.nless { position: relative; display: inline-block; }
.nless::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 0.8em;
  height: 0.09em;
  background: currentColor;
  transform: rotate(-55deg);
}
/* The ripple-carry diagram is tall/portrait — cap its width so it doesn't
   tower over the reading column. */
.figure--tall img { max-width: 460px; }
.figure figcaption {
  margin-top: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--hairline);
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--soft-ink);
  text-align: left;
}

/* Wider content container — lets a section reach further toward the screen
   edges than the default 72rem measure. */
.container--wide { max-width: 88rem; }

/* Body text beside a small figure: text on the left, a compact image on the
   right, vertically centered. Stacks on narrow screens. */
.split {
  margin-top: var(--space-4);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(var(--space-3), 4vw, var(--space-5));
  align-items: center;
}
.split .prose { margin: 0; }
.split .figure { margin: 0; }
/* Lists inside prose — numbered (e.g. the Final Upgrade "two tricks") or
   bulleted — always ride a shaded card: the same raised surface as the picture
   frames, so each list reads as a distinct block rather than plain body text.
   The extra left padding keeps the (accent-colored) markers inside the frame. */
.prose ol,
.prose ul {
  margin: var(--space-4) 0;
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  padding-left: calc(clamp(var(--space-3), 3vw, var(--space-4)) + 1.6rem);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.prose li { margin-bottom: var(--space-2); }
.prose li:last-child { margin-bottom: 0; }
.prose ol li::marker,
.prose ul li::marker {
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 600;
}
/* Titled prose card: the same shaded surface as the list card, wrapping a short
   passage under a mono accent kicker (e.g. the Stalling / Predicting approaches
   under Control Hazards). */
.term-card {
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  margin: var(--space-4) 0;
}
/* p.term-card__title so the accent color outranks the base `.prose p` soft-ink. */
.term-card p.term-card__title {
  font-family: var(--font-mono);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.82rem;
  color: var(--accent);
  margin: 0 0 var(--space-2);
}
.term-card p:not(.term-card__title) { margin: 0; }
.term-card p:not(.term-card__title) + p { margin-top: var(--space-3); }
/* A list already sitting inside a card (e.g. the edge-card) must not become a
   card-within-a-card — drop the frame and let the parent card hold it. */
.edge-card ol,
.edge-card ul {
  margin: 0;
  padding: 0;
  padding-left: 1.35rem;
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
/* Addition Incoming: pull the heading nearer the copy, and top-align the text
   with the top of the timing widget instead of vertically centering it. */
#addition-incoming .section__head { margin-bottom: var(--space-2); }
#addition-incoming .split { margin-top: var(--space-2); align-items: stretch; }
/* Stacked / mobile: normal paragraph rhythm. */
#addition-incoming .prose p { margin: 0; }
#addition-incoming .prose p + p { margin-top: var(--space-3); }
/* Two-column layout: the copy stretches to the widget's height, but the three
   paragraphs sit as a tight group centered against it (normal paragraph rhythm)
   rather than being spread the full height. */
@media (min-width: 769px) {
  #addition-incoming .prose {
    display: flex;
    flex-direction: column;
    justify-content: center;
  }
  #addition-incoming .prose p + p { margin-top: var(--space-3); }
}
/* "Your First ALU": on desktop the diagram sits beside the explanation (via the
   base .split grid — prose left, card right). On phones it stacks, with the
   diagram first (see it, then read about it). */
.split--stack .prose { max-width: none; }
/* Media-left variant: figure on the left, text on the right. */
.split--media-left { grid-template-columns: auto minmax(0, 1fr); }
/* Media-right variant: text on the left, figure on the right. The figure stays
   first in the source (so it leads on mobile), so `order` swaps the columns on
   desktop — text into the wide left track, figure into the auto right track. */
.split--media-right { grid-template-columns: minmax(0, 1fr) auto; }
.split--media-right .figure { order: 2; }
.split--media-right .prose { order: 1; max-width: none; }
/* Main Control (control-unit lesson): the sc_cpu_control module sits on the left,
   the signal table on the right. Top-align them so both start level, and let the
   code take the flexible column while the table caps at its right track. The note
   sits directly under the code as a caption. */
/* The code sits on the left; the right column holds the explanatory paragraphs
   with the signal table stacked beneath them. Top-align the two columns. */
#main-control .mc-split {
  grid-template-columns: minmax(0, 0.9fr) minmax(0, 1fr);
  align-items: start;
  margin-block: var(--space-4);
}
#main-control .mc-code { min-width: 0; }
#main-control .mc-side { min-width: 0; }
#main-control .mc-side .prose { max-width: none; margin: 0; }
#main-control .mc-table { max-width: none; margin: var(--space-4) 0 0; }
@media (max-width: 768px) {
  /* Stack: code + note first, then the paragraphs, then the table. */
  #main-control .mc-split { grid-template-columns: 1fr; }
}

/* Testing lesson, "Choosing What to Test": the explanatory paragraphs sit on the
   left, the program.mem listing on the right. Top-align them; the listing sizes
   to its own content in the auto track, in a slightly smaller mono so the whole
   program fits. */
#choosing-tests .ct-split { align-items: start; margin-block: var(--space-4);
  grid-template-columns: minmax(0, 1fr) 26rem; }
#choosing-tests .ct-split .prose { max-width: none; margin: 0; }
#choosing-tests .prog-card { margin: 0; width: 100%; }
#choosing-tests .prog-card .code-editor__hl { font-size: 1.08rem; }
@media (max-width: 768px) {
  /* Stack: the explanation first, then the program listing below it. */
  #choosing-tests .ct-split { grid-template-columns: 1fr; }
  #choosing-tests .prog-card { margin-top: var(--space-4); }
}

/* Branch Logic (control-unit lesson): the explanatory paragraphs sit on the
   left, the branch-decode case block on the right. Top-align the two columns. */
#branch-logic .bl-split {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  align-items: start;
  margin-block: var(--space-4);
}
#branch-logic .bl-text { min-width: 0; max-width: none; margin: 0; }
#branch-logic .bl-code { min-width: 0; }
@media (max-width: 768px) {
  /* Stack: the text first, then the code block below it. */
  #branch-logic .bl-split { grid-template-columns: 1fr; }
}

/* ALU Control (control-unit lesson): the ALUOp table sits beside the list card.
   Keep the block's spacing above and below equal — drop the trailing margin of
   the intro paragraph and the list card's own margins so the only gap on each
   side is the split's symmetric --space-4. */
#alu-control .split { margin-block: var(--space-4); }
#alu-control .prose--flow > p:last-child { margin-bottom: 0; }
#alu-control .split .prose ul { margin: 0; }
/* Control Flow diagrams: top-align the text and image (rather than centring the
   shorter one), so each paragraph starts level with its diagram. Keep every gap
   between blocks at the page's standard one-paragraph rhythm (--space-3). */
#control-flow .split { align-items: start; margin-top: var(--space-3); }
#control-flow .split + .prose--full { margin-top: var(--space-3); }
/* Golden Model: the testbench flowchart is a tall portrait sketch, so top-align
   it with the copy — the text should start level with the top of the chart and
   run down the right column (with the golden-model code block below it), rather
   than floating in the vertical middle the way a short diagram would. */
#golden-model .split { align-items: start; }
/* The golden-model case block floats to the right of its paragraph so the copy
   wraps beside it and continues underneath. Sized to fit its own longest line
   (fit-content) so it stays as narrow as possible without clipping any code,
   far tighter than the default floated code width — leaving the copy room to
   wrap beside it inside the split's right column. */
#golden-model .code-editor--float-right { width: fit-content; max-width: 100%; }

/* "Choosing Good Tests and Reading Waveforms": let the heading use the full
   section width so it sits on ONE line on desktop (the default prose cap forces
   it to wrap). On phones it drops to a controlled two-line break after "and",
   with a slightly smaller face so the first line never overflows a narrow
   screen. */
#coverage .section__head { max-width: none; }
.cov-heading__break { display: none; }
@media (max-width: 768px) {
  #cov-title { font-size: 1.5rem; }
  .cov-heading__break { display: inline; }
}

/* Framed "common edge cases" list floated beside the coverage copy — the same
   raised paper surface as the numbered-list cards, but bulleted and in white
   text with accent bullets. The copy wraps beside it and continues underneath;
   on phones it unfloats to full width and sits above the text. */
.edge-card {
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  margin: 0.4rem 0 var(--space-4) var(--space-5);
  color: #fdfdfb;
}
.prose--flow .edge-card { float: right; width: 34rem; max-width: 100%; }
.edge-card__title {
  font-family: var(--font-mono);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--accent);
  margin: 0 0 var(--space-2);
}
.edge-card ul { margin: 0; padding-left: 1.35rem; list-style: disc; }
.edge-card li { color: #fdfdfb; margin-bottom: var(--space-3); }
.edge-card li:last-child { margin-bottom: 0; }
.edge-card li::marker { color: var(--accent); }
/* The concrete example for each case: a short, display-only Verilog code block
   (same VS Code chrome as the lesson's other snippets) sitting under the case it
   illustrates. */
.edge-card .code-editor { margin: var(--space-2) 0 0; }
@media (max-width: 768px) {
  .prose--flow .edge-card { float: none; width: 100%; margin: var(--space-4) 0; }
}

/* The waveform figure sits beside the (tall) edge-card, below the paragraphs:
   establishing its own block-formatting context makes its box shrink to the
   space left of the float rather than sliding under it, so it fills the column
   next to the card. It unfloats to full width once the card stacks on phones. */
#coverage .flow-figure { display: flow-root; }
/* Keep the GitHub sendoff beside the edge-card, right under the waveform, rather
   than clearing to a full-width line below the float (its flex box already
   avoids overlapping the card). */
#coverage .code-card { clear: none; }
/* In the wider mux section, let the explanation fill its column rather than
   staying pinned to the default prose measure. */
.split--media-left .prose { max-width: none; }
/* The mux figure — a large diagram hung to the left of its explanation. */
.figure--mux { width: 100%; max-width: 34rem; margin-inline: 0 auto; }
/* Small hand-drawn diagrams set beside prose in a .split (e.g. the Binary
   Addition worked sums + bit-addition table): cap the width so the sketch sits
   neatly next to the text rather than dominating the auto grid track. */
.figure--diagram { width: 100%; max-width: 26rem; margin-inline: auto; }
/* A wider variant for the landscape bit-addition table, which reads better a
   little larger than the near-square worked-sums sketch. */
.figure--diagram-lg { max-width: 34rem; }
/* A narrow cap for a portrait diagram set beside prose (the ripple-carry chain,
   which is tall and skinny). */
.figure--diagram-tall { max-width: 20rem; }
/* Overbar for a complemented signal (b-bar = "not b"), drawn a hair lower than a
   plain text-decoration overline so the bar sits closer to the letter. */
.ovl { position: relative; }
.ovl::before {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0.08em;
  border-top: 0.06em solid currentColor;
}
/* Adder → adder-subtractor cross-fade. The two sketches share identical
   dimensions (padded so their full-adder chains register exactly), so stacking
   them and fading the subtractor in on top makes the XOR gates + Sub line appear
   over an unchanging chain. Toggled by the button below the diagram. */
.adder-reveal { position: relative; }
.adder-reveal__img { display: block; width: 100%; height: auto; }
.adder-reveal__over {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 1.2s var(--ease);
}
.adder-reveal.is-revealed .adder-reveal__over { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .adder-reveal__over { transition-duration: 0.01ms; }
}
/* The toggle sits full-width at the bottom of the card, below the caption. */
.adder-reveal__toggle {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-top: var(--space-3);
}
/* Ripple-carry section: open up the paragraph rhythm a touch, and nudge the
   diagram card a little left of its column (desktop only — the split stacks and
   re-centers on mobile). */
#multi-bit .prose p { margin-bottom: var(--space-4); }
@media (min-width: 769px) {
  #multi-bit .figure { transform: translateX(-2rem); }
}

/* "See the code" card: a paper panel that links out to the GitHub source, with
   the GitHub mark on the left and a nudging arrow on the right. */
.code-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  color: var(--ink);
  text-decoration: none;
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease),
              border-color 0.18s var(--ease);
}
.code-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
  border-color: var(--accent);
}
.code-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.code-card__logo { flex: none; width: 2.25rem; height: 2.25rem; fill: var(--ink); }
.code-card__text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.code-card__title {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
}
.code-card__sub { color: var(--soft-ink); font-size: 0.9rem; }
.code-card__arrow {
  margin-left: auto;
  flex: none;
  color: var(--accent);
  font-size: 1.2rem;
  transition: transform 0.18s var(--ease);
}
.code-card:hover .code-card__arrow { transform: translateX(3px); }

/* Document-thumbnail variant: the standard GitHub sendoff card with a preview
   image of the linked document stacked ON TOP, so the two read as one widget.
   The card becomes a vertical stack, and the GitHub row (.code-card__row) keeps
   the exact flex layout and padding of the plain .code-card, so its design is
   unchanged. The whole card is still one <a>, and still lifts on hover. */
.code-card--doc {
  display: block;
  padding: 0;
  overflow: hidden;                 /* clip the thumbnail area to the card's rounded corners */
  max-width: 36rem;
  margin-inline: auto;
}
/* The preview sits on the SAME paper the card is made of: a padded frame around
   the thumbnail, divided from the GitHub row below by a hairline. */
.code-card--doc .code-card__thumbwrap {
  display: block;
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  border-bottom: 1px solid var(--hairline);
}
.code-card--doc .code-card__thumb {
  display: block;
  width: 100%;
  height: auto;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
}
.code-card--doc .code-card__row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: clamp(var(--space-3), 3vw, var(--space-4));
}

/* "Go back / revisit" card — a deliberately DIFFERENT shape from the GitHub
   .code-card: a faint accent wash with an accent spine down the left edge, and a
   return-arrow icon on the left that nudges *left* on hover (the code-card's
   arrow lives on the right and nudges right). Use it to point the reader back to
   an earlier lesson. */
.recall-card {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  background: rgba(183, 148, 246, 0.10);
  border: 1px solid var(--hairline);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius);
  color: var(--ink);
  text-decoration: none;
  transition: transform 0.18s var(--ease), box-shadow 0.18s var(--ease),
              border-color 0.18s var(--ease), background 0.18s var(--ease);
}
.recall-card:hover {
  transform: translateY(-2px);
  box-shadow: var(--shadow-lift);
  border-color: var(--accent);
  background: rgba(183, 148, 246, 0.16);
}
.recall-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.recall-card__icon {
  flex: none;
  width: 2rem;
  height: 2rem;
  color: var(--accent);
  transition: transform 0.18s var(--ease);
}
.recall-card:hover .recall-card__icon { transform: translateX(-3px); }
.recall-card__text { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
.recall-card__eyebrow {
  font-family: var(--font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent);
}
.recall-card__title {
  font-family: var(--font-mono);
  font-weight: 600;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
}
.recall-card__sub { color: var(--soft-ink); font-size: 0.9rem; }

/* Landscape card: image hung to the left, caption to its right, laid out as a
   wide panel about twice as wide as it is tall (the Sum-circuit XOR sketch). */
.figure--media-row {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-3), 3vw, var(--space-4));
  width: 100%;
  max-width: 42rem;
  /* Trim top/bottom padding so the card's height sits just above the image. */
  padding-block: var(--space-2);
}
.figure--media-row img {
  width: auto;
  max-width: 55%;
  margin-inline: 0;
}
.figure--media-row figcaption {
  margin-top: 0;
  padding: 0 0 0 clamp(var(--space-3), 3vw, var(--space-4));
  border-top: 0;
  border-left: 1px solid var(--hairline);
}
/* On narrow screens fall back to the stacked figure look (image over caption). */
@media (max-width: 40rem) {
  .figure--media-row {
    aspect-ratio: auto;
    flex-direction: column;
    align-items: stretch;
  }
  .figure--media-row img { max-width: 100%; max-height: none; }
  .figure--media-row figcaption {
    padding: var(--space-2) 0 0;
    margin-top: var(--space-3);
    border-top: 1px solid var(--hairline);
    border-left: 0;
  }
}
/* An extra-wide cap for a large landscape table shown full-width and centered
   (the 3-input full-adder truth table). */
.figure--diagram-xl { max-width: 48rem; }
/* Quieter checklist: bold lead-ins use the muted prose color (matches
   .prose strong) rather than the brighter site default. */
.checklist--quiet strong { color: var(--soft-ink); }
/* Center a prose block and its text (used above the truth table), on a wider
   measure than the default body prose. */
.prose--center { text-align: center; margin-inline: auto; max-width: 62rem; }
/* Meet the Gates intro: fill the wide container so the sentence sits on one
   line on desktop (it still wraps gracefully on narrow screens). */
#gates .prose { max-width: none; }

/* Inline copy of the card's flip control, so prose can reference the exact
   symbol. Sized in em to track the surrounding text. */
.inline-flip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.55em;
  height: 1.55em;
  vertical-align: -0.32em;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--accent);
}
.inline-flip svg {
  width: 1em;
  height: 1em;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Photographic figure: the image fills the card edge-to-edge (no sketch
   padding), with the caption/credit on its own strip below. */
.figure--photo {
  width: 100%;
  max-width: 38rem;                /* ~its native 612px — the section's focal image */
  margin-inline: auto;
  padding: 0;
  overflow: hidden;
}
.figure--photo img { border-radius: 0; display: block; }
@media (max-width: 768px) {
  /* Stack to one column; the figure comes first in the DOM, so the mux image
     sits above its explanation. */
  .split,
  .split--media-left,
  .split--media-right { grid-template-columns: 1fr; }
  /* Media-right stacks figure-first (it leads in the DOM), same as media-left,
     so the diagram sits above its explanation on phones. */
  .split--media-right .figure { order: 1; }
  .split--media-right .prose { order: 2; }
  .figure--mux { max-width: 340px; margin-inline: auto; }   /* re-center on mobile */
  /* First-ALU section: stack with the diagram above its explanation. `order`
     puts the card first without moving its long markup ahead of the prose. */
  .split--stack { display: flex; flex-direction: column; }
  .split--stack .gate-card--solo { order: 1; }
  .split--stack .prose { order: 2; margin-top: var(--space-4); }
}

/* ------------------------------------------------------------------ *
   GATE GALLERY — the hand-drawn gates laid out as a responsive grid of
   paper-card figures (Logic Gates lesson). Each sketch already carries a
   worked example in blue, so captions stay to a one-line rule.
 * ------------------------------------------------------------------ */
.gate-grid {
  margin-top: var(--space-4);
  display: grid;
  /* On phones the gates stack into a single column the reader scrolls through,
     so each sketch and caption gets full width and reads large. */
  grid-template-columns: minmax(0, 26rem);
  justify-content: center;
  gap: var(--space-4);
}
@media (min-width: 769px) {
  /* Back to three across on desktop, filling the section so all six sit larger
     and fit on one screen. */
  .gate-grid {
    grid-template-columns: repeat(3, 1fr);
    justify-content: stretch;
    gap: var(--space-3);
  }
  .gate-grid .figure img { height: 9rem; }
}

/* Instruction / Data memory flip cards on the Fetch-Decode-Execute page. Each
   sits beside its explanation in a .split — the instruction card in the left
   (media) track, the data card in the right track — vertically centered against
   the copy. The cards are larger than the little gate tiles, and the square
   block diagram simply fills the whole card (width 100%, natural height) so the
   card takes the image's own shape rather than being letterboxed onto a fixed
   white tile the way the gate grid does. */
.mem-split { align-items: center; }
.mem-card { width: 36rem; max-width: 100%; margin-inline: auto; }
.mem-card .gate-card__face--front img {
  width: 100%;
  height: auto;
  border-radius: var(--radius-sm);
}
/* A sliver more height on the card — extra breathing room below the diagram on
   the front, which the flipped editor face grows to match. */
.mem-card .gate-card__face--front { padding-bottom: 3.2rem; }
/* Instruction memory sits a hair taller than the data card. */
.split--textleft .mem-card .gate-card__face--front { padding-bottom: 4rem; }

/* Text-left / card-right split: the card is first in the source (its markup is
   long), so `order` drops the copy into the wide left track and the card into
   the narrow right track without moving the markup. */
.split--textleft { grid-template-columns: minmax(0, 1fr) auto; }
.split--textleft .prose { order: 1; max-width: none; }
.split--textleft .mem-card { order: 2; }
@media (max-width: 768px) {
  /* Stack card-first on phones (see the diagram, then read about it). */
  .split--textleft { grid-template-columns: 1fr; }
  .split--textleft .mem-card { order: 1; }
  .split--textleft .prose { order: 2; }
}
/* Breathing room above the full-width Harvard-architecture paragraph. */
.mem-harvard { margin-top: var(--space-5); }
/* Inline control-signal names in prose (readEnable, clk) — the same light blue
   as the cards' control chips and the waveform's control traces (overrides the
   default purple <code> color + tint). */
code.sig-ctrl { color: #4a90d9; background: rgba(74, 144, 217, 0.10); }

/* Putting It All Together: the fetch-decode-execute diagram on the standard
   `.figure` image card (paper background, hairline border, radius, soft shadow —
   see the Figures / image cards entry in STYLE_GUIDE.md); just centered and
   width-capped here. */
#all-together .together-fig { max-width: 58rem; margin-inline: auto; }
#all-together .together-fig figcaption { text-align: center; }
@media (max-width: 768px) {
  .mem-card { width: 100%; }
}

/* Tighter vertical rhythm so a two-row card grid clears one screen. */
.section--tight { padding-block: var(--space-5); }
.section--tight .section__head { margin-bottom: var(--space-3); }
.gate-grid .figure { margin: 0; text-align: center; }
/* Every sketch sits on a uniform white tile of the same height, centered, so the
   gates line up regardless of their individual shapes and the caption's faint
   divider falls at exactly the same level on every card. */
.gate-grid .figure img {
  display: block;
  width: 100%;
  height: 8.5rem;
  object-fit: contain;
  background: #fff;
  border-radius: var(--radius-sm);
}
/* Captions are centered (symmetrical about the card's center line) and kept to a
   similar length so the cards balance. Bold terms keep the muted prose color so
   emphasis reads the same as body paragraphs (see .prose strong). */
.gate-grid figcaption { text-align: center; }
/* Card front captions center in any context — the gate grid or a standalone
   .gate-card--solo sitting in a split — so the gate-name eyebrow + caption stay
   symmetrical about the card's center line. */
.gate-card__face--front figcaption { text-align: center; }
/* Mono eyebrow naming the gate at the top of each caption. */
.gate-name {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.14em;
  color: var(--accent);
  margin-bottom: 0.35rem;
}

/* ------------------------------------------------------------------ *
   FLIP CARDS — front shows the gate sketch; click it to flip to an editable
   Verilog playground on the back (a mini VS Code editor + a live y readout).
   The two faces share one grid cell so they keep the same footprint; only the
   FRONT keeps the .figure class, so scroll.js's `.figure` reveal never touches
   the back face (animating its transform would break the 3D flip).
 * ------------------------------------------------------------------ */
.gate-card { position: relative; perspective: 1200px; }
/* A standalone flip card (mux / 1-bit ALU) sitting outside the gate grid —
   capped and centered so its editor back isn't overly wide. */
.gate-card--solo { max-width: 30rem; margin-inline: auto; }
/* Nudge a right-column flip card left on the two-column desktop layout so it
   centers over the text column below it. Removed once the split stacks. */
@media (min-width: 769px) {
  .gate-card--nudge-left { transform: translateX(-7rem); }
}
.gate-card__inner {
  display: grid;                    /* stack both faces → identical footprint */
  transform-style: preserve-3d;
  transition: transform 0.6s var(--ease);
}
.gate-card.is-flipped .gate-card__inner { transform: rotateY(180deg); }
.gate-card__face {
  grid-area: 1 / 1;                 /* overlap the faces in one cell */
  position: relative;
  -webkit-backface-visibility: hidden;
  backface-visibility: hidden;
}
/* Only the face currently showing is interactive (the hidden one can't be
   clicked or tabbed into). */
.gate-card:not(.is-flipped) .gate-card__face--back { pointer-events: none; }
.gate-card.is-flipped .gate-card__face--front { pointer-events: none; }

/* Front: the clickable sketch. Enlarges slightly on hover/focus so it reads as
   interactive — only while the front is showing. */
.gate-card__face--front {
  cursor: pointer;
  padding-bottom: 2.1rem;           /* room for the flip arrow */
  transition: transform 0.2s var(--ease);
}
.gate-card:not(.is-flipped) .gate-card__face--front:hover,
.gate-card__face--front:focus-visible { transform: scale(1.045); }
.gate-card__face--front:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }

/* Back: a column that fills the card — editor on top, output box below. Only
   the return arrow flips back (wired in gate-card.js); the frame is inert. */
.gate-card__face--back {
  transform: rotateY(180deg);
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(var(--space-2), 2.5vw, var(--space-3));
  transition: transform 0.2s var(--ease);
}
/* Hovering the return arrow bulges the whole card a touch — the mirror of the
   cue the front gives before its flip — rather than recoloring the arrow. */
.gate-card.is-flipped .gate-card__face--back:has(.gate-card__return:hover) {
  transform: rotateY(180deg) scale(1.045);
}

/* A miniature VS Code editor filling the card: titlebar (<> glyph + "Verilog
   HDL" + a return-to-front button) over an editable body. */
.code-editor {
  margin: 0;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  border: 1px solid #333;
  border-radius: var(--radius-sm);
  overflow: hidden;
  background: #1e1e1e;
  font-family: Consolas, "Courier New", ui-monospace, monospace;
  text-align: left;
}
.code-editor__bar {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.3rem 0.35rem 0.3rem 0.6rem;
  background: #252526;                   /* VS Code titlebar gray */
  border-bottom: 1px solid #333;
  color: #cccccc;
  font-size: 0.62rem;
  letter-spacing: 0.02em;
}
/* The </> mark — literal text so it reads exactly as typed, three distinct
   characters with a little tracking between them. */
.code-editor__glyph {
  flex: none;
  color: #569cd6;                        /* the </> in VS Code keyword-blue */
  font-weight: 700;
  font-size: 0.8rem;
  letter-spacing: 0.12em;
}
/* The </> glyph + "Verilog HDL" label sit on the LEFT; `margin-right: auto` on the
   label pushes the Reset button over to the right. */
.code-editor__lang { margin-right: auto; }
/* Reset action in the titlebar — reverts the editor to its original code. */
.code-editor__reset {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.12rem 0.4rem;
  border: 1px solid #3a3a3a;
  border-radius: 4px;
  background: none;
  color: #cccccc;
  font-family: inherit;
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  cursor: pointer;
}
.code-editor__reset:hover { background: #37373d; color: #fff; border-color: #4a4a4a; }
.code-editor__reset svg {
  width: 0.8rem;
  height: 0.8rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Copy action in the titlebar — a Windows-Explorer-style two-sheets glyph plus a
   "Copy" label (injected by main.js) that copies the block's exact code. Shares
   the Reset button's chrome (icon + uppercase text). Sits at the far right, or to
   the left of Reset when a block has one. On success the glyph flips to a green
   checkmark and the label reads "Copied". */
.code-editor__copy {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.12rem 0.4rem;
  border: 1px solid #3a3a3a;
  border-radius: 4px;
  background: none;
  color: #cccccc;
  font-family: inherit;
  font-size: 0.58rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  cursor: pointer;
}
.code-editor__copy:hover { background: #37373d; color: #fff; border-color: #4a4a4a; }
.code-editor__copy svg {
  width: 0.8rem;
  height: 0.8rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
.code-editor__copy .code-editor__copy-ico--done,
.code-editor__copy .code-editor__copy-txt--done { display: none; color: #6ec06e; }
.code-editor__copy.is-copied { border-color: #3a6a3a; color: #6ec06e; }
.code-editor__copy.is-copied .code-editor__copy-ico--copy,
.code-editor__copy.is-copied .code-editor__copy-txt--copy { display: none; }
.code-editor__copy.is-copied .code-editor__copy-ico--done { display: inline; }
.code-editor__copy.is-copied .code-editor__copy-txt--done { display: inline; }
/* A caption strip belonging to the editor itself — a footer bar mirroring the
   titlebar (same VS Code grays), so a one-line note reads as part of the widget
   rather than floating text below it. Clipped to the editor's rounded corners by
   the figure's overflow:hidden. */
.code-editor__note {
  padding: 0.45rem 0.7rem;
  background: #252526;                    /* same gray as the titlebar */
  border-top: 1px solid #333;
  color: #a8a8a8;
  font-family: var(--font-display);       /* readable, not mono — it's prose */
  font-size: 0.78rem;
  line-height: 1.5;
  letter-spacing: 0.01em;
}
.code-editor__note em { color: #cfcfcf; font-style: italic; }
/* Editable body: a highlighted <pre> underlay with a transparent-text
   <textarea> layered on top, sharing identical metrics so the caret lines up
   over the colored tokens. */
.code-editor__body { position: relative; flex: 1; min-height: 5.5rem; overflow: hidden; }
.code-editor__hl,
.code-editor__ta {
  margin: 0;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 0.55rem 0.7rem;
  font-family: inherit;
  font-size: 0.85rem;
  line-height: 1.55;
  letter-spacing: 0;
  tab-size: 2;
  white-space: pre;
}
.code-editor__hl {
  position: absolute;
  inset: 0;
  overflow: hidden;                      /* scroll is synced from the textarea */
  color: #d4d4d4;                        /* default editor text */
  background: #1e1e1e;
  pointer-events: none;
}
.code-editor__hl code {
  font: inherit;
  background: none;
  color: inherit;
  padding: 0;
  white-space: inherit;
}
.code-editor__ta {
  position: absolute;
  inset: 0;
  border: 0;
  resize: none;
  overflow: auto;
  color: transparent;                    /* real glyphs come from the underlay */
  background: transparent;
  caret-color: #d4d4d4;
}
.code-editor__ta:focus { outline: none; }
.code-editor__ta::selection { background: rgba(38, 79, 120, 0.75); color: transparent; }
/* VS Code Dark+ Verilog token colors. */
.tok-kw { color: #569cd6; }              /* keywords: input / output / wire / reg / assign */
.tok-cm { color: #6a9955; }              /* comments */
.tok-op { color: #b794f6; }              /* operators: = & | ^ ~ ? + - == && … */
.tok-num { color: #b5cea8; }             /* numeric literals: 42, 1'b0, 4'hF */
.tok-type { color: #4ec9b0; }            /* module names — declared or instantiated */
.tok-inst { color: #dcdcaa; }            /* instance names in a module instantiation */
.tok-paren { color: #ffd700; }           /* parentheses */
/* Syntax error — red wavy underline under the offending code (VS Code style). */
.tok-err {
  text-decoration: underline wavy #f14c4c;
  text-decoration-skip-ink: none;
  text-underline-offset: 2px;
}

/* Static, display-only code block. Same VS Code chrome + `.tok-*` token colors as
   the editable card, but a plain in-flow <pre> that grows to fit its code (no
   textarea, no JS, no Reset button). Used to show read-only Verilog snippets in
   the Testing lesson. The panel scrolls horizontally on its own when a line is
   wider than the column, so the page never scrolls sideways. */
.code-editor--static { flex: none; }
.code-editor--static .code-editor__body {
  position: static;
  min-height: 0;
  overflow-x: auto;
}
.code-editor--static .code-editor__hl {
  position: static;
  height: auto;
  overflow: visible;
  padding: 0.8rem 1rem;                 /* a touch more room than the compact editor cards */
}

/* Testing lesson: a prose column that hosts floated code blocks, so the body text
   wraps beside each block and flows underneath it when the copy runs longer.
   flow-root keeps every float contained within its own section. It fills the full
   container width (max-width: none) so its left AND right edges match the body
   copy in alu-slice's "Final Upgrade" section. */
.prose--flow { display: flow-root; max-width: none; }
/* Full-width code (the text sits above and below it). */
.prose--flow .code-editor--full { width: 100%; margin: var(--space-4) 0; }
/* Editable code block (typable, with a Reset button and live error detection):
   a prominent left-aligned block, taller than the read-only snippets so the whole
   testbench interface is visible without scrolling, with the text above and below
   it. On narrow screens it fills the column. */
.prose--flow .code-editor--edit {
  width: 100%;
  max-width: 48rem;
  margin: var(--space-4) 0;
}
.prose--flow .code-editor--edit .code-editor__body { min-height: 22rem; }
/* Floated code: prose wraps beside the block, then continues underneath it.
   Alternating sides give the page rhythm; generous margins keep the text off the
   block. Unfloats to full width on narrow screens (the same 768px seam .split
   stacks at). */
.prose--flow .code-editor--float-left,
.prose--flow .code-editor--float-right { width: 33rem; max-width: 100%; }
.prose--flow .code-editor--float-right { float: right; margin: 0.4rem 0 var(--space-4) var(--space-5); }
.prose--flow .code-editor--float-left  { float: left;  margin: 0.4rem var(--space-5) var(--space-4) 0; }
@media (max-width: 768px) {
  .prose--flow .code-editor--float-left,
  .prose--flow .code-editor--float-right {
    float: none; width: 100%; margin: var(--space-4) 0;
  }
}
/* The takeaway list and the GitHub card always start on a fresh full-width line,
   below any float above them, rather than wrapping into the narrow side gap. */
.prose--flow .checklist,
.prose--flow .code-card { clear: both; }

/* A hand-drawn diagram shown in place of (or beside) a code block: a paper card
   that floats like the code blocks so the copy wraps beside it. Kept a plain
   <figure> WITHOUT the .figure class so it reveals once (as a prose child) rather
   than also being caught by the generic .figure scroll reveal. */
.prose--flow .flow-figure {
  margin: 0;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: clamp(var(--space-2), 3vw, var(--space-3));
  box-shadow: var(--shadow-soft);
}
.prose--flow .flow-figure img { width: 100%; height: auto; border-radius: var(--radius-sm); }
.prose--flow .flow-figure--float-left,
.prose--flow .flow-figure--float-right { width: 33rem; max-width: 100%; }
.prose--flow .flow-figure--float-right { float: right; margin: 0.4rem 0 var(--space-4) var(--space-5); }
.prose--flow .flow-figure--float-left  { float: left;  margin: 0.4rem var(--space-5) var(--space-4) 0; }

/* --- PC-increment reveal (Constructing a Datapath) ---------------------------
   Two hand-drawn diagrams share the same left half (PC + instruction memory +
   first adder) and the same height; the branch-capable "upgrade" is simply
   wider. So instead of the ALU adder-reveal's padded cross-fade, this widget
   keeps a FIXED height and animates its WIDTH: it starts hugging the small
   diagram and, on toggle, grows rightward — revealing whitespace beside the
   shared left half while the wider diagram stretches and fades in.

   The two widths come straight from the source images at equal height:
     pc-inc.jpg          878 x 1210  -> small
     pc-inc-upgrade.jpg 1796 x 1208  -> large  (small is 878/1796 = 48.9% of it)
   Height is held constant, so there is never a vertical jump.                 */
/* The card hugs the canvas (fit-content) so it stays sized to whichever diagram
   is showing; it keeps the site's standard .figure padding around the image. */
.pc-reveal-fig {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin-inline: auto;
}
/* The caption would otherwise be the widest child and stretch the fit-content
   card past the small diagram. width:0 + min-width:100% lets it FILL the card
   without CONTRIBUTING to its width, so the card stays sized to the canvas and
   the caption simply wraps. */
.pc-reveal-fig figcaption { width: 0; min-width: 100%; box-sizing: border-box; }
.pc-reveal {
  --pc-h: 29.6rem;          /* fixed render height, shared by both states      */
  --pc-w-large: 43.17rem;   /* expanded width  (= --pc-h x 1.4585)             */
  --pc-w-small: 21.48rem;   /* collapsed width (= --pc-h x 0.7256)             */
  position: relative;
  width: var(--pc-w-large);
  max-width: 100%;
  height: var(--pc-h);
  overflow: hidden;
  background: #fff;         /* the "whitespace" beside the small diagram       */
  border-radius: var(--radius-sm);
  transition: width 0.7s var(--ease);
}
.pc-reveal.is-collapsed { width: var(--pc-w-small); }
/* Both layers are pinned to the left edge and sized by height, so their shared
   left half lines up; the small one leaves whitespace to its right. Higher
   specificity than `.figure img` so width stays intrinsic (not stretched). */
.pc-reveal .pc-reveal__img {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  width: auto;
  max-width: none;
  margin: 0;
  border-radius: 0;
  transition: opacity 0.5s var(--ease);
}
/* The simple path stays put and fully visible; the wider upgrade fades in
   directly on top of it so the lines that already existed appear to hold their
   position. The upgrade is aligned by a constant transform (same in both states,
   so it never MOVES — only fades) that lands its shared left half — PC,
   instruction memory, first adder — on the simple one. The two drawings differ
   slightly in scale, so a plain offset can't line them up: the transform is
   scale(0.995) about the top-left corner plus a small nudge, fit by edge
   cross-correlation over the shared region. */
.pc-reveal .pc-reveal__small { opacity: 1; }
/* The upgrade also carries a left-to-right clip wipe: while the box slides open,
   the wipe uncovers the image column by column, so the new horizontal wires
   appear to draw rightward and the components at their ends (second adder, mux,
   branch logic) surface last. Over the shared left half the wipe is invisible —
   it just swaps in identical, aligned lines. The wipe runs with the width slide
   (0.7s) while the opacity fade keeps its original 0.35s-delayed timing. */
.pc-reveal .pc-reveal__large {
  opacity: 0;
  transform-origin: 0 0;
  transform: translate(-0.682%, 0.663%) scale(0.995);
  clip-path: inset(0 100% 0 0);
  transition: opacity 0.4s var(--ease), clip-path 0.5s linear;
}
.pc-reveal:not(.is-collapsed) .pc-reveal__large {
  opacity: 1;
  clip-path: inset(0 0 0 0);
  transition: opacity 0.5s var(--ease) 0.35s, clip-path 0.7s linear;
}
/* Toggle sits full-width under the caption, matching the ALU adder-reveal. */
.pc-reveal__toggle {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-top: var(--space-3);
}
@media (max-width: 760px) {
  /* Scale the whole trio down so the expanded width fits a phone. */
  .pc-reveal { --pc-h: 60vw; --pc-w-large: 89.7vw; --pc-w-small: 43.5vw; }
}
@media (prefers-reduced-motion: reduce) {
  .pc-reveal,
  .pc-reveal .pc-reveal__img { transition-duration: 0.01ms; transition-delay: 0s; }
}
/* The ripple-carry row and the branch row share ONE column template: a fixed left
   track sized to the widget's expanded width (--pc-w-large 43.17rem PLUS the
   figure's padding on both sides — the clamp mirrors .figure's padding — so the
   wide image isn't squeezed under its natural width and clipped) and a 1fr right
   track. This lines the two rows up: the code-card ends up exactly as wide as,
   and directly above, the branch paragraph, while the ripple-carry copy sits over
   the widget. In the branch row the widget is left-pinned (margin-inline:0) so it
   slides rightward into the reserved space rather than reflowing the text. Desktop
   only; below 768px both splits stack. */
.pc-rca-split,
.pc-branch-split { --pc-track: calc(43.17rem + 2 * clamp(var(--space-2), 3vw, var(--space-4))); }
.pc-rca-split .prose { max-width: none; }
/* Drop the paragraph's trailing margin so its box hugs the visible text — then
   align-items:center lands the card centered on the paragraph, not on a box that
   runs taller than the text. */
.pc-rca-split .prose p { margin: 0; }
.pc-rca-split .code-card { margin-top: 0; }
@media (min-width: 769px) {
  .pc-rca-split,
  .pc-branch-split { grid-template-columns: var(--pc-track) minmax(0, 1fr); }
  .pc-branch-split .pc-reveal-fig { margin-inline: 0; }
}

/* --- Step 2 "operate" reveal (next-phase -> next-phase-2) --------------------
   A FULL-WIDTH widget: left-pinned, it grows from a collapsed size to span the
   entire column. The two drawings share a common SCALE rather than a common
   height (the upgrade is wider AND ~17% taller — it adds the data-memory stage on
   the right and a feedback wire along the bottom). Sizes are in container-query
   units (of .op-stage, i.e. the full column) so the box HEIGHT stays constant at
   the full-width height while only the WIDTH animates, and both images keep a
   matched scale no matter the box width. The wider upgrade fades in on top —
   nudged so its shared half sits exactly on the small one — and a left-to-right
   clip wipe draws the new horizontal wires.
     next-phase.jpg   1294 x 769  ratio 1.6827
     next-phase-2.jpg 1937 x 869  ratio 2.229   (shared content at scale 1.0,
                                                  offset dx 0px / dy -14px;
                                                  collapsed width = 1294/1937 = 66.8%) */
.op-stage { container-type: inline-size; width: 100%; margin-top: var(--space-5); }
/* Give the paragraph beneath the widget extra breathing room from its card. */
.op-stage + .prose--flow { margin-top: var(--space-5); }
/* The paper card the widget rests on: it hugs the box (so it reaches the column
   edges only when the box is expanded), carries the site's standard figure
   padding, and is left-pinned. */
.op-reveal-fig {
  position: relative;
  width: fit-content;
  max-width: 100%;
  margin: 0;
  padding: clamp(var(--space-2), 3vw, var(--space-4));
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.op-reveal {
  /* Expanded width = the full column minus the card's padding (and border) on
     both sides, so the white surface sits INSIDE the card and the card itself
     reaches the margins. */
  --op-pad: clamp(var(--space-2), 3vw, var(--space-4));
  --op-full: calc(100cqw - 2 * var(--op-pad) - 4px);
  position: relative;
  width: var(--op-full);               /* expanded                              */
  height: calc(var(--op-full) / 2.229);
  overflow: hidden;
  background: #fff;
  border-radius: var(--radius-sm);
  transition: width 0.7s var(--ease);
}
.op-reveal.is-collapsed { width: calc(0.668 * var(--op-full)); }
.op-reveal .op-reveal__img {
  position: absolute;
  top: 0;
  left: 0;
  width: auto;
  max-width: none;
  margin: 0;
  border-radius: 0;
  transition: opacity 0.5s var(--ease);
}
/* Sized in cqw (fixed to the column, not the animating box) so they never
   rescale as the box opens. Small at 88.49% height, top-aligned, so its shared
   upper-left half lines up with the large one's. */
.op-reveal .op-reveal__small { height: 88.49%; opacity: 1; }
.op-reveal .op-reveal__large {
  height: 100%;
  opacity: 0;
  transform-origin: 0 0;
  transform: translate(0%, -1.611%);
  clip-path: inset(0 100% 0 0);
  transition: opacity 0.4s var(--ease), clip-path 0.5s linear;
}
.op-reveal:not(.is-collapsed) .op-reveal__large {
  opacity: 1;
  clip-path: inset(0 0 0 0);
  transition: opacity 0.5s var(--ease) 0.35s, clip-path 0.7s linear;
}
.op-reveal__toggle {
  display: flex;
  justify-content: center;
  width: 100%;
  margin-top: var(--space-3);
}
@media (prefers-reduced-motion: reduce) {
  .op-reveal,
  .op-reveal .op-reveal__img { transition-duration: 0.01ms; transition-delay: 0s; }
}

/* --- "slide right" hint arrow (both reveal widgets) --------------------------
   A purple arrow sitting just past the collapsed card's right edge, vertically
   centered on the diagram, nudging rightward to suggest the widget grows that way
   on press. Shown only while collapsed; fades out once expanded. */
.reveal-hint {
  position: absolute;
  left: calc(100% + 0.6rem);
  color: var(--accent);
  opacity: 0;
  transition: opacity 0.3s var(--ease);
  pointer-events: none;
  display: flex;
  align-items: center;
}
.reveal-hint__svg {
  display: block;
  width: 3rem;
  height: auto;
  animation: reveal-hint-nudge 1.3s ease-in-out infinite;
}
@keyframes reveal-hint-nudge {
  0%, 100% { transform: translateX(0); }
  50%      { transform: translateX(0.4rem); }
}
/* Vertically span the diagram box so align-items:center lands the arrow on its
   middle (the box height differs per widget). */
.pc-reveal-fig .reveal-hint {
  top: clamp(var(--space-2), 3vw, var(--space-4));
  height: 29.6rem;                       /* = --pc-h */
}
.op-reveal-fig .reveal-hint {
  top: clamp(var(--space-2), 3vw, var(--space-4));
  height: calc((100cqw - 2 * clamp(var(--space-2), 3vw, var(--space-4)) - 4px) / 2.229);
}
.pc-reveal-fig:has(.pc-reveal.is-collapsed) .reveal-hint,
.op-reveal-fig:has(.op-reveal.is-collapsed) .reveal-hint { opacity: 1; }
@media (prefers-reduced-motion: reduce) {
  .reveal-hint__svg { animation: none; }
}

@media (max-width: 768px) {
  /* On phones the card fills the width, leaving no room beside it — hide the hint. */
  .reveal-hint { display: none; }
  .prose--flow .flow-figure--float-left,
  .prose--flow .flow-figure--float-right {
    float: none; width: 100%; max-width: 28rem; margin: var(--space-4) auto;
  }
}

/* GitHub report card floated to the right of the What's New prose, so the text
   flows down its left side. Keeps its original 36rem size; unfloats and centers
   once the column is too narrow to sit a readable paragraph beside it. */
.prose--flow .code-card--aside {
  float: right;
  width: 36rem;
  max-width: 100%;
  margin: 0.4rem 0 var(--space-4) var(--space-5);
}
@media (max-width: 1100px) {
  .prose--flow .code-card--aside {
    float: none; width: 100%; max-width: 36rem; margin: var(--space-4) auto;
  }
}
/* R-type banner: a wider float on large screens (it's a wide two-row diagram, so
   it reads better big); keeps the default flow-figure size on mid widths and
   unfloats on mobile via the rules above. */
/* Keep the Data Transfer blocks on the page's standard one-paragraph rhythm
   (--space-3): the i-type / s-type diagram splits and the alignment paragraph
   that follows them all sit the same distance apart as ordinary paragraphs. */
#data-transfer .split { margin-top: var(--space-3); }
#data-transfer .prose--flow p:first-of-type { margin-top: var(--space-3); }

/* R-type intro: the first two paragraphs sit in a left column with the diagram
   in a right column, vertically centered against them (align-items: center). This
   keeps the image centered top-to-bottom between the top of the first paragraph
   and the top of the third paragraph (which follows below), instead of floating
   at the very top. */
#arith-logic .arith-lead {
  display: flex;
  align-items: center;
  gap: clamp(var(--space-3), 4vw, var(--space-5));
}
#arith-logic .arith-lead__text { flex: 1 1 0; min-width: 0; }
/* Avoid a lone short word on the last line (e.g. "register") — pull it up. */
#arith-logic .arith-lead__text p { text-wrap: pretty; }
#arith-logic .arith-lead__text p:first-child { margin-top: 0; }
#arith-logic .arith-lead__text p:last-child { margin-bottom: 0; }
#arith-logic .arith-lead__fig { flex: 0 0 33rem; max-width: 33rem; }
/* Clear paragraph break between the lead block (paragraphs 1–2) and the third
   paragraph that follows below it. */
#arith-logic .arith-lead + p { margin-top: var(--space-3); }
@media (max-width: 768px) {
  /* Stack: text first, then the diagram, then the third paragraph below. */
  #arith-logic .arith-lead { flex-direction: column; }
  #arith-logic .arith-lead__fig {
    flex-basis: auto; width: 100%; max-width: 28rem; margin: var(--space-4) auto 0;
  }
}

@media (min-width: 1025px) {
  #arith-logic .arith-lead__fig { flex-basis: 38rem; max-width: 38rem; }
  #data-transfer .flow-figure--float-right { width: 40rem; }
  /* The i-type / s-type banners sit image-left, text-right; give them room to
     read on large screens (they keep the default figure width on mid widths). */
  #data-transfer .split--media-left .figure--diagram { max-width: 38rem; }
  /* The sb-type / uj-type branch diagrams and the addi diagram sit image-right,
     text-left; match the Data Transfer banners' large-screen width. */
  #control-flow .split--media-right .figure--diagram,
  #register-immediate .split--media-right .figure--diagram { max-width: 38rem; }
}
/* Whole Kit: the full single-cycle datapath sits image-right; give the detailed
   diagram room to read (capped so the wrap-up copy keeps a comfortable measure). */
#whole-kit .split--media-right .figure--diagram { max-width: 42rem; }

/* Field-name <code> tinted to the colors of the R-type diagram
   (assets/single-cycle-cpu/r-type.jpg) so a field named in the prose reads as
   the same field shown in the image: funct7/funct3/opcode stay the default
   purple code chip, while rs2/rs1/rd take the diagram's green/amber/periwinkle.
   Scoped under .theme-dark with a `code` element to outrank the base `.theme-dark
   code` chip color. */
.theme-dark code.fld-funct7,
.theme-dark code.fld-funct3,
.theme-dark code.fld-opcode { background: rgba(183, 148, 246, 0.18); color: #d9c6ff; }
.theme-dark code.fld-rs2 { background: rgba(134, 214, 162, 0.18); color: #8fe0a0; }
.theme-dark code.fld-rs1 { background: rgba(240, 200, 134, 0.18); color: #f0c886; }
.theme-dark code.fld-rd  { background: rgba(160, 168, 232, 0.22); color: #b3baf2; }
.theme-dark code.fld-imm { background: rgba(216, 150, 170, 0.20); color: #eaa9ba; }  /* rose immediate */

/* Bottom row of the back: the output box + the return button at bottom-right.
   The output box flexes but leaves room for the button. */
.gate-card__backrow {
  display: flex;
  align-items: stretch;
  gap: 0.5rem;
}
.gate-card__backrow .gate-sim { flex: 1; min-width: 0; }
.gate-card__return {
  flex: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.5rem;
  height: 2.05rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--accent);
  cursor: pointer;
}
/* The arrow doesn't recolor on hover — hovering it grows the whole card
   instead (see .gate-card__face--back:has(.gate-card__return:hover) above). */
.gate-card__return svg {
  width: 1.15rem;
  height: 1.15rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}

/* Output box: a toggle chip per input wire + the live value of y. */
.gate-sim {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding: 0.4rem 0.55rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: rgba(0, 0, 0, 0.02);
  font-family: var(--font-mono);
}
.gate-sim__inputs { display: flex; gap: 0.35rem; flex-wrap: wrap; }
.gate-sim__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.12rem 0.45rem;
  border: 1px solid var(--hairline);
  border-radius: 999px;
  background: var(--paper);
  color: var(--soft-ink);
  font: inherit;
  font-size: 0.68rem;
  cursor: pointer;
}
/* Data inputs (a, b …) and the output value keep the purple accent. Control
   signals (sel, op) wear the light blue used for the hand-drawn example
   inputs/outputs on the gate images. */
.gate-sim__chip b { color: var(--accent); font-weight: 600; }
.gate-sim__chip[data-val="1"] {
  border-color: var(--accent);
  color: var(--accent-deep);
  background: rgba(107, 47, 201, 0.06);
}
.gate-sim__chip--control b { color: #4a90d9; }
.gate-sim__chip--control[data-val="1"] {
  border-color: #4a90d9;
  color: #2f6fb0;
  background: rgba(74, 144, 217, 0.08);
}
.gate-sim__out { font-size: 0.82rem; color: var(--soft-ink); white-space: nowrap; }
/* When a card shows more than one output (e.g. the full adder's sum + cout),
   the readouts sit together as one group on the right of the sim bar, wrapping
   to a new line if the chips leave no room. */
.gate-sim__outs { display: flex; align-items: center; gap: 0.15rem 0.85rem; flex-wrap: wrap; }
.gate-sim__val {
  font-weight: 600;
  font-size: 1rem;
  color: var(--accent-deep);
}
.gate-sim__val[data-bad="1"] { color: var(--soft-ink); }   /* couldn't parse */

/* Instruction-memory stepper bar (Fetch-Decode-Execute page). Reuses the
   .gate-sim frame but swaps the 1-bit chips for a hex instAddress readout with
   up/down steppers on the left and the instruction hex + assembly on the right. */
.imem-sim { gap: 0.45rem 0.9rem; }
.imem-sim__field,
.imem-sim__out { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.imem-sim__lhs { font-size: 0.7rem; color: var(--soft-ink); }
.imem-sim__val { font-weight: 600; font-size: 0.9rem; color: var(--accent-deep); }
/* instAddress sits in a pill, matching the gate cards' input chips (purple
   accent name + value on a paper bubble). Not clickable — the steppers drive
   it — so no pointer cursor. */
.imem-sim__chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.28rem 0.5rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: rgba(107, 47, 201, 0.06);
  font-family: var(--font-mono);
  font-size: 0.68rem;
}
.imem-sim__chip b { color: var(--accent); font-weight: 600; }
.imem-sim__addr { color: var(--accent); font-weight: 600; }
.imem-sim__asm { font-size: 0.72rem; color: var(--soft-ink); }
.imem-sim__steppers { display: inline-flex; flex-direction: column; gap: 2px; }
.imem-sim__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.2rem;
  height: 0.85rem;
  padding: 0;
  border: 1px solid var(--hairline);
  border-radius: 3px;
  background: var(--paper);
  color: var(--soft-ink);
  font-size: 0.5rem;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s var(--ease), color 0.15s var(--ease);
}
.imem-sim__step:hover,
.imem-sim__step:focus-visible { border-color: var(--accent); color: var(--accent-deep); }

/* Data-memory playground bar. Inputs (dataAddress pill + steppers, the writeData
   hex field, and the blue control toggles) wrap along the top; the readData
   readout sits below, styled like the instruction memory's output. */
.dmem-sim { gap: 0.45rem 0.7rem; }
.dmem-sim__inputs { display: flex; align-items: center; gap: 0.4rem; flex-wrap: wrap; }
.dmem-sim__out { display: flex; align-items: center; gap: 0.4rem 1.2rem; flex-wrap: wrap; }
.dmem-sim__group { display: inline-flex; align-items: center; gap: 0.4rem; }
/* `memory` is an internal signal — neither input nor output — so its value reads
   in neutral gray to set it apart from readData's purple. */
.dmem-sim__mem { color: var(--soft-ink); }
.dmem-sim__bit { font-weight: 600; }
/* writeData: a purple data-input pill (matching dataAddress) wrapping an inline
   text field. The faint placeholder (0x00000000) shows the expected hex format
   and clears as soon as the user types. */
.dmem-sim__wd {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.14rem 0.5rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: rgba(107, 47, 201, 0.06);
  font-family: var(--font-mono);
  font-size: 0.68rem;
}
.dmem-sim__wd b { color: var(--accent); font-weight: 600; }
/* The "0x" is a fixed prefix (always shown); the field holds only the 8 digits
   after it. Prefix + field sit flush so together they read "0x########". */
.dmem-sim__wd-hex { display: inline-flex; align-items: center; }
.dmem-sim__wd-prefix { color: var(--accent); font-weight: 600; }
.dmem-sim__wd-input {
  width: 5rem;
  border: none;
  background: transparent;
  font: inherit;
  color: var(--accent);
  font-weight: 600;
  padding: 0;
}
.dmem-sim__wd-input:focus { outline: none; }
.dmem-sim__wd-input::placeholder { color: var(--soft-ink); opacity: 0.5; font-weight: 400; }

/* ---- Live data-memory timing diagram (dmem-wave.js) ---------------------- *
   Rides the shared .aluwave card + .awv-* structure, but recolors every role
   for the light lesson page (the home page's waveforms are tuned for the dark
   theme). */
/* Data memory: the flip card on the left; to its right a column holding the
   section copy on top and the live waveform below. The right column is
   bottom-aligned to the card, and the waveform is its last child, so the
   waveform's lower edge sits flush with the card's lower edge with the copy
   stacked just above it. */
.dmem-layout {
  display: flex;
  align-items: flex-start;               /* card + waveform ride up to the top */
  gap: clamp(var(--space-3), 3vw, var(--space-4));
  margin-top: var(--space-2);
}
/* Drop the card's top by the same space-4 the intro paragraph uses, so its top
   edge stays flush with the "But a program is not just…" paragraph. The card
   takes its OWN natural height (align-self: flex-start) instead of stretching to
   match the waveform column — otherwise toggling signals off shrinks the flex
   row and squeezes this card, forcing a scrollbar. */
.dmem-layout .mem-card { flex: 0 0 36rem; width: auto; max-width: 36rem; margin: var(--space-4) 0 0; align-self: flex-start; }
.dmem-layout .mem-card .gate-card__inner { height: auto; }
/* The editor's <pre>/<textarea> are normally absolutely positioned, so the body
   has no height of its own and only shows all the code when the card is stretched
   tall (which coupled it to the waveform). Float the highlighted <pre> back into
   normal flow so the body grows to fit the whole module; the transparent
   <textarea> still overlays it (the body stays position:relative). Now the card
   is sized by its code, and the waveform's height can't change it. */
.dmem-layout .code-editor__body { min-height: 0; }
.dmem-layout .code-editor__hl { position: static; height: auto; }
/* The front face is a `.figure` (top margin: space-5) while the back is a plain
   div, so the front's card used to sit ~48px lower than the back and broke the
   flush-with-paragraph alignment. Zero that margin and let the front stretch to
   fill the cell, centring the diagram, so both faces line up at the top. */
.dmem-layout .mem-card .gate-card__face--front {
  margin-top: 0;
  align-self: stretch;
  display: flex;
  flex-direction: column;
  justify-content: center;
}
/* Return arrow: fill the gap between the control panel and the card's right edge
   as a tall rectangle (the backrow already stretches its children), rather than
   a small square floating at bottom-right. */
.dmem-layout .gate-card__return { height: auto; align-self: stretch; width: 2.75rem; }
.dmem-right { flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; gap: var(--space-2); }
.dmem-right .dmem-intro { margin: var(--space-4) 0 0; }   /* nudged down from the top */
.dmem-right .dmemwave { margin: 0; }
@media (max-width: 900px) {
  .dmem-layout { flex-direction: column; align-items: stretch; }
  /* Stacked: natural size, no flush-alignment stretch or top offset. */
  .dmem-layout .mem-card { flex: none; width: 100%; max-width: 100%; margin: 0; }
  .dmem-layout .mem-card .gate-card__inner { height: auto; }
  /* Front face back to its natural (unstretched) figure layout + margin. */
  .dmem-layout .mem-card .gate-card__face--front { display: block; margin-top: var(--space-5); }
  .dmem-right { width: 100%; }
}
/* No caption sits above the scope anymore, so drop its top hairline + padding —
   the card's own padding gives the header room, and the SVG's own header rule
   under "Signal / clk pulse" is the only divider up top. */
.dmemwave .aluwave__scope { border-top: 0; padding-top: 0; }
.dmemwave__cap {
  font-family: var(--font-body);
  font-size: 0.95rem;
  line-height: 1.55;
  color: var(--soft-ink);
  margin: 0;
}
.dmemwave__cap b { color: var(--ink); font-weight: 600; }
/* Light-theme separators / gridlines (the base .awv rules are light-on-dark).
   Step gridlines + row separators are faint hairlines, the same weight as the
   style-guide scope, just inverted for the light card. */
/* This lesson uses the dark theme, so the scope card is dark — gridlines and
   separators must be LIGHT (like the home widget), not dark, or they vanish
   against the card. A slightly thicker unit stroke keeps them crisp at this
   card's render scale. */
.dmemwave .awv-grid   { stroke: rgba(253, 253, 251, 0.10); stroke-width: 2; }
.dmemwave .awv-rowsep { stroke: rgba(253, 253, 251, 0.07); stroke-width: 2; }
.dmemwave .awv-div    { stroke-width: 1.6; }
.dmemwave .awv-name--muted { fill: rgba(253, 253, 251, 0.42); }
/* The hover tooltip is a dark box (like the style guide's), so its "pulse N"
   header and border read light — not the page's dark soft-ink / hairline. */
.dmemwave .awv-tip { border-color: rgba(253, 253, 251, 0.18); }
.dmemwave .awv-tip__t { color: rgba(253, 253, 251, 0.6); }
/* Scaled up: taller lanes (set in dmem-wave.js) with proportionally thicker
   traces, fatter ripples, and larger labels so everything reads easily. */
.dmemwave .awv-trace      { stroke-width: 4.2; }
.dmemwave .awv-trace--out { stroke-width: 4.8; }
.dmemwave .awv-bead       { stroke-width: 10; }
.dmemwave .awv-hdr        { font-size: 13.5px; }
.dmemwave .awv-time       { font-size: 13.5px; }
.dmemwave .awv-name       { font-size: 15px; }
.dmemwave .awv-busval     { font-size: 12px; }
/* Role colors: data inputs purple, control blue, output deep purple, and the
   internal `memory` signal in neutral gray — matching the card's own readouts. */
.dmemwave .awv-name--in         { fill: var(--accent); }
.dmemwave .awv-trace--in        { stroke: var(--accent); }
.dmemwave .awv-busval--in       { fill: var(--accent); }
.dmemwave .awv-name--ctrl       { fill: #4a90d9; }
.dmemwave .awv-trace--ctrl      { stroke: #4a90d9; }
.dmemwave .awv-busval--ctrl     { fill: #4a90d9; }
.dmemwave .awv-name--out        { fill: var(--accent-deep); }
.dmemwave .awv-trace--out       { stroke: var(--accent-deep); }
.dmemwave .awv-busval--out      { fill: var(--accent-deep); }
.dmemwave .awv-name--internal   { fill: var(--soft-ink); }
.dmemwave .awv-trace--internal  { stroke: var(--soft-ink); }
.dmemwave .awv-busval--internal { fill: var(--soft-ink); }
/* Ripple beads pull their color from the SAME tokens as their trace (via the
   role class), so every bead matches its signal's trace exactly. A tighter glow
   than the dark-card default keeps the bead reading as the crisp trace color
   rather than a lighter smudge on the light card. */
.dmemwave .awv-bead           { filter: drop-shadow(0 0 1.5px currentColor); }
.dmemwave .awv-bead--in       { color: var(--accent); }
.dmemwave .awv-bead--ctrl     { color: #4a90d9; }
.dmemwave .awv-bead--out      { color: var(--accent-deep); }
.dmemwave .awv-bead--internal { color: var(--soft-ink); }
/* Empty-state hint shown before the first capture. */
.awv-wave-hint { font-family: var(--font-mono); font-size: 17px; fill: var(--soft-ink); }

/* Flip arrow on the front — a horizontal arrow in a bottom-right square box,
   matching the return button on the back. */
.gate-card__flip {
  position: absolute;
  right: 0.7rem;
  bottom: 0.55rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.1rem;
  height: 2.1rem;
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  background: var(--paper);
  color: var(--accent);
  pointer-events: none;                  /* clicks fall through to the card */
}
.gate-card__flip svg {
  width: 1.2rem;
  height: 1.2rem;
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-linecap: round;
  stroke-linejoin: round;
}
@media (prefers-reduced-motion: reduce) {
  .gate-card__inner,
  .gate-card__face--front,
  .gate-card__face--back { transition: none; }
}

/* A slightly smaller hero image for this lesson. */
.doc-hero__art--slim { max-width: 30rem; margin-inline: auto; }

/* Static (display-only) flip cards hold longer, wider Verilog than the 1-bit
   editable snippets. Give them a touch more width, shrink the code slightly,
   and soft-wrap it so a long line (e.g. a trailing comment) can never force a
   left-right scrollbar. */
.gate-card[data-static] { max-width: 32rem; }
.gate-card[data-static] .code-editor__hl,
.gate-card[data-static] .code-editor__ta {
  font-size: 0.72rem;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
.gate-card[data-static] .code-editor__ta { overflow-x: hidden; }
/* The register-file card is bigger than the memory cards (a wider card that its
   square-ish diagram also makes taller), which gives its module room to read at
   a comfortable size without a scrollbar. */
#register-files .mem-card { width: 38rem; max-width: 38rem; }
#register-files .code-editor__hl,
#register-files .code-editor__ta { font-size: 0.74rem; }

/* ------------------------------------------------------------------ *
   CHECK-YOURSELF QUIZ — a multiple-choice widget: a diagram, four answer
   buttons that turn green (correct) or red (wrong) on click, a status line,
   and a "continue" button revealed once the right answer is picked.
 * ------------------------------------------------------------------ */
.quiz {
  max-width: 52rem;
  margin-inline: auto;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: clamp(var(--space-4), 5vw, var(--space-5));
  box-shadow: var(--shadow-soft);
}

/* Empty placeholder frame: a Check Yourself section whose interactive content
   hasn't been built yet. Same paper card as the quiz, just blank, so the hero's
   "Skip to Check Yourself" button still has a real destination to land on. */
.blank-frame {
  max-width: 52rem;
  min-height: 20rem;
  margin-inline: auto;
  background: var(--paper);
  border: 1px dashed var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}
.quiz__q {
  font-family: var(--font-body);
  font-size: clamp(1.2rem, 2.2vw, 1.5rem);
  font-weight: 400;
  font-style: italic;
  text-align: center;
  margin: 0 0 var(--space-3);
}
.quiz__figure { max-width: 48rem; margin: 0 auto var(--space-4); }

/* Answer reveal: once the correct answer is picked (.quiz.is-solved), the truth
   table scales down a little and slides toward center-left to make room, while
   the Cout circuit fades in beside it — the two then sit side by side. The Cout
   panel grows into place first, then fades in (delayed opacity) so it only
   appears once the table has moved over. Driven purely by the is-solved class. */
.quiz__reveal {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0;
  margin: 0 auto var(--space-4);
  transition: gap 0.55s var(--ease);
}
.quiz__reveal .quiz__figure { margin: 0; }
.quiz__figure--tt {
  width: 100%;                 /* white frame spans the card, matching the answer buttons' margins */
  max-width: none;
  background: #fff;
  transition: width 0.55s var(--ease);
}
.quiz__figure--tt img { max-width: 34rem; margin-inline: auto; }  /* drawing keeps its size, centered */
.quiz__figure--cout {
  width: 0;
  opacity: 0;
  transition: width 0.55s var(--ease), opacity 0.45s var(--ease) 0.45s;
}
.quiz.is-solved .quiz__reveal { gap: clamp(0.75rem, 2.5vw, 1.75rem); }
.quiz.is-solved .quiz__figure--tt { width: min(26rem, 58%); }
.quiz.is-solved .quiz__figure--cout { width: min(18rem, 40%); opacity: 1; }
/* Narrow screens: stack the two panels instead of squeezing them side by side. */
@media (max-width: 600px) {
  .quiz.is-solved .quiz__figure--tt { width: min(30rem, 100%); }
  .quiz.is-solved .quiz__figure--cout { width: min(24rem, 100%); }
}
@media (prefers-reduced-motion: reduce) {
  .quiz__reveal,
  .quiz__figure--tt,
  .quiz__figure--cout { transition: none; }
}

/* ALU-slice Check Yourself reveal: the slice sketch sits small on its own, then
   on a correct answer slides toward center-left while the De Morgan sketch fades
   in beside it — the two then sit side by side. Same mechanism as the full-adder
   reveal above, sized for these two images. */
/* A roomier card so the slice + De Morgan sketches can sit larger side by side. */
.quiz--wide { max-width: 66rem; }
/* Complete-ALU equality Check Yourself: the drawing sits on a full-width white
   frame spanning the widened card, centered and padded so there's white space
   around it (matches the slice/truth-table sketches). */
.quiz__figure--equality {
  width: 100%;
  max-width: none;
  background: #fff;
  border-radius: var(--radius-sm);
  padding: clamp(var(--space-3), 4vw, var(--space-5));
}
.quiz__figure--equality img { max-width: 40rem; margin-inline: auto; }
/* Testing Check Yourself: the finished-ALU sketch on the same full-width white
   frame as the equality drawing, but capped smaller since it's a near-square
   symbol rather than a wide diagram. */
.quiz__figure--alu {
  width: 100%;
  max-width: none;
  background: #fff;
  border-radius: var(--radius-sm);
  padding: clamp(var(--space-3), 4vw, var(--space-5));
}
.quiz__figure--alu img { max-width: 22rem; margin-inline: auto; }
.quiz__figure--slice {
  width: 100%;                 /* white frame spans the card, matching the answer buttons' margins */
  max-width: none;
  background: #fff;
  transition: width 0.55s var(--ease);
}
.quiz__figure--slice img { max-width: 21rem; margin-inline: auto; }  /* drawing keeps its size, centered */
.quiz__figure--demorgan {
  width: 0;
  opacity: 0;
  transition: width 0.55s var(--ease), opacity 0.45s var(--ease) 0.45s;
}
.quiz.is-solved .quiz__figure--slice { width: min(22rem, 40%); }
.quiz.is-solved .quiz__figure--demorgan { width: min(34rem, 56%); opacity: 1; }
/* Narrow screens: stack the two panels instead of squeezing them side by side. */
@media (max-width: 600px) {
  .quiz.is-solved .quiz__figure--slice { width: min(24rem, 100%); }
  .quiz.is-solved .quiz__figure--demorgan { width: min(30rem, 100%); }
}
@media (prefers-reduced-motion: reduce) {
  .quiz__figure--slice,
  .quiz__figure--demorgan { transition: none; }
}

/* Basics-of-Instructions Check Yourself reveal: the encoded R-type instruction
   sits alone at first; on a correct answer it shrinks and slides toward
   center-left while the decoded answer (register meanings + the assembled `sub`
   instruction) fades in beside it — the two then sit side by side. Exactly the
   full-adder reveal mechanism above, sized for these two wide diagrams. */
.quiz__figure--instr {
  width: 100%;                 /* white frame spans the card, matching the answer buttons' margins */
  max-width: none;
  background: #fff;
  transition: width 0.55s var(--ease);
}
.quiz__figure--instr img { max-width: 40rem; margin-inline: auto; }  /* drawing keeps its size, centered */
.quiz__figure--decode {
  width: 0;
  opacity: 0;
  background: #fff;
  transition: width 0.55s var(--ease), opacity 0.45s var(--ease) 0.45s;
}
.quiz.is-solved .quiz__figure--instr  { width: min(28rem, 46%); }
.quiz.is-solved .quiz__figure--decode { width: min(34rem, 50%); opacity: 1; }
/* Narrow screens: stack the two panels instead of squeezing them side by side. */
@media (max-width: 600px) {
  .quiz.is-solved .quiz__figure--instr  { width: min(30rem, 100%); }
  .quiz.is-solved .quiz__figure--decode { width: min(34rem, 100%); }
}
@media (prefers-reduced-motion: reduce) {
  .quiz__figure--instr,
  .quiz__figure--decode { transition: none; }
}
.quiz__options {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
  counter-reset: quizopt;
}
.quiz__opt {
  counter-increment: quizopt;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  text-align: left;
  font-family: var(--font-display);
  font-size: 1.05rem;
  padding: 0.9rem 1.1rem;
  background: var(--paper);
  color: var(--ink);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background-color 0.15s var(--ease),
              border-color 0.15s var(--ease), color 0.15s var(--ease);
}
/* Numbered prefix before each choice: 1) 2) 3) 4) */
.quiz__opt::before {
  content: counter(quizopt) ")";
  flex: none;
  color: var(--accent);
  font-family: var(--font-mono);
  font-weight: 600;
}
/* Typeset a choice as an equation (Word-style): italic variables, upright
   operators, and a touch more space around them, so a Boolean expression reads
   as a formula rather than plain typed text. Keeps the same display font *and*
   size as the standard style-guide answer choice (`.quiz__opt`, 1.05rem). */
.eq {
  word-spacing: 0.14em;      /* breathing room around + and ⊕ operators */
  white-space: nowrap;
}
.eq var {
  font-style: italic;        /* variables lean, like a real formula */
  padding-inline: 0.03em;    /* keep italic letters from touching */
}
.eq sub {
  font-style: italic;
  font-size: 0.72em;
}
.quiz__opt:hover { border-color: var(--accent); }
.quiz__opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Correct pick — light green fill. */
.quiz__opt.is-correct { background: #86efac; border-color: #86efac; color: #052e16; }
/* Wrong pick — reddish fill, kept filled after the click. */
.quiz__opt.is-wrong { background: #e57373; border-color: #e57373; color: #3d0a0a; }
/* Once solved, lock the buttons so the answered state stays put. */
.quiz.is-solved .quiz__opt { pointer-events: none; }

/* Trim the focal image and pull the card in vertically — smaller image, less
   top/bottom padding, and tighter gaps around the question and image — so the
   whole card sits noticeably shorter. */
#check .quiz { padding-top: var(--space-3); padding-bottom: var(--space-3); }
#check .quiz__q { margin-bottom: var(--space-2); }
#check .quiz__figure { max-width: 31rem; margin-bottom: var(--space-2); }

/* Code-answer variant: each choice holds a small, syntax-colored Verilog
   snippet instead of prose. It sits in the standard two-column .quiz__options
   grid (same as the Fetch/Decode/Execute quiz), so the snippet is written across
   a few lines to fit the box. Tighter padding keeps each box a hair shorter. */
.quiz__opt--code { align-items: center; padding: 0.35rem 0.9rem; }
.quiz-code {
  margin: 0;
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  background: #1e1e1e;
  border: 1px solid #333;
  border-radius: var(--radius-sm);
  padding: 0.45rem 0.7rem;
  font-family: Consolas, "Courier New", ui-monospace, monospace;
  font-size: 0.8rem;
  line-height: 1.45;
  color: #d4d4d4;
  white-space: pre;
  text-align: left;
}
/* The green/red result fill lands on the bubble; the dark code chip stays dark
   inside it, so keep the counter prefix readable against either. */
.quiz__opt--code.is-correct .quiz-code,
.quiz__opt--code.is-wrong .quiz-code { border-color: rgba(0, 0, 0, 0.35); }

.quiz__feedback {
  min-height: 1.5em;
  margin: var(--space-3) 0 0;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.95rem;
  font-weight: 500;
}
.quiz__feedback.is-correct { color: #4ade80; }
.quiz__feedback.is-wrong { color: #f87171; }

/* --- Control-signal Check Yourself (Constructing a Datapath) -----------------
   A wider quiz card holding the gray datapath plus a row of toggle "chips": one
   per 1-bit control signal, and a group of four bit-toggles for the ALU control.
   Each toggle flips 0/1 and lights up purple when 1; Submit checks the set. */
.ctrl-quiz { max-width: 64rem; }
.ctrl-quiz__fig { margin: 0 auto var(--space-4); max-width: 40rem; }
.ctrl-quiz__signals {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: stretch;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
}
.ctrl-sigbtn,
.ctrl-quiz__alu {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  min-width: 6.5rem;
  padding: 0.7rem 0.9rem;
  border-radius: var(--radius);
}
.ctrl-sigbtn {
  background: var(--bg);
  border: 1px solid var(--hairline);
  color: var(--soft-ink);
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              color 0.15s var(--ease);
}
.ctrl-sigbtn:hover { border-color: var(--accent); }
.ctrl-sigbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ctrl-sigbtn__name { font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.02em; }
.ctrl-sigbtn__val {
  font-family: var(--font-mono);
  font-size: 1.4rem;
  font-weight: 600;
  line-height: 1;
}
.ctrl-sigbtn.is-on {
  background: rgba(183, 148, 246, 0.16);
  border-color: var(--accent);
  color: var(--accent);
}
/* ALU control: a small label over four bit-toggles that read as a 4-bit number. */
.ctrl-quiz__alu { border: 1px solid transparent; }
.ctrl-quiz__alu .ctrl-sigbtn__name { color: var(--soft-ink); }
.ctrl-bits { display: flex; gap: 0.3rem; }
.ctrl-bit {
  width: 2.1rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bg);
  border: 1px solid var(--hairline);
  border-radius: var(--radius-sm);
  color: var(--soft-ink);
  font-family: var(--font-mono);
  font-size: 1.2rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s var(--ease), border-color 0.15s var(--ease),
              color 0.15s var(--ease);
}
.ctrl-bit:hover { border-color: var(--accent); }
.ctrl-bit:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.ctrl-bit.is-on {
  background: rgba(183, 148, 246, 0.16);
  border-color: var(--accent);
  color: var(--accent);
}
.ctrl-quiz__actions { display: flex; justify-content: center; }
/* Solved: every toggle turns the same green as the "Correct!" feedback, and the
   purple continue stub appears. */
.ctrl-quiz.is-solved .ctrl-sigbtn,
.ctrl-quiz.is-solved .ctrl-bit {
  background: rgba(74, 222, 128, 0.16);
  border-color: #4ade80;
  color: #4ade80;
}
/* Solved: the Submit button disappears and "Correct!" takes its place, with the
   continue stub beneath. */
.ctrl-quiz.is-solved .ctrl-quiz__actions { display: none; }
.ctrl-quiz.is-solved .quiz__feedback { margin-top: 0; }
.ctrl-quiz__next { display: none; justify-content: center; margin-top: var(--space-3); }
.ctrl-quiz.is-solved .ctrl-quiz__next { display: flex; }

/* Revealed only after the correct answer is chosen (toggled by the class). */
.quiz__next {
  display: none;
  margin-top: var(--space-4);
  justify-content: center;
}
.quiz__next.is-shown { display: flex; }
/* Completion stub (revealed on the right answer): the "you finished the ALU"
   message sits centered in the card's gray space, with the next-project button
   stacked beneath it. */
.quiz__next--done.is-shown { flex-direction: column; align-items: center; gap: var(--space-3); }
.quiz__done {
  margin: 0;
  text-align: center;
  font-family: var(--font-display);
  font-weight: 600;
  font-size: clamp(1.15rem, 2.2vw, 1.4rem);
  color: #fdfdfb;
}
/* Two next-project stubs sit side by side beneath the completion message,
   wrapping onto separate lines when the card is too narrow. */
.quiz__done-actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--space-2); }

@media (max-width: 560px) {
  .quiz__options { grid-template-columns: 1fr; }
}

/* ------------------------------------------------------------------ *
   DATA TABLES
 * ------------------------------------------------------------------ */
.table-wrap {
  margin-top: var(--space-4);
  overflow-x: auto;                 /* horizontal scroll on narrow phones */
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
}
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.95rem;
}
.data-table th,
.data-table td {
  text-align: left;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--hairline);
  vertical-align: top;
}
.data-table thead th {
  font-family: var(--font-mono);
  font-size: 0.72rem;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
  background: rgba(107, 47, 201, 0.04);
  white-space: nowrap;
}
.data-table tbody tr:last-child td { border-bottom: 0; }
.data-table tbody tr:hover { background: rgba(35, 35, 42, 0.02); }
.data-table code { white-space: nowrap; }
.data-table .yes {
  color: var(--accent);
  font-weight: 600;
  text-align: center;
}
.data-table--compact th,
.data-table--compact td {
  padding: 0.5rem 0.7rem;
  font-size: 0.88rem;
}

/* ------------------------------------------------------------------ *
   CODE BLOCKS — multi-line <pre>; inline <code> is already styled in main.css
 * ------------------------------------------------------------------ */
.code {
  margin: var(--space-4) 0 0;
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: var(--space-3) var(--space-4);
  overflow-x: auto;
  font-family: var(--font-mono);
  font-size: 0.85rem;
  line-height: 1.7;
  color: var(--ink);
}
.code code {
  /* reset the inline-code chrome from main.css inside a block */
  background: none;
  color: inherit;
  padding: 0;
  border-radius: 0;
  font-size: inherit;
  white-space: pre;
}
.code--tree { color: var(--soft-ink); }
.code--banner {
  color: var(--accent-deep);
  background: rgba(107, 47, 201, 0.05);
  border-color: rgba(107, 47, 201, 0.18);
}

/* ------------------------------------------------------------------ *
   DISCLOSURE — <details> for the long control decode + manual build
 * ------------------------------------------------------------------ */
.disclosure {
  margin-top: var(--space-4);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  background: var(--paper);
  box-shadow: var(--shadow-soft);
  overflow: hidden;
}
.disclosure > summary {
  cursor: pointer;
  list-style: none;
  padding: var(--space-3) var(--space-4);
  font-family: var(--font-mono);
  font-size: 0.82rem;
  letter-spacing: 0.04em;
  color: var(--accent-deep);
  display: flex;
  align-items: center;
  gap: 0.6rem;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before {
  content: "+";
  font-size: 1.1em;
  line-height: 1;
  color: var(--accent);
  transition: transform 0.2s var(--ease);
}
.disclosure[open] > summary::before { content: "\2212"; } /* minus */
.disclosure > summary:hover { color: var(--accent); }
.disclosure__body {
  padding: 0 var(--space-4) var(--space-4);
}
.disclosure__body .table-wrap { box-shadow: none; }

/* ------------------------------------------------------------------ *
   NEXT STEP — a gently tinted closing band
 * ------------------------------------------------------------------ */
.nextstep {
  /* Kept transparent so the continuous page background carries through to the
     closing band with no visible seam. */
  background: none;
}

/* ------------------------------------------------------------------ *
   RESPONSIVE
 * ------------------------------------------------------------------ */
@media (max-width: 768px) {
  /* Stack the hero: text first, then the ALU block diagram below it.
     The clamp minimum for padding-block-start depends on --space-6, which the
     mobile variable override shrinks to 3.5 rem — leaving only ~14 px of
     breathing room above the fixed 42 px topbar.  Pin it to 4.5 rem (72 px)
     so there's a comfortable ~30 px gap, matching the home-page hero's
     clearance intent (calc(4rem + var(--space-4))). */
  .doc-hero {
    grid-template-columns: 1fr;
    gap: var(--space-4);
    padding-block-start: 4.5rem;
  }
  /* Logic Gates lesson (the balanced hero): nudge the image + title a touch
     lower still, for a little more clearance below the top bar. */
  .doc-hero--balanced { padding-block-start: 5.5rem; }
  .doc-hero__text { max-width: none; }
  .doc-hero__art { max-width: 340px; margin-inline: auto; }
  /* Logic Gates lesson: stack the hero in reading order — title, image,
     paragraph, buttons. Promoting the text block to display:contents lets the
     image (a sibling of .doc-hero__text) slot between the title and the lead via
     `order`, while the grid gap keeps the spacing even. Margins are zeroed so the
     gap alone drives the rhythm. */
  .doc-hero--balanced .doc-hero__text { display: contents; }
  .doc-hero--balanced .title-eyebrow { order: 1; margin-bottom: 0; }
  /* Drop the accent tick on phones so the (possibly two-line) title sits truly
     centered rather than nudged right by the tick. */
  .doc-hero--balanced h1.title-eyebrow::before { display: none; }
  .doc-hero--balanced .doc-hero__art { order: 2; }
  .doc-hero--balanced .doc-hero__lead { order: 3; margin-bottom: 0; }
  .doc-hero--balanced .doc-hero__actions { order: 4; }
  .doc-hero__lead { font-size: 1.1rem; }
  .doc-hero__actions .btn,
  .nextstep .doc-hero__actions .btn { flex: 1 1 auto; justify-content: center; }
  .figure--tall img { max-width: 100%; }
}

/* ------------------------------------------------------------------ *
   PLAY TIME (complete-alu) — the home page's ALU datapath explorer, split
   into TWO frames: the datapath on the left as its own white plate, and a
   right column holding the explanatory copy above the control panel, which
   is broken out as its own card at the bottom-right of the datapath. The
   white-plate + color machinery still comes from `.handson-grid
   .alu-widget*` (main.css); the rules below drop the single outer card,
   lay the widget out in two columns, and give the panel back its own frame.
 * ------------------------------------------------------------------ */
/* Single full-width cell; the widget itself becomes the two-column grid. */
.handson-grid.playtime-grid {
  grid-template-columns: 1fr;
  max-width: none;
  margin: var(--space-5) 0 0;
}
/* Drop the combined outer card — the two inner frames are the cards now — and
   lay the datapath (left) beside the text-over-panel column (right). */
.handson-grid.playtime-grid .alu-widget {
  display: grid;
  grid-template-columns: minmax(0, 34rem) minmax(0, 1fr);
  gap: clamp(var(--space-3), 3vw, var(--space-5));
  align-items: stretch;
  margin-top: 0;
  padding: 0;
  background: transparent;
  border: 0;
  box-shadow: none;
}
/* The datapath + its title ride their own paper card (the white plate sits
   inside it, just like the original combined card). */
.playtime-left {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
  padding: clamp(var(--space-4), 5vw, var(--space-5));
}
.playtime-right { display: flex; flex-direction: column; gap: var(--space-4); }
.playtime-intro { max-width: none; margin: 0; }
/* Push the control panel to the bottom of the right column so it sits at the
   bottom-right of the datapath, with the copy above it. */
.handson-grid.playtime-grid .alu-panel {
  margin-top: auto;
  padding: clamp(var(--space-3), 3vw, var(--space-4));
  background: var(--paper);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  box-shadow: var(--shadow-soft);
}

@media (max-width: 768px) {
  .handson-grid.playtime-grid .alu-widget { grid-template-columns: 1fr; }
  .handson-grid.playtime-grid .alu-panel { margin-top: 0; }
}

/* =========================================================================
   MODE SWITCHER (Transistor Basics — "Applying Voltages")
   The same treatment as the full adder's ripple-carry / adder-subtractor
   reveal: the standard .figure card, a caption under the artwork, and ghost
   buttons below that. The three drawings are pre-cropped to a common frame
   (see assets/introduction-to-physical-design/modes/), so only the charge in
   the picture appears to change as they cross-fade.
   ========================================================================= */
/* The frames are stacked on top of one another, so the stage has to carry its
   own height: this is the aspect ratio every crop was rendered at. */
.mode-switch__stage {
  position: relative;
  aspect-ratio: 1400 / 846;
  /* The drawings are on white. Cross-fading them by opacity means that midway
     through, both are half transparent and whatever sits behind shows through,
     which on the dark card reads as a dip to grey. Backing the stage with the
     same white the artwork is on keeps the brightness constant all the way
     across the fade. */
  background: #fff;
  border-radius: var(--radius-sm);
}
.mode-switch__stage img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: contain;
  border-radius: var(--radius-sm);
  opacity: 0;
  transition: opacity 1.2s var(--ease);
}
.mode-switch__stage img.is-active { opacity: 1; }

/* One equal-width column per button, so they share the full width of the card
   and stay the same size whatever their labels are. */
.mode-switch__tabs {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: 0.5rem;
  margin-top: var(--space-3);
}
.mode-switch__tab { justify-content: center; padding-inline: 0.75rem; }
@media (max-width: 520px) {
  /* Too narrow for three across without the labels crowding: stack them. */
  .mode-switch__tabs { grid-auto-flow: row; }
}
/* Selected: the same accent border and deep-accent text a ghost button takes on
   hover in the full adder lesson, so the pressed state and the hover state read
   as one idea. */
.mode-switch__tab[aria-pressed="true"] {
  color: var(--accent-deep);
  border-color: var(--accent);
}
@media (prefers-reduced-motion: reduce) {
  .mode-switch__stage img { transition-duration: 0.01ms; }
}

/* Bare schematic artwork (Transistor Basics — "Putting Transistors in a
   Circuit"). The drawings already carry their own coloured card, so they sit on
   the page without a figure frame and are simply rounded off. `corner-shape`
   makes that rounding a squircle, where the curve eases into the straight edge
   instead of meeting it at a hard tangent; browsers that don't support it get
   the ordinary circular corner from border-radius. The black outer line of each
   drawing is trimmed off in assets/introduction-to-physical-design/schematics/,
   so nothing dark is left for the corner to clip through. */
.schematic-art {
  border-radius: 2.25rem;
  corner-shape: squircle;
}
/* Caption under a bare schematic. The standard .figure figcaption hangs below a
   hairline rule, which only reads correctly inside a framed card; with no frame
   the rule has nothing to divide, so this one is simply centred under the art. */
.schematic-cap {
  margin-top: var(--space-3);
  font-size: 0.92rem;
  line-height: 1.6;
  color: var(--soft-ink);
  text-align: center;
  border-top: 0;
  padding-top: 0;
}
