/* The ready-queue widget.
 *
 * Every colour is a theme variable, which is the whole reason this is DOM and
 * not the recorded animation from the talk: it follows the theme picker. Blocks
 * are laid out by normal flow and moved with FLIP, so nothing here depends on
 * absolute coordinates. */

.post-meta {
  font-size: 0.9em;
  color: var(--on_surface_variant);
}

.rq {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

.rq-stage {
  display: flex;
  gap: 0.9rem;
  align-items: stretch;   /* equal-height columns, so the slots fill their own */
  flex-wrap: wrap;
}

.rq h4 {
  margin: 0 0 0.5rem;
  font-size: 0.85em;
  font-weight: 600;
  color: var(--on_surface_variant);
}

/* ---- the queue ---------------------------------------------------------- */

.rq-queue {
  flex: 0 0 auto;
  min-width: 8.5rem;
  display: flex;
  flex-direction: column;
}

.rq-blocks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  /* Fixed, not just capped: the same height as the two run widgets below, so
     all three queues present the same box. It has to be a floor as well as a
     ceiling because the column otherwise shrinks the instant a block leaves,
     and the blocks reflowing upward were animating from below the new, shorter
     edge and getting clipped by the overflow. The run widgets pin this from JS;
     this queue is static markup, so it can be pure CSS. */
  min-height: 12.2rem;
  max-height: 12.2rem;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.rq-block {
  /* The blocks are <li>s that get moved out of their <ol> into the winner,
     candidate and discarded slots, which are plain <div>s -- so the list-style
     on .rq-blocks stops applying and the default marker reappears next to them.
     Set it on the item itself, which travels with it. */
  list-style: none;
  padding: 0.3rem 0.6rem;
  border-radius: 6px;
  font-family: monospace;
  font-size: 0.85em;
  text-align: center;
  white-space: nowrap;
  border: 1px solid transparent;
}

/* ds_load and WMMA must stay tellable apart in all 66 themes, so they take two
 * different theme roles rather than two shades of one. */
.rq-block[data-kind="ds"] {
  background: var(--secondary);
  color: var(--on_secondary);
}

.rq-block[data-kind="wmma"] {
  background: var(--primary);
  color: var(--on_primary);
}

.rq-block.is-flash {
  outline: 2px solid var(--tertiary);
  outline-offset: 2px;
}

.rq-block.is-issued {
  outline: 2px solid var(--tertiary);
  outline-offset: 3px;
}

/* ---- slots and bin ------------------------------------------------------ */

.rq-arena {
  flex: 1 1 14rem;
  min-width: 0;
}

/* Winner, candidate and the bin stack in their own right-hand column, so the
 * queue on the left feeds across the comparator into them and the loser drops
 * out of the comparison into the slot directly below it. */
.rq-held {
  flex: 0 0 auto;
  min-width: 8.5rem;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

/* Fill the column rather than hugging their contents -- one small block in a
 * label-sized box read as an afterthought next to the queue. */
.rq-held .rq-slot {
  flex: 1 1 0;
  max-height: 9rem;
}

/* The label is pinned rather than laid out with the block: as a flex item it
 * was centred together with whatever the slot held, so it jumped every time a
 * block arrived or left. */
.rq-slot {
  position: relative;
  min-height: 3.4rem;
  padding: 1.5rem 0.3rem 0.3rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
}

/* Dashed, because unlike the two above it nothing in it is still in play. It
 * needs no margin-top:auto now that it is one of three equal slots rather than
 * something hanging off the bottom of the draining queue. */
.rq-bin { border-style: dashed; }

.rq-slot-label {
  position: absolute;
  top: 0.35rem;
  left: 0;
  right: 0;
  text-align: center;
  font-size: 0.72em;
  color: var(--on_surface_variant);
}

/* ---- the cascade -------------------------------------------------------- */

.rq-ladder {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: rung;
  position: relative;   /* positioning context for .rq-cursor */
}

/* One highlight that slides and stretches down the cascade, rather than each
 * rung colouring in turn -- the single travelling cursor is what shows the
 * cascade stopping at a rung instead of running the whole list. */
.rq-cursor {
  position: absolute;
  left: 0;
  right: 0;
  border: 2px solid var(--on_surface_variant);
  border-radius: 4px;
  pointer-events: none;
  z-index: 1;
}

.rq-cursor.is-decided {
  border-color: var(--error);
}

.rq-ladder li {
  counter-increment: rung;
  border-radius: 4px;
}

.rq-ladder button {
  display: block;
  width: 100%;
  text-align: left;
  font: inherit;
  font-family: monospace;
  font-size: 0.82em;
  padding: 0.16rem 0.4rem;
  border: 1px solid transparent;
  border-radius: 4px;
  background: none;
  color: var(--on_surface_variant);
  cursor: pointer;
}

.rq-ladder button::before {
  content: counter(rung) ". ";
  opacity: 0.6;
}

.rq-ladder button:hover {
  color: var(--on_surface);
  border-color: var(--outline);
}

/* the rung the cascade returned at */
.rq-ladder li.is-decided button {
  color: var(--error);
  border-color: var(--error);
}

.rq-ladder li.is-open button {
  color: var(--on_surface);
  border-color: var(--primary);
}

/* With JS off every explanation is simply visible; the script collapses them. */
.rq-ladder li.is-collapsible .rq-doc {
  display: none;
}

.rq-ladder li.is-open .rq-doc {
  display: block;
}

.rq-doc {
  margin: 0.3rem 0 0.5rem;
  padding: 0.5rem 0.7rem;
  border-left: 2px solid var(--primary);
  background: var(--surface);
  border-radius: 0 6px 6px 0;
  font-size: 0.88em;
}

/* ---- caption and controls ---------------------------------------------- */

.rq-caption {
  min-height: 1.4em;
  margin: 0.7rem 0 0;
  font-size: 0.9em;
  color: var(--on_surface_variant);
}

.rq-caption.is-hot { color: var(--error); }
.rq-caption.is-ok { color: var(--tertiary); }

.rq-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--outline);
}

