From e4f0c66d7b9808d995655ff747cc3e578be02b57 Mon Sep 17 00:00:00 2001 From: Marco Date: Sun, 19 Jul 2026 11:34:52 +0000 Subject: [PATCH] Add ToDo-Karten product page, fluid design tokens, and Navbar/Hero fixes New /todo-cards page (Hero, How-it-works, Focus, Testimonials, Pricing) built with the fluid clamp() token system (app/lib/fluid.ts) and scroll-reveal animations (app/components/Reveal.tsx), matching styling consistency with /challenge's testimonial section. Navbar: page-aware active state and anchor-link navigation from any route (not just "/"), fixed logo click to actually navigate instead of silently rewriting the URL, fluid nav text size, custom hash-scroll handling for cross-page anchor links. Hero: responsive breakpoint fix for the text/image split at Tablet widths, entrance animation for the brand's orange dot, removed a red dot artifact from hero.png. Also includes prior uncommitted work on About/Blog/Newsletter/Footer and the cart lib (app/lib/cart.ts). Co-Authored-By: Claude Sonnet 5 --- app/challenge/page.tsx | 118 ++++-- app/components/About.tsx | 82 ++-- app/components/Blog.tsx | 87 ++-- app/components/Divider.tsx | 111 ++--- app/components/Footer.tsx | 24 +- app/components/Hero.tsx | 188 +++++---- app/components/Navbar.tsx | 379 +++++++++++++++--- app/components/Newsletter.tsx | 38 +- app/components/Reveal.tsx | 126 ++++++ app/components/Tools.tsx | 55 +-- app/globals.css | 145 ++++++- app/layout.tsx | 32 +- app/lib/cart.ts | 56 +++ app/lib/fluid.ts | 18 + app/page.tsx | 24 ++ app/todo-cards/components/AddToCartButton.tsx | 31 ++ app/todo-cards/components/Focus.tsx | 45 +++ app/todo-cards/components/HowItWorks.tsx | 84 ++++ app/todo-cards/components/Pricing.tsx | 64 +++ app/todo-cards/components/Testimonials.tsx | 64 +++ app/todo-cards/components/TodoKartenHero.tsx | 85 ++++ app/todo-cards/page.tsx | 45 +++ next.config.ts | 8 + package-lock.json | 69 ++++ package.json | 1 + public/about-author.jpg | Bin 1709363 -> 412290 bytes public/about-signature.png | Bin 13019 -> 0 bytes public/avatar-todo-1.png | Bin 0 -> 19745 bytes public/avatar-todo-2.png | Bin 0 -> 18208 bytes public/avatar-todo-3.png | Bin 0 -> 20473 bytes public/blog-featured.jpg | Bin 337283 -> 356011 bytes public/focus-todo-karten.png | Bin 0 -> 752925 bytes public/hero-todo-karten.png | Bin 0 -> 1471397 bytes public/hero.jpg | Bin 504151 -> 376557 bytes public/hero.png | Bin 504151 -> 691368 bytes public/icon-arrow-connector.svg | 10 + public/icon-bullet-dot.svg | 3 + public/icon-lock.svg | 6 + public/icon-step-1.png | Bin 0 -> 46932 bytes public/icon-step-2.png | Bin 0 -> 33682 bytes public/icon-step-3.png | Bin 0 -> 40871 bytes public/product-todo-karten.png | Bin 0 -> 135932 bytes 42 files changed, 1635 insertions(+), 363 deletions(-) create mode 100644 app/components/Reveal.tsx create mode 100644 app/lib/cart.ts create mode 100644 app/lib/fluid.ts create mode 100644 app/todo-cards/components/AddToCartButton.tsx create mode 100644 app/todo-cards/components/Focus.tsx create mode 100644 app/todo-cards/components/HowItWorks.tsx create mode 100644 app/todo-cards/components/Pricing.tsx create mode 100644 app/todo-cards/components/Testimonials.tsx create mode 100644 app/todo-cards/components/TodoKartenHero.tsx create mode 100644 app/todo-cards/page.tsx delete mode 100644 public/about-signature.png create mode 100644 public/avatar-todo-1.png create mode 100644 public/avatar-todo-2.png create mode 100644 public/avatar-todo-3.png create mode 100644 public/focus-todo-karten.png create mode 100644 public/hero-todo-karten.png create mode 100644 public/icon-arrow-connector.svg create mode 100644 public/icon-bullet-dot.svg create mode 100644 public/icon-lock.svg create mode 100644 public/icon-step-1.png create mode 100644 public/icon-step-2.png create mode 100644 public/icon-step-3.png create mode 100644 public/product-todo-karten.png diff --git a/app/challenge/page.tsx b/app/challenge/page.tsx index 1570add..3ffeaca 100644 --- a/app/challenge/page.tsx +++ b/app/challenge/page.tsx @@ -1,5 +1,30 @@ +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 ( @@ -132,7 +157,7 @@ function EmailCapture({ buttonLabel = "Challenge starten" }: { buttonLabel?: str /> @@ -154,7 +179,7 @@ export default function ChallengePage() {
{/* Left: text + form */} -
+ {/* Breadcrumb */}

@@ -194,14 +219,17 @@ export default function ChallengePage() { {/* Email form */} -

+ - {/* Right: hero image with badge */} -
+ {/* 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. */} + 7-Tage-Challenge {/* Badge */}
@@ -213,7 +241,7 @@ export default function ChallengePage() {
-
+
@@ -221,7 +249,7 @@ export default function ChallengePage() {
-
+

Jeden Tag ein Impuls. In nur wenigen Minuten.

-

+ -
+ {steps.flatMap((step, i) => [ -
-
+ +
{step.icon}

{step.title}

{step.desc}

-
, + , i < steps.length - 1 ? (
) : null, ])} -
+
@@ -257,7 +285,7 @@ export default function ChallengePage() {
-
+

7 Tage. 7 Impulse. Für mehr Klarheit in deinem Alltag.

-

+
{/* Image */} -
+ Notizbuch -
+ {/* Checklist */} -
    - {benefits.map((b) => ( -
  • - -
    -

    {b.title}

    -

    {b.desc}

    -
    -
  • - ))} -
+ +
    + {benefits.map((b) => ( +
  • + +
    +

    {b.title}

    +

    {b.desc}

    +
    +
  • + ))} +
+
@@ -300,34 +333,37 @@ export default function ChallengePage() {
-

Was andere sagen -

+ -
+ {/* Same micro-interactions as /todo-cards's identically-styled + testimonial cards (kept in sync deliberately): hover-lift on + the card, avatar scale on the same hover via `group`. */} + {testimonials.map((t) => ( -

{t.quote}

{t.name}

{t.name}

{t.role}

-
+ ))} -
+
@@ -335,7 +371,7 @@ export default function ChallengePage() { {/* ── Bottom CTA ── */}
-
+ {/* Left: icon + copy */}
@@ -359,11 +395,11 @@ export default function ChallengePage() {
{/* Right: form */} -
+
-
+
diff --git a/app/components/About.tsx b/app/components/About.tsx index becc3bc..8ed7eb7 100644 --- a/app/components/About.tsx +++ b/app/components/About.tsx @@ -1,72 +1,88 @@ +import { Reveal } from "./Reveal"; + export function About() { return ( -
+
- {/* Left: text content — relative + z-10 so it renders above the overlapping photo */} -
+ {/* Text content — relative + z-10 so it renders above the overlapping + photo at md+. Comes first in DOM at every breakpoint (no reorder + here — unlike Hero, there's no conversion CTA at stake). */} + {/* Large serif statement — width-constrained as per design */}

Ich glaube nicht, dass Menschen mehr Disziplin brauchen.

- {/* Quote row: script quote + golden divider + author bio */} -
+ {/* Quote row: script quote / divider / author bio — side-by-side + from md+, stacked with a horizontal divider below md */} +
{/* Caveat script text with signature positioned below */}

Ich glaube,

sie brauchen 

weniger Reibung.

- {/* Signature — absolute, sits just below the three Caveat lines (3 × 32px = 96px) */} + {/* Signature underline — hand-drawn organic stroke, sits just below the three Caveat lines (3 × 32px = 96px) */}
- +
- {/* Golden vertical divider + author lines */} -
-
-
-

Björn.

-

Führungskraft.

-

Familienmensch.

-

Gründer von einfach-produktiv.

-
+ {/* Divider — horizontal full-width line below md, vertical + gold line beside the author bio from md+ */} +
+ +
+

Björn.

+

Führungskraft.

+

Familienmensch.

+

Gründer von einfach-produktiv.

-
+ - {/* Right: author photo — overlaps text column via -ml-32, gradient covers the overlap */} -
+ {/* Author photo — overlaps the text column via -ml-48 from md+ only + (that overlap trick has nothing to blend into once stacked); + plain full-width photo below the text on Mobile. */} + Björn - {/* Left gradient: wide enough to cover the text-column overlap */} -
-
+ {/* Left gradient: wide enough to cover the text-column overlap — md+ only */} +
+
); diff --git a/app/components/Blog.tsx b/app/components/Blog.tsx index 9a5a623..d861e5f 100644 --- a/app/components/Blog.tsx +++ b/app/components/Blog.tsx @@ -1,5 +1,7 @@ import Link from "next/link"; +import Image from "next/image"; import { getBlogPosts } from "../lib/payload"; +import { Reveal, RevealGroup, RevealItem } from "./Reveal"; export async function Blog() { const posts = await getBlogPosts(3); @@ -11,107 +13,114 @@ export async function Blog() { .map((post) => ({ ...post, href: `/blog/${post.slug}` })); return ( -
+
{/* Section header */} -
-

+ +

Neue Impulse

Gedanken, Methoden & Impulse

-
+ {/* Posts grid */} -
+ - {/* Featured post — thumbnail flush left, card clips with overflow-hidden */} -
-
- + {/* Featured post — image on top on Mobile, side-by-side from md+ */} + +
+ {featured.thumbnail && ( + + )}
-
+
-
+
{featured.category} {featured.readTime} Min
-
+

{featured.title}

-

+

{featured.excerpt}

→ Zum Beitrag
-
+ - {/* Secondary posts — thumbnail with own rounded-xl, card py-2 only */} -
+ {/* Secondary posts — image on top on Mobile, side-by-side from md+ */} +
{secondary.map((post) => ( -
- {/* Thumbnail: own border-radius so it sits neatly inside the padded card */} -
- +
+ {post.thumbnail && ( + + )}
-
+
-
+
{post.category} {post.readTime} Min

{post.title}

-

+

{post.excerpt}

→ Zum Beitrag
-
+ ))}
-
+
); } diff --git a/app/components/Divider.tsx b/app/components/Divider.tsx index 2f7db5e..30e63f7 100644 --- a/app/components/Divider.tsx +++ b/app/components/Divider.tsx @@ -1,57 +1,68 @@ -export function Divider() { +import { Reveal } from "./Reveal"; + +function Word({ children }: { children: string }) { return ( -
-
+

+ {children} +

+ ); +} -

- Klarheit -

- - {/* Arrow separator (flipped) */} -
-
-
- -
-
+function Arrow() { + return ( +
+
+
+
- -

- Fokus -

- - {/* Arrow separator (flipped) */} -
-
-
- -
-
-
- -

- Entlastung -

- - {/* Sparkle icon */} -
-
-
- -
-
-
-
); } + +export function Divider() { + return ( + + + {/* Word + its trailing icon are grouped into one shrink-0 flex unit + so flex-wrap only ever breaks BETWEEN pairs, never leaving an + arrow stranded alone on its own line — the arrows are always + visible now (previously hidden below md: entirely to sidestep + that exact problem), this fixes the root cause instead. */} +
+ Klarheit + +
+ +
+ Fokus + +
+ +
+ Entlastung + + {/* Sparkle icon — sizes now fluid (--divider-sparkle-*) to match + the text-h2 words next to it, previously hard rem values that + stayed visually static while the words scaled. */} +
+
+
+ +
+
+
+
+ +
+ ); +} diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx index e9bf72f..a4af052 100644 --- a/app/components/Footer.tsx +++ b/app/components/Footer.tsx @@ -9,43 +9,43 @@ const links = [ export function Footer() { return ( -