/* Mega menu data */
const MEGA_MENUS = {
projects: {
cols: 1,
items: [
{ label: "FiveM Scripts", sub: "01", desc: "Server-side systems and tools. The first complete project.", href: "fivem.html" },
{ label: "Untitled Tool", sub: "02", desc: "A small utility quietly in development.", href: "#", soon: true },
{ label: "Notebook", sub: "03", desc: "Notes, experiments, and writing. Public when ready.", href: "#", soon: true },
],
},
fivem: {
cols: 2,
left: [
{ label: "Overview", desc: "What FiveM scripts we ship and why." },
{ label: "Faction", desc: "Group structures, ranks, shared tooling." },
{ label: "Sidejobs", desc: "Small, repeatable work for downtime." },
{ label: "Robbery", desc: "Heist and stickup systems with real friction." },
],
right: [
{ label: "Core", desc: "Foundations the rest of the suite leans on." },
{ label: "Jobs", desc: "Whitelisted work — the structured side." },
{ label: "Custom work", desc: "Need something specific? Write to us." },
],
footer: { label: "Browse all 18 scripts →", href: "fivem.html" },
},
manifesto: {
cols: 1,
items: [
{ label: "Why after hours?", sub: "i", desc: "Ideas breathe best without a roadmap." },
{ label: "How we build", sub: "ii", desc: "One complete thought at a time, shipped when ready." },
{ label: "Who it's for", sub: "iii", desc: "Server owners who'd rather read docs than guess." },
],
},
contact: {
cols: 1,
items: [
{ label: "Discord", sub: "↗", desc: "Fastest way to reach us — community + support.", href: "https://discord.gg/afterhoursofficial" },
{ label: "Email", sub: "↗", desc: "For custom work and longer conversations.", href: "#" },
{ label: "Custom work", sub: "→", desc: "After-hours builds happen after-hours — write us.", href: "#" },
],
},
};
/* Single mega dropdown panel */
const MegaMenu = ({ id, data, visible }) => {
if (!data) return null;
return (
{data.cols === 2 ? (
) : (
)}
);
};
/* Top nav with theme toggle, scroll-state, and mega menus */
const Nav = ({ theme, onToggleTheme }) => {
const [scrolled, setScrolled] = React.useState(false);
const [openMenu, setOpenMenu] = React.useState(null);
const closeTimer = React.useRef(null);
const navRef = React.useRef(null);
React.useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 24);
window.addEventListener("scroll", onScroll, { passive: true });
onScroll();
return () => window.removeEventListener("scroll", onScroll);
}, []);
// Close on click outside
React.useEffect(() => {
const handler = (e) => {
if (navRef.current && !navRef.current.contains(e.target)) {
setOpenMenu(null);
}
};
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, []);
const handleEnter = (id) => {
clearTimeout(closeTimer.current);
setOpenMenu(id);
};
const handleLeave = () => {
closeTimer.current = setTimeout(() => setOpenMenu(null), 120);
};
const isFivem = typeof location !== "undefined" && /fivem/i.test(location.pathname);
const home = isFivem ? "index.html" : "#top";
const link = (anchor) => isFivem ? `index.html${anchor}` : anchor;
const isDark = theme === "dark";
const NAV_ITEMS = [
{ id: "manifesto", label: "Manifesto", href: link("#manifesto") },
{ id: "projects", label: "Projects", href: link("#projects") },
{ id: "fivem", label: "FiveM", href: "fivem.html" },
{ id: "contact", label: "Contact", href: link("#contact") },
];
return (
);
};
/* HERO */
const Hero = () => {
return (
Studio open · 24/7
est. MMXXVI
v 01 — fivem
Built after
hours.
Released for everyone.
After-Hours is a quiet studio for unusual software.
One project at a time, made carefully, shipped without fanfare.
01 / Currently shipping
FiveM scripts
View work →
);
};
/* TICKER */
const Ticker = () => {
const items = [
"Built after hours",
"Released for everyone",
"Scripts. Tools. Strange ideas.",
"Quiet software, loud care",
"FiveM · Vol. 01",
];
const Row = () => (
{items.map((t, i) => (
{t}
))}
);
return (
);
};
Object.assign(window, { Nav, Hero, Ticker });