761 lines
27 KiB
TypeScript
761 lines
27 KiB
TypeScript
import Image from "next/image";
|
|
import Link from "next/link";
|
|
|
|
import { ContactForm } from "~/app/_components/contact-form";
|
|
import { MobileNav } from "~/app/_components/mobile-nav";
|
|
import { ThemeToggle } from "~/app/_components/theme-toggle";
|
|
|
|
const services = [
|
|
{
|
|
title: "Networks & Infrastructure",
|
|
body: "UniFi networking with wired links, wireless access, VLANs, and hardware that stays up when it counts.",
|
|
tag: "UniFi",
|
|
},
|
|
{
|
|
title: "Servers & Cloud",
|
|
body: "Local and cloud servers with web hosting, S3 storage, and backups tested by restore.",
|
|
tag: "Cloud",
|
|
},
|
|
{
|
|
title: "Databases",
|
|
body: "Designed, deployed, and operated under the stack, with backups, monitoring, and tested restores.",
|
|
tag: "Data",
|
|
},
|
|
{
|
|
title: "Web & Applications",
|
|
body: "Sites built and hosted from the marketing page to the ticket platform. When a site fails, one number gets the call.",
|
|
tag: "Web",
|
|
},
|
|
{
|
|
title: "Commerce & Ticketing",
|
|
body: "A ticket system built from scratch, plus a Shopify store running real event-day traffic, not a demo.",
|
|
tag: "Commerce",
|
|
},
|
|
{
|
|
title: "On-Site Operations",
|
|
body: "Event-day coverage at the counter, the gate, and wherever else the day requires. Most clients get this as part of the job.",
|
|
tag: "On-site",
|
|
},
|
|
];
|
|
|
|
export default function Home() {
|
|
return (
|
|
<div className="relative min-h-screen overflow-x-clip">
|
|
<Header />
|
|
<main>
|
|
<Hero />
|
|
<Services />
|
|
<Approach />
|
|
<Projects />
|
|
<NameStory />
|
|
<Contact />
|
|
</main>
|
|
<Footer />
|
|
</div>
|
|
);
|
|
}
|
|
|
|
function Wordmark() {
|
|
return (
|
|
<Link href="/" className="group flex shrink-0 items-center">
|
|
<img
|
|
src="/branding/logo_white.svg"
|
|
alt="Hadlock Technologies"
|
|
className="theme-logo-dark h-7 w-auto transition-opacity group-hover:opacity-75"
|
|
/>
|
|
<img
|
|
src="/branding/logo_blue.svg"
|
|
alt="Hadlock Technologies"
|
|
className="theme-logo-light h-7 w-auto transition-opacity group-hover:opacity-75"
|
|
/>
|
|
</Link>
|
|
);
|
|
}
|
|
|
|
function Header() {
|
|
return (
|
|
<header className="sticky top-0 z-50">
|
|
<div className="mx-auto max-w-6xl px-4 pt-2">
|
|
<div className="border-border bg-bg relative flex h-14 items-center justify-between border px-4 shadow-lg">
|
|
<Wordmark />
|
|
<nav className="text-text-secondary hidden items-center gap-6 text-sm font-medium md:flex">
|
|
<a href="#services" className="hover:text-text transition">
|
|
Services
|
|
</a>
|
|
<a href="#approach" className="hover:text-text transition">
|
|
Approach
|
|
</a>
|
|
<a href="#projects" className="hover:text-text transition">
|
|
Projects
|
|
</a>
|
|
<a href="#name" className="hover:text-text transition">
|
|
The name
|
|
</a>
|
|
</nav>
|
|
<div className="flex items-center gap-2">
|
|
<ThemeToggle />
|
|
<a
|
|
href="#contact"
|
|
className="bg-text text-bg px-4 py-2 text-sm font-semibold transition hover:opacity-80"
|
|
>
|
|
Get in touch
|
|
</a>
|
|
<MobileNav />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
);
|
|
}
|
|
|
|
const capabilities = [
|
|
"Networks",
|
|
"Servers",
|
|
"Cloud",
|
|
"Storage",
|
|
"Databases",
|
|
"Web",
|
|
"Commerce",
|
|
];
|
|
|
|
function Hero() {
|
|
return (
|
|
<section className="relative isolate -mt-16 flex min-h-screen flex-col overflow-hidden">
|
|
{/* The Hadlock Pond image fills the section 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="hero-photo object-cover"
|
|
/>
|
|
</div>
|
|
{/* Scrims darken the top for the nav and the left for the text. They fade the base into the page */}
|
|
<div className="from-scrim-b-from to-scrim-b-to absolute inset-0 -z-10 bg-gradient-to-b via-transparent" />
|
|
<div className="from-scrim-l-from via-scrim-l-mid to-scrim-l-end absolute inset-0 -z-10 bg-gradient-to-r" />
|
|
{/* Dark overlay, same treatment as the Racetix card, in dark mode */}
|
|
<div className="from-hero-overlay-start via-hero-overlay-mid to-hero-overlay-end absolute inset-0 -z-10 bg-gradient-to-r" />
|
|
|
|
<div className="mx-auto flex w-full max-w-6xl flex-1 flex-col items-start justify-center px-6 pt-32 pb-16">
|
|
<img
|
|
src="/branding/logo_white.svg"
|
|
alt="Hadlock Technologies"
|
|
className="theme-logo-dark h-24 w-auto"
|
|
/>
|
|
<img
|
|
src="/branding/logo_blue.svg"
|
|
alt="Hadlock Technologies"
|
|
className="theme-logo-light h-24 w-auto"
|
|
/>
|
|
|
|
<h1 className="text-text mt-7 max-w-4xl text-4xl font-semibold tracking-tight lowercase sm:text-6xl sm:leading-[1.05]">
|
|
Everything technical, <span className="text-accent">handled.</span>
|
|
</h1>
|
|
|
|
<p className="text-text-secondary mt-6 max-w-xl text-lg leading-relaxed">
|
|
The same stack powers clients across Long Island — race tracks,
|
|
retailers, and web platforms. UniFi, cloud infrastructure, databases,
|
|
websites, ticketing, and commerce, built and operated by one person.
|
|
Hadlock Technologies is that work under one name.
|
|
</p>
|
|
|
|
<div className="mt-9 flex flex-wrap items-center gap-4">
|
|
<a
|
|
href="#contact"
|
|
className="bg-accent text-on-accent hover:bg-accent-hover px-6 py-3 text-sm font-semibold transition"
|
|
>
|
|
Start a conversation
|
|
</a>
|
|
<a
|
|
href="#services"
|
|
className="border-border-strong bg-surface/50 text-text hover:border-border-strong hover:bg-surface border px-6 py-3 text-sm font-semibold backdrop-blur-sm transition"
|
|
>
|
|
See what we do
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
{/* A capability strip, where DUNE puts its client logos */}
|
|
<div className="bg-bg/40 border-border relative border-t backdrop-blur-sm">
|
|
<div className="text-muted mx-auto flex max-w-6xl flex-wrap items-center gap-x-6 gap-y-2 px-6 py-5 text-xs tracking-widest uppercase">
|
|
<span className="text-faint">Working across</span>
|
|
{capabilities.map((c) => (
|
|
<span key={c} className="text-text-secondary">
|
|
{c}
|
|
</span>
|
|
))}
|
|
</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 person. The whole stack."
|
|
description=" Most places use a network contractor, a hosting company, a web
|
|
agency, and a POS vendor. That is four invoices and four different
|
|
people to call when something breaks. Hadlock is all four."
|
|
/>
|
|
|
|
<div className="border-border bg-border mt-14 grid grid-cols-1 gap-px overflow-hidden border sm:grid-cols-2 lg:grid-cols-3">
|
|
{services.map((s) => (
|
|
<div
|
|
key={s.title}
|
|
className="group bg-surface hover:bg-surface-hover relative p-7 transition"
|
|
>
|
|
<div className="border-accent-border bg-accent-bg text-accent mb-4 inline-flex items-center border px-2.5 py-1 text-xs font-medium">
|
|
{s.tag}
|
|
</div>
|
|
<h3 className="text-text text-lg font-semibold">{s.title}</h3>
|
|
<p className="text-muted mt-2.5 text-sm leading-relaxed">
|
|
{s.body}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function Approach() {
|
|
const points = [
|
|
{
|
|
title: "Full ownership",
|
|
body: "Everything from the network cable to the interface is built and owned in one place. Nothing gets lost in a handoff, because there is no handoff.",
|
|
},
|
|
{
|
|
title: "Hands-on, not hands-off",
|
|
body: "The person who designed the system is the person who fixes it, on-site when it matters. No ticket queue for an answer.",
|
|
},
|
|
{
|
|
title: "Built to stay up",
|
|
body: "Backups are tested. Monitoring alerts before a problem surfaces. The systems handle the busy night, not just an average Tuesday.",
|
|
},
|
|
];
|
|
|
|
return (
|
|
<section
|
|
id="approach"
|
|
className="bg-surface/40 border-border-faint scroll-mt-20 border-y"
|
|
>
|
|
<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="No title covers the job. There is not one."
|
|
description="The same person is the software engineer, the network admin, and the systems operator for every client. On a busy night, that person might work the counter too. The job is whatever keeps the place running."
|
|
/>
|
|
</div>
|
|
<div className="border-border bg-border grid gap-px overflow-hidden border">
|
|
{points.map((p) => (
|
|
<div key={p.title} className="bg-surface p-7">
|
|
<h3 className="text-text text-base font-semibold">{p.title}</h3>
|
|
<p className="text-muted mt-2 text-sm leading-relaxed">
|
|
{p.body}
|
|
</p>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
const racetixFeatures = [
|
|
{
|
|
title: "Multi-tenant by track",
|
|
body: "Each track runs its own catalog, prices, and gate.",
|
|
},
|
|
{
|
|
title: "QR gate scanning",
|
|
body: "The scanner app verifies tickets offline at the gate.",
|
|
},
|
|
{
|
|
title: "Apple & Google Wallet",
|
|
body: "Paid tickets go straight into the phone wallet.",
|
|
},
|
|
{
|
|
title: "Stripe payments",
|
|
body: "Stripe Connect runs checkout, refunds, and tax.",
|
|
},
|
|
];
|
|
|
|
const beenvoiceFeatures = [
|
|
{
|
|
title: "Invoices that stay organized",
|
|
body: "Draft line items, apply taxes, send a link, and export a polished PDF.",
|
|
},
|
|
{
|
|
title: "Built-in time clock",
|
|
body: "Track billable hours as you work, then pull them into an invoice without retyping.",
|
|
},
|
|
{
|
|
title: "Recurring invoices",
|
|
body: "Set up retainers once, then let beenvoice generate the next invoice on schedule.",
|
|
},
|
|
{
|
|
title: "Send and follow up",
|
|
body: "Email invoices from the app and keep status visible from draft through paid.",
|
|
},
|
|
];
|
|
|
|
const racehubFeatures = [
|
|
{
|
|
title: "Events & schedule",
|
|
body: "Race events, special events, and iCal feeds for every fan's calendar.",
|
|
},
|
|
{
|
|
title: "Standings & results",
|
|
body: "Points, lineups, and results served live from the Hotlap API.",
|
|
},
|
|
{
|
|
title: "News & media",
|
|
body: "Articles, announcements, and promotions published from the CMS.",
|
|
},
|
|
{
|
|
title: "Granular permissions",
|
|
body: "97+ permissions, role-based access, and a full audit trail.",
|
|
},
|
|
];
|
|
|
|
const hristudioFeatures = [
|
|
{
|
|
title: "Visual experiment designer",
|
|
body: "Drag-and-drop blocks build branching protocols. No code required.",
|
|
},
|
|
{
|
|
title: "Guided wizard interface",
|
|
body: "Step-by-step trial execution with every action logged in time.",
|
|
},
|
|
{
|
|
title: "Robot-agnostic",
|
|
body: "Design once, run on any robot — NAO, Pepper, or TurtleBot.",
|
|
},
|
|
{
|
|
title: "Automatic data capture",
|
|
body: "Synchronized multi-modal streams, timestamped and exportable.",
|
|
},
|
|
];
|
|
|
|
function Projects() {
|
|
return (
|
|
<section id="projects" className="scroll-mt-20">
|
|
<div className="mx-auto max-w-6xl px-6 py-24">
|
|
<SectionHeading
|
|
eyebrow="Projects"
|
|
title="Four products, built end to end."
|
|
description="Racetix sells tickets for short tracks. beenvoice invoices for freelancers and small businesses. RaceHub runs the official Riverhead Raceway site. HRIStudio makes human-robot interaction research reproducible. All four run in production, operated end to end."
|
|
/>
|
|
</div>
|
|
|
|
<a
|
|
href="https://racetix.us"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group border-border relative isolate block overflow-hidden border-y"
|
|
>
|
|
<div className="absolute inset-0 -z-20">
|
|
<Image
|
|
src="/racetix-hero.webp"
|
|
alt="Race cars on a short track under the lights"
|
|
fill
|
|
sizes="100vw"
|
|
className="racetix-photo object-cover"
|
|
/>
|
|
</div>
|
|
<div className="from-racetix-overlay-start via-racetix-overlay-mid to-racetix-overlay-end absolute inset-0 -z-10 bg-gradient-to-r" />
|
|
<div className="bg-racetix-red/20 absolute -bottom-16 -left-16 h-64 w-64 blur-[100px]" />
|
|
|
|
<div className="relative mx-auto grid max-w-6xl gap-10 px-6 py-16 md:grid-cols-[1.1fr_0.9fr] md:py-24">
|
|
<div>
|
|
<img
|
|
src="/logo_white.svg"
|
|
alt="Racetix"
|
|
className="theme-logo-dark h-8 w-auto"
|
|
/>
|
|
<img
|
|
src="/logo_red.svg"
|
|
alt="Racetix"
|
|
className="theme-logo-light h-8 w-auto"
|
|
/>
|
|
<p className="text-text-secondary font-display mt-6 max-w-xl text-base leading-relaxed">
|
|
Multi-tenant ticketing for short tracks. It owns ticket products,
|
|
orders, payments, admission redemptions, and ticket email. Tracks
|
|
embed checkout in their own site through the versioned API.
|
|
</p>
|
|
<span className="text-racetix-red font-display mt-8 inline-flex items-center gap-2 text-sm font-bold tracking-widest uppercase transition group-hover:opacity-70">
|
|
Explore Racetix
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4 transition-transform group-hover:translate-x-1"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M5 12h14M12 5l7 7-7 7" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid content-center gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
|
|
{racetixFeatures.map((f) => (
|
|
<div key={f.title} className="flex items-start gap-3">
|
|
<span className="text-racetix-red mt-0.5 shrink-0">
|
|
<CheckIcon className="h-4 w-4" />
|
|
</span>
|
|
<div>
|
|
<h3 className="text-text font-display text-sm font-bold tracking-wide uppercase">
|
|
{f.title}
|
|
</h3>
|
|
<p className="text-text-secondary/80 font-display mt-1 text-xs leading-relaxed">
|
|
{f.body}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
<a
|
|
href="https://beenvoice.app"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group border-border relative isolate block overflow-hidden border-y"
|
|
>
|
|
{/* beenvoice backdrop: hairline grid + soft animated blob */}
|
|
<div className="beenvoice-grid absolute inset-0 -z-20" />
|
|
<div className="beenvoice-blob absolute top-1/2 left-1/2 -z-20 h-[800px] w-[800px] -translate-x-1/2 -translate-y-1/2 rounded-full bg-[var(--beenvoice-blob)] blur-3xl" />
|
|
{/* Scrim keeps the text readable in both themes */}
|
|
<div className="from-bg/80 via-bg/35 absolute inset-0 -z-10 bg-gradient-to-r to-transparent" />
|
|
|
|
<div className="relative mx-auto grid max-w-6xl gap-10 px-6 py-16 md:grid-cols-[1.1fr_0.9fr] md:py-24">
|
|
<div>
|
|
<div className="text-text flex items-center font-mono text-2xl font-bold tracking-tight">
|
|
<span>$</span>
|
|
<span className="inline-block w-1" />
|
|
<span>been</span>
|
|
<span className="text-text/70">voice</span>
|
|
</div>
|
|
<h3 className="text-text mt-6 max-w-xl font-serif text-3xl font-semibold tracking-tight sm:text-4xl">
|
|
Run your freelance admin from one place.
|
|
</h3>
|
|
<p className="text-text-secondary font-app-sans mt-4 max-w-xl text-base leading-relaxed">
|
|
beenvoice manages clients, tracks time, sends invoices, and stays
|
|
on top of getting paid, without the weight of a full accounting
|
|
suite.
|
|
</p>
|
|
<span className="text-text mt-8 inline-flex items-center gap-2 font-mono text-sm font-bold tracking-widest uppercase transition group-hover:opacity-70">
|
|
Explore beenvoice
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4 transition-transform group-hover:translate-x-1"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M5 12h14M12 5l7 7-7 7" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid content-center gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
|
|
{beenvoiceFeatures.map((f) => (
|
|
<div key={f.title} className="flex items-start gap-3">
|
|
<span className="text-text mt-0.5 shrink-0">
|
|
<CheckIcon className="h-4 w-4" />
|
|
</span>
|
|
<div>
|
|
<h4 className="text-text font-serif text-sm font-semibold">
|
|
{f.title}
|
|
</h4>
|
|
<p className="text-text-secondary/80 font-app-sans mt-1 text-xs leading-relaxed">
|
|
{f.body}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
<a
|
|
href="https://riverheadraceway.com"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group border-border relative isolate block overflow-hidden border-y"
|
|
>
|
|
{/* racehub backdrop: dark checkered band, stays dark in both themes */}
|
|
<div className="racehub-checkered absolute inset-0 -z-20" />
|
|
<div className="absolute inset-0 -z-10 bg-[radial-gradient(90%_90%_at_50%_50%,var(--racehub-red)/30,var(--racehub-red)/10_60%,transparent_85%)]" />
|
|
<div className="absolute inset-0 -z-10 bg-gradient-to-l from-black/70 to-transparent" />
|
|
|
|
<div className="relative mx-auto grid max-w-6xl gap-10 px-6 py-16 md:grid-cols-[1.1fr_0.9fr] md:py-24">
|
|
<div>
|
|
<img
|
|
src="/racehub/logo_white.svg"
|
|
alt="Riverhead Raceway"
|
|
className="h-9 w-auto"
|
|
/>
|
|
<h3 className="font-racehub mt-6 max-w-xl text-3xl font-bold tracking-tight text-white uppercase sm:text-4xl">
|
|
The official site for Riverhead Raceway.
|
|
</h3>
|
|
<p className="font-racehub mt-4 max-w-xl text-base leading-relaxed text-white/80">
|
|
RaceHub is the website and CMS for Long Island's only auto
|
|
racing venue. It publishes events, news, standings, and results,
|
|
with live 2026 race data served from the Hotlap API.
|
|
</p>
|
|
<span className="text-racehub-red font-racehub mt-8 inline-flex items-center gap-2 text-sm font-bold tracking-widest uppercase transition group-hover:opacity-70">
|
|
Visit Riverhead Raceway
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4 transition-transform group-hover:translate-x-1"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M5 12h14M12 5l7 7-7 7" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid content-center gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
|
|
{racehubFeatures.map((f) => (
|
|
<div key={f.title} className="flex items-start gap-3">
|
|
<span className="text-racehub-red mt-0.5 shrink-0">
|
|
<CheckIcon className="h-4 w-4" />
|
|
</span>
|
|
<div>
|
|
<h4 className="font-racehub text-sm font-bold tracking-wide text-white uppercase">
|
|
{f.title}
|
|
</h4>
|
|
<p className="font-racehub mt-1 text-xs leading-relaxed text-white/70">
|
|
{f.body}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
|
|
<a
|
|
href="https://github.com/soconnor0919/hristudio"
|
|
target="_blank"
|
|
rel="noreferrer"
|
|
className="group border-border relative isolate block overflow-hidden border-y"
|
|
>
|
|
{/* hristudio backdrop: teal dot grid, adapts to theme */}
|
|
<div className="hristudio-dots absolute inset-0 -z-20" />
|
|
<div className="bg-hristudio-teal/20 absolute -top-24 -left-24 h-96 w-96 blur-[120px]" />
|
|
<div className="from-hristudio-teal/10 via-hristudio-teal/5 absolute inset-0 -z-10 bg-gradient-to-r to-transparent" />
|
|
|
|
<div className="relative mx-auto grid max-w-6xl gap-10 px-6 py-16 md:grid-cols-[1.1fr_0.9fr] md:py-24">
|
|
<div>
|
|
<div className="flex items-center gap-2.5">
|
|
<span className="bg-hristudio-teal flex aspect-square h-8 items-center justify-center rounded-lg p-1.5">
|
|
<BotIcon className="h-5 w-5 text-white" />
|
|
</span>
|
|
<span className="text-text font-hristudio text-2xl font-bold tracking-tight">
|
|
HRI<span className="font-normal">Studio</span>
|
|
</span>
|
|
</div>
|
|
<h3 className="text-text font-hristudio mt-6 max-w-xl text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Wizard-of-Oz studies, made scientific.
|
|
</h3>
|
|
<p className="text-text-secondary font-hristudio mt-4 max-w-xl text-base leading-relaxed">
|
|
HRIStudio is an open-source platform for human-robot interaction
|
|
research. Design experiments visually, run live trials with a
|
|
wizard, and capture synchronized data — reproducible by default.
|
|
</p>
|
|
<span className="text-hristudio-teal font-hristudio mt-8 inline-flex items-center gap-2 text-sm font-bold tracking-widest uppercase transition group-hover:opacity-70">
|
|
Explore HRIStudio
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className="h-4 w-4 transition-transform group-hover:translate-x-1"
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M5 12h14M12 5l7 7-7 7" />
|
|
</svg>
|
|
</span>
|
|
</div>
|
|
|
|
<div className="grid content-center gap-4 sm:grid-cols-2 md:grid-cols-1 lg:grid-cols-2">
|
|
{hristudioFeatures.map((f) => (
|
|
<div key={f.title} className="flex items-start gap-3">
|
|
<span className="text-hristudio-teal mt-0.5 shrink-0">
|
|
<CheckIcon className="h-4 w-4" />
|
|
</span>
|
|
<div>
|
|
<h4 className="text-text font-hristudio text-sm font-semibold tracking-wide">
|
|
{f.title}
|
|
</h4>
|
|
<p className="text-text-secondary/80 font-hristudio mt-1 text-xs leading-relaxed">
|
|
{f.body}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
</div>
|
|
</a>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function CheckIcon({ className }: { className?: string }) {
|
|
return (
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2.5"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className={className}
|
|
aria-hidden="true"
|
|
>
|
|
<path d="M5 13l4 4L19 7" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
function BotIcon({ className }: { className?: string }) {
|
|
return (
|
|
<svg
|
|
viewBox="0 0 24 24"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
strokeWidth="2"
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
className={className}
|
|
aria-hidden="true"
|
|
>
|
|
<rect x="4" y="8" width="16" height="12" rx="2" />
|
|
<path d="M12 8V4" />
|
|
<circle cx="12" cy="4" r="1" />
|
|
<path d="M2 14h2" />
|
|
<path d="M20 14h2" />
|
|
<path d="M9 13v2" />
|
|
<path d="M15 13v2" />
|
|
</svg>
|
|
);
|
|
}
|
|
|
|
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="text-accent font-mono text-sm font-semibold tracking-widest uppercase">
|
|
The name
|
|
</div>
|
|
<h2 className="text-text mt-3 text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Named for still water in the Adirondacks.
|
|
</h2>
|
|
<p className="text-text-secondary mx-auto mt-4 max-w-xl text-base leading-relaxed">
|
|
The water at the top of this page is Hadlock Pond in Fort Ann, New
|
|
York. It is quiet, dependable, and built to last. This is the standard
|
|
held for infrastructure. You should not have to think about it for it
|
|
to work.
|
|
</p>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function Contact() {
|
|
return (
|
|
<section
|
|
id="contact"
|
|
className="bg-surface/50 border-border-faint scroll-mt-20 border-t"
|
|
>
|
|
<div className="mx-auto max-w-3xl px-6 py-24">
|
|
<div className="bg-surface border-border relative overflow-hidden border p-8 md:p-12">
|
|
<div className="absolute inset-0 -z-10 bg-[radial-gradient(50%_60%_at_50%_0%,var(--accent-glow),transparent_70%)]" />
|
|
<div className="mb-8 text-center">
|
|
<div className="text-accent font-mono text-sm font-semibold tracking-widest uppercase">
|
|
Get in touch
|
|
</div>
|
|
<h2 className="text-text mx-auto mt-3 max-w-2xl text-3xl font-bold tracking-tight sm:text-4xl">
|
|
Have something technical that needs an owner?
|
|
</h2>
|
|
</div>
|
|
<ContactForm />
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|
|
|
|
function Footer() {
|
|
return (
|
|
<footer className="border-border-faint border-t">
|
|
<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-faint text-sm">
|
|
Hadlock Technologies LLC · New York · © {new Date().getFullYear()}
|
|
</p>
|
|
</div>
|
|
</footer>
|
|
);
|
|
}
|
|
|
|
function SectionHeading({
|
|
eyebrow,
|
|
title,
|
|
description,
|
|
display = false,
|
|
}: {
|
|
eyebrow: string;
|
|
title: string;
|
|
description: string;
|
|
display?: boolean;
|
|
}) {
|
|
return (
|
|
<div className="max-w-2xl">
|
|
<div
|
|
className={`text-accent text-sm font-semibold tracking-widest uppercase ${
|
|
display ? "font-display" : "font-mono"
|
|
}`}
|
|
>
|
|
{eyebrow}
|
|
</div>
|
|
<h2
|
|
className={`text-text mt-3 text-3xl font-bold tracking-tight sm:text-4xl ${
|
|
display ? "font-display uppercase" : ""
|
|
}`}
|
|
>
|
|
{title}
|
|
</h2>
|
|
<p className="text-text-secondary mt-4 text-base leading-relaxed">
|
|
{description}
|
|
</p>
|
|
</div>
|
|
);
|
|
}
|