/* Techonymous — shared React components (Navbar, Footer, Cursor, Aurora, etc.) Babel-transpiled; components exported to window so other scripts can use them. */ const { useState, useEffect, useRef, useCallback } = React; /* ---------- Tiny inline icon set (Lucide-style strokes) ---------- */ const Icon = ({ name, size = 20, strokeWidth = 1.7, className = "" }) => { const props = { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth, strokeLinecap: "round", strokeLinejoin: "round", className, }; switch (name) { case "arrow-right": return ; case "arrow-up-right":return ; case "check": return ; case "plus": return ; case "menu": return ; case "x": return ; case "code": return ; case "search": return ; case "trending-up": return ; case "target": return ; case "sparkles": return ; case "wand": return ; case "layers": return ; case "zap": return ; case "shield-check": return ; case "users": return ; case "bar-chart": return ; case "compass": return ; case "rocket": return ; case "globe": return ; case "play": return ; case "twitter": return ; case "linkedin": return ; case "instagram": return ; case "youtube": return ; case "star": return ; default: return null; } }; /* ---------- Custom cursor ---------- */ function Cursor() { useEffect(() => { const orb = document.createElement("div"); orb.className = "cursor-orb"; orb.innerHTML = '
'; document.body.appendChild(orb); let x = window.innerWidth/2, y = window.innerHeight/2; let tx = x, ty = y; const onMove = (e) => { tx = e.clientX; ty = e.clientY; }; window.addEventListener("mousemove", onMove); let raf; const tick = () => { x += (tx - x) * 0.22; y += (ty - y) * 0.22; orb.style.transform = `translate(${x}px, ${y}px) translate(-50%, -50%)`; raf = requestAnimationFrame(tick); }; tick(); const matchHover = (el) => el && (el.closest('a, button, .btn, .nav__link, .case-card, .case-filter, .faq-q, .bento__card, [data-cursor="hover"]')); const onOver = (e) => { if (matchHover(e.target)) orb.classList.add("is-hover"); }; const onOut = (e) => { if (matchHover(e.target)) orb.classList.remove("is-hover"); }; document.addEventListener("mouseover", onOver); document.addEventListener("mouseout", onOut); return () => { cancelAnimationFrame(raf); window.removeEventListener("mousemove", onMove); document.removeEventListener("mouseover", onOver); document.removeEventListener("mouseout", onOut); orb.remove(); }; }, []); return null; } /* ---------- Aurora background (animated blobs + noise + grid) ---------- */ function Aurora({ variant = "default" }) { return ( <> > ); } /* ---------- Logo mark ---------- */ function Logo({ size = 45 }) { return (
);
}
/* Icon-only mark — for tight spots or favicons */
function LogoMark({ size = 32 }) {
return (
);
}
/* ---------- Navbar ---------- */
function Navbar({ active = "home" }) {
const [scrolled, setScrolled] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 12);
onScroll();
window.addEventListener("scroll", onScroll, { passive: true });
return () => window.removeEventListener("scroll", onScroll);
}, []);
useEffect(() => {
document.body.style.overflow = mobileOpen ? "hidden" : "";
}, [mobileOpen]);
const base = (typeof window !== "undefined" && window.__base) || "";
const links = [
{ id: "home", label: "Home", href: base + "index.html" },
{ id: "services", label: "Services", href: base + "services/index.html" },
{ id: "work", label: "Work", href: base + "work/index.html" },
{ id: "about", label: "About", href: base + "about/index.html" },
{ id: "blog", label: "Blog", href: "#" },
{ id: "contact", label: "Contact", href: base + "contact/index.html" },
];
return (
<>
{mobileOpen && (
)}
>
);
}
/* ---------- Footer ---------- */
function Footer() {
return (
);
}
/* ---------- ScrollReveal helper ---------- */
function useScrollReveal(rootSelector = ".reveal") {
useEffect(() => {
const els = document.querySelectorAll(rootSelector);
const io = new IntersectionObserver((entries) => {
entries.forEach(e => {
if (e.isIntersecting) {
e.target.classList.add("is-in");
io.unobserve(e.target);
}
});
}, { threshold: 0.12, rootMargin: "0px 0px -8% 0px" });
els.forEach(el => io.observe(el));
return () => io.disconnect();
}, [rootSelector]);
}
/* ---------- Count-up hook ---------- */
function useCountUp(target, { duration = 1800, start = false, decimals = 0 } = {}) {
const [val, setVal] = useState(0);
useEffect(() => {
if (!start) return;
const t0 = performance.now();
let raf;
const tick = (t) => {
const p = Math.min(1, (t - t0) / duration);
const eased = 1 - Math.pow(1 - p, 3);
setVal(target * eased);
if (p < 1) raf = requestAnimationFrame(tick);
};
raf = requestAnimationFrame(tick);
return () => cancelAnimationFrame(raf);
}, [start, target, duration]);
return decimals === 0 ? Math.round(val) : val.toFixed(decimals);
}
/* ---------- Sparkline SVG ---------- */
function Sparkline({ data, color = "#00D4FF", className = "spark" }) {
const max = Math.max(...data);
const min = Math.min(...data);
const w = 200, h = 36;
const stepX = w / (data.length - 1);
const pts = data.map((v, i) => {
const x = i * stepX;
const y = h - ((v - min) / (max - min || 1)) * (h - 4) - 2;
return `${x},${y}`;
});
const d = "M " + pts.join(" L ");
const dArea = d + ` L ${w},${h} L 0,${h} Z`;
return (
);
}
/* ---------- Magnetic button effect ---------- */
function attachMagnetic(el, strength = 18) {
if (!el || el.__magnetic) return;
el.__magnetic = true;
const onMove = (e) => {
const r = el.getBoundingClientRect();
const x = e.clientX - (r.left + r.width / 2);
const y = e.clientY - (r.top + r.height / 2);
el.style.transform = `translate(${x / strength}px, ${y / strength}px)`;
};
const onLeave = () => { el.style.transform = ""; };
el.addEventListener("mousemove", onMove);
el.addEventListener("mouseleave", onLeave);
}
/* Auto-attach magnetic to all .btn-primary */
function MagneticInit() {
useEffect(() => {
document.querySelectorAll(".btn-primary").forEach(b => attachMagnetic(b));
});
return null;
}
/* ---------- Page shell ---------- */
function PageShell({ children, active }) {
return (
<>