← 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>
  );
}