/* ====================================================================
   Flowseg - Atmosphere Control System - UI kit (single light theme)
   --------------------------------------------------------------------
   This stylesheet is organised as a small DESIGN SYSTEM so the remaining
   tabs are built from a handful of reusable parts, not new bespoke CSS:

     1. TOKENS      - every colour/size lives in a :root variable. Never
                      hard-code a colour in a component; reference a token.
     2. BASE        - reset + page background (the graph-paper grid).
     3. LAYOUT      - topbar, tabbar, tab panels, per-tab content frames.
     4. PRIMITIVES  - the reusable vocabulary. Build new tabs from THESE:
                        .panel / .panel-head / .panel-foot   surfaces
                        .data-table (+ .setup-table grid)    tables
                        .field                               inputs
                        .btn / .icon-btn                     buttons
                        .badge (+ .badge--good/warn/bad/info/neutral)  pills
                        .tag  .num  .note  .dd*  status dot
     5. COMPONENTS  - tab-specific glue (sources, checkpoints, instruments,
                      logs, direct control, pascal…).
                      These mostly just COMPOSE primitives.

   Conventions for new work, see docs/ui_kit.md:
     - One flat solid fill per table item (no gradients, no transparency
       over the paper). Accents are the only tints.
     - Accent = a triplet: solid text colour + `-line` border + `-fill`
       background. Pills/buttons/focus all draw from the same triplets.
     - Light theme only - there is no dark variant. Don't reintroduce
       white-on-dark hovers (rgba(255,255,255,…)); use --fill* tokens.
   ==================================================================== */

:root {
  /* ---- surfaces (paper) ---- */
  --bg:        #f7f9fc;   /* page - very pale; panels lift off it via shadow, not contrast */
  --bg-glow:   #ffffff;   /* soft top glow in the page radial */
  --panel:     #f3f6fa;   /* recessed surface (editor row, fullscreen bg) */
  --panel-2:   #ffffff;   /* raised surface (popovers, fields-on-panel) */
  --topbar-bg: #ffffff;   /* app header */
  --tabbar-bg: rgba(255,255,255,0.5);   /* tab strip (lets the grid show faintly) */

  /* ---- hairlines & grid ---- */
  --line:      rgba(30, 41, 59, 0.12);   /* default border */
  --line-soft: rgba(30, 41, 59, 0.07);   /* faint inner separators */
  --grid:      rgba(30, 41, 59, 0.05);   /* graph-paper rule */

  /* Sparse "darker squares" scattered on the graph-paper grid. Generated (tools: python, seed 42) by rolling a 1% chance
     per cell over a 48x48-cell (1728px @ 36px) tile - 23 cells tinted at fill-opacity .025 (paler than the .05 grid lines).
     The large tile barely repeats on screen, so the scatter reads as random. Layered under the grid lines so the lines
     still draw on top. To regenerate: change seed/PROB and re-run the generator. (Next line is a single SVG data-URI -
     one unbreakable value, the one legitimate >240-col line in this file.) */
  --paper-cells: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' width='1728' height='1728'><g fill='%231e293b' fill-opacity='.025'><rect x='684' width='36' height='36'/><rect x='1008' y='72' width='36' height='36'/><rect x='1044' y='180' width='36' height='36'/><rect x='72' y='216' width='36' height='36'/><rect x='324' y='216' width='36' height='36'/><rect x='1152' y='252' width='36' height='36'/><rect x='360' y='288' width='36' height='36'/><rect x='1548' y='288' width='36' height='36'/><rect x='36' y='360' width='36' height='36'/><rect x='432' y='504' width='36' height='36'/><rect x='648' y='648' width='36' height='36'/><rect x='648' y='756' width='36' height='36'/><rect x='1044' y='828' width='36' height='36'/><rect x='576' y='864' width='36' height='36'/><rect x='792' y='864' width='36' height='36'/><rect x='1692' y='1008' width='36' height='36'/><rect x='1116' y='1332' width='36' height='36'/><rect x='432' y='1368' width='36' height='36'/><rect x='1476' y='1440' width='36' height='36'/><rect y='1584' width='36' height='36'/><rect x='288' y='1584' width='36' height='36'/><rect x='1476' y='1584' width='36' height='36'/><rect x='468' y='1656' width='36' height='36'/></g></svg>");

  /* ---- text ---- */
  --text:      #1c2531;   /* primary */
  --muted:     #5b6878;   /* secondary, still readable on a tinted band */
  --dim:       #97a3b3;   /* tertiary / hints (use only on white) */

  /* ---- accents (each: solid text/icon, -line border ~0.4, -fill bg ~0.10) ----
     The solid colour is darkened for contrast on white; the -line/-fill tints
     are mixed from the brighter source hue so a low-alpha wash stays lively. */
  --good: #189b6c;  --good-line: rgba(70,211,154,0.40);  --good-fill: rgba(70,211,154,0.10);  --good-fill-2: rgba(70,211,154,0.16);
  --warn: #b9740a;  --warn-line: rgba(240,160,42,0.40);  --warn-fill: rgba(240,160,42,0.08);  --warn-fill-2: rgba(240,160,42,0.16);
  --bad:  #d8394a;  --bad-line:  rgba(255,95,107,0.40);  --bad-fill:  rgba(255,95,107,0.12);  --bad-fill-2:  rgba(255,95,107,0.16);
  --info: #2b8fd6;  --info-line: rgba(63,182,255,0.40);  --info-fill: rgba(63,182,255,0.10);

  /* ---- neutral fills (tints of slate-blue; for chips, inert buttons, bars) ---- */
  --fill-soft:  rgba(150,170,200,0.05);   /* faintest (field rest, inert btn) */
  --fill:       rgba(150,170,200,0.07);   /* chips / .tag / .ch-id */
  --fill-2:     rgba(150,170,200,0.10);   /* hover-ish / progress tracks */
  --fill-hover: rgba(150,170,200,0.20);   /* button hover (clearly darker, reads over tinted panel-heads) */

  /* ---- focus (one ring everywhere a field/control is active) ---- */
  --focus-line: rgba(70,211,154,0.55);
  --focus-fill: rgba(70,211,154,0.05);

  /* ---- elevation ---- */
  --shadow-pop: 0 8px 24px rgba(30,41,59,0.16);   /* popovers (light theme) */

  /* ---- table bands (one flat fill per row type; darker = more structural) ----
     header + footer share one band; grouping > column-label > rows (white). */
  --tbl-head:     #dbe3ee;   /* .panel-head - title band */
  --tbl-group:    #e3e9f1;   /* .inst-header - grouping rows */
  --tbl-collabel: #edf1f6;   /* thead th + .panel-foot - column-label / footer band */
  --line-crisp:   rgba(30, 41, 59, 0.28);   /* thin crisp dark outline (table cards) */
  --tbl-row:      #ffffff;   /* td           - normal data rows */

  /* ---- type & metrics ---- */
  --font-ui:   "IBM Plex Sans", system-ui, sans-serif;
  --font-mono: "IBM Plex Mono", ui-monospace, monospace;
  --radius:    10px;   /* panels */
  --radius-sm: 6px;    /* fields, buttons */
  --radius-xs: 4px;    /* pills, chips */
}

