/* global React */
const { useState, useEffect, useRef, useMemo } = React;
/* ============================================================
GÊNESIS — Logo component (G with diagonal slash)
Recreated faithfully from the brand manual.
============================================================ */
function GenesisLogo({ size = 36, variant = "full", color }) {
if (variant === "full") {
return (
);
}
if (variant === "icon") {
return (
);
}
const fg = color || "currentColor";
return (
);
}
/* Decorative slash motif — appears in section corners */
function SlashMotif({ opacity = 0.06 }) {
return (
);
}
/* ============================================================
Reveal-on-scroll wrapper
============================================================ */
function Reveal({ children, stagger = 0, as: Tag = "div", className = "", ...rest }) {
const ref = useRef(null);
useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver(
(entries) => entries.forEach((e) => e.isIntersecting && el.classList.add("in")),
{ threshold: 0.12, rootMargin: "0px 0px -40px 0px" }
);
io.observe(el);
return () => io.disconnect();
}, []);
return (
{children}
);
}
/* ============================================================
Rotating word (hero)
============================================================ */
function RotatingWord({ words, interval = 2400 }) {
const [i, setI] = useState(0);
useEffect(() => {
const t = setInterval(() => setI((x) => (x + 1) % words.length), interval);
return () => clearInterval(t);
}, [words.length, interval]);
return (
{words[i]}
);
}
/* ============================================================
Eyebrow + structured section header
============================================================ */
function Eyebrow({ children, number }) {
return (
{number && (
{number}
)}
{children}
);
}
/* ============================================================
COUNT UP — número que sobe quando entra na tela
============================================================ */
function CountUp({ value, suffix = "", prefix = "", dur = 1400, decimals = 0 }) {
const [n, setN] = React.useState(0);
const ref = React.useRef(null);
const done = React.useRef(false);
React.useEffect(() => {
const el = ref.current;
if (!el) return;
const io = new IntersectionObserver((entries) => {
if (!entries[0].isIntersecting || done.current) return;
done.current = true;
const t0 = performance.now();
const tick = (now) => {
const t = Math.min((now - t0) / dur, 1);
const e = 1 - Math.pow(1 - t, 3);
setN(value * e);
if (t < 1) requestAnimationFrame(tick);
};
requestAnimationFrame(tick);
}, { threshold: 0.4 });
io.observe(el);
return () => io.disconnect();
}, [value, dur]);
const shown = decimals > 0 ? n.toFixed(decimals) : Math.round(n).toLocaleString("pt-BR");
return {prefix}{shown}{suffix};
}
/* Number badge — for "01 / 06" style */
function StepBadge({ n }) {
return (
{n}
);
}
Object.assign(window, { GenesisLogo, SlashMotif, Reveal, RotatingWord, Eyebrow, StepBadge, CountUp });