← back to Stayclaim

src/app/people/page.tsx

121 lines

import Link from 'next/link';
import type { Metadata } from 'next';
import { listEntitiesWithCounts } from '@/lib/db';
import { BreadcrumbArchive } from '@/components/BreadcrumbArchive';
import { CrossPromo } from '@/components/CrossPromo';
import { currentSurface } from '@/lib/site';

export const metadata: Metadata = {
  title: 'People',
  description: 'Architects, residents, and studios in the archive — historical only.',
  alternates: { canonical: '/people' },
};

const KIND_LABEL: Record<string, string> = {
  architect: 'Architects',
  studio: 'Studios',
  person: 'People',
  business: 'Businesses',
};

export default async function PeopleIndex() {
  const surface = await currentSurface();
  const all = await listEntitiesWithCounts();

  // Featured = entities with the most public associations; max 4 across kinds.
  const featured = [...all].sort((a, b) => b.oeuvre_count - a.oeuvre_count).slice(0, 4);
  const grouped: Record<string, typeof all> = {};
  for (const e of all) {
    if (!grouped[e.kind]) grouped[e.kind] = [];
    grouped[e.kind]!.push(e);
  }

  return (
    <>
      <div className="max-w-5xl mx-auto px-6 pt-6">
        <BreadcrumbArchive items={[{ label: 'Archive', href: '/' }, { label: 'People' }]} />
      </div>

      <header className="max-w-5xl mx-auto px-6 py-12 border-b border-ink/10">
        <p className="text-xs uppercase tracking-[0.15em] text-ink/50 mb-2">People</p>
        <h1 className="font-display text-5xl md:text-6xl text-ink tracking-[-0.02em] leading-[1.02]">
          The people in the archive.
        </h1>
        <p className="mt-4 font-display italic text-xl text-ink/70 max-w-prose">
          Historical only. Architects, studios, and prior occupants whose ties to a place are documented.
        </p>
      </header>

      {featured.length > 0 && featured[0]!.oeuvre_count > 0 && (
        <section className="max-w-5xl mx-auto px-6 py-10 border-b border-ink/10">
          <h2 className="font-display text-2xl text-ink tracking-[-0.01em] mb-5">Most cited</h2>
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
            {featured.map(e => (
              <Link
                key={e.id}
                href={`/entity/${encodeURIComponent(e.slug)}`}
                className="block border border-ink/10 bg-sand p-5 hover:border-ink/40 transition group"
              >
                <p className="font-mono text-[10px] uppercase tracking-[0.15em] text-ink/45">
                  {e.kind}
                </p>
                <h3 className="font-display text-xl text-ink leading-tight mt-1 group-hover:text-coral transition">
                  {e.display_name}
                </h3>
                <p className="font-mono text-xs text-ink/50 mt-1">
                  {e.birth_year ?? '?'}–{e.death_year ?? '?'}
                </p>
                <p className="mt-3 font-display text-3xl text-ink/80 group-hover:text-coral">
                  {e.oeuvre_count}
                  <span className="ml-1 font-sans text-[10px] uppercase tracking-[0.15em] text-ink/40">
                    {e.oeuvre_count === 1 ? 'work' : 'works'}
                  </span>
                </p>
              </Link>
            ))}
          </div>
        </section>
      )}

      <section className="max-w-5xl mx-auto px-6 py-12">
        {Object.entries(grouped).map(([kind, list]) => (
          <div key={kind} className="mb-12">
            <h2 className="font-display text-3xl text-ink tracking-[-0.01em] mb-5">{KIND_LABEL[kind] ?? kind}</h2>
            <ul className="grid md:grid-cols-2 gap-3">
              {list.map(e => (
                <li key={e.id}>
                  <Link
                    href={`/entity/${encodeURIComponent(e.slug)}`}
                    className="block border border-ink/10 bg-sand p-4 hover:border-ink/40 transition group"
                  >
                    <div className="flex items-baseline justify-between gap-3">
                      <div>
                        <div className="font-display text-2xl text-ink leading-tight group-hover:text-coral">
                          {e.display_name}
                        </div>
                        <div className="mt-1 text-xs text-ink/60 font-mono">
                          {e.birth_year ?? '?'}–{e.death_year ?? '?'} · {e.kind}
                        </div>
                      </div>
                      <span className="font-mono text-xs text-ink/50 whitespace-nowrap">
                        {e.oeuvre_count} {e.oeuvre_count === 1 ? 'addr' : 'addrs'}
                      </span>
                    </div>
                  </Link>
                </li>
              ))}
            </ul>
          </div>
        ))}
        <p className="mt-12 text-xs text-ink/40 max-w-prose leading-relaxed">
          We do not include living persons. The privacy gate enforces this at the database level — any
          association with a living person ending within the last 25 years cannot be made public, no
          matter how it&rsquo;s entered.
        </p>
      </section>

      <CrossPromo current={surface.key} />
    </>
  );
}