/* ════════════════════════════════════════════════════════════════
   Color Cycling from Scratch · Tutorial-specific styles
   Builds on tutorial.css design tokens.

   Page-level rules only. Each widget ships its own block, every
   selector prefixed with its #cc-* root, concatenated after this file.
   ════════════════════════════════════════════════════════════════ */

/* ── Narrow-viewport shell ───────────────────────────────────────── */
/* style.css makes <body> a column flex container, so <main> is a flex item and
   Blink floors a stretched item at its min-content width. One wide table cell
   or code line then widens the whole shell and the page scrolls sideways. The
   floor is removed here, and below 720 px the three-column reading grid
   collapses to a single full-width track: at 400 px the two 2rem gutters plus
   the centre column no longer fit, and the leftover pushed the content out.
   minmax(0, 1fr) also stops a wide grid item from expanding the track. */
.tut-shell { min-width: 0; }

@media (max-width: 720px) {
  .tut-shell { grid-template-columns: 0 minmax(0, 1fr) 0; column-gap: 0; }
}

/* A repository path in inline code (`src/blitter/{8bpp_base,...}.cpp` in the
   source notes) carries no break opportunity a line breaker will take, so at
   400 px it hangs off the side of the page. `anywhere` also lowers the min
   content width, which is what keeps the shell from widening. */
code.inline-code { overflow-wrap: anywhere; }

/* tutorial.css keeps the .tip hover popover in the layout at all times and
   fades it with opacity, but a transparent box still counts toward scrollable
   overflow: a term near the right margin puts its 280 px popover past the
   viewport and the page scrolls sideways before anyone hovers anything. This
   page carries around 90 tips, so the popover is taken out of layout until it
   is wanted. The trade is the 0.18 s fade, which a display change cannot
   animate; the popover now appears on hover and focus at once. */
.tip::after, .tip::before { display: none; }
.tip:hover::after, .tip:hover::before,
.tip:focus::after, .tip:focus::before,
.tip:focus-visible::after, .tip:focus-visible::before { display: block; }

/* ── History timeline (same shape as sort-tutorial.css / graph-tutorial.css;
      duplicated so this tutorial does not need to load those stylesheets) ─ */
.sort-timeline { margin: 1.75rem 0 2.25rem; border-left: 1px solid var(--tut-border); padding-left: 1.25rem; }
.sort-timeline-row { display: grid; grid-template-columns: 80px 1fr; gap: 1.25rem; padding: 0.55rem 0; align-items: baseline; }
.sort-timeline-year { font-family: var(--mono); font-size: 0.78rem; color: var(--tut-accent); font-weight: 600; letter-spacing: 0.04em; position: relative; }
.sort-timeline-year::before { content: ''; position: absolute; left: -1.95rem; top: 0.4em; width: 9px; height: 9px; border-radius: 50%; background: var(--tut-accent); box-shadow: 0 0 0 3px rgba(167, 139, 250, 0.15); }
.sort-timeline-body { font-size: 0.97rem; line-height: 1.65; color: var(--tut-soft); }
.sort-timeline-body strong { color: var(--tut-text); font-weight: 600; }

/* ── Code blocks ─────────────────────────────────────────────────── */
/* The UA stylesheet gives <code> a generic monospace family that would
   override pre.code's var(--mono); reset it so both panes match. */
pre.code code { font-family: inherit; background: none; border: none; padding: 0; color: inherit; white-space: pre; display: block; }

/* Language tab icons. tutorial.css colours js, py, cpp and rs only; this page
   adds a GLSL pane, an HLSL pane and a DOS C pane. */
.lang-tab[data-lang="glsl-frag"] .lang-icon { background: #5586a4; }
.lang-tab[data-lang="hlsl-ps"]   .lang-icon { background: #7a5cc4; }
.lang-tab[data-lang="c-dos"]     .lang-icon { background: #8a8a8a; }

/* ── Tables (reuse from existing tutorials) ───────────────────────── */
.rep-table { margin: 1.5rem 0; overflow-x: auto; }
.rep-table table { width: 100%; border-collapse: collapse; font-size: 0.92rem; line-height: 1.55; }
.rep-table th { text-align: left; font-weight: 700; color: var(--tut-text); padding: 0.55rem 0.75rem; border-bottom: 2px solid var(--tut-border); font-size: 0.82rem; letter-spacing: 0.03em; text-transform: uppercase; }
.rep-table td { padding: 0.55rem 0.75rem; border-bottom: 1px solid var(--tut-border-soft); color: var(--tut-soft); vertical-align: top; }
.rep-table td strong { color: var(--tut-text); }

/* ── Quiz next button ─────────────────────────────────────────────── */
/* The quiz JS renders a .quiz-next button that no stylesheet in the library
   styles, so it lands as a native control. Same treatment as .quiz-retry. */
.quiz-next {
  background: transparent;
  border: 1px solid var(--tut-border);
  color: var(--tut-soft);
  font-family: var(--mono);
  font-size: 0.7rem;
  padding: 0.25rem 0.65rem;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.15s;
  text-transform: uppercase;
  letter-spacing: 0.06em;
}
.quiz-next:hover { color: var(--tut-text); border-color: var(--tut-accent); }
.quiz-next[hidden] { display: none; }

/* ── Widget canvases ──────────────────────────────────────────────── */
/* Every picture on this page is an indexed framebuffer drawn through
   CC.createPixelView at an integer scale. Smoothing would blur the pixels the
   page is about. */
.widget canvas { image-rendering: pixelated; }

/* ── Glossary modal ───────────────────────────────────────────────── */
/* tutorial.css hides the closed modal with opacity and pointer-events only, so
   its close button stays in the layout. visibility takes it out; the JS also
   sets inert, and the transition delay keeps the fade-out visible. */
/* Giving visibility a duration keeps the computed value `hidden` through the
   first half of the transition, and focus() into a hidden dialog is dropped,
   which left the trigger focused after the panel opened. Flip it in zero time
   on open, and delay the flip by the fade on close so the fade-out is seen. */
.gloss-modal { transition: opacity 0.2s ease, visibility 0s linear 0.2s; }
.gloss-modal.open { transition: opacity 0.2s ease, visibility 0s linear 0s; }
.gloss-modal:not(.open) { visibility: hidden; }

/* ── Byte rows, swatch grids and hex dumps ───────────────────────── */
/* Shared by the chunk widgets and by prose that shows file bytes. Each sits in
   its own scroll container so a 400 px viewport never scrolls the page. */
.cc-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }

.cc-bytes {
  display: grid;
  grid-auto-flow: column;
  gap: 3px;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.6;
  min-width: max-content;
}
.cc-bytes > * {
  min-width: 2.35rem;
  padding: 0.25rem 0.1rem;
  text-align: center;
  background: var(--tut-code-bg);
  border: 1px solid var(--tut-code-border);
  border-radius: 4px;
  color: var(--tut-soft);
}
.cc-bytes input {
  color: var(--tut-text);
  text-transform: uppercase;
  width: 2.6rem;
}
.cc-bytes input:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 1px; }
.cc-bytes .cc-byte-fixed { color: var(--tut-muted); }
.cc-bytes .cc-byte-bad { border-color: var(--tut-danger); color: var(--tut-danger); }

.cc-swatches {
  display: grid;
  grid-template-columns: repeat(16, 1fr);
  gap: 2px;
  min-width: 16rem;
}
.cc-swatches > * {
  aspect-ratio: 1;
  border: 0;
  border-radius: 2px;
  padding: 0;
  cursor: pointer;
}
.cc-swatches > *:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 1px; }
.cc-swatches > .selected { box-shadow: inset 0 0 0 2px var(--tut-text); }

