/* Wider than the shared 860px .inner: this page's board + chart need more
   room side by side than a normal text column does. Matches cycle-time-scatter-plot's
   own override so both ladder pages feel like the same size widget. */
section.alt > .inner {
  max-width: 1320px;
}

.article-header + section.alt {
  padding-top: 1.5rem;
}

.explanation {
  margin-top: 3rem;
  padding-top: 2rem;
  border-top: 1px solid var(--border);
}

.explanation p {
  max-width: 640px;
  line-height: 1.7;
  color: #374151;
  margin-bottom: 1rem;
}

.widget-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.75rem 1.5rem;
  margin-bottom: 1.5rem;
}

.widget-instructions h2 {
  font-size: 1.1rem;
  margin-bottom: 0.5rem;
}

.widget-instructions ol {
  margin: 0;
  padding-left: 1.4rem;
}

.widget-instructions li + li {
  margin-top: 0.25rem;
}

/* Deliberately not the marketing `.btn` -- an understated control, same as
   the reset-button it replaced. Only shown while the live run's own tab is
   the one being viewed (see index.ts's refreshControls). Same styling as
   cycle-time-scatter-plot's own stop-button, so the two widgets' controls
   read as one language. Lives in .run-tabs-bar itself, pinned at its fixed
   right edge after "+" and "Example" -- a control over whichever tab is showing, read
   from the same row that tab belongs to, rather than a separate row above
   it. margin-bottom lines its own bottom edge up with the tabs' baseline,
   same trick .add-run-button already uses. */
.stop-button {
  flex-shrink: 0;
  margin-bottom: 0.35rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.4rem 1rem;
  font-size: 0.85rem;
  font-weight: 600;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}

.stop-button svg {
  fill: currentColor;
}

.stop-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

/* Browser-style tab strip: tabs scroll once there are too many to fit,
   Stop and "+" both stay put at the end rather than scrolling away with
   them. Same markup/classes cycle-time-scatter-plot's own run tabs use
   (minus Stop, which stays in that page's own widget-controls bar for
   now). */
.run-tabs-bar {
  display: flex;
  align-items: flex-end;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}

.run-tabs {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
}

.run-tab {
  flex-shrink: 0;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 0.5rem 0.9rem;
  font-size: 0.85rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
  cursor: pointer;
  white-space: nowrap;
}

.run-tab:hover {
  color: var(--accent);
}

.run-tab[aria-selected="true"] {
  color: var(--accent-dark);
  border-bottom-color: var(--accent);
}

.add-run-button {
  flex-shrink: 0;
  width: 1.75rem;
  height: 1.75rem;
  margin-bottom: 0.4rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 1rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}

.add-run-button:hover:not(:disabled) {
  border-color: var(--accent);
  color: var(--accent);
}

.add-run-button:disabled {
  opacity: 0.35;
  cursor: default;
}

/* Right of "+", same height and look, just wide enough for its word. Never
   disabled: an example can be opened at any time. */
.example-button {
  flex-shrink: 0;
  height: 1.75rem;
  margin-bottom: 0.4rem;
  padding: 0 0.6rem;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}

.example-button:hover {
  border-color: var(--accent);
  color: var(--accent);
}

.board-widget {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 600px;
  gap: 1.5rem;
  align-items: start;
}

/* Chart and scrubber share a column of their own, separate from
   board-column, so the scrubber's position follows the chart's own height
   -- it would otherwise follow the grid row's height, which is dictated by
   whichever of board-column or this column is taller (almost always the
   board), stranding the scrubber far below the chart it belongs to. */
.chart-column {
  display: flex;
  flex-direction: column;
  gap: 0;
}

/* Replay controls (see spec.md's "Replay controls", src/lib/replayTransport.ts):
   a scrubber hugging the chart's bottom edge with five VCR-style buttons
   under it, the same look as team-northbound's replay controls. index.ts
   insets the rail and the button row inline from cfdChart's own plot
   margins, so the track lines up with the x-axis exactly. Same cap the
   chart's own svg uses -- needed below the 1420px breakpoint, where the grid
   collapses to a single column wider than 600px. */
.replay-wrap {
  max-width: 600px;
}

.replay-scrubber {
  position: relative;
  height: 1rem;
  margin-bottom: 0.5rem;
  cursor: pointer;
  touch-action: none;
}

