/* global React */
const { useState, useEffect, useRef } = React;
// ============================================
// LOGO
// ============================================
window.Logo = function Logo({ size = 26 }) {
return (
);
};
// ============================================
// MAGNETIC CURSOR
// ============================================
window.MagnetCursor = function MagnetCursor() {
const dotRef = useRef(null);
const ringRef = useRef(null);
useEffect(() => {
if (window.matchMedia("(max-width: 720px)").matches) return;
let mouseX = window.innerWidth / 2, mouseY = window.innerHeight / 2;
let ringX = mouseX, ringY = mouseY;
let raf;
const onMove = (e) => {
mouseX = e.clientX; mouseY = e.clientY;
if (dotRef.current) {
dotRef.current.style.left = mouseX + "px";
dotRef.current.style.top = mouseY + "px";
}
const el = document.elementFromPoint(mouseX, mouseY);
const interactive = el && el.closest && el.closest("a, button, .cta, .svc-card, .stack-cell, [data-magnet]");
const textArea = el && el.closest && el.closest("input, textarea, [contenteditable]");
if (dotRef.current && ringRef.current) {
dotRef.current.classList.toggle("large", !!interactive);
dotRef.current.classList.toggle("text", !!textArea);
ringRef.current.classList.toggle("hidden", !!interactive);
}
};
const tick = () => {
ringX += (mouseX - ringX) * 0.18;
ringY += (mouseY - ringY) * 0.18;
if (ringRef.current) {
ringRef.current.style.left = ringX + "px";
ringRef.current.style.top = ringY + "px";
}
raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
window.addEventListener("mousemove", onMove);
return () => {
window.removeEventListener("mousemove", onMove);
cancelAnimationFrame(raf);
};
}, []);
return (
<>
>
);
};
// ============================================
// REVEAL ON SCROLL
// ============================================
window.useReveal = function useReveal() {
useEffect(() => {
const els = document.querySelectorAll(".reveal");
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add("in");
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: "0px 0px -80px 0px" });
els.forEach(el => io.observe(el));
return () => io.disconnect();
});
};
// ============================================
// ANIMATED COUNTER
// ============================================
window.Counter = function Counter({ value, suffix = "", duration = 1600 }) {
const [current, setCurrent] = useState(0);
const ref = useRef(null);
const startedRef = useRef(false);
useEffect(() => {
const node = ref.current;
if (!node) return;
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting && !startedRef.current) {
startedRef.current = true;
const start = performance.now();
const animate = (t) => {
const p = Math.min(1, (t - start) / duration);
const eased = 1 - Math.pow(1 - p, 3);
setCurrent(Math.round(eased * value));
if (p < 1) requestAnimationFrame(animate);
};
requestAnimationFrame(animate);
}
});
}, { threshold: 0.4 });
io.observe(node);
return () => io.disconnect();
}, [value, duration]);
return (
{current}
{suffix}
);
};
// ============================================
// LANGUAGE TOGGLE
// ============================================
window.LangToggle = function LangToggle({ lang, setLang }) {
return (
);
};
// ============================================
// STATUS BAR + NAV
// ============================================
window.StatusBar = function StatusBar({ t }) {
const [time, setTime] = useState("");
useEffect(() => {
const update = () => {
const d = new Date();
const h = String(d.getUTCHours()).padStart(2, "0");
const m = String(d.getUTCMinutes()).padStart(2, "0");
const s = String(d.getUTCSeconds()).padStart(2, "0");
setTime(`${h}:${m}:${s} UTC`);
};
update();
const i = setInterval(update, 1000);
return () => clearInterval(i);
}, []);
return (
{t.status.label}
{t.status.loc}
{t.status.uptime}
{time}
);
};
window.Footer = function Footer({ t }) {
return (
);
};
window.Nav = function Nav({ t, lang, setLang, direction, setDirection, navLinks, navCtaHref }) {
const [menuOpen, setMenuOpen] = useState(false);
const links = navLinks || [
{ href: "#services", idx: "01", label: t.nav.services },
{ href: "#process", idx: "02", label: t.nav.process },
{ href: "#stack", idx: "03", label: t.nav.stack },
{ href: "#contact", idx: "04", label: t.nav.contact },
];
useEffect(() => {
const onKey = (e) => { if (e.key === "Escape") setMenuOpen(false); };
document.addEventListener("keydown", onKey);
return () => document.removeEventListener("keydown", onKey);
}, []);
useEffect(() => {
document.body.style.overflow = menuOpen ? "hidden" : "";
return () => { document.body.style.overflow = ""; };
}, [menuOpen]);
const close = () => setMenuOpen(false);
return (
);
};