Files
mintel.me/apps/web/app/[locale]/page.tsx
T
mmintelandClaude Opus 5.5 3e0c9a1605 feat: a red thread through the homepage, in words and as a line
The rail and the "next, you are wondering" links are gone (owner: flat, and
the question stood twice in a row). The page now reads from top to bottom:

Every chapter opens with one sentence, and the sentences follow each other
as the way of a project: you need someone who builds it, first we find out
what you need, sometimes it is less work by hand, sometimes a meeting, then
it always runs the same way, in the end something runs, a few questions
remain, and it starts with an email.

One line runs down the whole page and draws itself as the visitor scrolls.
Under each sentence it crosses the page, stands a figure on the line
(diamond, hexagon, screen, circle, square, triangle) and continues down the
other side; a glowing dot travels at its head and comes to rest in front of
the last sentence.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 10:35:55 +02:00

307 lines
11 KiB
TypeScript

import type { Metadata } from "next";
import Link from "next/link";
import { BitAura } from "@/src/components/bits/BitAura";
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 {
PresentationCompare,
ReasonDrawing,
} from "@/src/components/illustrations/WebPresentation";
import { WorkShowcase } from "@/src/components/illustrations/WorkShowcase";
import { Reveal } from "@/src/components/Reveal";
import { StoryThread } from "@/src/components/story/StoryThread";
import { SwipeRow } from "@/src/components/SwipeRow";
import { contentFor } from "@/src/content";
import type { SplitTitle } from "@/src/content/types";
import { DEFAULT_LOCALE, parseLocale } from "@/src/domain/locale";
import { languageAlternates, pathFor } from "@/src/domain/routes";
type Props = { readonly params: Promise<{ readonly locale: string }> };
export async function generateMetadata({ params }: Props): Promise<Metadata> {
const locale = parseLocale((await params).locale) ?? DEFAULT_LOCALE;
const { site } = contentFor(locale);
return {
title: { absolute: site.title },
description: site.description,
alternates: {
canonical: pathFor("home", locale),
languages: languageAlternates("home"),
},
};
}
const MUTED = "text-slate-400";
const BODY = "m-0 font-sans text-lg leading-relaxed text-slate-500 md:text-xl";
const LIST_TITLE =
"m-0 font-sans text-xl font-bold tracking-tight text-slate-900 md:text-2xl";
const Title: React.FC<{ title: SplitTitle; muted?: string }> = ({
title,
muted = MUTED,
}) => (
<>
{title.lead} <span className={muted}>{title.rest}</span>
</>
);
export default async function LandingPage({ params }: Props) {
const locale = parseLocale((await params).locale) ?? DEFAULT_LOCALE;
const content = contentFor(locale);
const {
hello,
services,
automation,
presentations,
story,
freelance,
working,
work,
faq,
} = content;
return (
<div className="relative flex flex-col bg-white">
<HomeJsonLd locale={locale} />
<HeroStatement hero={content.hero} />
<StickyBook />
<PortraitSection
id="hello"
index="01"
label={hello.label}
title={hello.title}
portraitAlt={hello.portraitAlt}
story={story.hello}
>
<p className="m-0 font-sans text-2xl leading-snug text-white md:text-4xl">
{hello.lead}
</p>
{hello.paragraphs.map((paragraph, i) => (
<p
key={paragraph}
className={`m-0 font-sans text-lg leading-relaxed text-slate-300 md:text-xl ${i === 0 ? "mt-8" : "mt-6"}`}
>
{paragraph}
</p>
))}
</PortraitSection>
<section className="relative overflow-clip bg-slate-900">
<div className="mx-auto w-full max-w-[1400px] px-5 pb-20 pt-4 md:px-12 md:pb-36 md:pt-0">
<p className="m-0 mb-10 font-mono text-[11px] uppercase tracking-[0.2em] text-slate-400">
{hello.careerLabel}
</p>
<CareerLine stations={hello.career} />
</div>
</section>
<Block
index="02"
label={services.label}
chapter={{ line: story.services, figure: "diamond", flip: false }}
title={<Title title={services.title} />}
>
<Reveal>
<PathPicker />
</Reveal>
</Block>
<Block
index="03"
label={automation.label}
chapter={{ line: story.automation, figure: "hexagon", flip: true }}
title={<Title title={automation.title} />}
>
<Reveal>
<p className={`${BODY} mb-12 max-w-3xl`}>{automation.intro}</p>
</Reveal>
<Reveal>
<AutomationFlow />
</Reveal>
<SwipeRow className="mt-12 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">
<h3 className={LIST_TITLE}>{group.title}</h3>
<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>
))}
</SwipeRow>
</Block>
<Block
index="04"
label={presentations.label}
chapter={{ line: story.presentations, figure: "screen", flip: false }}
tone="dark"
title={<Title title={presentations.title} muted="text-slate-500" />}
>
<Reveal>
<p className="m-0 mb-12 max-w-3xl font-sans text-lg leading-relaxed text-slate-300 md:mb-16 md:text-xl">
{presentations.intro}
</p>
</Reveal>
<Reveal>
<BitAura source="around" tone="dark">
<div className="rounded-3xl border border-slate-700 bg-slate-950 px-4 py-8 md:px-10 md:py-12">
<PresentationCompare />
</div>
</BitAura>
</Reveal>
<SwipeRow className="mt-12 grid grid-cols-1 gap-x-10 gap-y-14 md:mt-28 md:grid-cols-2 lg:grid-cols-4">
{presentations.reasons.map((reason, i) => (
<Reveal key={reason.title} delay={0.08 * i}>
<div className="rounded-2xl border border-slate-800 bg-slate-950/60 p-4">
<ReasonDrawing index={i} />
</div>
<h3 className="m-0 mt-6 font-sans text-2xl font-bold tracking-tight text-white">
{reason.title}
</h3>
<p className="m-0 mt-3 font-sans text-base leading-relaxed text-slate-300 md:text-lg">
{reason.text}
</p>
</Reveal>
))}
</SwipeRow>
<div className="mt-16 grid grid-cols-1 gap-12 md:mt-28 md:grid-cols-12 md:gap-12">
<Reveal className="md:col-span-6">
<div className="border-t border-slate-700 pt-6">
<h3 className="m-0 font-sans text-xl font-bold tracking-tight text-white md:text-2xl">
{presentations.goodFor.title}
</h3>
<ul className="m-0 mt-4 list-none p-0">
{presentations.goodFor.items.map((item) => (
<li
key={item}
className="border-b border-slate-800 py-4 font-sans text-lg text-slate-100 md:text-xl"
>
{item}
</li>
))}
</ul>
</div>
</Reveal>
<Reveal delay={0.1} className="md:col-span-6">
<div className="rounded-3xl border border-slate-700 bg-slate-950/60 p-6 md:p-10">
<h3 className="m-0 font-sans text-2xl font-bold tracking-tight text-white md:text-3xl">
{presentations.honest.title}
</h3>
<p className="m-0 mt-4 font-sans text-base leading-relaxed text-slate-300 md:text-lg">
{presentations.honest.text}
</p>
<a
href="#contact"
className="bit-cta bit-cta-light mt-8 inline-flex items-center gap-3 rounded-full bg-white px-6 py-3 font-sans text-[11px] font-bold uppercase tracking-[0.15em] text-slate-900 no-underline transition-transform duration-300 hover:-translate-y-0.5"
>
{presentations.cta} <span aria-hidden="true">→</span>
</a>
</div>
</Reveal>
</div>
</Block>
<Block
index="05"
label={working.label}
chapter={{ line: story.working, figure: "circle", flip: true }}
title={<Title title={working.title} />}
>
<Reveal>
<BitAura source="bottom" tone="light" className="mb-16 md:mb-24">
<div className="overflow-hidden rounded-3xl bg-slate-900 p-4 md:p-10">
<ContactDiagram />
</div>
</BitAura>
</Reveal>
<SwipeRow className="grid grid-cols-1 gap-x-12 gap-y-12 md:grid-cols-2 md:gap-y-16">
{working.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>
))}
</SwipeRow>
<Reveal>
<Link
href={pathFor("freelance", locale)}
className="group mt-14 flex flex-col gap-5 rounded-3xl bg-slate-50 p-6 no-underline transition-colors duration-300 hover:bg-slate-100 md:mt-24 md:flex-row md:items-center md:justify-between md:p-10"
>
<span className="block max-w-2xl">
<span className="block font-sans text-2xl font-bold tracking-tight text-slate-900 md:text-3xl">
{freelance.teaser.title}
</span>
<span className="mt-3 block font-sans text-base leading-relaxed text-slate-500 md:text-lg">
{freelance.teaser.text}
</span>
</span>
<span className="inline-flex shrink-0 items-center gap-3 self-start rounded-full bg-slate-900 px-6 py-3 font-sans text-[11px] font-bold uppercase tracking-[0.15em] text-white md:self-auto">
{freelance.teaser.cta}
<span
aria-hidden="true"
className="transition-transform duration-300 group-hover:translate-x-1"
>
→
</span>
</span>
</Link>
</Reveal>
</Block>
<Block
index="06"
label={work.label}
chapter={{ line: story.work, figure: "square", flip: false }}
tone="dark"
title={<Title title={work.title} muted="text-slate-500" />}
>
<WorkShowcase />
<Reveal>
<p className="m-0 mt-20 max-w-3xl border-t border-slate-800 pt-8 font-sans text-lg leading-relaxed text-slate-300 md:mt-28 md:text-xl">
{work.note}
</p>
</Reveal>
</Block>
<Block
index="07"
label={faq.label}
chapter={{ line: story.faq, figure: "triangle", flip: true }}
title={<Title title={faq.title} />}
>
<Reveal>
<Faq items={faq.items} />
</Reveal>
</Block>
<ClosingCta contact={content.contact} story={story.contact} />
<StoryThread />
</div>
);
}