/* CRUMB companion site — shared styles.
   Design tokens mirror animations/model_viz/style.py so web figures and paper
   figures read as one visual system. */

:root {
  --surface: #fcfcfb;
  --ink: #0b0b0b;
  --ink-2: #52514e;
  --ink-muted: #a7a6a0;
  --track: #f0efec;
  --node-unassigned: #eceae6;
  --patch: #2a78d6;
  --patch-soft: #e8f0fa;
  --proposal: #eb6834;
  --patch-var: #eda100;
  --good: #0ca30c;
  --bad: #d03b3b;
  --font-ui: "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-math: Georgia, "Times New Roman", serif;
  --prose-width: 60rem;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-ui);
  font-size: 1rem;
  line-height: 1.55;
}

[hidden] { display: none !important; }

var {
  font-family: var(--font-math);
  font-style: italic;
}

/* ---------------------------------------------------------- header / nav */
.site-header {
  border-bottom: 1px solid var(--track);
  background: var(--surface);
}
.site-header-inner {
  max-width: var(--prose-width);
  margin: 0 auto;
  padding: 0.9rem 1.25rem;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.4rem 1.5rem;
}
.brand { text-decoration: none; color: var(--ink); }
.brand strong { font-size: 1.05rem; letter-spacing: 0.04em; }
.brand-sub { color: var(--ink-2); font-size: 0.85rem; margin-left: 0.6rem; }
.site-header nav { display: flex; gap: 1.1rem; margin-left: auto; }
.site-header nav a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: 0.95rem;
  padding: 0.15rem 0;
  border-bottom: 2px solid transparent;
}
.site-header nav a:hover { color: var(--ink); }
.site-header nav a[aria-current="page"] {
  color: var(--ink);
  border-bottom-color: var(--ink);
}

/* ----------------------------------------------------------------- prose */
main.prose {
  max-width: var(--prose-width);
  margin: 0 auto;
  padding: 1.5rem 1.25rem 3rem;
}
h1 { font-size: 1.6rem; line-height: 1.25; margin: 1.2rem 0 0.8rem; }
h2 { font-size: 1.15rem; margin: 2.2rem 0 0.5rem; }
h3 { font-size: 1.02rem; margin: 1.5rem 0 0.4rem; }
p { margin: 0.7rem 0; }

/* equation displays (the warm-up constraints, shown on their own line) */
.eq-display {
  margin: 0.9rem 0;
  padding: 0.35rem 0.9rem;
  border-left: 3px solid var(--track);
  font-size: 1.15rem;
}
.eq-display .eq-gloss {
  color: var(--ink-2);
  font-size: 0.92rem;
  margin-left: 0.8rem;
}

/* truth tables of satisfying assignments — no gridlines, just a rule
   separating the variable names from the values */
table.truth-table { border-collapse: collapse; margin: 0.3rem 0; }
.truth-table th,
.truth-table td {
  min-width: 2.4rem;
  height: 1.45rem;
  padding: 0 0.45rem;
  text-align: center;
  font-size: 1rem;
  line-height: 1.2;
}
.truth-table thead tr { border-bottom: 1.5px solid var(--ink-2); }
.truth-table td { font-variant-numeric: tabular-nums; }
.truth-table th.determined { background: var(--patch-soft); }
.truth-table td.determined {
  background: var(--patch-soft);
  color: var(--patch);
  font-weight: 700;
}

.table-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 3.5rem;
  align-items: flex-start;
}
figure.table-fig { margin: 1rem 0; }
figure.table-fig.mini { margin: 0.6rem 0; }
.mini-title { margin: 0 0 0.35rem; color: var(--ink-2); font-size: 0.95rem; }

/* constraint list */
ol.constraint-list { list-style: none; margin: 0.9rem 0; padding: 0; }
ol.constraint-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.35rem 1rem;
  padding: 0.4rem 0.7rem;
  border-left: 3px solid var(--track);
  border-radius: 2px;
  cursor: default;
}
ol.constraint-list li:hover,
ol.constraint-list li.active,
ol.constraint-list li:focus-visible {
  border-left-color: var(--patch);
  background: var(--patch-soft);
  outline: none;
}
.constraint-list .cid { color: var(--ink-muted); font-size: 0.85rem; min-width: 1.7rem; }
.constraint-list .eq { font-size: 1.05rem; }
.constraint-list .gloss { color: var(--ink-2); font-size: 0.9rem; }

/* figure */
figure { margin: 1.4rem 0 0.4rem; }
figure svg { display: block; width: 100%; height: auto; }
figcaption { color: var(--ink-2); font-size: 0.9rem; margin-top: 0.5rem; }

