(function(){
/* FORGE — Aetherion (Space tenant) traveler station 4: Test
   Wave A1 / Space / Agent #14.

   Single file. Babel-standalone in-browser pattern. NO ES imports / exports.
   Globals attach to window at end-of-file. Route registered via
   registerSpaceRoute() — falls back to registerPumpRoute() if the space
   route registry has not been loaded yet so the screen is reachable in
   either build order.

   Spec source-of-truth:
     forge-web/demo/tenant-spec/space.md
       §8 station (s4) — TVAC + EMI + vibration → flight acceptance
       §7 — qualification artefacts at the ACC gate
       §9 — ledger event names (QUAL_PASSED, EMC_PASSED, ACC_PASSED)
       §10 — cool-blue palette, mono everywhere, units mandatory, no emoji
   Architectural reference:
     forge-web/client/pump/act4-screens.jsx → ScreenStationTest
     forge-web/client/space/cross-cutting.jsx → AE* atoms (CSS vars)
     forge-web/client/space/ledger-events.jsx → emitAetherionLedger()

   Component: ScreenAetherionTest (route /traveler/test, mode traveler).

   Four-tab interface — TVAC · EMI · Vibration · Shock — each with:
     · binding test parameters (numeric, with units in mono)
     · real-time chart (TVAC profile, EMI spectra, PSD, SRS)
     · pass/fail criteria block
     · "Run sequence" button — emits QUAL_PASSED on completion
       (EMI also emits EMC_PASSED per §9 catalog)
   Plus:
     · "Generate qual report PDF" button → toast banner
     · "Advance to acceptance" gate — disabled until all four green;
       on confirm, emits ACC_PASSED and surfaces the receipt hash inline.

   Style: cool blues, mono digits, units everywhere. No emoji.
*/

const { useState: uSAT, useEffect: uEAT, useMemo: uMAT, useRef: uRAT } = React;

// ──────────────────────────────────────────────────────────────────────
// Style atoms — rebound to the canonical light theme tokens (Wave 12D).
// The traveler station now reads as a sibling of dashboard.jsx (cream
// surface, dark ink, mission-blue accent). cross-cutting.jsx still
// injects the legacy --space-* vars but flipped to the light palette.
// ──────────────────────────────────────────────────────────────────────
const AT_BG       = "var(--bg-0)";       // cream page bg
const AT_BG2      = "var(--bg-1)";       // elevated card surface
const AT_FG       = "var(--ink)";        // primary text (dark)
const AT_ACCENT   = "#2D5BEA";           // saturated blue on cream
const AT_MONO     = "var(--ink)";        // mono digits (dark)
const AT_SLATE    = "var(--ink-3)";      // muted labels
const AT_LINE     = "var(--line)";       // hairline divider
const AT_PASS     = "var(--ok)";
const AT_FAIL     = "var(--err)";
const AT_WARN     = "var(--warn)";
const AT_FONT_UI  = "var(--font-ui)";
const AT_FONT_MONO= "var(--font-mono)";

// Hoisted style atoms — extracted from inline JSX for react-doctor compliance.
const _at_metricValueBase = {
  display: "inline-flex", alignItems: "baseline", gap: 5,
  fontFamily: AT_FONT_MONO, fontSize: 18, fontWeight: 600,
  fontFeatureSettings: '"tnum" 1',
};
const _at_tabBtnBase = {
  padding: "12px 18px",
  border: "none",
  fontFamily: AT_FONT_MONO, fontSize: 12,
  letterSpacing: "0.10em", textTransform: "uppercase",
  cursor: "pointer",
  display: "inline-flex", alignItems: "center", gap: 8,
};
const _at_statusPillBase = {
  fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.06em",
  textTransform: "uppercase",
  padding: "1px 6px", borderRadius: 3,
};
const _at_criteriaRowBase = {
  display: "flex", alignItems: "center", gap: 8, padding: "5px 0",
  fontFamily: AT_FONT_UI, fontSize: 12, color: AT_FG,
};
const _at_runBtnBase = {
  height: 32, padding: "0 18px",
  borderRadius: 4,
  fontFamily: AT_FONT_MONO, fontSize: 12,
  letterSpacing: "0.06em", textTransform: "uppercase",
  fontWeight: 600,
};
const _at_headerBadgeBase = {
  display: "inline-flex", alignItems: "center", justifyContent: "center",
  width: 32, height: 32, borderRadius: "50%",
  background: "rgba(79,143,255,0.16)", color: AT_ACCENT,
  border: "1px solid rgba(79,143,255,0.30)",
  fontFamily: AT_FONT_MONO, fontSize: 12, fontWeight: 600, letterSpacing: "0.04em",
};
const _at_seqTagBase = {
  marginLeft: 6, padding: "1px 6px",
  border: "1px solid " + AT_LINE, borderRadius: 3,
  fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE, letterSpacing: "0.04em",
};
const _at_travelerLabelBase = {
  display: "inline-flex", alignItems: "center", gap: 8,
  fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE,
  letterSpacing: "0.10em", textTransform: "uppercase",
};
const _at_toastRowBase = {
  display: "flex", alignItems: "center", gap: 10, padding: "8px 12px",
  borderRadius: 3, fontFamily: AT_FONT_UI, fontSize: 12,
  color: AT_FG, lineHeight: 1.4,
};
const _at_toastKindBase = {
  fontFamily: AT_FONT_MONO, fontSize: 12,
  letterSpacing: "0.10em", textTransform: "uppercase",
  padding: "1px 6px", borderRadius: 3,
  background: "rgba(0,0,0,0.18)", color: AT_FG, flexShrink: 0,
};
const _at_toastCloseBtn = {
  background: "transparent", border: "1px solid " + AT_LINE,
  color: AT_SLATE, fontFamily: AT_FONT_MONO, fontSize: 12,
  padding: "1px 6px", borderRadius: 3, cursor: "pointer", flexShrink: 0,
};
const _at_reportBtn = {
  height: 32, padding: "0 16px", background: "transparent",
  color: AT_ACCENT, border: "1px solid " + AT_ACCENT, borderRadius: 4,
  fontFamily: AT_FONT_MONO, fontSize: 12,
  letterSpacing: "0.06em", textTransform: "uppercase", cursor: "pointer",
};
const _at_advanceBtnBase = {
  height: 32, padding: "0 18px",
  borderRadius: 4,
  fontFamily: AT_FONT_MONO, fontSize: 12,
  letterSpacing: "0.06em", textTransform: "uppercase",
  fontWeight: 600,
};

// ──────────────────────────────────────────────────────────────────────
// Tab catalog — binding parameters per §7. Each entry carries the
// numeric envelope, the canonical pass/fail copy, and the QUAL ledger
// payload that lands in the ledger when the operator runs the sequence.
// ──────────────────────────────────────────────────────────────────────
const AT_TABS = [
  {
    id: "tvac",
    label: "TVAC",
    title: "Thermal-vacuum cycle",
    sub:   "ECSS-E-ST-10-03 · NASA-STD-7001 envelope",
    refid: "TVAC-AET-014R",
    duration_s: 38,
    qualEvent: "QUAL_PASSED",
    qualSummary: "TVAC qual at -40 / +85 C across 24 cycles passed",
    params: [
      { label: "Temperature low",  value: "-40.0",  unit: "C"     },
      { label: "Temperature high", value: "+85.0",  unit: "C"     },
      { label: "Cycle count",      value: "24",     unit: "cycles"},
      { label: "Ramp rate",        value: "1.4",    unit: "C/min" },
      { label: "Dwell time",       value: "90",     unit: "min"   },
      { label: "Vacuum target",    value: "1.0e-5", unit: "torr"  },
      { label: "Pump-down hold",   value: "12",     unit: "h"     },
      { label: "Strain-gauge log", value: "1.0",    unit: "Hz"    },
    ],
    criteria: [
      "Pre-load loss < 25 microns at any cycle",
      "Chamber pressure holds < 5e-5 torr for 24 h",
      "Heater duty < 78 % of bus power across cycle",
      "No leak signature on RGA mass scan",
    ],
  },
  {
    id: "emi",
    label: "EMI",
    title: "EMI / EMC spectra",
    sub:   "MIL-STD-461G · Anechoic-South chamber",
    refid: "EMI-AET-008",
    duration_s: 26,
    qualEvent: "QUAL_PASSED",
    qualSummary: "EMI / EMC qual per MIL-STD-461G CE/CS/RE/RS passed",
    secondary: "EMC_PASSED",
    secondarySummary: "EMI / EMC per MIL-STD-461G passed",
    params: [
      { label: "CE102 limit",      value: "94.0",   unit: "dBuV"  },
      { label: "CE102 floor",      value: "30.0",   unit: "dBuV"  },
      { label: "CE102 sweep",      value: "10k-10M",unit: "Hz"    },
      { label: "RE102 limit",      value: "44.0",   unit: "dBuV/m"},
      { label: "RE102 floor",      value: "20.0",   unit: "dBuV/m"},
      { label: "RE102 sweep",      value: "10k-18G",unit: "Hz"    },
      { label: "Antenna distance", value: "1.000",  unit: "m"     },
      { label: "Chamber margin",   value: "+6.0",   unit: "dB"    },
    ],
    criteria: [
      "CE102 conducted emission peak < 94 dBuV",
      "RE102 radiated emission peak < 44 dBuV/m",
      "Susceptibility tests show no upset on bus",
      "Self-emission floor > 6 dB below limit line",
    ],
  },
  {
    id: "vibration",
    label: "Vibration",
    title: "Random + sine vibration",
    sub:   "Falcon 9 user guide envelope · GEVS +3 dB",
    refid: "VIB-AET-011",
    duration_s: 22,
    qualEvent: "QUAL_PASSED",
    qualSummary: "Vibration qual at GEVS+3 dB envelope passed",
    params: [
      { label: "Sine sweep low",   value: "5",      unit: "Hz"      },
      { label: "Sine sweep high",  value: "2000",   unit: "Hz"      },
      { label: "Sweep rate",       value: "2.0",    unit: "oct/min" },
      { label: "Random PSD floor", value: "0.020",  unit: "g^2/Hz"  },
      { label: "Random PSD peak",  value: "0.160",  unit: "g^2/Hz"  },
      { label: "Random duration",  value: "120",    unit: "s/axis"  },
      { label: "Grms input",       value: "14.1",   unit: "Grms"    },
      { label: "Axes",             value: "3",      unit: "X/Y/Z"   },
    ],
    criteria: [
      "First-mode shift < 5 % across all axes",
      "No discontinuities in pre/post low-level sine",
      "No fastener loosening at post-test torque audit",
      "No visible damage at 10x optical inspection",
    ],
  },
  {
    id: "shock",
    label: "Shock",
    title: "Pyroshock survival",
    sub:   "ECSS-E-ST-10-03 · half-sine SRS",
    refid: "SHK-AET-004",
    duration_s: 18,
    qualEvent: "QUAL_PASSED",
    qualSummary: "Pyroshock qual at 1500 g half-sine SRS passed",
    params: [
      { label: "Peak acceleration", value: "1500", unit: "g"      },
      { label: "Pulse duration",    value: "0.5",  unit: "ms"     },
      { label: "SRS knee frequency",value: "1000", unit: "Hz"     },
      { label: "SRS roll-off",      value: "+9",   unit: "dB/oct" },
      { label: "Q factor",          value: "10",   unit: ""       },
      { label: "Hits per axis",     value: "3",    unit: "shots"  },
      { label: "Axes",              value: "3",    unit: "X/Y/Z"  },
      { label: "Tolerance band",    value: "+/-6", unit: "dB"     },
    ],
    criteria: [
      "SRS envelope met within +/-6 dB at all frequencies",
      "Functional check passes immediately post-shot",
      "No relay chatter on power monitor channels",
      "Optical bench alignment within 25 microns",
    ],
  },
];
const AT_TAB_BY_ID = AT_TABS.reduce((acc, t) => { acc[t.id] = t; return acc; }, {});

// ──────────────────────────────────────────────────────────────────────
// Helpers — toast hook, ledger emit, inline numeric pair (val + unit)
// ──────────────────────────────────────────────────────────────────────
function _atUseToast() {
  const [toast, setToast] = uSAT(null);
  const timerRef = uRAT(null);
  function flash(kind, msg, ms) {
    if (timerRef.current) clearTimeout(timerRef.current);
    setToast({ kind, msg });
    timerRef.current = setTimeout(() => { setToast(null); timerRef.current = null; }, ms || 3200);
  }
  function dismiss() {
    if (timerRef.current) clearTimeout(timerRef.current);
    timerRef.current = null;
    setToast(null);
  }
  uEAT(() => () => { if (timerRef.current) clearTimeout(timerRef.current); }, []);
  return { toast, flash, dismiss };
}

// Safely call emitAetherionLedger if loaded. Returns receipt or null.
function _atEmit(eventName, payload) {
  try {
    if (typeof emitAetherionLedger === "function") return emitAetherionLedger(eventName, payload);
  } catch (e) { if (typeof console !== "undefined") console.warn("traveler-test:", e); }
  return null;
}

// Numeric pair: value (mono) + unit (smaller mono, slate)
function _AtNum({ value, unit, color }) {
  return (
    <span style={{
      display: "inline-flex", alignItems: "baseline", gap: 4,
      fontFamily: AT_FONT_MONO,
      fontFeatureSettings: '"tnum" 1',
      color: color || AT_MONO,
    }}>
      <span>{value}</span>
      {unit ? <span style={{ fontSize: 12, color: AT_SLATE, letterSpacing: "0.04em" }}>{unit}</span> : null}
    </span>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Subviz · TVAC profile (sawtooth across 24 cycles)
// ──────────────────────────────────────────────────────────────────────
function _AtTvacProfile() {
  const W = 520, H = 200, padL = 32, padR = 10, padT = 14, padB = 24;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const tMin = -55, tMax = 100, cycleN = 24, ptsPerCycle = 8;
  const totalPts = cycleN * ptsPerCycle;
  const sx = i => padL + (i / (totalPts - 1)) * innerW;
  const sy = t => padT + innerH - ((t - tMin) / (tMax - tMin)) * innerH;
  const profile = [];
  for (let c = 0; c < cycleN; c++) {
    profile.push(-40, -40, 20, 85, 85, 20, -40, -40);
  }
  const path = profile.map((t, i) => (i === 0 ? "M" : "L") + sx(i).toFixed(1) + "," + sy(t).toFixed(1)).join(" ");
  // Highlight historic cycle-14 chatter from §6
  const chatterIx = (14 - 1) * ptsPerCycle + 3;
  const cx = sx(chatterIx), cy = sy(85);

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H}
         style={{ background: AT_BG, border: "1px solid " + AT_LINE, borderRadius: 4 }}>
      {[-40, 0, 40, 85].map(t => (
        <g key={"h" + t}>
          <line x1={padL} y1={sy(t)} x2={W - padR} y2={sy(t)} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={padL - 4} y={sy(t) + 4} fontSize="9" textAnchor="end"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{t} C</text>
        </g>
      ))}
      <rect x={padL} y={sy(85)} width={innerW} height={sy(-40) - sy(85)} fill="rgba(79,143,255,0.04)" />
      <path d={path} stroke={AT_ACCENT} strokeWidth="1.4" fill="none" />
      <circle cx={cx} cy={cy} r="4" fill={AT_WARN} stroke={AT_BG} strokeWidth="1.2" />
      <text x={cx + 8} y={cy - 8} fontSize="10" fontFamily={AT_FONT_MONO} fill={AT_WARN}>
        cycle 14 · re-test in flight
      </text>
      <text x="6" y="14" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_SLATE}>T (C)</text>
      <text x={W - padR - 4} y={H - 6} fontSize="9.5" textAnchor="end"
            fontFamily={AT_FONT_MONO} fill={AT_SLATE}>cycle 1 to 24</text>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Subviz · EMI spectrum (CE102 + RE102 stacked traces, log-x)
// ──────────────────────────────────────────────────────────────────────
function _AtEmiSpectrum() {
  const W = 520, H = 200, padL = 38, padR = 10, padT = 14, padB = 26;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const fMin = 4, fMax = 10.26, lvMin = 0, lvMax = 100;
  const sx = lf => padL + ((lf - fMin) / (fMax - fMin)) * innerW;
  const sy = v => padT + innerH - ((v - lvMin) / (lvMax - lvMin)) * innerH;
  const ce102 = [[4.0,30],[4.6,40],[5.4,56],[6.0,62],[6.8,70],[7.2,64],
                 [7.6,58],[8.0,50],[8.4,44],[9.0,38],[9.6,34],[10.0,32]];
  const re102 = [[5.0,22],[6.0,28],[7.0,34],[7.6,38],[8.0,32],[8.4,30],
                 [9.0,26],[9.4,24],[9.8,22],[10.0,22]];
  const limitCE = [[4.0,94],[10.0,94]];
  const limitRE = [[4.0,44],[10.26,44]];
  const pf = arr => arr.map((p, i) => (i === 0 ? "M" : "L") + sx(p[0]).toFixed(1) + "," + sy(p[1]).toFixed(1)).join(" ");

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H}
         style={{ background: AT_BG, border: "1px solid " + AT_LINE, borderRadius: 4 }}>
      {[0, 20, 40, 60, 80, 100].map(v => (
        <g key={"g" + v}>
          <line x1={padL} y1={sy(v)} x2={W - padR} y2={sy(v)} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={padL - 4} y={sy(v) + 4} fontSize="9" textAnchor="end"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{v}</text>
        </g>
      ))}
      {[4, 5, 6, 7, 8, 9, 10].map(d => (
        <g key={"x" + d}>
          <line x1={sx(d)} y1={padT} x2={sx(d)} y2={H - padB} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={sx(d)} y={H - padB + 12} fontSize="9" textAnchor="middle"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>1e{d}</text>
        </g>
      ))}
      <path d={pf(limitCE)} stroke={AT_FAIL} strokeWidth="1" fill="none" strokeDasharray="4 3" />
      <path d={pf(limitRE)} stroke={AT_WARN} strokeWidth="1" fill="none" strokeDasharray="4 3" />
      <path d={pf(ce102)}   stroke={AT_ACCENT} strokeWidth="1.4" fill="none" />
      <path d={pf(re102)}   stroke={AT_PASS}   strokeWidth="1.4" fill="none" />
      <text x="6" y="14" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_SLATE}>dBuV</text>
      <text x={W - padR - 4} y={H - 6} fontSize="9.5" textAnchor="end"
            fontFamily={AT_FONT_MONO} fill={AT_SLATE}>Hz (log)</text>
      <g transform={`translate(${W - 200}, 18)`}>
        <rect x="0" y="-8" width="188" height="56" fill={AT_BG2} stroke={AT_LINE} rx="3" />
        <line x1="6"  y1="0"  x2="22" y2="0"  stroke={AT_ACCENT} strokeWidth="1.4" />
        <text x="28" y="3"  fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_FG}>CE102 measured</text>
        <line x1="6"  y1="14" x2="22" y2="14" stroke={AT_PASS}   strokeWidth="1.4" />
        <text x="28" y="17" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_FG}>RE102 measured</text>
        <line x1="6"  y1="28" x2="22" y2="28" stroke={AT_FAIL}   strokeWidth="1" strokeDasharray="4 3" />
        <text x="28" y="31" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_FG}>CE102 limit (94)</text>
        <line x1="6"  y1="42" x2="22" y2="42" stroke={AT_WARN}   strokeWidth="1" strokeDasharray="4 3" />
        <text x="28" y="45" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_FG}>RE102 limit (44)</text>
      </g>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Subviz · Vibration random PSD breakpoints (log-log)