.cc-hex {
  margin: 1.25rem 0;
  padding: 0.85rem 1rem;
  background: var(--tut-code-bg);
  border: 1px solid var(--tut-code-border);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.78rem;
  line-height: 1.65;
  color: var(--tut-soft);
  white-space: pre;
  overflow-x: auto;
  tab-size: 2;
}
.cc-hex .cc-hex-mark { color: var(--tut-accent); }

@media (max-width: 720px) {
  .cc-swatches { min-width: 13rem; }
  .cc-bytes { font-size: 0.68rem; }
  .cc-bytes > * { min-width: 2.1rem; }
}

/* ── cc-scene (build/widgets/cc-scene.css) ─────────────────────────── */
/* ── cc-scene · §01 · One image, many motions ─────────────────────────── */

/* Three header children (title, view toggle, play) do not fit one 400 px row,
   so the header wraps instead of squeezing the toggle labels. */
#cc-scene .widget-header { flex-wrap: wrap; gap: 0.6rem; }

/* ── Range toggles ─────────────────────────────────────────────────────── */
#cc-scene .cc-scene-ranges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem;
}

#cc-scene .cc-scene-range {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  font-weight: 600;
  padding: 0.35rem 0.6rem;
  border-radius: 6px;
  border: 1px solid var(--tut-border);
  background: var(--tut-bg);
  color: var(--tut-muted);
  cursor: pointer;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}

#cc-scene .cc-scene-range:hover { color: var(--tut-text); border-color: var(--tut-accent); }

/* Pressed state carries a border and a fill as well as the dot color, so the
   on/off reading does not depend on hue. */
#cc-scene .cc-scene-range[aria-pressed="true"] {
  color: var(--tut-text);
  border-color: var(--tut-accent);
  background: rgba(167, 139, 250, 0.12);
}

#cc-scene .cc-scene-range[aria-pressed="false"] .cc-scene-dot { opacity: 0.3; }
#cc-scene .cc-scene-range[aria-pressed="false"] { text-decoration: line-through; }

#cc-scene .cc-scene-range:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 2px; }

#cc-scene .cc-scene-rate { color: var(--tut-muted); font-weight: 400; letter-spacing: 0.02em; }
#cc-scene .cc-scene-range[aria-pressed="true"] .cc-scene-rate { color: var(--tut-soft); }

#cc-scene .cc-scene-dots { display: inline-flex; gap: 2px; }
#cc-scene .cc-scene-dot {
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 2px;
  background: var(--tut-muted);
  display: inline-block;
}

/* The recolor slider ships disabled and only enables once an entry is picked,
   which makes it the first control a reader meets on the page. Same treatment
   as the cc-dac dropdown and the cc-rotate inputs: the control has to look as
   disabled as it is. */
#cc-scene input[type="range"]:disabled { opacity: 0.45; cursor: not-allowed; }

/* ── Palette grid ──────────────────────────────────────────────────────── */
#cc-scene .cc-scene-palette-block { display: grid; gap: 0.4rem; }

/* 16 columns across the full body width would give 42 px cells and a grid
   taller than the picture. This cap measures 20.1 px per cell on desktop; a
   400 px viewport has room for 17.5 px and takes the whole column. */
#cc-scene .cc-swatches { max-width: 22rem; }

/* A ring on every cell so all 256 read as a grid; the 147 entries this scene
   never uses are nearly the color of the card without it. */
#cc-scene .cc-swatches > button {
  border: 0;
  box-shadow: inset 0 0 0 1px rgba(139, 139, 160, 0.15);
}

/* A slash marks an entry no pixel in the scene references. Kept faint: 147 of
   the 256 cells carry it. */
#cc-scene .cc-swatches > .cc-scene-unused {
  background-image: linear-gradient(135deg, transparent 44%, rgba(139, 139, 160, 0.22) 44%,
                    rgba(139, 139, 160, 0.22) 56%, transparent 56%);
}

/* An outline marks the 53 entries that belong to a cycling range. It sits
   inside the cell so it does not enlarge the grid. */
#cc-scene .cc-swatches > .cc-scene-cycling {
  outline: 1px solid rgba(232, 232, 238, 0.5);
  outline-offset: -1px;
}

/* Selection wins over the cycling outline: accent ring plus a dark inner ring,
   readable on a dark or a light swatch. */
#cc-scene .cc-swatches > .selected {
  outline: 2px solid var(--tut-accent);
  outline-offset: -2px;
  box-shadow: inset 0 0 0 1px rgba(7, 7, 13, 0.9);
}

#cc-scene .cc-scene-note {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--tut-muted);
  letter-spacing: 0.02em;
}

/* ── Stats ─────────────────────────────────────────────────────────────── */
/* The inspect and last-write readouts are sentences, not numbers, so they take
   the full row and drop to text size. */
#cc-scene .cc-scene-wide { grid-column: 1 / -1; }
#cc-scene .cc-scene-wide .stat-value {
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--tut-soft);
  overflow-wrap: anywhere;
}

@media (max-width: 480px) {
  /* Give the canvas and the 16-column grid back the body padding; at 400 px
     every pixel of width shows up as swatch size. */
  #cc-scene .widget-body { padding: 1rem 0.75rem; }
  #cc-scene .cc-scene-range { font-size: 0.68rem; padding: 0.3rem 0.45rem; }
}

/* ── cc-dac (build/widgets/cc-dac.css) ─────────────────────────── */
/* ── cc-dac · DAC writes against the beam ──────────────────────────────── */

/* The header carries a long title plus two buttons; without wrapping it
   overflows the card at phone width. */
#cc-dac .widget-header { flex-wrap: wrap; gap: 0.6rem; }

/* Canvas left, controls right, stats across the bottom. The picture is 360 by
   220 logical pixels, and the split column is wide enough to draw it at 1:1. */
#cc-dac .widget-body.split { align-items: start; }
#cc-dac .cc-dac-screen { display: grid; gap: 0.6rem; min-width: 0; }
#cc-dac .stat-grid { grid-column: 1 / -1; margin-top: 0; }
#cc-dac .stat-value { font-size: 0.95rem; }

#cc-dac .control-label { flex-wrap: wrap; gap: 0.3rem; }
#cc-dac .toggle-group { flex-wrap: wrap; }
#cc-dac select.dropdown { width: 100%; }
/* Split loads half the range per frame, so the entry count is not the
   program's to choose; the control has to look as disabled as it is. */
#cc-dac select.dropdown:disabled { opacity: 0.45; cursor: not-allowed; }

/* The 0.25 step grid cannot land on the AT preset's 8.15625, so the model keeps
   the exact figure and the group dims when the slider takes the cost over. */
#cc-dac .toggle-group.cc-custom button.active {
  background: transparent;
  color: var(--tut-muted);
}

