diff --git a/app/components/ProductBlocks.tsx b/app/components/ProductBlocks.tsx
new file mode 100644
index 0000000..015fa5e
--- /dev/null
+++ b/app/components/ProductBlocks.tsx
@@ -0,0 +1,204 @@
+import Link from "next/link";
+import Image from "next/image";
+import type { Product, ProductBlock, PageBlock } from "../lib/payload";
+import { getShippingSettings, getDefaultTaxRatePercent, getKleinunternehmer } from "../lib/payload";
+import { formatPrice, discountPercent } from "../lib/format";
+import { effectiveTaxRate } from "../lib/cartTotals";
+import { AddToCartButton } from "./AddToCartButton";
+import { ProductName } from "./ProductName";
+import { RichText } from "./RichText";
+import { renderPageBlockSync } from "./PageBlocks";
+
+// Renders a Payload Products document's `layout` blocks field — same
+// "page sections, not inline Lexical nodes" shape as PageBlocks.tsx, which
+// this delegates to directly for every block type the two fields share
+// (richTextSection/stepRow/quote/image/icon/pillList/checklistImage/table/
+// ctaCard are the exact same Block configs, just registered a second time
+// on Products.layout — see that field's own comment in Products.ts). Only
+// `productHero` and `productPricingPanel` are product-specific, since they
+// need the live product/shipping/tax context that a generic content page
+// doesn't have.
+export async function ProductBlocks({ product }: { product: Product }) {
+ const [shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([
+ getShippingSettings(),
+ getDefaultTaxRatePercent(),
+ getKleinunternehmer(),
+ ]);
+ const ctx: ProductBlockContext = { product, shipping, taxRate: effectiveTaxRate(product, defaultTaxRate), kleinunternehmer };
+
+ return (
+ <>
+ {product.layout.map((block) => (
+
+ {renderProductBlock(block, ctx)}
+
+ ))}
+ >
+ );
+}
+
+type ProductBlockContext = {
+ product: Product;
+ shipping: Awaited>;
+ taxRate: number;
+ kleinunternehmer: boolean;
+};
+
+function renderProductBlock(block: ProductBlock, ctx: ProductBlockContext): React.ReactNode {
+ switch (block.blockType) {
+ case "productHero":
+ return ;
+ case "productPricingPanel":
+ return ;
+ // Every other block type is identical to Pages.layout's own — same
+ // Block config, reused a second time (see this file's own comment).
+ default:
+ return renderPageBlockSync(block as PageBlock);
+ }
+}
+
+function ProductHero({ product, shipping, taxRate, kleinunternehmer, body }: ProductBlockContext & { body: unknown }) {
+ const discount = discountPercent(product.price, product.compareAtPrice);
+ 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 (
+
+ Lieferzeit: {shipping.totalDays.min}–{shipping.totalDays.max} Werktage innerhalb Deutschlands
+
+ )}
+
+ {anyLowStock &&
Nur noch wenige verfügbar
}
+
+
+
+ );
+}
diff --git a/app/einfach-anfangen/components/Focus.tsx b/app/einfach-anfangen/components/Focus.tsx
deleted file mode 100644
index 92fd1e6..0000000
--- a/app/einfach-anfangen/components/Focus.tsx
+++ /dev/null
@@ -1,42 +0,0 @@
-import Image from "next/image";
-import { Reveal, RevealGroup, RevealItem } from "../../components/Reveal";
-
-const bullets = [
- "10 ToDo-Karten",
- "Platz für bis zu 7 Aufgaben",
- "7,5 × 12,5 cm – passt in jede Tasche",
- "Freie Rückseite für Notizen",
- "Kleine Trink-Erinnerung auf jeder Karte",
- "Hochwertiges Papier",
- "Produziert in Deutschland",
-];
-
-// Spec bullet list — same "hand-written, not modeled in Products" reasoning
-// as todo-cards/Pricing.tsx's own bullets. Two-column at sm+ since 7 items
-// in one column ran noticeably longer than every other section on the page.
-export function Focus() {
- return (
-
-
-
- 10 Karten für deinen Alltag
-
-
- Eine Karte für heute. Für das, was gerade ansteht.
-
-
-
-
- {bullets.map((b) => (
-
-
- {b}
-
- ))}
-
-
- );
-}
diff --git a/app/einfach-anfangen/components/Hero.tsx b/app/einfach-anfangen/components/Hero.tsx
deleted file mode 100644
index 4817b77..0000000
--- a/app/einfach-anfangen/components/Hero.tsx
+++ /dev/null
@@ -1,120 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { AddToCartButton } from "../../components/AddToCartButton";
-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";
-
-// Same "compact early teaser + delivery-time repeated next to every buy
-// button" reasoning as TodoKartenHero.tsx/der-eine's Hero.tsx. Body copy is
-// hardcoded here (not read from product.description) — same convention as
-// those two, see der-eine/page.tsx's own comment on why the CMS field and
-// this page's tuned prose are allowed to say different things.
-export async function Hero() {
- const [product, shipping, defaultTaxRate, kleinunternehmer] = await Promise.all([
- getProductBySlug("todo-starter"),
- getShippingSettings(),
- getDefaultTaxRatePercent(),
- getKleinunternehmer(),
- ]);
- const discount = product ? discountPercent(product.price, product.compareAtPrice) : null;
- const taxRate = product ? effectiveTaxRate(product, defaultTaxRate) : null;
- const fullyOutOfStock = product
- ? !product.active || (product.variants.length > 0 ? product.variants.every((v) => v.outOfStock) : product.outOfStock)
- : false;
- const anyLowStock = product
- ? product.active && (product.variants.length > 0 ? product.variants.some((v) => v.lowStock) : product.lowStock)
- : false;
-
- return (
-
-
- Nicht alles. Nicht irgendwann. Nur die paar Dinge, die heute wichtig sind.
-
-
- Einfach anfangen. sind ToDo-Karten für deinen Alltag – mit Platz für bis zu sieben Aufgaben, einem kleinen Feld für „Das war heute schön" und einer freien Rückseite für Notizen.
-
-
- Dazu eine kleine Trink-Erinnerung. Weil man das zwischen all den anderen Dingen gern mal vergisst.
-