);
}
// ----------------------------------------------------------------------------
// Problem
// ----------------------------------------------------------------------------
function Problem({ t }) {
return (
{t.problem.eyebrow}
{t.problem.title}
{t.problem.sub}
{t.problem.items.map((it, i) => (
{it.n}
{it.t}
{it.d}
))}
);
}
// ----------------------------------------------------------------------------
// Showcase — full-bleed editorial image
// ----------------------------------------------------------------------------
function Showcase({ t }) {
return (
{t.showcase.meta}
);
}
// ----------------------------------------------------------------------------
// Platform
// ----------------------------------------------------------------------------
function Platform({ t }) {
return (
{t.platform.eyebrow}
{t.platform.title}
{t.platform.sub}
{t.platform.features.map((f, i) => (
{f.k}
{f.t}
{f.d}
{f.tag}
))}
);
}
// ----------------------------------------------------------------------------
// Architecture — flow with pulse
// ----------------------------------------------------------------------------
function Architecture({ t }) {
const wrapRef = useRef(null);
const [active, setActive] = useState(0);
useEffect(() => {
const el = wrapRef.current;
if (!el) return;
let timer = null;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting) {
// Start cycling
if (!timer) {
timer = setInterval(() => {
setActive((a) => (a + 1) % t.architecture.nodes.length);
}, 1800);
}
} else {
if (timer) { clearInterval(timer); timer = null; }
}
});
}, { threshold: 0.3 });
io.observe(el);
return () => {
io.disconnect();
if (timer) clearInterval(timer);
};
}, [t.architecture.nodes.length]);
return (
{t.architecture.eyebrow}
{t.architecture.title}
{t.architecture.sub}
{t.architecture.nodes.map((n, i) => (
0{i + 1}
{n.t}
{n.d}
{n.tag}
{i < t.architecture.nodes.length - 1 && }
))}
);
}
// ----------------------------------------------------------------------------
// AI Assistant with animated chat
// ----------------------------------------------------------------------------
function AI({ t }) {
const [visible, setVisible] = useState(0);
const wrapRef = useRef(null);
const startedRef = useRef(false);
useEffect(() => {
const el = wrapRef.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting && !startedRef.current) {
startedRef.current = true;
let i = 0;
const total = t.ai.chat.length;
const step = () => {
i++;
setVisible(i);
if (i < total) setTimeout(step, 1400);
};
setTimeout(step, 400);
}
});
}, { threshold: 0.35 });
io.observe(el);
return () => io.disconnect();
}, [t.ai.chat.length]);
// Reset when language changes
useEffect(() => {
startedRef.current = false;
setVisible(0);
const el = wrapRef.current;
if (!el) return;
// Retrigger check
const rect = el.getBoundingClientRect();
if (rect.top < window.innerHeight && rect.bottom > 0) {
startedRef.current = true;
let i = 0;
const total = t.ai.chat.length;
const step = () => {
i++;
setVisible(i);
if (i < total) setTimeout(step, 1200);
};
setTimeout(step, 300);
}
}, [t]);
const renderBubble = (text) => {
// Support **bold**
const parts = text.split(/(\*\*[^*]+\*\*)/g);
return parts.map((p, i) =>
p.startsWith("**") && p.endsWith("**")
? {p.slice(2, -2)}
: {p}
);
};
return (
{t.ai.eyebrow}
{t.ai.title}
{t.ai.sub}
{t.ai.bullets.map((b, i) =>
{b}
)}
peopleflow · ai assistant · mcp
{t.ai.chat.map((m, i) => (
{renderBubble(m.text)}
))}
{'>'}{t.ai.chat[0].text.slice(0, 24)}...
);
}
// ----------------------------------------------------------------------------
// Security
// ----------------------------------------------------------------------------
function Security({ t }) {
return (
{t.security.eyebrow}
{t.security.title}
{t.security.sub}
{t.security.items.map((it, i) => (
{it.t}
{it.d}
))}
);
}
// ----------------------------------------------------------------------------
// Comparison
// ----------------------------------------------------------------------------
function Compare({ t }) {
return (
{t.compare.eyebrow}
{t.compare.title}
{t.compare.cols[0]}
{t.compare.cols[1]}
{t.compare.cols[2]}
{t.compare.rows.map((r, i) => (
{r.l}
{r.v[0]}
{r.v[1]}
{r.v[2]}
))}
);
}
// ----------------------------------------------------------------------------
// Stats — animated counters
// ----------------------------------------------------------------------------
function StatValue({ value }) {
// Extract leading number if any, animate it; else just render
const numMatch = String(value).match(/^([≥]?)(\d+(?:[.,]\d+)?)(%?.*)$/);
const ref = useRef(null);
const done = useRef(false);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
entries.forEach((e) => {
if (e.isIntersecting && !done.current) {
done.current = true;
if (!numMatch) return;
const prefix = numMatch[1] || "";
const target = parseFloat(numMatch[2].replace(",", "."));
const suffix = numMatch[3] || "";
const isInt = !numMatch[2].includes(".") && !numMatch[2].includes(",");
const dur = 1200;
const t0 = performance.now();
const step = (t) => {
const p = Math.min(1, (t - t0) / dur);
const eased = 1 - Math.pow(1 - p, 3);
const v = target * eased;
el.textContent = prefix + (isInt ? Math.round(v) : v.toFixed(1)) + suffix;
if (p < 1) requestAnimationFrame(step);
else el.textContent = value;
};
requestAnimationFrame(step);
}
});
}, { threshold: 0.5 });
io.observe(el);
return () => io.disconnect();
}, [value]);
// Fallback: render the real value so it's readable even if the observer
// never fires (JS off, some sandboxed iframes, etc). The animation just
// overwrites textContent when it starts.
return
{value}
;
}
function Stats({ t }) {
return (
— 2026
{t.stats.title}
{t.stats.items.map((it, i) => (
{it.l}
{it.sub}
))}
);
}
// ----------------------------------------------------------------------------
// BI Integration
// ----------------------------------------------------------------------------
function BI({ t }) {
return (
{t.bi.eyebrow}
{t.bi.title}
{t.bi.sub}
{t.bi.badges.map((b, i) => {b})}
{t.bi.items.map((it, i) => (
{it.t}
{it.d}
))}
);
}
// ----------------------------------------------------------------------------
// Testimonials
// ----------------------------------------------------------------------------
function Testimonials({ t }) {
return (
{t.testimonials.eyebrow}
{t.testimonials.title}
{t.testimonials.items.map((it, i) => (
"
{it.quote}
{it.name}
{it.role}
))}
);
}
// ----------------------------------------------------------------------------
// FAQ
// ----------------------------------------------------------------------------
function FAQ({ t }) {
const [open, setOpen] = useState(0);
return (
{t.faq.eyebrow}
{t.faq.title}
{t.faq.items.map((it, i) => (
setOpen(open === i ? -1 : i)}
>
{it.q}+
{it.a}
))}
);
}
// ----------------------------------------------------------------------------
// Final CTA
// ----------------------------------------------------------------------------
function FinalCTA({ t }) {
return (