/* A live run's controls: visible, but nothing to press yet. */
.replay-scrubber.replay-disabled {
  opacity: 0.35;
  cursor: default;
  pointer-events: none;
}

.replay-scrubber-dragging,
.replay-scrubber-dragging .replay-scrubber-handle {
  cursor: grabbing;
}

.replay-scrubber-rail {
  position: relative;
  height: 100%;
}

.replay-scrubber-track,
.replay-scrubber-fill,
.replay-scrubber-unreachable {
  position: absolute;
  top: 50%;
  height: 6px;
  border-radius: 999px;
  transform: translateY(-50%);
}

.replay-scrubber-track {
  left: 0;
  right: 0;
  background: var(--border);
}

/* Past a run stopped early: the playhead can't go there. */
.replay-scrubber-unreachable {
  right: 0;
  background: repeating-linear-gradient(
    -45deg,
    var(--bg-alt) 0 3px,
    var(--border) 3px 6px
  );
  opacity: 0.8;
}

.replay-scrubber-fill {
  left: 0;
  width: 0;
  background: var(--text);
  transition: width 0.15s;
}

.replay-scrubber-handle {
  position: absolute;
  top: 50%;
  left: 0;
  width: 16px;
  height: 16px;
  margin-left: -8px;
  border-radius: 50%;
  background: #fff;
  border: 2px solid var(--text);
  transform: translateY(-50%);
  cursor: grab;
  transition: left 0.15s;
}

/* No lag while dragging or playing: the handle and fill should track the
   playhead 1:1. The transition stays for a step or a jump, where a little
   motion reads as "something changed". */
.replay-scrubber-dragging .replay-scrubber-fill,
.replay-scrubber-dragging .replay-scrubber-handle,
.replay-scrubber-playing .replay-scrubber-fill,
.replay-scrubber-playing .replay-scrubber-handle {
  transition: none;
}

.replay-controls {
  display: flex;
  justify-content: center;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
}

.replay-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}

.replay-button svg {
  fill: currentColor;
}

.replay-button:not(:disabled):hover {
  border-color: var(--accent);
  color: var(--accent);
}

.replay-button:disabled {
  opacity: 0.35;
  cursor: default;
}

.board-column {
  display: grid;
  gap: 1.5rem;
}

.board-stack {
  position: relative;
}

.board {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 1rem;
}

/* Covers the board underneath it completely while a chart moment is
   hovered, so the board appears to "time travel" rather than showing a
   separate preview alongside it. Reverts the instant the pointer leaves the
   chart. Its inner .board reuses the ordinary board's own column/zone
   styling, so the only visible difference is which cards sit in which
   column. */
.board-history {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--bg-alt);
}

.card-ghost {
  opacity: 0.75;
  cursor: default;
}

.column h3 {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-bottom: 0.5rem;
}

.column-count {
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

.column-count:empty {
  display: none;
}

.column-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}

.column-header h3 {
  margin-bottom: 0;
}

.add-card-btn {
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 0.3rem 0.75rem;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
  margin-bottom: 0.5rem;
}

.add-card-btn:hover:not(:disabled) {
  background: var(--accent-dark);
}

.add-card-btn:disabled {
  background: var(--border);
  color: var(--muted);
  cursor: default;
}

.zone {
  background: #fff;
  border: 1px dashed var(--border);
  border-radius: 8px;
  height: 380px;
  padding: 0.75rem;
  overflow-y: auto;
  overflow-x: hidden;
}

.card {
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.75rem 1rem;
  font-size: 0.9rem;
  font-weight: 500;
  cursor: default;
  user-select: none;
  touch-action: none;
  display: flex;
  flex-direction: column;
}

.card + .card {
  margin-top: 0.5rem;
}

/* One line, cut short with "…", so every card is the same height whatever
   its title (see spec.md). The full title is the card's own tooltip. */
