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
+11 -54
View File
@@ -1,57 +1,14 @@
import { MetadataRoute } from "next";
import { getAllPosts } from "@/src/lib/posts";
import { technologies } from "./technologies/[slug]/data";
import type { MetadataRoute } from "next";
/**
* Sitemap Generator
*
* Standard Next.js 15 App Router sitemap generation.
* This file dynamically generates /sitemap.xml
*/
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const allPosts = await getAllPosts();
/** The site is a single page. */
export default function sitemap(): MetadataRoute.Sitemap {
const baseUrl = process.env.NEXT_PUBLIC_BASE_URL || "https://mintel.me";
// 1. Core Pages
const routes = [
"",
"/about",
"/blog",
"/case-studies",
"/case-studies/klz-cables",
"/contact",
"/websites",
].map((route) => ({
url: `${baseUrl}${route}`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: route === "" ? 1.0 : 0.8,
}));
// 2. Dynamic Blog Posts
const blogRoutes = allPosts.map((post) => ({
url: `${baseUrl}/blog/${post.slug}`,
lastModified: new Date(post.date),
changeFrequency: "monthly" as const,
priority: 0.7,
}));
// 3. Technology Detail Pages
const techRoutes = Object.keys(technologies).map((slug) => ({
url: `${baseUrl}/technologies/${slug}`,
lastModified: new Date(),
changeFrequency: "monthly" as const,
priority: 0.6,
}));
// 4. Tag Pages
const allTags = [...new Set(allPosts.flatMap((post) => post.tags))];
const tagRoutes = allTags.map((tag) => ({
url: `${baseUrl}/tags/${tag}`,
lastModified: new Date(),
changeFrequency: "weekly" as const,
priority: 0.3,
}));
return [...routes, ...blogRoutes, ...techRoutes, ...tagRoutes];
return [
{
url: baseUrl,
lastModified: new Date(),
changeFrequency: "monthly",
priority: 1,
},
];
}