#cc-dac .cc-check {
  display: flex;
  align-items: center;
  gap: 0.55rem;
}
#cc-dac .cc-check input {
  width: 1rem;
  height: 1rem;
  accent-color: var(--tut-accent);
  cursor: pointer;
  flex: none;
}
#cc-dac .cc-check label {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--tut-muted);
  cursor: pointer;
}

#cc-dac .cc-dac-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.85rem;
  font-family: var(--mono);
  font-size: 0.67rem;
  letter-spacing: 0.03em;
  color: var(--tut-muted);
}
#cc-dac .cc-dac-legend > span { display: inline-flex; align-items: center; gap: 0.35rem; }
#cc-dac .cc-key {
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 2px;
  flex: none;
}
#cc-dac .cc-key-blank { background: #4d4d61; }
#cc-dac .cc-key-clean { background: var(--tut-success); }
#cc-dac .cc-key-late { background: var(--tut-danger); }
#cc-dac .cc-key-beam { background: var(--tut-warn); }
#cc-dac .cc-dac-written strong { color: var(--tut-soft); font-weight: 600; }

@media (max-width: 720px) {
  #cc-dac .cc-dac-legend { font-size: 0.64rem; gap: 0.25rem 0.7rem; }
  #cc-dac .stat-grid { grid-template-columns: repeat(auto-fit, minmax(96px, 1fr)); }
}

/* ── cc-copper (build/widgets/cc-copper.css) ─────────────────────────── */
/* ── cc-copper · §04 · A Copper list, run against the beam ─────────────── */

/* The raster is 320 by 256 logical pixels and CC.createPixelView draws it at an
   integer scale, so the canvas column is sized to hold it at 1x with a couple
   of pixels to spare. A wider column would only add letterbox. */
#cc-copper .widget-body {
  grid-template-columns: 322px minmax(0, 1fr);
  grid-template-rows: auto auto auto;
  align-items: start;
  gap: 1rem 1.25rem;
}
#cc-copper .cc-copper-stage { grid-column: 1; grid-row: 1; }
#cc-copper .cc-copper-words-wrap { grid-column: 1; grid-row: 2; }
/* The editor runs the full height beside them, which is where a 33-instruction
   list needs the room. */
#cc-copper .cc-copper-editor { grid-column: 2; grid-row: 1 / 3; }
#cc-copper .cc-copper-controls { grid-column: 1 / -1; grid-row: 3; }

/* The title and the preset buttons share one line on a wide screen and take
   their own lines when the column narrows. */
#cc-copper .widget-header { flex-wrap: wrap; gap: 0.5rem; }

#cc-copper canvas { width: 100%; }

#cc-copper .cc-copper-hint {
  margin: 0.5rem 0 0;
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--tut-muted);
  font-family: var(--mono);
}

/* ── Editor ───────────────────────────────────────────────────────────── */
#cc-copper .cc-copper-editor {
  display: grid;
  gap: 0.6rem;
  align-content: start;
  min-width: 0;
}

#cc-copper .cc-copper-editor-head {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--tut-muted);
  letter-spacing: 0.04em;
}

/* One stop that steps over the ~200 the instruction rows hold. It is out of
   the layout until it takes focus, then it reads as a small button in the gap
   above the list. */
#cc-copper .cc-copper-skip {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
  background: none;
  color: inherit;
}

#cc-copper .cc-copper-skip:focus {
  position: static;
  display: inline-block;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip-path: none;
  justify-self: start;
  text-decoration: none;
  padding: 0.3rem 0.6rem;
  border: 1px solid var(--tut-accent);
  border-radius: 5px;
  background: var(--tut-surface);
  color: var(--tut-text);
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.04em;
  cursor: pointer;
}

#cc-copper .cc-copper-list {
  display: grid;
  gap: 3px;
  max-height: 430px;
  overflow-y: auto;
  padding-right: 2px;
}

#cc-copper .cc-copper-ins {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.3rem;
  padding: 0.28rem 0.4rem;
  border: 1px solid var(--tut-border-soft);
  border-left: 3px solid var(--tut-border);
  border-radius: 5px;
  background: var(--tut-bg);
  font-family: var(--mono);
  font-size: 0.72rem;
  color: var(--tut-soft);
}

/* The Copper is parked on this instruction while the beam is on this line. */
#cc-copper .cc-copper-ins.cc-copper-active {
  border-left-color: var(--tut-accent);
  background: rgba(167, 139, 250, 0.08);
}
/* This instruction's register write is just behind the beam. */
#cc-copper .cc-copper-ins.cc-copper-firing {
  border-left-color: var(--tut-warn);
  background: rgba(251, 191, 36, 0.10);
}
#cc-copper .cc-copper-ins.cc-copper-unreached { opacity: 0.55; }

#cc-copper .cc-copper-ins-num {
  min-width: 1.6rem;
  color: var(--tut-muted);
  font-size: 0.68rem;
  letter-spacing: 0.04em;
}

#cc-copper .cc-copper-ins-end {
  margin-top: 0.1rem;
  border-left-color: var(--tut-border);
  color: var(--tut-muted);
}
#cc-copper .cc-copper-ins-fixed { font-size: 0.68rem; }

#cc-copper .cc-copper-field {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
}
/* [hidden] loses to the display rule above, so restate it. */
#cc-copper .cc-copper-field[hidden] { display: none; }

#cc-copper .cc-copper-field-label {
  color: var(--tut-muted);
  font-size: 0.66rem;
  letter-spacing: 0.04em;
}

#cc-copper .cc-copper-number,
#cc-copper .cc-copper-hex {
  font-family: var(--mono);
  font-size: 0.72rem;
  background: var(--tut-code-bg);
  color: var(--tut-text);
  border: 1px solid var(--tut-code-border);
  border-radius: 4px;
  padding: 0.16rem 0.3rem;
}
#cc-copper .cc-copper-number { width: 3.6rem; }
#cc-copper .cc-copper-hex { width: 3rem; text-transform: uppercase; }
#cc-copper .cc-copper-number:focus-visible,
#cc-copper .cc-copper-hex:focus-visible,
#cc-copper .cc-copper-check:focus-visible {
  outline: 2px solid var(--tut-accent);
  outline-offset: 1px;
}
#cc-copper .cc-copper-bad { border-color: var(--tut-danger); color: var(--tut-danger); }

#cc-copper select.cc-copper-kind,
#cc-copper select.cc-copper-register {
  font-size: 0.7rem;
  padding: 0.16rem 0.3rem;
}
#cc-copper select.cc-copper-kind { width: 4.6rem; }

#cc-copper .cc-copper-check {
  accent-color: var(--tut-accent);
  width: 0.85rem;
  height: 0.85rem;
  margin: 0;
}

#cc-copper .cc-copper-swatch {
  width: 0.95rem;
  height: 0.95rem;
  border-radius: 3px;
  border: 1px solid var(--tut-border);
  flex: none;
}

#cc-copper .cc-copper-note {
  margin-left: auto;
  font-size: 0.64rem;
  color: var(--tut-muted);
  letter-spacing: 0.03em;
  white-space: nowrap;
}
#cc-copper .cc-copper-unreached .cc-copper-note { color: var(--tut-danger); }

