/* sandbox.css — the GTRE Sandbox console.
 *
 * WHY A SEPARATE STYLESHEET AND NOT TAILWIND UTILITIES
 *
 * The same reason the nav and footer live in site.css, stated in BUILD.md:
 * tailwind.css is a PRE-BUILT artefact containing only the utilities that were
 * present in the markup when it was generated. A class written afterwards
 * resolves to nothing and fails silently. This page is a dense instrument
 * console with roughly two hundred distinct style decisions, none of which
 * existed at generation time, so every one of them would have been a silent
 * drop. Defining them here makes the page independent of whether the Tailwind
 * CLI is ever run again.
 *
 * The palette is not a new one. Every colour below is a token already in
 * tailwind.config.js — void, panel, panel2, grid, txt, dim, accent, go, warn,
 * alert — plus the #ffe14d used for TOOLS in the primary nav. The sandbox reads
 * as part of the site because it is literally the same ten values.
 *
 * ONE DELIBERATE DEPARTURE: yellow, not teal, carries identity here. Teal means
 * "the API" everywhere else on this site, and the whole point of this page is
 * that it is NOT the API. Anything yellow on this page is sandbox-local and
 * uncertified. Teal is reserved for links out to the real thing.
 */

/* ── [hidden] must actually hide ───────────────────────────────────────────
 *
 * sandbox.js shows and hides eleven panels by toggling the `hidden` attribute.
 * The browser's own `[hidden] { display: none }` — and Tailwind's copy of it in
 * tailwind.css — sits at the same specificity as a single class, so ANY class
 * here that sets `display` overrides it and the "hidden" element renders anyway.
 *
 * It did, three times over, on the live page: the RIC legend drawn on top of the
 * inertial legend at the same 11px offset (the overlapping text in the stage),
 * the series view's HOVER READ hints beside the orbital ones, and the source
 * manifest's column of dashes before any file was loaded.
 *
 * It had been found twice before and patched per component — `.seg-group[hidden]`
 * and `.prog[hidden]`, further down, are those patches. That only covers the
 * components someone remembered, which is how the next three leaked. `!important`
 * on the bare attribute closes the whole class: nothing on this page is ever meant
 * to be visible while it carries `hidden`, and a panel being shown has the
 * attribute removed, so the rule simply stops matching it. */
[hidden] { display: none !important; }

/* ── Shell ────────────────────────────────────────────────────────────────── */

/* HOW FAR THE PHOTOGRAPHS ARE HELD BACK, in one place. They sit in a dark
 * instrument console, and at full strength they pull the eye away from the
 * panels that do the work -- the same problem the home page plates had.
 * Applied as a flat wash over the image rather than as opacity on the element,
 * so the panel keeps its own ground colour underneath and the value means the
 * same thing for both pictures. 0 is the untouched photograph, 1 is black. */
:root { --shot-dim: 0.55; }

.sbx-wrap { margin: 0 auto; max-width: 1600px; padding: 18px 12px 56px; }
@media (min-width: 768px) { .sbx-wrap { padding: 22px 16px 72px; } }

.sbx-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 14px;
}
/* WRAPS. The row is six items -- rule, TOOLS, //, GTRE SANDBOX, //, OFFLINE --
 * and at 0.18em tracking it comes to about 325px, which does not fit the 286px
 * a 320px phone leaves. Without flex-wrap it could only overflow, and it did:
 * 39px of horizontal scroll on the whole page.
 *
 * flex: none on the rule matters once wrapping is allowed. Its width is 24px
 * but flex items shrink by default, so under pressure it was being squeezed to
 * zero -- the marker vanished and the text still overflowed, which is the worst
 * of both. Fixed now, it either sits at 24px or moves to the next line. */