.rq-controls button {
  font: inherit;
  padding: 0.25rem 0.8rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: var(--surface);
  color: var(--on_surface);
  cursor: pointer;
}

/* Disabled arrows read as disabled, as they already did in the budget widget:
   at step 0 the back arrow is dead in every one of these, and only one of them
   was saying so. :not(:disabled) on the hover too, so a dead button does not
   light up under the pointer. */
.rq-controls button:disabled { opacity: 0.4; cursor: default; }
.rq-controls button:hover:not(:disabled) {
  background: var(--hover);
  color: var(--on_hover);
}

.rq-progress {
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  color: var(--on_surface_variant);
}

.rq figcaption {
  margin-top: 0.7rem;
  font-size: 0.85em;
  color: var(--on_surface_variant);
}

@media (max-width: 34rem) {
  .rq-stage { flex-direction: column; }
  .rq-queue,
  .rq-held { width: 100%; }
  /* A sideways strip, like the run widgets, rather than a wrapped grid: a
     queue that wraps onto a second row stops reading as an ordered queue. */
  .rq-queue { min-width: 0; }
  .rq-blocks {
    flex-direction: row;
    min-width: 0;
    min-height: 0;
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 0.25rem;
  }
  .rq-block { flex: 0 0 auto; }
  .rq-held { flex-direction: row; }
  .rq-held .rq-slot { flex: 1 1 0; }
}

/* --- "no mutation on a real kernel" ------------------------------------- */
/* Same theme variables as the ready-queue widget; the timeline is plain DOM so
 * the bars recolour with the page rather than being baked into a video. */