.card-title {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card.done {
  color: var(--muted);
}

/* Hovering (or clicking to pin) a card highlights it here on the board,
   the same accent-dark used for its trace on the chart -- "this is the
   one" said in both places at once. */
.card.card-hover-highlight {
  border-color: var(--accent-dark);
  box-shadow: 0 0 0 2px rgba(0, 136, 204, 0.25);
}

/* Same treatment, for tapping a mobile checklist row to pin it (see
   spec.md's "Mobile layout") -- a mobile row and a desktop card are two
   different elements for the same pin, so each needs its own rule. */
.mobile-item.card-hover-highlight {
  border-color: var(--accent-dark);
  box-shadow: 0 0 0 2px rgba(0, 136, 204, 0.25);
}

/* Holds the tag row and the Start/Finish button on one line at the card's
   own bottom edge (.card is the flex column that makes margin-top: auto
   meaningful here), not just under the title, so both read in the same
   place on every card regardless of how many lines the title wraps to. */
.card-footer {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.35rem;
}

/* Same pill language cycle-time-scatter-plot's own age badge uses, so the two
   notebook widgets read as views of the same team rather than unrelated
   ones. One row, not a wrapping pile: lead time (when it exists) sits at
   this row's left edge, age/cycle time always at its right edge (.card-tag-
   right's margin-left: auto is what pushes it there, whether it's sharing
   the row with lead time or -- for a Doing card's lone "age" tag -- on its
   own). The container just collapses to nothing when there's no tag yet to
   show (a card still in To Do, before its Start button has been pressed).*/
.card-tags {
  display: flex;
  gap: 0.35rem;
}

/* A Done card has no button, so its tag row spans the footer and cycle
   time sits at the right edge even when the short labels leave room. */
.card.done .card-tags {
  flex: 1;
}

.card-tags:empty {
  display: none;
}

.card-tag-right {
  margin-left: auto;
}

/* "lead time: 5d  cycle time: 3d" needs about a 15rem-wide column; a
   narrower one shows "LT 5d  CT 3d" instead, so a Done card's tags stay on
   one row in a fixed-width column (see spec.md and lib/cardTag.ts). */
.zone {
  container-type: inline-size;
}

.card-tag-label-short {
  display: none;
}

@container (max-width: 15rem) {
  .card-tag-label {
    display: none;
  }

  .card-tag-label-short {
    display: inline;
  }
}

/* A Doing card's age tag and Finish button need a little more room than
   a narrow column's cards leave inside their usual padding. */
@container (max-width: 9.5rem) {
  .card {
    padding-left: 0.6rem;
    padding-right: 0.6rem;
  }

  .card-footer {
    gap: 0.35rem;
  }

  .zone .card-action-btn {
    padding-left: 0.5rem;
    padding-right: 0.5rem;
  }
}

/* Moves a card forward without dragging it -- the fix for a phone-sized
   viewport where the target column can be out of reach (see the "Start" /
   "Finish" buttons note below). Deliberately separate from the card body
   itself, which still carries its own click-to-pin behavior -- a bare tap
   on the card still pins, only this button advances it. Shown at every
   screen width, not just narrow ones: dragging keeps working unchanged
   everywhere, this is purely additive. Hidden outright on a Done card --
   see index.ts's syncActionButton -- since there's nowhere further to go.
   Same pill shape as .add-card-btn, so the two forward-moving controls on
   this page read as one language. */
.card-action-btn {
  flex-shrink: 0;
  margin-left: auto;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  font-size: 0.7rem;
  font-weight: 600;
  cursor: pointer;
  transition: background 0.15s;
}

.card-action-btn:hover:not(:disabled) {
  background: var(--accent-dark);
}

.card-action-btn:disabled {
  background: var(--border);
  color: var(--muted);
  cursor: default;
}

.card-action-btn[hidden] {
  display: none;
}

.card-tag {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  background: #e6f6ff;
  border-radius: 999px;
  padding: 0.1rem 0.5rem;
  white-space: nowrap;
}

.card.draggable {
  cursor: grab;
  border-color: var(--accent);
}

.card.dragging {
  cursor: grabbing;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.15);
}

.chart {
  width: 100%;
  max-width: 600px;
  height: auto;
}

/* Full rectangular frame plus gridlines, the way a real dashboard tool
   frames its chart -- not just an L-shaped pair of axes. */
.chart .cfd-border {
  fill: none;
  stroke: var(--border);
  stroke-width: 1;
}

.chart .tick {
  stroke: var(--border);
  stroke-width: 1;
}

.chart .tick-label {
  fill: var(--muted);
  font-size: 8px;
  text-anchor: middle;
}

.chart .tick-label-y {
  text-anchor: end;
}

.chart .axis-label {
  fill: var(--muted);
  font-size: 9px;
  text-anchor: middle;
}