#cc-copper .cc-copper-row-buttons { display: inline-flex; gap: 2px; }
#cc-copper .cc-copper-icon {
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1;
  width: 1.4rem;
  height: 1.4rem;
  padding: 0;
  border: 1px solid var(--tut-border);
  border-radius: 4px;
  background: var(--tut-surface);
  color: var(--tut-soft);
  cursor: pointer;
}
#cc-copper .cc-copper-icon:hover { border-color: var(--tut-accent); color: var(--tut-text); }
#cc-copper .cc-copper-delete:hover { border-color: var(--tut-danger); color: var(--tut-danger); }

#cc-copper .cc-copper-add .btn { padding: 0.35rem 0.7rem; font-size: 0.7rem; }

#cc-copper .cc-copper-error {
  margin: 0;
  padding: 0.45rem 0.6rem;
  border: 1px solid var(--tut-danger);
  border-radius: 5px;
  background: rgba(248, 113, 113, 0.08);
  color: var(--tut-danger);
  font-size: 0.74rem;
  line-height: 1.45;
}
#cc-copper .cc-copper-error[hidden] { display: none; }

/* ── Word panel ───────────────────────────────────────────────────────── */
#cc-copper .cc-copper-words-title {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tut-muted);
  margin-bottom: 0.35rem;
}

#cc-copper .cc-copper-words {
  margin: 0;
  max-height: 168px;
  overflow-y: auto;
  padding: 0.6rem 0.8rem;
  background: var(--tut-code-bg);
  border: 1px solid var(--tut-code-border);
  border-radius: 8px;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.55;
  color: var(--tut-soft);
  white-space: pre;
}
#cc-copper .cc-copper-word-line { display: block; }
#cc-copper .cc-copper-word-active {
  background: rgba(167, 139, 250, 0.14);
  color: var(--tut-text);
}

/* ── Phone ────────────────────────────────────────────────────────────── */
@media (max-width: 720px) {
  /* One column, and the explicit rows above go with it: the editor spans two
     rows on a wide screen and would otherwise share a cell with the words. */
  #cc-copper .widget-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: none; }
  #cc-copper .cc-copper-stage,
  #cc-copper .cc-copper-editor,
  #cc-copper .cc-copper-words-wrap,
  #cc-copper .cc-copper-controls { grid-column: 1; grid-row: auto; }
  #cc-copper .cc-copper-list { max-height: 260px; }
  #cc-copper .cc-copper-note { margin-left: 0; }
}

/* The skip link is hidden until focused; tutorial.css marks .sr-only with
   !important, so revealing it on focus has to match that weight. */
#cc-copper .cc-copper-skip:focus {
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 0 0.4rem !important;
  padding: 0.3rem 0.6rem !important;
  overflow: visible !important;
  clip: auto !important;
  display: inline-block;
  font-size: 0.78rem;
  color: var(--tut-bg);
  background: var(--tut-accent);
  border-radius: 6px;
  text-decoration: none;
}

/* ── cc-rotate (build/widgets/cc-rotate.css) ─────────────────────────── */
/* ── cc-rotate (§05) · direction and rate lab ───────────────────────── */

/* Three buttons plus a long title do not fit one flex line on a phone. */
#cc-rotate .widget-header { flex-wrap: wrap; gap: 0.7rem; }

/* Nothing on this canvas responds to the pointer, and every mark is drawn
   with vector calls rather than an indexed blit, so the page-level
   pixelated hint and the crosshair cursor both come off. */
#cc-rotate canvas { cursor: default; image-rendering: auto; }

/* The lookup buttons carry formulas and the clock buttons carry product
   names, which the house uppercase transform mangles. */
#cc-rotate .toggle-group { flex-wrap: wrap; max-width: 100%; }
#cc-rotate .toggle-group button { text-transform: none; white-space: nowrap; letter-spacing: 0.02em; }

#cc-rotate .control-label { flex-wrap: wrap; gap: 0.5rem; }

#cc-rotate input[type="number"] {
  font-family: var(--mono);
  font-size: 0.85rem;
  background: var(--tut-bg);
  color: var(--tut-text);
  border: 1px solid var(--tut-border);
  border-radius: 5px;
  padding: 0.4rem 0.6rem;
  width: 100%;
  max-width: 11rem;
}
#cc-rotate input[type="number"]:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 1px; }
#cc-rotate input:disabled { opacity: 0.45; cursor: not-allowed; }

/* Three readouts are sentences, not numbers: they take the full grid width
   at a size that does not wrap mid-word on a 400 px column. */
#cc-rotate .cc-rotate-wide { grid-column: 1 / -1; }
#cc-rotate .cc-rotate-wide .stat-value {
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--tut-soft);
}
#cc-rotate .stat-value { overflow-wrap: anywhere; }

@media (max-width: 720px) {
  #cc-rotate .widget-header .btn { padding: 0.45rem 0.7rem; font-size: 0.72rem; }
  #cc-rotate .toggle-group button { font-size: 0.68rem; padding: 0.32rem 0.5rem; }
}

/* ── cc-crng (build/widgets/cc-crng.css) ─────────────────────────── */
/* ── cc-crng · One CRNG chunk, four readers ──────────────────────── */

/* The header carries a title, a long preset name and the play button. The
   house rule is one flex row with no wrap, which clips the select on a phone. */
#cc-crng .widget-header { flex-wrap: wrap; gap: 0.6rem 0.9rem; }
#cc-crng .cc-crng-preset { display: flex; align-items: center; gap: 0.6rem; flex: 1 1 250px; min-width: 0; }
#cc-crng .cc-crng-preset .control-label { flex: none; }
#cc-crng select.dropdown { flex: 1 1 auto; width: 100%; min-width: 0; }

/* The canvas is a fixed logical picture (480 by 160, or 300 by 300 when the
   lanes stack two by two). Capping its width keeps the integer scale at 1x and
   the letterbox thin. Nothing on it is clickable, so the house crosshair lies. */
#cc-crng canvas { max-width: 520px; margin: 0 auto; cursor: default; }

/* ── The chunk byte row ─────────────────────────────────────────── */
#cc-crng .cc-crng-chunk { display: grid; gap: 0.4rem; }
#cc-crng .cc-crng-chunk-title {
  font-family: var(--mono);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tut-muted);
}
#cc-crng .cc-crng-chunk-title { color: var(--tut-soft); }
#cc-crng .cc-crng-chunk-title span { text-transform: none; letter-spacing: 0; color: var(--tut-muted); }

/* Explicit track minimums, so the inputs' own intrinsic width (a text input is
   20 characters wide by default) cannot inflate the columns. */
#cc-crng .cc-crng-bytes {
  display: grid;
  grid-template-columns: repeat(16, minmax(2.1rem, 1fr));
  gap: 3px;
  font-family: var(--mono);
}
#cc-crng .cc-crng-cell { display: grid; gap: 2px; justify-items: stretch; }
#cc-crng .cc-crng-tag {
  font-size: 0.56rem;
  letter-spacing: 0.04em;
  text-align: center;
  color: var(--tut-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
#cc-crng .cc-crng-box {
  display: block;
  width: 100%;
  min-width: 0;
  box-sizing: border-box;
  font-family: var(--mono);
  font-size: 0.74rem;
  line-height: 1.5;
  padding: 0.25rem 0.1rem;
  text-align: center;
  background: var(--tut-code-bg);
  border: 1px solid var(--tut-code-border);
  border-radius: 4px;
  color: var(--tut-text);
}
#cc-crng .cc-crng-fixed { color: var(--tut-muted); }
#cc-crng input.cc-crng-byte { text-transform: uppercase; caret-color: var(--tut-accent); }
#cc-crng input.cc-crng-byte:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 1px; }
#cc-crng input.cc-crng-byte.cc-crng-bad { border-color: var(--tut-danger); color: var(--tut-danger); }