.te {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

.te h4 {
  margin: 0 0 0.5rem;
  font-size: 0.85em;
  font-weight: 600;
  color: var(--on_surface_variant);
}

.te-kernels { display: flex; gap: 0.4rem; margin: 0 0 0.7rem; }
.te-kernels button {
  font: inherit;
  font-size: 0.82em;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: none;
  color: var(--on_surface);
  cursor: pointer;
}
.te-kernels button[aria-pressed="true"] { border-color: var(--primary); background: var(--surface); }

/* Dense mode is gone: it shrank the blocks so a 25-fragment queue would not
   tower over the timeline, which the scrollbar above now handles properly. The
   class is still set on the figure in case something else wants it, but the
   blocks are one size again so both kernels look alike. */

.te-sub {
  margin: 0 0 0.9rem;
  font-size: 0.9em;
  color: var(--on_surface_variant);
}

.te-stage {
  display: flex;
  gap: 0.9rem;
  /* stretch, so the queue column is as tall as the timeline beside it and the
     scroll box can fill that height instead of leaving it blank. */
  align-items: stretch;
  flex-wrap: wrap;
}

/* Fixed width, and a column so the block list can flex to fill what is left
   after the heading and the note. */
.te-queue { flex: 0 0 11rem; display: flex; flex-direction: column; }
.te-queue h4, .te-none { flex: 0 0 auto; }
.te-mid   { flex: 1 1 22rem; min-width: 0; display: flex; gap: 0.8rem; align-items: flex-start; }
.te-compcol { flex: 1 1 0; min-width: 0; }
.te-count { flex: 0 0 auto; text-align: right; }

/* Timeline sits under the comparator on the right rather than spanning the full
 * width beneath the queue -- narrower, but it makes the whole figure shorter. */
.te-right { flex: 1 1 18rem; min-width: 0; display: flex; flex-direction: column; }
.te-toprow { display: flex; gap: 0.9rem; align-items: flex-start; }

.te-blocks {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  /* Fills the column rather than taking a fixed height: f16's 25 blocks then
     use all the room the timeline beside them already occupies, and scroll for
     the rest, so the figure never grows to accommodate the queue. min-height:0
     is what lets a flex item shrink below its content and actually scroll. */
  flex: 1 1 0;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

.te-block {
  list-style: none;   /* moved into .te-comp, a div -- see .rq-block */
  padding: 0.25rem 0.5rem;
  border-radius: 6px;
  background: var(--secondary);
  color: var(--on_secondary);
  font-family: monospace;
  font-size: 0.8em;
  white-space: nowrap;
}

.te-none {
  margin: 0.5rem 0 0;
  font-size: 0.8em;
  color: var(--error);
}

.te-comp {
  min-height: 2.6rem;
  padding: 0.3rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  display: flex;
  align-items: center;
  justify-content: center;
}

.te-verdict {
  margin: 0.4rem 0 0;
  /* Hidden rather than absent, and hidden from the start so it does not flash
     before the data loads. The text itself reserves the box, which min-height
     did not do exactly -- it was a pixel short and everything below twitched. */
  visibility: hidden;
  white-space: nowrap;
  font-family: monospace;
  font-size: 0.8em;
  color: var(--error);
}

.te-count-label { display: block; font-size: 0.75em; color: var(--on_surface_variant); }
.te-count-value {
  display: inline-block;
  font-size: 1.6em;
  font-variant-numeric: tabular-nums;
  color: var(--error);
}

/* ---- the timeline ------------------------------------------------------- */

.te-timeline {
  position: relative;
  margin-top: auto;
  padding-top: 0.8rem;
}

.te-rows { position: relative; }

/* Held but not yet read: from where the load issued to where it is first used. */
.te-bar {
  position: absolute;
  height: 10px;
  border-radius: 2px;
  background: var(--error);
}

.te-now {
  position: absolute;
  top: 0;
  bottom: 1.3rem;
  width: 0;
  border-left: 2px dashed var(--tertiary);
  /* Above the bars. .te-bar carries z-index:1 so it sits over the ghosts, and
     that also lifted it over this line, which used to win on DOM order alone. */
  z-index: 2;
}

.te-now::after {
  content: attr(data-label);
  position: absolute;
  top: -0.2rem;
  left: 0.3rem;
  font-size: 0.72em;
  white-space: nowrap;
  color: var(--tertiary);
}

.te-axis {
  display: flex;
  justify-content: space-between;
  margin-top: 0.35rem;
  padding-top: 0.35rem;
  border-top: 1px solid var(--outline);
  font-size: 0.72em;
  color: var(--on_surface_variant);
}

/* Always shown, and deliberately muted: it is the branch that never gets taken
 * while anything is left in the queue. */
.te-coda {
  flex: 1 1 0;
  min-width: 0;
  margin-top: 1.6rem;   /* line up with the comparator box, past its heading */
  padding: 0.5rem 0.7rem;
  border-left: 2px solid var(--outline);
  background: var(--surface);
  border-radius: 0 6px 6px 0;
  opacity: 0.65;
}

.te-coda pre { margin: 0 0 0.3rem; font-size: 0.72em; overflow-x: auto; color: var(--on_surface_variant); }
.te-coda p { margin: 0; font-size: 0.82em; color: var(--on_surface_variant); }

.te-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.9rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--outline);
}

.te-controls button {
  font: inherit;
  padding: 0.25rem 0.8rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: var(--surface);
  color: var(--on_surface);
  cursor: pointer;
}

.te-controls button:disabled { opacity: 0.4; cursor: default; }
.te-controls button:hover:not(:disabled) { background: var(--hover); color: var(--on_hover); }

