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>
This commit is contained in:
2026-10-03 22:33:06 +02:00
co-authored by Claude Opus 5.5
parent 4eb663f42a
commit fa40fefe2f
781 changed files with 4925 additions and 55137 deletions
+54
View File
@@ -0,0 +1,54 @@
import * as React from "react";
import { Reveal } from "@/src/components/Reveal";
interface BlockProps {
/** Two-digit section index, shown with its binary form. */
index: string;
label: string;
title: React.ReactNode;
id?: string;
children: React.ReactNode;
}
/**
* Block: one section of the homepage. A single left axis, one kicker line,
* one oversized headline, then the content. No sidebars.
*/
export const Block: React.FC<BlockProps> = ({
index,
label,
title,
id,
children,
}) => (
<section
id={id}
className="relative scroll-mt-24 border-t border-slate-200 bg-white"
>
<div className="mx-auto w-full max-w-[1400px] px-5 py-20 md:px-12 md:py-36">
<Reveal>
<div className="mb-8 flex items-center gap-4 md:mb-12">
<span className="font-mono text-[11px] font-bold tracking-[0.2em] text-slate-900">
{index}
</span>
<span className="h-px w-10 bg-slate-300" />
<span className="font-mono text-[11px] uppercase tracking-[0.25em] text-slate-500">
{label}
</span>
<span
className="ml-auto hidden font-mono text-[10px] tracking-[0.3em] text-slate-300 md:block"
aria-hidden="true"
>
{parseInt(index, 10).toString(2).padStart(8, "0")}
</span>
</div>
</Reveal>
<Reveal delay={0.1}>
<h2 className="m-0 max-w-6xl text-4xl font-bold leading-[0.95] tracking-tighter text-slate-900 md:text-7xl lg:text-[6.5rem]">
{title}
</h2>
</Reveal>
<div className="mt-14 md:mt-24">{children}</div>
</div>
</section>
);