/* ── CRM VISUAL — light theme, used on login page left side ────────── */ function CRMVisual() { // The "card" we move through a mini pipeline: New → Qualified → Won const [cardCol, setCardCol] = useState(0); // 0,1,2 useEffect(() => { const t = setInterval(() => setCardCol(c => (c + 1) % 4), 1900); // 4 phases incl reset return () => clearInterval(t); }, []); // Live activity feed const ACTIVITY = [ { who: "Priya S.", text: "captured a new lead from", src: "Meta Ads", color: "#1877F2", icon: "spark" }, { who: "Auto-flow", text: "sent WhatsApp follow-up to", src: "Rahul M.", color: "#25D366", icon: "chat" }, { who: "Arjun K.", text: "moved deal to", src: "Closed Won", color: "#10b981", icon: "check" }, { who: "Pipeline", text: "scored hot lead from", src: "Google Ads", color: "#EA4335", icon: "fire" }, { who: "Sara R.", text: "booked demo via", src: "Booking link", color: "#8b5cf6", icon: "cal" }, ]; const [feed, setFeed] = useState([0, 1, 2]); // visible indices, newest first useEffect(() => { const t = setInterval(() => { setFeed(f => [(f[0] + 1) % ACTIVITY.length, f[0], f[1]]); }, 2400); return () => clearInterval(t); }, []); // Animated counters const [leadsToday, setLeadsToday] = useState(128); const [conv, setConv] = useState(34); useEffect(() => { const t = setInterval(() => { setLeadsToday(v => v + Math.floor(Math.random() * 3)); setConv(v => Math.max(28, Math.min(42, v + (Math.random() - 0.5) * 1.2))); }, 2800); return () => clearInterval(t); }, []); // Sparkline const sparkPath = "M 0 30 L 24 22 L 48 26 L 72 12 L 96 18 L 120 6 L 144 14 L 168 4"; const sparkArea = "M 0 30 L 24 22 L 48 26 L 72 12 L 96 18 L 120 6 L 144 14 L 168 4 L 168 40 L 0 40 Z"; // ring math const ringR = 38; const ringC = 2 * Math.PI * ringR; const dash = ringC * (1 - Math.round(conv) / 100); return (
{/* ─── Mini Pipeline ──────────────────────────────────────── */}
{/* header */}

Pipeline · Today

3 teammates viewing

Live
{/* columns */}
{[ { label: "New", color: "#3b82f6", count: 12 }, { label: "Qualified", color: "#8b5cf6", count: 8 }, { label: "Won", color: "#10b981", count: 38 }, ].map((col, ci) => (
{col.label} {col.count}
{/* Fixed cards per column */} {ci === 0 && } {ci === 1 && } {ci === 2 && } {/* The MOVING card: appears in the column matching cardCol */} {cardCol === ci && (
)}
{/* arrow indicator to next column */} {ci < 2 && (
)}
))}
{/* sparkline footer */}

Leads · 7d

{leadsToday}

{/* ─── Floating: live activity toasts ───────────────────────── */}

Activity feed

LIVE
{feed.map((idx, j) => { const a = ACTIVITY[idx]; return (

{a.who} {a.text}

{a.src}

); })}
{/* ─── Floating: conversion ring ────────────────────────────── */}
{Math.round(conv)}%

Conversion

↑ this week

vs 27% last week

{/* ─── Floating: top funnel card ────────────────────────────── */}

+ {leadsToday} new leads

today, auto-captured

); } function MiniCard({ title, sub, tag, tagColor, ring }) { return (
{title}
{sub}
{tag}
); } function ActivityIcon({ kind }) { const p = { width: 13, height: 13, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: 2.2, strokeLinecap: "round", strokeLinejoin: "round" }; if (kind === "spark") return ; if (kind === "check") return ; if (kind === "fire") return ; if (kind === "cal") return ; if (kind === "chat") return ; return null; } Object.assign(window, { CRMVisual });