Files
einfach-produktiv/app/challenge/page.tsx
T
Marco 3da8b75395 Fix several mobile responsive issues across Hero, Newsletter, cart, and Challenge
- Hero CTA: back to a single line below lg: (wrapping put the icon
  beside two lines and looked broken), sized down instead so the full
  phrase fits.
- Hero heading: dropped the forced <br> after "darf" at true mobile
  widths, kept it only for the sm-lg tablet range it was added for.
- Hero social proof: avatars+text now wrap and center instead of
  cramming onto one row (flex-wrap + justify-center, no fixed
  breakpoint needed).
- Newsletter card icon: stacked above the copy and centered below md:
  instead of squeezed into a row beside it.
- TrustRow: left-aligned below md: instead of centered (shop, cart, and
  every other page using this component).
- Cart line item image: full width below sm: instead of a fixed 150px
  square.
- Challenge EmailCapture (top + bottom CTA, same shared component):
  input and button stack full-width below sm: instead of squeezing
  into one row.
- Challenge steps: icon above text and centered at every breakpoint
  (previously only from lg: up), connector arrows centered to match.
2026-07-24 18:48:18 +00:00

372 lines
16 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 Image from "next/image";
import { draftMode } from "next/headers";
import { Footer } from "../components/Footer";
import { Reveal, RevealGroup, RevealItem } from "../components/Reveal";
import { TestimonialsGrid } from "../components/TestimonialsGrid";
import { LiveTestimonialsGrid } from "../components/LiveTestimonialsGrid";
import { getTestimonials } from "../lib/payload";
import { EmailCapture } from "./components/EmailCapture";
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>
);
}
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." },
];
export default async function ChallengePage() {
const { isEnabled: isPreview } = await draftMode();
const testimonials = await getTestimonials("challenge", { draft: isPreview });
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<span className="text-brand">.</span>
</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}>
<Image
alt="7-Tage-Challenge"
src="/blog-featured.jpg"
fill
sizes="48vw"
className="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>
{/* items-center below lg: (was items-start) — the step blocks
are centered columns now (see RevealItem below), so the
connector arrows between them need to be centered too,
not flush against the left edge. */}
<RevealGroup className="flex flex-col lg:flex-row items-center lg:items-start gap-8 lg:gap-2 w-full">
{steps.flatMap((step, i) => [
// Icon-above-text, centered, at every breakpoint now
// (previously a left-aligned icon+text row below lg: —
// fixed 2026-07-24 to match the lg: layout instead of
// diverging from it).
<RevealItem key={step.title} className="group flex flex-col 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 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 ? (
// Same /icon-arrow-connector.svg asset and rotate-on-stack
// pattern as todo-cards/newsletter's HowItWorks — this
// used to be its own hand-drawn SVG arrow, inconsistent
// with those two. Always visible (rotated 90° while
// stacked below lg, this page's own structural
// breakpoint) rather than hidden below lg like before.
<div key={`arrow-${i}`} className="flex items-center justify-center shrink-0 lg:mt-5">
<Image
alt=""
src="/icon-arrow-connector.svg"
width={24}
height={24}
className="w-6 h-6 rotate-90 lg:w-10 lg:h-3 lg:rotate-0"
/>
</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 relative w-full lg:w-[44%] lg:shrink-0 rounded-xl overflow-hidden"
style={{ minHeight: "18rem" }}
>
<Image
alt="Notizbuch"
src="/challenge-content.jpg"
fill
sizes="(min-width: 1024px) 44vw, 100vw"
className="object-cover transition-transform duration-500 group-hover:scale-105"
/>
</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 ── */}
{isPreview ? (
<LiveTestimonialsGrid testimonials={testimonials} />
) : (
<TestimonialsGrid testimonials={testimonials} />
)}
{/* ── 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 />
</>
);
}