/* ==================================================================== 2. BASE */
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { height: 100%; }

body {
  font-family: var(--font-ui);
  color: var(--text);
  background-color: var(--bg);
  background-image:
    linear-gradient(var(--grid) 1px, transparent 1px),
    linear-gradient(90deg, var(--grid) 1px, transparent 1px),
    var(--paper-cells),
    radial-gradient(ellipse 120% 80% at 50% -10%, var(--bg-glow) 0%, var(--bg) 60%);
  background-size: 36px 36px, 36px 36px, 1728px 1728px, 100% 100%;   /* grid · grid · tinted cells · glow */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  letter-spacing: 0.01em;
  -webkit-font-smoothing: antialiased;
}

/* ---- utilities (composable layout helpers - prefer these over inline style=) ---- */
.hidden { display: none !important; }                              /* shared visibility util */
.push-right { margin-left: auto; }                                 /* shove a control to the row's right edge */
.row { display: flex; align-items: center; gap: 14px; }            /* inline flex row (label + control + note) */

/* ==================================================================== 3. LAYOUT */

/* ---- topbar ---- */
.topbar {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 22px;
  border-bottom: 1px solid var(--line);
  background: var(--topbar-bg, #ffffff);
  flex-shrink: 0;
}
.brand { display: flex; align-items: center; gap: 12px; }
.mark {
  width: 20px; height: 20px;
  border: 1.5px solid var(--good); transform: rotate(45deg);
  position: relative; box-shadow: 0 0 14px rgba(70, 211, 154, 0.35); flex-shrink: 0;
}
.mark::after { content: ""; position: absolute; inset: 4px; background: var(--good); box-shadow: 0 0 10px var(--good); }
.wordmark { font-family: var(--font-mono); font-weight: 600; font-size: 16px; letter-spacing: 0.32em; padding-left: 0.32em; color: inherit; text-decoration: none; }
.wordmark:hover { color: var(--good); }
/* "mentation" tail: small, thin, lowered - FLOWSEG + tail reads "flow segmentation".
   No letter-spacing/margin here: it INHERITS .wordmark's computed 0.32em spacing so the
   gaps between its letters - and after "G" - match FLOWSEG exactly. */
.wordmark-tail { font-size: 0.6em; font-weight: 200; vertical-align: baseline; position: relative; bottom: -0.5em; }

.conn-wrap { display: flex; align-items: center; gap: 14px; }
.version {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.04em;
  color: var(--dim); text-decoration: none; padding: 2px 6px; border-radius: var(--radius-xs);
}
.version:hover { color: var(--text); background: var(--fill-2); }
.conn {
  display: flex; align-items: center; gap: 8px;
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--muted);
}
.conn.down .txt  { color: var(--bad); }

/* status dot - live/down indicator; reused anywhere a small pulsing state is needed */
.dot { width: 8px; height: 8px; border-radius: 50%; background: var(--good); box-shadow: 0 0 8px var(--good); animation: pulse 1.6s ease-in-out infinite; }
.conn.down .dot  { background: var(--bad); box-shadow: 0 0 8px var(--bad); animation: none; }
@keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.25; } }

/* ---- tabbar ---- */
.tabbar {
  display: flex; padding: 0 22px; overflow-x: auto;
  border-bottom: 1px solid var(--line); background: var(--tabbar-bg, rgba(255,255,255,0.5)); flex-shrink: 0;
}
.tab {
  font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--dim); background: none; border: none; border-bottom: 2px solid transparent;
  padding: 13px 16px 11px; cursor: pointer; white-space: nowrap; margin-bottom: -1px;
  transition: color 0.15s, border-color 0.15s;
}
.tab:hover  { color: var(--muted); }
.tab.active { color: var(--text); border-bottom-color: var(--good); }

/* ---- tab content + per-tab content frames ---- */
.tab-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-height: 0; }
.tab-panel { display: none; flex: 1; min-height: 0; }
.tab-panel.active { display: flex; flex-direction: column; }