.chart .crosshair {
  stroke: var(--text);
  stroke-width: 1;
  stroke-dasharray: 3 3;
  opacity: 0.5;
}

.chart .now-label {
  fill: var(--text);
  font-size: 8px;
  font-weight: 600;
  text-anchor: middle;
}

/* Dotted, not dashed -- never mistaken for the "now" crosshair even when
   both show at once. mix-blend-mode inverts against whatever it's drawn
   over, so the line stays visible crossing the Done band (which happens to
   share the same blue a plain stroke color would) as well as the lighter
   bands and the bare chart background. Thin, sparse dots and partial
   opacity keep it a quiet marker -- the scrubber below already carries the
   emphasis, so this just needs to be findable, not loud. No text label of
   its own -- the reconstructed board underneath already shows what
   changed. */
.chart .hover-line {
  stroke: #fff;
  stroke-width: 0.9;
  stroke-dasharray: 1 3;
  mix-blend-mode: difference;
  opacity: 0.55;
}

/* The playhead coming to rest settles the line in place -- a touch heavier
   and more opaque, so the line itself shows the drag has ended rather than
   still being in progress, but it stays dotted throughout: pausing the
   scrubber is a statement about motion, not something that should also
   change what the line looks like. */
.chart .hover-line-settled {
  stroke-width: 1.1;
  opacity: 0.75;
}

/* A quiet, near-white chip regardless of which band it sits over -- reads as
   a label sitting on top of the chart, not a badge shouting over it. */
.chart .band-tag-bg {
  fill: rgba(255, 255, 255, 0.82);
}

.chart .band-tag-text {
  fill: var(--text);
  font-size: 7.5px;
  font-weight: 500;
}

/* Traces one card's own history on hover -- same accent color one-card-
   one-dot's own card/dot highlighting uses, so the "this is you, hovered"
   language matches across both notebook widgets even though this chart has
   no per-card dot of its own to scale up. */
.chart .card-marker-circle {
  fill: none;
  stroke: var(--accent-dark);
  stroke-width: 1.75;
}

.chart .card-marker-arrow {
  stroke: var(--accent-dark);
  stroke-width: 1.5;
}

.chart .card-marker-arrowhead {
  fill: var(--accent-dark);
}

/* A white halo (paint-order + stroke) keeps each arrow's duration legible
   over any band color it happens to cross, the same trick the chart's now-
   removed measurement labels used to use. */
.chart .card-marker-label {
  fill: var(--accent-dark);
  font-size: 7.5px;
  font-weight: 600;
  paint-order: stroke;
  stroke: #fff;
  stroke-width: 2.5px;
  stroke-linejoin: round;
}

/* Three cumulative boundary lines, stroked over each other -- distinct in
   both hue and lightness (blue / amber / mid slate), so the bands still read
   apart from each other for colorblind readers even before the legend's own
   text labels settle it. Same three hues team-northbound's own CFD uses for
   its boundary lines, so "Done"/"Doing"/"To Do" read as the same colors
   everywhere on the site. */
.chart .cfd-line {
  fill: none;
  stroke-width: 1.75;
}

.chart .cfd-line-todo {
  stroke: #94a3b8;
}

.chart .cfd-line-doing {
  stroke: #f59e0b;
}

.chart .cfd-line-done {
  stroke: var(--accent);
}

/* Doing and Done are filled as translucent areas between their own boundary
   line and the one below it; To Do (the topmost band) is left unfilled --
   see cfdChart.ts's own module doc comment for why. */
.chart .cfd-fill {
  stroke: none;
}

.chart .cfd-fill-doing {
  fill: #f59e0b;
  fill-opacity: 0.18;
}

.chart .cfd-fill-done {
  fill: var(--accent);
  fill-opacity: 0.18;
}

.chart .legend-swatch-todo {
  stroke: #94a3b8;
}

.chart .legend-swatch-doing {
  stroke: #f59e0b;
}

.chart .legend-swatch-done {
  stroke: var(--accent);
}

.chart .legend-swatch {
  stroke-width: 2;
}

.chart .legend-label {
  fill: var(--muted);
  font-size: 8px;
}