.te-progress {
  font-size: 0.85em;
  font-variant-numeric: tabular-nums;
  color: var(--on_surface_variant);
}

.te figcaption {
  margin-top: 0.7rem;
  font-size: 0.85em;
  color: var(--on_surface_variant);
}

/* --- inline info popovers ------------------------------------------------ */
/* Uses the HTML popover API, so no JavaScript: the button toggles it, Esc and
 * clicking away dismiss it. Where popover is unsupported the panel simply
 * renders inline, which is still readable. */

.info-link {
  font: inherit;
  padding: 0;
  border: 0;
  border-bottom: 1px dashed currentColor;
  background: none;
  color: var(--primary);
  cursor: pointer;
}

.info-link:hover { color: var(--hover); }

.info-pop {
  max-width: 40rem;
  padding: 0.9rem 1.1rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
  color: var(--on_surface);
}

.info-pop::backdrop { background: rgb(0 0 0 / 0.35); }
.info-pop h4 { margin: 0 0 0.6rem; font-size: 0.95em; }
.info-pop pre {
  margin: 0 0 0.6rem;
  padding: 0.6rem 0.7rem;
  overflow-x: auto;
  border-radius: 6px;
  background: var(--surface);
  font-size: 0.82em;
}
.info-pop p { margin: 0; font-size: 0.9em; color: var(--on_surface_variant); }

/* --- the four DAG edits -------------------------------------------------- */
/* Four edge kinds take four distinct theme roles rather than four shades of
 * one, so they stay tellable apart across every theme. */