#cc-crng .cc-crng-hint { margin: 0; font-size: 0.72rem; color: var(--tut-muted); }
#cc-crng .cc-crng-error { margin: 0; font-size: 0.78rem; color: var(--tut-danger); font-family: var(--mono); }
/* Kept in the accessibility tree when empty so the status update is announced;
   display:none would take it out and swallow the first message. */
#cc-crng .cc-crng-error:empty { height: 0; overflow: hidden; }

/* ── The four verdict rows ──────────────────────────────────────── */
#cc-crng .cc-crng-verdicts { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.45rem; }
#cc-crng .cc-crng-lane {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr) minmax(0, 15rem);
  gap: 0.2rem 0.7rem;
  align-items: baseline;
  padding: 0.5rem 0.7rem;
  background: var(--tut-bg);
  border: 1px solid var(--tut-border-soft);
  border-radius: 6px;
}
#cc-crng .cc-crng-lane-n {
  grid-row: span 2;
  font-family: var(--mono);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--tut-accent);
}
#cc-crng .cc-crng-reader {
  grid-row: span 2;
  font-size: 0.82rem;
  color: var(--tut-text);
  display: grid;
  gap: 0.15rem;
}
#cc-crng .cc-crng-rule { font-family: var(--mono); font-size: 0.7rem; color: var(--tut-muted); line-height: 1.5; }
#cc-crng .cc-crng-verdict { font-family: var(--mono); font-size: 0.82rem; font-weight: 600; color: var(--tut-soft); }
#cc-crng .cc-crng-verdict.is-forward { color: var(--tut-info); }
#cc-crng .cc-crng-verdict.is-reverse { color: var(--tut-warn); }
#cc-crng .cc-crng-verdict.is-pingpong { color: var(--tut-accent); }
#cc-crng .cc-crng-verdict.is-off { color: var(--tut-muted); }
#cc-crng .cc-crng-verdict.is-broken { color: var(--tut-danger); }
#cc-crng .cc-crng-note { font-size: 0.72rem; color: var(--tut-muted); line-height: 1.5; }
#cc-crng .cc-crng-note:empty { display: none; }

/* Six readouts of hex, decimal and a register range: the house 1.05rem value
   wraps every one of them onto two lines in a 110px stat box. */
#cc-crng .stat-value { font-size: 0.86rem; line-height: 1.45; }
#cc-crng .stat-grid { grid-template-columns: repeat(auto-fit, minmax(128px, 1fr)); }

/* ── The flags bit strip ────────────────────────────────────────── */
#cc-crng .cc-crng-bits { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem 0.75rem; }
#cc-crng .cc-crng-bits-label,
#cc-crng .cc-crng-bits-key {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--tut-muted);
}
#cc-crng .cc-crng-bitrow { display: flex; gap: 2px; }
#cc-crng .cc-crng-bit {
  width: 1.05rem;
  padding: 0.1rem 0;
  text-align: center;
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--tut-soft);
  background: var(--tut-code-bg);
  border: 1px solid var(--tut-code-border);
  border-radius: 3px;
}
#cc-crng .cc-crng-bit:nth-child(4n) { margin-right: 0.3rem; }
#cc-crng .cc-crng-bit:last-child { margin-right: 0; }
#cc-crng .cc-crng-bit-key { border-bottom: 2px solid var(--tut-accent); color: var(--tut-text); }
#cc-crng .cc-crng-bit.cc-crng-bit-set { color: var(--tut-text); border-color: var(--tut-accent); }

@media (max-width: 720px) {
  /* Two rows of eight, so a 400 px column never scrolls the page sideways. */
  #cc-crng .cc-crng-bytes { grid-template-columns: repeat(8, minmax(2.1rem, 1fr)); min-width: 0; }
  #cc-crng .cc-crng-lane { grid-template-columns: 1.4rem minmax(0, 1fr); }
  #cc-crng .cc-crng-verdict,
  #cc-crng .cc-crng-note { grid-column: 2; }
  /* Sixteen bit cells plus their gaps must clear a 313 px column. */
  #cc-crng .cc-crng-bit { width: 0.95rem; font-size: 0.68rem; }
}

/* ── cc-boing (build/widgets/cc-boing.css) ─────────────────────────── */
/* ── cc-boing · §07 · The Boing Ball, rebuilt from its register values ── */

/* The title, the three view buttons and the two action buttons do not fit on
   one 680 px line, so the title takes its own row and the two groups share the
   next one. */
#cc-boing .widget-header { flex-wrap: wrap; gap: 0.6rem 0.9rem; }
#cc-boing .widget-title { flex: 1 1 100%; }
#cc-boing .toggle-group { flex-wrap: wrap; }

/* The canvas carries three layouts: the 320 by 200 screen, and the bitplane
   grid in two or three columns. The JS rewrites aspect-ratio and max-height
   when the view changes; these are the values the composite view starts on. */
#cc-boing canvas { width: 100%; }

#cc-boing .cc-boing-checks {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 0.45rem 1rem;
}

#cc-boing .cc-boing-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--tut-muted);
}

#cc-boing .cc-boing-check input {
  flex: none;
  width: 15px;
  height: 15px;
  accent-color: var(--tut-accent);
  cursor: pointer;
}

#cc-boing .cc-boing-check label { cursor: pointer; line-height: 1.35; }
#cc-boing .cc-boing-check input:checked + label { color: var(--tut-soft); }

/* Six readouts. The house grid puts five on a 680 px row and strands the sixth,
   and 1.05rem values clip inside the box; both shrink to fit one row. */
#cc-boing .stat-grid { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); gap: 0.5rem; }
#cc-boing .stat-value { font-size: 0.88rem; }

@media (max-width: 560px) {
  #cc-boing .toggle-group button { padding: 0.3rem 0.45rem; font-size: 0.66rem; }
}

/* ── cc-waterfall (build/widgets/cc-waterfall.css) ─────────────────────────── */
/* ── cc-waterfall (§08) ─────────────────────────────────────────────────
   One indexed picture, two cycling ranges, three sliders and two
   checkboxes. Everything else comes from tutorial.css.
   ──────────────────────────────────────────────────────────────────── */

/* The picture is 240 by 180 logical pixels drawn at an integer scale, so
   smoothing would blur the bands the widget is about. */
/* tutorial.css puts a crosshair on every widget canvas. Nothing here responds
   to a click, so the pointer says so. */
/* A canvas box wider than 4:3 only adds letterbox: the picture is 240 by 180
   at integer scale 2, so 480 CSS px is the whole of it and a 678 px column
   left 99 px of dead bar on each side. Same cap and centring as cc-planes,
   480 = 360 x 4/3. Below 480 px nothing changes. */
#cc-waterfall canvas {
  image-rendering: pixelated;
  cursor: default;
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
}

