/* ═══════════════════════════════════════════════════════════════════ CALL UI · the pieces every desktop surface uses to show ONE Shadow call (pt74, 2026-09-05) — moved here from pages/shadow/shadow/signals.jsx so the trade page's Shadow Signal panel draws the same legs, the same confidence bar and the same SIGNAL/PAPER pill as the Shadow page, from the same rules. It had its own copies: a marker typed at 70%, a colour ladder typed at 70/55, distances measured from the LIVE price, and no word for a call the engine had filed as PAPER — one click from a real order form. Loaded as ../_fx/call-ui.jsx AFTER _kit/terminal.js (ST_num, ST_fmt, ST_normPred) and BEFORE the page's own panels. Gated by BOTH tools/check_d_signals.js and tools/check_d_tradesignal.js — a check tagged `file: FX` mutates this file. What each piece means is written above it. Read those before changing a word: every sentence here is one a screen commits to. ═══════════════════════════════════════════════════════════════════ */ /* The numbers under ENTRY / NOW / TARGET / STOP, and what each is measured FROM (pt72). Distances to the target and the stop are the CALL's own — from its entry, the price at creation (`entry_price`) — never from the live price: a call's bracket does not move because the tape did. With no entry there is no distance ("—"), whatever the price now. The price now gets its own leg, NOW, carrying the signed move since entry when both are known, coloured by whether it moved the call's way. */ function ST_callLegs(p) { const entry = ST_num(p.entry), price = ST_num(p.price), target = ST_num(p.target), stop = ST_num(p.stop); const dist = (x) => (entry != null && entry !== 0 && x != null) ? Math.abs((x - entry) / entry * 100) : null; const move = (entry != null && entry !== 0 && price != null) ? (price - entry) / entry * 100 : null; return { entry, price, target, stop, tgtPct: dist(target), stopPct: dist(stop), move }; } function ST_distPct(v, sign) { return v == null ? '—' : `${sign}${v.toFixed(1)}%`; } function ST_movePct(v) { return v == null ? null : `${v >= 0 ? '▲' : '▼'} ${Math.abs(v).toFixed(2)}%`; } function ST_moveColor(v, dir) { if (v == null || v === 0 || !dir) return ST.ink3; const toward = dir === 'LONG' ? v > 0 : v < 0; /* a SHORT wants the price DOWN */ return toward ? ST.green : ST.red; } function CallLegs({ L, dir, priceTs }) { const noEntry = "the box did not send this call's entry price"; /* `current_price` carried no timestamp, so a tick the feed last wrote an hour ago read as NOW (pt73). The router sends `price_ts` now; the leg says on hover when its number was read, or says nothing. */ const read = priceTs != null ? ` · price read ${agoText(priceTs)}` : ''; return ( <> {L.price != null && } ); } function agoText(ms) { const s = Math.max(0, Math.round((Date.now() - ms) / 1000)); return s < 60 ? `${s}s ago` : s < 3600 ? `${Math.round(s / 60)}m ago` : `${(s / 3600).toFixed(1)}h ago`; } /* ── the confidence bar, drawn from the ENGINE's lines (pt73) ──────────── The marker used to sit at a typed 70 and the fill turned amber at a typed 65 — numbers that matched neither SHADOW.signal_threshold (the line that files a call as SIGNAL rather than PAPER) nor the session ladder (the line the governor trades from). Worse, the fill glowed amber over calls the engine had ALREADY filed as PAPER: on 2026-09-05 every one of the 17 open calls was paper — a gate (ev_gate, thesis, governor_veto, probation…) had knocked each off the real path — and the page said "above threshold" about calls the box had refused to alert on. Now the marker is `thresholds.signal` from the payload or nothing, and the fill is amber only when the engine's own `kind` says SIGNAL. */ function ConfBar({ conf, kind, thresholds, role, glow = false }) { const pct = (v) => { const n = ST_num(v); return n == null ? null : (n <= 1 ? n * 100 : n); }; const thr = pct(thresholds && thresholds.signal); const thrPct = thr == null ? null : Math.round(thr * 10) / 10; const sessPct = pct(thresholds && thresholds.session); const isSig = kind === 'signal'; const title = thrPct == null ? 'the box sent no threshold — no line is drawn' : `Shadow files a call as SIGNAL from ${Math.round(thrPct)}% and as PAPER below it` + (sessPct != null ? ` · this session (${(thresholds && thresholds.session_name) || '—'}) trades nothing under ${Math.round(sessPct)}%` : ''); return (
{thrPct != null &&
} {conf != null &&
}
); } /* The engine's verdict, in the engine's word (pt73). SIGNAL is alert-grade; PAPER was kept off the real path by a named gate, and the hover names it — the slug is the box's own (ev_gate, thesis, governor_veto, probation…), never re-worded here. No kind → no pill: unknown is not "paper". */ function KindPill({ kind, why, role }) { if (kind !== 'signal' && kind !== 'paper') return null; const sig = kind === 'signal'; const c = sig ? ST.amber : ST.ink4; const title = sig ? 'the box filed this call as a SIGNAL — alert-grade, above its own line after every gate' : why === 'none' ? 'the box filed this call as PAPER — under its own SIGNAL line, no gate needed; recorded for the record, never alerted' : why ? `the box filed this call as PAPER (gate: ${why}) — recorded for the record, never alerted` : 'the box filed this call as PAPER — recorded for the record, never alerted; it sent no gate name'; return ( {sig ? 'SIGNAL' : 'PAPER'} ); } /* "· PAPER" in the OPEN SIGNALS title was typed (pt73): the engine has filed 296 all-time calls as SIGNAL, and one of them open would have sat under a header calling it paper. Count the engine's own kinds; a call with no kind is counted as unknown, folded into neither. */ /* The empty state's sentence (pt73). "No setup currently clears Shadow's confidence threshold in this regime" was a RULE typed here — and a wrong one: paper calls (below the signal line) ARE shown when they exist, so an empty queue means nothing at or above the paper floor is open, not that nothing clears the signal line. The numbers come from the payload, or the sentence says less. */ function ThresholdLine({ thresholds, sym }) { const pct = (v) => { const n = ST_num(v); return n == null ? null : Math.round(n <= 1 ? n * 100 : n); }; const pf = pct(thresholds && thresholds.paper_floor), sg = pct(thresholds && thresholds.signal); /* `sym` names the coin on a per-coin surface (the trade page); without it the sentence is the queue's. Either way the numbers are the payload's. */ const none = sym ? `No open call for ${sym} right now.` : 'No open call right now.'; if (pf == null || sg == null) return {none}; return ( {none} Shadow records a call from {`${pf}%`} confidence and files it as a SIGNAL from {`${sg}%`}. ); } /* A leg whose number the box did not send reads "—" in the page's grey — never "$0.00", never "$—" (pt72). */ function PriceLeg({ label, value, c, sub, subC, title }) { const known = ST_num(value) != null; return (
{label} {known ? `$${ST_fmt.price(value)}` : '—'} {sub && {sub}}
); } window.ST_callLegs = ST_callLegs; window.ST_distPct = ST_distPct; window.ST_movePct = ST_movePct; window.ST_moveColor = ST_moveColor; window.CallLegs = CallLegs; window.agoText = agoText; window.ConfBar = ConfBar; window.KindPill = KindPill; window.ThresholdLine = ThresholdLine; window.PriceLeg = PriceLeg;