.de {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

/* The key used to sit in a 15rem column beside the diagram, which cost the
   diagram a third of its width for four short lines of text. Stacked, the
   drawing gets the full measure and the key reads as a key. */
.de-stage { display: flex; flex-direction: column; gap: 0.85rem; }
.de-svg { width: 100%; height: auto; }

.de-node rect { fill: var(--surface); stroke: var(--outline); stroke-width: 1.5; }
.de-node text { font-family: monospace; font-size: 12px; fill: var(--on_surface); }
.de-wmma rect { fill: var(--primary); }
.de-wmma text { fill: var(--on_primary); }
.de-ds rect { fill: var(--secondary); }
.de-ds text { fill: var(--on_secondary); }

.de-edge line:not(.de-hit) { stroke-width: 2; stroke-linecap: round; }
/* The click target, like the cube's lineHitStrokeWidth: hitting a 2.5px line
   exactly is not something anyone should have to do, and these are drifting.
   It has to live here rather than as a stroke-width attribute in the TS,
   because a CSS rule beats a presentation attribute -- setting it there left
   the band at 2.5px and the widening silently did nothing. The :not() on the
   two rules around it keeps them from clawing the width back. */
.de-edge .de-hit { cursor: pointer; stroke-width: 28; stroke: transparent; }
.de-edge.is-dim { opacity: 0.22; }
.de-edge.is-on line:not(.de-hit) { stroke-width: 3.2; }

.de-e0 line { stroke: var(--tertiary); }
.de-e1 line { stroke: var(--secondary); }
.de-e2 line { stroke: var(--on_surface_variant); }
.de-e3 line { stroke: var(--error); }

/* L1's window: the bracket from the end of the manim scene, in the same colour
   as the budget edge that creates it. */
.de-window line { stroke-width: 2; stroke-linecap: round; }
.de-w-budget { stroke: var(--error); }
.de-w-lat { stroke: var(--on_surface_variant); }
.de-window.is-dim { opacity: 0.22; }
.de-window text { font-family: monospace; }
.de-wtitle { font-size: 12px; fill: var(--on_surface); }
.de-wsub { font-size: 10px; fill: var(--on_surface_variant); }

.de-legend {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  /* One entry per row: at two columns the longer two labels wrapped, and a
     key whose rows are ragged is worse than one that is tall. */
  grid-template-columns: repeat(auto-fit, minmax(22rem, 1fr));
  gap: 0.1rem 0.6rem;
}
.de-legend button {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  width: 100%;
  text-align: left;
  font: inherit;
  font-size: 0.82em;
  padding: 0.3rem 0.4rem;
  border: 1px solid transparent;
  border-radius: 5px;
  background: none;
  color: var(--on_surface);
  cursor: pointer;
}
.de-legend button:hover { border-color: var(--outline); }
.de-legend button[aria-pressed="true"] { border-color: var(--primary); background: var(--surface); }
.de-legend em { color: var(--on_surface_variant); font-style: normal; }

.de-key { flex: 0 0 auto; width: 1.4rem; height: 0; border-top: 3px solid; border-radius: 2px; }
.de-k0 { color: var(--tertiary); }
.de-k1 { color: var(--secondary); }
.de-k2 { color: var(--on_surface_variant); }
.de-k3 { color: var(--error); }

.de-info {
  margin-top: 0.8rem;
  padding: 0.7rem 0.9rem;
  min-height: 5.5rem;
  border-left: 2px solid var(--primary);
  background: var(--surface);
  border-radius: 0 6px 6px 0;
}
.de-info h4 { margin: 0 0 0.4rem; font-size: 0.92em; }
.de-info p { margin: 0; font-size: 0.9em; }
.de-hint { color: var(--on_surface_variant); }

.de figcaption { margin-top: 0.7rem; font-size: 0.85em; color: var(--on_surface_variant); }

/* --- "calculating each load fragment's window" --------------------------- */
/* The two-panel budget figure. Same palette as the lifelines charts it
 * continues: red is held-but-unread, blue is in use, and the darker blue marks
 * the fragments the budget actually clamped. */

.bw {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

.bw-svg { width: 100%; height: auto; display: block; }

.bw-kernels { display: flex; gap: 0.4rem; margin: 0 0 0.7rem; }
.bw-kernels button {
  font: inherit;
  font-size: 0.82em;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: none;
  color: var(--on_surface);
  cursor: pointer;
}
.bw-kernels button[aria-pressed="true"] {
  border-color: var(--primary);
  background: var(--surface);
}

/* Always in flow, only made visible -- see .te-verdict for why. */
.bw-sub {
  margin: 0.35rem 0 0;
  min-height: 1.2em;
  font-size: 0.82em;
  color: var(--on_surface_variant);
  visibility: hidden;
}

.bw-held { fill: var(--error); }
.bw-use { fill: var(--secondary); }
.bw-use.is-clamped { fill: var(--primary); }
.bw-held.is-dim, .bw-use.is-dim { opacity: 0.25; }

.bw-histfill { fill: var(--on_surface_variant); opacity: 0.2; }
.bw-histline { fill: none; stroke: var(--on_surface_variant); stroke-width: 1.4; }

.bw-budget line { stroke: var(--error); stroke-width: 1.5; stroke-dasharray: 6 4; }
.bw-budget text { font-family: monospace; font-size: 11px; fill: var(--error); }

/* Shared by both two-panel charts -- see chart.ts. */
.ch-grid { stroke: var(--outline); stroke-width: 1; opacity: 0.5; }
.ch-tick { font-family: monospace; font-size: 10px; fill: var(--on_surface_variant); }
.ch-axis { font-size: 10.5px; fill: var(--on_surface_variant); }
.ch-axis-sub { font-size: 8.5px; }

/* Transparent, and wider than the bar so a fragment can be picked anywhere
 * along its row rather than only where it happens to be drawn. */
.bw-rowhit { fill: transparent; cursor: pointer; }
.bw-rowhit.is-on { fill: var(--on_surface); opacity: 0.07; }

.bw-legend {
  list-style: none;
  margin: 0.7rem 0 0;
  padding: 0;
  display: grid;
  /* One per row: at two columns the third key is left orphaned beside a gap. */
  grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  gap: 0.1rem 0.6rem;
  font-size: 0.8em;
  color: var(--on_surface_variant);
}
.bw-legend li { display: flex; align-items: center; gap: 0.45rem; }
.bw-key { flex: 0 0 auto; width: 1.1rem; height: 0.55rem; border-radius: 2px; }
.bw-kheld { background: var(--error); }
.bw-kinuse { background: var(--secondary); }
.bw-kclamp { background: var(--primary); }

.bw-info {
  margin-top: 0.8rem;
  padding: 0.7rem 0.9rem;
  /* So min-height below means the whole box. Content-box would add the padding
     on top of it and reserve ~1.4rem more than the measurement asked for. */
  box-sizing: border-box;
  /* Just tall enough for the longest explanation, so clicking between fragments
     does not walk the controls up and down the page. Measured across both
     kernels, not guessed: 9.11rem at full width, 13.16rem once the bullets wrap
     on a phone. */
  min-height: 9.25rem;
  border-left: 2px solid var(--primary);
  background: var(--surface);
  border-radius: 0 6px 6px 0;
}
.bw-info h4 { margin: 0 0 0.4rem; font-size: 0.92em; font-family: monospace; }
.bw-info p { margin: 0; font-size: 0.9em; }
.bw-info ul { margin: 0; padding-left: 1.1rem; font-size: 0.9em; }
.bw-info li { margin: 0 0 0.3rem; }
.bw-info li:last-child { margin-bottom: 0; }
.bw-hint { color: var(--on_surface_variant); }

.bw-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.8rem;
  padding-top: 0.7rem;
  border-top: 1px solid var(--outline);
}
.bw-controls button {
  font: inherit;
  font-size: 0.85em;
  padding: 0.3rem 0.7rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: var(--surface);
  color: var(--on_surface);
  cursor: pointer;
}
.bw-controls button:hover:not(:disabled) { border-color: var(--primary); }
.bw-controls button:disabled { opacity: 0.4; cursor: default; }
.bw-progress { font-size: 0.8em; color: var(--on_surface_variant); }

.bw figcaption { margin-top: 0.7rem; font-size: 0.8em; color: var(--on_surface_variant); }

@media (max-width: 34rem) {
  .bw-info { min-height: 13.3rem; }   /* the bullets wrap; see above */
}


/* --- the same run, with the mutation ------------------------------------- */
/* Reuses the .te-* frame wholesale -- the point of the figure is that it is the
 * same picture as the run above it -- and adds only what this run has: the
 * eligibility gate on each queue block, the live/idle state of the bumpCycle
 * box, and the ghosts of where the loads went without the mutation. */

/* A gated block is in the queue but not in Available, so it reads as present
   but inactive rather than as a normal waiting candidate. */
.te-block { display: flex; align-items: baseline; justify-content: space-between; gap: 0.4rem; }
.te-block.is-gated { opacity: 0.55; }
.dr-gate {
  flex: 0 0 auto;
  font-size: 0.82em;
  opacity: 0.85;
  font-variant-numeric: tabular-nums;
}

/* The bumpCycle loop is greyed by default (see the run without the mutation,
   where it never applies). Here it genuinely toggles. */
.te-coda.is-active {
  opacity: 1;
  /* The same colour as the "now" line, which is the thing this box moves: while
     it is live the clock is advancing. Deliberately not a green -- 17 of the 66
     themes have no green to reach for, since --terminal_normal_green is warm in
     the gruvbox and ayu families. */
  border-left-color: var(--tertiary);
}
.te-coda.is-active pre code,
.te-coda.is-active p { color: var(--on_surface); }

.dr-ghost {
  position: absolute;
  height: 10px;
  border-radius: 2px;
  background: var(--on_surface_variant);
  opacity: 0.3;
  z-index: 0;
}
.te-bar { z-index: 1; }

/* Out of flow until the ghosts arrive, unlike .te-verdict and .bw-sub, which
   reserve their boxes. Those two appear and vanish on every step, so a
   reservation is the only way to stop the chart twitching. This one appears
   exactly once, at the last step, and reserving it left an empty strip above
   the controls for the whole run -- 47px of gap against 14px in the widget
   without the mutation. The one-off shift when it arrives is the better trade. */
.dr-ghostkey {
  display: none;
  align-items: center;
  gap: 0.45rem;
  margin: 0.8rem 0 0;
  font-size: 0.82em;
  color: var(--on_surface_variant);
}
.dr-ghostkey.is-on { display: flex; }
.dr-ghostswatch {
  flex: 0 0 auto;
  width: 1.1rem;
  height: 0.55rem;
  border-radius: 2px;
  background: var(--on_surface_variant);
  opacity: 0.3;
}


/* --- live ranges vs WMMAs scheduled in order ----------------------------- */
/* Same frame as the budget chart (chart.ts draws both) and the same palette as
 * the lifelines figure it continues. The inactive mutation state stays on
 * screen as a ghost, so the comparison is always visible rather than something
 * you have to hold in your head across a click. */

.lr {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

.lr-svg { width: 100%; height: auto; display: block; }

.lr-controls { display: flex; flex-wrap: wrap; gap: 0.4rem 1.2rem; margin: 0 0 0.7rem; }
.lr-kernels, .lr-dag, .lr-view { display: flex; gap: 0.4rem; }
.lr-controls button {
  font: inherit;
  font-size: 0.82em;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: none;
  color: var(--on_surface);
  cursor: pointer;
}
.lr-controls button[aria-pressed="true"] { border-color: var(--primary); background: var(--surface); }

.lr-held.is-dim, .lr-use.is-dim { opacity: 0.25; }
.lr-held { fill: var(--error); }
.lr-use { fill: var(--secondary); }
.lr-use.is-clamped { fill: var(--primary); }

/* Opacity and width are set per frame from the toggle's position, not by a
   class: at mutation-off the "on" curve is fully hidden rather than ghosted,
   and the "off" curve fades back into being the ghost as you switch. */
/* Same treatment as the budget chart's histogram: a light wash under a thin
   line, not a solid block. The live pair had no opacity at all, so the fill
   defaulted to 1 and swallowed the curve. */
.lr-curve { fill: none; stroke: var(--on_surface_variant); }
.lr-fill { fill: var(--on_surface_variant); }
.lr-curve.lr-live { stroke-width: 1.4; }
.lr-fill.lr-live { opacity: 0.2; }
.lr-curve.lr-ghostc { stroke-width: 1.1; }

/* Where the bar reached before the mutation shortened it. Tinted like the held
   span it is a ghost of, not neutral: a grey ghost was hard to tell from the
   in-use segment beside it, and what it means is "this red used to run to
   here". */
.lr-ghost { fill: var(--error); }

.lr-legend {
  list-style: none;
  margin: 0.7rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(24rem, 1fr));
  gap: 0.1rem 0.6rem;
  font-size: 0.8em;
  color: var(--on_surface_variant);
}
.lr-legend li { display: flex; align-items: center; gap: 0.45rem; }
.lr-key { flex: 0 0 auto; width: 1.1rem; height: 0.55rem; border-radius: 2px; }
.lr-kheld { background: var(--error); }
.lr-kinuse { background: var(--secondary); }
.lr-kclamp { background: var(--primary); }
.lr-kghost { background: var(--error); opacity: 0.3; }

/* The wait-stall view. One bar per s_wait_dscnt, clickable straight through to
   the instruction in the listing below. */
.lr-stall { fill: var(--tertiary); pointer-events: none; }
.lr-stallhit { fill: transparent; cursor: pointer; }
.lr-stallhit:hover { fill: var(--on_surface); opacity: 0.07; }
.lr-stallhit.is-on { fill: var(--on_surface); opacity: 0.12; }
/* The live-range keys mean nothing in the stall view. */
.lr.is-stalls .lr-legend { display: none; }


/* --- the assembly listing ------------------------------------------------ */
/* Opens on the hot loop -- ~740 lines of the 2200-3400 in the file -- which is
 * both the part that matters and small enough not to need virtualising. The
 * instruction classes take the same two colours the charts use, so a red line
 * here is the same thing as a red bar up there. */

.av { margin-top: 0.9rem; border-top: 1px solid var(--outline); padding-top: 0.7rem; }
.av summary { font-size: 0.85em; cursor: pointer; color: var(--on_surface_variant); }
.av summary:hover { color: var(--on_surface); }

.av-bar { display: flex; align-items: center; gap: 0.4rem; margin: 0.7rem 0 0.5rem; }
.av-bar button {
  font: inherit;
  font-size: 0.8em;
  padding: 0.2rem 0.6rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: var(--surface);
  color: var(--on_surface);
  cursor: pointer;
}
.av-bar button:hover:not(:disabled) { border-color: var(--primary); }
.av-bar button:disabled { opacity: 0.4; cursor: default; }
.av-status { font-size: 0.78em; color: var(--on_surface_variant); }

.av-code {
  margin: 0;
  position: relative;
  max-height: 26rem;
  overflow: auto;
  padding: 0.5rem 0;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: var(--surface);
  font-size: 0.72em;
  line-height: 1.45;
  white-space: pre;
  tab-size: 4;
}

.av-l { display: block; padding: 0 0.6rem; color: var(--on_surface_variant); }
/* Line numbers, dimmed and unselectable so copying the pane copies code. */
.av-l i {
  display: inline-block;
  width: 3.2em;
  margin-right: 0.7em;
  text-align: right;
  font-style: normal;
  opacity: 0.4;
  user-select: none;
}
.av-l.is-outside { opacity: 0.5; }
/* One colour per instruction class, matching the widgets above: a ds_load is
   --secondary wherever it appears, a WMMA --primary, a wait --tertiary. Red is
   reserved for spill code, which appears nowhere else. */
.av-l.is-ds { color: var(--secondary); }
.av-l.is-wmma { color: var(--primary); }
.av-l.is-wait { color: var(--tertiary); }
.av-l.is-scratch { color: var(--error); }

/* The selected fragment and the WMMAs that read it, in the same two colours but
   at full strength with a tinted row, so they stand out from the other loads
   and WMMAs rather than looking like more of the same. */
.av-l.is-sub { color: var(--secondary); font-weight: 600; background: color-mix(in srgb, var(--secondary) 14%, transparent); }
/* A picked wait takes the wait colour, not the load colour -- it is neither a
   subload nor a consumer, and reusing the red made it read as one. */
.av-l.is-waitsel { color: var(--tertiary); font-weight: 600; background: color-mix(in srgb, var(--tertiary) 14%, transparent); }
/* The DS ops a selected wait is actually waiting on. Load-coloured, because
   that is what they are, but lighter than a picked fragment so the wait itself
   stays the focus. Selection is the same colour bolded, not a different one --
   otherwise a picked load and the spill code would both be red. */
.av-l.is-op { color: var(--secondary); background: color-mix(in srgb, var(--secondary) 7%, transparent); }
.av-l.is-cons { color: var(--primary); font-weight: 600; background: color-mix(in srgb, var(--primary) 14%, transparent); }
.av-l.is-here { outline: 1px solid var(--outline); outline-offset: -1px; }
/* Only the instructions that map back to something on the chart invite a
   click; the rest of the listing is inert. */
.av-l.is-clickable { cursor: pointer; }
.av-l.is-clickable:hover { background: color-mix(in srgb, var(--on_surface) 8%, transparent); }

.lr-rowhit { fill: transparent; cursor: pointer; }
.lr-rowhit.is-on { fill: var(--on_surface); opacity: 0.07; }


/* --- results ------------------------------------------------------------- */
/* One chart, four buttons. The metrics span 718,570 down to 2.45, so they get
 * one axis at a time rather than four panels inviting a comparison between
 * quantities that have nothing to do with each other. */

.rs {
  margin: 1.5rem 0;
  padding: 0.9rem;
  border: 1px solid var(--outline);
  border-radius: 8px;
  background: var(--surface_variant);
}

.rs-svg { width: 100%; height: auto; display: block; }

.rs-tabs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 0 0 0.7rem; }
.rs-tabs button {
  font: inherit;
  font-size: 0.82em;
  padding: 0.25rem 0.7rem;
  border: 1px solid var(--outline);
  border-radius: 6px;
  background: none;
  color: var(--on_surface);
  cursor: pointer;
}
.rs-tabs button[aria-pressed="true"] { border-color: var(--primary); background: var(--surface); }