@media (max-width: 1420px) {
  .board-widget {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Below 600px the 3-column board (and its own hover-history overlay) is
   replaced outright by a flat checklist -- see spec.md's "Mobile layout" for
   why a merely-restacked or merely-grouped board still didn't read as
   designed for a phone. Both checklist elements exist in the DOM at every
   width (built once in index.ts, same as the board itself) and are simply
   hidden outside their own breakpoint, the same toggle the board/board-
   history pair already uses via [hidden] -- not reflowed into existence via
   `order`, since this list's own ordering rule (plain insertion order,
   never regrouped by status) has nothing in common with the board's
   column-based one for `order` tricks to reuse. */
.mobile-checklist,
.mobile-history-list {
  display: none;
}

@media (max-width: 600px) {
  .board,
  .board-history {
    display: none;
  }

  .mobile-checklist {
    display: block;
  }

  /* The shared top-bar Stop -- [data-stop], not the per-run one beside
     mobile "+ Add" -- would otherwise sit visible at the same time as its
     mobile replacement; it's not gated by [hidden] at this width the way
     board vs. board-history is, since JS only ever toggles it for the
     live/started/active-tab condition, not for viewport width. */
  [data-stop] {
    display: none;
  }

  /* Toggled by the same [data-board-history]-style hidden attribute the
     desktop ghost overlay uses -- see index.ts's createHistoryRenderer.
     Only the "shown" half needs a rule here: [hidden] (site/styles.css)
     already forces display: none regardless of width. */
  .mobile-history-list:not([hidden]) {
    display: block;
  }
}

/* Holds "+ Add" and (once the run has started) Stop on one row -- keeping
   Stop next to the per-run control it actually acts on, rather than in the
   shared bar above the tabs the way it sits at wider widths (see index.ts's
   mobileStopButton). */
.mobile-checklist-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}

.mobile-add-btn {
  margin-bottom: 0;
}

/* Same fixed-height/internal-scroll pattern .zone uses at wider widths, so
   the checklist's own footprint stays just as bounded as the board's. */
.mobile-checklist-list {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  height: 380px;
  overflow-y: auto;
  overflow-x: hidden;
  background: #fff;
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 0.75rem;
}

/* Overlays the live checklist in place, the same stacked-absolute trick
   .board-history uses over .board -- both live inside the same .board-stack. */
.mobile-history-list {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--bg-alt);
  overflow-y: auto;
  overflow-x: hidden;
  padding: 0.75rem;
}

.mobile-item {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  background: var(--bg-alt);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 0.6rem 0.75rem;
  transition: opacity 0.3s ease;
}

.mobile-history-list .mobile-item {
  background: #fff;
}

.mobile-item + .mobile-item {
  margin-top: 0.5rem;
}

/* Read-only reconstruction row (see createHistoryRenderer) -- no control of
   its own, same quiet dimming .card-ghost uses on the desktop ghost board. */
.mobile-item-ghost {
  opacity: 0.75;
  cursor: default;
}

/* A finished row stays in the list (see spec.md), checked with its title
   struck through. pointer-events: none keeps the box from being unchecked
   without touching the checkbox's own :disabled (dimmed) styling, which
   would mute the checked state while the run is still live. */
.mobile-item-finished .mobile-item-title {
  text-decoration: line-through;
  color: var(--muted);
}

.mobile-item-finished .mobile-finish-checkbox {
  pointer-events: none;
}

.mobile-item-main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
}

.mobile-item-title {
  font-size: 0.9rem;
  font-weight: 500;
}

.mobile-item-control {
  flex-shrink: 0;
  width: 2.25rem;
  height: 2.25rem;
}

/* Same hand-drawn-SVG-icon, square-button language team-northbound's own
   .replay-button transport controls use, so a play icon reads the same way
   on both pages rather than inventing a second "start" visual language. */
.mobile-start-btn {
  width: 100%;
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: #fff;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 6px;
  cursor: pointer;
  transition: border-color 0.15s, color 0.15s;
}

.mobile-start-btn svg {
  fill: currentColor;
}

.mobile-start-btn:not(:disabled):hover {
  border-color: var(--accent);
  color: var(--accent);
}

.mobile-start-btn:disabled {
  opacity: 0.35;
  cursor: default;
}

/* A custom checkbox (native input, visually hidden but still the real,
   keyboard/focus-accessible control) replacing the play icon in place once
   an item is in progress -- same footprint as .mobile-start-btn so swapping
   the two doesn't shift the row's own layout. */