/* Checkboxes sit on one wrapping row rather than in their own control rows:
   both are one-word switches and the column is narrow at phone width. */
#cc-waterfall .cc-waterfall-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.4rem;
  align-items: center;
}

#cc-waterfall .cc-check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--tut-muted);
  cursor: pointer;
}

#cc-waterfall .cc-check:hover { color: var(--tut-text); }

#cc-waterfall .cc-check input {
  width: 14px;
  height: 14px;
  accent-color: var(--tut-accent);
  cursor: pointer;
  margin: 0;
}

/* Two of the six readouts carry a unit, which overflows the default
   1.05rem in a 110px stat cell. */
#cc-waterfall .stat-value { font-size: 0.9rem; }

/* ── cc-rain (build/widgets/cc-rain.css) ─────────────────────────── */
/* ── cc-rain · §08 ────────────────────────────────────────────────────
   Two panels under the picture: the six ranges as live swatch rows, and the
   repeat-period table. Both stack under the canvas on a phone. */

/* Three header items (title, rate toggle, play) do not fit on one 400 px row. */
#cc-rain .widget-header { flex-wrap: wrap; gap: 0.6rem 0.75rem; }

/* Nothing on this canvas is clickable, so the shared crosshair would lie. */
#cc-rain canvas { cursor: default; }

#cc-rain .rain-panels {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
  gap: 1.25rem;
  align-items: start;
}

#cc-rain .rain-panel-title {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tut-muted);
  margin-bottom: 0.55rem;
}

#cc-rain .rain-panel-note {
  margin: 0.55rem 0 0;
  font-size: 0.74rem;
  line-height: 1.5;
  color: var(--tut-muted);
}

/* ── The six ranges ─────────────────────────────────────────────────── */
#cc-rain .rain-ramps { display: grid; gap: 3px; }

#cc-rain .rain-ramp-row {
  display: grid;
  grid-template-columns: 8.5rem repeat(5, minmax(0, 1fr));
  gap: 3px;
  align-items: center;
}

#cc-rain .rain-ramp-name {
  display: flex;
  justify-content: space-between;
  gap: 0.4rem;
  font-family: var(--mono);
  font-size: 0.66rem;
  color: var(--tut-muted);
  padding-right: 0.35rem;
}

#cc-rain .rain-ramp-rate { color: var(--tut-soft); }

#cc-rain .rain-ramp-cell {
  height: 1.3rem;
  border: 1px solid var(--tut-border-soft);
  border-radius: 3px;
}

/* Position and shape, not hue: the ring reads the same for every reader. */
#cc-rain .rain-ramp-cell.is-drop { box-shadow: inset 0 0 0 2px var(--tut-text); }

/* ── Repeat periods ─────────────────────────────────────────────────── */
#cc-rain .rain-period-rows { margin-top: 0.75rem; }

#cc-rain .rain-period-row {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.15rem 0.8rem;
  padding: 0.35rem 0;
  border-bottom: 1px solid var(--tut-border-soft);
  font-size: 0.78rem;
}

#cc-rain .rain-period-label { color: var(--tut-muted); }

#cc-rain .rain-period-value {
  font-family: var(--mono);
  font-size: 0.74rem;
  color: var(--tut-soft);
}

#cc-rain .rain-period-row.is-joint .rain-period-value {
  color: var(--tut-accent);
  font-weight: 600;
}

#cc-rain select.dropdown { width: 100%; max-width: 100%; }

/* ── Checkboxes ─────────────────────────────────────────────────────── */
#cc-rain .rain-checks { display: flex; flex-wrap: wrap; gap: 0.5rem 1.5rem; }

#cc-rain .rain-check {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--mono);
  font-size: 0.76rem;
  color: var(--tut-soft);
}

#cc-rain .rain-check input {
  width: 0.95rem;
  height: 0.95rem;
  accent-color: var(--tut-accent);
  cursor: pointer;
}

#cc-rain .rain-check label { cursor: pointer; }

@media (max-width: 720px) {
  #cc-rain .rain-panels { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  #cc-rain .rain-ramp-row { grid-template-columns: 6rem repeat(5, minmax(0, 1fr)); }
  #cc-rain .rain-ramp-name { flex-direction: column; align-items: flex-start; gap: 0; font-size: 0.64rem; }

  /* The picture is 320 logical pixels wide. Inside the body padding a phone
     column leaves about 302, which forces a fractional scale that drops pixel
     rows; bleeding to the widget edge buys back enough for a clean 1:1. */
  #cc-rain canvas {
    width: calc(100% + 2.5rem);
    margin-left: -1.25rem;
    margin-right: -1.25rem;
    border-left: 0;
    border-right: 0;
    border-radius: 0;
  }
}

/* ── cc-blendshift (build/widgets/cc-blendshift.css) ─────────────────────────── */
/* ── cc-blendshift · §09 · hard shift against BlendShift ────────────────── */

/* Title, ramp toggle and play button wrap instead of squeezing at 400 px. */
#cc-blendshift .widget-header { flex-wrap: wrap; gap: 0.6rem; }

/* The picture is 384 by 160 logical pixels. Capping the box just above that
   keeps the pixel view at an exact 1x on a 720 px column, so the bands and the
   8 chips under them stay crisp instead of landing on a fractional scale. */
#cc-blendshift canvas { max-width: 424px; margin-left: auto; margin-right: auto; }

/* The scrub slider is live only while the widget is paused, and it ships
   disabled. Same treatment as the cc-dac dropdown and the cc-rotate inputs:
   the control has to look as disabled as it is, or a reader drags it and
   nothing moves. */
#cc-blendshift input[type="range"]:disabled { opacity: 0.45; cursor: not-allowed; }

#cc-blendshift .cc-drift {
  border: 1px solid var(--tut-border);
  border-radius: 8px;
  padding: 0.85rem 0.9rem;
  background: rgba(0, 0, 0, 0.18);
  display: grid;
  gap: 0.7rem;
}

#cc-blendshift .cc-drift-title {
  font-family: var(--mono);
  font-size: 0.68rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--tut-soft);
}

#cc-blendshift .cc-drift-note,
#cc-blendshift .cc-drift-counts {
  margin: 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.55;
  color: var(--tut-muted);
}

#cc-blendshift .cc-drift-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

#cc-blendshift .cc-drift-foot .stat { flex: 1 1 200px; margin: 0; }

@media (max-width: 720px) {
  #cc-blendshift .stat-grid { grid-template-columns: repeat(auto-fit, minmax(92px, 1fr)); }
  #cc-blendshift .toggle-group button { padding: 0.35rem 0.5rem; }
}

/* ── cc-daylight (build/widgets/cc-daylight.css) ─────────────────────────── */
/* ── cc-daylight · §10 · A day of palettes ─────────────────────────────── */

/* The mode label runs long, so the header wraps instead of pushing the widget
   wider than the column. */
#cc-daylight .widget-header { flex-wrap: wrap; gap: 0.6rem; }
#cc-daylight .toggle-group { flex-wrap: wrap; max-width: 100%; }

#cc-daylight .widget-body { gap: 0.9rem; }

/* The valley is 240 by 135 logical pixels at integer scale 2, so 480 CSS px is
   the whole picture. A wider box only adds letterbox: at 678 px it wasted
   99 px on each side and 35 px top and bottom. 16/9 of 480 is 270, under the
   340 px height cap, so the box lands exactly on the picture. */
