/* ==========================================================================
   Power RECON — Tethra Systems

   Design tokens carried over verbatim from LogIt Studio (apps/studio) so the
   Scenario Lab here reads as the same instrument the team already built —
   same panel/ink scale, same source colours, same load blue. Light and dark
   both defined; the source palette is the load-bearing part.
   ========================================================================== */

:root {
  /* --- LogIt Studio core (light) --- */
  --brand: #3d78e7; --brand-deep: #2a5cc4; --brand-ink: #2f63cf;
  /* Text-safe variants. --brand at 4.17:1 on white fails AA for body text but
     passes the 3:1 non-text threshold, so it stays for fills and borders while
     text uses these. Verified with the audit in tests/contrast.mjs. */
  --brand-text: #2f63cf;   /* 5.52:1 on panel */
  --ts-load-text: #0b7db7; /* 4.53:1 on panel */
  --input-line: #8297ad;   /* 3.01:1 on panel — form borders are UI components */
  --certified: #22c98f; --live: #37bdf8; --recent: #f4b740; --bad: #e24b4a;

  --panel: #ffffff; --panel-2: #ffffff; --panel-3: #f0f3f6;
  --panel-line: #e2e7ec; --panel-grid: #eef1f4;
  --panel-ink: #10171f; --panel-ink-2: #48535f; --panel-ink-3: #5f6b76;
  --inset: #eef1f4; --control: #eef2f6;
  --overlay: rgba(16, 23, 31, .045); --overlay-hover: rgba(16, 23, 31, .085);
  --ws-xhair: #cdd6de;   /* canvas crosshair lattice — must read against --inset */

  /* Demand is its own colour — it is a destination, not a contributor */
  --ts-load: #0d8fd1; --ts-fuel: #0a8a7e; --ts-cum: #bd7d12;

  /* --- Dispatch sources. Used in the canvas, chart, and legend, nowhere else --- */
  --ws-sol: #c68900;   /* 3.01:1 on panel */
  --ws-grd: #2f7fd0;
  --ws-gen: #e0601f;
  --ws-bat: #15a06f;
  --ws-chg: #7a45d0;
  --ws-socln: #7a45d0;
  --ws-dump: #c0392b;

  /* --- Tethra brand, for chrome only --- */
  --navy: #0c2633; --navy-soft: #173b49; --cyan: #20c5c7; --cyan-deep: #14989a;

  --font: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Helvetica, Arial, sans-serif;
  --mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

  --r: 8px; --r-sm: 6px; --r-lg: 12px;
  --s1: 4px; --s2: 8px; --s3: 12px; --s4: 16px; --s5: 24px; --s6: 32px; --s7: 48px;
  --rail: 258px; --topbar: 56px;
}

[data-theme="dark"] {
  --panel: #0d131a; --panel-2: #121b24; --panel-3: #172230;
  --panel-line: #212d3a; --panel-grid: #1a2430;
  --panel-ink: #e7eef5; --panel-ink-2: #9aabbc; --panel-ink-3: #8593a4;
  --inset: #0a0f15; --control: #0b1118;
  --ws-xhair: #22303d;   /* lifts off the dark canvas by the same small margin */
  --overlay: rgba(255, 255, 255, .05); --overlay-hover: rgba(255, 255, 255, .09);
  --ts-load: #37bdf8; --ts-fuel: #11b6a6; --ts-cum: #f4b740;
  --ws-sol: #ffc83d; --ws-grd: #4aa3f0; --ws-gen: #ff7847; --ws-bat: #34d39a;
  --ws-chg: #9a7ae8; --ws-socln: #9a7ae8; --ws-dump: #ff6b5e;
  --bad: #ff6b5e;
}

* { box-sizing: border-box; }
/* `clip` not `hidden`: overflow-x:hidden makes <html> a scroll container, which
   silently breaks `position: sticky` on the topbar. `clip` prevents horizontal
   scroll without establishing one. */
html { overflow-x: clip; }
html, body {
  margin: 0; padding: 0;
  color: var(--panel-ink);
  font-family: var(--font); font-size: 14px; line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
/* The topbar is sticky, so a rubber-band scroll past the top exposes whatever
   sits behind it. With html and body sharing the light page colour that read
   as a white gap tearing away from the header. Giving the root the header's
   own navy means an overscroll just shows more header. */
html { background: var(--navy); height: 100%; }
/* min-height, NOT height. At height:100% the body is exactly one viewport tall
   while the content scrolls past it, so everything below the fold showed the
   root's navy as a dark band under the page. min-height lets the body grow with
   its content and keep painting the page colour, while the root's navy is still
   there for a rubber-band scroll above the sticky header. */
body { background: var(--panel-3); min-height: 100%; }

h1, h2, h3, h4 { margin: 0; font-weight: 600; letter-spacing: -0.01em; }
p { margin: 0 0 var(--s3); }
a { color: var(--brand-text); text-decoration: none; }
a:hover { text-decoration: underline; }
code { font-family: var(--mono); font-size: 0.92em; }

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }
.muted { color: var(--panel-ink-3); }
.small { font-size: 12px; }

/* ---------- Top bar ---------- */

.topbar {
  position: sticky; top: 0; z-index: 40; height: var(--topbar);
  display: flex; align-items: center; gap: var(--s4);
  padding: 0 var(--s5);
  background: var(--navy); color: #e7f0f3;
  border-bottom: 1px solid var(--navy-soft);
}
.brand { display: flex; align-items: center; gap: 11px; }
.brand img { height: 27px; width: 27px; display: block; }
.brand-text { display: flex; flex-direction: column; line-height: 1.05; }
.brand-name { font: 700 13px/1.1 var(--font); letter-spacing: 0.18em; text-transform: uppercase; }
.brand-sub { font: 600 9px/1.2 var(--mono); letter-spacing: 0.16em; color: #7da0ad; text-transform: uppercase; }
.topbar-spacer { flex: 1; }
.topbar-actions { display: flex; align-items: center; gap: var(--s2); }

.tbtn {
  appearance: none; border: 1px solid #2f4f5e; background: transparent;
  color: #c3d7de; font: 500 12px/1 var(--font);
  padding: 7px 11px; border-radius: var(--r-sm); cursor: pointer;
}
.tbtn:hover { background: #143443; color: #eaf4f7; }

/* ---------- Layout ---------- */

.shell { display: grid; grid-template-columns: var(--rail) 1fr; min-height: calc(100vh - var(--topbar)); }
.shell > * { min-width: 0; }

.rail {
  border-right: 1px solid var(--panel-line);
  background: var(--panel);
  padding: var(--s5) var(--s4);
  display: flex; flex-direction: column; gap: var(--s5);
}
.stepcount { font: 600 20px/1 var(--font); letter-spacing: -0.02em; }
.stepcount span { color: var(--panel-ink-3); font-size: 15px; }

.steps { display: flex; flex-direction: column; gap: 2px; }
.step {
  display: grid; grid-template-columns: 22px 1fr; gap: 10px; align-items: center;
  width: 100%; text-align: left; border: 0; background: transparent;
  border-radius: var(--r-sm); padding: 9px 10px;
  font: 500 13px/1.2 var(--font); color: var(--panel-ink-3); cursor: pointer;
}
.step:hover:not(:disabled) { background: var(--inset); color: var(--panel-ink); }
.step[aria-current="step"] { background: color-mix(in srgb, var(--brand) 10%, var(--panel)); color: var(--panel-ink); font-weight: 600; }
.step:disabled { opacity: 0.42; cursor: not-allowed; }
.step-dot {
  width: 22px; height: 22px; display: grid; place-items: center;
  border: 1px solid var(--panel-line); border-radius: 50%;
  font: 600 10px/1 var(--mono); color: var(--panel-ink-3); background: var(--panel);
}
.step[aria-current="step"] .step-dot { border-color: var(--brand); color: var(--brand); }
.step.done .step-dot { background: var(--certified); border-color: var(--certified); color: #fff; }
.step small { display: block; font-size: 11px; font-weight: 400; color: var(--panel-ink-3); margin-top: 2px; }

.rail-block { border-top: 1px solid var(--panel-line); padding-top: var(--s4); }
.rail-block h4 { font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase; color: var(--panel-ink-3); margin-bottom: var(--s3); font-family: var(--mono); }
.rail-kv { display: flex; justify-content: space-between; gap: var(--s2); padding: 3px 0; font-size: 12px; }
.rail-kv dt { color: var(--panel-ink-3); }
.rail-kv dd { margin: 0; font-family: var(--mono); font-variant-numeric: tabular-nums; text-align: right; }

.main { padding: var(--s6) var(--s6) var(--s7); max-width: 1240px; }
.pagehead { margin-bottom: var(--s5); }
.pagehead h1 { font-size: 22px; margin-bottom: 6px; }
.pagehead p { color: var(--panel-ink-3); max-width: 70ch; margin: 0; }

/* ---------- Cards ---------- */

.card {
  background: var(--panel); border: 1px solid var(--panel-line);
  border-radius: var(--r-lg); padding: var(--s5); margin-bottom: var(--s4);
}
.card-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--s4); margin-bottom: var(--s4); flex-wrap: wrap; }
.card-head > div:first-child { min-width: 0; flex: 1 1 320px; }
.card-head h3 { font-size: 15px; }
.card-head p { margin: 4px 0 0; color: var(--panel-ink-3); font-size: 12.5px; max-width: 72ch; }
.card.flush { padding: 0; overflow: hidden; }
.card.flush > .card-head { padding: var(--s5) var(--s5) 0; margin-bottom: var(--s3); }
.card-head > .chip { flex: 0 0 auto; }

/* ---------- Forms ---------- */

.field { margin-bottom: var(--s4); }
.field > label { display: block; font-weight: 600; font-size: 12.5px; margin-bottom: 5px; }
.field .hint { color: var(--panel-ink-3); font-size: 12px; margin: 0 0 7px; }

input[type="text"], input[type="number"], input[type="date"], select, textarea {
  width: 100%; font: inherit; color: var(--panel-ink); background: var(--panel);
  border: 1px solid var(--input-line); border-radius: var(--r-sm); padding: 8px 10px;
}
input:focus, select:focus, textarea:focus {
  outline: none; border-color: var(--brand);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent);
}
textarea { min-height: 250px; resize: vertical; line-height: 1.6; font-size: 13.5px; }
input[type="number"] { font-family: var(--mono); }

