feat: redesign homepage — RSC, 8 sections, remove beta form

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-30 06:48:37 -04:00
parent 895a2e7406
commit b26bc5263b
+221 -342
View File
@@ -1,6 +1,4 @@
"use client"; import type { Metadata } from "next";
import { FormEvent, useState } from "react";
import Image from "next/image"; import Image from "next/image";
import Link from "next/link"; import Link from "next/link";
import { import {
@@ -8,112 +6,21 @@ import {
ShieldExclamationIcon, ShieldExclamationIcon,
CurrencyDollarIcon, CurrencyDollarIcon,
BookmarkSquareIcon, BookmarkSquareIcon,
BuildingStorefrontIcon, LinkIcon,
ChatBubbleLeftRightIcon, LockOpenIcon,
} from "@heroicons/react/20/solid"; } from "@heroicons/react/20/solid";
export const metadata: Metadata = {
title: { absolute: "Battl Builders — Free AR-15 Build Planner" },
description:
"Pick compatible AR-15 parts, catch mismatches before you order, and compare live prices from real retailers. Free, no account required.",
};
export default function HomePage() { export default function HomePage() {
const [email, setEmail] = useState("");
const [useCase, setUseCase] = useState("");
const [status, setStatus] = useState<
"idle" | "loading" | "success" | "error"
>("idle");
const [message, setMessage] = useState<string | null>(null);
const [accepted, setAccepted] = useState(false);
const TOS_VERSION = "2025-12-27";
async function handleSubmit(e: FormEvent) {
e.preventDefault();
if (!email) {
setMessage("Drop an email in first, operator.");
setStatus("error");
return;
}
if (!accepted) {
setMessage(
"You must accept the Terms and confirm youre responsible for safe/legal assembly."
);
setStatus("error");
return;
}
try {
setStatus("loading");
setMessage(null);
const res = await fetch("/api/beta-signup", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
email,
useCase,
acceptedTos: accepted,
tosVersion: TOS_VERSION,
}),
});
if (!res.ok) {
const txt = await res.text().catch(() => "");
throw new Error(txt || "Failed to save your signup.");
}
setStatus("success");
setMessage(
"✅ Youre on the list. Invites drop soon — well email your access link when its go-time."
);
setEmail("");
setUseCase("");
} catch (err) {
console.error(err);
setStatus("error");
setMessage("Something broke. Try again in a minute.");
}
}
const features = [
{
name: "Guided part roles",
description: "Pick parts by role so youre not guessing what fits where.",
icon: WrenchScrewdriverIcon,
},
{
name: "Basic compatibility warnings",
description:
"Early guardrails to catch obvious mismatches before you buy.",
icon: ShieldExclamationIcon,
},
{
name: "Live pricing totals",
description:
"See the running total as you build, with real retailer listings.",
icon: CurrencyDollarIcon,
},
{
name: "Save builds",
description: "Save a build to your account so you can finish it later.",
icon: BookmarkSquareIcon,
},
{
name: "Limited retailers (for now)",
description:
"Were starting small with a couple merchants and expanding fast.",
icon: BuildingStorefrontIcon,
},
{
name: "Feedback-driven roadmap",
description:
"Tell us whats confusing, wrong, or missing — well fix that first.",
icon: ChatBubbleLeftRightIcon,
},
];
return ( return (
<main className="min-h-screen bg-black text-zinc-50"> <main className="min-h-screen bg-black text-zinc-50">
{/* Abstract Brand Background */} {/* Grid background — retained from previous implementation */}
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-[0.55]"> <div className="pointer-events-none fixed inset-0 z-0 overflow-hidden opacity-[0.55]">
<svg <svg
width="100%" width="100%"
height="100%" height="100%"
@@ -137,7 +44,6 @@ export default function HomePage() {
strokeOpacity="0.35" strokeOpacity="0.35"
/> />
</pattern> </pattern>
<pattern <pattern
id="battlHex" id="battlHex"
width="140" width="140"
@@ -153,214 +59,129 @@ export default function HomePage() {
/> />
</pattern> </pattern>
</defs> </defs>
<rect width="100%" height="100%" fill="url(#battlGrid)" /> <rect width="100%" height="100%" fill="url(#battlGrid)" />
<rect width="100%" height="100%" fill="url(#battlHex)" /> <rect width="100%" height="100%" fill="url(#battlHex)" />
</svg> </svg>
</div> </div>
<div className="mx-auto flex min-h-screen max-w-5xl flex-col px-4 py-10">
{/* Top Bar / Logo */}
<header className="flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<Image
src="/battl/battl-logo-mark-f.svg"
alt="Battl Builders Logo"
width={260}
height={48}
className="block"
/>
</div>
<nav className="flex items-center gap-2 sm:gap-3">
<Link
href="/builder"
className="rounded-md border border-zinc-800 bg-zinc-950/60 px-3 py-2 text-xs font-medium text-zinc-200 hover:bg-zinc-900 hover:text-white"
>
Go To Builder
</Link>
<div className="relative z-10">
{/* ── 1. Nav ── */}
<nav className="mx-auto flex max-w-5xl items-center justify-between px-4 py-5">
<Image
src="/battl/battl-logo-mark-f.svg"
alt="Battl Builders"
width={260}
height={48}
/>
<div className="flex items-center gap-2">
<Link <Link
href="/login" href="/login"
className="rounded-md border border-amber-500/60 bg-amber-500/15 px-3 py-2 text-xs font-medium text-amber-200 hover:bg-amber-500/20" className="rounded-md border border-zinc-800 bg-zinc-950/60 px-3 py-2 text-xs font-medium text-zinc-200 hover:bg-zinc-900 hover:text-white"
> >
Sign In Sign In
</Link> </Link>
</nav> <Link
</header> href="/builder"
className="rounded-md border border-amber-500/70 bg-amber-500/90 px-3 py-2 text-xs font-semibold text-black hover:bg-amber-400"
>
Start a Build
</Link>
</div>
</nav>
{/* Hero */} {/* ── 2. Hero ── */}
<section className="mt-16 grid gap-10 md:grid-cols-[minmax(0,1.25fr)_minmax(0,1fr)] md:items-start"> <section className="mx-auto max-w-[760px] px-4 pb-16 pt-20 text-center">
<div> <div className="mb-7 inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/40 px-4 py-[5px] text-[10px] uppercase tracking-[0.2em] text-white/55">
<div className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-black/30 px-3 py-1 text-xs uppercase tracking-[0.25em] text-white/70"> <span className="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-amber-400" />
<span className="h-1.5 w-1.5 rounded-full bg-amber-400" /> AR-15 Builder &nbsp;·&nbsp; 100% Free
Private Beta
<span className="text-white/40"></span>
<span className="text-white/60">AR-15 focus</span>
</div>
<h1 className="mt-6 text-balance text-3xl font-semibold tracking-tight sm:text-4xl md:text-5xl">
Stop Guessing.
<span className="block text-amber-300">Start Building.</span>
</h1>
<p className="mt-4 max-w-xl text-sm text-zinc-400 sm:text-base">
Battl Builders helps you plan an AR-15 build faster: choose parts
by role, get basic compatibility guidance, and compare real
listingswithout juggling tabs or spreadsheets.
</p>
<ul className="mt-6 space-y-2 text-sm text-zinc-300">
<li> Guided part roles (no where does this go?)</li>
<li> Basic compatibility warnings</li>
<li> Live pricing from current retailers</li>
<li> Save builds to your account</li>
</ul>
<p className="mt-4 text-xs text-white/50">
Early access beta. Data, pricing, and available parts are still
evolving.
</p>
</div> </div>
{/* Beta Form */} <h1 className="text-[clamp(38px,6.5vw,64px)] font-bold leading-[1.05] tracking-[-0.035em] text-white">
<div Stop Guessing.
id="request-access" <span className="block text-amber-300">Start Building.</span>
className="scroll-mt-24 rounded-lg border border-zinc-800 bg-zinc-950/70 p-5 shadow-[0_0_0_1px_rgba(24,24,27,0.9)]" </h1>
>
<h2 className="text-sm font-semibold uppercase tracking-[0.18em] text-zinc-500">
Join the Beta List
</h2>
<p className="mt-2 text-xs text-zinc-400">
Drop your email and we&apos;ll send early access when the Builder
goes hot. No spam, just builds.
</p>
<form onSubmit={handleSubmit} className="mt-4 space-y-3"> <p className="mx-auto mt-6 max-w-[540px] text-[17px] leading-[1.65] text-zinc-400">
<div> Not sure which parts work together? We&apos;ll walk you through it.
<label Pick your components, get warned about anything that won&apos;t fit,
htmlFor="email" and see real prices from real retailers all in one place.
className="text-xs font-medium text-zinc-300" </p>
>
Email
</label>
<input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="you@gearjunkie.com"
disabled={status === "loading" || status === "success"}
className="mt-1 w-full rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-sm text-zinc-50 outline-none ring-amber-500/30 placeholder:text-zinc-600 focus:border-amber-400/80 focus:ring-2 disabled:cursor-not-allowed disabled:opacity-60"
required
/>
</div>
<div> <div className="mt-10 flex flex-col items-center gap-3">
<label <Link
htmlFor="useCase" href="/builder"
className="text-xs font-medium text-zinc-300" className="inline-flex items-center gap-2 rounded-lg bg-amber-500/90 px-[38px] py-[15px] text-[15px] font-extrabold tracking-[0.03em] text-black shadow-[0_0_40px_rgba(251,191,36,0.2)] hover:bg-amber-400"
> >
What will you use Battl Builders for? Start a Build
<span className="text-zinc-500"> (optional)</span> </Link>
</label> <span className="text-[11px] tracking-[0.05em] text-zinc-600">
<textarea 100% Free · No account required
id="useCase" </span>
value={useCase}
onChange={(e) => setUseCase(e.target.value)}
rows={3}
disabled={status === "loading" || status === "success"}
placeholder="E.g. Planning a build, comparing costs, sanity-checking compatibility..."
className="mt-1 w-full rounded-md border border-zinc-800 bg-zinc-950 px-3 py-2 text-xs text-zinc-50 outline-none ring-amber-500/30 placeholder:text-zinc-600 focus:border-amber-400/80 focus:ring-2 disabled:cursor-not-allowed disabled:opacity-60"
/>
</div>
<div className="rounded-md border border-zinc-800 bg-black/30 p-3">
<label className="flex items-start gap-2 text-[11px] text-zinc-400">
<input
type="checkbox"
checked={accepted}
onChange={(e) => setAccepted(e.target.checked)}
disabled={status === "loading" || status === "success"}
className="mt-0.5 h-4 w-4 rounded border-zinc-700 bg-zinc-950 text-amber-400 focus:ring-2 focus:ring-amber-500/40"
required
/>
<span>
I agree to the{" "}
<Link
href="/tos"
className="text-amber-300 underline hover:text-amber-200"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="text-amber-300 underline hover:text-amber-200"
>
Privacy Policy
</Link>
. I understand Battl Builders is informational only and Im
solely responsible for legality, compatibility, and safe
assembly/use of any firearm or components.
</span>
</label>
</div>
<button
type="submit"
disabled={
status === "loading" || status === "success" || !accepted
}
className="flex w-full items-center justify-center rounded-md border border-amber-500/70 bg-amber-500/90 px-3 py-2 text-sm font-medium text-black transition hover:bg-amber-400 disabled:cursor-not-allowed disabled:opacity-70"
>
{status === "loading"
? "Enlisting…"
: status === "success"
? "On the List ✅"
: "Get Beta Access"}
</button>
{message && (
<p
className={`text-xs ${
status === "success"
? "text-emerald-400"
: status === "error"
? "text-red-400"
: "text-zinc-400"
}`}
>
{message}
</p>
)}
<div className="mt-6 text-center text-[11px] text-zinc-500">
Already invited?{" "}
<a href="/login" className="underline hover:text-zinc-300">
Log in
</a>
</div>
</form>
</div> </div>
</section> </section>
{/* Screen Recording Section */} {/* ── 3. How It Works ── */}
<section className="relative mt-24"> <section className="mx-auto max-w-5xl px-4 py-[72px]">
<div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8"> <p className="mb-3 text-center text-[10px] font-bold uppercase tracking-[0.25em] text-amber-400 opacity-85">
<div className="relative overflow-hidden rounded-2xl border border-white/10 bg-black shadow-2xl"> THE PROCESS
{/* Screen Recording */} </p>
<h2 className="mb-1.5 text-center text-[30px] font-bold tracking-[-0.025em] text-white">
3 Steps. One Battl-Ready Build.
</h2>
<p className="mb-12 text-center text-sm text-zinc-500">
No experience required. No part numbers to memorize.
</p>
<div
className="mx-auto grid max-w-[840px] grid-cols-1 overflow-hidden rounded-xl border border-zinc-900 bg-zinc-900 sm:grid-cols-3"
style={{ gap: "2px" }}
>
{[
{
num: "Step 01",
title: "Pick your parts",
body: "Browse real components organized by what they do — barrel, trigger, lower, upper, and more. No prior knowledge required.",
},
{
num: "Step 02",
title: "Flag the conflicts",
body: "Gas length, buffer weight, caliber — Battl catches the mismatches before you spend money on the wrong part.",
},
{
num: "Step 03",
title: "Price it. Pull the trigger.",
body: "Live prices from real retailers, side by side. Click through to buy. We never touch your order.",
},
].map((step) => (
<div key={step.num} className="bg-[#0d0d0f] px-6 py-8">
<p className="mb-3.5 text-[11px] font-bold uppercase tracking-[0.15em] text-amber-400 opacity-80">
{step.num}
</p>
<h3 className="mb-2.5 text-base font-bold tracking-[-0.01em] text-white">
{step.title}
</h3>
<p className="text-[13px] leading-[1.6] text-zinc-500">
{step.body}
</p>
</div>
))}
</div>
</section>
{/* ── 4. Builder Preview Video ── */}
<section className="px-4 pb-[72px]">
<div className="mx-auto max-w-[900px] overflow-hidden rounded-[14px] border border-white/[0.07] bg-black shadow-[0_24px_80px_rgba(0,0,0,0.7),0_0_0_1px_rgba(251,191,36,0.05)]">
<div className="relative">
<video <video
autoPlay autoPlay
loop loop
muted muted
playsInline playsInline
poster="/builder-preview.png"
className="w-full object-cover" className="w-full object-cover"
> >
<source src="/builder-preview.mp4" type="video/mp4" /> <source src="/builder-preview.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video> </video>
{/* Bottom fade */}
<div <div
aria-hidden="true" aria-hidden="true"
className="pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black" className="pointer-events-none absolute inset-x-0 bottom-0 h-24 bg-gradient-to-t from-black"
@@ -369,70 +190,128 @@ export default function HomePage() {
</div> </div>
</section> </section>
{/* Info Section */} {/* ── 5. Feature Cards ── */}
<section className="mt-14 rounded-2xl border border-white/10 bg-white/5 px-6 py-14 sm:px-8"> <section className="mx-auto max-w-5xl px-4 py-[72px]">
<div className="mx-auto max-w-2xl text-left sm:text-center"> <p className="mb-3 text-center text-[10px] font-bold uppercase tracking-[0.25em] text-amber-400 opacity-85">
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-amber-300/90"> WHAT YOU GET
Everything you need to test the core </p>
</p> <h2 className="mb-2 text-center text-[30px] font-bold tracking-[-0.025em] text-white">
<h2 className="mt-3 text-balance text-3xl font-semibold tracking-tight sm:text-4xl"> Built by gun nerds, for the everyday AR builder.
Build smarter rifles without the spreadsheet lifestyle. </h2>
</h2> <p className="mb-11 text-center text-[13px] uppercase tracking-[0.02em] text-zinc-600">
<p className="mt-4 text-sm leading-relaxed text-white/70 sm:text-base"> NO SUBSCRIPTIONS · NO SPONSORED RANKINGS · NO BULLSHIT
This private beta is focused on one thing: helping you plan an </p>
AR-15 build faster and with fewer mistakes. Its early, its
evolving, and your feedback will decide what we build next.
</p>
</div>
<dl className="mx-auto mt-10 grid max-w-2xl grid-cols-1 gap-x-8 gap-y-8 text-sm text-white/70 sm:grid-cols-2 lg:max-w-none lg:grid-cols-3"> <div
{features.map((feature) => ( className="mx-auto grid max-w-[900px] grid-cols-1 overflow-hidden rounded-xl border border-zinc-900 bg-zinc-900 sm:grid-cols-2 lg:grid-cols-3"
<div style={{ gap: "1px" }}
key={feature.name} >
className="relative rounded-xl border border-white/10 bg-black/30 p-5" {(
> [
<dt className="flex items-center gap-2 text-sm font-semibold text-white"> {
<feature.icon Icon: WrenchScrewdriverIcon,
aria-hidden="true" title: "Build by role, not part number",
className="h-5 w-5 text-amber-300" body: "Parts organized by function. Know what you need, not what SKU to search for.",
/> },
{feature.name} {
</dt> Icon: ShieldExclamationIcon,
<dd className="mt-2 text-sm text-white/70"> title: "Conflicts caught early",
{feature.description} body: "Gas system, buffer weight, caliber — flagged before checkout, not after the box arrives.",
</dd> },
{
Icon: CurrencyDollarIcon,
title: "Real prices. Real retailers.",
body: "Running total with live prices. No estimated MSRP. What it actually costs, today.",
},
{
Icon: BookmarkSquareIcon,
title: "Save your work",
body: "Free account. Save builds, pick up where you left off, run multiple configs in parallel.",
},
{
Icon: LinkIcon,
title: "Share the build",
body: "One link. Your buddy sees the full parts list — no login, no friction, no excuses.",
},
{
Icon: LockOpenIcon,
title: "No gates. No friction.",
body: "Browse and build freely. Create an account only when you're ready to save.",
},
] as const
).map(({ Icon, title, body }) => (
<div key={title} className="bg-[#0d0d0f] p-6">
<Icon
className="mb-3.5 h-5 w-5 text-amber-400"
aria-hidden="true"
/>
<h3 className="mb-1.5 text-[13px] font-bold tracking-[-0.01em] text-zinc-200">
{title}
</h3>
<p className="text-xs leading-[1.6] text-zinc-600">{body}</p>
</div> </div>
))} ))}
</dl> </div>
</section>
<div className="mx-auto mt-10 max-w-2xl rounded-xl border border-white/10 bg-black/30 p-5"> {/* ── 6. SEO Copy Block ── */}
<div className="text-xs font-semibold uppercase tracking-[0.25em] text-white/60"> <section className="border-t border-zinc-900 px-4 py-[72px]">
What feedback we want <div className="mx-auto max-w-[680px]">
</div> <h2 className="mb-4 text-[22px] font-bold tracking-[-0.02em] text-zinc-300">
<ul className="mt-3 space-y-2 text-sm text-white/80"> What is Battl Builders?
<li> Where you got confused or stuck</li> </h2>
<li> Warnings that felt wrong (or missing)</li> <p className="mb-3.5 text-sm leading-[1.75] text-zinc-500">
<li> Parts you expected to find but couldnt</li> Battl Builders is a free{" "}
<li> If this beats your current build-planning process</li> <strong className="font-semibold text-zinc-300">
</ul> AR-15 build planner
<p className="mt-3 text-xs text-white/50"> </strong>{" "}
By joining the private beta, you agree features, data, and pricing the tool you&apos;ve been doing in spreadsheets, rebuilt for how
may change. builders actually work. Select parts by what they do, validate
compatibility as you go, and price your build across multiple
retailers without opening a new tab.
</p>
<p className="mb-3.5 text-sm leading-[1.75] text-zinc-500">
Most{" "}
<strong className="font-semibold text-zinc-300">
AR-15 compatibility mistakes
</strong>{" "}
happen at the parts list stage: wrong gas system length for the
barrel, buffer weight that fights your stock, caliber mismatches
between upper and lower. Battl flags those conflicts automatically,
so you catch them before they cost you a return label.
</p>
<p className="text-sm leading-[1.75] text-zinc-500">
Whether you&apos;re planning your first rifle or spec&apos;ing a
dedicated precision upper, Battl Builders gives you a structured,
guided way to research parts and{" "}
<strong className="font-semibold text-zinc-300">
buy with confidence
</strong>
.
</p> </p>
</div> </div>
</section> </section>
{/* Footer strip */} {/* ── 7. Final CTA ── */}
<footer className="mt-12 border-t border-zinc-900 pt-4 text-xs text-zinc-500"> <section className="border-t border-zinc-900 px-4 py-24 text-center">
<div className="flex flex-wrap items-center justify-between gap-3"> <h2 className="mb-3 text-[36px] font-bold tracking-[-0.03em] text-white">
<span> Your build starts here.
© {new Date().getFullYear()} BATTL BUILDERS </h2>
<span className="align-super text-[0.9em] ml-0.5"></span> <p className="mb-8 text-sm text-zinc-600">
</span> Free. No account required. Takes five minutes to get rolling.
<span className="text-zinc-600"> </p>
v0.1 Import engine online Builder UI in progress <Link
</span> href="/builder"
</div> className="inline-flex items-center gap-2 rounded-lg bg-amber-500/90 px-[38px] py-[15px] text-[15px] font-extrabold tracking-[0.03em] text-black shadow-[0_0_40px_rgba(251,191,36,0.2)] hover:bg-amber-400"
>
Start a Build
</Link>
</section>
{/* ── 8. Footer ── */}
<footer className="mx-auto flex max-w-5xl items-center justify-between border-t border-zinc-900 px-4 py-5 text-[11px] text-zinc-700">
<span>© {new Date().getFullYear()} BATTL BUILDERS</span>
<span>Free AR-15 build planner</span>
</footer> </footer>
</div> </div>
</main> </main>