Remove auth/db stack, migrate contact email flow, and update for Next 16

This commit is contained in:
2026-07-10 20:35:24 -04:00
parent ed948f5264
commit bf10f26d26
31 changed files with 1005 additions and 832 deletions
+388 -94
View File
@@ -1,103 +1,397 @@
import { headers } from "next/headers";
import Image from "next/image";
import Link from "next/link";
import { redirect } from "next/navigation";
import { LatestPost } from "~/app/_components/post";
import { auth } from "~/server/better-auth";
import { getSession } from "~/server/better-auth/server";
import { api, HydrateClient } from "~/trpc/server";
import { ContactForm } from "~/app/_components/contact-form";
export default async function Home() {
const hello = await api.post.hello({ text: "from tRPC" });
const session = await getSession();
const services = [
{
title: "Networks & Infrastructure",
body: "UniFi networks designed, deployed, and managed end to end — wired, wireless, VLANs, and the hardware that has to stay up.",
tag: "UniFi",
},
{
title: "Servers & Cloud",
body: "Provisioning and running local and cloud servers — web servers, object storage (S3), and automated backups that actually restore.",
tag: "Cloud",
},
{
title: "Databases",
body: "Design, deployment, and day-to-day operation. Reliable data, backed up, monitored, and recoverable when it counts.",
tag: "Data",
},
{
title: "Web & Applications",
body: "Marketing sites to production web apps — built, hosted, and maintained by the same person who runs the servers under them.",
tag: "Web",
},
{
title: "Commerce & Ticketing",
body: "Shopify storefronts plus a custom ticket-sales platform built from scratch to handle real event-day volume.",
tag: "Commerce",
},
{
title: "On-Site Operations",
body: "When it's race day, whatever it takes to keep things moving — up to and including working the counter and selling tires.",
tag: "On-site",
},
];
if (session) {
void api.post.getLatest.prefetch();
}
const stats = [
{ value: "5+ yrs", label: "Running the stack at Riverhead Raceway" },
{ value: "1 partner", label: "From the wall jack to the checkout button" },
{ value: "24/7", label: "Systems built to stay online" },
];
export default function Home() {
return (
<HydrateClient>
<main className="flex min-h-screen flex-col items-center justify-center bg-gradient-to-b from-[#2e026d] to-[#15162c] text-white">
<div className="container flex flex-col items-center justify-center gap-12 px-4 py-16">
<h1 className="text-5xl font-extrabold tracking-tight sm:text-[5rem]">
Create <span className="text-[hsl(280,100%,70%)]">T3</span> App
</h1>
<div className="grid grid-cols-1 gap-4 sm:grid-cols-2 md:gap-8">
<Link
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
href="https://create.t3.gg/en/usage/first-steps"
target="_blank"
>
<h3 className="text-2xl font-bold">First Steps </h3>
<div className="text-lg">
Just the basics - Everything you need to know to set up your
database and authentication.
</div>
</Link>
<Link
className="flex max-w-xs flex-col gap-4 rounded-xl bg-white/10 p-4 hover:bg-white/20"
href="https://create.t3.gg/en/introduction"
target="_blank"
>
<h3 className="text-2xl font-bold">Documentation </h3>
<div className="text-lg">
Learn more about Create T3 App, the libraries it uses, and how
to deploy it.
</div>
</Link>
</div>
<div className="flex flex-col items-center gap-2">
<p className="text-2xl text-white">
{hello ? hello.greeting : "Loading tRPC query..."}
</p>
<div className="flex flex-col items-center justify-center gap-4">
<p className="text-center text-2xl text-white">
{session && <span>Logged in as {session.user?.name}</span>}
</p>
{!session ? (
<form>
<button
className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20"
formAction={async () => {
"use server";
const res = await auth.api.signInSocial({
body: {
provider: "github",
callbackURL: "/",
},
});
if (!res.url) {
throw new Error("No URL returned from signInSocial");
}
redirect(res.url);
}}
>
Sign in with Github
</button>
</form>
) : (
<form>
<button
className="rounded-full bg-white/10 px-10 py-3 font-semibold no-underline transition hover:bg-white/20"
formAction={async () => {
"use server";
await auth.api.signOut({
headers: await headers(),
});
redirect("/");
}}
>
Sign out
</button>
</form>
)}
</div>
</div>
{session?.user && <LatestPost />}
</div>
<div className="relative min-h-screen overflow-x-hidden">
<Header />
<main>
<Hero />
<Stats />
<Services />
<Approach />
<NameStory />
<Contact />
</main>
</HydrateClient>
<Footer />
</div>
);
}
// Reflected-"H" monogram: an H standing on a waterline, mirrored below like a
// reflection on still water — a nod to Hadlock Pond.
function Mark({ className }: { className?: string }) {
return (
<svg viewBox="0 0 32 32" fill="none" className={className} aria-hidden="true">
<g
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
fill="none"
>
<path d="M10 7v8M22 7v8M10 11h12" />
</g>
<line
x1="6"
y1="16.5"
x2="26"
y2="16.5"
stroke="currentColor"
strokeWidth="1.5"
strokeLinecap="round"
opacity="0.5"
/>
<g
stroke="currentColor"
strokeWidth="2.5"
strokeLinecap="round"
fill="none"
opacity="0.3"
>
<path d="M10 18v6M22 18v6" />
</g>
</svg>
);
}
function Wordmark() {
return (
<Link href="/" className="group flex items-center gap-2.5">
<span className="flex h-8 w-8 items-center justify-center rounded-md border border-brand-400/40 bg-brand-400/10 text-brand-300 transition group-hover:border-brand-400/70">
<Mark className="h-5 w-5" />
</span>
<span className="text-[15px] font-semibold tracking-tight text-white">
Hadlock<span className="text-brand-400"> Technologies</span>
</span>
</Link>
);
}
function Header() {
return (
<header className="absolute inset-x-0 top-0 z-50">
<div className="mx-auto flex h-20 max-w-6xl items-center justify-between px-6">
<Wordmark />
<nav className="hidden items-center gap-1 rounded-full border border-white/10 bg-white/5 p-1 text-sm text-slate-200 backdrop-blur-md md:flex">
<a
href="#services"
className="rounded-full px-4 py-1.5 transition hover:bg-white/10 hover:text-white"
>
Services
</a>
<a
href="#approach"
className="rounded-full px-4 py-1.5 transition hover:bg-white/10 hover:text-white"
>
Approach
</a>
<a
href="#name"
className="rounded-full px-4 py-1.5 transition hover:bg-white/10 hover:text-white"
>
The name
</a>
</nav>
<a
href="#contact"
className="rounded-full bg-white px-4 py-2 text-sm font-semibold text-ink-950 transition hover:bg-slate-200"
>
Get in touch
</a>
</div>
</header>
);
}
const capabilities = [
"Networks",
"Servers",
"Cloud",
"Storage",
"Databases",
"Web",
"Commerce",
];
function Hero() {
return (
<section className="relative isolate flex min-h-screen flex-col overflow-hidden">
{/* Hadlock Pond, full-bleed with a slight darkening filter */}
<div className="absolute inset-0 -z-20">
<Image
src="/hadlock-pond.jpg"
alt="Hadlock Pond at dawn in Fort Ann, New York"
fill
priority
sizes="100vw"
className="object-cover brightness-[0.68] saturate-[0.95]"
/>
</div>
{/* Scrims: darken top for the nav, left for the text, and fade the base into the page */}
<div className="absolute inset-0 -z-10 bg-gradient-to-b from-ink-950/70 via-transparent to-ink-950" />
<div className="absolute inset-0 -z-10 bg-gradient-to-r from-ink-950/85 via-ink-950/30 to-transparent" />
<div className="mx-auto flex w-full max-w-6xl flex-1 flex-col justify-center px-6 pt-32 pb-16">
<div className="inline-flex w-fit items-center gap-2 rounded-full border border-white/15 bg-white/5 px-3.5 py-1.5 text-xs font-medium text-slate-200 backdrop-blur-sm">
<span className="h-1.5 w-1.5 rounded-full bg-brand-400" />
Full-stack technical partner · Long Island, NY
</div>
<h1 className="mt-7 max-w-4xl font-mono text-4xl font-semibold tracking-tight text-white uppercase sm:text-6xl sm:leading-[1.05]">
Everything technical,{" "}
<span className="text-brand-400">handled.</span>
</h1>
<p className="mt-6 max-w-xl text-lg leading-relaxed text-slate-300">
The networks, servers, and software that keep a business running
designed, built, and maintained under one accountable name, instead of
five vendors pointing at each other.
</p>
<div className="mt-9 flex flex-wrap items-center gap-4">
<a
href="#contact"
className="rounded-full bg-brand-400 px-6 py-3 text-sm font-semibold text-ink-950 transition hover:bg-brand-300"
>
Start a conversation
</a>
<a
href="#services"
className="rounded-full border border-white/20 bg-white/5 px-6 py-3 text-sm font-semibold text-white backdrop-blur-sm transition hover:border-white/40 hover:bg-white/10"
>
See what we do
</a>
</div>
</div>
{/* Capability strip — where DUNE puts its client logos */}
<div className="relative border-t border-white/10 bg-ink-950/40 backdrop-blur-sm">
<div className="mx-auto flex max-w-6xl flex-wrap items-center gap-x-6 gap-y-2 px-6 py-5 font-mono text-xs tracking-widest text-slate-400 uppercase">
<span className="text-slate-500">Working across</span>
{capabilities.map((c) => (
<span key={c} className="text-slate-300">
{c}
</span>
))}
</div>
</div>
</section>
);
}
function Stats() {
return (
<section className="border-y border-white/5 bg-ink-900/50">
<div className="mx-auto grid max-w-6xl grid-cols-1 divide-y divide-white/5 px-6 sm:grid-cols-3 sm:divide-x sm:divide-y-0">
{stats.map((s) => (
<div key={s.label} className="px-2 py-8 sm:px-6">
<div className="text-2xl font-bold text-white">{s.value}</div>
<div className="mt-1 text-sm text-slate-400">{s.label}</div>
</div>
))}
</div>
</section>
);
}
function Services() {
return (
<section id="services" className="scroll-mt-20">
<div className="mx-auto max-w-6xl px-6 py-24">
<SectionHeading
eyebrow="What we do"
title="One partner. The whole stack."
description="Most businesses stitch together a network vendor, a hosting company, a web agency, and a point-of-sale provider. Hadlock is all of it — and the same hands that wire the network ship the app on top of it."
/>
<div className="mt-14 grid grid-cols-1 gap-px overflow-hidden rounded-2xl border border-white/10 bg-white/10 sm:grid-cols-2 lg:grid-cols-3">
{services.map((s) => (
<div
key={s.title}
className="group relative bg-ink-900 p-7 transition hover:bg-ink-850"
>
<div className="mb-4 inline-flex items-center rounded-md border border-brand-400/30 bg-brand-400/10 px-2.5 py-1 text-xs font-medium text-brand-300">
{s.tag}
</div>
<h3 className="text-lg font-semibold text-white">{s.title}</h3>
<p className="mt-2.5 text-sm leading-relaxed text-slate-400">
{s.body}
</p>
</div>
))}
</div>
</div>
</section>
);
}
function Approach() {
const points = [
{
title: "Full ownership",
body: "From the physical layer up to the interface. Nothing gets lost in the handoff between vendors, because there is no handoff.",
},
{
title: "Hands-on, not hands-off",
body: "The person who designed it is the person who fixes it — on-site when it matters, no ticket queue between you and a resolution.",
},
{
title: "Built to stay up",
body: "Backups that restore, monitoring that warns you first, and systems sized for the busy day, not the average one.",
},
];
return (
<section
id="approach"
className="scroll-mt-20 border-y border-white/5 bg-ink-900/40"
>
<div className="mx-auto max-w-6xl px-6 py-24">
<div className="grid grid-cols-1 gap-14 lg:grid-cols-[1fr_1.2fr]">
<div>
<SectionHeading
eyebrow="The approach"
title="A titleless everything-engineer, on your side."
description="Software engineer, network admin, systems operator, and — on the right day — the person selling tires at the counter. The job is whatever keeps the operation running."
/>
</div>
<div className="grid gap-px overflow-hidden rounded-2xl border border-white/10 bg-white/10">
{points.map((p) => (
<div key={p.title} className="bg-ink-900 p-7">
<h3 className="text-base font-semibold text-white">
{p.title}
</h3>
<p className="mt-2 text-sm leading-relaxed text-slate-400">
{p.body}
</p>
</div>
))}
</div>
</div>
</div>
</section>
);
}
function NameStory() {
return (
<section id="name" className="scroll-mt-20">
<div className="mx-auto max-w-3xl px-6 py-24 text-center">
<div className="font-mono text-sm font-semibold tracking-widest text-brand-400 uppercase">
The name
</div>
<h2 className="mt-3 text-3xl font-bold tracking-tight text-white sm:text-4xl">
Named for still water in the Adirondacks.
</h2>
<p className="mx-auto mt-4 max-w-xl text-base leading-relaxed text-slate-300">
The water at the top of this page is Hadlock Pond in Fort Ann, New
York quiet, dependable, and built to last. It&apos;s a good standard
for infrastructure: you shouldn&apos;t have to think about it for it to
be working.
</p>
</div>
</section>
);
}
function Contact() {
return (
<section
id="contact"
className="scroll-mt-20 border-t border-white/5 bg-ink-900/50"
>
<div className="mx-auto max-w-3xl px-6 py-24">
<div className="relative overflow-hidden rounded-3xl border border-white/10 bg-gradient-to-br from-ink-850 to-ink-900 p-8 md:p-12">
<div className="absolute inset-0 -z-10 bg-[radial-gradient(50%_60%_at_50%_0%,rgba(45,212,191,0.12),transparent_70%)]" />
<div className="mb-8 text-center">
<div className="font-mono text-sm font-semibold tracking-widest text-brand-400 uppercase">
Get in touch
</div>
<h2 className="mx-auto mt-3 max-w-2xl text-3xl font-bold tracking-tight text-white sm:text-4xl">
Have something technical that needs an owner?
</h2>
</div>
<ContactForm />
</div>
</div>
</section>
);
}
function Footer() {
return (
<footer className="border-t border-white/5">
<div className="mx-auto flex max-w-6xl flex-col items-center justify-between gap-4 px-6 py-10 sm:flex-row">
<Wordmark />
<p className="text-sm text-slate-500">
Hadlock Technologies LLC · New York · © {new Date().getFullYear()}
</p>
</div>
</footer>
);
}
function SectionHeading({
eyebrow,
title,
description,
}: {
eyebrow: string;
title: string;
description: string;
}) {
return (
<div className="max-w-2xl">
<div className="font-mono text-sm font-semibold tracking-widest text-brand-400 uppercase">
{eyebrow}
</div>
<h2 className="mt-3 text-3xl font-bold tracking-tight text-white sm:text-4xl">
{title}
</h2>
<p className="mt-4 text-base leading-relaxed text-slate-300">
{description}
</p>
</div>
);
}