// Shared primitives - logo, icons, animated helpers
const { useState, useEffect, useRef, useMemo } = React;
// Compact mark echoing the logo's language (hex + eye + up-arrow) - original composition
function LogoMark({ size = 36 }) {
return (
);
}
function Eyebrow({ children, tone = "" }) {
return
{children}
;
}
// Subtle scrolling hex backdrop for any section
function HexBackdrop() {
return ;
}
// Ticker/counter that animates to target
function useCountUp(target, duration = 1400, start = 0) {
const [v, setV] = useState(start);
useEffect(() => {
let raf, t0;
function tick(t) {
if (!t0) t0 = t;
const p = Math.min(1, (t - t0) / duration);
const eased = 1 - Math.pow(1 - p, 3);
setV(start + (target - start) * eased);
if (p < 1) raf = requestAnimationFrame(tick);
}
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [target, duration]);
return v;
}
function useInView(ref, opts = { threshold: 0.05, rootMargin: "0px 0px -10% 0px" }) {
const [seen, setSeen] = useState(false);
useEffect(() => {
if (!ref.current) return;
// If already in viewport on mount, fire immediately
const rect = ref.current.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
setSeen(true);
return;
}
const io = new IntersectionObserver(([e]) => {
if (e.isIntersecting) { setSeen(true); io.disconnect(); }
}, opts);
io.observe(ref.current);
return () => io.disconnect();
}, []);
return seen;
}
// Simple SVG line chart used in hero console
function MiniChart({ data, w = 520, h = 220, stroke = "#2BE876", fill = "rgba(43,232,118,0.18)", gridColor = "#163544" }) {
const max = Math.max(...data);
const min = Math.min(...data);
const pad = 20;
const pts = data.map((v, i) => {
const x = pad + (i / (data.length - 1)) * (w - pad * 2);
const y = h - pad - ((v - min) / (max - min || 1)) * (h - pad * 2);
return [x, y];
});
const d = pts.map((p, i) => (i === 0 ? "M" : "L") + p[0].toFixed(1) + "," + p[1].toFixed(1)).join(" ");
const area = d + ` L ${w - pad},${h - pad} L ${pad},${h - pad} Z`;
// gridlines
const ys = [0.25, 0.5, 0.75];
return (
);
}
Object.assign(window, { LogoMark, Eyebrow, HexBackdrop, useCountUp, useInView, MiniChart });