.grid2 { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--s4); }
.grid3 { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: var(--s4); }
.grid4 { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); }

/* ---------- Buttons ---------- */

.btn {
  appearance: none; font: 600 13px/1 var(--font); padding: 10px 16px;
  border-radius: var(--r-sm); border: 1px solid transparent; cursor: pointer;
  display: inline-flex; align-items: center; gap: 8px;
}
.btn-primary { background: var(--navy); color: #fff; }
.btn-primary:hover:not(:disabled) { background: var(--navy-soft); }
.btn-accent { background: var(--brand); color: #fff; }
.btn-accent:hover:not(:disabled) { background: var(--brand-deep); }
.btn-ghost { background: var(--panel); border-color: var(--panel-line); color: var(--panel-ink); }
.btn-ghost:hover:not(:disabled) { background: var(--inset); }
.btn:disabled { opacity: 0.45; cursor: not-allowed; }
.btn-row { display: flex; gap: var(--s2); align-items: center; flex-wrap: wrap; }
.btn-row.end { justify-content: flex-end; }

/* ---------- Chips / badges ---------- */

.chip {
  display: inline-flex; align-items: center; gap: 5px;
  font: 600 11px/1 var(--mono); letter-spacing: 0.06em; text-transform: uppercase;
  padding: 4px 7px; border-radius: 4px; border: 1px solid transparent; white-space: nowrap;
}
.chip-measured, .chip-stated { background: color-mix(in srgb, var(--certified) 14%, var(--panel)); color: #12855e; border-color: color-mix(in srgb, var(--certified) 34%, transparent); }
.chip-inferred, .chip-derived { background: color-mix(in srgb, var(--brand) 12%, var(--panel)); color: var(--brand-ink); border-color: color-mix(in srgb, var(--brand) 30%, transparent); }
.chip-assumed, .chip-estimated { background: color-mix(in srgb, var(--recent) 20%, var(--panel)); color: #8a6412; border-color: color-mix(in srgb, var(--recent) 42%, transparent); }
.chip-published, .chip-neutral { background: var(--inset); color: var(--panel-ink-2); border-color: var(--panel-line); }
.chip-alert { background: color-mix(in srgb, var(--bad) 12%, var(--panel)); color: #a63b2c; border-color: color-mix(in srgb, var(--bad) 32%, transparent); }

[data-theme="dark"] .chip-measured, [data-theme="dark"] .chip-stated { color: var(--certified); }
[data-theme="dark"] .chip-assumed, [data-theme="dark"] .chip-estimated { color: var(--recent); }
[data-theme="dark"] .chip-alert { color: var(--bad); }

.sim-badge {
  font: 700 9.5px/1 var(--mono); letter-spacing: 0.09em; text-transform: uppercase;
  padding: 5px 9px; border-radius: 6px;
  background: color-mix(in srgb, var(--brand) 13%, var(--panel)); color: var(--brand-ink);
}
.sim-badge.meas { background: color-mix(in srgb, var(--certified) 15%, var(--panel)); color: #12855e; }

.sev-high { color: #a63b2c; }
.sev-moderate { color: #8a6412; }
.sev-low { color: var(--panel-ink-3); }

/* ---------- Stat tiles ---------- */

.tiles { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: var(--s3); margin-bottom: var(--s4); }
.tile { background: var(--panel); border: 1px solid var(--panel-line); border-radius: var(--r); padding: var(--s4); }
.tile .k { font: 600 11px/1.25 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--panel-ink-3); overflow-wrap: anywhere; hyphens: auto; }
.tile .v { font: 600 26px/1.15 var(--font); letter-spacing: -0.02em; margin-top: 7px; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.tile .v.text { font-size: 17px; line-height: 1.25; letter-spacing: -0.01em; }
.tile .v small { font-size: 14px; font-weight: 500; color: var(--panel-ink-3); margin-left: 3px; }
.tile .d { font-size: 12px; margin-top: 5px; color: var(--panel-ink-3); }
.tile .d.pos { color: var(--certified); }
.tile .d.neg { color: var(--bad); }
.tile.accent { background: var(--navy); border-color: var(--navy); }
.tile.accent .k { color: #7da0ad; }
.tile.accent .v { color: #fff; }
.tile.accent .d { color: #a9c6d0; }
.tile.accent .d.pos { color: #7fe0c4; }

/* ---------- Stat cells (LogIt rail pattern) ---------- */

.st-cell { display: flex; flex-direction: column; gap: 2px; }
.st-cell label { font: 600 10.5px/1.2 var(--mono); letter-spacing: 0.1em; text-transform: uppercase; color: var(--panel-ink-3); }
.st-cell b { font: 600 15px/1.15 var(--font); font-variant-numeric: tabular-nums; }

/* ---------- Tables ---------- */

.table-wrap { overflow-x: auto; }
table.data { width: 100%; min-width: 720px; border-collapse: collapse; font-size: 12.5px; }
table.data td.name { min-width: 190px; }
.phase-body table.data { min-width: 900px; }
.phase-body table.data td.name { min-width: 230px; }
table.data th, table.data td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--panel-line); }
table.data thead th {
  font: 600 11px/1.2 var(--mono); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--panel-ink-3); border-bottom: 1px solid var(--panel-line);
  white-space: nowrap; background: var(--panel);
}
table.data td.n, table.data th.n { text-align: right; font-family: var(--mono); font-variant-numeric: tabular-nums; }
table.data tbody tr:last-child td { border-bottom: 0; }
table.data tbody tr:hover { background: var(--overlay); }
table.data td.name { font-weight: 500; }

/* ======================================================================
   SCENARIO LAB
   Canvas, contributor nodes, load zone, dispatch chart, stat rail —
   the LogIt Studio workspace, carried over.
   ====================================================================== */

.ws-shop {
  position: relative; border: 1px solid var(--panel-line); border-radius: 14px;
  background: var(--panel); padding: 12px 14px 14px; margin-bottom: var(--s4);
}
.ws-shop-h { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.ws-shop-h h3 { font-size: 14px; }
.ws-shop-sub { font-size: 11px; color: var(--panel-ink-3); }

/* --- Asset chips: the library you drag from --- */
.ws-chips { cursor: grab; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.ws-pgrp { display: inline-flex; align-items: center; gap: 5px; padding: 4px 8px; border-radius: 9px; background: var(--inset); }
.ws-pgrp > i { font-style: normal; font-size: 8.5px; font-family: var(--mono); letter-spacing: 0.07em; color: var(--panel-ink-3); margin-right: 2px; }
.ws-pgrp.gen > i { color: var(--ws-gen); }
.ws-pgrp.bat > i { color: var(--ws-bat); }
.ws-pgrp.sol > i { color: var(--ws-sol); }
.ws-pgrp.grd > i { color: var(--ws-grd); }

.ws-chip {
  font-size: 11.5px; font-weight: 600; font-family: var(--mono);
  padding: 6px 12px 6px 9px; border: 1.5px solid; border-radius: 8px;
  color: var(--panel-ink); cursor: grab; display: inline-flex; align-items: center; gap: 7px;
  transition: transform .12s, filter .12s;
}
.ws-chip::before { content: ''; width: 11px; height: 11px; border-radius: 3px; flex: 0 0 auto; }
.ws-chip.gen { border-color: var(--ws-gen); background: color-mix(in srgb, var(--ws-gen) 13%, var(--panel)); }
.ws-chip.gen::before { background: var(--ws-gen); }
.ws-chip.bat { border-color: var(--ws-bat); background: color-mix(in srgb, var(--ws-bat) 13%, var(--panel)); }
.ws-chip.bat::before { background: var(--ws-bat); }
.ws-chip.sol { border-color: var(--ws-sol); background: color-mix(in srgb, var(--ws-sol) 15%, var(--panel)); }
.ws-chip.sol::before { background: var(--ws-sol); }
.ws-chip.grd { border-color: var(--ws-grd); background: color-mix(in srgb, var(--ws-grd) 13%, var(--panel)); }
.ws-chip.grd::before { background: var(--ws-grd); }
.ws-chip:hover { filter: brightness(1.06); transform: translateY(-1px); }
.ws-chip:active { cursor: grabbing; transform: none; }
.ws-chip small { font-weight: 500; color: var(--panel-ink-3); font-size: 10px; }
.ws-chip.ref { border-style: dashed; }

/* --- Canvas --- */
.ws-canvas {
  position: relative; height: 460px;
  border: 1px solid var(--panel-line); border-radius: 10px;
  background: var(--inset); overflow: hidden; margin-bottom: 10px;
}
.ws-shop { overflow-x: auto; }
.ws-canvas { min-width: 720px; }
.ws-canvas.drag { border-color: var(--brand); background: color-mix(in srgb, var(--brand) 5%, var(--inset)); }
.ws-canvas svg { position: absolute; inset: 0; width: 100%; height: 100%; }
.ws-canvas.empty::after {
  content: 'Drag an asset from the library above onto the bus';
  position: absolute; left: 24px; top: 50%; transform: translateY(-50%);
  font: 500 12.5px var(--font); color: var(--panel-ink-3); pointer-events: none;
}

.ws-bus { stroke: var(--panel-ink-3); stroke-width: 2.5; opacity: 0.5; }
.ws-flow { stroke: var(--panel-line); stroke-width: 1.4; transition: stroke-width .2s; }
.ws-flow.on { stroke: var(--certified); stroke-dasharray: 7 5; animation: wsdash .45s linear infinite; stroke-width: 2; }
.ws-flow.load.on { stroke: var(--ts-load); }
.ws-tapc { fill: var(--panel-ink-3); opacity: 0.55; }
@keyframes wsdash { to { stroke-dashoffset: -12; } }

/* --- Contributor node --- */
.ws-node {
  position: absolute; width: 178px;
  background: var(--panel); border: 1px solid var(--panel-line); border-left-width: 3px;
  border-radius: 9px; padding: 8px 10px 8px 12px;
  font-size: 12px; line-height: 1.4;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
  cursor: grab; touch-action: none; user-select: none;
}
.ws-node:active { cursor: grabbing; }
.ws-node.gen { border-left-color: var(--ws-gen); }
.ws-node.sol { border-left-color: var(--ws-sol); }
.ws-node.bat { border-left-color: var(--ws-bat); }
.ws-node.grd { border-left-color: var(--ws-grd); }
.ws-node b { display: block; font-size: 11.5px; }
.ws-node span { display: block; color: var(--panel-ink-3); font-family: var(--mono); font-size: 11px; }
.ws-node .ws-live { color: var(--certified); font-weight: 600; min-height: 13px; font-size: 11px; }
.ws-live.run { color: var(--ws-gen); }
.ws-live.imp { color: var(--ws-grd); }
.ws-live.dis { color: var(--ws-bat); }
.ws-live.chg { color: var(--ws-chg); }
.ws-live.prod { color: var(--ws-sol); }
.ws-live.bad { color: var(--bad); }
.ws-live.idle { color: var(--panel-ink-3); }

.ws-kicon {
  position: absolute; top: -9px; left: -9px; width: 20px; height: 20px;
  border-radius: 6px; display: flex; align-items: center; justify-content: center;
  font-size: 10px; font-weight: 700; font-style: normal; font-family: var(--mono); color: #fff;
}
.ws-kicon.gen { background: var(--ws-gen); }
.ws-kicon.sol { background: var(--ws-sol); }
.ws-kicon.bat { background: var(--ws-bat); }
.ws-kicon.grd { background: var(--ws-grd); }

.ws-x {
  position: absolute; top: -7px; right: -7px; width: 22px; height: 22px;
  display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--panel-line); border-radius: 50%;
  background: var(--panel); color: var(--panel-ink-2); cursor: pointer;
  font-size: 13px; line-height: 1; box-shadow: 0 1px 4px rgba(10, 20, 35, .14);
}
.ws-x:hover { color: var(--bad); border-color: var(--bad); background: color-mix(in srgb, var(--bad) 8%, var(--panel)); }

.ws-tag { margin-top: 3px; font-size: 10px !important; letter-spacing: 0.04em; }
.ws-nsub { display: block; font-size: 11px; font-family: var(--mono); color: var(--panel-ink-3); margin-top: 1px; }

/* Node quantity stepper */
.ws-spi {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  margin-top: 6px; font-size: 11px; font-family: var(--mono); color: var(--panel-ink-3);
}
.ws-node .ws-spi > span:first-child { display: inline-block; margin: 0; letter-spacing: 0.03em; white-space: nowrap; }
.ws-node span.ws-spv { display: inline-flex; align-items: center; gap: 5px; }
.ws-spv button {
  flex: 0 0 auto; width: 19px; height: 19px; padding: 0;
  border: 1px solid var(--panel-line); border-radius: 5px;
  background: var(--panel); color: var(--panel-ink-2);
  font: 600 12px/1 var(--font); cursor: pointer;
}
.ws-spv button:hover:not(:disabled) { background: var(--inset); color: var(--panel-ink); }
.ws-spv button:disabled { opacity: 0.3; cursor: not-allowed; }
.ws-spv b { min-width: 20px; text-align: center; font-size: 11px; color: var(--panel-ink); }

/* --- Load zone: the demand side is a destination, not a contributor --- */
.ws-loadzone {
  position: absolute; top: 12px; bottom: 12px; right: 12px; width: 172px;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-start;
  padding: 14px; border: 1.5px solid var(--ts-load); border-radius: 12px;
  background: color-mix(in srgb, var(--ts-load) 6%, var(--panel));
}
.ws-lz-cap { font-family: var(--mono); font-size: 11px; letter-spacing: 0.09em; color: var(--ts-load-text); font-weight: 700; }
.ws-loadzone .ws-live { font-family: var(--mono); font-size: 24px; font-weight: 700; color: var(--panel-ink); line-height: 1.05; min-height: 26px; }
.ws-loadzone .ws-live.bad { color: var(--bad); }
.ws-zserve { display: inline-flex; align-items: center; gap: 6px; padding: 5px 10px; border-radius: 8px; font-size: 11px; font-weight: 600; }
.ws-zserve.ok { color: var(--ws-bat); background: color-mix(in srgb, var(--ws-bat) 10%, var(--panel)); border: 1px solid color-mix(in srgb, var(--ws-bat) 30%, transparent); }
.ws-zserve.bad { color: var(--bad); background: color-mix(in srgb, var(--bad) 9%, var(--panel)); border: 1px solid color-mix(in srgb, var(--bad) 34%, transparent); }
.ws-lz-un { height: 15px; line-height: 15px; font-family: var(--mono); font-size: 10.5px; font-weight: 700; color: var(--bad); opacity: 0; transition: opacity .3s ease; pointer-events: none; }
.ws-lz-un.on { opacity: 1; }
.ws-lz-stats { font-family: var(--mono); font-size: 11px; color: var(--panel-ink-2); }
.ws-lz-stats i { font-style: normal; color: var(--panel-ink-3); cursor: help; border-bottom: 1px dotted var(--panel-ink-3); }
.ws-lz-sub { margin-top: auto; font-size: 11px; line-height: 1.45; color: var(--panel-ink-3); }

/* --- Transport controls --- */
.ws-transport { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 8px; }
.ws-play {
  width: 34px; height: 30px; border: 1px solid var(--panel-line); border-radius: var(--r-sm);
  background: var(--panel); color: var(--panel-ink); font-size: 12px; cursor: pointer;
}
.ws-play:hover { background: var(--inset); }
.ws-scrub { flex: 1 1 220px; min-width: 160px; accent-color: var(--brand); }
.ws-clock { font-family: var(--mono); font-size: 11.5px; color: var(--panel-ink-2); min-width: 190px; }

/* --- Dispatch chart --- */
.ws-chartwrap {
  position: relative;
  /* pan-y leaves vertical finger-scroll to the page (never hijacked) while
     handing horizontal pans and pinches to the Lab — the Studio rule */
  touch-action: pan-y; user-select: none; -webkit-user-select: none;
  cursor: crosshair;
  /* A two-finger horizontal swipe on a macOS trackpad is a BACK/FORWARD
     navigation gesture unless the element under it declares that it consumes
     horizontal overscroll. Without this, panning the profile left or right
     yanked the browser to the previous page or bounced the whole window. */
  overscroll-behavior-x: contain;
}
/* The chart is inside these, and the gesture has to be contained the whole way
   up or the browser still claims it. */
.ws-shop, .ws-canvas, .ws-brush { overscroll-behavior-x: contain; }
.ws-chartwrap svg { display: block; width: 100%; }
.ws-cursor { stroke: var(--panel-ink); stroke-width: 1; opacity: 0.5; }
.ws-tip { font-size: 11.5px;
  position: absolute; top: 6px; display: none; flex-direction: column; gap: 2px;
  background: var(--panel); border: 1px solid var(--panel-line); border-radius: 8px;
  padding: 7px 10px; font-size: 10px; font-family: var(--mono); color: var(--panel-ink-2);
  pointer-events: none; min-width: 124px; box-shadow: 0 3px 14px rgba(0, 0, 0, .14); z-index: 4;
}
.ws-tip.on { display: flex; }
.ws-tip b { color: var(--panel-ink); }
.ws-tip .ws-tip-t { font-size: 10px; letter-spacing: 0.03em; margin-bottom: 2px; }
.ws-tip .bad2 { color: var(--bad); font-weight: 600; }

.ws-sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; border: 1px solid rgba(16,23,31,.28); }
.ws-sw.ln { height: 2px; background: var(--panel-ink); border-radius: 0; }
.ws-sw.ln.soc { background: var(--ws-socln); }
.ws-sw.un { background: var(--bad); opacity: 0.5; }
.ws-leg { display: flex; gap: 14px; flex-wrap: wrap; font-size: 11.5px; font-family: var(--mono); color: var(--panel-ink-3); margin: 4px 0 6px; }

/* --- Stat rail --- */
.ws-rail {
  /* A column, not a wrapping row. As a flex row the header, the price control
     and every metric group became siblings competing for the same line, which
     is what made this block read as a jumble. Each band now owns its row and
     the cells inside it sit on their own aligned grid. */
  display: flex; flex-direction: column; align-items: stretch; gap: 0;
  border-top: 1px solid var(--panel-line); padding-top: 12px; margin-top: 10px;
}
.ws-d { font-style: normal; font-size: 10px; color: var(--certified); margin-left: 4px; }
.ws-d.up { color: var(--bad); }
.ws-serve { font-size: 11px; font-family: var(--mono); padding: 4px 10px; border-radius: 8px; }
.ws-serve.ok { background: color-mix(in srgb, var(--certified) 14%, var(--panel)); color: #12855e; }
.ws-serve.bad { background: color-mix(in srgb, var(--bad) 14%, var(--panel)); color: #a32d2d; font-weight: 600; }
[data-theme="dark"] .ws-serve.ok { color: var(--certified); }
[data-theme="dark"] .ws-serve.bad { color: var(--bad); }
.ws-note { font-size: 11px; color: var(--panel-ink-3); font-family: var(--mono); flex-basis: 100%; line-height: 1.6; }

/* ---------- Phase / load editor ---------- */

.phase { border: 1px solid var(--panel-line); border-radius: var(--r); margin-bottom: var(--s3); overflow: hidden; }
.phase-head {
  display: grid; grid-template-columns: 1fr auto; gap: var(--s4); align-items: center;
  padding: var(--s3) var(--s4); background: var(--panel-3); border-bottom: 1px solid var(--panel-line); cursor: pointer;
}
.phase-head h4 { font-size: 13.5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.phase-meta { font-family: var(--mono); font-size: 11.5px; color: var(--panel-ink-3); margin-top: 3px; }
.phase-body { padding: var(--s4); }
.phase-body[hidden] { display: none; }
.phase-notes { color: var(--panel-ink-3); font-size: 12.5px; margin: 0 0 var(--s3); max-width: 78ch; }

/* ---------- Callouts ---------- */

.callout {
  border-left: 3px solid var(--panel-line); background: var(--panel);
  border-radius: 0 var(--r-sm) var(--r-sm) 0; padding: var(--s3) var(--s4);
  margin-bottom: var(--s3); font-size: 13px;
}
.callout.ok { border-left-color: var(--certified); background: color-mix(in srgb, var(--certified) 8%, var(--panel)); }
.callout.warn { border-left-color: var(--recent); background: color-mix(in srgb, var(--recent) 12%, var(--panel)); }
.callout.bad { border-left-color: var(--bad); background: color-mix(in srgb, var(--bad) 8%, var(--panel)); }
.callout.info { border-left-color: var(--brand); background: color-mix(in srgb, var(--brand) 7%, var(--panel)); }
.callout strong { display: block; margin-bottom: 3px; }

/* ---------- Segmented control ---------- */

.seg { display: inline-flex; border: 1px solid var(--panel-line); border-radius: var(--r-sm); overflow-x: auto; max-width: 100%; background: var(--panel); scrollbar-width: thin; }
.seg button { flex: 0 0 auto; border: 0; background: var(--panel); color: var(--panel-ink-3); font: 500 12px/1 var(--font); padding: 7px 12px; cursor: pointer; white-space: nowrap; }
.seg button + button { border-left: 1px solid var(--panel-line); }
.seg button:hover { background: var(--inset); color: var(--panel-ink); }
.seg button.on { background: color-mix(in srgb, var(--brand) 11%, var(--panel)); color: var(--panel-ink); font-weight: 600; }

/* ---------- Modal ---------- */

.modal { position: fixed; inset: 0; z-index: 100; display: none; }
.modal[open] { display: block; }
.modal-scrim { position: absolute; inset: 0; background: rgba(12, 38, 51, .45); }
.modal-card {
  position: relative; margin: 5vh auto; width: min(880px, 92vw); max-height: 90vh;
  overflow-y: auto; background: var(--panel); border-radius: var(--r-lg); padding: var(--s6);
}
.modal-card h2 { font-size: 19px; margin-bottom: var(--s2); }
.modal-card h4 { font-size: 13px; margin: var(--s5) 0 var(--s2); }
.modal-card p { font-size: 13px; color: var(--panel-ink-2); }
.modal-x { position: absolute; top: var(--s4); right: var(--s4); }

/* ---------- Status ---------- */

.banner { display: flex; align-items: center; gap: 10px; padding: var(--s3) var(--s4); border-radius: var(--r); margin-bottom: var(--s4); font-size: 13px; }
.banner.err { background: color-mix(in srgb, var(--bad) 10%, var(--panel)); color: #8f3325; border: 1px solid color-mix(in srgb, var(--bad) 30%, transparent); }
.banner.info { background: color-mix(in srgb, var(--brand) 8%, var(--panel)); color: var(--brand-ink); border: 1px solid color-mix(in srgb, var(--brand) 26%, transparent); }
[data-theme="dark"] .banner.err { color: var(--bad); }

.spinner { width: 14px; height: 14px; border-radius: 50%; border: 2px solid rgba(255,255,255,.35); border-top-color: #fff; animation: spin .7s linear infinite; }
.btn-ghost .spinner { border-color: var(--panel-line); border-top-color: var(--brand); }
@keyframes spin { to { transform: rotate(360deg); } }

.empty { text-align: center; padding: var(--s7) var(--s5); color: var(--panel-ink-3); }
.empty h3 { color: var(--panel-ink); margin-bottom: 6px; font-size: 15px; }

/* ---------- Responsive ---------- */

@media (max-width: 1080px) {
  .tiles { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
/* Two columns of a mono, letter-spaced, uppercase label is about 14 characters
   at this width — "GENERATOR STARTS" and "SPECIFIC YIELD" do not fit, and a
   forced 2-up grid made them collide with the value beneath. One column below
   this, and the letter-spacing eases off so labels stop fighting the box. */
@media (max-width: 620px) {
  .tiles { grid-template-columns: 1fr; }
  .tile .k { letter-spacing: 0.06em; font-size: 10.5px; }
  .tile .v { font-size: 22px; }
}
/* ----------------------------------------------------------------------
   Small screens

   Intake, the power profile, and the report are readable on a phone and stay
   fully usable. The Scenario Lab is not a phone interface — it is a
   drag-and-drop canvas with a bus, and shrinking it produces something that
   technically renders and practically cannot be operated. Rather than ship a
   degraded version of it, the Lab says plainly what it needs and points at the
   parts that do work. From ~600px up the Lab renders — narrow widths get the
   vertical riser bus rather than a refusal, because a single-column riser is
   genuinely readable on a tablet held portrait. Only below 600px, where the
   nodes themselves stop fitting, do we decline.
   ---------------------------------------------------------------------- */
.lab-smallscreen { display: none; }

@media (max-width: 599px) {
  .lab-smallscreen {
    display: flex; gap: 12px; align-items: flex-start;
    border: 1px solid var(--panel-line); border-left: 3px solid var(--recent);
    border-radius: var(--r); background: color-mix(in srgb, var(--recent) 9%, var(--panel));
    padding: var(--s4); margin-bottom: var(--s4);
  }
  .lab-smallscreen svg { flex: 0 0 auto; margin-top: 1px; }
  .lab-smallscreen b { display: block; margin-bottom: 3px; font-size: 13.5px; }
  .lab-smallscreen p { margin: 0; font-size: 12.5px; color: var(--panel-ink-2); }
  .ws-shop { display: none; }
}

@media (max-width: 860px) {
  .shell { grid-template-columns: 1fr; }
  .rail { border-right: 0; border-bottom: 1px solid var(--panel-line); }
  .steps { flex-direction: row; overflow-x: auto; min-width: 0; scrollbar-width: thin; }
  .step { min-width: 150px; flex: 0 0 auto; }
  .rail-block { display: none; }
  .main { padding: var(--s5) var(--s4) var(--s6); }
  .grid2, .grid3 { grid-template-columns: 1fr; }
  .ws-canvas { height: 460px; }
}

/* ---------- Report disclaimer ---------- */

.disclaimer {
  margin-top: var(--s6); padding: var(--s4) var(--s5);
  border: 1px solid var(--panel-line); border-left: 3px solid var(--panel-ink-3);
  border-radius: var(--r-sm); background: var(--panel-3);
  break-inside: avoid;
}
.disclaimer h4 { font-size: 12.5px; font-weight: 650; color: var(--panel-ink); margin-bottom: var(--s2); }
.disclaimer p { font-size: 11.5px; line-height: 1.6; color: var(--panel-ink-2); margin-bottom: var(--s2); }
.disclaimer p:last-of-type { margin-bottom: 0; }
.disclaimer-f { font-family: var(--mono); font-size: 10px !important; color: var(--panel-ink-3) !important; }

/* ---------- Printed report ---------- */

#printHead { display: none; }

.printhead { break-after: avoid; margin-bottom: var(--s5); }
.printhead-brand {
  display: flex; align-items: center; gap: 14px;
  background: var(--navy); border-radius: var(--r-sm);
  padding: 14px 18px; margin-bottom: 12px;
}
.printhead-brand img { height: 26px; width: auto; }
.printhead-tool {
  margin-left: auto; text-align: right; color: #fff;
  font: 650 13px/1.2 var(--font); letter-spacing: .02em;
}
.printhead-tool small { display: block; font: 500 9.5px/1.3 var(--mono); color: var(--cyan); letter-spacing: .08em; text-transform: uppercase; }
.printhead-meta { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 24px; }
.printhead-meta div { display: flex; gap: 8px; align-items: baseline; border-bottom: 1px solid var(--panel-line); padding-bottom: 4px; }
.printhead-meta span { font: 600 9px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .05em; color: var(--panel-ink-3); flex: 0 0 108px; }
.printhead-meta b { font-size: 11.5px; font-weight: 600; color: var(--panel-ink); }

@page { margin: 14mm 12mm 16mm; }

@media print {
  /* Backgrounds carry meaning here — the navy masthead and the chart fills
     are the difference between a branded report and a stack of grey tables. */
  html, body { background: #fff !important; -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  * { -webkit-print-color-adjust: exact; print-color-adjust: exact; }

  .topbar, .rail, .btn-row, .seg, .ws-chips, .ws-transport, .ws-x,
  .startrow, .ws-bs, .ws-lib, .stepper, .pagehead p { display: none !important; }
  .shell { grid-template-columns: 1fr; display: block; }
  .main { padding: 0 !important; }

  #printHead { display: block; }

  .card, .ws-shop, .tiles, .table-wrap, .disclaimer, ol.method-steps, .callout { break-inside: avoid; }
  .card { border-color: #d5dbe1; box-shadow: none; margin-bottom: 10px; }
  .card-head h3, .sec-h { break-after: avoid; }
  table.data { font-size: 9.5px; }
  table.data thead { display: table-header-group; }  /* repeat headers across pages */
  table.data th, table.data td { padding: 4px 6px; }

  /* Tiles were laid out for a wide screen. On paper the auto-fit grid drops to
     one column and a four-tile row becomes four pages of whitespace. */
  .tiles { grid-template-columns: repeat(4, minmax(0, 1fr)) !important; gap: 6px !important; }
  .tile { padding: 8px 10px !important; }
  .tile .v { font-size: 16px !important; margin-top: 4px !important; }
  .tile .v.text { font-size: 11px !important; }
  .tile .k { font-size: 8px !important; letter-spacing: .08em !important; }
  .tile .d { font-size: 9px !important; margin-top: 3px !important; }
  .tile .v small { font-size: 10px !important; }

  a[href]::after { content: ""; }
}

/* ---------- Scenario Lab: library picker, favourites, scenario I/O ---------- */

.ws-shop-act { margin-left: auto; display: flex; gap: 6px; flex-wrap: wrap; }
.ws-shop-act .btn { padding: 7px 12px; font-size: 12px; }

.ws-more {
  font: 600 11.5px/1 var(--font);
  padding: 8px 13px; border: 1px dashed var(--panel-line); border-radius: 8px;
  background: var(--panel); color: var(--panel-ink-2); cursor: pointer;
}
.ws-more:hover { border-color: var(--brand); color: var(--brand-text); background: color-mix(in srgb, var(--brand) 6%, var(--panel)); }
.ws-more small { font-family: var(--mono); font-size: 10px; opacity: .75; }

.ws-lib { border: 1px solid var(--panel-line); border-radius: 10px; background: var(--panel); padding: 12px; margin: 0 0 12px; }
.ws-lib-h { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; margin-bottom: 10px; }
.ws-lib-h input { flex: 1 1 260px; }
.ws-lib-b { max-height: 320px; overflow-y: auto; border: 1px solid var(--panel-line); border-radius: 8px; }

.ws-librow {
  display: grid; grid-template-columns: 30px 1fr auto auto; gap: 12px; align-items: center;
  padding: 9px 12px; background: var(--panel);
}
.ws-librow + .ws-librow { border-top: 1px solid var(--panel-line); }
.ws-librow:hover { background: var(--overlay); }
.ws-librow.on { background: color-mix(in srgb, var(--brand) 5%, var(--panel)); box-shadow: inset 3px 0 0 var(--brand); }
.ws-libname { display: flex; flex-direction: column; min-width: 0; }
.ws-libname b { font-size: 12.5px; font-weight: 600; overflow-wrap: anywhere; }
.ws-libname span { font-family: var(--mono); font-size: 11px; color: var(--panel-ink-3); overflow-wrap: anywhere; }
.ws-libmeta { display: flex; gap: 5px; align-items: center; flex-wrap: wrap; }

.ws-star {
  width: 26px; height: 26px; border: 1px solid var(--panel-line); border-radius: 6px;
  background: var(--panel); color: var(--panel-ink-3); cursor: pointer; font-size: 14px; line-height: 1; padding: 0;
}
.ws-star:hover { border-color: var(--recent); color: var(--recent); }
.ws-star.on { color: var(--recent); border-color: color-mix(in srgb, var(--recent) 50%, transparent); background: color-mix(in srgb, var(--recent) 12%, var(--panel)); }

.ws-lib .ws-spv { display: inline-flex; align-items: center; gap: 5px; }
.ws-lib .ws-spv button { width: 22px; height: 22px; padding: 0; border: 1px solid var(--panel-line); border-radius: 5px; background: var(--panel); color: var(--panel-ink-2); font: 600 13px/1 var(--font); cursor: pointer; }
.ws-lib .ws-spv button:hover:not(:disabled) { background: var(--inset); color: var(--panel-ink); }
.ws-lib .ws-spv button:disabled { opacity: .3; cursor: not-allowed; }
.ws-lib .ws-spv b { min-width: 20px; text-align: center; font-family: var(--mono); font-size: 12px; }

@media (max-width: 700px) {
  .ws-librow { grid-template-columns: 30px 1fr auto; }
  .ws-libmeta { display: none; }
}

/* ---------- Scenario Lab: zoom bar and transport ---------- */

.ws-zoomrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 0 0 10px; }
.ws-zoomspan { font-family: var(--mono); font-size: 11px; color: var(--panel-ink-3); }
.seg.scrollx { max-width: 380px; }

.ws-brush {
  position: relative; margin-top: 10px; user-select: none; touch-action: none;
  border: 1px solid var(--panel-line); border-radius: 9px; overflow: hidden; background: var(--inset);
  cursor: grab;
}
.ws-brush:active { cursor: grabbing; }
.ws-brush svg { display: block; width: 100%; height: 46px; }
.ws-brush-win { fill: color-mix(in srgb, var(--brand) 16%, transparent); stroke: var(--brand); stroke-width: 1.5; }
.ws-brush-h { fill: var(--panel); stroke: var(--brand); stroke-width: 2; cursor: ew-resize; }
.bz-tag {
  position: absolute; top: 4px; left: 8px; pointer-events: none;
  font: 700 9px/1 var(--mono); letter-spacing: .12em; color: var(--panel-ink-3);
}
.ws-brush-hint { font-size: 11px; color: var(--panel-ink-3); margin: 5px 2px 0; }

/* Low-load verdict emphasis in the stat rail */
.st-cell b.ll-material { color: #8a6412; }
.st-cell b.ll-severe { color: var(--bad); }

/* ======================================================================
   TethraAI — the AI-integration brand surface
   Badge + review panel, following the LogIt Studio .tai-b convention:
   anything the AI produced or is producing carries the mark.
   ====================================================================== */

.tai-b {
  display: inline-flex; align-items: center; gap: 5px;
  font: 800 11px/1 var(--font); letter-spacing: 0.01em;
  color: var(--brand-text); white-space: nowrap;
}
.tai-b img { height: 13px; width: auto; display: block; }
/* The mark is a single-colour glyph, so it is tinted by filter rather than
   shipped in three variants. Which tint depends on what it sits ON, not on the
   theme alone: white on a filled accent button in either theme, and page ink
   on a ghost button — where the old unconditional invert made it white on
   white and the "Price for this site" control looked like it had no icon. */
.tai-btn-icon { height: 15px; width: auto; display: block; filter: brightness(0) invert(1); }
.btn-ghost .tai-btn-icon,
.btn-outline .tai-btn-icon { filter: brightness(0) opacity(.72); }
[data-theme="dark"] .btn-ghost .tai-btn-icon,
[data-theme="dark"] .btn-outline .tai-btn-icon { filter: brightness(0) invert(1) opacity(.82); }
.btn-ghost:hover .tai-btn-icon { filter: brightness(0) opacity(1); }
[data-theme="dark"] .btn-ghost:hover .tai-btn-icon { filter: brightness(0) invert(1); }

.tai-panel {
  border: 1px solid color-mix(in srgb, var(--brand) 32%, var(--panel-line));
  border-radius: var(--r-lg);
  background: color-mix(in srgb, var(--brand) 4%, var(--panel));
  padding: var(--s5); margin-bottom: var(--s4);
}
.tai-head { display: flex; align-items: center; gap: 12px; margin-bottom: var(--s3); flex-wrap: wrap; }
.tai-head img { height: 22px; width: auto; display: block; }
.tai-head .k { font: 600 10.5px/1.2 var(--mono); letter-spacing: 0.12em; text-transform: uppercase; color: var(--panel-ink-3); }
.tai-summary { font-size: 13.5px; color: var(--panel-ink); max-width: 78ch; margin: 0 0 var(--s3); }

.tai-q { border-top: 1px solid var(--panel-line); padding: var(--s3) 0; }
.tai-q:last-of-type { padding-bottom: 0; }
.tai-q-label { font-weight: 600; font-size: 13px; margin-bottom: 2px; }
.tai-why { font-size: 12px; color: var(--panel-ink-3); margin: 0 0 8px; max-width: 74ch; }
.tai-assume { font-size: 11.5px; color: var(--panel-ink-3); margin-top: 6px; font-family: var(--mono); }
.tai-assume b { color: var(--panel-ink-2); font-weight: 600; }
.tai-q input[type="text"] { max-width: 480px; }
.tai-q .seg button.on { background: color-mix(in srgb, var(--brand) 14%, var(--panel)); }
.tai-actions { display: flex; gap: var(--s2); margin-top: var(--s4); flex-wrap: wrap; }

/* ---------- Scenario Lab: A/B comparison ---------- */

.sim-badge.pin { background: color-mix(in srgb, var(--recent) 20%, var(--panel)); color: #8a6412; }
[data-theme="dark"] .sim-badge.pin { color: var(--recent); }

.ws-abrow { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 10px 0 4px; }
.ws-abrow:first-of-type { margin-top: 2px; }
.ws-cmp-sum { font-family: var(--mono); font-size: 11.5px; color: var(--panel-ink-2); }
.ws-cmp-vs { font: 700 10px/1 var(--mono); letter-spacing: 0.1em; color: var(--panel-ink-3); }
.ws-unpin { position: static; width: 22px; height: 22px; margin-left: auto; }

.ws-cmp { border: 1px solid var(--panel-line); border-radius: 10px; background: var(--panel); padding: 12px; margin: 12px 0 4px; }
.ws-cmp-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
table.data.cmp { min-width: 560px; }
table.data.cmp td.name { min-width: 150px; }
.cmp-good { color: #12855e; font-weight: 600; }
.cmp-bad { color: #a63b2c; font-weight: 600; }
[data-theme="dark"] .cmp-good { color: var(--certified); }
[data-theme="dark"] .cmp-bad { color: var(--bad); }

/* ---------- BESS controller toggle (LogIt Studio ws-tgl) ---------- */
.ws-tgl {
  position: relative; width: 30px; height: 17px; flex: none;
  border: 1px solid var(--input-line); border-radius: 99px;
  background: var(--inset); cursor: pointer; padding: 0;
  transition: background .16s, border-color .16s;
}
.ws-tgl::after {
  content: ''; position: absolute; top: 1px; left: 1px; width: 13px; height: 13px;
  border-radius: 50%; background: var(--panel-ink-3); transition: left .16s, background .16s;
}
.ws-tgl.on { background: color-mix(in srgb, var(--certified) 25%, var(--panel)); border-color: var(--certified); }
.ws-tgl.on::after { left: 14px; background: var(--certified); }

/* ---------- Plant controller panel ---------- */
.ws-ctrl { margin: 12px 0 4px; border: 1px solid var(--panel-line); border-radius: var(--r); background: var(--panel); }
.ws-ctrl > summary {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  padding: 10px 14px; cursor: pointer; list-style: none; border-radius: var(--r);
}
.ws-ctrl > summary::-webkit-details-marker { display: none; }
.ws-ctrl > summary::before {
  content: "▸"; color: var(--panel-ink-3); font-size: 11px; transition: transform .15s; display: inline-block;
}
.ws-ctrl[open] > summary::before { transform: rotate(90deg); }
.ws-ctrl > summary:hover { background: var(--overlay); }
.ws-ctrl > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.ws-ctrl-h { font-size: 12px; font-weight: 650; letter-spacing: .02em; color: var(--panel-ink); }
.ws-ctrl-sum { font-family: var(--mono); font-size: 11px; color: var(--panel-ink-3); }
.ws-ctrl-body { padding: 2px 14px 12px; }

/* One row per setting: name and what it is currently doing on the left,
   the control on the right. The subtitle is the value, not a restatement. */
.ws-crow {
  display: flex; align-items: flex-start; gap: 16px; justify-content: space-between;
  padding: 10px 0; border-top: 1px solid var(--panel-grid);
}
.ws-crow-t { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ws-crow-t b { font-size: 12px; font-weight: 600; color: var(--panel-ink); }
.ws-crow-t small { font-size: 11px; line-height: 1.45; color: var(--panel-ink-2); max-width: 62ch; }
.ws-crow.warn .ws-crow-t small { color: #8a6412; }
.ws-crow-c { display: flex; align-items: center; gap: 8px; flex: none; padding-top: 1px; }
.ws-ctrl-note {
  font-size: 11px; line-height: 1.5; color: var(--panel-ink-2); margin: 0 0 4px;
  padding: 8px 10px; background: var(--inset); border-radius: var(--r-sm); max-width: 72ch;
}
.seg.sm button { font-size: 11px; padding: 4px 9px; }

/* ---------- One node per machine ---------- */
.ws-ord { font-family: var(--mono); font-size: 10px; font-weight: 500; color: var(--panel-ink-3); }
.ws-libadd { display: flex; align-items: center; gap: 8px; flex: none; }
.ws-onbus {
  font-family: var(--mono); font-size: 10px; color: var(--panel-ink-3);
  background: var(--inset); border-radius: 99px; padding: 2px 7px; white-space: nowrap;
}
.btn.sm { font-size: 11px; padding: 4px 10px; }

/* ---------- Results rail: banded, on an aligned grid ---------- */
/* Cells sit on a uniform grid rather than wrapping flex, so columns line up
   across groups. Wrapping flex gave every group a different rhythm and the
   whole block read as a jumble. */
.ws-rail-h { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 10px; }
.ws-rail-input {
  padding: 10px 12px; margin-bottom: 4px;
  background: var(--inset); border-radius: var(--r-sm);
}
.ws-rgroup { border-top: 1px solid var(--panel-grid); padding-top: 10px; margin-top: 10px; }
.ws-rgroup h5 {
  margin: 0 0 9px; font-size: 10px; font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--panel-ink-3);
}
.ws-rcells {
  display: grid; gap: 14px 22px;
  grid-template-columns: repeat(auto-fill, minmax(178px, 1fr));
}
.ws-rail .st-cell { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.ws-rail .st-cell label { font-size: 10.5px; letter-spacing: .04em; text-transform: uppercase; color: var(--panel-ink-3); }
.ws-rail .st-cell b { font-family: var(--mono); font-size: 17px; font-weight: 600; color: var(--panel-ink); line-height: 1.2; }
.ws-rail .st-cell small { font-size: 11px; color: var(--panel-ink-2); line-height: 1.4; }
.ws-serve.inline { margin: 0; }

/* Fuel price is a band, so the control that sets it sits with the results */
.ws-fuelbasis { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ws-fuelbasis > label { font-size: 11px; color: var(--panel-ink-2); }
.ws-fb-to { font-size: 11px; color: var(--panel-ink-3); }

/* Example-brief banner: the brief is editable, and the point is that editing it works */
.banner.ok { background: color-mix(in srgb, var(--certified) 12%, var(--panel)); border-color: var(--certified); }
.banner-sub { display: block; margin-top: 5px; font-size: 11.5px; line-height: 1.5; color: var(--panel-ink-2); }

/* ---------- Vertical bus (narrow viewports) ---------- */
/* The bus turns a right angle and the plant stacks down the page, so a narrow
   window reads as a riser diagram rather than forcing an ever-taller zigzag. */
.ws-canvas.vert { padding-right: 12px; }
.ws-canvas.vert .ws-node { width: calc(100% - 84px); max-width: 420px; }
.ws-loadzone.vert {
  position: absolute; left: 66px; right: 12px; bottom: 8px; top: auto;
  width: auto; min-height: 0; flex-direction: row; flex-wrap: wrap;
  align-items: baseline; gap: 6px 14px;
}
.ws-loadzone.vert .ws-lz-sub { flex-basis: 100%; }

/* ---------- TethraAI staged progress ---------- */
/* Anchored beside the control that started the work, never in the page banner:
   an operator who scrolled down to press the button has no reason to scroll
   back up to find out whether anything happened. */
.tai-progress {
  border: 1px solid var(--panel-line); border-left: 3px solid var(--brand);
  border-radius: var(--r); background: var(--panel);
  padding: var(--s4) var(--s4) var(--s3); margin-bottom: var(--s4);
}
.tai-prog-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tai-prog-h b { font-size: 14px; }
.tai-elapsed {
  margin-left: auto; font-family: var(--mono); font-size: 12px;
  color: var(--panel-ink-2); background: var(--inset);
  padding: 2px 8px; border-radius: 99px;
}
.tai-prog-sub { margin: 6px 0 10px; font-size: 12px; line-height: 1.5; color: var(--panel-ink-2); max-width: 74ch; }
.tai-steps { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.tai-steps li { display: flex; align-items: center; gap: 9px; font-size: 12.5px; color: var(--panel-ink-3); }
.tai-steps li.done { color: var(--panel-ink-2); }
.tai-steps li.active { color: var(--panel-ink); font-weight: 550; }
.tai-step-m {
  flex: 0 0 auto; width: 16px; height: 16px; display: inline-flex;
  align-items: center; justify-content: center; font-size: 11px; color: var(--certified);
}
.spinner.sm { width: 11px; height: 11px; border-width: 2px; }

/* ---------- Inactive machines read as present but not engaged ---------- */
/* Dimmed enough to scan past, never so far that it cannot be read — an idle
   set is still part of the plant and its ratings still matter. A pack that is
   charging is NOT inactive: taking a charge is participating. */
.ws-node.off { opacity: 0.58; }
.ws-node.off .ws-live { color: var(--panel-ink-3); }
.ws-node.off:hover, .ws-node.off:focus-within { opacity: 1; }
.ws-live.low { color: var(--recent); }        /* running under its minimum load */
.ws-live.chg { color: var(--ws-chg); }
.ws-live.dis { color: var(--ws-bat); }

/* Unpin control on a quick-drag chip — remove without opening the library */
.ws-chip { position: relative; }
.ws-chip-x {
  margin-left: 7px; border: 0; background: transparent; cursor: pointer;
  color: var(--panel-ink-3); font-size: 13px; line-height: 1; padding: 0 1px;
  border-radius: 3px; opacity: 0; transition: opacity .12s, color .12s;
}
.ws-chip:hover .ws-chip-x, .ws-chip:focus-within .ws-chip-x { opacity: 1; }
.ws-chip-x:hover { color: var(--bad); background: color-mix(in srgb, var(--bad) 12%, transparent); }

/* Live one-liner; the unchanging provenance sits behind a disclosure so it
   cannot read as a stale caption under numbers that just moved. */
.ws-note { font-size: 11px; color: var(--panel-ink-3); margin: 10px 0 0; line-height: 1.5; }
.ws-note-more {
  border: 0; background: none; padding: 0 0 0 4px; cursor: pointer;
  color: var(--brand-text); font-size: 11px; text-decoration: underline;
}
.ws-basis { display: block; margin-top: 6px; max-width: 92ch; }

/* Each genset card carries its band colour, so a band in the chart can be tied
   back to the machine that produced it without counting positions. */
.ws-node.gen[style*="--node-accent"] { border-left-color: var(--node-accent); }

/* Delivered-fuel pricing: the proposal, and its working */

/* ---------- Baseline and counterfactuals ---------- */
.ws-cmp-wrap { margin-top: 14px; }
.ws-cmp-h { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 8px; }
.ws-cmp-h h4 { font-size: 13px; }
.ws-cmp-note { font-size: 11px; color: var(--recent); }
table.ws-cmp th { text-align: left; vertical-align: top; padding: 8px 12px 8px 0; }
table.ws-cmp th.base { border-bottom: 2px solid var(--brand); }
.ws-cmp-name { display: block; font-size: 12px; font-weight: 650; color: var(--panel-ink); }
.ws-cmp-sub { display: block; font-family: var(--mono); font-size: 10.5px; color: var(--panel-ink-3); max-width: 26ch; }
.ws-cmp-acts { display: flex; gap: 6px; margin-top: 4px; }
.ws-cmp-acts button {
  border: 1px solid var(--panel-line); background: var(--panel); border-radius: 4px;
  font-size: 10px; padding: 1px 6px; cursor: pointer; color: var(--panel-ink-2);
}
.ws-cmp-acts button:hover { border-color: var(--brand); color: var(--brand-text); }
table.ws-cmp td.n { font-family: var(--mono); text-align: right; white-space: nowrap; }
table.ws-cmp td.base { background: color-mix(in srgb, var(--brand) 5%, transparent); }

/* ---------- Intake: start-from row and the geocode readout ---------- */

/* Entry points sit under the input rather than in a distant button row —
   the operator is already looking at the box, so this is where "or start from
   something existing" has to be to get seen. */
.startrow {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 10px; padding-top: 12px; border-top: 1px solid var(--panel-line);
}
.startrow-lbl { font-size: 11.5px; font-weight: 600; color: var(--panel-ink-3); letter-spacing: .01em; }
.startrow-file {
  border: 1px dashed var(--panel-line); background: var(--panel); color: var(--panel-ink-2);
  border-radius: var(--r-sm); font: 500 12px/1 var(--font); padding: 7px 12px; cursor: pointer;
}
.startrow-file:hover { border-color: var(--brand); border-style: solid; color: var(--brand-text); background: var(--inset); }
.startrow-file:focus-visible, .seg button:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.geo-out { font-size: 11.5px; line-height: 1.5; margin-top: 6px; color: var(--panel-ink-3); }
.geo-out.ok { color: var(--panel-ink-2); }
.geo-out.ok strong { color: var(--panel-ink); font-weight: 600; }
.geo-out.warn { color: #8a6412; }  /* 4.62:1 on panel — same amber the crew-row warnings use */
.geo-out.busy { color: var(--panel-ink-3); }
.geo-src {
  display: inline-block; font: 500 10px/1 var(--mono); text-transform: uppercase; letter-spacing: .04em;
  padding: 2px 5px; border-radius: 3px; background: var(--inset); color: var(--panel-ink-3); vertical-align: 1px;
}

/* ---------- Report: derivation sections ---------- */
.sec-h { font-size: 13px; font-weight: 650; color: var(--panel-ink); margin: var(--s5) 0 var(--s2); }
.sec-p { font-size: 12.5px; line-height: 1.6; color: var(--panel-ink-2); margin-bottom: var(--s3); }
ol.method-steps { margin: 0 0 var(--s3) 0; padding-left: 22px; }
ol.method-steps li { font-size: 12.5px; line-height: 1.65; color: var(--panel-ink-2); margin-bottom: 7px; }
ol.method-steps li strong { color: var(--panel-ink); font-weight: 600; }
ol.method-steps li::marker { color: var(--panel-ink-3); font-family: var(--mono); font-size: 11px; }
.mini-bar { display: block; width: 100%; min-width: 70px; height: 7px; border-radius: 4px; background: var(--inset); overflow: hidden; }
.mini-bar i { display: block; height: 100%; background: var(--ws-sol); border-radius: 4px; }
th.bar-col, td:has(> .mini-bar) { width: 92px; }

/* A tap line carries power INTO the bus by default; a charging battery is the
   one node that reverses, so its dashes must run the other way. Colour comes
   from the flow (set inline), not the node, so the line and the card agree. */
.ws-flow.on.in { animation-direction: reverse; }
.ws-flownote {
  display: block; font: 500 9.5px/1.3 var(--mono); color: var(--panel-ink-3);
  letter-spacing: .01em; min-height: 12px; margin-top: 1px;
}
.ws-flownote.gone { visibility: hidden; }
/* --panel-grid is #eef1f4 and the canvas background is --inset, also #eef1f4:
   the crosshairs were drawn in exactly the colour they sat on and were
   literally invisible. Needs its own token, not a borrowed one. */
.ws-xhair { stroke: var(--ws-xhair); stroke-width: 1; fill: none; }

/* ---------- Build switcher: baseline and its counterfactuals ---------- */
.ws-bs {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin: 0 0 10px; padding-bottom: 10px; border-bottom: 1px solid var(--panel-line);
}
.ws-bs-tabs { display: flex; gap: 0; border: 1px solid var(--panel-line); border-radius: var(--r-sm); overflow: hidden; background: var(--panel); }
.ws-bs-tab {
  display: flex; flex-direction: column; gap: 1px; align-items: flex-start;
  border: 0; background: var(--panel); cursor: pointer; padding: 7px 14px; text-align: left;
}
.ws-bs-tab + .ws-bs-tab { border-left: 1px solid var(--panel-line); }
.ws-bs-tab:hover { background: var(--inset); }
.ws-bs-tab.on { background: color-mix(in srgb, var(--brand) 11%, var(--panel)); box-shadow: inset 0 -2px 0 var(--brand); }
.ws-bs-n { font: 600 12px/1.2 var(--font); color: var(--panel-ink-2); white-space: nowrap; }
.ws-bs-tab.on .ws-bs-n { color: var(--panel-ink); }
.ws-bs-tab.base .ws-bs-n::before {
  content: 'REF'; font: 600 8.5px/1 var(--mono); letter-spacing: .06em; color: var(--brand-text);
  background: color-mix(in srgb, var(--brand) 15%, transparent); border-radius: 3px;
  padding: 2px 4px; margin-right: 6px; vertical-align: 1px;
}
.ws-bs-g { font: 500 10px/1 var(--mono); color: var(--panel-ink-3); white-space: nowrap; }
.ws-bs-add, .ws-bs-x, .ws-bs-ren {
  border: 1px dashed var(--panel-line); background: var(--panel); color: var(--panel-ink-2);
  border-radius: var(--r-sm); font: 500 11.5px/1 var(--font); padding: 7px 11px; cursor: pointer;
}
.ws-bs-add:hover, .ws-bs-ren:hover { border-color: var(--brand); border-style: solid; color: var(--brand-text); }
.ws-bs-x:hover { border-color: var(--bad); border-style: solid; color: var(--bad); }
.ws-bs-full { font: 500 11px/1 var(--mono); color: var(--panel-ink-3); }
.ws-bs-tab:focus-visible, .ws-bs-add:focus-visible, .ws-bs-x:focus-visible, .ws-bs-ren:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Resume prompt — offered, never imposed. */
.banner.resume { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.banner.resume .resume-acts { margin-left: auto; display: flex; gap: 8px; }
.banner.resume .btn { padding: 6px 12px; font-size: 12px; }

/* A setpoint that cannot change the answer must not look like one that can.
   Dimmed rather than hidden: the value is still worth reading, because turning
   generator charging back on makes it govern again. */
.ws-spi.inert { opacity: .42; }
.ws-spi.inert .ws-spv button { cursor: not-allowed; }
.ws-spi.inert .ws-spv button:hover { background: inherit; border-color: var(--panel-line); color: inherit; }
.ws-polnote {
  margin-top: 7px; padding-top: 6px; border-top: 1px dashed var(--panel-line);
  font: 500 9.5px/1.45 var(--mono); color: var(--panel-ink-3);
}
.ws-polnote b { color: var(--panel-ink-2); font-weight: 700; }

/* Setpoint values are editable in place — type 25 rather than pressing + four
   times. Styled to read as the text it replaced, not as a form field. */
.ws-spnum { display: inline-flex; align-items: baseline; color: var(--panel-ink); font-weight: 700; }
.ws-spnum input {
  width: 3.6ch; padding: 1px 2px; border: 1px solid transparent; border-radius: 3px;
  background: transparent; color: inherit; font: 700 11px/1.2 var(--mono);
  text-align: right; -moz-appearance: textfield; appearance: textfield;
}
.ws-spnum input::-webkit-outer-spin-button, .ws-spnum input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ws-spnum input:hover:not(:disabled) { border-color: var(--panel-line); background: var(--panel); }
.ws-spnum input:focus { outline: none; border-color: var(--brand); background: var(--panel); }
.ws-spnum input:disabled { cursor: not-allowed; }

/* Deferred re-dispatch: mark the canvas so a pending edit never reads as an
   answered one. Subtle — this is a 400 ms state, not an operation. */
.ws-canvas.pending { box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--brand) 35%, transparent); }
.ws-canvas.pending::before {
  content: 're-dispatching…';
  position: absolute; right: 10px; top: 8px; z-index: 3;
  font: 600 9.5px/1 var(--mono); letter-spacing: .06em; text-transform: uppercase;
  color: var(--brand-text); background: var(--panel); border: 1px solid var(--panel-line);
  border-radius: 4px; padding: 4px 7px;
}

/* Charge sources, as chips on the pack itself. Each is a switch: what can put
   energy in, and across what band. Replaces a sentence that said the same
   thing in more words and could not be acted on. */
.ws-srcs { display: flex; gap: 5px; margin-top: 7px; }
.ws-src {
  flex: 1 1 0; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 1px;
  border: 1px solid var(--panel-line); border-radius: 5px; background: var(--panel);
  padding: 4px 5px; cursor: pointer; opacity: .45;
}
.ws-src b { font: 700 8.5px/1 var(--mono); letter-spacing: .08em; color: var(--panel-ink-3); }
.ws-src span { font: 600 9px/1.1 var(--mono); color: var(--panel-ink-3); white-space: nowrap; }
.ws-src.on { opacity: 1; }
.ws-src.gen.on { border-color: var(--ws-gen); background: color-mix(in srgb, var(--ws-gen) 10%, var(--panel)); }
.ws-src.gen.on b, .ws-src.gen.on span { color: #9c4413; }        /* 4.61:1 on the tinted chip */
.ws-src.sol.on { border-color: var(--ws-sol); background: color-mix(in srgb, var(--ws-sol) 13%, var(--panel)); }
.ws-src.sol.on b, .ws-src.sol.on span { color: #7a5400; }        /* 5.34:1 on the tinted chip */
.ws-src:hover { border-color: var(--panel-ink-3); }
.ws-src:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* The step marker sits on a white panel; the default spinner is white on
   white and rendered as an empty gap, so the active step looked stalled. */
.tai-steps .spinner, .tai-step-m .spinner {
  border-color: color-mix(in srgb, var(--brand) 25%, transparent);
  border-top-color: var(--brand);
}

/* Workflow-level "AI is working" — the rail is on screen the whole time, so
   it is where a long background job has to be visible from. */
.rail-working {
  display: flex; align-items: center; gap: 10px;
  margin: var(--s3) var(--s4) 0; padding: 9px 11px;
  border: 1px solid color-mix(in srgb, var(--brand) 30%, var(--panel-line));
  border-radius: var(--r-sm); background: color-mix(in srgb, var(--brand) 7%, var(--panel));
}
.rail-working b { display: block; font-size: 11.5px; font-weight: 650; color: var(--brand-text); }
.rail-working small { display: block; font: 500 10px/1.3 var(--mono); color: var(--panel-ink-3); }
.rail-working-dot {
  flex: 0 0 auto; width: 8px; height: 8px; border-radius: 50%; background: var(--brand);
  animation: railpulse 1.4s ease-in-out infinite;
}
@keyframes railpulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .35; transform: scale(.7); } }
.step.working .step-dot {
  border-color: var(--brand); color: var(--brand-text);
  animation: railpulse 1.4s ease-in-out infinite;
}
@media (prefers-reduced-motion: reduce) {
  .rail-working-dot, .step.working .step-dot { animation: none; }
}

/* ---------- Machine cards: instrument, not label ----------
   Each card is one physical machine. Before this the content sat in the left
   quarter of a card stretched to 420px in vertical mode, which read as broken
   rather than spacious. Now the width is used: a header row that fills it, a
   live readout, and a utilisation bar answering "is this being worked well"
   without the reader doing arithmetic on two numbers. */
.ws-nhead { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.ws-nhead b { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ws-nhead .ws-kicon { position: static; flex: 0 0 auto; }
.ws-nhead .ws-x { position: static; flex: 0 0 auto; }
.ws-info {
  flex: 0 0 auto; width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid var(--panel-line); background: var(--panel);
  font: 700 10px/1 var(--mono); color: var(--panel-ink-3); cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.ws-info:hover { border-color: var(--brand); color: var(--brand-text); background: var(--inset); }
.ws-info:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

.ws-nread { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; }
.ws-nread .ws-live { flex: 0 0 auto; }
.ws-nread .ws-flownote { flex: 1 1 auto; text-align: right; }

.ws-util {
  position: relative; height: 5px; margin: 5px 0 4px;
  border-radius: 3px; background: var(--inset); overflow: hidden;
}
.ws-util > i { display: block; height: 100%; border-radius: 3px; background: var(--node-accent, var(--panel-ink-3)); transition: width .18s linear; }
.ws-node.gen .ws-util > i { background: var(--node-accent, var(--ws-gen)); }
.ws-node.sol .ws-util > i { background: var(--ws-sol); }
.ws-node.bat .ws-util > i { background: var(--ws-bat); }
/* Minimum-load tick: below it a set is accumulating damage the fuel number
   does not show, so the threshold belongs on the gauge, not just in a tooltip. */
.ws-util > u { position: absolute; top: -1px; width: 1px; height: 7px; background: var(--bad); opacity: .55; }
.ws-node.off .ws-util > i { opacity: .35; }

/* Vertical riser: use the width rather than stretching to fill it. */
.ws-canvas.vert .ws-node { width: calc(100% - 84px); max-width: 300px; }

/* ---------- Charge sources: one block per source, same shape ---------- */
.ws-chg { display: flex; flex-direction: column; gap: 6px; margin-top: 8px; }
.ws-chgblk { border: 1px solid var(--panel-line); border-radius: 6px; background: var(--panel); overflow: hidden; }
.ws-chgtgl {
  display: flex; align-items: center; gap: 7px; width: 100%;
  border: 0; background: var(--inset); cursor: pointer; padding: 5px 8px; text-align: left;
}
.ws-chgtgl b { font: 700 9.5px/1 var(--mono); letter-spacing: .07em; text-transform: uppercase; color: var(--panel-ink-2); }
.ws-chgtgl em { margin-left: auto; font: 500 9px/1 var(--mono); font-style: normal; color: var(--panel-ink-3); }
/* An actual switch, so it is obvious it toggles. The old chip relied on
   opacity alone and read as a status light rather than a control. */
.ws-chgsw {
  flex: 0 0 auto; width: 22px; height: 12px; border-radius: 7px; position: relative;
  background: var(--panel-ink-3); opacity: .45; transition: background .15s, opacity .15s;
}
.ws-chgsw::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 8px; height: 8px;
  border-radius: 50%; background: #fff; transition: transform .15s;
}
.ws-chgblk.on .ws-chgsw { opacity: 1; }
.ws-chgblk.on .ws-chgsw::after { transform: translateX(10px); }
.ws-chgblk.gen.on .ws-chgsw { background: var(--ws-gen); }
.ws-chgblk.sol.on .ws-chgsw { background: var(--ws-sol); }
.ws-chgblk.gen.on { border-color: color-mix(in srgb, var(--ws-gen) 45%, var(--panel-line)); }
.ws-chgblk.sol.on { border-color: color-mix(in srgb, var(--ws-sol) 45%, var(--panel-line)); }
.ws-chgblk.on .ws-chgtgl b { color: var(--panel-ink); }
.ws-chgtgl:hover { background: var(--overlay-hover); }
.ws-chgtgl:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.ws-chgsp { display: flex; gap: 16px; padding: 5px 8px 6px; }
.ws-chgsp label {
  display: inline-flex; align-items: baseline; gap: 4px;
  font: 500 9.5px/1.2 var(--mono); color: var(--panel-ink-3);
}
.ws-chgsp label > span { flex: 0 0 auto; }
.ws-chgsp input {
  width: 3.2ch; padding: 1px 2px; text-align: right;
  border: 1px solid transparent; border-radius: 3px; background: transparent;
  font: 700 10.5px/1.2 var(--mono); color: var(--panel-ink);
  -moz-appearance: textfield; appearance: textfield;
}
.ws-chgsp input::-webkit-outer-spin-button, .ws-chgsp input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ws-chgsp input:hover:not(:disabled) { border-color: var(--panel-line); background: var(--inset); }
.ws-chgsp input:focus { outline: none; border-color: var(--brand); background: var(--panel); }
.ws-chgblk:not(.on) .ws-chgsp { opacity: .4; }
.ws-chgblk:not(.on) .ws-chgsp input { cursor: not-allowed; }

/* Plant-wide staging setpoint, on the lead genset card. */
.ws-genpol {
  display: flex; align-items: baseline; gap: 8px; margin-top: 7px;
  padding-top: 6px; border-top: 1px dashed var(--panel-line);
}
.ws-genpol label { display: inline-flex; align-items: baseline; gap: 4px; font: 500 9.5px/1.2 var(--mono); color: var(--panel-ink-3); }
.ws-genpol em { margin-left: auto; font: 500 9px/1 var(--mono); font-style: normal; color: var(--panel-ink-3); opacity: .7; }
.ws-genpol input {
  width: 3.2ch; padding: 1px 2px; text-align: right; border: 1px solid transparent; border-radius: 3px;
  background: transparent; font: 700 10.5px/1.2 var(--mono); color: var(--panel-ink);
  -moz-appearance: textfield; appearance: textfield;
}
.ws-genpol input::-webkit-outer-spin-button, .ws-genpol input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ws-genpol input:hover { border-color: var(--panel-line); background: var(--inset); }
.ws-genpol input:focus { outline: none; border-color: var(--brand); background: var(--panel); }

/* ---------- Asset datasheet: the data behind one machine ---------- */
.sheet-wrap { position: fixed; inset: 0; z-index: 120; }
.sheet-scrim { position: absolute; inset: 0; background: rgba(12, 38, 51, .5); }
.sheet-card {
  position: relative; margin: 4vh auto; width: min(620px, 94vw); max-height: 92vh; overflow-y: auto;
  background: var(--panel); border-radius: var(--r-lg); padding: var(--s5) var(--s5) var(--s6);
  box-shadow: 0 18px 60px rgba(0,0,0,.32);
}
.sheet-x { position: absolute; top: var(--s4); right: var(--s4); }
.sheet-card h3 { font-size: 16px; padding-right: 84px; }
.sheet-sub { font: 500 11px/1.4 var(--mono); color: var(--panel-ink-3); margin-bottom: var(--s4); }
.sheet-h { font-size: 12px; font-weight: 650; color: var(--panel-ink); margin: var(--s5) 0 var(--s2); }
.sheet-grid2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 6px 18px; }
.sheet-grid2 > div { display: flex; gap: 8px; align-items: baseline; border-bottom: 1px solid var(--panel-line); padding-bottom: 4px; }
.sheet-grid2 span { font: 600 9px/1.4 var(--mono); text-transform: uppercase; letter-spacing: .05em; color: var(--panel-ink-3); flex: 1 1 auto; }
.sheet-grid2 b { font-size: 11.5px; font-weight: 650; color: var(--panel-ink); white-space: nowrap; }
.sheet-svg { display: block; width: 100%; height: auto; margin-bottom: 4px; }
.sheet-axis { stroke: var(--panel-ink-3); stroke-width: 1; }
.sheet-grid { stroke: var(--panel-grid); stroke-width: 1; }
.sheet-ax { font: 500 8px var(--mono); fill: var(--panel-ink-3); }
.sheet-line { stroke: var(--ws-gen); stroke-width: 2; fill: none; }
.sheet-line.spec { stroke: var(--ts-fuel); }
.sheet-pt { fill: var(--panel); stroke: var(--ws-gen); stroke-width: 2; }
.sheet-floor { fill: color-mix(in srgb, var(--bad) 9%, transparent); }
.sheet-note { font-size: 11.5px; line-height: 1.6; color: var(--panel-ink-2); margin-bottom: var(--s2); }
.sheet-note b { color: var(--panel-ink); font-weight: 650; }
.ws-bs-d { font-style: normal; font-weight: 700; color: var(--bad); }
.ws-bs-d.good { color: var(--certified); }

/* ---------- Numeric entry, used everywhere a value is typed ----------
   One component so the fuel price, the SOC setpoints and the staging
   threshold all behave identically: unit inside the box, type the number you
   want, commit on Enter or blur. */
.numfield {
  display: inline-flex; align-items: baseline; gap: 1px;
  border: 1px solid var(--input-line); border-radius: var(--r-sm);
  background: var(--panel); padding: 3px 7px;
  font-family: var(--mono);
}
.numfield:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px color-mix(in srgb, var(--brand) 16%, transparent); }
.numfield-p, .numfield-s { font: 500 11px/1 var(--mono); color: var(--panel-ink-3); }
.numfield input {
  border: 0; background: transparent; padding: 0; text-align: right;
  font: 700 12.5px/1.2 var(--mono); color: var(--panel-ink);
  -moz-appearance: textfield; appearance: textfield;
}
.numfield input:focus { outline: none; }
.numfield input::-webkit-outer-spin-button, .numfield input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* ---------- Boot cover ----------
   The app used to paint an empty intake, then jump into a restored session a
   beat later, which reads as a glitch. The cover holds until restore has
   finished, so the app is revealed once, already in the right place. */
.boot {
  position: fixed; inset: 0; z-index: 200; display: grid; place-items: center;
  background: var(--navy); opacity: 1; transition: opacity .38s ease;
}
.boot.gone { opacity: 0; pointer-events: none; }
.boot-card { display: flex; flex-direction: column; align-items: center; gap: 14px; padding: 0 24px; text-align: center; }
.boot-mark { height: 46px; width: auto; animation: bootpulse 1.9s ease-in-out infinite; }
.boot-name { font: 700 15px/1.1 var(--font); letter-spacing: .14em; color: #fff; }
.boot-name small {
  display: block; margin-top: 4px; font: 500 9.5px/1 var(--mono);
  letter-spacing: .22em; color: var(--cyan); text-transform: uppercase;
}
.boot-bar { width: 168px; height: 3px; border-radius: 2px; background: rgba(255,255,255,.16); overflow: hidden; }
.boot-bar i {
  display: block; height: 100%; width: 40%; border-radius: 2px;
  background: linear-gradient(90deg, transparent, var(--cyan), transparent);
  animation: bootslide 1.15s ease-in-out infinite;
}
.boot-msg { font: 500 11.5px/1.4 var(--mono); color: rgba(255,255,255,.62); margin: 0; }
.boot.failed .boot-bar { display: none; }
.boot.failed .boot-msg { color: #ffb4b0; }
.boot.failed .boot-mark { animation: none; }
@keyframes bootpulse { 0%, 100% { opacity: 1; } 50% { opacity: .55; } }
@keyframes bootslide { 0% { transform: translateX(-110%); } 100% { transform: translateX(310%); } }
@media (prefers-reduced-motion: reduce) {
  .boot-mark, .boot-bar i { animation: none; }
  .boot-bar i { width: 100%; }
}