#cc-daylight canvas {
  max-width: 480px;
  margin-left: auto;
  margin-right: auto;
}

#cc-daylight .cc-daylight-row-label {
  font-family: var(--mono);
  font-size: 0.65rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--tut-muted);
  margin-bottom: 0.35rem;
}

/* ── Keyframe strip ─────────────────────────────────────────────────────
   One tick per keyframe, positioned by its second of the day. The two ticks
   being interpolated carry .active; the playhead is the light line. */
#cc-daylight .cc-daylight-strip {
  position: relative;
  height: 26px;
  border-radius: 4px;
  border: 1px solid var(--tut-border-soft);
  background: var(--tut-bg);
}

#cc-daylight .cc-daylight-key {
  position: absolute;
  top: 4px;
  width: 9px;
  height: 16px;
  padding: 0;
  transform: translateX(-50%);
  background: var(--tut-surface-2);
  border: 1px solid var(--tut-border);
  border-radius: 2px;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s;
}

#cc-daylight .cc-daylight-key:hover { border-color: var(--tut-accent); }
#cc-daylight .cc-daylight-key:focus-visible { outline: 2px solid var(--tut-accent); outline-offset: 2px; }
#cc-daylight .cc-daylight-key.active { background: var(--tut-accent); border-color: var(--tut-accent); }

#cc-daylight .cc-daylight-head {
  position: absolute;
  top: -2px;
  width: 2px;
  height: 28px;
  margin-left: -1px;
  background: var(--tut-text);
  box-shadow: 0 0 6px rgba(232, 232, 238, 0.45);
  pointer-events: none;
}

#cc-daylight .cc-daylight-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 0.25rem;
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.06em;
  color: var(--tut-muted);
}

/* ── Strata chips ───────────────────────────────────────────────────────
   Live colors of entries 20 to 25. They are one color at sunrise and diverge
   through the morning, which is the point of the section. */
#cc-daylight .cc-daylight-strata {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(52px, 1fr));
  gap: 0.35rem;
}

#cc-daylight .cc-daylight-chip {
  display: grid;
  gap: 0.15rem;
  justify-items: start;
  min-width: 0;
}

#cc-daylight .cc-daylight-swatch {
  display: block;
  width: 100%;
  height: 18px;
  border-radius: 3px;
  border: 1px solid var(--tut-border-soft);
  background: #000;
}

#cc-daylight .cc-daylight-chip-name,
#cc-daylight .cc-daylight-chip-state {
  font-family: var(--mono);
  font-size: 0.62rem;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

#cc-daylight .cc-daylight-chip-name { color: var(--tut-muted); }
#cc-daylight .cc-daylight-chip-state { color: var(--tut-soft); }

/* ── Controls ───────────────────────────────────────────────────────────── */
#cc-daylight .cc-daylight-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-family: var(--mono);
  font-size: 0.73rem;
  letter-spacing: 0.04em;
  color: var(--tut-muted);
  cursor: pointer;
}

#cc-daylight .cc-daylight-check input {
  width: 15px;
  height: 15px;
  accent-color: var(--tut-accent);
  cursor: pointer;
  flex: none;
}

/* The string reader produces an offset of 1,980,086,400 s, which is wider than
   a stat cell; let it wrap rather than overflow the widget. */
#cc-daylight .stat-value {
  font-size: 0.95rem;
  overflow-wrap: anywhere;
}

/* Six readouts in two rows of three rather than five and an orphan. */
#cc-daylight .stat-grid { grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); }

@media (max-width: 560px) {
  #cc-daylight .cc-daylight-chip-name,
  #cc-daylight .cc-daylight-chip-state { font-size: 0.58rem; }
  #cc-daylight .stat-value { font-size: 0.85rem; }
  #cc-daylight .stat-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  /* Two rows of three rather than five and a widow. */
  #cc-daylight .cc-daylight-strata { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  /* The five morning keyframes are 30 minutes apart, about 6 px of a 300 px
     strip, so the ticks narrow rather than merge into one block. */
  #cc-daylight .cc-daylight-key { width: 5px; }
}

/* ── cc-planes (build/widgets/cc-planes.css) ─────────────────────────── */
/* ── cc-planes · §11 · Layers and frames in four bitplanes ──────────── */

/* tutorial.css lays the header out as a single non-wrapping flex row, and
   .widget clips its overflow, so four rule buttons plus the play button lose
   their right-hand end on a phone. Both rows wrap here. */
#cc-planes .widget-header { flex-wrap: wrap; gap: 0.6rem; }
#cc-planes .toggle-group { flex-wrap: wrap; }

/* The picture is 128 by 96 logical pixels drawn at an integer scale. A canvas
   box wider than 4:3 only adds letterbox, so cap the width at the height's
   worth and centre it. */
#cc-planes canvas {
  max-width: 428px;
  margin-left: auto;
  margin-right: auto;
}

/* The 16 entries of a 4-bit palette. Sixteen columns on a wide column, two
   rows of eight on a phone, where a 16-column cell is narrower than its own
   bit label. */
#cc-planes .cc-planes-swatches {
  display: grid;
  grid-template-columns: repeat(16, minmax(0, 1fr));
  gap: 3px;
  min-width: 19rem;
  margin-top: 0.75rem;
}

#cc-planes .cc-planes-swatch {
  display: block;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  text-align: center;
}

#cc-planes .cc-planes-chip {
  display: block;
  aspect-ratio: 1;
  border-radius: 3px;
  border: 1px solid var(--tut-border);
  background: var(--tut-code-bg);
}

#cc-planes .cc-planes-swatch.selected .cc-planes-chip {
  box-shadow: 0 0 0 2px var(--tut-accent);
}

#cc-planes .cc-planes-bits {
  display: block;
  margin-top: 2px;
  font-family: var(--mono);
  font-size: 0.55rem;
  line-height: 1.3;
  letter-spacing: 0;
  color: var(--tut-muted);
}

#cc-planes .cc-planes-swatch.selected .cc-planes-bits { color: var(--tut-text); }
#cc-planes .cc-planes-swatch:hover .cc-planes-bits { color: var(--tut-soft); }

#cc-planes .cc-planes-swatch:focus-visible {
  outline: 2px solid var(--tut-accent);
  outline-offset: 2px;
  border-radius: 4px;
}

#cc-planes .cc-planes-note {
  margin: 0.55rem 0 0;
  font-family: var(--mono);
  font-size: 0.72rem;
  line-height: 1.5;
  color: var(--tut-muted);
}

/* A control row that does nothing in the current rule is removed, not greyed,
   so nothing on screen is inert. */
#cc-planes .control-row[hidden] { display: none; }

#cc-planes .cc-planes-check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

#cc-planes .cc-planes-check input[type="checkbox"] {
  width: 15px;
  height: 15px;
  accent-color: var(--tut-accent);
  cursor: pointer;
  flex: 0 0 auto;
}

#cc-planes .cc-planes-check .control-label { cursor: pointer; }

@media (max-width: 620px) {
  #cc-planes .cc-planes-swatches {
    grid-template-columns: repeat(8, minmax(0, 1fr));
    gap: 4px;
    min-width: 0;
  }
  #cc-planes .cc-planes-bits { font-size: 0.6rem; }
  #cc-planes .cc-planes-note { font-size: 0.68rem; }
}

