"use client";
import * as React from "react";
import { cn } from "@/lib/utils";
import { careersContent, type CareersContent, type Role } from "./data";
import { Navbar } from "./sections/navbar";
import { Hero, Mission } from "./sections/hero";
import { Benefits, Team, Values } from "./sections/culture";
import { Roles } from "./sections/roles";
import { RoleDetail, type Application } from "./sections/role-detail";
import { Cta, Faq, Footer, Process } from "./sections/closing";
import { scrollToId } from "./sections/ui";
export type { CareersContent, Role, Application };
export interface CareersTemplateProps {
/** Override brand, hero, values, team, benefits, roles, process, FAQ or footer. Missing keys fall back to the demo copy. */
content?: Partial<CareersContent>;
/** Called with the form values and CV file after a successful application. */
onApply?: (application: Application) => void;
className?: string;
}
/**
* Nimbus Careers — a company careers page.
* Floating pill navbar, mission hero with CSS-art team collage, scroll-revealed mission quote, values,
* team photo bento, benefits grid, filterable open roles, hiring process timeline, FAQ, CTA and footer —
* plus a role detail view with a validated application form (CV upload with drag & drop).
*/
export function CareersTemplate({ content, onApply, className }: CareersTemplateProps) {
const c: CareersContent = { ...careersContent, ...content };
const [roleId, setRoleId] = React.useState<string | null>(null);
const pending = React.useRef<string | null | undefined>(undefined);
const homeScroll = React.useRef(0);
const role = roleId ? c.roles.items.find((r) => r.id === roleId) : undefined;
React.useLayoutEffect(() => {
const t = pending.current;
if (t === undefined) return;
pending.current = undefined;
if (t === "__restore") window.scrollTo({ top: homeScroll.current, behavior: "auto" });
else if (t) requestAnimationFrame(() => scrollToId(t));
else {
window.scrollTo({ top: 0, behavior: "auto" });
document.getElementById("nc-role-title")?.focus({ preventScroll: true });
}
}, [roleId]);
const openRole = (id: string) => {
if (!roleId) homeScroll.current = window.scrollY;
pending.current = null;
setRoleId(id);
};
const goHome = (anchor?: string) => {
if (!roleId) {
if (anchor) scrollToId(anchor);
else window.scrollTo({ top: 0, behavior: "smooth" });
return;
}
pending.current = anchor ?? "__restore";
setRoleId(null);
};
const footerNav = (label: string) => {
const map: Record<string, string> = { "Open roles": "roles", "Life at Nimbus": "team", Benefits: "benefits", "Hiring process": "process" };
if (map[label]) goHome(map[label]);
};
return (
<div className={cn("relative w-full overflow-x-clip bg-background text-foreground antialiased", className)}>
<a
href="#nc-main"
className="sr-only z-[90] rounded-md bg-foreground px-3 py-2 text-sm text-background focus:not-sr-only focus:fixed focus:left-3 focus:top-3"
>
Skip to content
</a>
<Navbar brand={c.brand} links={c.nav} onNav={(h) => goHome(h.slice(1))} onHome={() => goHome()} cta="Open roles" spy={!role} />
<main id="nc-main">
{role ? (
<RoleDetail key={role.id} role={role} onBack={() => goHome("roles")} onApplySubmit={onApply} />
) : (
<>
<Hero hero={c.hero} onRoles={() => scrollToId("roles")} onTeam={() => scrollToId("team")} />
<Mission mission={c.mission} />
<Values values={c.values} />
<Team team={c.team} />
<Benefits benefits={c.benefits} />
<Roles roles={c.roles} onOpen={openRole} />
<Process process={c.process} />
<Faq faq={c.faq} />
<Cta cta={c.cta} onRoles={() => scrollToId("roles")} />
</>
)}
</main>
<Footer brand={c.brand} footer={c.footer} onNav={footerNav} />
</div>
);
}
export default CareersTemplate;