/* components.jsx — shared across Site A pages */ const { useState, useEffect, useMemo, useRef } = React; const useS = useState; window.useS = useS; /* ───────── NAV ───────── */ function Nav({ current }) { const [open, setOpen] = useState(false); const links = [ { href: 'index.html', label: 'Home', id: 'home' }, { href: 'indicator.html', label: 'Indicator', id: 'indicator' }, { href: 'guide.html', label: 'Guide', id: 'guide' }, { href: 'course.html', label: 'Course', id: 'course' }, { href: 'tutorials.html', label: 'Tutorials', id: 'tutorials' }, { href: 'pricing.html', label: 'Pricing', id: 'pricing' }, { href: 'about.html', label: 'About', id: 'about' }, { href: 'contact.html', label: 'Contact', id: 'contact' }, ]; return ( ); } /* ───────── FOOTER ───────── */ function Footer() { return ( ); } /* ───────── DASHBOARD WIDGET ───────── */ function Dashboard({ push, score, status, signal, signalText }) { const arrow = (d) => d === 'UP' ? '▲' : d === 'DOWN' ? '▼' : '—'; const pushCls = push === 'UP' ? 'good' : push === 'DOWN' ? 'bad' : ''; const scoreCls = score?.dir === 'UP' ? 'good' : score?.dir === 'DOWN' ? 'bad' : ''; const statusCls = status === 'TRENDING' ? 'good' : status === 'CHOP' ? 'warn' : status === 'LOCKOUT' ? 'bad' : ''; const statusText = status === 'LOCKOUT' ? '🔒 LOCKOUT' : (status || '—'); const sigText = signal === 'BUY' ? '▲ BUY' : signal === 'SELL' ? '▼ SELL' : signal === 'BUY_SETUP' ? '▲ BUY SETUP' : signal === 'SELL_SETUP' ? '▼ SELL SETUP' : signal === 'NO_TRADE' ? '✕ NO TRADE' : signal === 'LOCKOUT_REASON' ? (signalText || 'LOCKOUT') : '—'; const sigCls = signal === 'BUY' || signal === 'BUY_SETUP' ? 'good' : signal === 'SELL' || signal === 'SELL_SETUP' ? 'bad' : signal === 'NO_TRADE' ? 'bad' : signal === 'LOCKOUT_REASON' ? 'bad' : ''; return (
Push
{arrow(push)} {push || '—'}
Score
{arrow(score?.dir)} {score?.value ?? '—'}/10
Status
{statusText}
Signal
{sigText}
); } /* ───────── STORE-LOCK / COMING-SOON OVERLAY ───────── Parameterised so the same component can gate Pricing, Tutorials, etc. Pass a `config` prop with: eyebrow, title, deck, points[], actions[], foot. */ function StoreLockOverlay({ config }) { const c = config || {}; const ghostsRef = useRef(null); const elsRef = useRef([]); const mouseRef = useRef({ x: -9999, y: -9999, active: false }); const rafRef = useRef(null); const [, force] = useState(0); useEffect(() => { const W = window.innerWidth, H = window.innerHeight; const sizes = [56, 64, 70, 80, 60, 110, 130]; ghostsRef.current = sizes.map((size, i) => ({ id: i + 1, size, x: Math.random() * (W - size - 40) + 20, y: Math.random() * (H - size - 40) + 20, vx: (Math.random() - 0.5) * 0.6, vy: (Math.random() - 0.5) * 0.6, phase: Math.random() * Math.PI * 2, phaseSpeed: 0.0006 + Math.random() * 0.0008, rot: (Math.random() - 0.5) * 20, rotPhase: Math.random() * Math.PI * 2, rotSpeed: 0.0004 + Math.random() * 0.0006, rotAmp: 18 + Math.random() * 10, })); force(n => n + 1); }, []); useEffect(() => { const onMove = (e) => { mouseRef.current = { x: e.clientX, y: e.clientY, active: true }; }; const onLeave = () => { mouseRef.current.active = false; }; window.addEventListener('mousemove', onMove); window.addEventListener('mouseleave', onLeave); return () => { window.removeEventListener('mousemove', onMove); window.removeEventListener('mouseleave', onLeave); }; }, []); useEffect(() => { const tick = () => { const arr = ghostsRef.current; if (arr) { const W = window.innerWidth, H = window.innerHeight; const t = performance.now(); for (let i = 0; i < arr.length; i++) { const g = arr[i]; g.phase += g.phaseSpeed * 16; g.vx += Math.cos(g.phase) * 0.012; g.vy += Math.sin(g.phase * 0.9) * 0.012; if (mouseRef.current.active) { const cx = g.x + g.size / 2, cy = g.y + g.size / 2; const ddx = cx - mouseRef.current.x, ddy = cy - mouseRef.current.y; const dist = Math.hypot(ddx, ddy); const radius = 240; if (dist < radius && dist > 0.1) { const f = (1 - dist / radius); g.vx += (ddx / dist) * f * 1.4; g.vy += (ddy / dist) * f * 1.4; } } g.vx *= 0.94; g.vy *= 0.94; const sp = Math.hypot(g.vx, g.vy); if (sp > 9) { g.vx = (g.vx / sp) * 9; g.vy = (g.vy / sp) * 9; } g.x += g.vx; g.y += g.vy; const m = 8; if (g.x < m) { g.x = m; g.vx = Math.abs(g.vx) * 0.6; } if (g.x > W - g.size - m) { g.x = W - g.size - m; g.vx = -Math.abs(g.vx) * 0.6; } if (g.y < m) { g.y = m; g.vy = Math.abs(g.vy) * 0.6; } if (g.y > H - g.size - m) { g.y = H - g.size - m; g.vy = -Math.abs(g.vy) * 0.6; } } // ghost-vs-ghost collisions: treat each as a circle of radius size/2, // push apart along the contact normal and exchange a bit of velocity. for (let i = 0; i < arr.length; i++) { const a = arr[i]; const ar = a.size / 2; const acx = a.x + ar, acy = a.y + ar; for (let j = i + 1; j < arr.length; j++) { const b = arr[j]; const br = b.size / 2; const bcx = b.x + br, bcy = b.y + br; const dx = bcx - acx, dy = bcy - acy; const d = Math.hypot(dx, dy); const min = ar + br; if (d > 0 && d < min) { const nx = dx / d, ny = dy / d; const overlap = (min - d); // separate along the normal a.x -= nx * overlap * 0.5; a.y -= ny * overlap * 0.5; b.x += nx * overlap * 0.5; b.y += ny * overlap * 0.5; // velocity along normal — exchange with mild damping const vrel = (b.vx - a.vx) * nx + (b.vy - a.vy) * ny; if (vrel < 0) { const e = 0.85; // restitution const j2 = -(1 + e) * vrel * 0.5; a.vx -= j2 * nx; a.vy -= j2 * ny; b.vx += j2 * nx; b.vy += j2 * ny; } } } } // pass 2: write transforms for (let i = 0; i < arr.length; i++) { const g = arr[i]; g.rotPhase += g.rotSpeed * 16; const targetRot = Math.sin(g.rotPhase) * Math.min(g.rotAmp, 30); g.rot += (targetRot - g.rot) * 0.04; const el = elsRef.current[i]; if (el) { const bobY = Math.sin(t / 900 + g.id) * 4; el.style.transform = `translate3d(${g.x.toFixed(1)}px, ${(g.y + bobY).toFixed(1)}px, 0) rotate(${g.rot.toFixed(2)}deg)`; } } } rafRef.current = requestAnimationFrame(tick); }; rafRef.current = requestAnimationFrame(tick); return () => cancelAnimationFrame(rafRef.current); }, []); const ghosts = ghostsRef.current || []; return (
{c.eyebrow || 'Coming Soon'}

{c.title || 'In Progress.'}

{c.deck}

{c.points && c.points.length > 0 && ( )} {c.actions && c.actions.length > 0 && (
{c.actions.map((a, i) => ( {a.label} ))}
)} {c.foot && (
{c.foot}
)}
); } window.Nav = Nav; window.Footer = Footer; window.Dashboard = Dashboard; window.StoreLockOverlay = StoreLockOverlay;