/* A tab's body is this frame. .inner-layout = stacked panels (Setup, Direct control, debug). */
.inner-layout { flex: 1; display: flex; flex-direction: column; gap: 18px; padding: 18px 22px; overflow: auto; min-height: 0; }
.inner-layout > .panel { flex-shrink: 0; }
.inner-layout > .panel.grow { flex: 1; display: flex; flex-direction: column; min-height: 0; }

/* ==================================================================== 4. PRIMITIVES */

/* ---- surface: .panel + head/foot ----
   Every card is a .panel. Optional .panel-head (title bar) and .panel-foot (note
   strip) share one band. .panel.grow fills its column; .panel.foldable collapses. */
.panel {
  background: var(--tbl-row);
  border: 1px solid var(--line-crisp); border-radius: var(--radius);   /* one crisp outline for every card - no per-content guessing */
  box-shadow: 0 3px 6px rgba(30,41,59,0.08), 0 16px 44px rgba(30,41,59,0.14);   /* deep, wide soft cast - lifts each card off the pale page so stacked tables read as separate surfaces */
  overflow: hidden;                        /* clip head/foot/thead fills to the radius */
  position: relative;
  animation: rise 0.45s cubic-bezier(0.2,0.8,0.2,1) both;
}
@keyframes rise { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }
.panel-head { display: flex; align-items: center; gap: 14px; padding: 12px 16px; background: var(--tbl-head); border-bottom: 1px solid var(--line-soft); flex-shrink: 0; }
.panel-head h2 { font-family: var(--font-mono); font-size: 11px; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase; color: var(--muted); }
.panel-foot { padding: 10px 16px; background: var(--tbl-collabel); border-top: 1px solid var(--line-soft); }   /* footer band matches the column-label row */
.panel-foot .note { color: var(--muted); }   /* dark enough to read on the band */

/* note text - secondary copy under/within panels */
.note { font-size: 11px; color: var(--dim); line-height: 1.6; }
.note-inline { font-size: 11px; color: var(--muted); }   /* --muted, not --dim: these captions sit on the tinted panel-head band */

/* foldable panels - collapse to the header (caret added by initFold in app.js) */
.fold-caret { color: var(--muted); font-size: 10px; line-height: 1; transition: transform 0.15s, color 0.12s ease; flex-shrink: 0; user-select: none; }
.panel.foldable.collapsed .fold-caret { transform: rotate(-90deg); }
/* Hover cue for the clickable title bar: a subtle tint bump (fill-hover layered over
   the band via an inset shadow so it animates) + a brighter caret. Scoped to NOT fire
   when the pointer is over an inner control (button/input/select/link) - those don't
   fold (see initFold's click guard), so the bar must not pretend they do. */
.panel.foldable > .panel-head { transition: box-shadow 0.12s ease; }
.panel.foldable > .panel-head:hover:not(:has(:is(button, input, select, a):hover)) { box-shadow: inset 0 0 0 999px var(--fill-hover); }
.panel.foldable > .panel-head:hover:not(:has(:is(button, input, select, a):hover)) .fold-caret { color: var(--text); }
.panel.foldable.collapsed > :not(.panel-head) { display: none; }
.panel.foldable.collapsed .panel-head { border-bottom: none; }
.panel.foldable.collapsed { flex: 0 0 auto; }   /* a collapsed grow/flex panel (Logs, Debug) shrinks to its header instead of stretching empty */
/* Setup panels: thick divider under the TITLE row too (matches the column-header
   divider) - title | thick | column headers | thick | rows. Expanded only. */
#tab-setup .panel.foldable:not(.collapsed) .panel-head { border-bottom: 2px solid var(--line); }

/* ---- table ----
   .data-table is the base. Add .setup-table for the shared fixed 5-column grid. */
.table-wrap { overflow: auto; }   /* both axes: vertical for long lists, horizontal for wide tables (e.g. the Data tab's run CSV) */
#runs-list-wrap tbody tr { cursor: pointer; }   /* Data tab: run rows are clickable (pair with the per-row radio) */
#runs-view-body > .note { display: block; padding: 14px 16px; }   /* breathing room for the viewer's placeholder / status notes */
.data-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.data-table th, .units-head > span, .dc-head > span {   /* shared column-header typography (tables + units panel + direct-control) */
  font-family: var(--font-mono); font-size: 10px; font-weight: 500; letter-spacing: 0.16em; text-transform: uppercase; color: var(--dim);
}
.data-table th { padding: 9px 16px; text-align: left; background: var(--tbl-collabel); border-bottom: 1px solid var(--line-soft); }
.data-table td { padding: 11px 16px; background: var(--tbl-row); border-bottom: 1px solid var(--line-soft); vertical-align: middle; }
.data-table tr:last-child td { border-bottom: none; }
/* grouping rows ("2Mix - Prototype", "Unknown connection points"): one flat band for
   ALL groups (real + simulated). The SIM badge alone marks simulated rows. */
.data-table tr.inst-header td { font-weight: 600; font-size: 12px; color: var(--text); background: var(--tbl-group); border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); }

/* Setup tables (Fluid sources, Checkpoints, Instruments & devices) - shared fixed grid.
   Convention is the RULE, not literal alignment: 25/25/20/20/10 columns; "tight" columns
   (10%) right-align, content columns are wider and left-aligned; leftover width goes to a
   trailing .setup-fill spacer. Need >5 columns? split a 20% slot with .setup-half (10%). */
