// AsymmetricTheses — surface the highest-asymmetry investment ideas // the team has scored, plus the graveyard of names that aged out // (preserved per the user's directive — never deleted, just rotated). // // Pulls from /api/theses/ranked which walks every memo with // asymmetry_score in its frontmatter (Growth Arbitrage Analyst is the // primary producer; any persona can write the field). Click a ticker // to drill into TickerContext for full team view. // // The point of this panel: the user trades on asymmetric information. // This is the at-a-glance "what's the highest-edge idea on the board // right now" surface — distilled from every memo the team has filed. const { useState: aUseState, useEffect: aUseEffect, useCallback: aUseCallback, useMemo: aUseMemo } = React; function AsymmetricTheses({ open, onClose }) { if (!open) return null; const [data, setData] = aUseState(null); const [loading, setLoading] = aUseState(true); const [err, setErr] = aUseState(''); const [view, setView] = aUseState('top'); // 'top' | 'graveyard' const load = aUseCallback(async () => { setLoading(true); setErr(''); try { const r = await fetch('/api/theses/ranked?limit_top=15&limit_graveyard=15'); if (!r.ok) throw new Error(`HTTP ${r.status}`); const j = await r.json(); setData(j); } catch (e) { setErr(String(e.message || e)); } finally { setLoading(false); } }, []); aUseEffect(() => { if (open) load(); }, [open, load]); const list = view === 'top' ? (data?.top || []) : (data?.graveyard || []); return (