diff --git a/app/components/Navbar.tsx b/app/components/Navbar.tsx
index d098d4b..b41df89 100644
--- a/app/components/Navbar.tsx
+++ b/app/components/Navbar.tsx
@@ -63,7 +63,7 @@ function getNavLinks(singleActiveProduct: boolean) {
// /der-eine and /tasse-die-pause (both bespoke product detail pages, same
// "own route, not nested" shape as /todo-cards) added here for the same
// reason (2026-08-25).
-const WERKZEUGE_ROUTES = ["/todo-cards", "/7-tage-klarheits-check", "/newsletter", "/der-eine", "/tasse-die-pause", "/lebensuhr", "/3x3-system"];
+const WERKZEUGE_ROUTES = ["/7-tage-klarheits-check", "/newsletter", "/der-eine", "/tasse-die-pause", "/lebensuhr", "/3x3-system"];
function isNavLinkActive(href: string, pathname: string, activeSection: string): boolean {
if (href === "#werkzeuge") {
diff --git a/app/components/ProductBlocks.tsx b/app/components/ProductBlocks.tsx
index 72841fe..39699c7 100644
--- a/app/components/ProductBlocks.tsx
+++ b/app/components/ProductBlocks.tsx
@@ -149,10 +149,27 @@ function renderProductBlock(block: ProductBlock, ctx: ProductBlockContext): Reac
// selector here (SVG presentation attributes lose to any stylesheet
// rule) scopes the recolor to PDPs only, without touching the shared
// icon set or PageBlocks.tsx's own rendering.
+ // Parallel fact cards, not a connected sequence — matches der-eine's
+ // own hand-coded Focus.tsx ("Was kann er?") exactly: icon on top,
+ // centered, no StepArrow connectors between them (those imply an
+ // order these facts don't have). Reuses stepRow's existing icon/
+ // title/description shape rather than a new block type; only the
+ // rendering diverges from Pages.layout's own connected-arrow stepRow
+ // (renderPageBlockSync), scoped to ProductBlocks.tsx. Icons black
+ // (#231f20), same override reasoning as the quote block below.
case "stepRow":
return (
-
);
// A PDP-specific full-bleed treatment for this one block type only —
@@ -278,6 +295,7 @@ function ProductHero({ product, shipping, taxRate, kleinunternehmer, body }: Pro
/next/image).
-function IconCheck() {
- return (
-
- );
-}
-
-export function Focus() {
- return (
-
-
-
-
-
-
- Für mehr Fokus im Alltag
-
-
- ToDo-Karten helfen dir, den Überblick zu behalten, Entscheidungen zu vereinfachen und deinen Tag bewusst zu gestalten.
-
-
- {bullets.map((b) => (
-
-
- {b}
-
- ))}
-
-
-
- );
-}
diff --git a/app/todo-cards/components/HowItWorks.tsx b/app/todo-cards/components/HowItWorks.tsx
deleted file mode 100644
index 9b45b8e..0000000
--- a/app/todo-cards/components/HowItWorks.tsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import { Fragment } from "react";
-import Image from "next/image";
-import { Reveal, RevealGroup, RevealItem } from "../../components/Reveal";
-import { StepArrow } from "../../components/StepArrow";
-
-// Each icon's own real pixel dimensions (not uniformly square, e.g.
-// icon-step-2 is 180x168) — needed so the `h-16 w-auto` sizing below infers
-// the correct aspect ratio instead of stretching/squashing to a guessed one.
-const steps = [
- {
- icon: "/icon-step-1.png",
- width: 165,
- height: 177,
- title: "1. Aufschreiben",
- desc: "Notiere deine Aufgaben und alles, was dir im Kopf herumgeht.",
- },
- {
- icon: "/icon-step-2.png",
- width: 180,
- height: 168,
- title: "2. Priorisieren",
- desc: "Wähle deine 3 wichtigsten Aufgaben für heute aus und schreibe sie auf.",
- },
- {
- icon: "/icon-step-3.png",
- width: 180,
- height: 177,
- title: "3. Umsetzen",
- desc: "Konzentriere dich auf das Wesentliche und hake ab, was wirklich zählt.",
- },
-];
-
-export function HowItWorks() {
- return (
-
-
-
- So einfach funktioniert es
-
-
- Ein paar Minuten am Morgen – mehr Klarheit den ganzen Tag.
-
-
-
- {/* Arrows always visible (see Divider lesson — hiding them below a
- breakpoint isn't the fix), just rotated 90° to point down while
- stacked below sm:. lg:px-[16.25rem] mirrors Figma's Desktop-only
- px-[260px] inset — that value doesn't get its own fluid token
- since it's a pure aesthetic narrowing, not something that breaks
- if it's just absent below lg:. RevealGroup/RevealItem stagger the
- 3 steps in on scroll, same pattern as Tools.tsx's card grid —
- arrows aren't part of the stagger (purely decorative connectors,
- not content), they just sit in the DOM between items. */}
-
- {steps.map((step, i) => (
-
-
- {/* Explicit per-icon width/height (real pixel dimensions, not
- a guessed/uniform size) — matters since one of the three
- source PNGs isn't square (icon-step-2 is 180x168); passing
- the real intrinsic size is what avoids next/image's
- "width or height modified, but not the other" warning that
- a wrong/guessed size would trigger against the `h-16
- w-auto` sizing below. group-hover scale is a small "step
- acknowledges you" touch — the whole text column is the
- hover target, not just the icon, so it's easy to trigger
- without precise pointing. */}
-
-
{step.title}
-
{step.desc}
-
- {i < steps.length - 1 && (
- // sm:mt-[1.625rem] (26px) centers the arrow on the h-16
- // (64px) icon above it — (64 - arrow's own 16px height) / 2
- // — same margin-based centering technique as Challenge's
- // step connector, not just the same icon asset. Bigger below
- // sm: (w-8 h-8, was w-6 h-6) per explicit feedback.
-
-
-
- )}
-
- ))}
-
-
- );
-}
diff --git a/app/todo-cards/components/Pricing.tsx b/app/todo-cards/components/Pricing.tsx
deleted file mode 100644
index e586109..0000000
--- a/app/todo-cards/components/Pricing.tsx
+++ /dev/null
@@ -1,137 +0,0 @@
-import Image from "next/image";
-import { AddToCartButton } from "../../components/AddToCartButton";
-import { Reveal } from "../../components/Reveal";
-import { getProductBySlug, getShippingSettings, getDefaultTaxRatePercent, getKleinunternehmer } from "../../lib/payload";
-import { formatPrice, discountPercent } from "../../lib/format";
-import { effectiveTaxRate } from "../../lib/cartTotals";
-
-const bullets = [
- "50 ToDo-Karten",
- "Hochwertiges Papier (120 g/m²)",
- "Format: 105 x 148 mm (A6)",
- "Inklusive Mini-Anleitung",
- "Nachhaltig produziert in Deutschland",
-];
-
-// Price/photo come from Payload (same "todo-karten" product the shop/cart
-// use), not a hardcoded literal — see ProductSpotlight.tsx for the same
-// reasoning; the bullet list stays hand-written since it's spec detail,
-// not something the Products collection models.
-export async function Pricing() {
- const [product, shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([
- getProductBySlug("todo-karten"),
- getShippingSettings(),
- getDefaultTaxRatePercent(),
- getKleinunternehmer(),
- ]);
- if (!product) return null;
- const discount = discountPercent(product.price, product.compareAtPrice);
- const taxRate = effectiveTaxRate(product, defaultTaxRate);
- // Same "any vs. every" split as ProductGrid.tsx/ProductSpotlight.tsx.
- // A deactivated product (product.active === false) isn't caught by
- // either — the shop grid/spotlight filter those out before they'd ever
- // reach this page, but this page resolves a product regardless of
- // active status (existing links to it should still 200, see
- // mapPayloadProduct's own comment in lib/payload.ts) — so it needs its
- // own explicit check here to read as "Ausverkauft" rather than fully
- // buyable (fixed 2026-07-24, feedback: deactivated should look
- // sold-out on its own detail page).
- const fullyOutOfStock =
- !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock);
- const anyLowStock = product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock);
-
- return (
-
- {/* Three flex children (photo, info, price/CTA) competing for space
- — same "big image next to text" shape as the Hero/Focus, so the
- same lg:-only structural exception applies here too. */}
-
-
- {/* MwSt./Versand disclosure on its own line, not crammed into
- the price row itself — "inkl. X% MwSt. zzgl. Versand" is
- long enough (once the rate is spelled out) that sharing a
- row with the price in this panel's narrow fixed-width
- column wrapped it mid-sentence onto a second line. */}
-
- Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands
-
- )}
-
- {/* Single product, no grid siblings to stay equal-height with —
- plain conditional line, same reasoning as ProductSpotlight.tsx. */}
- {anyLowStock &&
Nur noch wenige verfügbar
}
- {/* No "Sichere Zahlung" trust note here (unlike Cart/Checkout) —
- this is an add-to-cart step, not the actual payment step, so
- a payment-security reassurance is premature here and just
- duplicates the one shown later at checkout. */}
- {/* variants={[]} when deactivated — AddToCartButton's own
- outOfStock prop is ignored whenever variants is non-empty (it
- defers to each variant's own outOfStock flag instead, see its
- currentlyOutOfStock calc), so a deactivated product with
- in-stock variants would otherwise still render as buyable. */}
-
-
-
-
- );
-}
diff --git a/app/todo-cards/components/TodoKartenHero.tsx b/app/todo-cards/components/TodoKartenHero.tsx
deleted file mode 100644
index 2e6cbde..0000000
--- a/app/todo-cards/components/TodoKartenHero.tsx
+++ /dev/null
@@ -1,199 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { AddToCartButton } from "../../components/AddToCartButton";
-import { ProductGallery } from "../../components/ProductGallery";
-import { Reveal } from "../../components/Reveal";
-import { ProductName } from "../../components/ProductName";
-import { getProductBySlug, getShippingSettings, getDefaultTaxRatePercent, getKleinunternehmer } from "../../lib/payload";
-import { formatPrice, discountPercent } from "../../lib/format";
-import { effectiveTaxRate } from "../../lib/cartTotals";
-
-const checklist = [
- "Klarer Fokus auf das, was wirklich zählt",
- "Flexibel einsetzbar – jeden Tag",
- "Minimalistisch, analog, effektiv",
-];
-
-// Inline, brand-orange stroke — icon-check.svg's fill is hardcoded to
-// #222221 via an internal CSS var that only resolves inside the SVG's own
-// document, so it can't be recolored from the host page when loaded via
-// /next/image. Same simple checkmark path as Challenge's own
-// Check() component, for the same orange-checkmark look (fixed 2026-07-24).
-function IconCheck() {
- return (
-
- );
-}
-
-// Same "todo-karten" product Pricing.tsx reads further down the page —
-// this is just a compact early teaser so the hero's CTA isn't asking for
-// a click without saying what it costs. Delivery time is repeated here too
-// (not just in Pricing.tsx) since this is also a purchase CTA — Art. 246a
-// §1 Abs.1 Nr.8 EGBGB's delivery-date disclosure needs to sit next to every
-// buy button, not just one of them.
-export async function TodoKartenHero() {
- const [product, shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([
- getProductBySlug("todo-karten"),
- getShippingSettings(),
- getDefaultTaxRatePercent(),
- getKleinunternehmer(),
- ]);
- const discount = product ? discountPercent(product.price, product.compareAtPrice) : null;
- const taxRate = product ? effectiveTaxRate(product, defaultTaxRate) : null;
- // Same "any vs. every" split as ProductGrid.tsx/Pricing.tsx.
- const anyLowStock = product
- ? product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock)
- : false;
-
- return (
-
- {/* Same lg:-only structural exception as the Home Hero (see
- figma-to-nextjs skill Gotcha #5) — a wide fixed-ratio photo next
- to a text column is exactly the shape that gets too cramped at
- Tablet widths even under the site-wide sm: (640px) structural
- consolidation. lg:items-center
- removed (default grid align-items is stretch) — that's what lets
- the breadcrumb below sit at a fixed Y position across every hero
- section on the site instead of shifting per page depending on how
- much content each one has above the fold. */}
- {/* pt-10 md:pt-12, no lg override — with lg:items-center gone, this
- padding is now the ONLY thing positioning the breadcrumb
- vertically at every tier (previously lg:pt-0 relied on centering
- to add its own implicit offset instead). Kept flat past md so it
- matches /newsletter's and /challenge's identical top offset. */}
-
-
- {/* Breadcrumb — 3 levels (Startseite › Werkzeuge › ToDo-Karten),
- matching /newsletter's pattern; was missing "Startseite" as
- the first crumb. Deliberately NOT part of the centered block
- below — see the wrapper's comment. */}
-
-
- {/* Everything else — centered in the remaining vertical space at
- lg+ (the column now stretches to match the image's height,
- since lg:items-center was removed above; flex-1 + justify-center
- here centers this block within that leftover space instead of
- the breadcrumb-inclusive whole column, which is what made the
- breadcrumb's Y position drift per page depending on total
- content height). */}
-
- {/* Heading — 48px Desktop doesn't match any existing type-scale
- step, uses the new --text-h-page token (see globals.css). */}
-
-
- ToDo-Karten.
-
- {product?.subline && (
-
- {product.subline}
-
- )}
-
-
-
- Ein einfaches Werkzeug, das dir hilft, deinen Kopf frei zu bekommen und das Wesentliche zu sehen.
-
- Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands
-
- )}
- {/* Single product, no grid siblings to stay equal-height with —
- plain conditional line, same reasoning as ProductSpotlight.tsx/Pricing.tsx. */}
- {anyLowStock &&
Nur noch wenige verfügbar
}
-
-
- {product && (
- // variants={[]} when deactivated — see Pricing.tsx's own
- // comment on why AddToCartButton's outOfStock prop alone
- // isn't enough once variants is non-empty.
-
- )}
-
-
-
- {/* group + subtle scale-on-hover — a common, restrained "photo
- feels alive" micro-interaction; overflow-hidden on the wrapper
- (not the image) keeps the zoom from spilling past the rounded
- corners. delay={0.15} matches the Home Hero's text→image
- stagger — both fire ~immediately since Hero is already in the
- initial viewport, so this doubles as the page's entrance.
- Falls back to the curated static hero photo (a lifestyle shot,
- "in Anwendung, mit Notizbuch und Kaffee" — deliberately not
- just the plain catalog image) whenever this product has no
- `gallery` entries — the common case, and zero visual change
- from before ProductGallery existed. Only switches to the
- gallery once someone actually adds extra photos in the admin. */}
- {product && product.gallery.length > 0 ? (
-
-
-
- ) : (
-
-
-
- )}
-
-
- );
-}
diff --git a/app/todo-cards/page.tsx b/app/todo-cards/page.tsx
deleted file mode 100644
index 1cfb6a6..0000000
--- a/app/todo-cards/page.tsx
+++ /dev/null
@@ -1,64 +0,0 @@
-import type { Metadata } from "next";
-import { draftMode } from "next/headers";
-import { TodoKartenHero } from "./components/TodoKartenHero";
-import { HowItWorks } from "./components/HowItWorks";
-import { Focus } from "./components/Focus";
-import { Pricing } from "./components/Pricing";
-import { Footer } from "../components/Footer";
-import { TestimonialsGrid } from "../components/TestimonialsGrid";
-import { LiveTestimonialsGrid } from "../components/LiveTestimonialsGrid";
-import { getTestimonials, getProductBySlug, getCompanySettings } from "../lib/payload";
-import { buildProductSchema } from "../lib/structuredData";
-
-const title = "ToDo-Karten – Kleine Karten. Große Wirkung.";
-const description =
- "Ein einfaches Werkzeug, das dir hilft, deinen Kopf frei zu bekommen und das Wesentliche zu sehen. 50 hochwertige ToDo-Karten für mehr Fokus im Alltag.";
-
-export const metadata: Metadata = {
- title,
- description,
- alternates: {
- canonical: "/todo-cards",
- },
- openGraph: {
- title,
- description,
- url: "/todo-cards",
- images: ["/hero-todo-karten.png"],
- },
- twitter: {
- title,
- description,
- images: ["/hero-todo-karten.png"],
- },
-};
-
-export default async function TodoCardsPage() {
- const { isEnabled: isPreview } = await draftMode();
- const [testimonials, product, seller] = await Promise.all([
- getTestimonials("todo-cards", { draft: isPreview }),
- getProductBySlug("todo-karten"),
- getCompanySettings(),
- ]);
- const productSchema = product ? buildProductSchema(product, "https://einfach-produktiv.mk360.de/todo-cards", seller) : null;
-
- return (
- <>
- {productSchema && (
-
- )}
-
-
-
-
- {isPreview ? (
-
- ) : (
-
- )}
-
-
-
- >
- );
-}