9e5532be63
- New /shop overview, /cart (real cart state via useSyncExternalStore), /versand (shipping policy page with TOC) and /newsletter detail page - Cart: quantity/removal, order summary, related-products cross-sell with randomized picks, VersandModal quick-reference instead of navigating away, MwSt. disclosure next to unit prices - Add-to-cart UX: inline success feedback (green state) plus a fly-to-navbar-cart-icon animation (CartFlyProvider) with a delayed badge count-up; AddToCartButton no longer navigates straight to /cart - Shared lib/products.ts catalog and lib/shipping.ts constants (cost, free-shipping threshold, handling/transit days) so cart, trust badges and the versand page can never drift apart - Fix Navbar smooth-scroll easing (ease-out instead of ease-in-out, no more perceived start delay); compress newsletter modal photo 2.4MB -> 85KB to fix first-open jank
447 lines
19 KiB
TypeScript
447 lines
19 KiB
TypeScript
import type { Metadata } from "next";
|
||
import Link from "next/link";
|
||
import { Footer } from "../components/Footer";
|
||
import { Reveal, RevealGroup, RevealItem } from "../components/Reveal";
|
||
|
||
const title = "7-Tage-Challenge – Mehr Klarheit in 7 Tagen";
|
||
const description =
|
||
"Die 7-Tage-Challenge für deinen klaren Kopf und mehr Struktur im Alltag: 7 kurze, praktische Impulse direkt ins Postfach – für mehr Fokus, Ruhe und Klarheit.";
|
||
|
||
export const metadata: Metadata = {
|
||
title,
|
||
description,
|
||
alternates: {
|
||
canonical: "/challenge",
|
||
},
|
||
openGraph: {
|
||
title,
|
||
description,
|
||
url: "/challenge",
|
||
images: ["/blog-featured.jpg"],
|
||
},
|
||
twitter: {
|
||
title,
|
||
description,
|
||
images: ["/blog-featured.jpg"],
|
||
},
|
||
};
|
||
|
||
function IconEnvelope() {
|
||
return (
|
||
<svg width="52" height="44" viewBox="0 0 52 44" fill="none">
|
||
<rect x="2" y="2" width="48" height="40" rx="3" stroke="#f6a701" strokeWidth="2" />
|
||
<path d="M2 2l24 22L50 2" stroke="#f6a701" strokeWidth="2" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function IconMailLines() {
|
||
return (
|
||
<svg width="56" height="44" viewBox="0 0 56 44" fill="none">
|
||
<line x1="2" y1="12" x2="12" y2="12" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<line x1="2" y1="20" x2="9" y2="20" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<line x1="2" y1="28" x2="7" y2="28" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<rect x="13" y="2" width="41" height="40" rx="3" stroke="#f6a701" strokeWidth="2" />
|
||
<path d="M13 2l20.5 19L54 2" stroke="#f6a701" strokeWidth="2" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function IconNotepad() {
|
||
return (
|
||
<svg width="46" height="52" viewBox="0 0 46 52" fill="none">
|
||
<rect x="2" y="4" width="32" height="42" rx="2" stroke="#f6a701" strokeWidth="2" />
|
||
<line x1="9" y1="16" x2="27" y2="16" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<line x1="9" y1="23" x2="24" y2="23" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<line x1="9" y1="30" x2="20" y2="30" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
<circle cx="37" cy="40" r="7" stroke="#f6a701" strokeWidth="2" />
|
||
<line x1="37" y1="27" x2="37" y2="33" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function IconCheckCircle() {
|
||
return (
|
||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none">
|
||
<circle cx="24" cy="24" r="21" stroke="#f6a701" strokeWidth="2" />
|
||
<path d="M14 24l7.5 7.5L34 16" stroke="#f6a701" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function Check() {
|
||
return (
|
||
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" className="shrink-0 mt-0.5">
|
||
<path d="M3 9.5l4 4L15 4" stroke="#f6a701" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function LockIcon() {
|
||
return (
|
||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" className="shrink-0">
|
||
<rect x="2" y="6" width="10" height="7" rx="1.5" stroke="#888" strokeWidth="1.3" />
|
||
<path d="M4.5 6V4.5a2.5 2.5 0 0 1 5 0V6" stroke="#888" strokeWidth="1.3" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
function ArrowRight() {
|
||
return (
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" className="text-[#ccc] shrink-0">
|
||
<path d="M5 12h14M13 6l6 6-6 6" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
|
||
</svg>
|
||
);
|
||
}
|
||
|
||
const steps = [
|
||
{
|
||
icon: <IconEnvelope />,
|
||
title: "1. Anmelden",
|
||
desc: "Trage dich mit deiner E-Mail-Adresse ein und starte sofort.",
|
||
},
|
||
{
|
||
icon: <IconMailLines />,
|
||
title: "2. E-Mail erhalten",
|
||
desc: "Du bekommst 7 Tage lang jeweils einen Impuls direkt in dein Postfach.",
|
||
},
|
||
{
|
||
icon: <IconNotepad />,
|
||
title: "3. Umsetzen",
|
||
desc: "Setze die einfache Aufgabe in wenigen Minuten um – für mehr Klarheit und Fokus.",
|
||
},
|
||
{
|
||
icon: <IconCheckCircle />,
|
||
title: "4. Dranbleiben",
|
||
desc: "Kleine Schritte führen zu großen Veränderungen – Tag für Tag.",
|
||
},
|
||
];
|
||
|
||
const benefits = [
|
||
{ title: "Klarheit gewinnen", desc: "Sortiere deine Gedanken und erkenne, was wirklich zählt." },
|
||
{ title: "Fokus stärken", desc: "Richte deine Aufmerksamkeit auf das Wesentliche." },
|
||
{ title: "Struktur schaffen", desc: "Einfache Methoden für mehr Ordnung im Alltag." },
|
||
{ title: "Gewohnheiten aufbauen", desc: "Kleine Schritte, die langfristig Wirkung zeigen." },
|
||
{ title: "Gelassener leben", desc: "Weniger Stress, mehr Zeit für die Dinge, die dir wichtig sind." },
|
||
];
|
||
|
||
const testimonials = [
|
||
{
|
||
avatar: "/avatar-1.jpg",
|
||
quote: "„Die 7-Tage-Challenge hat mir geholfen, wieder klar zu sehen und mit kleinen Schritten wirklich etwas zu verändern.“",
|
||
name: "Sarah M.",
|
||
role: "Marketing Managerin",
|
||
},
|
||
{
|
||
avatar: "/avatar-2.jpg",
|
||
quote: "„Kurz, konkret und unglaublich wirkungsvoll. Ich habe direkt mehr Fokus und weniger Druck im Kopf.“",
|
||
name: "Thomas K.",
|
||
role: "Selbständiger Berater",
|
||
},
|
||
{
|
||
avatar: "/avatar-3.jpg",
|
||
quote: "„Endlich eine Challenge, die nicht überfordert, sondern genau die richtigen Impulse gibt – jeden Tag.“",
|
||
name: "Miriam L.",
|
||
role: "Projektleiterin",
|
||
},
|
||
];
|
||
|
||
function EmailCapture({ buttonLabel = "Challenge starten" }: { buttonLabel?: string }) {
|
||
return (
|
||
<div className="flex flex-col gap-2 w-full">
|
||
<div className="flex gap-3 w-full">
|
||
<input
|
||
type="email"
|
||
placeholder="Deine E-Mail-Adresse"
|
||
className="flex-1 min-w-0 bg-white border border-[#d9d9d9] rounded-lg px-4 py-3 text-[1rem] text-[#868686] outline-none focus:border-[#f6a701] transition-colors"
|
||
/>
|
||
<button
|
||
type="submit"
|
||
className="shrink-0 bg-[#f6a701] rounded-lg px-5 py-3 font-bold text-[1rem] text-[#222221] whitespace-nowrap hover:brightness-95 active:scale-[0.97] transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[#f6a701] focus-visible:ring-offset-2"
|
||
>
|
||
{buttonLabel}
|
||
</button>
|
||
</div>
|
||
<p className="flex items-center gap-1.5 text-[0.8rem] text-[#888]">
|
||
<LockIcon />
|
||
Keine Werbung. Jederzeit abbestellbar.
|
||
</p>
|
||
</div>
|
||
);
|
||
}
|
||
|
||
export default function ChallengePage() {
|
||
return (
|
||
<>
|
||
<main className="flex flex-col flex-1 bg-[#f5f0e8]">
|
||
|
||
{/* ── Hero ── */}
|
||
<section className="w-full flex items-stretch overflow-hidden" style={{ minHeight: "32rem" }}>
|
||
|
||
{/* Left: text + form. justify-center removed from here — the
|
||
outer section's items-stretch already makes this column full
|
||
height, but centering its whole content (breadcrumb included)
|
||
made the breadcrumb's Y position drift depending on this
|
||
page's content length vs. /todo-cards's and /newsletter's own
|
||
(different) content lengths. Same fix as both of those. */}
|
||
{/* pt-10 md:pt-12 (not the original py-10 lg:py-14's top value) —
|
||
matches /todo-cards's and /newsletter's breadcrumb top offset
|
||
exactly; pb-10 lg:pb-14 keeps this page's original bottom
|
||
spacing, which was never the inconsistent part. */}
|
||
<Reveal className="flex flex-col gap-6 px-8 lg:px-[5rem] pt-10 md:pt-12 pb-10 lg:pb-14 w-full lg:w-[52%] lg:shrink-0">
|
||
|
||
{/* Breadcrumb — 3 levels (Startseite › Werkzeuge ›
|
||
7-Tage-Challenge), matching /todo-cards's and
|
||
/newsletter's pattern; was missing "Werkzeuge" in the
|
||
middle. Also switched from this page's hardcoded hex
|
||
colors to the shared text-body-sm/text-text-muted/
|
||
text-text-primary tokens the other two breadcrumbs use —
|
||
text-body-sm is 0.875rem, identical to the old literal
|
||
value, so this is a pure consistency fix, not a visual
|
||
change. Deliberately NOT part of the centered block below. */}
|
||
<p className="text-body-sm text-text-muted flex items-center gap-2">
|
||
<Link href="/" className="hover:text-brand transition-colors">Startseite</Link>
|
||
<span>›</span>
|
||
<Link href="/#werkzeuge" className="hover:text-brand transition-colors">Werkzeuge</Link>
|
||
<span>›</span>
|
||
<span className="text-text-primary">7-Tage-Challenge</span>
|
||
</p>
|
||
|
||
{/* Everything else — centered in the remaining vertical space,
|
||
same fix as /todo-cards's and /newsletter's Hero sections. */}
|
||
<div className="flex flex-col gap-6 flex-1 justify-center">
|
||
{/* Headline */}
|
||
<h1
|
||
className="font-semibold text-[#222221] leading-[1.15]"
|
||
style={{ fontFamily: "var(--font-lora)", fontSize: "clamp(2.25rem, 5vw, 3.75rem)" }}
|
||
>
|
||
7 Tage.<br />
|
||
Mehr Klarheit.<br />
|
||
Weniger Stress.
|
||
</h1>
|
||
|
||
{/* Subtitle */}
|
||
<p className="text-[1rem] text-[#444] leading-[1.6] max-w-[30rem]">
|
||
Die 7-Tage-Challenge für deinen klaren Kopf und mehr Struktur im Alltag.
|
||
</p>
|
||
|
||
{/* Checklist */}
|
||
<ul className="flex flex-col gap-2">
|
||
{[
|
||
"7 kurze Impulse – direkt in dein Postfach",
|
||
"Praktisch, umsetzbar und alltagstauglich",
|
||
"Für mehr Fokus, Ruhe und Klarheit",
|
||
].map((item) => (
|
||
<li key={item} className="flex items-start gap-2 text-[#222221] text-[0.95rem]">
|
||
<Check />
|
||
{item}
|
||
</li>
|
||
))}
|
||
</ul>
|
||
|
||
{/* Email form */}
|
||
<EmailCapture />
|
||
</div>
|
||
</Reveal>
|
||
|
||
{/* Right: hero image with badge — group + scale-105 on hover,
|
||
same restrained "photo feels alive" treatment as the Home/
|
||
todo-cards hero images; delay={0.15} mirrors their text→image
|
||
stagger. */}
|
||
<Reveal className="hidden lg:block group flex-1 relative overflow-hidden" delay={0.15}>
|
||
<img
|
||
alt="7-Tage-Challenge"
|
||
src="/blog-featured.jpg"
|
||
className="absolute inset-0 w-full h-full object-cover object-center pointer-events-none transition-transform duration-500 group-hover:scale-105"
|
||
/>
|
||
{/* Badge */}
|
||
<div className="absolute top-8 right-8 w-[7.5rem] h-[7.5rem] rounded-full bg-white shadow-md flex flex-col items-center justify-center text-center p-3 gap-1">
|
||
<p className="text-[0.65rem] font-semibold text-[#222221] leading-tight">
|
||
Jeden Tag ein neuer Impuls in deinem Postfach.
|
||
</p>
|
||
<svg width="20" height="16" viewBox="0 0 20 16" fill="none">
|
||
<rect x="1" y="1" width="18" height="14" rx="2" stroke="#f6a701" strokeWidth="1.5" />
|
||
<path d="M1 1l9 8 9-8" stroke="#f6a701" strokeWidth="1.5" />
|
||
</svg>
|
||
</div>
|
||
</Reveal>
|
||
|
||
</section>
|
||
|
||
{/* ── So funktioniert die Challenge ── */}
|
||
<section className="bg-white w-full py-14 lg:py-20">
|
||
<div className="px-8 lg:px-[5rem] max-w-[1280px] mx-auto flex flex-col items-center gap-12">
|
||
|
||
<Reveal className="flex flex-col items-center gap-2 text-center">
|
||
<h2
|
||
className="font-semibold text-[#222221]"
|
||
style={{ fontFamily: "var(--font-lora)", fontSize: "clamp(1.5rem, 3vw, 2rem)" }}
|
||
>
|
||
So funktioniert die Challenge
|
||
</h2>
|
||
<p className="text-[1rem] text-[#666]">Jeden Tag ein Impuls. In nur wenigen Minuten.</p>
|
||
</Reveal>
|
||
|
||
<RevealGroup className="flex flex-col lg:flex-row items-start lg:items-start gap-8 lg:gap-2 w-full">
|
||
{steps.flatMap((step, i) => [
|
||
<RevealItem key={step.title} className="group flex lg:flex-col items-start lg:items-center gap-4 lg:gap-5 flex-1 min-w-0">
|
||
<div className="flex items-center justify-center w-16 h-14 shrink-0 transition-transform duration-300 group-hover:scale-110">
|
||
{step.icon}
|
||
</div>
|
||
<div className="flex flex-col gap-1 lg:text-center">
|
||
<p className="font-semibold text-[#222221] text-[1rem]">{step.title}</p>
|
||
<p className="text-[0.875rem] text-[#666] leading-[1.5]">{step.desc}</p>
|
||
</div>
|
||
</RevealItem>,
|
||
i < steps.length - 1 ? (
|
||
<div key={`arrow-${i}`} className="hidden lg:flex items-center shrink-0 mt-5">
|
||
<ArrowRight />
|
||
</div>
|
||
) : null,
|
||
])}
|
||
</RevealGroup>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── Das erwartet dich ── */}
|
||
<section className="w-full py-14 lg:py-20">
|
||
<div className="px-8 lg:px-[5rem] max-w-[1280px] mx-auto flex flex-col gap-10">
|
||
|
||
<Reveal className="flex flex-col items-center gap-2 text-center">
|
||
<h2
|
||
className="font-semibold text-[#222221]"
|
||
style={{ fontFamily: "var(--font-lora)", fontSize: "clamp(1.5rem, 3vw, 2rem)" }}
|
||
>
|
||
Das erwartet dich
|
||
</h2>
|
||
<p className="text-[1rem] text-[#666]">7 Tage. 7 Impulse. Für mehr Klarheit in deinem Alltag.</p>
|
||
</Reveal>
|
||
|
||
<div className="flex flex-col lg:flex-row gap-10 lg:gap-16 items-center">
|
||
|
||
{/* Image */}
|
||
<Reveal
|
||
className="group w-full lg:w-[44%] lg:shrink-0 rounded-xl overflow-hidden"
|
||
style={{ minHeight: "18rem" }}
|
||
>
|
||
<img
|
||
alt="Notizbuch"
|
||
src="/challenge-content.jpg"
|
||
className="w-full h-full object-cover transition-transform duration-500 group-hover:scale-105"
|
||
style={{ minHeight: "18rem" }}
|
||
/>
|
||
</Reveal>
|
||
|
||
{/* Checklist */}
|
||
<Reveal delay={0.1} className="flex-1 w-full">
|
||
<ul className="flex flex-col gap-5">
|
||
{benefits.map((b) => (
|
||
<li key={b.title} className="flex items-start gap-3">
|
||
<Check />
|
||
<div className="flex flex-col gap-0.5">
|
||
<p className="font-semibold text-[#222221] text-[1rem]">{b.title}</p>
|
||
<p className="text-[0.9rem] text-[#555] leading-[1.5]">{b.desc}</p>
|
||
</div>
|
||
</li>
|
||
))}
|
||
</ul>
|
||
</Reveal>
|
||
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── Was andere sagen ── */}
|
||
{/* max-w-[1600px], not this page's other sections' 1280px — a
|
||
deliberate compromise with /todo-cards's and /newsletter's
|
||
unbounded fluid width, so all three testimonial sections cap
|
||
at the same width instead of Challenge's reading narrower on
|
||
wide viewports. Only this one section's cap changed, not the
|
||
rest of the page. */}
|
||
<section className="bg-white w-full py-14 lg:py-20">
|
||
<div className="px-8 lg:px-[5rem] max-w-[1600px] mx-auto flex flex-col gap-10">
|
||
|
||
<Reveal
|
||
className="font-semibold text-[#222221] text-center"
|
||
style={{ fontFamily: "var(--font-lora)", fontSize: "clamp(1.5rem, 3vw, 2rem)" }}
|
||
>
|
||
Was andere sagen
|
||
</Reveal>
|
||
|
||
{/* Same style + micro-interactions as /todo-cards's and
|
||
/newsletter's identically-styled testimonial cards (kept in
|
||
sync deliberately): decorative quote-mark, hover-lift on
|
||
the card, avatar scale on the same hover via `group`. */}
|
||
<RevealGroup className="flex flex-col lg:flex-row gap-6">
|
||
{testimonials.map((t) => (
|
||
<RevealItem
|
||
key={t.name}
|
||
className="group relative flex-1 bg-[#f5f0e8] rounded-xl p-6 flex flex-col gap-4 transition-transform duration-300 hover:-translate-y-1"
|
||
>
|
||
<span
|
||
aria-hidden
|
||
className="absolute top-4 right-6 font-bold text-[2.5rem] text-[#ccc] leading-none select-none"
|
||
>
|
||
”
|
||
</span>
|
||
<p className="text-[#222221] text-[0.95rem] leading-[1.6] flex-1 pr-8">{t.quote}</p>
|
||
<div className="flex items-center gap-3">
|
||
<img
|
||
alt={t.name}
|
||
src={t.avatar}
|
||
className="w-10 h-10 rounded-full object-cover shrink-0 transition-transform duration-300 group-hover:scale-110"
|
||
/>
|
||
<div>
|
||
<p className="font-semibold text-[#222221] text-[0.9rem]">{t.name}</p>
|
||
<p className="text-[#777] text-[0.8rem]">{t.role}</p>
|
||
</div>
|
||
</div>
|
||
</RevealItem>
|
||
))}
|
||
</RevealGroup>
|
||
|
||
</div>
|
||
</section>
|
||
|
||
{/* ── Bottom CTA ── */}
|
||
<section className="w-full py-14 lg:py-16">
|
||
<div className="px-8 lg:px-[5rem] max-w-[1280px] mx-auto">
|
||
<Reveal className="bg-[#f8f3ec] rounded-xl flex flex-col lg:flex-row gap-8 lg:gap-[3.5rem] items-start lg:items-center px-6 lg:px-10 py-8">
|
||
|
||
{/* Left: icon + copy */}
|
||
<div className="flex gap-5 items-start flex-1 min-w-0">
|
||
<div className="shrink-0 -rotate-4">
|
||
<svg width="52" height="44" viewBox="0 0 52 44" fill="none">
|
||
<rect x="2" y="2" width="48" height="40" rx="3" stroke="#f6a701" strokeWidth="2" />
|
||
<path d="M2 2l24 22L50 2" stroke="#f6a701" strokeWidth="2" />
|
||
</svg>
|
||
</div>
|
||
<div className="flex flex-col gap-2">
|
||
<p
|
||
className="font-semibold text-[#222221] leading-normal"
|
||
style={{ fontFamily: "var(--font-lora)", fontSize: "clamp(1.125rem, 2vw, 1.375rem)" }}
|
||
>
|
||
Starte jetzt deine 7-Tage-Challenge
|
||
</p>
|
||
<p className="text-[0.9rem] text-[#555] leading-[1.5]">
|
||
Trage dich ein und erhalte ab sofort täglich einen Impuls für mehr Klarheit und Fokus.
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Right: form */}
|
||
<div className="w-full lg:w-[30rem] lg:shrink-0">
|
||
<EmailCapture />
|
||
</div>
|
||
|
||
</Reveal>
|
||
</div>
|
||
</section>
|
||
|
||
</main>
|
||
<Footer />
|
||
</>
|
||
);
|
||
}
|