// ──────────────────────────────────────────────────────────────────────
function _AtVibPSD() {
  const W = 520, H = 200, padL = 42, padR = 10, padT = 14, padB = 26;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const fMin = Math.log10(5), fMax = Math.log10(2000);
  const pMin = Math.log10(0.01), pMax = Math.log10(0.5);
  const sx = f => padL + ((Math.log10(f) - fMin) / (fMax - fMin)) * innerW;
  const sy = p => padT + innerH - ((Math.log10(p) - pMin) / (pMax - pMin)) * innerH;
  const psd = [[5,0.020],[20,0.040],[50,0.160],[800,0.160],[2000,0.026]];
  const path = psd.map((p, i) => (i === 0 ? "M" : "L") + sx(p[0]).toFixed(1) + "," + sy(p[1]).toFixed(1)).join(" ");
  const floor = sy(0.01);
  const area = path + " L" + sx(2000).toFixed(1) + "," + floor.toFixed(1)
                    + " L" + sx(5).toFixed(1) + "," + floor.toFixed(1) + " Z";

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H}
         style={{ background: AT_BG, border: "1px solid " + AT_LINE, borderRadius: 4 }}>
      {[0.01, 0.1, 0.5].map(v => (
        <g key={"py" + v}>
          <line x1={padL} y1={sy(v)} x2={W - padR} y2={sy(v)} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={padL - 4} y={sy(v) + 4} fontSize="9" textAnchor="end"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{v}</text>
        </g>
      ))}
      {[5, 20, 100, 500, 2000].map(f => (
        <g key={"px" + f}>
          <line x1={sx(f)} y1={padT} x2={sx(f)} y2={H - padB} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={sx(f)} y={H - padB + 12} fontSize="9" textAnchor="middle"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{f}</text>
        </g>
      ))}
      <path d={area} fill="rgba(79,143,255,0.10)" stroke="none" />
      <path d={path} stroke={AT_ACCENT} strokeWidth="1.6" fill="none" />
      {psd.map((p, i) => (
        <circle key={"bp" + i} cx={sx(p[0])} cy={sy(p[1])} r="3"
                fill={AT_PASS} stroke={AT_BG} strokeWidth="1" />
      ))}
      <text x="6" y="14" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_SLATE}>PSD (g^2/Hz)</text>
      <text x={W - padR - 4} y={H - 6} fontSize="9.5" textAnchor="end"
            fontFamily={AT_FONT_MONO} fill={AT_SLATE}>Hz (log)</text>
      <g transform={`translate(${W - 130}, 22)`}>
        <rect x="0" y="-8" width="116" height="28" fill={AT_BG2} stroke={AT_LINE} rx="3" />
        <text x="8" y="5"  fontSize="10" fontFamily={AT_FONT_MONO} fill={AT_SLATE}>Grms input</text>
        <text x="8" y="17" fontSize="12" fontFamily={AT_FONT_MONO} fill={AT_FG} fontWeight="600">14.1 Grms</text>
      </g>
    </svg>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Subviz · Shock SRS curve with +/-6 dB tolerance band
