feat(portfolio): rebuild w-make.com as a bilingual editorial studio

Give clients a real IA, SMTP contact to eldov@w-make.de, and live Batch
evidence instead of a single-page placeholder.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jan Wagner
2026-08-16 03:20:16 +02:00
co-authored by Cursor
parent 1f6894d781
commit 5657b1d202
54 changed files with 2281 additions and 398 deletions
+181
View File
@@ -0,0 +1,181 @@
import Link from "next/link";
import { HomeJsonLd } from "@/components/json-ld";
import { getOffers } from "@/content/offers";
import { getNotes } from "@/content/notes";
import { getPerson } from "@/content/person";
import { getProjects } from "@/content/projects";
import { getDictionary } from "@/i18n/dictionaries";
import { publicPath } from "@/i18n/routes";
import { pageMetadata } from "@/lib/metadata";
import { requireLocale } from "@/lib/locale";
import { getUpdates } from "@/lib/updates";
export async function generateMetadata({ params }: PageProps<"/[lang]">) {
const locale = requireLocale((await params).lang);
const dict = getDictionary(locale);
return pageMetadata(
locale,
"home",
locale === "de"
? "Jan Wagner — Software für anspruchsvolle Prozesse"
: "Jan Wagner — Software for demanding processes",
dict.home.lede,
);
}
export default async function HomePage({ params }: PageProps<"/[lang]">) {
const locale = requireLocale((await params).lang);
const dict = getDictionary(locale);
const person = getPerson(locale);
const projects = getProjects(locale);
const offers = getOffers(locale);
const notes = getNotes(locale).slice(0, 2);
const updates = await getUpdates();
return (
<main>
<HomeJsonLd />
<section className="mx-auto grid w-full max-w-6xl gap-16 px-6 py-20 md:grid-cols-[1.35fr_0.65fr] md:items-end md:px-10 md:py-28">
<div>
<p className="kicker">{dict.home.kicker}</p>
<h1 className="display mt-6 max-w-4xl text-5xl text-paper md:text-7xl">{dict.home.title}</h1>
<p className="mt-8 max-w-2xl text-lg leading-8 text-mute-strong md:text-xl">{dict.home.lede}</p>
<div className="mt-10 flex flex-wrap gap-8 text-sm">
<Link href={publicPath(locale, "work")} className="border-b border-copper pb-1 text-copper">
{dict.actions.viewWork}
</Link>
<Link href={publicPath(locale, "contact")} className="border-b border-line-strong pb-1 text-paper">
{dict.actions.startConversation}
</Link>
</div>
</div>
<aside className="border-l border-copper/50 pl-6">
<p className="kicker">{dict.home.asideLabel}</p>
<p className="mt-4 font-serif text-2xl leading-snug text-paper">{dict.home.asideTitle}</p>
<p className="mt-5 text-sm leading-7 text-mute-strong">{dict.home.asideBody}</p>
</aside>
</section>
<section className="border-t border-line">
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
<div className="mb-12 flex flex-wrap items-end justify-between gap-6">
<div>
<p className="kicker">{dict.home.workKicker}</p>
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.workTitle}</h2>
</div>
<p className="max-w-md text-sm leading-7 text-mute-strong">{dict.home.workLede}</p>
</div>
<div className="grid gap-12 md:grid-cols-2">
{projects.map((project) => (
<article key={project.slug} className="border-t border-line pt-8">
<div className="flex items-baseline justify-between gap-4">
<p className="kicker">{project.label}</p>
<span className="font-mono text-xs text-mute">{project.number}</span>
</div>
<h3 className="mt-5 font-serif text-3xl">{project.title}</h3>
<p className="mt-4 leading-7 text-mute-strong">{project.summary}</p>
<p className="mt-6 font-mono text-xs uppercase tracking-[0.14em] text-mute">{project.status}</p>
<Link
href={publicPath(locale, "workItem", project.slug)}
className="mt-6 inline-block border-b border-copper pb-1 text-sm text-copper"
>
{dict.actions.openCase}
</Link>
</article>
))}
</div>
</div>
</section>
<section className="border-t border-line">
<div className="mx-auto grid w-full max-w-6xl gap-12 px-6 py-20 md:grid-cols-[0.8fr_1.2fr] md:px-10">
<div>
<p className="kicker">{dict.home.methodKicker}</p>
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.methodTitle}</h2>
<p className="mt-6 max-w-md leading-7 text-mute-strong">{dict.home.methodLede}</p>
</div>
<ol className="grid gap-8">
{person.principles.map((principle, index) => (
<li key={principle.title} className="grid gap-3 border-b border-line pb-8 md:grid-cols-[3rem_1fr]">
<span className="font-mono text-sm text-copper">0{index + 1}</span>
<div>
<h3 className="font-serif text-2xl">{principle.title}</h3>
<p className="mt-2 max-w-xl leading-7 text-mute-strong">{principle.text}</p>
</div>
</li>
))}
</ol>
</div>
</section>
<section className="border-t border-line">
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
<p className="kicker">{dict.home.offerKicker}</p>
<h2 className="display mt-4 max-w-2xl text-4xl md:text-5xl">{dict.home.offerTitle}</h2>
<div className="mt-12 grid gap-10 md:grid-cols-3">
{offers.map((offer, index) => (
<article key={offer.title} className="border-l border-line-strong pl-5">
<span className="font-mono text-xs text-copper">0{index + 1}</span>
<h3 className="mt-4 font-serif text-2xl">{offer.title}</h3>
<p className="mt-3 text-sm leading-7 text-mute-strong">{offer.text}</p>
</article>
))}
</div>
</div>
</section>
<section className="border-t border-line">
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
<p className="kicker">{dict.home.notesKicker}</p>
<h2 className="display mt-4 text-4xl md:text-5xl">{dict.home.notesTitle}</h2>
<div className="mt-12 grid gap-10 md:grid-cols-2">
{notes.map((note) => (
<article key={note.slug}>
<time className="font-mono text-xs text-mute" dateTime={note.date}>
{new Date(note.date).toLocaleDateString(locale === "de" ? "de-DE" : "en-GB")}
</time>
<h3 className="mt-3 font-serif text-2xl">{note.title}</h3>
<p className="mt-3 text-sm leading-7 text-mute-strong">{note.summary}</p>
<Link
href={publicPath(locale, "noteItem", note.slug)}
className="mt-5 inline-block text-sm text-copper"
>
{dict.actions.openNote}
</Link>
</article>
))}
</div>
{updates.length ? (
<div className="mt-16 border-t border-line pt-10">
<p className="kicker">Batchmaker</p>
<div className="mt-6 grid gap-6 md:grid-cols-3">
{updates.map((update) => (
<article key={update.slug}>
<h3 className="font-serif text-xl">{update.title}</h3>
<p className="mt-2 text-sm leading-6 text-mute-strong">{update.summary}</p>
</article>
))}
</div>
</div>
) : null}
</div>
</section>
<section className="border-t border-line">
<div className="mx-auto w-full max-w-6xl px-6 py-20 md:px-10">
<div className="border border-copper/30 bg-copper/5 px-8 py-12 md:px-12">
<p className="kicker">{dict.home.ctaKicker}</p>
<h2 className="display mt-4 max-w-3xl text-4xl md:text-6xl">{dict.home.ctaTitle}</h2>
<p className="mt-6 max-w-2xl leading-7 text-mute-strong">{dict.home.ctaBody}</p>
<Link
href={publicPath(locale, "contact")}
className="mt-8 inline-block border border-copper bg-copper px-6 py-3 text-sm font-medium text-ink"
>
{dict.actions.startConversation}
</Link>
</div>
</div>
</section>
</main>
);
}