.sbx-eyebrow {
  display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap;
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  letter-spacing: 0.18em; text-transform: uppercase; color: #5a7090;
}
.sbx-eyebrow .bar { height: 1px; width: 24px; background: #ffe14d; flex: none; }
.sbx-eyebrow .lit { color: #ffe14d; }
.sbx-h1 {
  font-size: 26px; font-weight: 800; letter-spacing: -0.02em;
  line-height: 1.1; color: #c7d5e8; margin-top: 8px;
}
@media (min-width: 768px) { .sbx-h1 { font-size: 34px; } }
/* The expansion under the wordmark. Deliberately quiet: the acronym is the name
 * people will use, and the full form is there to be read once. */
.sbx-sub {
  margin-top: 6px;
  font-family: "JetBrains Mono", monospace; font-size: 11px;
  letter-spacing: 0.13em; text-transform: uppercase; color: #5a7090;
}
.sbx-lede {
  margin-top: 10px; max-width: 74ch; font-size: 13px; line-height: 1.7; color: #5a7090;
}

/* ── The standing disclosure ──────────────────────────────────────────────
 * Not a dismissible toast. This page produces numbers that look exactly like
 * the numbers the certified engine produces, and the only thing distinguishing
 * them is this notice, so it does not go away. */

.sbx-notice {
  margin: 16px 0 20px; border: 1px solid rgba(255,225,77,0.42);
  background: rgba(255,225,77,0.055); padding: 12px 14px;
  display: grid; gap: 7px;
}
.sbx-notice .hd {
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  letter-spacing: 0.16em; text-transform: uppercase; color: #ffe14d;
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
}
.sbx-notice .hd .dot {
  width: 6px; height: 6px; background: #ffe14d; flex: none;
}
.sbx-notice p { font-size: 12px; line-height: 1.72; color: #8ea4c4; max-width: 96ch; }
.sbx-notice p + p { margin-top: 2px; }
.sbx-notice b { color: #c7d5e8; font-weight: 600; }
.sbx-notice code {
  font-family: "JetBrains Mono", monospace; font-size: 11px;
  color: #ffe14d; background: rgba(255,225,77,0.09); padding: 1px 5px;
  border: 1px solid rgba(255,225,77,0.22);
}

/* ── Console grid ─────────────────────────────────────────────────────────── */

.sbx-console {
  display: grid; gap: 1px; background: #1a2740; border: 1px solid #1a2740;
  grid-template-columns: 1fr;
}
@media (min-width: 1180px) {
  .sbx-console { grid-template-columns: 306px minmax(0, 1fr) 286px; }
}

.sbx-rail { background: #0a0f18; min-width: 0; display: flex; flex-direction: column; }
.sbx-stage { background: #05070c; min-width: 0; display: flex; flex-direction: column; }

/* ── Panels inside the rails ──────────────────────────────────────────────── */

.pnl { border-bottom: 1px solid #1a2740; }
.pnl:last-child { border-bottom: none; }
.pnl-hd {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 8px 11px; background: #0d1420; border-bottom: 1px solid #1a2740;
}
.pnl-hd h2 {
  font-family: "JetBrains Mono", monospace; font-size: 9.5px;
  letter-spacing: 0.17em; text-transform: uppercase; color: #5a7090;
  font-weight: 500; display: flex; align-items: center; gap: 7px;
}
.pnl-hd h2::before {
  content: ""; width: 4px; height: 4px; background: #1a2740; flex: none;
}
.pnl.armed .pnl-hd h2::before { background: #ffe14d; }
.pnl-hd .tag {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.12em; color: #3c4d68;
}
.pnl-body { padding: 11px; display: grid; gap: 10px; }

/* ── Form controls ────────────────────────────────────────────────────────── */

.fld { display: grid; gap: 4px; min-width: 0; }
.fld > label, .lbl {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.14em; text-transform: uppercase; color: #5a7090;
  display: flex; align-items: center; justify-content: space-between; gap: 6px;
}
.fld > label .unit { color: #3c4d68; letter-spacing: 0.08em; }

.row { display: grid; gap: 8px; grid-template-columns: 1fr 1fr; }
.row.three { grid-template-columns: 1fr 1fr 1fr; }

input[type="number"], input[type="text"], select {
  width: 100%; min-width: 0; background: #05070c; color: #c7d5e8;
  border: 1px solid #1a2740; padding: 6px 8px;
  font-family: "JetBrains Mono", monospace; font-size: 11.5px;
  font-variant-numeric: tabular-nums;
  appearance: none; -webkit-appearance: none; border-radius: 0;
}
input[type="number"]:hover, select:hover { border-color: #24365a; }
input[type="number"]:focus, input[type="text"]:focus, select:focus {
  border-color: #ffe14d; outline: none;
}
input[type="number"]:disabled, select:disabled {
  color: #3c4d68; background: #080c14; cursor: not-allowed;
}
/* Spinners are 22px of chrome on a 6px-tall field and they differ per browser. */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }

select {
  background-image:
    linear-gradient(45deg, transparent 50%, #5a7090 50%),
    linear-gradient(135deg, #5a7090 50%, transparent 50%);
  background-position: right 11px center, right 6px center;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
  padding-right: 22px;
}
select option { background: #0a0f18; color: #c7d5e8; }

/* Segmented control — mutually exclusive modes read better as one strip of
 * cells than as a dropdown, because the alternatives stay visible. */
.seg { display: flex; border: 1px solid #1a2740; background: #05070c; }
.seg button {
  flex: 1 1 0; min-width: 0; background: transparent; border: none;
  border-right: 1px solid #1a2740; color: #5a7090; cursor: pointer;
  padding: 6px 4px; font-family: "JetBrains Mono", monospace;
  font-size: 9.5px; letter-spacing: 0.1em; text-transform: uppercase;
  transition: color 110ms linear, background-color 110ms linear;
}
.seg button:last-child { border-right: none; }
.seg button:hover { color: #c7d5e8; background: #0d1420; }
.seg button[aria-pressed="true"] {
  color: #ffe14d; background: rgba(255,225,77,0.11);
}

/* A segmented control whose halves swap depending on the loaded file. The
 * [hidden] rule needs the higher specificity to beat the display:flex above it,
 * which is why it is written as .seg-group[hidden] rather than a bare [hidden]. */
.seg-group { display: flex; flex: 1 1 auto; min-width: 0; }
.seg-group[hidden] { display: none; }

/* ── Channel list ─────────────────────────────────────────────────────────
 * Generated at load, because the columns are not known until a file arrives.
 * The colour swatch sits beside the name so the plot can be read without a
 * separate legend, and the range is shown because "is this column all zeros"
 * is the first question anyone asks of a verification table. */
.chan-list {
  max-height: 190px; overflow-y: auto;
  border: 1px solid #1a2740; background: #05070c;
}
.chan {
  display: flex; align-items: center; gap: 7px;
  padding: 4px 8px; cursor: pointer;
  border-bottom: 1px solid rgba(26,39,64,0.55);
}
.chan:last-child { border-bottom: none; }
.chan:hover { background: #0a0f18; }
.chan input { position: absolute; opacity: 0; width: 0; height: 0; }
.chan .sw { width: 9px; height: 9px; flex: none; opacity: 0.25; }
.chan input:checked ~ .sw { opacity: 1; }
.chan input:focus-visible ~ .sw { outline: 2px solid #38e1c9; outline-offset: 2px; }
.chan .nm {
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  color: #5a7090; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chan input:checked ~ .nm { color: #c7d5e8; }
.chan .rg {
  margin-left: auto; flex: none;
  font-family: "JetBrains Mono", monospace; font-size: 8.5px;
  color: #24365a; font-variant-numeric: tabular-nums; white-space: nowrap;
}

.chan-readout { display: grid; gap: 3px; }
.chan-readout .crow {
  display: flex; align-items: center; gap: 7px;
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  font-variant-numeric: tabular-nums;
}
.chan-readout .crow .sw { width: 9px; height: 2px; flex: none; }
.chan-readout .crow .nm {
  color: #5a7090; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chan-readout .crow .val { margin-left: auto; flex: none; color: #c7d5e8; }
.chan-readout .crow.sub { color: #3c4d68; font-size: 9px; padding-left: 16px; }

/* Checkbox: the native control cannot be recoloured consistently, and a 13px
 * OS checkbox next to 9px mono labels is visually out of scale. */
.chk { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.chk input { position: absolute; opacity: 0; width: 0; height: 0; }
.chk .box {
  width: 12px; height: 12px; flex: none; border: 1px solid #24365a;
  background: #05070c; position: relative;
}
.chk input:checked + .box { border-color: #ffe14d; background: rgba(255,225,77,0.14); }
.chk input:checked + .box::after {
  content: ""; position: absolute; left: 3px; top: 0px;
  width: 4px; height: 8px; border: solid #ffe14d;
  border-width: 0 1.5px 1.5px 0; transform: rotate(42deg);
}
.chk input:focus-visible + .box { outline: 2px solid #38e1c9; outline-offset: 2px; }
.chk span.t {
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  letter-spacing: 0.08em; color: #8ea4c4;
}

/* Buttons */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  width: 100%; padding: 8px 11px; cursor: pointer;
  background: #0d1420; border: 1px solid #1a2740; color: #c7d5e8;
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  letter-spacing: 0.15em; text-transform: uppercase;
  transition: color 110ms linear, background-color 110ms linear, border-color 110ms linear;
}
.btn:hover { border-color: #24365a; background: #141d2e; }
.btn:disabled { color: #3c4d68; border-color: #131d30; background: #080c14; cursor: not-allowed; }
.btn.go { color: #ffe14d; border-color: rgba(255,225,77,0.45); background: rgba(255,225,77,0.08); }
.btn.go:hover:not(:disabled) { background: rgba(255,225,77,0.17); }
.btn.danger { color: #ff5468; border-color: rgba(255,84,104,0.4); background: rgba(255,84,104,0.07); }
.btn.danger:hover:not(:disabled) { background: rgba(255,84,104,0.15); }
.btn.slim { padding: 5px 9px; font-size: 9px; letter-spacing: 0.12em; }

/* ── Drop target ──────────────────────────────────────────────────────────── */

.drop {
  border: 1px dashed #24365a; background: #05070c; padding: 16px 12px;
  text-align: center; cursor: pointer; display: grid; gap: 5px;
  transition: border-color 110ms linear, background-color 110ms linear;
}
.drop:hover, .drop.over { border-color: #ffe14d; background: rgba(255,225,77,0.05); }
.drop .big {
  font-family: "JetBrains Mono", monospace; font-size: 10.5px;
  letter-spacing: 0.13em; text-transform: uppercase; color: #c7d5e8;
}
.drop .sm { font-family: "JetBrains Mono", monospace; font-size: 9px; color: #3c4d68; letter-spacing: 0.06em; }
.drop input[type="file"] { display: none; }

/* ── Stage: canvases ──────────────────────────────────────────────────────── */

.stage-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 7px 11px; background: #0d1420; border-bottom: 1px solid #1a2740;
  flex-wrap: wrap;
}
.stage-bar .seg { flex: 0 0 auto; }
.stage-bar .seg button { padding: 5px 11px; }
.stage-meta {
  font-family: "JetBrains Mono", monospace; font-size: 9.5px;
  letter-spacing: 0.1em; color: #3c4d68; display: flex; gap: 12px; flex-wrap: wrap;
}
.stage-meta b { color: #8ea4c4; font-weight: 500; }

.canvas-hold { position: relative; background: #05070c; min-height: 340px; flex: 1 1 auto; }
@media (min-width: 1180px) { .canvas-hold { min-height: 460px; } }
.canvas-hold canvas { display: block; width: 100%; height: 100%; position: absolute; inset: 0; }

.canvas-empty {
  position: absolute; inset: 0; display: grid; place-content: center;
  gap: 8px; text-align: center; padding: 24px; pointer-events: none;
}
.canvas-empty .t {
  font-family: "JetBrains Mono", monospace; font-size: 11px;
  letter-spacing: 0.16em; text-transform: uppercase; color: #24365a;
}
.canvas-empty .s { font-size: 12px; color: #3c4d68; max-width: 44ch; line-height: 1.7; }

/* Overlay legend, drawn in DOM rather than on the canvas so it stays selectable
 * and does not need re-rasterising every frame. */
.legend {
  position: absolute; left: 11px; top: 11px; display: grid; gap: 3px;
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.08em; color: #5a7090; pointer-events: none;
}
.legend div { display: flex; align-items: center; gap: 6px; }
.legend i { width: 9px; height: 2px; flex: none; }

/* Swatch colours are CLASSES, not style attributes. `style-src 'self'` blocks a
 * style attribute exactly as it blocks a <style> block — CSP makes no
 * distinction — so an inline `style="background:#38e1c9"` here renders as an
 * invisible zero-colour bar and logs a violation. Every other page on this site
 * is inline-style-free for the same reason; this is the one place a legend made
 * it tempting. */
.legend i.sw-ref  { background: #38e1c9; }
.legend i.sw-ens  { background: #ffe14d; }
.legend i.sw-plan { background: #5a7090; }
.legend i.sw-sig  { background: #38e1c9; opacity: 0.5; }

/* ── Transport ────────────────────────────────────────────────────────────── */

.transport { background: #0a0f18; border-top: 1px solid #1a2740; }
.tl-hold { position: relative; height: 92px; background: #05070c; cursor: ew-resize; }
.tl-hold canvas { display: block; width: 100%; height: 100%; position: absolute; inset: 0; }

.tp-row {
  display: flex; align-items: center; gap: 12px; padding: 9px 11px; flex-wrap: wrap;
  border-top: 1px solid #1a2740;
}
.tp-keys { display: flex; gap: 1px; background: #1a2740; border: 1px solid #1a2740; flex: none; }
.tp-keys button {
  background: #0d1420; border: none; color: #8ea4c4; cursor: pointer;
  width: 34px; height: 28px; display: grid; place-content: center;
  transition: color 110ms linear, background-color 110ms linear;
}
.tp-keys button:hover { background: #141d2e; color: #c7d5e8; }
.tp-keys button:disabled { color: #24365a; cursor: not-allowed; background: #080c14; }
.tp-keys button.play[data-on="1"] { color: #ffe14d; background: rgba(255,225,77,0.12); }
.tp-keys svg { width: 12px; height: 12px; fill: currentColor; }

.tp-clock {
  font-family: "JetBrains Mono", monospace; font-variant-numeric: tabular-nums;
  display: flex; align-items: baseline; gap: 9px; flex: none;
}
.tp-clock .d { font-size: 17px; color: #c7d5e8; letter-spacing: 0.02em; }
.tp-clock .u { font-size: 9px; color: #3c4d68; letter-spacing: 0.14em; text-transform: uppercase; }

.tp-speed { display: flex; align-items: center; gap: 10px; flex: 1 1 260px; min-width: 200px; }
.tp-speed .cap {
  font-family: "JetBrains Mono", monospace; font-size: 9px; letter-spacing: 0.13em;
  text-transform: uppercase; color: #5a7090; flex: none;
}
.tp-speed output {
  font-family: "JetBrains Mono", monospace; font-size: 10px; color: #ffe14d;
  letter-spacing: 0.06em; flex: none; min-width: 118px; text-align: right;
  font-variant-numeric: tabular-nums;
}

input[type="range"] {
  -webkit-appearance: none; appearance: none; width: 100%; min-width: 0;
  background: transparent; height: 16px; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 2px; background: #1a2740;
}
input[type="range"]::-moz-range-track { height: 2px; background: #1a2740; }
input[type="range"]::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none; margin-top: -5px;
  width: 4px; height: 12px; background: #ffe14d; border: none; border-radius: 0;
}
input[type="range"]::-moz-range-thumb {
  width: 4px; height: 12px; background: #ffe14d; border: none; border-radius: 0;
}
input[type="range"]:disabled::-webkit-slider-thumb { background: #24365a; }
input[type="range"]:disabled::-moz-range-thumb { background: #24365a; }

.tp-presets { display: flex; gap: 1px; background: #1a2740; border: 1px solid #1a2740; flex-wrap: wrap; }
.tp-presets button {
  background: #0d1420; border: none; color: #5a7090; cursor: pointer;
  padding: 5px 8px; font-family: "JetBrains Mono", monospace;
  font-size: 9px; letter-spacing: 0.08em; white-space: nowrap;
}
.tp-presets button:hover { color: #c7d5e8; background: #141d2e; }
.tp-presets button[aria-pressed="true"] { color: #ffe14d; background: rgba(255,225,77,0.12); }

/* ── Readouts ─────────────────────────────────────────────────────────────── */

.kvs {
  display: grid; grid-template-columns: auto 1fr; gap: 4px 10px;
  font-family: "JetBrains Mono", monospace; font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}
.kvs dt { color: #5a7090; white-space: nowrap; letter-spacing: 0.04em; }
.kvs dd { color: #c7d5e8; text-align: right; overflow-wrap: anywhere; }
.kvs dd.dim { color: #3c4d68; }
.kvs dd.go { color: #3fe08b; }
.kvs dd.warn { color: #ffb547; }
.kvs dd.bad { color: #ff5468; }
.kvs dd.lit { color: #ffe14d; }

/* Vector triples read as a unit, so they get a bordered strip rather than three
 * separate rows that the eye has to reassemble. */
.vec {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: #1a2740; border: 1px solid #1a2740;
}
.vec div { background: #05070c; padding: 5px 6px; display: grid; gap: 2px; text-align: center; }
.vec .k {
  font-family: "JetBrains Mono", monospace; font-size: 8px;
  letter-spacing: 0.12em; color: #3c4d68; text-transform: uppercase;
}
.vec .v {
  font-family: "JetBrains Mono", monospace; font-size: 11px;
  color: #c7d5e8; font-variant-numeric: tabular-nums;
}

/* ── Event log ────────────────────────────────────────────────────────────── */

.log {
  max-height: 208px; overflow-y: auto; background: #05070c;
  border: 1px solid #1a2740; font-family: "JetBrains Mono", monospace;
  font-size: 10px; line-height: 1.6;
}
.log div { padding: 4px 8px; border-bottom: 1px solid rgba(26,39,64,0.6); display: flex; gap: 8px; }
.log div:last-child { border-bottom: none; }
.log .ts { color: #24365a; flex: none; font-variant-numeric: tabular-nums; }
.log .m { color: #8ea4c4; min-width: 0; overflow-wrap: anywhere; }
.log .m.ok { color: #3fe08b; }
.log .m.warn { color: #ffb547; }
.log .m.bad { color: #ff5468; }
.log .m.lit { color: #ffe14d; }
.log .empty { color: #24365a; }

/* ── Progress ─────────────────────────────────────────────────────────────── */

.prog { display: grid; gap: 5px; }
.prog .bar { height: 3px; background: #1a2740; position: relative; overflow: hidden; }
.prog .bar i { position: absolute; inset: 0 auto 0 0; background: #ffe14d; width: 0%; }
.prog .txt {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.1em; color: #5a7090; display: flex; justify-content: space-between; gap: 8px;
}
.prog[hidden] { display: none; }

/* ── Status pill ──────────────────────────────────────────────────────────── */

.pill {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.13em; text-transform: uppercase;
  border: 1px solid #1a2740; padding: 2px 7px; color: #5a7090; white-space: nowrap;
}
.pill.lit { color: #ffe14d; border-color: rgba(255,225,77,0.4); }
.pill.go { color: #3fe08b; border-color: rgba(63,224,139,0.4); }
.pill.warn { color: #ffb547; border-color: rgba(255,181,71,0.4); }
.pill.bad { color: #ff5468; border-color: rgba(255,84,104,0.4); }

/* ── Plan console ─────────────────────────────────────────────────────────
 *
 * Full width and below the instrument panels on purpose: a plan is read
 * top-to-bottom like a document, and while it runs you want to see the panels
 * above it moving. Editor on the left, transcript on the right, so the thing
 * you wrote and the thing it did sit side by side.
 */
.plan { margin-top: 22px; border: 1px solid #1a2740; background: #0a0f18; }

.plan-bar {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 11px; background: #0d1420; border-bottom: 1px solid #1a2740;
}
.plan-bar h2 {
  font-family: "JetBrains Mono", monospace; font-size: 9.5px;
  letter-spacing: 0.17em; text-transform: uppercase; color: #5a7090; font-weight: 500;
}
.plan-note {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.06em; color: #3c4d68;
}
.plan-actions { margin-left: auto; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.plan-actions .btn { width: auto; }
.plan-actions select { width: auto; min-width: 170px; }
.kbd { color: #3c4d68; letter-spacing: 0.04em; }
.btn.slim.go { color: #ffe14d; border-color: rgba(255,225,77,0.45); background: rgba(255,225,77,0.08); }
.btn.slim.go:hover:not(:disabled) { background: rgba(255,225,77,0.17); }

.plan-grid { display: grid; grid-template-columns: 1fr; gap: 1px; background: #1a2740; }
@media (min-width: 1000px) { .plan-grid { grid-template-columns: 1fr 1fr; } }

/* The gutter is a sibling of the textarea rather than a background image, so an
 * error can colour one specific line number. Both scroll together. */
.plan-edit { display: flex; background: #05070c; min-height: 340px; }
.plan-gutter {
  flex: none; width: 42px; overflow: hidden; padding: 10px 8px 10px 0;
  text-align: right; user-select: none;
  font-family: "JetBrains Mono", monospace; font-size: 12px; line-height: 1.65;
  color: #24365a; background: #080c14; border-right: 1px solid #1a2740;
}
.plan-gutter .err { color: #ff5468; font-weight: 700; }

.plan-edit textarea {
  flex: 1 1 auto; min-width: 0; resize: none;
  border: none; outline: none; background: transparent; color: #c7d5e8;
  padding: 10px 12px;
  font-family: "JetBrains Mono", monospace; font-size: 12px; line-height: 1.65;
  tab-size: 4;
}
.plan-edit textarea:focus { outline: none; }
.plan-edit:focus-within { outline: 1px solid rgba(255,225,77,0.35); outline-offset: -1px; }

.plan-out {
  background: #05070c; padding: 10px 12px; overflow: auto;
  min-height: 340px; max-height: 620px;
  font-family: "JetBrains Mono", monospace; font-size: 11px; line-height: 1.6;
  color: #8ea4c4;
}

/* ── The leftover strip in the output column ───────────────────────────────
 *
 * .plan-out is capped at 620px and the editor beside it runs to 792px, so the
 * grid row is 792 and the output column had 172px spare showing the grid's own
 * #1a2740 -- the same pale slab as the empty cells elsewhere on the site.
 *
 * THE PICTURE GOES IN THAT STRIP, NOT IN .plan-out. The first attempt put it
 * behind the output panel itself, which is where the run log is written; a
 * photograph belongs nowhere near it. Wrapping the panel in a column and
 * giving the picture the remainder keeps them completely separate: the log
 * never overlaps the picture, and the picture never moves when the log fills.
 *
 * Only from 1000px, the width at which the console becomes two columns. Below
 * that the output sits under the editor, the row has no remainder, and the
 * media query is never matched so the file is never fetched.
 *
 * The crop is set high in the frame on purpose. The source is 2.2:1 and this
 * strip is about 4.3:1, so only a horizontal slice survives; 25% puts that
 * slice across the scattered satellites and the top of the Earth's limb rather
 * than on empty sky above or ocean below. */
.plan-side {
  display: flex;
  flex-direction: column;
  min-height: 0;
  background: #05070c;
}
.plan-shot { display: none; }
@media (min-width: 1000px) {
  /* THE BASIS HAS TO BE STATED, and it must track the max-height above.
     As a grid item .plan-out stretched to the row and was capped at 620 by
     max-height. As a flex item it sizes from content instead, which is nothing
     -- so it collapsed to its 340px minimum and handed the picture 452px, more
     than twice the strip that was actually empty. Starting it at 620 and
     letting only the picture grow reproduces the original split exactly: the
     log panel is the size it always was, and the picture gets the remainder
     and nothing more.

     Sized here rather than globally on purpose. Below this width the console
     is a single column, .plan-shot is display:none, and .plan-out must go back
     to sizing from its own 340px minimum. */
  .plan-out { flex: 0 1 620px; }
  .plan-shot {
    display: block;
    flex: 1 1 auto;
    min-height: 0;
    border-top: 1px solid #1a2740;
    background-image:
      linear-gradient(rgba(5,7,12,var(--shot-dim)), rgba(5,7,12,var(--shot-dim))),
      url('../sandbox-1.jpg');
    background-size: cover;
    background-position: center 25%;
    background-repeat: no-repeat;
  }
}

.pl-line { white-space: pre-wrap; overflow-wrap: anywhere; min-height: 1.6em; }
.pl-line.info { color: #5a7090; }
.pl-line.pass { color: #3fe08b; }
.pl-line.fail { color: #ff5468; }
.pl-line.err  { color: #ff5468; background: rgba(255,84,104,0.08); }

.plan-status {
  padding: 7px 11px; border-top: 1px solid #1a2740; background: #0d1420;
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  letter-spacing: 0.12em; text-transform: uppercase; color: #5a7090;
}
.plan-status.run  { color: #ffe14d; }
.plan-status.good { color: #3fe08b; }
.plan-status.bad  { color: #ff5468; }

/* ── Notes under the console ──────────────────────────────────────────────── */

.sbx-notes {
  margin-top: 22px; display: grid; gap: 1px; background: #1a2740;
  border: 1px solid #1a2740; grid-template-columns: 1fr;
}
@media (min-width: 900px) { .sbx-notes { grid-template-columns: 1fr 1fr; } }

/* ── The eleventh note leaves a twelfth cell ───────────────────────────────
 *
 * Two columns and an odd number of notes, so the last row is one note and one
 * hole -- and a hole in this grid is not empty, it is the container's #1a2740
 * showing through the 1px gap, which is the same pale slab the home page had
 * beside its short rows.
 *
 * DISPLAY: NONE IS THE DEFAULT and the media query turns it on, not the other
 * way round. Below 900px the grid is a single column and there is no hole at
 * all; a picture left in the flow there would not be filling anything, it
 * would be an extra full-width photograph wedged between the notes and the
 * footer on a phone. */
.sbx-shot { display: none; }
@media (min-width: 900px) {
  .sbx-shot {
    display: block;
    background-color: #0a0f18;
    background-image:
      linear-gradient(rgba(5,7,12,var(--shot-dim)), rgba(5,7,12,var(--shot-dim))),
      url('../sandbox-2.jpg');
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    /* The cell takes its height from the note beside it and must contribute
       none of its own, or the picture's aspect ratio would set the row height
       and drag that note taller than its text needs. cover then crops to
       whatever height the row settles at. */
    min-height: 0;
  }
}
.sbx-note { background: #0a0f18; padding: 14px 15px 16px; }
.sbx-note h3 {
  font-family: "JetBrains Mono", monospace; font-size: 9.5px;
  letter-spacing: 0.17em; text-transform: uppercase; color: #ffe14d; margin-bottom: 8px;
}
.sbx-note p, .sbx-note li { font-size: 12px; line-height: 1.72; color: #5a7090; }
.sbx-note p + p { margin-top: 9px; }
.sbx-note b { color: #c7d5e8; font-weight: 600; }
.sbx-note ul { display: grid; gap: 6px; margin-top: 8px; }
.sbx-note li { list-style: none; display: flex; gap: 9px; align-items: baseline; }
.sbx-note li::before {
  content: "--"; font-family: "JetBrains Mono", monospace; font-size: 9px;
  color: #24365a; flex: none;
}
.sbx-note code {
  font-family: "JetBrains Mono", monospace; font-size: 11px; color: #38e1c9;
  overflow-wrap: anywhere;
}
.sbx-note pre {
  margin-top: 9px; overflow-x: auto; border: 1px solid #1a2740; background: #05070c;
  padding: 10px 11px; font-family: "JetBrains Mono", monospace;
  font-size: 11px; line-height: 1.65; color: #8ea4c4;
}
.sbx-note pre .c { color: #3c4d68; }
.sbx-note a { color: #38e1c9; text-decoration: underline;
              text-decoration-color: rgba(56,225,201,0.4); text-underline-offset: 2px; }
.sbx-note a:hover { text-decoration-color: #38e1c9; }

/* ── Operation manual ─────────────────────────────────────────────────────
 * Reference-document furniture, used only by sandbox-manual.html. It lives in
 * this stylesheet rather than site.css because it is part of the sandbox's
 * documentation, and because site.css is loaded by eleven pages that will never
 * use any of it.
 */
.man-toc {
  margin: 18px 0 26px; border: 1px solid #1a2740; background: #0a0f18;
  display: grid; gap: 1px; grid-template-columns: 1fr;
}
@media (min-width: 700px) { .man-toc { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1100px) { .man-toc { grid-template-columns: repeat(3, 1fr); } }
.man-toc a {
  background: #0a0f18; padding: 9px 12px; text-decoration: none;
  display: flex; align-items: baseline; gap: 9px;
  font-size: 12.5px; color: #8ea4c4;
}
.man-toc a:hover { background: #0d1420; color: #c7d5e8; }
.man-toc a .n {
  font-family: "JetBrains Mono", monospace; font-size: 10px;
  color: #ffe14d; flex: none; min-width: 2ch;
}

.man-sec { margin-top: 34px; scroll-margin-top: 12px; }
.man-sec > h2 {
  font-size: 19px; font-weight: 700; color: #c7d5e8; letter-spacing: -0.01em;
  padding-bottom: 8px; border-bottom: 1px solid #1a2740;
  display: flex; align-items: baseline; gap: 10px;
}
.man-sec > h2 .n {
  font-family: "JetBrains Mono", monospace; font-size: 12px; color: #ffe14d; flex: none;
}
.man-sec > h3 {
  margin-top: 20px; font-size: 13.5px; font-weight: 700; color: #c7d5e8;
}
.man-sec p, .man-sec li { font-size: 13px; line-height: 1.75; color: #8ea4c4; }
.man-sec p { margin-top: 10px; max-width: 86ch; }
.man-sec b { color: #c7d5e8; }
.man-sec code {
  font-family: "JetBrains Mono", monospace; font-size: 11.5px;
  color: #38e1c9; overflow-wrap: anywhere;
}
.man-sec a { color: #38e1c9; text-decoration: underline;
             text-decoration-color: rgba(56,225,201,0.4); text-underline-offset: 2px; }
.man-sec ul { margin-top: 10px; display: grid; gap: 7px; }
.man-sec ul li { list-style: none; display: flex; gap: 10px; align-items: baseline; }
.man-sec ul li::before {
  content: "--"; font-family: "JetBrains Mono", monospace; font-size: 10px;
  color: #24365a; flex: none;
}
.man-sec pre {
  margin-top: 12px; overflow-x: auto; border: 1px solid #1a2740; background: #05070c;
  padding: 12px 14px; font-family: "JetBrains Mono", monospace;
  font-size: 11.5px; line-height: 1.7; color: #c7d5e8;
}
.man-sec pre .c { color: #3c4d68; }
.man-sec pre .k { color: #ffe14d; }
.man-sec pre .s { color: #9db4d4; }

/* Numbered procedure. The numbers are load-bearing here — a procedure IS a
 * sequence, and step 4 before step 3 is a different test. */
.man-steps { margin-top: 14px; counter-reset: step; display: grid; gap: 1px;
             background: #1a2740; border: 1px solid #1a2740; }
.man-steps li {
  list-style: none; background: #0a0f18; padding: 11px 13px 12px 46px;
  position: relative; font-size: 13px; line-height: 1.7; color: #8ea4c4;
}
.man-steps li::before {
  counter-increment: step; content: counter(step);
  position: absolute; left: 13px; top: 11px;
  font-family: "JetBrains Mono", monospace; font-size: 11px; color: #ffe14d;
}
.man-steps li b { color: #c7d5e8; }
.man-steps li code { color: #38e1c9; }

/* Callout for the things that will otherwise cost someone an afternoon. */
.man-note {
  margin-top: 14px; border-left: 2px solid #ffe14d;
  background: rgba(255,225,77,0.05); padding: 10px 14px;
}
.man-note.warn { border-left-color: #ff5468; background: rgba(255,84,104,0.05); }
.man-note p { margin-top: 0; font-size: 12.5px; }
.man-note p + p { margin-top: 8px; }
.man-note .lbl {
  font-family: "JetBrains Mono", monospace; font-size: 9px;
  letter-spacing: 0.16em; text-transform: uppercase; color: #ffe14d;
  display: block; margin-bottom: 5px;
}
.man-note.warn .lbl { color: #ff5468; }

/* A no-script page that renders as a bare wall of controls which do nothing is
 * worse than one that says so. */
.sbx-noscript {
  margin: 16px 0; border: 1px solid rgba(255,84,104,0.42);
  background: rgba(255,84,104,0.06); padding: 12px 14px;
  font-size: 12px; line-height: 1.7; color: #8ea4c4;
}

/* Under 1180px the three rails stack, and a 460px canvas plus two full rails is
 * a very long page. Capping the rails' height keeps the stage reachable. */
@media (max-width: 1179px) {
  .sbx-console { grid-template-columns: 1fr; }
  .sbx-rail { max-height: none; }
  .canvas-hold { min-height: 320px; }
}

/* ── Phone overflow: grid items will not shrink on their own ───────────────
 *
 * A grid item's default min-width is auto, not zero, so a track cannot shrink
 * below the widest unbreakable thing inside it. Measured at 390px: .sbx-notes
 * resolved its single column to 463px inside a 354px box (109px of page
 * overflow) and the manual's lists to 489px inside 356px (133px), because both
 * carry code fragments and CSP directives that do not wrap.
 *
 * The page then scrolled sideways and text ran outside its panel. min-width: 0
 * lets the track shrink to the box; overflow-wrap gives the long tokens
 * somewhere to break so they fold instead of scrolling, which is the better
 * outcome for a directive read as prose rather than copied as a command.
 * ------------------------------------------------------------------------- */
.sbx-notes > *,
.sbx-head > *,
.man-sec ul > * { min-width: 0; }

/* The list items are themselves flex rows (a marker, then the text), and a
   flex item has the same min-width:auto floor as a grid one. At 320px that put
   the row 6px outside a 254px list. Reached separately from the rule above
   because these are grandchildren, not children. */
.sbx-note li, .man-sec li,
.sbx-note li > *, .man-sec li > * { min-width: 0; }

.sbx-note code, .man-sec code, .sbx-note li, .man-sec li { overflow-wrap: anywhere; }