.mobile-finish-checkbox {
  position: relative;
  width: 100%;
  height: 100%;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.mobile-finish-checkbox input {
  position: absolute;
  inset: 0;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.mobile-finish-checkbox-box {
  position: relative;
  width: 1.3rem;
  height: 1.3rem;
  border: 2px solid var(--border);
  border-radius: 4px;
  background: #fff;
  transition: background 0.15s, border-color 0.15s;
  /* The real input sits beneath this, inset: 0 across the whole label --
     without this, the box (painted after it) would catch the click instead
     of letting it reach the input underneath. */
  pointer-events: none;
}

.mobile-finish-checkbox input:hover:not(:disabled) + .mobile-finish-checkbox-box {
  border-color: var(--accent);
}

.mobile-finish-checkbox input:checked + .mobile-finish-checkbox-box {
  background: var(--accent);
  border-color: var(--accent);
}

.mobile-finish-checkbox input:checked + .mobile-finish-checkbox-box::after {
  content: "";
  position: absolute;
  left: 0.4rem;
  top: 0.18rem;
  width: 0.3rem;
  height: 0.6rem;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}

.mobile-finish-checkbox input:disabled {
  cursor: default;
}

.mobile-finish-checkbox input:disabled + .mobile-finish-checkbox-box {
  opacity: 0.35;
}

/* ── Phone views (see spec.md's "Phone views", ../styles.css) ── */

/* The how-to's two wordings: one per width. */
.instructions-phone {
  display: none;
}

@media (max-width: 600px) {
  .instructions-wide {
    display: none;
  }

  .instructions-phone {
    display: block;
  }

  /* Explanation view: the how-to moves below the explanation, as the
     closing call to action leading into the Simulation tab. With the widget
     gone from between them, the explanation's divider and gap go too. */
  body.phone-views:not([data-phone-view="simulation"]) section.alt > .inner {
    display: flex;
    flex-direction: column;
  }

  .widget-controls {
    order: 1;
    margin-top: 0.5rem;
    margin-bottom: 0;
  }

  .explanation {
    margin-top: 0;
    padding-top: 0;
    border-top: none;
  }

  /* Lets the tabs scroll sideways instead of pushing "+" off screen. */
  .run-tabs {
    min-width: 0;
  }

  /* Simulation view: fills the screen above the bottom bar exactly, using
     the shared [data-phone-view-screen]/[data-phone-view-fill] chain in
     ../styles.css (marked from section.alt down to the checklist, see the
     HTML and index.ts's buildBoardWidget). Stop stays beside "+ Add" on the
     checklist's own top row. Only the spacing is this page's. */
  body.phone-views[data-phone-view="simulation"] .run-tabs-bar {
    margin-bottom: 0.75rem;
  }

  /* stretch: the wide layout's align-items: start would otherwise shrink
     the chart column to the svg's default width here. */
  body.phone-views[data-phone-view="simulation"] .board-widget {
    gap: 0.75rem;
    align-items: stretch;
  }
}

/* ── Presentation mode (see spec.md's "Presentation mode", ../styles.css) ──
   The run panels take the height the tabs row leaves, and are a
   size container, so the chart can be as wide as that height allows (the
   chart is 4:3, the replay controls sit under it) without crowding the board out:
   at most a bit over half the width. Board and chart stay side by side at
   every wide width, and the board's columns stretch to the full height. */
@media (min-width: 601px) {
  body[data-presenting] .run-tabs-bar {
    flex-shrink: 0;
  }

  body[data-presenting] .run-panels {
    flex: 1;
    min-height: 0;
    container-type: size;
  }

  body[data-presenting] .run-panel {
    height: 100%;
  }

  body[data-presenting] .board-widget {
    height: 100%;
    grid-template-columns: minmax(0, 1fr) min(55cqw, (100cqh - 4.25rem) * 4 / 3);
    grid-template-rows: minmax(0, 1fr);
  }

  body[data-presenting] .chart,
  body[data-presenting] .replay-wrap {
    max-width: none;
  }

  body[data-presenting] .board-column {
    height: 100%;
    min-height: 0;
    grid-template-rows: minmax(0, 1fr);
  }

  body[data-presenting] .board-stack,
  body[data-presenting] .board,
  body[data-presenting] .board-history .board {
    height: 100%;
  }

  body[data-presenting] .column {
    display: flex;
    flex-direction: column;
    min-height: 0;
  }

  body[data-presenting] .zone {
    flex: 1;
    min-height: 0;
    height: auto;
  }
}
