// Graphite Ledger theme — shared across all screens const TH = { bg: '#13130f', bg2: '#0d0d0a', panel: '#1c1c17', panelHi: '#23231d', ink: '#e8e4d6', inkSoft: '#9a9485', muted: '#5f5a4e', mutedHi: '#7a7464', rule: '#2a2a22', ruleHi: '#363630', accent: '#d4a84b', accentDim: '#8a6f2e', accentHi: '#e8bb5a', red: '#c86a5a', green: '#7ba15c', serif: '"Source Serif 4", "Source Serif Pro", Georgia, serif', sans: '"Inter", -apple-system, system-ui, sans-serif', mono: '"JetBrains Mono", ui-monospace, monospace', }; // ── Logos ─────────────────────────────────────────────────── function Logo({ variant = 'bars', size = 24, color }) { const c = color || TH.accent; if (variant === 'bars') { return ( ); } if (variant === 'arrow') { // Forward-stepping arrow — walk-forward metaphor return ( ); } if (variant === 'w') { // Serif W with an underline tick return ( W ); } return null; } // ── Common button ─────────────────────────────────────────── function Btn({ children, onClick, primary, ghost, small, style = {}, type = 'button' }) { const base = { padding: small ? '7px 14px' : '12px 20px', fontFamily: TH.mono, fontSize: small ? 11 : 12, fontWeight: primary ? 700 : 500, textTransform: 'uppercase', letterSpacing: 1.2, border: '1px solid', cursor: 'pointer', textAlign: 'center', userSelect: 'none', transition: 'all .15s', display: 'inline-flex', alignItems: 'center', justifyContent: 'center', gap: 8, }; const styles = primary ? { ...base, background: TH.accent, color: TH.bg, borderColor: TH.accent, ...style } : ghost ? { ...base, background: 'transparent', color: TH.inkSoft, borderColor: TH.rule, ...style } : { ...base, background: 'transparent', color: TH.ink, borderColor: TH.ruleHi, ...style }; return ; } // ── Ticker strip ──────────────────────────────────────────── function Ticker() { const SYMBOLS = ['BTCUSDT','ETHUSDT','SOLUSDT','AVAXUSDT','ARBUSDT','LINKUSDT','DOGEUSDT']; const LABELS = { BTCUSDT:'BTC', ETHUSDT:'ETH', SOLUSDT:'SOL', AVAXUSDT:'AVAX', ARBUSDT:'ARB', LINKUSDT:'LINK', DOGEUSDT:'DOGE' }; const [ticks, setTicks] = React.useState([]); const fetchPrices = React.useCallback(() => { fetch(`https://api.binance.com/api/v3/ticker/24hr?symbols=${JSON.stringify(SYMBOLS)}`) .then(r => r.json()) .then(data => setTicks(data.map(d => ({ label: LABELS[d.symbol], price: parseFloat(d.lastPrice), pct: parseFloat(d.priceChangePercent), })))) .catch(() => {}); }, []); React.useEffect(() => { fetchPrices(); const id = setInterval(fetchPrices, 10000); return () => clearInterval(id); }, [fetchPrices]); const fmt = (v) => v >= 1000 ? v.toLocaleString(undefined, { maximumFractionDigits: 0 }) : v >= 1 ? v.toFixed(2) : v.toFixed(4); if (!ticks.length) return (
LOADING PRICES…
); return (
{ticks.map((tk, i) => (
{tk.label} {fmt(tk.price)} = 0 ? TH.green : TH.red }}> {tk.pct >= 0 ? '+' : ''}{tk.pct.toFixed(2)}%
))}
); } // ── Nav ───────────────────────────────────────────────────── function Nav({ route, go, user, logoVariant = 'bars' }) { const [mobileOpen, setMobileOpen] = React.useState(false); const isWide = useWide(); const items = user ? [['home', 'Dashboard'], ['backtester', 'Backtest'], ['videos', 'Videos'], ['telegram', 'Community']] : [['landing', 'Home'], ['pricing', 'Pricing'], ['videos', 'Preview']]; return (
go(user ? 'home' : 'landing')}>
Walkforward.
{isWide && (
{items.map(([k, label]) => ( go(k)} style={{ cursor: 'pointer', color: route === k ? TH.accent : TH.inkSoft, borderBottom: route === k ? `1px solid ${TH.accent}` : '1px solid transparent', paddingBottom: 3, }}>{label} ))}
)}
{user ? ( <> {isWide && go('billing')} style={{ fontSize: 12, color: TH.inkSoft, cursor: 'pointer', fontFamily: TH.mono, textTransform: 'uppercase', letterSpacing: 1 }}>Billing}
go('home')} style={{ width: 32, height: 32, borderRadius: '50%', background: TH.panelHi, display: 'flex', alignItems: 'center', justifyContent: 'center', fontFamily: TH.mono, fontSize: 12, color: TH.accent, cursor: 'pointer', border: `1px solid ${TH.rule}`, }}>{(user.name || 'U')[0].toUpperCase()}
) : ( <> {isWide && go('login')} style={{ fontSize: 13, color: TH.inkSoft, cursor: 'pointer' }}>Log in} go('signup')}>Join → )} {!isWide && (
setMobileOpen(v => !v)} style={{ padding: 6, color: TH.ink, cursor: 'pointer' }}>
)}
{!isWide && mobileOpen && (
{items.map(([k, label]) => (
{ go(k); setMobileOpen(false); }} style={{ padding: '12px 0', color: route === k ? TH.accent : TH.inkSoft, borderBottom: `1px solid ${TH.rule}`, cursor: 'pointer', }}>{label}
))} {user && (
{ go('billing'); setMobileOpen(false); }} style={{ padding: '12px 0', color: TH.inkSoft, cursor: 'pointer' }}>Billing
)}
)}
); } // ── Footer ────────────────────────────────────────────────── function Footer() { const wide = useWide(); return (
Walkforward.
A journal & workbench for systematic crypto traders. Built by quants, open to anyone willing to read the data.
{[ ['Product', ['Backtester', 'Research memos', 'Video library', 'Telegram']], ['Company', ['Methodology', 'Changelog', 'Press', 'Contact']], ['Legal', ['Terms', 'Privacy', 'Risk disclosure', 'Not advice']], ].map(([title, links]) => (
{title.toUpperCase()}
{links.map(l =>
{l}
)}
))}
© 2026 WALKFORWARD RESEARCH NOT INVESTMENT ADVICE · TRADE AT YOUR OWN RISK BILLING VIA WHOP
); } // Hook — viewport width >= 820 function useWide() { const [w, setW] = React.useState(typeof window !== 'undefined' ? window.innerWidth >= 820 : true); React.useEffect(() => { const h = () => setW(window.innerWidth >= 820); window.addEventListener('resize', h); return () => window.removeEventListener('resize', h); }, []); return w; } Object.assign(window, { TH, Logo, Btn, Ticker, Nav, Footer, useWide });