.setup-table { table-layout: fixed; width: 100%; }
.setup-table td { padding: 7px 16px; }
.setup-table th, .setup-table td { overflow: hidden; text-overflow: ellipsis; text-align: left; }
.setup-table th { border-bottom: 2px solid var(--line); }   /* stronger divider under column headers */
.setup-table th:nth-child(1), .setup-table td:nth-child(1) { width: 25%; }
.setup-table th:nth-child(2), .setup-table td:nth-child(2) { width: 25%; }
.setup-table th:nth-child(3), .setup-table td:nth-child(3) { width: 20%; }
.setup-table th:nth-child(4), .setup-table td:nth-child(4) { width: 20%; }
.setup-table th:nth-child(5), .setup-table td:nth-child(5) { width: 10%; }
.setup-table th.setup-half, .setup-table td.setup-half { width: 10%; }   /* after the grid: wins ties */
.setup-table th.setup-fill, .setup-table td.setup-fill { width: auto; }

/* monospace numerals (readouts, tables of figures) */
.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }
.num small { color: var(--dim); margin-left: 3px; font-size: 11px; }

/* ---- field: text/number inputs ----
   .field is THE base - every input wears it in markup. Variants layer on top as modifiers:
   .src-in (transparent table-cell), .dd-search (dropdown search), .dc-sp (setpoint).
   Adding an input = `class="field …"` + an optional modifier; NEVER a new base selector.
   One focus ring for all - dropdowns + the editor wrap borrow it in the focus rule. */
.field {
  font-family: var(--font-mono); font-size: 13px; color: var(--text);
  background: var(--fill-soft); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 6px 10px;
}
.field::placeholder { color: var(--dim); }
.field:focus, .dd.dd-open .dd-btn, .pascal-editor-wrap:focus-within {
  outline: none; border-color: var(--focus-line); background: var(--focus-fill);
}
/* table-cell input: transparent fill at rest but keeps a DIM edge (same faded outline as the
   .dd-btn dropdown), so an editable box is always discoverable; full box on hover */
