Files
einfach-produktiv/app/not-found.tsx
T
Marco 0664e11d3b Add site-wide 404 page
app/not-found.tsx — root not-found handles all unmatched routes
automatically (Next 16, no global-not-found flag needed for a single
root layout). Hero (Ups. / subheading / CTA) with a natively-drawn
sticky-note illustration (Lora "404" + Caveat handwritten line, no
image asset needed), 3 help links (Startseite/Blog/Werkzeuge), a
decorative search bar (no search backend exists yet to wire it to),
photo+quote testimonial band, TrustRow, Footer. Sets robots: noindex.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 20:16:22 +00:00

215 lines
9.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import Link from "next/link";
import { Reveal, RevealGroup, RevealItem } from "./components/Reveal";
import { Footer } from "./components/Footer";
import { TrustRow } from "./components/TrustRow";
export const metadata: Metadata = {
title: "Seite nicht gefunden",
description: "Diese Seite ist gerade nicht dort, wo sie sein sollte.",
robots: { index: false, follow: true },
};
const helpLinks = [
{
href: "/",
title: "Startseite",
desc: "Zurück zu den wichtigsten Inhalten und Angeboten.",
cta: "Zur Startseite",
icon: (
<svg viewBox="0 0 24 24" className="size-6" fill="none" aria-hidden="true">
<path
d="M4 11.5 12 4l8 7.5M6 9.5V20h5v-5.5h2V20h5V9.5"
stroke="currentColor"
strokeWidth="1.6"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
),
},
{
href: "/blog",
title: "Im Blog stöbern",
desc: "Lass dich von Impulsen, Methoden und Gedanken inspirieren.",
cta: "Zum Blog",
icon: (
<svg viewBox="0 0 24 24" className="size-6" fill="none" aria-hidden="true">
<path
d="M6 3.5h9.5L20 8v12.5a1 1 0 0 1-1 1H6a1 1 0 0 1-1-1v-16a1 1 0 0 1 1-1Z"
stroke="currentColor"
strokeWidth="1.6"
strokeLinejoin="round"
/>
<path d="M8.5 9.5h7M8.5 13h7M8.5 16.5h4.5" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
</svg>
),
},
{
href: "/#werkzeuge",
title: "Werkzeuge entdecken",
desc: "Praktische Tools für mehr Fokus, Struktur und Klarheit.",
cta: "Zu den Werkzeugen",
icon: (
<svg viewBox="0 0 24 24" className="size-6" fill="none" aria-hidden="true">
<path
d="M4 9.5h16a1 1 0 0 1 1 1V19a1 1 0 0 1-1 1H4a1 1 0 0 1-1-1v-8.5a1 1 0 0 1 1-1Z"
stroke="currentColor"
strokeWidth="1.6"
strokeLinejoin="round"
/>
<path
d="M8.5 9.5V7a2 2 0 0 1 2-2h3a2 2 0 0 1 2 2v2.5"
stroke="currentColor"
strokeWidth="1.6"
strokeLinejoin="round"
/>
</svg>
),
},
];
export default function NotFound() {
return (
<>
<main className="flex flex-col flex-1 bg-bg-base">
{/* Hero — eyebrow / Ups. / subheading / copy / CTA, sticky note beside it */}
<div className="flex flex-col lg:flex-row gap-12 lg:gap-16 items-center pt-16 pb-20 px-[var(--layout-padding-x)] w-full">
<Reveal className="flex flex-col gap-4 items-start w-full lg:flex-1 max-w-[32rem]">
<p className="font-semibold text-body-sm text-brand tracking-wide">404 Seite nicht gefunden</p>
<p
className="font-semibold text-[3.5rem] leading-[1.05] text-text-primary"
style={{ fontFamily: "var(--font-lora)" }}
>
Ups.
</p>
<p
className="font-semibold text-h-feature text-text-primary"
style={{ fontFamily: "var(--font-lora)" }}
>
Diese Seite ist gerade nicht dort, wo sie sein sollte.
</p>
<div className="h-[0.125rem] w-8 bg-brand" />
<p className="text-body text-text-muted">
Vielleicht wurde sie verschoben, umbenannt oder es hat sich ein kleiner Tippfehler eingeschlichen.
</p>
<p className="text-body text-text-muted">Aber kein Problem wir bringen dich gerne zurück auf Kurs.</p>
<Link
href="/"
className="flex items-center gap-2 mt-2 px-7 py-4 rounded-sm bg-brand text-h4 font-bold text-text-primary hover:bg-brand-hover active:scale-[0.97] transition-all"
>
Zur Startseite
<svg viewBox="0 0 20 20" className="size-4" fill="none" aria-hidden="true">
<path d="M4 10h12m0 0-5-5m5 5-5 5" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Link>
</Reveal>
{/* Sticky note — drawn natively (rotated card + tape + Caveat
handwriting) rather than a cropped photo asset, since there's
no such image in /public and this reads just as well as a
coded illustration built from the same tokens (Lora for the
"404", Caveat for the handwritten line) the rest of the site
already uses for handwritten-style accents (see About.tsx's
signature quote). */}
<Reveal delay={0.15} className="w-full lg:flex-1 flex items-center justify-center">
<div className="relative w-full max-w-[22rem] aspect-square rotate-2 rounded-sm bg-[#EDE3D0] shadow-modal p-8 flex flex-col items-center text-center">
<div className="absolute -top-3 left-1/2 -translate-x-1/2 -rotate-3 w-16 h-6 bg-[#d8cdb8]/80" />
<p
className="font-bold text-text-primary text-[4.5rem] leading-none mt-6"
style={{ fontFamily: "var(--font-lora)" }}
>
404
</p>
<div className="h-[0.1875rem] w-24 bg-brand mt-3 mb-4" />
<p
className="text-text-primary text-[1.75rem] leading-[1.15]"
style={{ fontFamily: "var(--font-caveat)" }}
>
Nicht hier.
<br />
Aber dein Ziel ist
<br />
sicher nicht weit.
</p>
<svg viewBox="0 0 32 32" className="size-8 mt-4 text-text-primary" fill="none" aria-hidden="true">
<circle cx="11" cy="13" r="1.6" fill="currentColor" />
<circle cx="21" cy="13" r="1.6" fill="currentColor" />
<path d="M9 19c1.8 2.4 4.4 3.6 7 3.6s5.2-1.2 7-3.6" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
</svg>
</div>
</Reveal>
</div>
{/* Help links + search */}
<div className="flex flex-col gap-10 items-center pb-16 px-[var(--layout-padding-x)] w-full">
<Reveal className="flex flex-col items-center gap-2">
<p
className="font-semibold text-h-small text-text-primary"
style={{ fontFamily: "var(--font-lora)" }}
>
Vielleicht hilft dir das weiter:
</p>
<div className="h-[0.125rem] w-8 bg-brand" />
</Reveal>
<RevealGroup className="grid grid-cols-1 md:grid-cols-3 gap-10 md:gap-8 w-full max-w-[64rem] divide-y md:divide-y-0 md:divide-x divide-border">
{helpLinks.map((link) => (
<RevealItem key={link.href} className="flex flex-col items-center text-center gap-3 pt-10 md:pt-0 first:pt-0 px-4">
<div className="flex size-14 items-center justify-center rounded-full bg-bg-muted text-brand">
{link.icon}
</div>
<p className="font-semibold text-body text-text-primary">{link.title}</p>
<p className="text-body-sm text-text-muted">{link.desc}</p>
<Link href={link.href} className="flex items-center gap-1.5 text-body-sm font-semibold text-brand hover:underline">
{link.cta}
<svg viewBox="0 0 20 20" className="size-3.5" fill="none" aria-hidden="true">
<path d="M4 10h12m0 0-5-5m5 5-5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Link>
</RevealItem>
))}
</RevealGroup>
<Reveal className="w-full max-w-[28rem]">
<div className="flex items-center gap-3 bg-bg-white border border-border rounded-sm px-6 py-3">
<svg viewBox="0 0 20 20" className="size-4 shrink-0 text-text-muted" fill="none" aria-hidden="true">
<circle cx="9" cy="9" r="6.5" stroke="currentColor" strokeWidth="1.6" />
<path d="m18 18-4-4" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" />
</svg>
<span className="text-body text-text-muted">Suche nach Impulsen, Tools, Themen </span>
</div>
</Reveal>
</div>
{/* Testimonial band — photo bleeds to the viewport edge, gradient
fade into bg-base on its trailing edge instead of a hard cut,
same idea as page-bestellbestaetigung's photo+quote pattern. */}
<Reveal className="relative w-full flex items-stretch min-h-[16rem] bg-bg-muted overflow-hidden">
<div className="relative w-full md:w-[45%] shrink-0">
<img
alt=""
src="/newsletter-modal-photo.jpg"
className="absolute inset-0 w-full h-full object-cover"
/>
<div className="hidden md:block absolute inset-y-0 right-0 w-40 bg-gradient-to-l from-bg-muted to-transparent" />
</div>
<div className="flex-1 flex flex-col justify-center gap-3 px-8 md:px-16 py-12">
<p
className="font-semibold text-h-section text-text-primary leading-[1.3] max-w-[28rem]"
style={{ fontFamily: "var(--font-lora)" }}
>
Manchmal führt der falsche Weg zu neuen klaren Gedanken.
</p>
<div className="h-[0.125rem] w-8 bg-brand" />
<p className="text-body text-text-muted">Björn</p>
</div>
</Reveal>
<TrustRow />
</main>
<Footer />
</>
);
}