/* controls.css: the small vocabulary of inputs and stages every panel uses.
   Panels do not ship their own CSS. Add to this file instead. */

/* the one controls block under the stages: transport, picker, sliders and sweeps, toggles and
   force rows, other buttons, in that order. Grid rows stretch (a slider or sweep spans the
   column); buttons go in a .controls-row, where each keeps its natural width. */
.controls { display: grid; gap: 0.75rem; }
/* a row of buttons or toggles: each its natural width, left-aligned, wrapping if it must */
.controls-row { display: flex; gap: 0.5rem 1rem; flex-wrap: wrap; align-items: center; justify-content: flex-start; }

.control { display: grid; gap: 0.2rem; font-size: 0.9rem; }
.control-label { display: flex; justify-content: space-between; gap: 1rem; }
.control-label output {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  color: var(--muted);
}
input[type="range"] { width: 100%; margin: 0; accent-color: var(--accent); }

fieldset.choice {
  margin: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  font-size: 0.9rem;
}
fieldset.choice legend { padding: 0 0.3rem; color: var(--muted); font-size: 0.8rem; }
fieldset.choice label { display: inline-flex; align-items: center; gap: 0.4rem; cursor: pointer; }
fieldset.choice input { accent-color: var(--accent); margin: 0; }

.readout {
  font-family: var(--mono);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
  white-space: pre-wrap;
  word-break: break-word;
}
.readout .label { color: var(--muted); }
.readout .stable { color: var(--stable); font-weight: 600; }
.readout .unstable { color: var(--unstable); font-weight: 600; }

/* The prose column, scaled to the page. `--prose-fit` is written by shared/ui/fit.js when the
   text would otherwise run off the bottom of a short viewport (where `.pane-body`'s
   `overflow: hidden` used to eat the last paragraph); it is 1, and this a no-op, for a pane
   that fits. Type and measure shrink together — the measure in `em` here rather than the
   root's `rem` — so the line stays 38 characters wide and only the column's height gives. */
.prose {
  font-size: calc(1rem * var(--prose-fit, 1));
  --measure: calc(var(--measure-chars) * 1em);
}

/* verdict words bound into prose (bindMath), now that a verdict this color-coded no longer
   needs a readout box to live in */
.prose .stable { color: var(--stable); font-weight: 600; }
.prose .unstable { color: var(--unstable); font-weight: 600; }

.btn {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: var(--radius);
  padding: 0.35rem 0.8rem;
  cursor: pointer;
}
.btn:hover { background: var(--accent-soft); }
.btn[aria-pressed="true"] { background: var(--accent); color: #fff; border-color: var(--accent); }

/* A stage is a positioned box holding one or more stacked canvases, a direct child of the
   `.viz` column (layout.css), sized entirely here by its kind (shared/gfx/stage.js):
     .fill    full width; flexes into its share of the column's free height, capped at its
              width (never taller than square) and floored at a third of it (3:1)
     .strip   full width, a third of its width tall (3:1), does not flex
     .square  the largest square that fits its `.stage-slot`, which flexes like a fill; the
              slot is a size container so the square can read both of its extents
   Several fill stages and slots split the free height equally; one that reaches its cap
   leaves the rest to the others, and whatever no stage takes collects at the bottom of the
   column, under the controls and equations.
   The 3:1 floor and the strip's height grow with the column's width alone, and the column is
   wider for its height the wider the viewport (0.9 at the reference viewports, 1.25 at
   1920×1080, 1.4 at 2560×1440), so both are also bounded by the column's height: the fill
   floor at 20cqh, the strip at 30.5cqh. At 1280×720 and 1440×900 the strip's bound does not
   bite (30.5cqh = 164.8 / 219.7px against strips of 162.7 / 216px), so strips stay 3:1 there.
   The floor's does (20cqh = 108 / 144px), but a floor only matters to a pane whose controls
   and equations leave its fill less than 3:1. Past that a strip runs wider than 3:1 and a
   fill at its floor flatter than 3:1, rather than the column running off the bottom of
   `.pane-body`, which clips. */
.stage {
  position: relative;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  touch-action: none;
}
.pane-body .viz > .stage.fill,
.pane-body .viz > .stage-slot {
  flex: 1 1 0;
  width: 100%;
  min-height: min(100cqw / 3, 20cqh);
  max-height: 100cqw;
}
.pane-body .viz > .stage.strip { width: 100%; height: min(100cqw / 3, 30.5cqh); }
.pane-body .viz > .stage-slot { container-type: size; }
.stage-slot > .stage.square { width: min(100cqw, 100cqh); aspect-ratio: 1; }
.stage > canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}
.stage[data-grab] { cursor: grab; }
.stage[data-grab="active"] { cursor: grabbing; }