/* ── cc-tables (build/widgets/cc-tables.css) ─────────────────────────── */
/* ── cc-tables · §12 · the stale light and blend tables ─────────────────── */

/* Five mode buttons and a play button do not fit one header row on a phone,
   so the mode group lives in the control column under its own label and the
   header keeps only the title and play. */
#cc-tables .widget-header { flex-wrap: wrap; gap: 0.6rem; }

#cc-tables .toggle-group { flex-wrap: wrap; row-gap: 3px; }
#cc-tables .toggle-group button { flex: 0 1 auto; }

/* The picture is an indexed framebuffer drawn at an integer scale; smoothing
   would blur the table cells the panel is there to show. */
#cc-tables canvas { image-rendering: pixelated; }

/* The table panel is a second canvas under the scene, built by the JS when the
   checkbox first asks for it. Its own box caps at the picture's width so it
   never letterboxes, and nothing on it responds to a click. */
#cc-tables .cc-tables-panel {
  margin-left: auto;
  margin-right: auto;
  cursor: default;
}
/* .widget canvas sets display: block, which outranks the UA rule for [hidden]. */
#cc-tables .cc-tables-panel[hidden] { display: none; }

#cc-tables .control-label[for="cc-tables-show"] { cursor: pointer; }
#cc-tables input[type="checkbox"] {
  accent-color: var(--tut-accent);
  width: 15px;
  height: 15px;
  cursor: pointer;
}

/* Five readouts, and two of them carry a per-step unit, so they need a little
   more room than the house default before they wrap. */
#cc-tables .stat-grid { grid-template-columns: repeat(auto-fit, minmax(124px, 1fr)); }
#cc-tables .stat-value { font-size: 0.95rem; }

@media (max-width: 480px) {
  #cc-tables .toggle-group button { padding: 0.35rem 0.5rem; }
  #cc-tables .stat-grid { grid-template-columns: repeat(auto-fit, minmax(104px, 1fr)); }
}

/* ── cc-sampler (build/widgets/cc-sampler.css) ─────────────────────────── */
/* ── cc-sampler · §13 · Sampling an index texture ───────────────────────── */

/* Three long mode names plus the play button overflow one flex line at phone
   width, so the header wraps instead of pushing the page sideways. */
#cc-sampler .widget-header { flex-wrap: wrap; gap: 0.6rem 0.9rem; }
#cc-sampler .toggle-group { flex-wrap: wrap; }

/* The canvas carries its own 1 px panel frames; the card border around it
   would double them up. */
#cc-sampler canvas { border-color: transparent; cursor: crosshair; }

/* Two columns of controls on a wide card, one on a narrow one. */
#cc-sampler .cc-sampler-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 0.85rem 1.4rem;
  align-items: start;
}

/* A control row that holds a toggle group instead of a slider still needs the
   label above it, and the group must not stretch to the column width. */
#cc-sampler .cc-sampler-grid .toggle-group { justify-self: start; max-width: 100%; }

/* The checkbox sits at the right end of its own label, which is already a
   space-between flex row, so it lines up with the value pills above it. */
#cc-sampler .cc-sampler-check .control-label {
  cursor: pointer;
  gap: 0.6rem;
  padding: 0.3rem 0;
}
#cc-sampler .cc-sampler-check input[type="checkbox"] {
  accent-color: var(--tut-accent);
  width: 15px;
  height: 15px;
  margin: 0;
  cursor: pointer;
  flex: none;
}

/* Five readouts, and three of them carry a phrase rather than a number, so
   they need a wider minimum than the house 110 px and a smaller value type. */
#cc-sampler .stat-grid { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
#cc-sampler .stat-value { font-size: 0.92rem; }

@media (max-width: 560px) {
  /* A 16 by 9 box on a phone leaves about 110 px of height for the magnified
     sprite, which is the one thing the reader has to be able to see. The
     portrait box gives it 160 and lets the two reduced panels sit under the
     view instead of beside it. The canvas carries its landscape aspect ratio
     inline, per the house widget markup, so this override needs !important. */
  #cc-sampler canvas { aspect-ratio: 3 / 4 !important; max-height: 440px !important; }
  #cc-sampler .widget-title { font-size: 0.7rem; }
  #cc-sampler .toggle-group button { padding: 0.35rem 0.5rem; font-size: 0.68rem; }
  #cc-sampler .stat-grid { grid-template-columns: repeat(auto-fit, minmax(132px, 1fr)); }
  #cc-sampler .stat-value { font-size: 0.85rem; }
}

/* ── cc-costs (build/widgets/cc-costs.css) ─────────────────────────── */
/* ── cc-costs · §14 · Three ways to animate one palette ───────────────── */

/* Three controls in the header (title, tick toggle, play) do not fit one row
   at 400 px, so the row wraps instead of pushing the card wide. */
#cc-costs .widget-header { flex-wrap: wrap; gap: 0.6rem; }

#cc-costs .cc-costs-panels {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.9rem;
}
/* minmax(0, 1fr) above and min-width: 0 here stop the stat rows from forcing
   the grid tracks wider than the column. */
#cc-costs .cc-costs-panel { display: grid; gap: 0.4rem; min-width: 0; }

#cc-costs .cc-costs-panel-title {
  font-family: var(--mono);
  font-size: 0.66rem;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--tut-soft);
  line-height: 1.35;
  /* Two lines reserved so a title that wraps does not drop its canvas below
     the other two. */
  min-height: 2.7em;
}

/* page.css sets this for every widget canvas; repeated here so the block stands
   on its own. Smoothing a 96 by 54 picture scaled 2x or 3x would blur the
   indexed pixels the panel is about. */
#cc-costs canvas { image-rendering: pixelated; cursor: default; }

/* The house stat grid puts cells side by side; a 200 px panel column fits one
   per row, label left and value right. */
#cc-costs .stat-grid { grid-template-columns: 1fr; gap: 0.3rem; margin-top: 0.1rem; }
#cc-costs .stat {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.3rem 0.5rem;
}
#cc-costs .stat-label { margin-bottom: 0; font-size: 0.6rem; }
#cc-costs .stat-value { font-size: 0.82rem; }

#cc-costs .cc-costs-note {
  margin: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  font-family: var(--mono);
  font-size: 0.7rem;
  line-height: 1.6;
  color: var(--tut-muted);
}
#cc-costs .cc-costs-clock { color: var(--tut-soft); }
#cc-costs .cc-costs-clock [data-cc-out] { color: var(--tut-accent); font-weight: 600; }

#cc-costs .cc-costs-check { display: flex; align-items: center; gap: 0.55rem; }
#cc-costs .cc-costs-check input {
  width: 15px;
  height: 15px;
  accent-color: var(--tut-accent);
  cursor: pointer;
}
#cc-costs .cc-costs-check label {
  font-family: var(--mono);
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  color: var(--tut-muted);
  cursor: pointer;
}

@media (max-width: 720px) {
  #cc-costs .cc-costs-panels { grid-template-columns: 1fr; gap: 1.1rem; }
  #cc-costs .cc-costs-panel-title { min-height: 0; }
}
