441 lines
16 KiB
TypeScript
441 lines
16 KiB
TypeScript
"use client";
|
||
|
||
import { FormEvent, useState } from "react";
|
||
import Image from "next/image";
|
||
import Link from "next/link";
|
||
import {
|
||
WrenchScrewdriverIcon,
|
||
ShieldExclamationIcon,
|
||
CurrencyDollarIcon,
|
||
BookmarkSquareIcon,
|
||
BuildingStorefrontIcon,
|
||
ChatBubbleLeftRightIcon,
|
||
} from "@heroicons/react/20/solid";
|
||
|
||
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 you’re 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(
|
||
"✅ You’re on the list. Invites drop soon — we’ll email your access link when it’s 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 you’re 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:
|
||
"We’re starting small with a couple merchants and expanding fast.",
|
||
icon: BuildingStorefrontIcon,
|
||
},
|
||
{
|
||
name: "Feedback-driven roadmap",
|
||
description:
|
||
"Tell us what’s confusing, wrong, or missing — we’ll 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]">
|
||
<svg
|
||
width="100%"
|
||
height="100%"
|
||
preserveAspectRatio="xMidYMid slice"
|
||
xmlns="http://www.w3.org/2000/svg"
|
||
>
|
||
<defs>
|
||
<pattern
|
||
id="battlGrid"
|
||
x="0"
|
||
y="0"
|
||
width="80"
|
||
height="80"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<path
|
||
d="M 80 0 L 0 0 0 80"
|
||
fill="none"
|
||
stroke="#fbbf24"
|
||
strokeWidth="0.5"
|
||
strokeOpacity="0.35"
|
||
/>
|
||
</pattern>
|
||
|
||
<pattern
|
||
id="battlHex"
|
||
width="140"
|
||
height="140"
|
||
patternUnits="userSpaceOnUse"
|
||
>
|
||
<path
|
||
d="M70 4 L136 40 L136 110 L70 146 L4 110 L4 40 Z"
|
||
fill="none"
|
||
stroke="#fbbf24"
|
||
strokeWidth="0.4"
|
||
strokeOpacity="0.25"
|
||
/>
|
||
</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">
|
||
<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>
|
||
|
||
<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"
|
||
>
|
||
Sign In
|
||
</Link>
|
||
</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>
|
||
</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
|
||
listings—without 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>
|
||
|
||
{/* 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'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{" "}
|
||
<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 I’m
|
||
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>
|
||
</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 */}
|
||
<video
|
||
autoPlay
|
||
loop
|
||
muted
|
||
playsInline
|
||
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"
|
||
/>
|
||
</div>
|
||
</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
|
||
</p>
|
||
<h2 className="mt-3 text-balance text-3xl font-semibold tracking-tight sm:text-4xl">
|
||
Build smarter rifles — without the spreadsheet lifestyle.
|
||
</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. It’s early, it’s
|
||
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">
|
||
{features.map((feature) => (
|
||
<div
|
||
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
|
||
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>
|
||
</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 couldn’t</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.
|
||
</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>
|
||
</footer>
|
||
</div>
|
||
</main>
|
||
);
|
||
}
|