// ──────────────────────────────────────────────────────────────────────
function _AtShockSRS() {
  const W = 520, H = 200, padL = 42, padR = 10, padT = 14, padB = 26;
  const innerW = W - padL - padR, innerH = H - padT - padB;
  const fMin = Math.log10(100), fMax = Math.log10(10000);
  const aMin = Math.log10(50),  aMax = Math.log10(3000);
  const sx = f => padL + ((Math.log10(f) - fMin) / (fMax - fMin)) * innerW;
  const sy = a => padT + innerH - ((Math.log10(a) - aMin) / (aMax - aMin)) * innerH;
  const srs   = [[100,150],[1000,1500],[3000,1500],[10000,1500]];
  const upper = srs.map(p => [p[0], p[1] * 2]);
  const lower = srs.map(p => [p[0], p[1] / 2]);
  const meas  = [[100,155],[200,320],[400,640],[800,1280],
                 [1000,1480],[2000,1490],[4000,1495],[10000,1495]];
  const pf = arr => arr.map((p, i) => (i === 0 ? "M" : "L") + sx(p[0]).toFixed(1) + "," + sy(p[1]).toFixed(1)).join(" ");
  const band = pf(upper) + " " + lower.slice().reverse().map(p =>
    "L" + sx(p[0]).toFixed(1) + "," + sy(p[1]).toFixed(1)).join(" ") + " Z";

  return (
    <svg viewBox={`0 0 ${W} ${H}`} width="100%" height={H}
         style={{ background: AT_BG, border: "1px solid " + AT_LINE, borderRadius: 4 }}>
      {[50, 100, 500, 1500, 3000].map(v => (
        <g key={"sy" + v}>
          <line x1={padL} y1={sy(v)} x2={W - padR} y2={sy(v)} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={padL - 4} y={sy(v) + 4} fontSize="9" textAnchor="end"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{v} g</text>
        </g>
      ))}
      {[100, 500, 1000, 5000, 10000].map(f => (
        <g key={"sx" + f}>
          <line x1={sx(f)} y1={padT} x2={sx(f)} y2={H - padB} stroke={AT_LINE} strokeWidth="0.5" />
          <text x={sx(f)} y={H - padB + 12} fontSize="9" textAnchor="middle"
                fontFamily={AT_FONT_MONO} fill={AT_SLATE}>{f}</text>
        </g>
      ))}
      <path d={band}     fill="rgba(79,143,255,0.10)" stroke="none" />
      <path d={pf(srs)}  stroke={AT_ACCENT} strokeWidth="1.6" fill="none" />
      <path d={pf(meas)} stroke={AT_PASS}   strokeWidth="1.4" fill="none" strokeDasharray="3 2" />
      <text x={sx(2000)} y={sy(1500) - 8} fontSize="10" fontFamily={AT_FONT_MONO}
            fill={AT_PASS} textAnchor="middle" fontWeight="600">1500 g peak</text>
      <text x="6" y="14" fontSize="9.5" fontFamily={AT_FONT_MONO} fill={AT_SLATE}>SRS (g)</text>
      <text x={W - padR - 4} y={H - 6} fontSize="9.5" textAnchor="end"
            fontFamily={AT_FONT_MONO} fill={AT_SLATE}>freq (Hz, log)</text>
    </svg>
  );
}

