/* ═══════════════════════════════════════════════════════════════════════════ GATE BAR — replaces the Market Clock (2026-07-30; audit cut #3 + idea #5). The old strip was 1,549 lines of animated equity-hub clock faces for a market that never closes. This one answers the question the dashboard could not answer anywhere: CAN SHADOW ACT RIGHT NOW, AND WHAT STOPS IT? Four cells, all tri-state — a failed read renders UNKNOWN, never a guess (the invariant this codebase keeps re-learning): 1. KILL SWITCH /api/paper/summary → kill_switch 2. REAL MODE /api/real/status → enabled 3. SESSION client clock (mirrors session_manager's UTC ladder — display only; the governor enforces server-side) 4. F1/F4 WINDOW /api/shadow/window → registered end date + n progress IIFE-wrapped like every no-build screen file. Depends on kit globals (ST, ST_fetch, ST_poll). ═══════════════════════════════════════════════════════════════════════ */ (() => { const SESSIONS = [ /* [fromHourUTC, toHourUTC, name, threshold, color-key] — session_manager's ladder */ [22, 24, 'DEAD ZONE', 0.65, 'ink3'], [0, 7, 'ASIAN', 0.55, 'green'], [7, 13, 'EU', 0.60, 'amber'], [13, 17, 'EU/US OVERLAP', 0.75, 'red'], [17, 22, 'US', 0.60, 'amber'], ]; function sessionNow() { const h = new Date().getUTCHours(); for (const [a, b, name, thr, ck] of SESSIONS) if (h >= a && h < b) return { name, thr, ck }; return { name: '?', thr: null, ck: 'ink3' }; } function Cell({ label, value, color, sub }) { return (
{label} {value} {sub && {sub}}
); } function GateBar() { const [kill, setKill] = React.useState(undefined); /* undefined=loading, null=UNKNOWN, bool=known */ const [real, setReal] = React.useState(undefined); const [win, setWin] = React.useState(undefined); const [, tick] = React.useState(0); /* minute tick re-derives the session */ React.useEffect(() => { const load = () => { ST_fetch('/paper/summary').then(d => setKill(!!d.kill_switch)).catch(() => setKill(null)); ST_fetch('/real/status').then(d => setReal(!!d.enabled)).catch(() => setReal(null)); ST_fetch('/shadow/window').then(setWin).catch(() => setWin(null)); }; load(); const id = ST_poll(load, 30000); const mi = setInterval(() => tick(t => t + 1), 60000); /* clock only, no backend */ return () => { clearInterval(id); clearInterval(mi); }; }, []); const s = sessionNow(); const killVal = kill === undefined ? '…' : kill === null ? 'UNKNOWN' : kill ? 'HALTED' : 'ENTRIES OPEN'; const killCol = kill === undefined ? ST.ink4 : kill === null ? ST.amber : kill ? ST.red : ST.green; const realVal = real === undefined ? '…' : real === null ? 'UNKNOWN' : real ? 'ARMED' : 'OFF'; const realCol = real === undefined ? ST.ink4 : real === null ? ST.amber : real ? ST.red : ST.ink3; let winVal = '…', winSub = null, winCol = ST.ink3; if (win === null) { winVal = 'UNKNOWN'; winCol = ST.amber; } else if (win) { const end = win.registered && win.registered.end_date; const days = end ? Math.max(0, Math.ceil((new Date(end + 'T00:00:00Z') - Date.now()) / 86400000)) : null; const m = win.measured || {}; winVal = days != null ? `${days}d left` : '—'; winSub = (m.n_resolved != null && win.registered && win.registered.n_target) ? `${m.n_resolved}/${win.registered.n_target} resolved` : null; winCol = ST.ink2; } return (
can Shadow act right now? — every cell UNKNOWN before it guesses
); } window.GateBar = GateBar; })();