Files
mintel.me/apps/web/app/(site)/page.tsx
T
mmintelandClaude Opus 5.5 fa40fefe2f feat: rebuild mintel.me as a single-page freelance site
The site now presents one freelance developer on one page: hero, introduction,
four typical cases (developer for a product, make an app reliable, automate a
process, a website) with what you get, how it runs and what it costs, a
process automation section, technology and project fit, how I work, two live
references, questions and contact.

- Hero and closing headline are drawn from running binary digits.
- Each case explains its four steps with an animated drawing; the automation
  section shows example workflows with work travelling between systems; a
  pipeline run shows what a test catches before a release.
- Contact is one email address, kept in parts and assembled in the browser so
  it never appears in the page source (src/domain/email.ts, spec first).
- Imprint and privacy pages added.
- The old site (blog, case studies, contact form, fixed-price pages, video
  rendering and related scripts) is removed. Its URLs redirect to the homepage.
- The deploy smoke test checks the legal pages and the redirects instead of
  the removed case study.
- Dev scripts use docker compose v2.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 22:33:06 +02:00

315 lines
11 KiB
TypeScript

import type { Metadata } from "next";
import { Block } from "@/src/components/home/Block";
import { ClosingCta } from "@/src/components/home/ClosingCta";
import { Faq } from "@/src/components/home/Faq";
import { HeroStatement } from "@/src/components/home/HeroStatement";
import { HomeJsonLd } from "@/src/components/home/HomeJsonLd";
import { PathPicker } from "@/src/components/home/PathPicker";
import { PortraitSection } from "@/src/components/home/PortraitSection";
import { StickyBook } from "@/src/components/home/StickyBook";
import { AutomationFlow } from "@/src/components/illustrations/AutomationFlow";
import { CareerLine } from "@/src/components/illustrations/CareerLine";
import { ContactDiagram } from "@/src/components/illustrations/ContactDiagram";
import { PipelineRun } from "@/src/components/illustrations/PipelineRun";
import {
WorkShowcase,
type WorkItem,
} from "@/src/components/illustrations/WorkShowcase";
import { Reveal } from "@/src/components/Reveal";
import { CAREER, FIT, TECH } from "@/src/content/about";
import { AUTOMATION_NOTES } from "@/src/content/automation";
export const metadata: Metadata = {
title: "Marc Mintel — Senior developer for web apps and websites",
description:
"Freelance developer with 15 years of experience. I build web apps, interfaces and websites: by the hour for product teams, or a complete website for your business.",
alternates: { canonical: "/" },
};
const MUTED = "text-slate-400";
const BODY = "m-0 font-sans text-lg leading-relaxed text-slate-500 md:text-xl";
const MONO = "font-mono text-[11px] uppercase tracking-[0.2em] text-slate-500";
const PRINCIPLES = [
{
title: "One contact",
text: "You talk to me, and I am the one who does the work.",
},
{
title: "Hours you can check",
text: "I track my time. The monthly invoice shows what the hours were spent on.",
},
{
title: "Careful work",
text: "I test what I build before I hand it over, and I say so when something is not finished or I am not sure.",
},
{
title: "What I build is yours",
text: "Code and design belong to you. You can hand them to another developer.",
},
] as const;
const WORK: readonly WorkItem[] = [
{
name: "klz-cables.com",
href: "https://klz-cables.com",
client: "Cable supplier",
text: "The company website of a supplier of energy cables, in German and English.",
did: [
"Product catalogue with categories and detail pages",
"Datasheets generated from the product data",
"Content the client can edit in a CMS",
],
tech: ["Next.js", "TypeScript", "Headless CMS", "Two languages"],
desktop: { src: "/work/klz-long.webp", width: 1200, height: 1680 },
mobile: { src: "/work/klz-mobile.webp", width: 390, height: 3600 },
},
{
name: "e-tib.com",
href: "https://e-tib.com",
client: "Cable network construction",
text: "The company website of a group in cable network construction, in German and English.",
did: [
"Pages for services, references, careers and events",
"The same content in two languages",
"Works on phone, tablet and desktop",
],
tech: ["Next.js", "TypeScript", "Headless CMS", "Two languages"],
desktop: { src: "/work/etib-long.webp", width: 1200, height: 4333 },
mobile: { src: "/work/etib-mobile.webp", width: 390, height: 3600 },
},
];
export default function LandingPage() {
return (
<div className="relative flex flex-col bg-white">
<HomeJsonLd />
<HeroStatement />
<StickyBook />
<PortraitSection
id="hello"
index="01"
label="Hello"
title={<>I am Marc.</>}
>
<p className="m-0 font-sans text-2xl leading-snug text-white md:text-4xl">
I have been building for the web for 15 years: first in agencies,
later as a senior developer, and now on my own.
</p>
<p className="m-0 mt-8 font-sans text-lg leading-relaxed text-slate-300 md:text-xl">
My strength is the part people see and use: design and interface. I am
also at home with what lies behind it, so I can build a complete
application and not just its surface.
</p>
<p className="m-0 mt-6 font-sans text-lg leading-relaxed text-slate-300 md:text-xl">
You work with me directly. Whether you run a product team or are
commissioning something like this for the first time.
</p>
</PortraitSection>
<section className="bg-white">
<div className="mx-auto w-full max-w-[1400px] px-5 py-16 md:px-12 md:py-28">
<p className={`${MONO} m-0 mb-10`}>The path so far</p>
<CareerLine stations={CAREER} />
</div>
</section>
<Block
index="02"
label="Services"
title={
<>
What do you need? <span className={MUTED}>Four typical cases.</span>
</>
}
>
<Reveal>
<PathPicker />
</Reveal>
</Block>
<Block
index="03"
label="Process automation"
title={
<>
Let the systems <span className={MUTED}>do the copying.</span>
</>
}
>
<Reveal>
<p className={`${BODY} mb-12 max-w-3xl`}>
In most companies, people move information from one tool to another:
read it here, type it in there, send an email about it. Automation
connects those tools so that the work passes from one to the next on
its own. Three typical examples:
</p>
</Reveal>
<Reveal>
<AutomationFlow />
</Reveal>
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-24 md:grid-cols-3 md:gap-10">
{AUTOMATION_NOTES.map((group, i) => (
<Reveal key={group.title} delay={0.1 * i}>
<div className="border-t border-slate-900 pt-6">
<p className={`${MONO} m-0`}>{group.title}</p>
<ul className="m-0 mt-4 list-none p-0">
{group.items.map((item) => (
<li
key={item}
className="border-b border-slate-100 py-4 font-sans text-lg text-slate-900 md:text-xl"
>
{item}
</li>
))}
</ul>
</div>
</Reveal>
))}
</div>
</Block>
<Block
index="04"
label="Technology"
title={
<>
Which projects <span className={MUTED}>I am right for.</span>
</>
}
>
<div className="grid grid-cols-1 gap-12 md:grid-cols-3 md:gap-10">
{FIT.map((group, i) => (
<Reveal key={group.title} delay={0.1 * i}>
<div
className={`border-t pt-6 ${i === 2 ? "border-slate-300" : "border-slate-900"}`}
>
<p className={`${MONO} m-0`}>{group.title}</p>
<ul className="m-0 mt-4 list-none p-0">
{group.items.map((item) => (
<li
key={item}
className={`border-b border-slate-100 py-4 font-sans text-lg md:text-xl ${
i === 2 ? "text-slate-400" : "text-slate-900"
}`}
>
{item}
</li>
))}
</ul>
</div>
</Reveal>
))}
</div>
<Reveal>
<div className="mt-16 rounded-3xl bg-slate-50 p-6 md:mt-24 md:p-10">
<p className={`${MONO} m-0`}>What I work with</p>
<div className="mt-8 grid grid-cols-1 gap-10 sm:grid-cols-2 lg:grid-cols-4">
{TECH.map((group) => (
<div key={group.area}>
<p className="m-0 font-sans text-xl font-bold tracking-tight text-slate-900 md:text-2xl">
{group.area}
</p>
<ul className="m-0 mt-4 flex list-none flex-wrap gap-2 p-0">
{group.items.map((item) => (
<li
key={item}
className="rounded-full border border-slate-200 bg-white px-4 py-2 font-mono text-[11px] uppercase tracking-[0.12em] text-slate-700"
>
{item}
</li>
))}
</ul>
</div>
))}
</div>
</div>
</Reveal>
<div className="mt-16 md:mt-24">
<Reveal>
<h3 className="m-0 font-sans text-3xl font-bold tracking-tighter text-slate-900 md:text-5xl">
What happens before a change goes live.
</h3>
<p className={`${BODY} mb-12 mt-6 max-w-3xl`}>
Software is connected: a change in one place can break another. I
secure what I build with automated tests, and every change has to
pass them before it is released. This is what such a run looks
like.
</p>
</Reveal>
<Reveal>
<PipelineRun />
</Reveal>
</div>
</Block>
<Block
index="05"
label="Working with me"
title={
<>
How I work. <span className={MUTED}>Nothing unusual.</span>
</>
}
>
<Reveal>
<div className="mb-16 overflow-hidden rounded-3xl bg-slate-900 p-4 md:mb-24 md:p-10">
<ContactDiagram />
</div>
</Reveal>
<div className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
{PRINCIPLES.map((item, i) => (
<Reveal key={item.title} delay={0.08 * i}>
<div className="border-t border-slate-900 pt-6">
<h3 className="m-0 font-sans text-3xl font-bold tracking-tight text-slate-900 md:text-4xl">
{item.title}
</h3>
<p className={`${BODY} mt-4`}>{item.text}</p>
</div>
</Reveal>
))}
</div>
</Block>
<Block
index="06"
label="My work"
title={
<>
Two sites I built. <span className={MUTED}>Both are live.</span>
</>
}
>
<WorkShowcase items={WORK} />
<Reveal>
<p
className={`${BODY} mt-20 max-w-3xl border-t border-slate-200 pt-8 md:mt-28`}
>
Besides client work I build my own software, among other things a
system in Rust and Solidity that runs on seven blockchains.
</p>
</Reveal>
</Block>
<Block
index="07"
label="Questions"
title={
<>
Good to know <span className={MUTED}>before we talk.</span>
</>
}
>
<Reveal>
<Faq />
</Reveal>
</Block>
<ClosingCta />
</div>
);
}