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
+201 -322
View File
@@ -1,6 +1,4 @@
"use client";
import { FormEvent, useState } from "react";
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
import {
@@ -8,112 +6,21 @@ import {
ShieldExclamationIcon,
CurrencyDollarIcon,
BookmarkSquareIcon,
BuildingStorefrontIcon,
ChatBubbleLeftRightIcon,
LinkIcon,
LockOpenIcon,
} 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() {
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 (
<main className="min-h-screen bg-black text-zinc-50">
{/* Abstract Brand Background */}
<div className="pointer-events-none absolute inset-0 overflow-hidden opacity-[0.55]">
{/* Grid background — retained from previous implementation */}
<div className="pointer-events-none fixed inset-0 z-0 overflow-hidden opacity-[0.55]">
<svg
width="100%"
height="100%"
@@ -137,7 +44,6 @@ export default function HomePage() {
strokeOpacity="0.35"
/>
</pattern>
<pattern
id="battlHex"
width="140"
@@ -153,214 +59,129 @@ export default function HomePage() {
/>
</pattern>
</defs>
<rect width="100%" height="100%" fill="url(#battlGrid)" />
<rect width="100%" height="100%" fill="url(#battlHex)" />
</svg>
</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">
<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 Logo"
alt="Battl Builders"
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="flex items-center gap-2">
<Link
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
</Link>
<Link
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>
</header>
{/* Hero */}
<section className="mt-16 grid gap-10 md:grid-cols-[minmax(0,1.25fr)_minmax(0,1fr)] md:items-start">
<div>
<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 rounded-full bg-amber-400" />
Private Beta
<span className="text-white/40"></span>
<span className="text-white/60">AR-15 focus</span>
{/* ── 2. Hero ── */}
<section className="mx-auto max-w-[760px] px-4 pb-16 pt-20 text-center">
<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">
<span className="h-1.5 w-1.5 flex-shrink-0 rounded-full bg-amber-400" />
AR-15 Builder &nbsp;·&nbsp; 100% Free
</div>
<h1 className="mt-6 text-balance text-3xl font-semibold tracking-tight sm:text-4xl md:text-5xl">
<h1 className="text-[clamp(38px,6.5vw,64px)] font-bold leading-[1.05] tracking-[-0.035em] text-white">
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 className="mx-auto mt-6 max-w-[540px] text-[17px] leading-[1.65] text-zinc-400">
Not sure which parts work together? We&apos;ll walk you through it.
Pick your components, get warned about anything that won&apos;t fit,
and see real prices from real retailers all in one place.
</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>
{/* Beta Form */}
<div
id="request-access"
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)]"
>
<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">
<div>
<label
htmlFor="email"
className="text-xs font-medium text-zinc-300"
>
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>
<label
htmlFor="useCase"
className="text-xs font-medium text-zinc-300"
>
What will you use Battl Builders for?
<span className="text-zinc-500"> (optional)</span>
</label>
<textarea
id="useCase"
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{" "}
<div className="mt-10 flex flex-col items-center gap-3">
<Link
href="/tos"
className="text-amber-300 underline hover:text-amber-200"
href="/builder"
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"
>
Terms of Service
</Link>{" "}
and{" "}
<Link
href="/privacy"
className="text-amber-300 underline hover:text-amber-200"
>
Privacy Policy
Start a Build
</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 className="text-[11px] tracking-[0.05em] text-zinc-600">
100% Free · No account required
</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>
</section>
{/* Screen Recording Section */}
<section className="relative mt-24">
<div className="mx-auto max-w-6xl px-4 sm:px-6 lg:px-8">
<div className="relative overflow-hidden rounded-2xl border border-white/10 bg-black shadow-2xl">
{/* Screen Recording */}
{/* ── 3. How It Works ── */}
<section className="mx-auto max-w-5xl px-4 py-[72px]">
<p className="mb-3 text-center text-[10px] font-bold uppercase tracking-[0.25em] text-amber-400 opacity-85">
THE PROCESS
</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
autoPlay
loop
muted
playsInline
poster="/builder-preview.png"
className="w-full object-cover"
>
<source src="/builder-preview.mp4" type="video/mp4" />
Your browser does not support the video tag.
</video>
{/* Bottom fade */}
<div
aria-hidden="true"
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>
</section>
{/* Info Section */}
<section className="mt-14 rounded-2xl border border-white/10 bg-white/5 px-6 py-14 sm:px-8">
<div className="mx-auto max-w-2xl text-left sm:text-center">
<p className="text-xs font-semibold uppercase tracking-[0.25em] text-amber-300/90">
Everything you need to test the core
{/* ── 5. Feature Cards ── */}
<section className="mx-auto max-w-5xl px-4 py-[72px]">
<p className="mb-3 text-center text-[10px] font-bold uppercase tracking-[0.25em] text-amber-400 opacity-85">
WHAT YOU GET
</p>
<h2 className="mt-3 text-balance text-3xl font-semibold tracking-tight sm:text-4xl">
Build smarter rifles without the spreadsheet lifestyle.
<h2 className="mb-2 text-center text-[30px] font-bold tracking-[-0.025em] text-white">
Built by gun nerds, for the everyday AR builder.
</h2>
<p className="mt-4 text-sm leading-relaxed text-white/70 sm:text-base">
This private beta is focused on one thing: helping you plan an
AR-15 build faster and with fewer mistakes. Its early, its
evolving, and your feedback will decide what we build next.
<p className="mb-11 text-center text-[13px] uppercase tracking-[0.02em] text-zinc-600">
NO SUBSCRIPTIONS · NO SPONSORED RANKINGS · NO BULLSHIT
</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">
{features.map((feature) => (
<div
key={feature.name}
className="relative rounded-xl border border-white/10 bg-black/30 p-5"
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"
style={{ gap: "1px" }}
>
<dt className="flex items-center gap-2 text-sm font-semibold text-white">
<feature.icon
{(
[
{
Icon: WrenchScrewdriverIcon,
title: "Build by role, not part number",
body: "Parts organized by function. Know what you need, not what SKU to search for.",
},
{
Icon: ShieldExclamationIcon,
title: "Conflicts caught early",
body: "Gas system, buffer weight, caliber — flagged before checkout, not after the box arrives.",
},
{
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"
className="h-5 w-5 text-amber-300"
/>
{feature.name}
</dt>
<dd className="mt-2 text-sm text-white/70">
{feature.description}
</dd>
<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>
))}
</dl>
<div className="mx-auto mt-10 max-w-2xl rounded-xl border border-white/10 bg-black/30 p-5">
<div className="text-xs font-semibold uppercase tracking-[0.25em] text-white/60">
What feedback we want
</div>
<ul className="mt-3 space-y-2 text-sm text-white/80">
<li> Where you got confused or stuck</li>
<li> Warnings that felt wrong (or missing)</li>
<li> Parts you expected to find but couldnt</li>
<li> If this beats your current build-planning process</li>
</ul>
<p className="mt-3 text-xs text-white/50">
By joining the private beta, you agree features, data, and pricing
may change.
</section>
{/* ── 6. SEO Copy Block ── */}
<section className="border-t border-zinc-900 px-4 py-[72px]">
<div className="mx-auto max-w-[680px]">
<h2 className="mb-4 text-[22px] font-bold tracking-[-0.02em] text-zinc-300">
What is Battl Builders?
</h2>
<p className="mb-3.5 text-sm leading-[1.75] text-zinc-500">
Battl Builders is a free{" "}
<strong className="font-semibold text-zinc-300">
AR-15 build planner
</strong>{" "}
the tool you&apos;ve been doing in spreadsheets, rebuilt for how
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>
</div>
</section>
{/* Footer strip */}
<footer className="mt-12 border-t border-zinc-900 pt-4 text-xs text-zinc-500">
<div className="flex flex-wrap items-center justify-between gap-3">
<span>
© {new Date().getFullYear()} BATTL BUILDERS
<span className="align-super text-[0.9em] ml-0.5"></span>
</span>
<span className="text-zinc-600">
v0.1 Import engine online Builder UI in progress
</span>
</div>
{/* ── 7. Final CTA ── */}
<section className="border-t border-zinc-900 px-4 py-24 text-center">
<h2 className="mb-3 text-[36px] font-bold tracking-[-0.03em] text-white">
Your build starts here.
</h2>
<p className="mb-8 text-sm text-zinc-600">
Free. No account required. Takes five minutes to get rolling.
</p>
<Link
href="/builder"
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>
</div>
</main>