// Components for Holistic Radar landing page // Exports: Radar, Pyramid, Icon const { useState, useEffect, useRef } = React; // ============ RADAR ============ function Radar({ pings = true }) { const [angle, setAngle] = useState(0); const rafRef = useRef(null); useEffect(() => { if (!pings) return; let last = performance.now(); const tick = (t) => { const dt = (t - last) / 1000; last = t; setAngle((a) => (a + dt * 60) % 360); // 60deg/s rafRef.current = requestAnimationFrame(tick); }; rafRef.current = requestAnimationFrame(tick); return () => cancelAnimationFrame(rafRef.current); }, [pings]); // fixed blip positions (so they don't jump on re-render) const blips = [ { r: 0.30, theta: 30, label: "Vision" }, { r: 0.50, theta: 90, label: "Strategy" }, { r: 0.62, theta: 160, label: "Brand" }, { r: 0.42, theta: 215, label: "Systems" }, { r: 0.68, theta: 285, label: "Sales" }, { r: 0.25, theta: 335, label: "Self" }, ]; return ( ); } // ============ PYRAMID ============ function Pyramid({ tiers, activeIdx, onSelect }) { // tiers ordered top -> bottom const total = tiers.length; const baseWidth = 100; // % const topWidth = 28; return (
{tiers.map((tier, i) => { const isActive = i === activeIdx; // i=0 is top tier (smallest) const rowH = 58; const y = 24 + i * rowH; const t1 = i / total; const t2 = (i + 1) / total; const w1 = topWidth + (baseWidth - topWidth) * t1; const w2 = topWidth + (baseWidth - topWidth) * t2; const cx = 250; const x1L = cx - (w1 / 2) * 4.4; const x1R = cx + (w1 / 2) * 4.4; const x2L = cx - (w2 / 2) * 4.4; const x2R = cx + (w2 / 2) * 4.4; return ( onSelect(i)} style={{ cursor: "pointer" }} > {/* tier number */} T{total - i} {/* tier label */} {tier.title} {/* keyword on right */} {tier.keyword.toUpperCase()} ); })}
); } // ============ Icon ============ function Icon({ name, size = 22 }) { const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", }; switch (name) { case "saas": return (); case "ecom": return (); case "founder": return (); case "arrow": return (); case "play": return (); default: return null; } } Object.assign(window, { Radar, Pyramid, Icon });