// PEN Landing — Live console mock for hero section. // Shows a compact "operations" panel with live-feel: // - scan progress bar that loops // - threat feed where rows reveal one-by-one then animate "containment" // - tabular numeric KPIs const { useState: useStateLC, useEffect: useEffectLC, useRef: useRefLC } = React; function LiveConsole({ copy }) { // ── Scan progress loop ───────────────────────────────────── const [scan, setScan] = useStateLC(0.12); useEffectLC(() => { const t = setInterval(() => { setScan((p) => p >= 0.98 ? 0.08 : p + 0.018); }, 220); return () => clearInterval(t); }, []); // ── Clock ticks (for the "ts" col + status) ──────────────── const [tick, setTick] = useStateLC(0); useEffectLC(() => { const t = setInterval(() => setTick((x) => x + 1), 1000); return () => clearInterval(t); }, []); // ── Threat feed: reveal rows, then mark them contained ───── const baseFeed = copy.threats; const [revealed, setRevealed] = useStateLC(2); // start with 2 rows visible const [contained, setContained] = useStateLC({}); // index -> true useEffectLC(() => { let r = 2; setRevealed(2); setContained({}); const revealTimer = setInterval(() => { r++; if (r > baseFeed.length) { clearInterval(revealTimer); } else { setRevealed(r); } }, 1100); // After 4s start containing the criticals one-by-one const containTimer = setTimeout(() => { let idx = 0; const ct = setInterval(() => { setContained((prev) => ({ ...prev, [idx]: true })); idx++; if (idx >= 2) clearInterval(ct); }, 1600); }, 4200); return () => {clearInterval(revealTimer);clearTimeout(containTimer);}; }, [copy]); // ── KPI nudges (animated count) ──────────────────────────── const kpis = copy.kpis; const hostsScanned = Math.round(scan * 217); const eta = String(Math.max(2, Math.round((1 - scan) * 90))).padStart(2, "0"); return (
{/* window chrome */}
{copy.eyebrow} LIVE
v2026.05.22
{/* KPI strip */}
{kpis.map((k, i) =>
{k.label}
{k.value} {k.unit && {k.unit}}
{k.foot}
)}
{/* Feed */}
{copy.feedEyebrow}
{copy.feedTitle}
stream
{baseFeed.slice(0, revealed).map((t, i) => { const sev = t.sev; const wasContained = contained[i]; return (
{t.title}
{t.host} {t.cve}
{wasContained ? ● {t.action} : {t.ts} }
); })}
{/* Scan side */}
{copy.scanLabel}
{copy.scanTitle}
{copy.scanCmd}
{hostsScanned} / 217 hosts ETA 00:00:{eta}
{/* Operator log */}
OPERATOR · LOG
▸ pen ops watch --org=acme --tier=managed
); } // Operator log — types out commands one at a time function OperatorLog({ tick }) { const lines = [ { t: "00:00:04", k: "ok", x: "▸ contain lax-04.pen.io" }, { t: "00:00:05", k: "ok", x: " ↳ host isolated · policy:fence-prod-v3" }, { t: "00:01:42", k: "ok", x: "▸ contain nyc-12.pen.io" }, { t: "00:02:11", k: "info", x: " ↳ forensic snapshot captured · 2.4 GB" }, { t: "00:04:21", k: "ok", x: "▸ incident IR-2026-04117 closed" }]; // staged reveal const N = Math.min(lines.length, Math.floor(tick / 1.6) + 1); return (
{lines.slice(0, N).map((l, i) =>
{l.t} {l.k === "ok" ? "●" : "◇"} {l.x}
)} {N < lines.length &&
}
); } Object.assign(window, { LiveConsole });