// Shared UI components used across Apex Algo variations. // Each variation may override tokens (colors, fonts) via props/context, // but structure + interactivity live here. window.ApexUI = (function () { const { useState, useRef, useEffect, useMemo } = React; // Minimal apply-for-license modal — multi-step, client-side only. function ApplyModal({ open, onClose, theme }) { const [step, setStep] = useState(0); const [form, setForm] = useState({ name: '', email: '', capital: '50k-250k', venues: [], experience: 'retail-systematic', notes: '', }); const [submitted, setSubmitted] = useState(false); useEffect(() => { if (!open) return; function onKey(e) { if (e.key === 'Escape') onClose(); } window.addEventListener('keydown', onKey); return () => window.removeEventListener('keydown', onKey); }, [open, onClose]); if (!open) return null; const t = theme || {}; const bg = t.surface || '#0F1114'; const border = t.border || 'rgba(255,255,255,0.08)'; const fg = t.fg || '#E7E9EC'; const dim = t.dim || 'rgba(231,233,236,0.55)'; const accent = t.accent || '#00D4C8'; const mono = t.mono || 'ui-monospace, SFMono-Regular, Menlo, monospace'; function toggleVenue(v) { setForm(f => ({ ...f, venues: f.venues.includes(v) ? f.venues.filter(x => x !== v) : [...f.venues, v] })); } function submit() { setSubmitted(true); } const steps = ['Contact', 'Profile', 'Review']; return (
e.stopPropagation()} style={{ width: 'min(560px, 100%)', background: bg, border: `1px solid ${border}`, borderRadius: 4, color: fg, fontFamily: t.sans || 'system-ui, sans-serif', }}> {submitted ? (
Application received
We review applications within 2 business days. You'll hear from us at {form.email || 'your email'}.
) : ( <>
Start 14-day free trial
{steps.map((s, i) => (
))}
{step === 0 && (
setForm({ ...form, name: e.target.value })} placeholder="Your full name" style={inputStyle(t)} /> setForm({ ...form, email: e.target.value })} placeholder="name@domain.com" style={inputStyle(t)} />
)} {step === 1 && (
{['Hyperliquid (crypto)', 'Interactive Brokers'].map(v => ( ))}
)} {step === 2 && (
Submitting forwards this to access@theapexalgo.com. Approved trials get dashboard access for 14 days — paper or live, your choice. No payment taken now.
)}
{step < 2 ? ( ) : ( )}
)}
); } function inputStyle(t) { return { width: '100%', background: 'transparent', color: t.fg || '#E7E9EC', border: `1px solid ${t.border || 'rgba(255,255,255,0.12)'}`, borderRadius: 2, padding: '9px 12px', fontSize: 13, outline: 'none', fontFamily: 'inherit', boxSizing: 'border-box', }; } function Field({ label, children, theme }) { const t = theme || {}; return (
{label}
{children}
); } function Row({ k, v, fg }) { return (
{k} {v}
); } // FAQ accordion function FAQ({ items, theme }) { const [open, setOpen] = useState(-1); const t = theme || {}; return (
{items.map((it, i) => { const isOpen = open === i; return (
{isOpen && (
{it.a}
)}
); })}
); } // Sortable pricing / strategy table function SortableStrategyTable({ data, theme, columns }) { const [sort, setSort] = useState({ key: 'sharpe', dir: 'desc' }); const t = theme || {}; const sorted = useMemo(() => { const out = [...data]; out.sort((a, b) => { const av = a[sort.key], bv = b[sort.key]; if (typeof av === 'number') return sort.dir === 'asc' ? av - bv : bv - av; return sort.dir === 'asc' ? String(av).localeCompare(String(bv)) : String(bv).localeCompare(String(av)); }); return out; }, [data, sort]); function click(key) { setSort(s => s.key === key ? { key, dir: s.dir === 'asc' ? 'desc' : 'asc' } : { key, dir: 'desc' }); } return (
{columns.map(c => ( ))} {sorted.map((row, i) => ( {columns.map(c => ( ))} ))}
c.sortable !== false && click(c.key)} style={{ textAlign: c.align || 'left', padding: '10px 12px', color: t.dim, fontSize: 10, letterSpacing: '0.12em', textTransform: 'uppercase', cursor: c.sortable !== false ? 'pointer' : 'default', userSelect: 'none', fontWeight: 400, whiteSpace: 'nowrap', }}> {c.label} {sort.key === c.key && {sort.dir === 'asc' ? '↑' : '↓'}}
{c.render ? c.render(row) : row[c.key]}
); } // Comparison table vs alt categories (static data below) const COMPARISON = { rows: [ { label: 'Pricing model', apex: 'One-time $15,000', signals: '$50–500 / month', copy: '$50–300 / month + fees', quant: '$500–5,000 / month', prop: 'Profit split 20–50%' }, { label: 'Your capital stays yours', apex: true, signals: true, copy: false, quant: true, prop: false }, { label: 'You hold the API keys', apex: true, signals: false, copy: false, quant: 'Varies', prop: false }, { label: 'Fully automated execution', apex: true, signals: false, copy: true, quant: true, prop: true }, { label: 'Live-account track record', apex: true, signals: 'Rare', copy: 'Shared', quant: 'Often paper', prop: 'Internal' }, { label: 'Multi-asset, multi-TF', apex: true, signals: false, copy: 'Pair-dependent', quant: 'Varies', prop: 'Varies' }, { label: 'Lifetime updates', apex: true, signals: false, copy: false, quant: false, prop: false }, { label: 'Lock-up / rules', apex: 'None', signals: 'None', copy: 'Vendor-driven', quant: 'None', prop: 'Strict drawdown rules' }, ], cols: ['apex', 'signals', 'copy', 'quant', 'prop'], colLabels: { apex: 'Apex Algo', signals: 'Signal / Discord', copy: 'Copy-trading', quant: 'Quant SaaS', prop: 'Prop firm', }, }; function ComparisonTable({ theme }) { const t = theme || {}; function cell(v) { if (v === true) return ; if (v === false) return ; return {v}; } return (
{COMPARISON.cols.map(k => ( ))} {COMPARISON.rows.map((row, i) => ( {COMPARISON.cols.map(k => ( ))} ))}
{COMPARISON.colLabels[k]}
{row.label} {cell(row[k])}
); } return { ApplyModal, FAQ, SortableStrategyTable, ComparisonTable }; })();