.src-in { background: transparent; border-color: color-mix(in srgb, #fff 67%, var(--line)); width: 100%; }
.src-in:hover { background: var(--fill-soft); border-color: var(--line); }
/* liquid-flag checkbox: same quiet border as every other field (the native box is too strong) */
.src-liquid { appearance: none; -webkit-appearance: none; width: 13px; height: 13px; margin: 0 5px 0 0; vertical-align: -2px; border: 1px solid var(--line); border-radius: var(--radius-xs); background: var(--fill-soft); cursor: pointer; }
.src-liquid:checked { background: var(--info); border-color: var(--info); }
/* number setpoint (Direct control) - bigger, fixed width, no spinners */
.dc-sp { font-size: 14px; width: 200px; -moz-appearance: textfield; appearance: textfield; padding: 7px 11px; }
.dc-sp::-webkit-inner-spin-button, .dc-sp::-webkit-outer-spin-button { -webkit-appearance: none; }
#log-filter { width: 160px; }
#log-filter, .dd-search { font-size: 12px; }
.dd-search { width: 100%; margin-bottom: 4px; }

/* ---- buttons ----
   .btn = labelled action; .icon-btn = bare glyph (delete ✕ etc.). */
.btn {
  font-family: var(--font-mono); font-size: 12px; letter-spacing: 0.1em; text-transform: uppercase; color: var(--text);
  background: var(--fill-soft); border: 1px solid var(--line); padding: 9px 18px; border-radius: var(--radius-sm); cursor: pointer;
  display: flex; align-items: center; gap: 7px; transition: background 0.15s, border-color 0.15s, transform 0.08s;
}
.btn:hover  { background: var(--fill-hover); }
.btn:active { transform: scale(0.97); }
.btn--sm    { padding: 5px 12px; }   /* compact button (panel-head actions: Scan, Refresh, …) */
.btn.stop   { color: var(--bad);  border-color: var(--bad-line); }

.icon-btn {
  background: none; border: none; cursor: pointer; color: var(--dim);
  font-size: 13px; line-height: 1; padding: 4px 6px; border-radius: var(--radius-xs);
}
.icon-btn:hover { color: var(--bad); background: var(--bad-fill); }

/* ---- badge / pill ----
   ONE master only: .badge base + one accent (.badge--good/warn/bad/info/neutral).
   Markup - and JS that builds markup - MUST write `.badge .badge--x`; never invent a
   bespoke pill class (see the REUSE-OR-DIE rule in docs/ui_kit.md). .tag is the single
   endorsed variant: an inline chip (neutral fill + right margin). */
.badge, .tag {
  display: inline-block; white-space: nowrap;
  font-family: var(--font-mono); font-size: 9px; font-weight: 500; letter-spacing: 0.14em; text-transform: uppercase;
  padding: 2px 8px; border-radius: var(--radius-xs); border: 1px solid var(--line); color: var(--muted);
}
/* accent groups (text + line + fill from one triplet) */
.badge--good    { color: var(--good); border-color: var(--good-line); background: var(--good-fill); }
.badge--warn    { color: var(--warn); border-color: var(--warn-line); background: var(--warn-fill); }
.badge--bad     { color: var(--bad);  border-color: var(--bad-line);  background: var(--bad-fill); }
.badge--info    { color: var(--info); border-color: var(--info-line); background: var(--info-fill); }
.badge--neutral, .tag { color: var(--muted); background: var(--fill); }
.tag { margin-right: 6px; }                 /* inline run of tags */

/* ---- chip: the cable/device id token ---- */
.ch-id { font-family: var(--font-mono); font-size: 12px; color: var(--muted); background: var(--fill); padding: 2px 7px; border-radius: var(--radius-xs); border: 1px solid var(--line); }

/* ---- two-line dropdown (dropdown.js) ----
   Trigger shows the selected TITLE; the portalled .dd-panel lists title + subtitle.
   Placeholder (nothing selected) wears the warn accent to flag an empty required field. */
.dd { position: relative; display: inline-block; }
.dd-btn {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; cursor: pointer;
  font-family: var(--font-mono); font-size: 12px; color: var(--text);
  /* resting state = a very pale hover: 33% of the way from white toward the hover colours (border + background) */
  background: color-mix(in srgb, #fff 67%, var(--fill-soft)); border: 1px solid color-mix(in srgb, #fff 67%, var(--line)); border-radius: var(--radius-sm); padding: 5px 9px;
}
.dd-btn:hover { background: var(--fill-soft); border-color: var(--line); }
.dd-btn:has(.dd-ph) { background: var(--warn-fill); border-color: var(--warn-line); }
.dd-btn:has(.dd-ph):hover { background: var(--warn-fill-2); }
.dd-btn:has(.dd-ph) .dd-caret { color: var(--warn); }
.dd-label { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd-label.dd-ph { color: var(--warn); }
.dd-caret { color: var(--dim); font-size: 10px; flex-shrink: 0; }
.dd-panel { position: fixed; z-index: 1000; background: var(--panel-2); border: 1px solid var(--line); border-radius: 8px; padding: 4px; box-shadow: var(--shadow-pop); }
.dd-list { max-height: 308px; overflow-y: auto; }
.dd-empty { padding: 8px 9px; font-family: var(--font-mono); font-size: 12px; color: var(--dim); }
.dd-item { display: flex; flex-direction: column; gap: 1px; padding: 6px 9px; border-radius: 5px; cursor: pointer; }
.dd-item:hover { background: var(--fill-2); }
.dd-item.selected { background: var(--good-fill); }
.dd-item.disabled { cursor: not-allowed; opacity: 0.4; }
.dd-item.disabled:hover { background: none; }
.dd-item-title { font-family: var(--font-mono); font-size: 12px; color: var(--text); }
.dd-item-sub   { font-family: var(--font-mono); font-size: 10px; color: var(--dim); }

/* ---- Modal dialog + copy-box (modal.js) - reusable blocking message ----
   ONE overlay, reused for every dialog (see modal.js). The card carries a left accent stripe
   whose colour comes from --accent; the severity modifiers just repoint --accent at a token, so
   the dialog speaks the same colour language as .badge / the status pills. Internals are scoped
   element selectors (header / footer / .modal-body) rather than extra classes, to keep the master
   count flat. Assemble dialog bodies from the normal kit classes + .copybox; never hand-roll an
   overlay elsewhere. */
.modal { position: fixed; inset: 0; z-index: 1100; display: flex; align-items: center; justify-content: center; padding: 24px; background: color-mix(in srgb, var(--text) 38%, transparent); }
.modal[hidden] { display: none; }   /* override the flex display so the `hidden` attribute still hides it */
.modal-card {
  --accent: var(--info);            /* severity modifiers below repoint this */
  width: 100%; max-width: 30rem; background: var(--panel-2);
  border: 1px solid var(--line); border-left: 4px solid var(--accent);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-pop); overflow: hidden;
}
.modal-card--warn { --accent: var(--warn); }
.modal-card--bad  { --accent: var(--bad); }
.modal-card > header { padding: 14px 18px; font-weight: 600; font-size: 15px; color: var(--text); border-bottom: 1px solid var(--line-soft); }
.modal-body { padding: 14px 18px; color: var(--text); font-size: 13px; line-height: 1.5; }
.modal-body > * + * { margin-top: 10px; }
.modal-card > footer { padding: 12px 18px; display: flex; justify-content: flex-end; border-top: 1px solid var(--line-soft); }

/* Copy-box: a verbatim command in a recessed strip with a Copy button (modal.js copyBox()).
   Reusable anywhere a user must run an exact command. */
.copybox { display: flex; align-items: center; gap: 8px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 10px; }
.copybox code { flex: 1; font-family: var(--font-mono); font-size: 12px; color: var(--text); white-space: pre-wrap; word-break: break-all; }

/* ==================================================================== 5. COMPONENTS */

/* ---- Fluid sources table (sources.js) ---- */
.src-contents-cell .src-contents-inner { display: flex; align-items: center; gap: 10px; }   /* summary + edit toggle */
.comp-summary { font-family: var(--font-mono); font-size: 12px; color: var(--muted); flex: 1; }
.comp-edit { background: var(--warn-fill); border: 1px solid var(--warn-line); color: var(--warn); cursor: pointer; font-size: 11px; padding: 3px 9px; border-radius: 5px; flex-shrink: 0; }
.comp-edit:hover { background: var(--warn-fill-2); }
/* Clone sits inline beside the delete X in the actions cell (the .btn base is display:flex) */
.src-clone { display: inline-flex; vertical-align: middle; }
.src-del-cell .src-clone + .icon-btn { margin-left: 6px; }
/* a disabled species dropdown (static built-in source: view-only) */
.dd-btn:disabled { opacity: 0.65; cursor: default; pointer-events: none; }
.dd-btn:disabled .dd-caret { visibility: hidden; }
.src-del-cell { text-align: right; }   /* delete ✕ hugs the right edge */

/* expanded composition editor row - a recessed surface inside the table */
.src-edit-row td { background: var(--panel); padding: 12px 16px; }
.src-edit-row .src-in, .src-edit-row .comp-gas .dd-btn { background: var(--panel-2); border-color: var(--line); }
.comp-add { padding: 4px 11px; font-size: 12px; }
.comp-sum { font-family: var(--font-mono); font-size: 11px; color: var(--dim); }
.comp-sum.off, .comp-warn { color: var(--bad); }
.comp-warn { font-size: 11px; }
/* liquid-fluid property rows (sources.js buildLiquidPropRows): inputs reuse .src-in (100% of the
   Contents column) so they line up with the table columns; label right-aligns under "Source and notes". */
.src-prop-row td { padding-top: 5px; padding-bottom: 5px; vertical-align: middle; }
.src-prop-row td:nth-child(2) { text-align: right; }                                   /* labels under "Source and notes" */
.src-prop-row td:nth-child(4) { white-space: nowrap; }                                 /* unit (+ auto button) under "Flags" */
.src-prop-row input[type="number"] { -moz-appearance: textfield; appearance: textfield; }
.src-prop-row input[type="number"]::-webkit-outer-spin-button, .src-prop-row input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.src-prop-row .comp-gas { width: 100%; }                                               /* dissolved-gas dropdown fills "Contents" */
.src-prop-row .comp-amt { width: 84px; text-align: right; margin-right: 6px; }          /* amount + unit sit under "Flags" */
.src-prop-row .comp-unit { width: 64px; }
.liq-auto { display: inline-flex; vertical-align: middle; padding: 4px 9px; font-size: 11px; margin-left: 6px; }
.liq-auto[aria-pressed="true"] { background: var(--info); border-color: var(--info); color: #fff; }

/* ---- Checkpoints table (setup_checkpoints.js) ---- */
.checkpoints-table .cp-temp, .checkpoints-table .cp-press { width: 80px; }
.cp-cond-cell { white-space: nowrap; }
.cp-cond-cell .src-in { display: inline-block; vertical-align: middle; }
.cp-unit { margin-left: 6px; color: var(--dim); font-family: var(--font-mono); font-size: 12px; }

/* ---- Instruments & devices table (app.js) ---- */
.inst-header-row { display: flex; align-items: center; gap: 8px; }   /* label + badges + ✕ inside the group row */
.mfc-input.dd  { min-width: 200px; max-width: 260px; }
.mfc-output.dd { min-width: 180px; max-width: 240px; }
.mfc-nag { display: block; margin-top: 4px; font-family: var(--font-mono); font-size: 10px; color: var(--warn); }   /* corrosive-on-non-rated nag */
/* 2Wet (pump) input cell: two stacked inlets (Gas + Fluid), each a small label + control */
.pump-io { display: flex; align-items: center; gap: 7px; margin: 2px 0; }
.pump-io-lbl { width: 34px; flex: 0 0 34px; }                                          /* aligns the Gas / Fluid labels */
.pump-derived { font-family: var(--font-mono); font-size: 12px; color: var(--muted); }   /* read-only "from 2Mix" gas feed */
/* Plot-style column: a colour swatch + a compact (glyph-only) symbol dropdown. The cell
   stays a normal table-cell (no display:flex - that drops it out of the fixed-grid layout
   and collapses the column); children sit inline. */
.mfc-plot-cell { white-space: nowrap; }
.mfc-color { width: 26px; height: 22px; padding: 0; margin-right: 6px; vertical-align: middle; border: 1px solid var(--line); border-radius: var(--radius-sm); background: var(--panel-2); cursor: pointer; }
.mfc-symbol.dd { min-width: 44px; max-width: 64px; vertical-align: middle; }

/* ---- Live graph (livegraph.js) - a reusable foldable graph panel ---- */
.lg-controls { display: flex; align-items: center; gap: 8px; }
.lg-range.dd, .lg-xmode.dd { min-width: 132px; }
.lg-body { padding: 10px 16px 14px; }
.lg-devices { display: flex; flex-wrap: wrap; gap: 6px 16px; margin-bottom: 10px; }
.lg-dev { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; cursor: pointer; user-select: none; }
.lg-swatch { width: 12px; height: 12px; border-radius: var(--radius-xs); display: inline-block; flex-shrink: 0; }
.lg-canvas-wrap { width: 100%; height: 260px; }
.lg-canvas { width: 100%; height: 100%; display: block; }
.lg-unit { margin-left: 8px; }

/* "+ Add simulated" inline form */
.sim-form { display: flex; align-items: center; flex-wrap: wrap; gap: 12px; padding: 10px 2px; border-bottom: 1px solid var(--line); }
.sim-form[hidden] { display: none; }
.sim-field { display: flex; align-items: center; gap: 6px; font-family: var(--font-mono); font-size: 11px; color: var(--muted); }
.sim-field .sim-name { width: 180px; }
.sim-check { display: flex; align-items: center; gap: 5px; font-family: var(--font-mono); font-size: 11px; color: var(--muted); cursor: pointer; }
.sim-variant.dd { min-width: 180px; }

/* ---- Logs table (app.js) ---- */
.logs-panel .table-wrap { flex: 1; overflow-y: auto; }
.logs-table td { padding: 6px 16px; font-size: 12px; vertical-align: top; }
.log-time { font-family: var(--font-mono); color: var(--dim); white-space: nowrap; }
.log-unit { font-family: var(--font-mono); color: var(--muted); white-space: nowrap; }
/* category cell is a plain `.badge .badge--{good|warn|bad|neutral}` chosen in app.js renderLog */

/* ---- Segment program (app.js buildProgram) - uses .data-table + `.badge .badge--good/warn` per step kind ---- */

/* ---- Direct control (app.js buildDirectControl) ---- */
.dc-rows { display: flex; flex-direction: column; }
/* header + data rows share ONE column template so cells line up (like the setup tables) */
/* last column is a FIXED width (not auto) so the header's empty cell and the row's input+buttons
   produce the SAME track widths - an auto last column sizes to content and shifts the fr columns,
   misaligning headers from data. */
.dc-head, .dc-row { display: grid; grid-template-columns: minmax(190px,1.4fr) 1.1fr 0.9fr 0.9fr 0.9fr 360px; align-items: center; gap: 18px; }
.dc-head { padding: 9px 16px; background: var(--tbl-collabel); border-bottom: 2px solid var(--line); }
.dc-row { padding: 14px 16px; border-bottom: 1px solid var(--line-soft); }
.dc-row:last-child { border-bottom: none; }
.dc-info { display: flex; align-items: center; gap: 10px; }
.dc-name { font-weight: 500; font-size: 15px; }
.dc-cell { display: flex; flex-direction: column; justify-content: center; gap: 1px; font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 14px; }   /* gas (gas name dark, source paler below) + the three flow columns */
.dc-state.bad { color: var(--bad); border-color: var(--bad-line); background: var(--bad-fill); }   /* lost device - bad triplet, not hardcoded */
.dc-input { display: flex; align-items: center; gap: 8px; justify-self: end; }

/* ---- Units & reference conditions (setup_units.js) - single GUI display reference ---- */
.units-body { display: grid; grid-template-columns: 190px 1fr; gap: 12px 18px; align-items: center; padding: 16px; }
.units-field { display: contents; }                       /* label + control fall into the two columns */
/* header row, styled like the setup-table column headers: collabel band + 2px divider, labels aligned to the rows below */
.units-head { grid-column: 1 / -1; display: grid; grid-template-columns: 190px 1fr; column-gap: 18px; align-items: center; margin: -16px -16px 0; padding: 9px 16px; background: var(--tbl-collabel); border-bottom: 2px solid var(--line); }
.units-body.is-mass .units-ref { display: none; }         /* mass flow needs no reference condition */
.units-lbl { font-size: 13px; color: var(--text); }
.units-body .u-num { width: 120px; }
.units-body small { font-size: 11px; color: var(--muted); margin-left: 4px; }
.units-body .u-conv { font-size: 12px; color: var(--dim); margin-left: 10px; font-variant-numeric: tabular-nums; }  /* read-only converted-value readout (K/°F, atm/kPa/psi/Torr) */
.units-body .dd { min-width: 340px; }

/* ---- Debug logs ---- */
.debug-panel { flex: 1; display: flex; flex-direction: column; min-height: 300px; }
.debug-output-wrap { flex: 1; overflow-y: auto; padding: 14px 16px; min-height: 0; }
#debug-output { font-family: var(--font-mono); font-size: 11px; line-height: 1.7; color: var(--muted); white-space: pre-wrap; word-break: break-all; }

/* ---- Editor (pascal.js) - dev tool, multi-mode highlighting ----
   A coloured <pre> (.pascal-hl) sits behind a transparent-text <textarea>
   (.pascal-text); identical metrics + locked scroll keep colours under the caret.
   Default surface is the Turbo-Pascal homage (Borland blue, deliberate); for
   .html/.txt/plain the wrap gets .ed-light and switches to a light surface.
   Token classes: tp-* Pascal (dark), h-* HTML + x-* text (both light). */
/* per-root file pickers in the Editor head (src / manual / state) */
.ed-roots { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; }
.ed-root { display: flex; align-items: center; gap: 6px; }
.ed-root-cap { font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.04em; color: var(--muted); white-space: nowrap; }
.ed-root-dd.dd { min-width: 150px; max-width: 200px; }
.pascal-editor-wrap { position: relative; margin: 10px 0; display: flex; height: 60vh; resize: vertical; overflow: hidden; border: 1px solid var(--line); border-radius: var(--radius-sm); }
.pascal-code { position: relative; flex: 1 1 auto; min-width: 0; }
.pascal-hl, .pascal-text {
  margin: 0; padding: 12px 14px; box-sizing: border-box; width: 100%; height: 100%; border: none;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.5;
  letter-spacing: 0;   /* CRITICAL: pin both to 0 so the highlight + textarea stay in caret lockstep */
  tab-size: 2; white-space: pre; overflow: auto;
}
.pascal-gutter {
  flex: 0 0 auto; overflow: hidden; box-sizing: border-box; padding: 12px 8px 12px 10px;
  font-family: var(--font-mono); font-size: 12px; line-height: 1.5; text-align: right; white-space: pre; user-select: none; letter-spacing: 0;
  color: #6f7fd6; background: #00008c; border-right: 1px solid var(--line);   /* darker Borland blue */
}
.pascal-hl { position: absolute; inset: 0; z-index: 0; pointer-events: none; overflow: hidden; background: #0000a8; color: #ffff54; }
.pascal-hl code { font: inherit; }
.pascal-text { position: relative; z-index: 1; resize: none; background: transparent; color: transparent; caret-color: #ffffff; }
.pascal-text::selection { background: rgba(255,255,255,0.28); }
.pascal-text:focus { outline: none; }
.tp-kw  { color: #ffffff; font-weight: 600; }    /* reserved words */
.tp-com { color: #9aa0b0; font-style: italic; }  /* comments */
.tp-dir { color: #54ff54; }                      /* compiler directives */
.tp-str { color: #54ffff; }                      /* string literals */
.tp-num { color: #ff8af0; }                      /* numbers */
.pascal-error { color: var(--bad); }

/* Light surface for non-Pascal modes (HTML / txt / plain). Overrides the blue. */
.ed-light .pascal-gutter { color: #94a3b8; background: #f1f5f9; }
.ed-light .pascal-hl     { background: #ffffff; color: #1e293b; }
.ed-light .pascal-text   { caret-color: #1e293b; }
.ed-light .pascal-text::selection { background: rgba(30,41,59,0.16); }
/* HTML tokens (light) */
.h-tag  { color: #2563bd; font-weight: 600; }    /* tag name */
.h-ang  { color: #64748b; }                      /* < > / angle brackets */
.h-attr { color: #9333a8; }                      /* attribute name */
.h-val  { color: #16894a; }                      /* attribute value / string */
.h-com  { color: #94a3b8; font-style: italic; }  /* <!-- comment --> */
.h-ent  { color: #c2680c; }                      /* &entity; */
.h-doc  { color: #64748b; font-style: italic; }  /* <!doctype> / <?pi?> */
/* text tokens (light) */
.x-url  { color: #2563bd; text-decoration: underline; }   /* URLs */
.x-mark { color: #c2410c; font-weight: 600; }             /* TODO / NOTE / … markers */
.x-num  { color: #9333a8; }                               /* numbers */

#pascal-panel.fullscreen { position: fixed; inset: 0; z-index: 60; margin: 0; border-radius: 0; padding: 14px 18px; display: flex; flex-direction: column; background: var(--panel); }
#pascal-panel.fullscreen .pascal-editor-wrap { flex: 1 1 auto; height: auto; min-height: 0; resize: none; }

/* ── Segment program tab (segprog.js) ───────────────────────────────────
   Assembled from the kit (.panel/.data-table/.field.src-in/.btn/.badge/.note/.num);
   the only genuinely new parts are layout glue + ONE reusable component:
   .glyph-btn - a transition glyph (step/ramp, up/down/flat) usable anywhere a
   tiny direction indicator is wanted. */
.data-table tr.row-sel td { background: var(--focus-fill); }     /* shared selected/active row highlight: Segment program list + Data tab run list */
#sp-pick-panel .data-table { width: max-content; }               /* panel 1: hug content on the LEFT so the checkpoint dropdown + program list don't drift to mid-page */
.data-table.sp-grid { width: max-content; }                      /* hug content on the LEFT (don't stretch 100% → lone Seg 0 floating to the right edge); segments grow rightward */
.sp-grid th, .sp-grid td { padding: 7px 10px; }                  /* transposed grid (MFC rows × segment cols) → tighter than 16px */
/* edit fields (time/flow): 4 chars wide by default, grown to fit the typed value by JS (fitField, segprog.js) -
   monospace ⇒ Nch is exact. Spinners removed: they're Firefox-permanent and would eat the narrow box (appearance
   keyword for WebKit/Blink + Firefox; the ::spin pseudo is the older WebKit lever). 22px = padding(20)+border(2). */
.sp-grid .src-in, .sp-cellin .src-in { width: calc(4ch + 22px); -moz-appearance: textfield; appearance: textfield; }
.sp-grid .src-in::-webkit-outer-spin-button, .sp-grid .src-in::-webkit-inner-spin-button,
.sp-cellin .src-in::-webkit-outer-spin-button, .sp-cellin .src-in::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.sp-rowlbl { white-space: nowrap; font-weight: 500; background: var(--tbl-collabel); }   /* the left-hand row labels (Time, each MFC, …) */
.sp-grid .sp-seg-col { text-align: center; }                     /* centre each segment-column caption */
.sp-grid td:not(.sp-rowlbl):not(.sp-endsel) { text-align: center; }   /* centre every column Seg 0 → End After - NOT the row labels, NOT the keep/stop radios */
.sp-sel-col { width: 1%; white-space: nowrap; }
.sp-seg { border-left: 1px solid var(--line); }                  /* a divider on the left of every segment column */
.sp-end { border-right: 1px solid var(--line); }                 /* the End (after-program) column closes the grid on the right */
.sp-endsel { font-size: 12px; white-space: nowrap; }             /* the end-policy radios (keep flows / stop all) */
.sp-col-sel { background: var(--focus-fill); }                   /* the segment column shown in the matrix */
.sp-total-row td { background: var(--fill-soft); }               /* the computed Total row (read-only sum of inputs) */
.sp-total-row td.sp-col-sel { background: var(--focus-fill); }
.sp-cap { display: block; margin-top: 2px; color: var(--dim); font-size: 11px; }    /* block sub-note under a seg cell (neutral) - e.g. the time d/h/m hint */
.sp-twarn { color: var(--warn); }                                /* the time-ordering warning variant is the loud one */
.sp-timetool { color: var(--text); font-size: 12px; }            /* "at time … min" - black + button-sized, matching the panel-head buttons */
.sp-attime { width: 80px; }                                      /* custom-time input in the atmosphere-details header */
.sp-calc { table-layout: fixed; }                                /* matrix: equal-width columns regardless of content */
.sp-calc th, .sp-calc td { overflow: hidden; text-overflow: ellipsis; }

/* fraction cell stays a TABLE-CELL (columns keep aligning); an inner flex wrapper keeps the field
   and its transition glyph together on one line, centred in the cell */
.sp-cell { white-space: nowrap; }
.sp-cellin { display: flex; align-items: center; justify-content: center; gap: 6px; }
.sp-cellin .src-in { flex: 0 1 auto; }

/* .glyph-btn - bare button holding a small SVG transition glyph; reusable direction indicator */
.glyph-btn { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 22px;
  flex: 0 0 auto; padding: 0; vertical-align: middle; cursor: pointer; color: var(--muted);
  background: var(--fill-soft); border: 1px solid var(--line); border-radius: var(--radius-xs); }
.glyph-btn:hover { color: var(--text); background: var(--fill); border-color: var(--line-crisp); }
.glyph-svg { width: 16px; height: 12px; fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; }