const AT_VIZ = { tvac: _AtTvacProfile, emi: _AtEmiSpectrum, vibration: _AtVibPSD, shock: _AtShockSRS };

// ──────────────────────────────────────────────────────────────────────
// Real-time numeric strip — 4 tickers driven by the run-state elapsed
// counter. Cosmetic; truth-of-record is `state[id].passed`.
// ──────────────────────────────────────────────────────────────────────
function _AtLiveStrip({ tab, running, elapsed, passed }) {
  const fr = Math.min(1, elapsed / Math.max(1, tab.duration_s));
  let cells = [];
  if (tab.id === "tvac") {
    const T = passed ? -40 : (-40 + Math.sin(fr * Math.PI * 2) * 62);
    const torr = passed ? "9.4e-6" : (running ? (1.6e-5 - fr * 0.6e-5).toExponential(1) : "1.0e-5");
    cells = [
      { k: "Chamber T",    v: T.toFixed(1),                       u: "C"    },
      { k: "Pressure",     v: torr,                                u: "torr" },
      { k: "Cycle",        v: (Math.floor(fr * 24) + 1) + "/24",   u: ""     },
      { k: "Heater duty",  v: running ? (44 + fr * 26).toFixed(0) : "0", u: "%" },
    ];
  } else if (tab.id === "emi") {
    const ce = passed ? 70.0 : (running ? 30 + fr * 44 : 30);
    const re = passed ? 38.0 : (running ? 20 + fr * 22 : 20);
    cells = [
      { k: "CE102 peak",   v: ce.toFixed(1),                       u: "dBuV"   },
      { k: "RE102 peak",   v: re.toFixed(1),                       u: "dBuV/m" },
      { k: "Sweep f",      v: running ? Math.round(10 + fr * 17990) : 10, u: "MHz" },
      { k: "Margin",       v: passed ? "+24" : (running ? Math.round(64 - fr * 40) : 64).toString(), u: "dB" },
    ];
  } else if (tab.id === "vibration") {
    const grms = passed ? "14.1" : (running ? (4 + fr * 10).toFixed(2) : "0.00");
    cells = [
      { k: "Grms input",   v: grms,                                u: "Grms"  },
      { k: "Sweep f",      v: running ? Math.round(5 + fr * 1995) : 5, u: "Hz" },
      { k: "PSD peak",     v: running ? (0.020 + fr * 0.140).toFixed(3) : "0.020", u: "g^2/Hz" },
      { k: "Axis",         v: running ? (fr < 0.34 ? "X" : fr < 0.67 ? "Y" : "Z") : "—", u: "" },
    ];
  } else {
    const peak = passed ? 1500 : (running ? Math.round(120 + fr * 1380) : 0);
    cells = [
      { k: "Peak g",       v: peak,                                u: "g"  },
      { k: "Pulse width",  v: "0.5",                               u: "ms" },
      { k: "Knee f",       v: "1000",                              u: "Hz" },
      { k: "Shot",         v: (running ? Math.min(9, Math.floor(fr * 9) + 1) : 0) + "/9", u: "" },
    ];
  }
  const valueColor = passed ? AT_PASS : (running ? AT_ACCENT : AT_FG);
  return (
    <div style={{
      display: "grid", gridTemplateColumns: "repeat(4, 1fr)", gap: 0,
      border: "1px solid " + AT_LINE, borderRadius: 4, background: AT_BG,
    }}>
      {cells.map((c, i) => (
        <div key={"ls" + i} style={{
          padding: "10px 12px",
          borderRight: i < cells.length - 1 ? "1px solid " + AT_LINE : "none",
          display: "flex", flexDirection: "column", gap: 4, minWidth: 0,
        }}>
          <div style={{
            fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
            textTransform: "uppercase", color: AT_SLATE,
          }}>{c.k}</div>
          <div style={{ ..._at_metricValueBase, color: valueColor }}>
            <span>{c.v}</span>
            {c.u ? <span style={{ fontSize: 12, color: AT_SLATE, letterSpacing: "0.04em" }}>{c.u}</span> : null}
          </div>
        </div>
      ))}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Tab strip
// ──────────────────────────────────────────────────────────────────────
function _AtTabStrip({ activeId, passedMap, onPick }) {
  return (
    <div style={{ display: "flex", borderBottom: "1px solid " + AT_LINE, background: AT_BG }}>
      {AT_TABS.map(t => {
        const active = t.id === activeId;
        const passed = !!passedMap[t.id];
        return (
          <button key={t.id} type="button" onClick={() => onPick(t.id)} style={{
            ..._at_tabBtnBase,
            background: active ? AT_BG2 : "transparent",
            color: active ? AT_FG : AT_SLATE,
            borderBottom: active ? "2px solid " + AT_ACCENT : "2px solid transparent",
          }}>
            <span>{t.label}</span>
            {passed ? (
              <span style={{
                display: "inline-flex", alignItems: "center", gap: 3,
                fontSize: 12, color: AT_PASS, letterSpacing: "0.04em",
              }}>
                <span style={{ display: "inline-block", width: 6, height: 6, background: AT_PASS, borderRadius: "50%" }} />
                pass
              </span>
            ) : null}
          </button>
        );
      })}
    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Tab body — params + criteria + run sequence | live strip + chart
// ──────────────────────────────────────────────────────────────────────
function _AtTabBody({ tab, state, onRun }) {
  const passed = !!state.passed, running = !!state.running, elapsed = state.elapsed || 0;
  const Viz = AT_VIZ[tab.id] || _AtTvacProfile;
  const statusLabel = passed ? "all green" : running ? "running" : "armed";
  const statusColor = passed ? AT_PASS : (running ? AT_ACCENT : AT_SLATE);
  const dotColor    = passed ? AT_PASS : (running ? AT_WARN : AT_SLATE);
  const stateLabel  = passed ? "pass"  : (running ? "live"   : "pending");

  return (
    <div style={{ padding: 18, display: "grid", gridTemplateColumns: "1fr 1fr", gap: 18, minHeight: 0, overflow: "auto" }}>

      {/* left column — params + criteria + run */}
      <div style={{ display: "flex", flexDirection: "column", gap: 14, minWidth: 0 }}>

        <div style={{ display: "flex", flexDirection: "column", gap: 2 }}>
          <span style={{
            fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
            textTransform: "uppercase", color: AT_SLATE,
          }}>{tab.refid} · {tab.sub}</span>
          <span style={{
            fontFamily: AT_FONT_UI, fontSize: 16, fontWeight: 600,
            color: AT_FG, lineHeight: 1.3,
          }}>{tab.title}</span>
        </div>

        {/* params */}
        <div style={{ background: AT_BG2, border: "1px solid " + AT_LINE, borderRadius: 6, padding: 14 }}>
          <div style={{
            fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
            textTransform: "uppercase", color: AT_SLATE, marginBottom: 8,
          }}>test parameters</div>
          {tab.params.map((p, i) => (
            <div key={"p" + i} style={{
              display: "flex", alignItems: "baseline", justifyContent: "space-between",
              gap: 12, padding: "5px 0",
              borderBottom: i < tab.params.length - 1 ? "1px dashed " + AT_LINE : "none",
              fontFamily: AT_FONT_UI,
            }}>
              <span style={{ fontSize: 12, color: AT_SLATE, letterSpacing: "0.02em" }}>{p.label}</span>
              <_AtNum value={p.value} unit={p.unit} />
            </div>
          ))}
        </div>

        {/* criteria */}
        <div style={{ background: AT_BG2, border: "1px solid " + AT_LINE, borderRadius: 6, padding: 14 }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 8 }}>
            <span style={{
              fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
              textTransform: "uppercase", color: AT_SLATE,
            }}>pass / fail criteria</span>
            <span style={{
              ..._at_statusPillBase,
              color: statusColor,
              border: "1px solid " + statusColor,
            }}>{statusLabel}</span>
          </div>
          {tab.criteria.map((k, i) => (
            <div key={"c" + i} style={{
              ..._at_criteriaRowBase,
              borderBottom: i < tab.criteria.length - 1 ? "1px dashed " + AT_LINE : "none",
            }}>
              <span style={{
                display: "inline-block", width: 7, height: 7,
                borderRadius: "50%", background: dotColor, flexShrink: 0,
              }} />
              <span style={{ flex: 1, minWidth: 0 }}>{k}</span>
              <span style={{
                fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.06em",
                textTransform: "uppercase", color: statusColor,
              }}>{stateLabel}</span>
            </div>
          ))}
        </div>

        {/* run row */}
        <div style={{ display: "flex", alignItems: "center", gap: 12, paddingTop: 4 }}>
          <button type="button" onClick={onRun} disabled={running} style={{
            ..._at_runBtnBase,
            background: running ? AT_BG2 : AT_ACCENT,
            color: running ? AT_SLATE : "#FFFFFF",
            border: "1px solid " + (running ? AT_LINE : AT_ACCENT),
            cursor: running ? "not-allowed" : "pointer",
          }}>
            {running ? "running · " + elapsed + " s" : (passed ? "re-run sequence" : "run sequence")}
          </button>
          <span style={{ fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE, letterSpacing: "0.04em" }}>
            duration ~{tab.duration_s} s · {tab.refid}
          </span>
          {passed && state.lastHash ? (
            <span style={{ marginLeft: "auto", fontFamily: AT_FONT_MONO,
                           fontSize: 12, color: AT_PASS, letterSpacing: "0.04em" }}>
              ledger {state.lastHash}
            </span>
          ) : null}
        </div>

      </div>

      {/* right column — live strip + chart */}
      <div style={{ display: "flex", flexDirection: "column", gap: 14, minWidth: 0 }}>
        <div style={{
          fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
          textTransform: "uppercase", color: AT_SLATE,
        }}>real-time</div>
        <_AtLiveStrip tab={tab} running={running} elapsed={elapsed} passed={passed} />
        <div style={{
          fontFamily: AT_FONT_MONO, fontSize: 12, letterSpacing: "0.10em",
          textTransform: "uppercase", color: AT_SLATE, marginTop: 4,
        }}>
          {tab.id === "tvac"      ? "thermal-vacuum profile"
            : tab.id === "emi"        ? "emi spectra · ce102 / re102"
            : tab.id === "vibration"  ? "random PSD breakpoints"
            : "shock response spectrum"}
        </div>
        <Viz />
      </div>

    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Main · ScreenAetherionTest
// ──────────────────────────────────────────────────────────────────────
function ScreenAetherionTest() {
  const operator    = "K. Iwata";
  const stationName = "Test (s4) — TVAC + EMI + Vibration + Shock";
  const wo          = "WO-AET-014R · KESTREL-3.00.00 · Aperture Earth · 1u · re-test";
  const seq         = "04 / 04";
  const serial      = "KST3-FU-001";   // canonical KESTREL-3 FU-01 (matches fixtures-space.jsx)

  const toastApi = _atUseToast();

  const [state, setState] = uSAT(() => {
    const init = {};
    AT_TABS.forEach(t => { init[t.id] = { running: false, elapsed: 0, passed: false, lastHash: null }; });
    return init;
  });
  const [activeId, setActiveId] = uSAT("tvac");
  const [accReceipt, setAccReceipt] = uSAT(null);
  const intervalsRef = uRAT({});

  // Cleanup on unmount
  uEAT(() => () => {
    Object.keys(intervalsRef.current).forEach(k => {
      if (intervalsRef.current[k]) clearInterval(intervalsRef.current[k]);
    });
    intervalsRef.current = {};
  }, []);

  // Reset on tenant switch
  uEAT(() => {
    function onTenant() {
      Object.keys(intervalsRef.current).forEach(k => {
        if (intervalsRef.current[k]) clearInterval(intervalsRef.current[k]);
      });
      intervalsRef.current = {};
      setState(() => {
        const reset = {};
        AT_TABS.forEach(t => { reset[t.id] = { running: false, elapsed: 0, passed: false, lastHash: null }; });
        return reset;
      });
      setAccReceipt(null);
    }
    if (typeof window !== "undefined") {
      window.addEventListener("forge:tenant-change", onTenant);
      return () => window.removeEventListener("forge:tenant-change", onTenant);
    }
    return undefined;
  }, []);

  // Wave 8E.2 — server-side checkin. Default station is "s4" (the routing
  // slot) but callers must override to the gate node ("TVAC", "EMI", "ACC")
  // to drive the qual_records ladder + FAB-B-reissue rerouting in
  // server/state-machines/aetherion.js applyCheckin. Returns the underlying
  // promise so callers can chain success/error toasts.
  function _trCheckin(extra) {
    const fApi = (typeof window !== "undefined") ? window.forgeApi : null;
    if (!fApi || !fApi.traveler || typeof fApi.traveler.checkin !== "function") {
      return Promise.resolve(null);
    }
    const cur = (typeof window !== "undefined" && window.__forgeCurrentUser) || { name: operator };
    const body = Object.assign({
      station: "s4",
      actor:   cur.name,
    }, extra || {});
    return fApi.traveler.checkin(serial, body);
  }

  function runSequence(tabId) {
    const tab = AT_TAB_BY_ID[tabId];
    if (!tab) return;
    if (state[tabId] && state[tabId].running) return;
    setState(prev => {
      const next = { ...prev };
      next[tabId] = { running: true, elapsed: 0, passed: false, lastHash: prev[tabId] ? prev[tabId].lastHash : null };
      return next;
    });
    const tickMs = 250;
    let elapsedMs = 0;
    const id = setInterval(() => {
      elapsedMs += tickMs;
      const elapsedS = Math.floor(elapsedMs / 1000);
      if (elapsedMs >= tab.duration_s * 1000) {
        clearInterval(id);
        delete intervalsRef.current[tabId];
        const receipt = _atEmit(tab.qualEvent, {
          actor: operator, ref: tab.refid, summary: tab.qualSummary,
        });
        if (tab.secondary) {
          _atEmit(tab.secondary, {
            actor: operator, ref: tab.refid, summary: tab.secondarySummary,
          });
        }
        const hash = receipt && receipt.hash ? receipt.hash : null;
        // Wave 8E.2 — persist this test pass on the server. Each tab is a
        // gate node in station_graph (TVAC, EMI, etc.) — override station to
        // the gate id so server inserts a qual_records row on pass.
        _trCheckin({
          station: tab.id.toUpperCase(),
          result: "pass",
          dimensions: { test: tab.id, ref: tab.refid, duration_s: tab.duration_s },
          photos: [],
          notes: tab.qualSummary,
        }).catch(function(err) {
          if (typeof console !== "undefined") console.warn("[aetherion-test] gate-pass checkin failed", err);
        });
        setState(prev => {
          const next = { ...prev };
          next[tabId] = { running: false, elapsed: tab.duration_s, passed: true, lastHash: hash };
          return next;
        });
        toastApi.flash(
          "ok",
          tab.label.toUpperCase() + " sequence passed · " + tab.refid + (hash ? " · ledger " + hash : ""),
          3600
        );
      } else {
        setState(prev => {
          const cur = prev[tabId] || {};
          if (!cur.running) return prev;
          const next = { ...prev };
          next[tabId] = { ...cur, elapsed: elapsedS };
          return next;
        });
      }
    }, tickMs);
    intervalsRef.current[tabId] = id;
  }

  // Wave 8E.2 — TVAC fail / Hold button. Posts a gate-fail to the server
  // which (per server/state-machines/aetherion.js:390) reroutes traveler
  // to FAB-B-reissue and inserts a qual_records row with status=fail. The
  // local UI clears the TVAC tab pass-state and surfaces a hold toast so
  // the operator sees the reroute.
  function failTvac() {
    _trCheckin({
      station: "TVAC",
      result: "fail",
      notes: "chatter at 12 kHz",
      attrs: { ref: "TVAC-AET-014R", cycle: 14, dispo: "fab-b-reissue" },
    }).then(function(r) {
      // Reset TVAC tab so the operator can re-run after FAB-B.
      setState(prev => {
        const next = { ...prev };
        next.tvac = { running: false, elapsed: 0, passed: false, lastHash: prev.tvac ? prev.tvac.lastHash : null };
        return next;
      });
      setActiveId("tvac");
      const qr = r && r.qualRecord;
      const tr = r && r.traveler;
      toastApi.flash(
        "warn",
        "TVAC fail · " + (tr && tr.current_station ? "routed to " + tr.current_station : "rerouting to FAB-B-reissue")
          + (qr ? " · qual " + qr.id : ""),
        3800
      );
    }).catch(function(err) {
      const msg = (err && err.message) || "TVAC fail checkin error";
      if (typeof console !== "undefined") console.warn("[aetherion-test] TVAC fail failed", err);
      toastApi.flash("error", "error · " + msg, 3800);
    });
  }

  function generateReport() {
    const ts = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
    toastApi.flash(
      "ok",
      "QUAL-RPT-" + serial + "-" + ts.slice(0, 10) + ".pdf · 4 envelopes · operator signed · binder-ready",
      3800
    );
  }

  function allPassed() {
    return AT_TABS.every(t => state[t.id] && state[t.id].passed);
  }

  function advanceToAcceptance() {
    if (!allPassed()) return;
    const receipt = _atEmit("ACC_PASSED", {
      actor: "Mission Director",
      ref: "ACC-AET-001",
      summary: "Flight acceptance signed by Mission Director",
    });
    // Wave 8E.2 — persist the ACC gate pass. Station "ACC" advances the
    // traveler along station_graph to LAUNCH (per aetherion.js applyCheckin
    // forward-edge selection) and writes a qual_records "ACC pass" row.
    _trCheckin({
      station: "ACC",
      result: "pass",
      dispo_path: "advance-to-acceptance",
      notes: "All 4 qual envelopes passed; advance to flight acceptance",
    }).catch(function(err) {
      if (typeof console !== "undefined") console.warn("[aetherion-test] ACC checkin failed", err);
    });
    if (receipt) setAccReceipt(receipt);
    toastApi.flash(
      "ok",
      "ACC_PASSED emitted · " + (receipt && receipt.hash ? receipt.hash : "ledger pending") + " · advancing to launch manifest",
      4200
    );
  }

  const passedCount = AT_TABS.filter(t => state[t.id] && state[t.id].passed).length;
  const passedMap = {};
  AT_TABS.forEach(t => { passedMap[t.id] = !!(state[t.id] && state[t.id].passed); });
  const activeTab = AT_TAB_BY_ID[activeId] || AT_TABS[0];
  const accReady  = allPassed();

  // Toast tones for the inline banner
  const toastTone = toastApi.toast && (
    toastApi.toast.kind === "ok"    ? { bg: "rgba(91,200,183,0.10)",  bar: AT_PASS }
    : toastApi.toast.kind === "warn"  ? { bg: "rgba(224,185,106,0.10)", bar: AT_WARN }
    : toastApi.toast.kind === "error" ? { bg: "rgba(216,106,106,0.10)", bar: AT_FAIL }
    : { bg: "rgba(79,143,255,0.10)", bar: AT_ACCENT }
  );

  return (
    <div style={{
      display: "flex", flexDirection: "column", height: "100%", minHeight: 0,
      background: AT_BG, color: AT_FG, fontFamily: AT_FONT_UI,
    }}>

      {/* Header */}
      <div style={{
        display: "flex", alignItems: "center", gap: 14, padding: "10px 16px",
        background: AT_BG2, borderBottom: "1px solid " + AT_LINE,
      }}>
        <span style={_at_headerBadgeBase}>KI</span>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{ display: "flex", alignItems: "center", gap: 8, fontSize: 13, color: AT_FG }}>
            <b>{operator}</b>
            <span style={{ color: AT_SLATE }}>·</span>
            <span style={{ color: AT_FG }}>{stationName}</span>
            <span style={_at_seqTagBase}>seq {seq}</span>
          </div>
          <div style={{
            fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE,
            marginTop: 2, letterSpacing: "0.02em",
          }}>{wo}</div>
        </div>
        <div style={{
          display: "inline-flex", alignItems: "center", gap: 8,
          fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE,
        }}>
          <span>vehicle</span>
          <span style={{
            fontSize: 13, color: AT_FG, padding: "2px 8px",
            border: "1px solid " + AT_ACCENT, borderRadius: 3,
            background: "rgba(79,143,255,0.10)", letterSpacing: "0.04em",
          }}>{serial}</span>
        </div>
      </div>

      {/* Stations strip */}
      <div style={{
        padding: "10px 16px", display: "flex", alignItems: "center", gap: 18,
        background: AT_BG, borderBottom: "1px solid " + AT_LINE,
      }}>
        <div style={_at_travelerLabelBase}>
          <span>traveler</span>
          <span style={{ color: AT_FG }}>aetherion // station 4</span>
        </div>
        <div style={{ flex: 1, display: "flex", gap: 12 }}>
          {["s1","s2","s3","s4"].map((s, i) => {
            const cur = i === 3;
            return (
              <div key={s} style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 4 }}>
                <div style={{
                  width: 14, height: 14, borderRadius: "50%",
                  background: cur ? AT_ACCENT : AT_PASS,
                  border: "1px solid " + (cur ? AT_ACCENT : AT_PASS),
                  boxShadow: cur ? "0 0 0 4px rgba(79,143,255,0.18)" : "none",
                }} />
                <span style={{
                  fontFamily: AT_FONT_MONO, fontSize: 12,
                  color: cur ? AT_FG : AT_SLATE,
                  letterSpacing: "0.06em", textTransform: "uppercase",
                }}>{s}</span>
              </div>
            );
          })}
        </div>
        <div style={{ display: "inline-flex", alignItems: "center", gap: 8,
                      fontFamily: AT_FONT_MONO, fontSize: 12, color: AT_SLATE }}>
          <span style={{
            padding: "2px 8px", border: "1px solid " + AT_LINE, borderRadius: 3,
            color: passedCount === 4 ? AT_PASS : AT_FG,
          }}>
            {passedCount} / 4 envelopes passed
          </span>
        </div>
      </div>

      {/* Tab strip */}
      <_AtTabStrip activeId={activeId} passedMap={passedMap} onPick={id => setActiveId(id)} />

      {/* Active tab body */}
      <div style={{ flex: 1, minHeight: 0, overflow: "auto", background: AT_BG2 }}>
        <_AtTabBody
          tab={activeTab}
          state={state[activeId] || { running: false, elapsed: 0, passed: false, lastHash: null }}
          onRun={() => runSequence(activeId)}
        />
      </div>

      {/* Toast banner (transient) */}
      {toastApi.toast ? (
        <div style={{ padding: "8px 16px", borderTop: "1px solid " + AT_LINE, background: AT_BG }}>
          <div style={{
            ..._at_toastRowBase,
            background: toastTone.bg, borderLeft: "3px solid " + toastTone.bar,
          }}>
            <span style={_at_toastKindBase}>{toastApi.toast.kind}</span>
            <span style={{ flex: 1, minWidth: 0 }}>{toastApi.toast.msg}</span>
            <button type="button" onClick={toastApi.dismiss} style={_at_toastCloseBtn}>close</button>
          </div>
        </div>
      ) : null}

      {/* Footer · report + advance */}
      <div style={{
        display: "flex", alignItems: "center", gap: 14,
        padding: "12px 16px", background: AT_BG2,
        borderTop: "1px solid " + AT_LINE,
      }}>
        <div style={{ flex: 1, minWidth: 0 }}>
          <div style={{
            fontFamily: AT_FONT_MONO, fontSize: 12,
            letterSpacing: "0.10em", textTransform: "uppercase",
            color: AT_SLATE, marginBottom: 4,
          }}>qualification report · acceptance gate</div>
          <div style={{
            fontFamily: AT_FONT_UI, fontSize: 12, color: AT_FG, lineHeight: 1.4,
          }}>
            Bound to serial {serial}. TVAC + EMI + Vibration + Shock envelopes are reduced
            into a single PDF binder per ECSS-E-ST-10-03.
          </div>
          {accReceipt ? (
            <div style={{
              marginTop: 6, fontFamily: AT_FONT_MONO, fontSize: 12,
              color: AT_PASS, letterSpacing: "0.04em",
            }}>
              ACC_PASSED · {accReceipt.event_id} · {accReceipt.ts} · {accReceipt.hash}
            </div>
          ) : null}
        </div>
        <button type="button" onClick={failTvac}
                title="Mark TVAC fail · routes to FAB-B-reissue · writes qual_records row"
                style={{
                  ..._at_advanceBtnBase,
                  background: "transparent",
                  color: AT_FAIL,
                  border: "1px solid " + AT_FAIL,
                  cursor: "pointer",
                }}>
          TVAC fail · hold
        </button>
        <button type="button" onClick={generateReport} style={_at_reportBtn}>Generate qual report PDF</button>
        <button type="button" onClick={advanceToAcceptance} disabled={!accReady}
                title={accReady
                  ? "Emit ACC_PASSED and advance to launch manifest lock"
                  : "Disabled until all four envelopes pass"}
                style={{
          ..._at_advanceBtnBase,
          background: accReady ? AT_PASS : AT_BG,
          color: accReady ? "#08221E" : AT_SLATE,
          border: "1px solid " + (accReady ? AT_PASS : AT_LINE),
          cursor: accReady ? "pointer" : "not-allowed",
        }}>
          {accReady ? "Advance to acceptance" : "Acceptance gated (" + passedCount + "/4)"}
        </button>
      </div>

    </div>
  );
}

// ──────────────────────────────────────────────────────────────────────
// Route registration — exclusively against the Space registry (spec §12
// task 12, owned by another agent). We do NOT fall back to the pump
// registry because /traveler/test already exists for the Tritan pump
// tenant, and clobbering it would break the pump demo. If the space
// registry has not been wired yet, the route is staged on
// window.SPACE_ROUTES so the registry can pick it up at first init.
// ──────────────────────────────────────────────────────────────────────
(function _atRegister() {
  const route = {
    path: "/traveler/test",
    mode: "traveler",
    title: "Test station",
    renderer: () => React.createElement(ScreenAetherionTest),
  };
  if (typeof registerSpaceRoute === "function") { registerSpaceRoute(route); return; }
  if (typeof window !== "undefined") {
    if (!Array.isArray(window.SPACE_ROUTES)) window.SPACE_ROUTES = [];
    const ix = window.SPACE_ROUTES.findIndex(r => r.path === route.path);
    if (ix >= 0) window.SPACE_ROUTES[ix] = route; else window.SPACE_ROUTES.push(route);
  }
})();

// ──────────────────────────────────────────────────────────────────────
// Window export
// ──────────────────────────────────────────────────────────────────────
if (typeof window !== "undefined") {
  window.ScreenAetherionTest = ScreenAetherionTest;
  window.AETHERION_TEST_TABS = AT_TABS;
}
})();
