Skip to content

Agency landing page

Work first, then process, then people — with the contact block folded into the footer.

BusinessintermediateFeaturedagencystudioportfoliocase-studies

Live preview

Open the full page
full widthLive preview — open it in a new tab for the full-height version.
Open the preview in a new tab

Source

This exact file renders the preview above.

import CenteredNavbar from '@/components/blocks/navigation/centered'
import AgencyHeader from '@/components/blocks/headers/agency'
import FeaturedCaseStudy from '@/components/blocks/sections/case-studies/featured'
import CaseStudyGrid from '@/components/blocks/sections/case-studies/grid'
import SplitDetailProcess from '@/components/blocks/sections/process/split-detail'
import LeadershipTeam from '@/components/blocks/sections/team/leadership'
import CaseStudyMetricsStrip from '@/components/blocks/sections/case-studies/metrics-strip'
import LogoQuotePairTestimonials from '@/components/blocks/sections/testimonials/logo-quote-pair'
import AgencyFooter from '@/components/blocks/footers/agency'

/**
 * Agency landing page
 *
 * A studio site is a portfolio with an argument attached, so the work comes
 * before the process and the process before the people. The closing contact
 * block is the footer itself rather than a separate CTA section.
 */
export default function AgencyLandingPattern() {
  return (
    <>
      <CenteredNavbar />
      <main>
        <AgencyHeader />
        <FeaturedCaseStudy />
        <CaseStudyGrid />
        <CaseStudyMetricsStrip />
        <SplitDetailProcess />
        <LogoQuotePairTestimonials />
        <LeadershipTeam />
      </main>
      <AgencyFooter />
    </>
  )
}

components/patterns/agency-landing.tsx

import CenteredNavbar from '@/components/blocks/navigation/centered'
import AgencyHeader from '@/components/blocks/headers/agency'
import FeaturedCaseStudy from '@/components/blocks/sections/case-studies/featured'
import CaseStudyGrid from '@/components/blocks/sections/case-studies/grid'
import SplitDetailProcess from '@/components/blocks/sections/process/split-detail'
import LeadershipTeam from '@/components/blocks/sections/team/leadership'
import CaseStudyMetricsStrip from '@/components/blocks/sections/case-studies/metrics-strip'
import LogoQuotePairTestimonials from '@/components/blocks/sections/testimonials/logo-quote-pair'
import AgencyFooter from '@/components/blocks/footers/agency'

/**
 * Agency landing page
 *
 * A studio site is a portfolio with an argument attached, so the work comes
 * before the process and the process before the people. The closing contact
 * block is the footer itself rather than a separate CTA section.
 */
export default function AgencyLandingPattern() {
  return (
    <>
      <CenteredNavbar />
      <main>
        <AgencyHeader />
        <FeaturedCaseStudy />
        <CaseStudyGrid />
        <CaseStudyMetricsStrip />
        <SplitDetailProcess />
        <LogoQuotePairTestimonials />
        <LeadershipTeam />
      </main>
      <AgencyFooter />
    </>
  )
}

components/blocks/headers/agency.tsx

import Link from 'next/link'
import { ArrowUpRight } from 'lucide-react'
import { Container } from '@/components/ui/layout'
import { companyLogos } from '@/content/demo'

/**
 * Agency header
 *
 * A statement of practice rather than a product pitch: an oversized claim, a
 * short capability list, and the client roster placed immediately below — for
 * a studio, the roster *is* the proof.
 *
 * The claim uses a mixed-weight line so it reads as a sentence, not a slogan.
 */
const capabilities = [
  'Design systems',
  'Product design',
  'Frontend architecture',
  'Accessibility audits',
]