/* legend swatches for prose: <span class="swatch exact"></span> */
.swatch {
  display: inline-block;
  width: 0.8em;
  height: 0.8em;
  border-radius: 2px;
  vertical-align: -0.05em;
  margin-right: 0.3em;
}
.swatch.exact { background: var(--exact); }
.swatch.approx { background: var(--approx); }
.swatch.stable { background: var(--stable); }
.swatch.unstable { background: var(--unstable); }
.swatch.region { background: var(--region); opacity: 0.55; }
.swatch.wind { background: var(--force-wind); }
.swatch.gravity { background: var(--force-gravity); }
.swatch.drag { background: var(--force-drag); }
.swatch.spring { background: var(--force-spring); }

/* player transport: play/pause, step, reset, optional speed buttons */
.transport { display: flex; gap: 1rem; flex-wrap: wrap; align-items: center; }
.transport-group { display: inline-flex; gap: 0.35rem; }
.transport .btn { min-width: 4rem; }

/* a row of one toggle-driven force beside its live equation (panel 2 spine and right, panel
   5 spine), on a .controls-row: a fixed min-height so the row's height cannot change while
   it runs, `nowrap` so a live number growing a digit (or the off-hint) cannot force a second
   line that would grow it anyway, and `overflow-x: auto` so a row too wide for its column
   scrolls instead of reflowing or bleeding into whatever is beside it. */
.force-row { min-height: 2.75rem; flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden; }
/* the off-hint stays in the DOM at its full width always, so toggling a force never itself
   changes the row's width; :has() reacts to the checkbox instead of a text swap doing it. */
.force-row:has(input:checked) .force-off-hint { visibility: hidden; }

/* the live equations, last in a .viz under the controls (CLAUDE.md, "Pane layout standard"):
   their natural height, never scaled. The column's own gap spaces them from the controls, so
   a block equation here drops its margins. */
.equations { display: grid; gap: 0.5rem; }
.equations > math[display="block"],
.pane-body .viz > math[display="block"] { margin: 0; }

/* the ΣF / a summary under a force stage (panel 2 spine): reserved height, and horizontal
   overflow contained the same way as .force-row, for the same reason */
.totals { min-height: 3.25rem; overflow-x: auto; }

/* a switch: toggle(store, key) / toggleFn(); the checkbox stays in the tree for keyboard and
   assistive tech, the track is what you see */
.toggle {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  cursor: pointer;
  user-select: none;
}
.toggle input { position: absolute; opacity: 0; width: 1px; height: 1px; margin: 0; pointer-events: none; }
.toggle-track {
  flex: none;
  width: 2rem;
  height: 1.15rem;
  border-radius: 1rem;
  background: var(--border-strong);
  transition: background 0.15s;
}
.toggle-track::after {
  content: '';
  display: block;
  width: calc(1.15rem - 4px);
  height: calc(1.15rem - 4px);
  margin: 2px;
  border-radius: 50%;
  background: #fff;
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
  transition: translate 0.15s;
}
.toggle input:checked + .toggle-track { background: var(--accent); }
.toggle input:checked + .toggle-track::after { translate: 0.85rem 0; }
.toggle input:focus-visible + .toggle-track { outline: 2px solid var(--accent); outline-offset: 2px; }

/* a scrubbable number in a formula (ui/scrub.js): drag it sideways */
.scrub {
  cursor: ew-resize;
  touch-action: none;
  user-select: none;
  -webkit-user-select: none;
  color: var(--accent);
  border-bottom: 2px dotted var(--accent);
  border-radius: 2px;
  padding: 0 0.1em;
}
.scrub:hover, .scrub[data-active] { background: var(--accent-soft); }
.scrub[data-active] { border-bottom-style: solid; }
.scrub:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* a matrix entry the control being dragged is writing (panel 6 left). mathbackground paints
   the bare glyph box, with no padding and no radius, so it reads as a colour block; this
   reads as a chip. */
.mn-hi {
  background: var(--accent-soft);
  color: var(--accent);
  padding: 0 0.15em;
  border-radius: 2px;
}

/* the sweep strip (ui/sweep.js): ticks, one per value; hover or drag selects */
.sweep {
  display: grid;
  gap: 0.3rem;
  font-size: 0.85rem;
  touch-action: none;
  user-select: none;
}
.sweep:focus-visible { outline: none; }
.sweep:focus-visible .sweep-track { outline: 2px solid var(--accent); outline-offset: 2px; }
.sweep-head { display: flex; justify-content: space-between; gap: 1rem; color: var(--muted); }
.sweep-head output { font-family: var(--mono); font-variant-numeric: tabular-nums; color: var(--fg); }
.sweep-track {
  height: 1.8rem;
  padding: 0 0.6rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  cursor: ew-resize;
}
.sweep-ticks { position: relative; height: 100%; }
.sweep-tick {
  position: absolute;
  top: 0.35rem;
  bottom: 0.35rem;
  width: 2px;
  margin-left: -1px;
  border-radius: 1px;
  background: var(--border-strong);
  transition: background 0.1s, width 0.1s;
}
.sweep-tick[aria-selected="true"] { width: 4px; margin-left: -2px; background: var(--accent); }
.sweep-ends { display: flex; justify-content: space-between; font-family: var(--mono); font-size: 0.75rem; color: var(--muted); }
