← back to Stayclaim
src/components/home/ClaimHome.tsx
213 lines
/**
* ClaimMyAddress — onboarding home. Conversion-focused.
* Hero: BIG search + claim CTA. Show "we already have this much on your house"
* to motivate the claim. Red accent (Gucci palette).
*/
import Link from 'next/link';
import { unstable_cache } from 'next/cache';
import { pool } from '@/lib/db';
import { sisterSurfaces } from '@/lib/site';
export const dynamic = 'force-dynamic';
const getCounts = unstable_cache(
async () => {
const { rows } = await pool.query<any>(`
SELECT
(SELECT count(*) FROM listing) as listings,
(SELECT count(*) FROM permit) as permits,
(SELECT count(*) FROM parcel) as parcels,
(SELECT count(*) FROM business_license) as businesses,
(SELECT count(*) FROM place_event) as events
`);
return rows[0];
},
['claim-home-counts'],
{ revalidate: 600 },
);
type RecentClaim = {
slug: string;
title: string;
address_line1: string | null;
city: string | null;
claimed_at: string | null;
};
async function getRecentClaims(): Promise<RecentClaim[]> {
const { rows } = await pool.query<RecentClaim>(`
SELECT slug, title, address_line1, city, claimed_at
FROM listing
WHERE claimed_by IS NOT NULL AND is_public = true
ORDER BY claimed_at DESC LIMIT 6
`);
return rows;
}
export default async function ClaimHome() {
const [counts, claims] = await Promise.all([getCounts(), getRecentClaims()]);
return (
<>
{/* HERO — bold red, claim-first */}
<section className="bg-coral text-ink">
<div className="max-w-7xl mx-auto px-6 py-20 md:py-32">
<p className="text-[10px] uppercase tracking-[0.3em] text-ink/70 font-mono mb-4">
ClaimMyAddress · est. 2026
</p>
<h1 className="font-display text-[14vw] md:text-[11rem] leading-[0.82] tracking-[-0.04em]">
Your home.<br/>
Your file.<br/>
<span className="italic text-ink/85">Forever.</span>
</h1>
<p className="mt-10 font-display italic text-2xl md:text-3xl max-w-3xl leading-tight text-ink/80">
We already have <span className="font-display not-italic font-medium">{Number(counts?.events ?? 0).toLocaleString()}</span> documented events
tied to LA-area addresses — permits, sales, evictions, code cases, historical residents.
Claim yours and own the file.
</p>
<form action="/search" className="mt-14 flex flex-col gap-3 max-w-2xl">
<input
type="search"
name="q"
placeholder="Type your street address…"
autoFocus
className="border-2 border-ink bg-sand px-6 py-5 text-xl font-sans focus:outline-none focus:border-ink"
/>
<button type="submit" className="bg-ink text-coral px-8 py-5 text-sm uppercase tracking-[0.25em] hover:bg-sand hover:text-ink transition font-mono font-medium">
See what we have →
</button>
</form>
<div className="mt-20 grid grid-cols-2 md:grid-cols-4 gap-x-8 gap-y-6 text-ink/85">
<Stat label="addresses indexed" value={Number(counts?.listings ?? 0).toLocaleString()} />
<Stat label="building permits" value={Number(counts?.permits ?? 0).toLocaleString()} />
<Stat label="parcel records" value={Number(counts?.parcels ?? 0).toLocaleString()} />
<Stat label="historical events" value={Number(counts?.events ?? 0).toLocaleString()} />
</div>
</div>
</section>
{/* WHY CLAIM — three-step explainer */}
<section className="bg-sand text-ink py-24 border-y border-ink/15">
<div className="max-w-6xl mx-auto px-6">
<p className="text-[10px] uppercase tracking-[0.25em] text-coral font-mono mb-4">How it works</p>
<h2 className="font-display text-5xl md:text-7xl tracking-[-0.02em] leading-[0.95] mb-16">
Three steps.<br/>
<span className="italic text-ink/60">No real-estate-agent voice.</span>
</h2>
<ol className="space-y-10 md:space-y-0 md:grid md:grid-cols-3 md:gap-12">
<Step n={1} title="Find your address" body="Type it in. We probably already have a free record built from public permits, sales, parcels, and historical archives." />
<Step n={2} title="Verify it's yours" body="Match against deed records, utility bills, or upload the docs. We never resell — claim is one-way." />
<Step n={3} title="Own the file" body="Add photos, copy, contact info, booking link, family history. The archive stays independent — you cannot rewrite the past, only add to it." />
</ol>
</div>
</section>
{/* CITED EVERYWHERE */}
<section className="bg-ink text-sand py-20">
<div className="max-w-6xl mx-auto px-6">
<p className="text-[10px] uppercase tracking-[0.25em] text-coral font-mono mb-4">Why this matters</p>
<h2 className="font-display text-4xl md:text-6xl tracking-[-0.02em] leading-[0.95] mb-10">
Every fact carries<br/>
<span className="italic text-coral">its own source.</span>
</h2>
<ul className="grid md:grid-cols-2 gap-x-10 gap-y-6 text-sm leading-relaxed">
<li>
<p className="font-display text-xl text-sand mb-1">Permits → data.lacity.org row</p>
<p className="text-sand/60 font-mono text-xs">Every LADBS permit links back to its canonical Socrata record.</p>
</li>
<li>
<p className="font-display text-xl text-sand mb-1">Parcels → portal.assessor.lacounty.gov</p>
<p className="text-sand/60 font-mono text-xs">APN deep-link to LA County Assessor PAIS detail page.</p>
</li>
<li>
<p className="font-display text-xl text-sand mb-1">Newspapers → archive.org scan</p>
<p className="text-sand/60 font-mono text-xs">Click any historical mention to see the actual scanned page.</p>
</li>
<li>
<p className="font-display text-xl text-sand mb-1">Archives → finding aids</p>
<p className="text-sand/60 font-mono text-xs">UCLA Special Collections etc. — never a generic library URL, always the actual collection.</p>
</li>
</ul>
</div>
</section>
{/* RECENT CLAIMS */}
{claims.length > 0 && (
<section className="bg-sand text-ink py-20 border-t border-ink/15">
<div className="max-w-6xl mx-auto px-6">
<p className="text-[10px] uppercase tracking-[0.25em] text-coral font-mono mb-4">Recently claimed</p>
<h2 className="font-display text-4xl md:text-5xl tracking-[-0.02em] leading-tight mb-8">
Houses now owned by their owners.
</h2>
<ul className="divide-y divide-ink/15">
{claims.map(c => (
<li key={c.slug} className="py-4 flex items-baseline justify-between gap-4 flex-wrap">
<Link href={`/address/${encodeURIComponent(c.slug)}`} className="font-display text-2xl hover:text-coral transition">
{c.title}
</Link>
<span className="font-mono text-xs text-ink/45">{c.address_line1 ?? c.city}</span>
</li>
))}
</ul>
</div>
</section>
)}
<section className="bg-coral text-ink py-12 border-t-2 border-ink">
<div className="max-w-4xl mx-auto px-6 flex flex-col md:flex-row items-center justify-between gap-6">
<p className="font-display italic text-2xl md:text-3xl">Free forever. No upsell.</p>
<Link href="/search" className="bg-ink text-coral px-8 py-4 text-xs uppercase tracking-[0.25em] hover:bg-sand hover:text-ink transition font-mono">
Claim your address →
</Link>
</div>
</section>
{/* SISTER SURFACES */}
<section className="bg-ink text-sand py-16">
<div className="max-w-7xl mx-auto px-6">
<p className="text-[10px] uppercase tracking-[0.25em] text-coral font-mono mb-6">Sister sites</p>
<div className="grid md:grid-cols-2 gap-6">
{sisterSurfaces('claim').map(s => (
<a
key={s.domain}
href={`https://${s.domain}${s.primaryCta.href}`}
className="block border-2 border-sand/15 hover:border-coral p-8 group transition"
>
<p className="font-mono text-[10px] uppercase tracking-[0.25em] text-sand/45">{s.domain}</p>
<h3 className="font-display text-3xl text-sand mt-2 tracking-[-0.01em] group-hover:text-coral transition">
{s.brandName}
</h3>
<p className="font-display italic text-lg text-sand/65 mt-1">{s.tagline}</p>
<p className="mt-4 font-mono text-[11px] uppercase tracking-[0.15em] text-coral">
{s.primaryCta.label} →
</p>
</a>
))}
</div>
</div>
</section>
</>
);
}
function Stat({ label, value }: { label: string; value: string }) {
return (
<div className="border-t border-ink/30 pt-3">
<p className="font-display text-3xl md:text-4xl text-ink tabular-nums leading-tight">{value}</p>
<p className="text-[10px] uppercase tracking-[0.2em] text-ink/55 mt-1 font-mono">{label}</p>
</div>
);
}
function Step({ n, title, body }: { n: number; title: string; body: string }) {
return (
<li className="border-t-2 border-coral pt-5">
<span className="font-display text-7xl text-coral leading-[0.7] tracking-tight">{n}</span>
<h3 className="font-display text-2xl text-ink mt-4 tracking-tight">{title}</h3>
<p className="text-sm text-ink/65 mt-3 leading-relaxed max-w-sm">{body}</p>
</li>
);
}