export default function AgencyHeader() {
  return (
    <header className="border-b border-line bg-canvas py-section">
      <Container>
        <h1 className="display-type max-w-5xl text-4xl leading-[1.08] font-semibold text-ink-strong sm:text-5xl lg:text-6xl">
          We build the systems{' '}
          <span className="text-ink-subtle">other teams build products on.</span>
        </h1>

        <div className="mt-10 grid gap-8 border-t border-line pt-8 lg:grid-cols-[1fr_auto] lg:items-end">
          <ul className="flex flex-wrap gap-x-8 gap-y-2">
            {capabilities.map((capability) => (
              <li key={capability} className="label-caps text-ink-muted">
                {capability}
              </li>
            ))}
          </ul>
          <Link
            href="/starters/agency/preview/work"
            className="inline-flex items-center gap-2 text-sm font-semibold text-ink-strong underline underline-offset-4 hover:text-accent"
          >
            Selected work
            <ArrowUpRight className="size-4" aria-hidden="true" />
          </Link>
        </div>

        <div className="mt-12">
          <p className="label-caps mb-4 text-ink-subtle">Working with</p>
          <ul className="grid grid-cols-2 gap-x-8 gap-y-4 sm:grid-cols-4">
            {companyLogos.slice(0, 8).map((logo) => (
              <li key={logo} className="display-type text-lg font-medium text-ink-subtle">
                {logo}
              </li>
            ))}
          </ul>
        </div>
      </Container>
    </header>
  )
}

components/blocks/sections/case-studies/featured.tsx

import Link from 'next/link'
import { ArrowUpRight } from 'lucide-react'
import { Container } from '@/components/ui/layout'
import { Badge } from '@/components/ui/badge'
import { caseStudies } from '@/content/demo'

/**
 * Featured case study
 *
 * One study given a whole section: client, claim, three outcome figures and a
 * link. Outcome figures are the part that turns a story into evidence, which is
 * why the layout reserves room for exactly three.
 */
export default function FeaturedCaseStudy() {
  const study = caseStudies[0]
  if (!study) return null

  return (
    <section className="border-b border-line bg-surface-sunken py-section">
      <Container>
        <div className="grid gap-10 lg:grid-cols-[1.2fr_1fr] lg:gap-16">
          <div>
            <div className="flex flex-wrap items-center gap-2">
              <Badge tone="accent">{study.client}</Badge>
              <Badge>{study.sector}</Badge>
              <Badge>{study.year}</Badge>
            </div>
            <h2 className="display-type mt-5 text-2xl leading-tight font-semibold text-ink-strong sm:text-3xl">
              {study.title}
            </h2>
            <p className="mt-4 max-w-lg text-md leading-relaxed text-ink-muted">{study.summary}</p>
            <Link
              href={`/starters/agency/preview/work/${study.slug}`}
              className="mt-7 inline-flex items-center gap-1.5 text-sm font-semibold text-accent underline underline-offset-4"
            >
              Read the case study
              <ArrowUpRight className="size-4" aria-hidden="true" />
            </Link>
          </div>

          <dl className="grid grid-cols-1 gap-px overflow-hidden rounded-xl border border-line bg-line sm:grid-cols-3 lg:grid-cols-1">
            {study.metrics.map((metric) => (
              <div key={metric.label} className="bg-surface px-6 py-6">
                <dd className="display-type text-3xl font-semibold text-ink-strong tabular-nums">
                  {metric.value}
                </dd>
                <dt className="label-caps mt-1.5 text-ink-subtle">{metric.label}</dt>
              </div>
            ))}
          </dl>
        </div>
      </Container>
    </section>
  )
}

Demo source — adapt to your project. Foundry is not published as a package.

Usage

A studio site is a portfolio with an argument attached. The work comes before the process, the process before the people, and the contact block is the footer itself.

  • Lead with one featured study rather than a uniform grid; the grid follows immediately.
  • The aggregate metrics strip works only after individual studies have earned it.

Variants and states

Every entry below is a genuine difference in behaviour or layout, and every one of them is visible in the preview above.

  • Centred navbar
  • Practice-statement hero with roster
  • Featured plus grid case studies
  • Deliverable-led process

Accessibility

Whole-card links
Case study cards are single anchors, so the page has no nested-link traps.
Inverted footer
The footer uses ink-inverse tokens, so contrast holds in every palette.

Foundry implements published ARIA patterns and is tested against them. No WCAG certification is claimed — see the accessibility documentation for what is and is not covered.