/* legend */
.legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem 1.4rem;
  color: var(--ink-2);
  font-size: 0.9rem;
  margin: 0.5rem 0;
}
.legend .key { display: inline-flex; align-items: center; gap: 0.45rem; }
.key-swatch {
  display: inline-block;
  width: 0.95rem;
  height: 0.95rem;
  border: 1.5px solid var(--ink-muted);
  background: var(--node-unassigned);
}
.key-swatch.circle { border-radius: 50%; }
.key-swatch.square { border-radius: 2px; background: var(--track); }
.key-swatch.filled { background: var(--ink); border-color: var(--ink); }
.key-swatch.open { background: #fff; border-color: var(--ink); }

/* --------------------------------------------------- interactive panels */
/* Hands-on widgets sit in a labeled colored panel so they read differently
   from prose, static figures, and animations. The body tint stays lighter
   than --patch-soft so highlight fills inside (hover states, selected
   squares, determined cells) keep their contrast. */
.interactive {
  margin: 1.6rem 0;
  border: 1.5px solid #cadef2;
  border-radius: 12px;
  background: #f6fafd;
  overflow: hidden;
}
.interactive-tag {
  padding: 0.4rem 1.2rem;
  background: var(--patch-soft);
  border-bottom: 1px solid #d9e7f6;
  color: var(--patch);
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
.interactive-body { padding: 0.35rem 1.2rem 1.1rem; }
.interactive-body h3 { margin: 0.8rem 0 0.4rem; }
.interactive-body .controls.big {
  background: #fff;
  border: 1px solid #d9e7f6;
}

/* page-1 explorer: the constraint list and the graph share one view, so
   hovering either side shows the coupling without scrolling */
.explorer {
  display: grid;
  grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: 0.3rem 2rem;
  align-items: center;
}
.explorer figure { margin: 0.6rem 0 0.2rem; }
.explorer ol.constraint-list li { padding: 0.28rem 0.4rem; }
/* uniform two-line items: equation, then the gloss on its own line (at
   this column width some glosses fit inline and some wrap — ragged) */
.explorer .constraint-list .gloss {
  flex-basis: 100%;
  padding-left: 1.05rem;
  font-size: 0.75rem;
}
@media (max-width: 52rem) {
  .explorer { grid-template-columns: 1fr; }
}

/* charts render at a capped width; keep them horizontally centered */
#scaling-chart, #threshold-chart, #progress-chart, .ordering-chart,
.external-chart {
  margin-left: auto;
  margin-right: auto;
}

/* solution */
button.toggle {
  font: inherit;
  font-size: 0.98rem;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--ink);
  border-radius: 6px;
  padding: 0.45rem 1.1rem;
  cursor: pointer;
}
button.toggle:hover { background: var(--track); }
button.toggle[aria-expanded="true"] { background: var(--ink); color: var(--surface); }
#solution-panel { margin-top: 1rem; }
ol.assignment-list {
  list-style: none;
  margin: 0.6rem 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.1rem;
}
ol.assignment-list li {
  padding: 0.25rem 0.7rem;
  background: var(--track);
  border-radius: 5px;
  font-size: 1rem;
}

/* page links (blue) */
main a:not(.toggle),
.nav-home {
  color: var(--patch);
  text-decoration: none;
}
main a:not(.toggle):hover { text-decoration: underline; }

/* homepage contents list */
ol.toc { list-style: none; margin: 1rem 0; padding: 0; }
ol.toc li { margin: 0.55rem 0; }
ol.toc a { font-size: 1.1rem; padding: 0.15rem 0; }
ol.toc a:hover { text-decoration: underline; }
.toc-num { color: var(--ink-muted); margin-right: 0.25rem; }

/* back-to-home link in the header (content pages only) */
.site-header nav a.nav-home { color: var(--patch); border-bottom-color: transparent; }
.site-header nav a.nav-home:hover { color: var(--patch); text-decoration: underline; }

footer {
  border-top: 1px solid var(--track);
  color: var(--ink-2);
  font-size: 0.85rem;
  max-width: var(--prose-width);
  margin: 2rem auto 0;
  padding: 1rem 1.25rem 2.5rem;
}

/* --------------------------------------------- page 2: solving components */
.side-by-side {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem 2.5rem;
  align-items: start;
}
.truth-table.compact th,
.truth-table.compact td { min-width: 1.55rem; height: 1.3rem; font-size: 0.85rem; padding: 0 0.2rem; }
/* wide many-guest tables: name headers rotate to keep columns narrow */
.truth-table.compact th {
  writing-mode: vertical-rl;
  transform: rotate(180deg);
  vertical-align: bottom;
  height: auto;
  padding: 0.25rem 0;
}
.truth-table .ellipsis td { color: var(--ink-muted); height: 1.05rem; font-size: 0.8rem; }
.bits-label { margin: 0.3rem 0 0; font-size: 0.9rem; color: var(--ink-2); }
.bits-label strong { color: var(--ink); font-variant-numeric: tabular-nums; }

.path-title { margin: 0.6rem 0 0.2rem; font-weight: 700; }
.step-head { margin: 0 0 0.25rem; font-size: 0.92rem; color: var(--ink-2); }
.step-head .eq { color: var(--ink); font-size: 0.98rem; }

.blowup {
  display: grid;
  grid-template-columns: repeat(3, auto);
  justify-content: start;
  gap: 1rem 2.5rem;
  align-items: start;
  margin: 1rem 0;
}

.game {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 1rem 2rem;
  align-items: start;
  margin: 1rem 0;
}
.order-readout { margin: 0.5rem 0 0.2rem; font-size: 0.95rem; color: var(--ink-2); }
.order-readout .cid { color: var(--ink); font-size: 0.95rem; min-width: 0; }
.game-status { margin: 0.3rem 0; font-weight: 600; min-height: 1.5em; }
.game-status.ok { color: var(--good); }
.game-status.fail { color: var(--bad); }
button.toggle.small { font-size: 0.85rem; padding: 0.3rem 0.85rem; }

.line-chart .axis path,
.line-chart .axis line { stroke: var(--ink-muted); }
.line-chart .axis text { fill: var(--ink-2); font-size: 11px; font-family: var(--font-ui); }
.line-chart .axis-label { fill: var(--ink-2); font-size: 13px; font-family: var(--font-ui); }
.chart-line { fill: none; stroke: var(--patch); stroke-width: 2; }
.chart-line.over { stroke: var(--bad); }
.chart-dot { fill: var(--patch); }
.chart-dot.over { fill: var(--bad); }
.chart-dot.ghost { fill: var(--bad); opacity: 0.3; }
.chart-spike { stroke: var(--bad); stroke-width: 2; }
.limit-line {
  stroke: var(--ink-2);
  stroke-width: 1.5;
  stroke-dasharray: 6 4;
}
.limit-label { fill: var(--ink-2); font-size: 12px; font-family: var(--font-ui); }
.chart-empty { fill: var(--ink-muted); font-size: 14px; font-family: var(--font-ui); }
.chart-vline {
  stroke: var(--ink-muted);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.6;
}
.chart-vline-label { fill: var(--ink-2); font-size: 11px; font-family: var(--font-ui); }

.subs {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem 3rem;
  align-items: start;
}
ol.constraint-list.small li { padding: 0.22rem 0.5rem; gap: 0.3rem 0.7rem; }
.constraint-list.small .eq { font-size: 0.95rem; }
.cond-arrow { color: var(--ink-muted); margin: 0 0.35rem; }
.cond-known { color: var(--ink-2); font-size: 0.85rem; }

@media (max-width: 52rem) {
  .side-by-side, .game, .subs { grid-template-columns: 1fr; }
  .blowup { grid-template-columns: 1fr; }
}

/* -------------------------------------------- page 3: optimal metareasoning */
ul.def-list { margin: 0.7rem 0; padding-left: 1.2rem; }
ul.def-list li { margin: 0.4rem 0; }
ol.process-list { margin: 0.8rem 0; padding-left: 1.6rem; }
ol.process-list > li { margin: 1.3rem 0; padding-left: 0.35rem; }
ol.process-list h3 { margin: 0 0 0.35rem; }
.controls {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 2.5rem;
  align-items: baseline;
  margin: 1rem 0;
}
.controls label {
  display: inline-flex;
  gap: 0.55rem;
  align-items: baseline;
  font-size: 0.95rem;
  color: var(--ink-2);
}
.controls select {
  font: inherit;
  font-size: 0.95rem;
  padding: 0.25rem 0.5rem;
  border: 1.5px solid var(--ink-muted);
  border-radius: 6px;
  background: var(--surface);
  color: var(--ink);
}
.meta-grid {
  display: grid;
  grid-template-columns: 1.15fr 1fr;
  gap: 1rem 2rem;
  align-items: start;
  margin: 0.5rem 0 1rem;
}
.meta-summary { margin: 0.2rem 0 0.4rem; }
.badge-sample { color: var(--patch); font-weight: 700; font-size: 0.85rem; }
.partial-vars { color: var(--ink-2); font-size: 0.85em; }
#scaling-chart { max-width: 34rem; }
.key-swatch.dashed { border-style: dashed; border-color: var(--ink); }
.key-swatch.wrong { background: var(--ink); border: 2.5px solid var(--bad); }
.key-swatch.unknown {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 0.7rem;
  color: var(--ink-2);
}
@media (max-width: 52rem) {
  .meta-grid { grid-template-columns: 1fr; }
}

/* ------------------------------------------- previous / next page links */
.page-nav {
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  margin-top: 3rem;
  padding-top: 1rem;
  border-top: 1px solid var(--track);
  font-size: 1rem;
}
.page-nav .page-nav-next { text-align: right; }

/* ------------------------------------------------- page 3: the model page */
.controls.big {
  gap: 0.8rem 2.2rem;
  padding: 0.8rem 1rem;
  background: var(--track);
  border-radius: 8px;
}
.controls.big label { font-size: 1rem; }
.controls.big select {
  font-size: 1rem;
  padding: 0.35rem 0.6rem;
}
.controls.big button.toggle { font-size: 1rem; padding: 0.4rem 1.6rem; }

pre.pseudocode {
  background: var(--track);
  border-radius: 8px;
  padding: 0.7rem 1rem;
  font-size: 0.72rem;
  line-height: 1.45;
  overflow-x: auto;
}

/* ------------------------------------------------- the model player */
.player {
  margin: 1rem 0;
  outline: none;
}
.player:focus-visible { box-shadow: 0 0 0 2px var(--patch-soft); }
.player-stage {
  display: grid;
  grid-template-columns: minmax(0, 23rem) minmax(0, 1fr);
  gap: 0.5rem 2.5rem;
  align-items: start;
}
/* fixed-height bar: caption text changing between frames must never move
   the timeline below it */
.player-caption {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  margin: 0.4rem 0 0.2rem;
  height: 2.1rem;
  overflow: hidden;
  border-top: 1px solid var(--track);
  padding-top: 0.5rem;
}
.phase-tag {
  flex: 0 0 auto;
  min-width: 7.5rem;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--ink-2);
}
.phase-tag.good { color: var(--good); }
.phase-tag.bad { color: var(--bad); }
.phase-tag.warn { color: var(--patch-var); }
.caption-text {
  flex: 1 1 auto;
  font-size: 0.9rem;
  line-height: 1.45;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.caption-text strong { font-weight: 700; }
.step-readout {
  flex: 0 0 auto;
  color: var(--ink-muted);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}
.player-timeline svg { display: block; width: 100%; height: auto; }
.player-transport {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0.4rem 0 0;
}
.player-transport .speed-label {
  margin-left: 1rem;
  color: var(--ink-2);
  font-size: 0.9rem;
  display: inline-flex;
  gap: 0.5rem;
  align-items: baseline;
}
.player-transport select {
  font: inherit;
  font-size: 0.9rem;
  padding: 0.2rem 0.4rem;
  border: 1.5px solid var(--ink-muted);
  border-radius: 6px;
  background: var(--surface);
}

/* the memory chart panel */
.player-chart { max-width: 34rem; }
.player-chart svg { display: block; width: 100%; height: auto; }

/* the optimal path, broken down by subproblem (page 2) */
.opt-subs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.4rem;
  margin: 0.8rem 0;
  align-items: flex-start;
}
.opt-sub-block {
  border-left: 3px solid var(--track);
  padding: 0.1rem 0.6rem 0.1rem 0.8rem;
}
.opt-sub-head {
  margin: 0 0 0.1rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.opt-sub-block ol.constraint-list { margin: 0; }
.opt-sub-block ol.constraint-list li { border-left: none; padding: 0.1rem 0; }


/* hero legend swatch tones */
.key-swatch.patch { background: var(--patch); border-color: var(--patch); }
.key-swatch.amber { background: var(--patch-var); border-color: var(--ink-2); }
.key-swatch.good { background: var(--good); border-color: var(--good); opacity: 0.7; }
.key-swatch.bad { background: var(--bad); border-color: var(--bad); opacity: 0.7; }
.key-swatch.ghosted { opacity: 0.25; }

/* threshold-decay chart */
#threshold-chart { max-width: 34rem; }
.th-line { fill: none; stroke: var(--patch); stroke-width: 2; }

/* progress-prior chart: easy/hard reuse the games-page order colors */
#progress-chart { max-width: 34rem; }
.pg-line { fill: none; stroke-width: 2; }
.pg-line.easy { stroke: var(--patch); }
.pg-line.hard { stroke: var(--bad); }
.th-label { font-family: var(--font-ui); font-size: 11px; fill: var(--ink-2); }

@media (max-width: 52rem) {
  .player-stage { grid-template-columns: 1fr; }
  .phase-tag { min-width: 0; }
}

/* -------------------------------------------------- constraint graph (SVG) */
svg.constraint-graph { --transition: 300ms; }
.constraint-graph .edge {
  stroke: var(--ink-muted);
  stroke-width: 1.5;
  transition: opacity 150ms, stroke 150ms, stroke-width 150ms;
}
.constraint-graph .con-node,
.constraint-graph .var-node { transition: opacity 150ms; }
.constraint-graph .con-node rect {
  fill: var(--track);
  stroke: var(--ink-muted);
  stroke-width: 1.5;
  transition: fill 150ms, stroke 150ms;
}
.constraint-graph .con-label {
  fill: var(--ink-2);
  font-family: var(--font-ui);
  font-size: 13.5px;
}
.constraint-graph .var-node circle {
  fill: var(--node-unassigned);
  stroke: var(--ink-muted);
  stroke-width: 1.5;
  transition: fill var(--transition), stroke var(--transition);
}
.constraint-graph .var-label {
  fill: var(--ink);
  font-family: var(--font-math);
  font-style: italic;
  font-size: 20px;
  transition: fill var(--transition);
}
.constraint-graph .var-name-hint {
  fill: var(--ink);
  font-family: var(--font-math);
  font-style: italic;
  font-size: 11px;
  opacity: 0;
  transition: fill var(--transition), opacity var(--transition);
}
.constraint-graph .var-node.assigned-1 .var-name-hint,
.constraint-graph .var-node.assigned-0 .var-name-hint { opacity: 0.7; }
.constraint-graph .var-node.assigned-1 .var-name-hint { fill: #fff; }
.constraint-graph .var-node.assigned-1 circle { fill: var(--ink); stroke: var(--ink); }
.constraint-graph .var-node.assigned-1 .var-label {
  fill: #fff;
  font-family: var(--font-ui);
  font-style: normal;
}
.constraint-graph .var-node.assigned-0 circle { fill: #fff; stroke: var(--ink); }
.constraint-graph .var-node.assigned-0 .var-label {
  font-family: var(--font-ui);
  font-style: normal;
}

/* path badges + per-variable outcomes (optimal-path display) */
.constraint-graph .order-badge {
  fill: var(--patch);
  font-family: var(--font-ui);
  font-size: 12px;
  font-weight: 700;
}
.constraint-graph .var-node.wrong circle { stroke: var(--bad); stroke-width: 3; }
.constraint-graph .var-node.guessed circle { stroke-dasharray: 4 3; }
.constraint-graph .var-node.unknown circle {
  fill: var(--node-unassigned);
  stroke: var(--ink-muted);
}
.constraint-graph .var-node.unknown .var-label {
  font-family: var(--font-ui);
  font-style: normal;
  fill: var(--ink-2);
}
.constraint-graph .var-node.unknown .var-name-hint { opacity: 0.7; }

/* clickable + selected constraint squares (the ordering game) */
.constraint-graph .con-node.clickable { cursor: pointer; }
.constraint-graph .con-node.clickable:hover rect { stroke: var(--patch); }
.constraint-graph .con-node.clickable:focus-visible { outline: none; }
.constraint-graph .con-node.clickable:focus-visible rect {
  stroke: var(--patch);
  stroke-width: 2.5;
}
.constraint-graph .con-node.selected rect {
  fill: var(--patch-soft);
  stroke: var(--patch);
  stroke-width: 2;
}
.constraint-graph .con-node.selected .con-label { fill: var(--patch); }
.constraint-graph .edge.selected {
  stroke: var(--patch);
  stroke-width: 2.2;
}
/* one color language across the site: blue constraints, amber variables
   (assigned nodes keep their black/white value encoding) */
.constraint-graph .var-node.in-sub:not(.assigned) circle {
  fill: var(--patch-var);
  stroke: var(--ink-2);
  stroke-width: 1.6;
}

/* colored base (page 1): blue constraints, amber variables; on hover the
   neighborhood keeps its colors and everything else grays out */
.constraint-graph.colored .con-node rect {
  fill: var(--patch);
  stroke: var(--patch);
}
.constraint-graph.colored .con-node .con-label { fill: #fff; }
.constraint-graph.colored
.var-node:not(.assigned) circle {
  fill: var(--patch-var);
  stroke: var(--ink-2);
}
.constraint-graph.colored.has-highlight .con-node:not(.highlighted) rect {
  fill: var(--track);
  stroke: var(--ink-muted);
}
.constraint-graph.colored.has-highlight
.con-node:not(.highlighted) .con-label { fill: var(--ink-2); }
.constraint-graph.colored.has-highlight .con-node.highlighted rect {
  fill: var(--patch);
  stroke: var(--patch);
}
.constraint-graph.colored.has-highlight .con-node.highlighted .con-label {
  fill: #fff;
}
.constraint-graph.colored.has-highlight
.var-node:not(.connected):not(.assigned) circle {
  fill: var(--node-unassigned);
  stroke: var(--ink-muted);
}

/* hover-highlight coupling (list item <-> constraint node) */
.constraint-graph.has-highlight .edge { opacity: 0.18; }
.constraint-graph.has-highlight .edge.highlighted {
  opacity: 1;
  stroke: var(--patch);
  stroke-width: 2.5;
}
.constraint-graph.has-highlight .con-node,
.constraint-graph.has-highlight .var-node { opacity: 0.3; }
.constraint-graph.has-highlight .con-node.highlighted,
.constraint-graph.has-highlight .var-node.connected { opacity: 1; }
.constraint-graph.has-highlight .con-node.highlighted rect {
  stroke: var(--patch);
  fill: var(--patch-soft);
}
.constraint-graph.has-highlight
.var-node.connected:not(.assigned) circle {
  fill: var(--patch-var);
  stroke: var(--ink-2);
}

/* ------------------------------------------- trace mode (page-4 player) */
/* Ghost backdrop with the current subproblem lit, plus the reference
   animations' three-state proposal color script: pending = patch blue /
   amber at low alpha; integrated = good; rejected/contradiction = bad.
   (The --proposal orange token is not part of this script; it stays
   declared for possible use elsewhere.) Hard cuts are the choreography:
   transitions are clamped to short opacity fades only. */
.constraint-graph.trace-mode .edge,
.constraint-graph.trace-mode .con-node,
.constraint-graph.trace-mode .con-node rect,
.constraint-graph.trace-mode .var-node,
.constraint-graph.trace-mode .var-node circle,
.constraint-graph.trace-mode .var-label,
.constraint-graph.trace-mode .var-name-hint {
  transition: opacity 120ms;
}
.constraint-graph.trace-mode .edge { opacity: 0.08; }
.constraint-graph.trace-mode .con-node { opacity: 0.2; }
.constraint-graph.trace-mode .var-node { opacity: 0.22; }

/* the solid subproblem */
.constraint-graph.trace-mode .con-node.in-patch { opacity: 1; }
.constraint-graph.trace-mode .con-node.in-patch rect {
  fill: var(--patch);
  stroke: var(--patch);
}
.constraint-graph.trace-mode .con-node.in-patch .con-label { fill: #fff; }
.constraint-graph.trace-mode .edge.patch-edge {
  opacity: 0.9;
  stroke: var(--patch);
  stroke-width: 2.2;
}
.constraint-graph.trace-mode .var-node.in-patch { opacity: 1; }
.constraint-graph.trace-mode .var-node.in-patch circle {
  fill: var(--patch-var);
  stroke: var(--ink-2);
  stroke-width: 1.6;
}

/* the proposal ghost: pending -> outcome */
.constraint-graph.trace-mode .con-node.proposal,
.constraint-graph.trace-mode .var-node.proposal-var { opacity: 0.45; }
.constraint-graph.trace-mode.prop-ok .con-node.proposal,
.constraint-graph.trace-mode.prop-no .con-node.proposal,
.constraint-graph.trace-mode.prop-ok .var-node.proposal-var,
.constraint-graph.trace-mode.prop-no .var-node.proposal-var { opacity: 0.7; }
.constraint-graph.trace-mode .con-node.proposal rect {
  fill: var(--patch);
  stroke: var(--patch);
}
.constraint-graph.trace-mode .con-node.proposal .con-label { fill: #fff; }
.constraint-graph.trace-mode .var-node.proposal-var circle {
  fill: var(--patch-var);
  stroke: var(--patch-var);
}
.constraint-graph.trace-mode .edge.prop-edge {
  opacity: 0.55;
  stroke: var(--patch);
  stroke-width: 2;
}
.constraint-graph.trace-mode.prop-ok .con-node.proposal rect,
.constraint-graph.trace-mode.prop-ok .var-node.proposal-var circle {
  fill: var(--good);
  stroke: var(--good);
}
.constraint-graph.trace-mode.prop-ok .edge.prop-edge {
  stroke: var(--good);
  opacity: 0.75;
}
.constraint-graph.trace-mode.prop-no .con-node.proposal rect,
.constraint-graph.trace-mode.prop-no .var-node.proposal-var circle {
  fill: var(--bad);
  stroke: var(--bad);
}
.constraint-graph.trace-mode.prop-no .edge.prop-edge {
  stroke: var(--bad);
  opacity: 0.75;
}

/* committed marks recede; fresh commits flash; exhausted constraints fade */
.constraint-graph.trace-mode .var-node.assigned-1,
.constraint-graph.trace-mode .var-node.assigned-0 { opacity: 0.35; }
.constraint-graph.trace-mode .var-node.commit-flash { opacity: 1; }
.constraint-graph.trace-mode .var-node.commit-flash circle {
  stroke: var(--good);
  stroke-width: 3;
}
.constraint-graph.trace-mode .var-node.commit-flash.flash-bad circle {
  stroke: var(--bad);
}
.constraint-graph.trace-mode .con-node.spent { opacity: 0.07; }

/* ------------------------------------------ the memory/time tradeoff panel */
.tradeoff-controls label { align-items: center; }
.tradeoff-controls input[type="range"] {
  width: 10rem;
  accent-color: var(--patch);
}
.tradeoff-controls .knob-value {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  min-width: 4.8rem;
}
.tr-readout { margin-left: auto; color: var(--ink-2); }
.tr-readout strong {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  font-size: 1.1rem;
  margin-left: 0.2rem;
}
.tradeoff-stage {
  display: grid;
  grid-template-columns: 0.95fr 0.85fr;
  gap: 0.5rem 3.5rem;
  align-items: top;
  margin: 0.6rem 0 0;
}
.tradeoff-stage figure { margin: 0; }
/* optical nudge: the map's bottom axis pulls its visual center up,
   so the graph sits a touch high to match */
.tradeoff-stage .tr-graph { transform: translateY(-1rem); }
.heat-cell { cursor: pointer; }
.heat-cell:hover { stroke: var(--ink-2); stroke-width: 1.5; }
.heat-sel {
  fill: none;
  stroke: var(--ink);
  stroke-width: 2.5;
  pointer-events: none;
}
.heat-chart .axis text { font-size: 14px; }

/* ------------------------------- mechanism 2: variable substitution */
.subst-controls { margin: 0 0 0.3rem; }
.subst-controls label {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  color: var(--ink-2);
}
.subst-controls input[type="range"] {
  width: 13rem;
  accent-color: var(--patch);
}
.subst-controls .knob-value {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.subst-stage {
  display: grid;
  grid-template-columns: 1fr 0.9fr;
  gap: 0.4rem 2.4rem;
  align-items: start;
}
.subst-stage figure { margin: 0; }
.subst-graph .edge,
.subst-graph .con-node { transition: opacity 0.25s; }
.constraint-graph .edge.snipped {
  opacity: 0.1;
  stroke-dasharray: 5 5;
}
.constraint-graph .con-node.satisfied { opacity: 0.2; }
#subst-conds { min-height: 2.4rem; margin-top: 0.3rem; }
.heat-chart .axis-label { font-size: 15px; }
.heat-scale {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.8rem;
  margin: 0.8rem 0 0;
  color: var(--ink-2);
  font-size: 0.85rem;
}
.scale-tick { fill: var(--ink-2); font-size: 10.5px; font-family: var(--font-ui); }
@media (max-width: 52rem) {
  .tradeoff-stage { grid-template-columns: 1fr; }
}

/* ------------------------------------------------ page 4: the game boards */
/* Board colors and glyphs reproduce the experiment clients
   (experiments/run/games/[game]/game.css) as modernized by the paper-figure
   renderers (mitpuzzles_website .../boards/[game].py) — deliberately NOT the
   site tokens: the boards should look like the games people played. The
   model-trace overlays on top of them (washes, rings) stay on site tokens. */
.game-duo {
  display: grid;
  grid-template-columns: minmax(0, 19rem) minmax(0, 21rem);
  gap: 1rem 1.5rem;
  align-items: center;
  justify-content: center;
}
.duo-board, .duo-graph { max-width: 100%; }
.game-duo figcaption { grid-column: 1 / -1; align-self: start; }

svg.game-board { display: block; width: 100%; height: auto; }
.game-board .grid-line { stroke-width: 1.1; }
.game-board .grid-frame {
  fill: none;
  stroke-width: 2.5;
  pointer-events: none;
}
.game-minesweeper .grid-line, .game-minesweeper .grid-frame { stroke: #60646a; }
.game-tents .grid-line, .game-tents .grid-frame { stroke: #6e6e6e; }
.game-nonograms .grid-line, .game-nonograms .grid-frame { stroke: #555; }

/* minesweeper: beveled covered cells, flat revealed cells, classic numbers */
.game-board .cell-fill.revealed { fill: #ece9e2; }
.game-board .cell-fill.covered { fill: #babec4; }
.game-board .bevel-light { fill: #e8ebf0; }
.game-board .bevel-dark { fill: #80858d; }
.game-board .bcell:has(.cell-fill.inert) { opacity: 0.55; }
.game-board .clue-num {
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 27px;
}

/* tents: warm ground, vector tree/tent; nonograms: white cells */
.game-tents .cell-fill.ground { fill: #f0f0e8; }
.game-nonograms .cell-fill.ground { fill: #fff; }
.game-board .gutter-num {
  fill: #373737;
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 21px;
}
.game-board .g-trunk { fill: #704e2e; }
.game-board .g-tree-top { fill: #2c6c38; }
.game-board .g-tree-bottom { fill: #1e522a; }

/* mark glyphs: pre-built, toggled by the cell's mark-1 / mark-0 classes */
.game-board .m-pos, .game-board .m-neg { opacity: 0; }
.game-board .bcell.mark-1 .m-pos { opacity: 1; }
.game-board .bcell.mark-0 .m-neg { opacity: 1; }
.game-board .g-flag { fill: #cd2d2d; }
.game-board .g-pole { fill: #373737; }
.game-board .g-tent { fill: #de7d2c; stroke: #964e16; stroke-width: 2; }
.game-board .g-tent-door { fill: #964e16; }
.game-board .g-fill { fill: #2f2f2f; }
.game-board .g-x-dark { fill: none; stroke: #282828; stroke-width: 2; }
.game-board .g-x-gray { fill: none; stroke: #9a9a9a; stroke-width: 3; }

/* amber wash: intro hover (.connected) and trace memory (.in-patch) */
.game-board .wash { fill: var(--patch-var); opacity: 0; pointer-events: none; }
.game-board .bcell.connected .wash { opacity: 0.45; }
.game-board .bcell.in-patch .wash { opacity: 0.4; }
.game-board .bcell.proposal-var .wash { opacity: 0.16; }
.game-board.prop-ok .bcell.proposal-var .wash {
  fill: var(--good);
  opacity: 0.3;
}
.game-board.prop-no .bcell.proposal-var .wash {
  fill: var(--bad);
  opacity: 0.3;
}

/* clue rings: intro hover (.highlighted), subproblem (.in-patch), proposal */
.game-board .clue-hit { fill: none; stroke: none; pointer-events: all; }
.game-board .clue-ring {
  fill: none;
  stroke: var(--patch);
  stroke-width: 2.5;
  opacity: 0;
  pointer-events: none;
}
.game-board .gclue { cursor: default; }
.game-board .gclue.highlighted .clue-ring,
.game-board .gclue.in-patch .clue-ring { opacity: 1; }
.game-board .gclue.proposal .clue-ring { opacity: 0.45; }
.game-board.prop-ok .gclue.proposal .clue-ring {
  stroke: var(--good);
  opacity: 0.85;
}
.game-board.prop-no .gclue.proposal .clue-ring {
  stroke: var(--bad);
  opacity: 0.85;
}
.game-board.trace-mode .gclue.spent { opacity: 0.35; }

/* commit / error / guess rings on cells */
.game-board .cell-ring { fill: none; opacity: 0; pointer-events: none; }
.game-board .bcell.guessed .cell-ring {
  opacity: 1;
  stroke: var(--ink-2);
  stroke-width: 1.5;
  stroke-dasharray: 5 4;
}
.game-board .bcell.wrong .cell-ring {
  opacity: 1;
  stroke: var(--bad);
  stroke-width: 2;
}
.game-board .bcell.commit-flash .cell-ring {
  opacity: 1;
  stroke: var(--good);
  stroke-width: 3.5;
  stroke-dasharray: none;
}
.game-board .bcell.commit-flash.flash-bad .cell-ring { stroke: var(--bad); }

/* committed marks recede so fresh activity stays salient — gated on the
   mark value classes, or a receded cell would show BOTH glyphs at once */
.game-board.trace-mode .bcell.receded.mark-1 .m-pos,
.game-board.trace-mode .bcell.receded.mark-0 .m-neg { opacity: 0.55; }

/* order badges (the ordering prediction figure): position numbers sit on
   white chips so they can't be mistaken for clue numbers */
.game-board .order-chip {
  fill: #fff;
  stroke: var(--patch);
  stroke-width: 2;
  opacity: 0;
  pointer-events: none;
}
.game-board .bcell.badged .order-chip { opacity: 1; }
.game-board .order-badge {
  fill: var(--patch);
  font-family: var(--font-ui);
  font-weight: 700;
  font-size: 18px;
}

/* hard cuts: in trace mode transitions are clamped to short opacity fades */
.game-board.trace-mode .bcell,
.game-board.trace-mode .gclue,
.game-board.trace-mode .wash,
.game-board.trace-mode .clue-ring,
.game-board.trace-mode .cell-ring,
.game-board.trace-mode .m-pos,
.game-board.trace-mode .m-neg {
  transition: opacity 120ms;
}

/* ------------------------------------------------ page 4: the autoplay loop */
.loop {
  max-width: 19rem;
  margin: 1rem auto;
  cursor: pointer;
  outline: none;
}
.loop:focus-visible { box-shadow: 0 0 0 2px var(--patch-soft); }
/* the play affordance centers on the BOARD, not the whole block (the meter
   and caption below would drag it off-center) */
.loop .loop-board { position: relative; }
.loop[data-playing="false"] .loop-board::before {
  content: "▶";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 2.6rem;
  height: 2.6rem;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.15rem;
  color: var(--ink);
  background: rgba(252, 252, 251, 0.85);
  border: 1.5px solid var(--ink);
  border-radius: 50%;
  pointer-events: none;
}
.legend.loop-legend { justify-content: center; }
/* the replay picker: one game's loop at a time, chosen from the menu */
.replay-controls { justify-content: center; margin: 1rem 0 0.4rem; }
.replay-stage .loop { max-width: 22rem; }
.cap-meter {
  position: relative;
  height: 0.55rem;
  margin: 0.55rem 0 0.15rem;
  background: var(--track);
  border-radius: 3px;
  overflow: hidden;
}
.cap-fill {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--patch);
  transition: width 120ms;
}
.cap-ghost {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--bad);
  opacity: 0.4;
  transition: width 120ms;
}
.cap-limit {
  position: absolute;
  top: -1px;
  bottom: -1px;
  border-left: 2px dashed var(--ink-2);
}
.loop-caption {
  display: flex;
  align-items: baseline;
  gap: 0.9rem;
  height: 1.5rem;
  overflow: hidden;
}
.loop-caption .phase-tag { min-width: 6.5rem; }
.loop-caption .cap-readout {
  flex: 1 1 auto;
  color: var(--ink-2);
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
}

/* ------------------------------------------- page 4: prediction figures */
.pred-multiples {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.5rem 1rem;
}
.pred-panel { margin: 0; }
.line-chart .series-label {
  font-family: var(--font-ui);
  font-size: 11px;
  fill: var(--ink-2);
}
.line-chart .chart-line.dashed { stroke-dasharray: 6 4; }

/* draw-in on scroll: series paths carry pathLength=1 (chart.js) */
.pred-panel.draw .series-line { stroke-dasharray: 1; stroke-dashoffset: 1; }
.pred-panel.draw .series-label { opacity: 0; }
.pred-panel.draw.drawn .series-line {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1100ms ease-out;
}
.pred-panel.draw.drawn .series-line:nth-of-type(2) { transition-delay: 150ms; }
.pred-panel.draw.drawn .series-line:nth-of-type(3) { transition-delay: 300ms; }
.pred-panel.draw.drawn .series-line:nth-of-type(4) { transition-delay: 450ms; }
.pred-panel.draw.drawn .series-label {
  opacity: 1;
  transition: opacity 400ms 900ms;
}
@media (prefers-reduced-motion: reduce) {
  .pred-panel.draw .series-line {
    stroke-dashoffset: 0 !important;
    transition: none !important;
  }
  .pred-panel.draw .series-label {
    opacity: 1 !important;
    transition: none !important;
  }
  .cap-fill, .cap-ghost { transition: none; }
}

.order-boards {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2.5rem;
  justify-content: center;
}
.order-board { margin: 0; flex: 0 1 12.5rem; }
.ordering-chart, .external-chart { max-width: 30rem; }

@media (max-width: 52rem) {
  .game-duo { grid-template-columns: 1fr; }
  .pred-multiples { grid-template-columns: 1fr; }
}

/* ------------------------------------ seating story (pages 1-2) */
.constraint-list.story .cid,
.constraint-list .cid {
  white-space: nowrap;
}
.constraint-list.story .cid {
  min-width: 7.6rem;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--ink-2);
}
.tables-chart {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 3.5rem;
  margin: 0.8rem 0;
}
.tables-chart .path-title { margin: 0 0 0.3rem; }

/* three-table assignment display (seating pages): A filled, B mid, C open */
.constraint-graph .var-node.assigned-A circle { fill: var(--ink); stroke: var(--ink); }
.constraint-graph .var-node.assigned-A .var-label,
.constraint-graph .var-node.assigned-A .var-name-hint {
  fill: #fff;
  font-family: var(--font-ui);
  font-style: normal;
}
.constraint-graph .var-node.assigned-B circle { fill: #8d8b86; stroke: #8d8b86; }
.constraint-graph .var-node.assigned-B .var-label,
.constraint-graph .var-node.assigned-B .var-name-hint {
  fill: #fff;
  font-family: var(--font-ui);
  font-style: normal;
}
.constraint-graph .var-node.assigned-C circle { fill: #fff; stroke: var(--ink); }
.constraint-graph .var-node.assigned-C .var-label {
  font-family: var(--font-ui);
  font-style: normal;
}
.constraint-graph .var-node.assigned-A .var-name-hint,
.constraint-graph .var-node.assigned-B .var-name-hint,
.constraint-graph .var-node.assigned-C .var-name-hint { opacity: 0.7; }

/* optional faint-edge treatment for hub constraints (graph.js faintCons) */
.constraint-graph .edge.faint { opacity: 0.13; }
.constraint-graph.has-highlight .edge.faint { opacity: 0.05; }
.constraint-graph.has-highlight .edge.faint.highlighted {
  opacity: 1;
}
.key-swatch.mid { background: #8d8b86; border-color: #8d8b86; }
.table-note { color: var(--ink-2); font-weight: 400; font-size: 0.85rem; }