.rs-bar.rs-off { fill: var(--error); }
.rs-bar.rs-on { fill: var(--primary); }
.rs-axis { stroke: var(--outline); stroke-width: 1; }
.rs-value { font-family: monospace; font-size: 11px; fill: var(--on_surface); }
.rs-kernel { font-family: monospace; font-size: 10px; fill: var(--on_surface_variant); }

.rs-legend {
  list-style: none;
  margin: 0.5rem 0 0;
  padding: 0;
  display: flex;
  gap: 1rem;
  font-size: 0.8em;
  color: var(--on_surface_variant);
}
.rs-legend li { display: flex; align-items: center; gap: 0.45rem; }
.rs-key { flex: 0 0 auto; width: 1.1rem; height: 0.55rem; border-radius: 2px; }
.rs-koff { background: var(--error); }
.rs-kon { background: var(--primary); }

/* Must come AFTER the .te-blocks base rule, not in the earlier breakpoint block
   with the ready-queue overrides: same specificity, so source order decides and
   the later `flex-direction: column` was winning inside the media query. */
@media (max-width: 34rem) {
  /* Stacked, the queue owns the full width and a tall column wastes all of it.
     A horizontal strip that scrolls sideways reads better and keeps the figure
     short. */
  /* align-items must become stretch: the base rule is flex-start, which in a
     column container sizes each item to its own content width -- so the queue
     grew to 2331px instead of filling the figure. min-width:0 as well, since a
     flex item defaults to min-width:auto and refuses to shrink below its
     content, which is what stopped overflow-x from ever engaging. */
  .te-stage { flex-direction: column; align-items: stretch; }
  .te-queue { flex: 0 0 auto; width: 100%; min-width: 0; }
  .te-blocks {
    flex: 0 0 auto;
    flex-direction: row;
    min-width: 0;
    overflow-x: auto;
    overflow-y: hidden;
    padding-bottom: 0.25rem;
  }
  .te-block { flex: 0 0 auto; }
}
