Commit Graph

421 Commits

Author SHA1 Message Date
Marco a2d4cb29fc Fix Page Builder's QuoteBlock label being silently ignored
app/components/Quote.tsx (the shared component PageBlocks.tsx/Pages.layout
uses) never had a `label` prop at all — dropped when it was extracted from
RichText.tsx's own local Quote. QuoteBlock.label was set correctly in the
admin and mapped through payload.ts, but PageBlocks.tsx's "quote" case
never passed it through, and even if it had, this component had nowhere
to put it. Both fixed; matches RichText.tsx's existing label treatment.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:50:11 +00:00
Marco 1d7f49217c Add PDP for Einfach anfangen. (todo-starter)
New product page at /einfach-anfangen — Hero (breadcrumb/price/CTA),
"Eine Karte. Ein Tag." 3-step section (same icon set as todo-cards'
HowItWorks), a spec bullet list, and a closing pricing panel. Follows the
same hand-coded structure as der-eine/todo-cards (bespoke Hero prose, not
read from Products.description). detailHref updated in Payload to point
here instead of the unbuilt /todo-starter.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:48:09 +00:00
Marco 8b9e2bdb42 PDP breadcrumbs: read the real product name instead of hardcoded copy
der-eine's breadcrumb hardcoded "Der Eine" — missing the trailing period
its own headline/pricing card already style in brand color. Switched all
three PDP breadcrumbs to the actual product.name via ProductName so they
can't drift from the real title again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:42:43 +00:00
Marco a1b0dffff7 Render the 6 new blog-post blocks (StepRow/Icon/PillList/ChecklistImage/Table/CtaCard)
Follows Posts.content's BlocksFeature update (docker/payload commit
5426a02) — mirrors PageBlocks.tsx's own JSX for each block field-for-field
since it's the same Block config reused a second time. Works in both the
server-rendered page and the client-side Live Preview renderer (shared
converters), unlike testimonialsRef which needs an async fetch and stays
page-builder only.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:40:12 +00:00
Marco 1330e3e621 Show product subline on cards/cart/PDP hero; brand-color trailing period
Follows Products.subline (docker/payload commit ad7156e). Cards and cart
line items showed nothing but the bare name — added the subline under it.
der-eine and todo-cards' hero taglines were hardcoded copy nearly
identical to this new field — switched both to read subline instead, so
future edits go through the CMS.

Also adds a shared ProductName component: every hand-written PDP headline
already styled a trailing "." in brand color, but the few places that
render product.name as plain text (cards, cart, the Payload-driven
tasse-die-pause hero) had silently lost that styling. Centralizes it so
it can't drift per call site again.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:26:14 +00:00
Marco 21b5f41127 Enlarge Hero block portrait (80px -> 128px)
/ueber-mich's about-author.jpg read as barely-there next to the full
headline at size-20 — bumped so it actually registers as a real photo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:04:32 +00:00
Marco 7489f83564 Follow Products.description consolidation to richText-only
Backend dropped the plain-text description field in favor of a single
richText one (see docker/payload commit 1ba8d07). Renders formatted
(bold/italic/multiple paragraphs) via the shared RichText component on
the PDP (der-eine, tasse-die-pause); everywhere else (Passend-dazu cards,
Product JSON-LD, homepage spotlight fallback) derives plain text at read
time via a new extractPlainText() helper instead of a second field.
Removes the description line from cart line items entirely — it only
bloated the cart with no real benefit there.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01J1Hu5bZ1kZUgKhab6yNwCt
2026-08-26 22:04:28 +00:00
Marco 82696cb259 Homepage blog teaser: make the whole card clickable, not just "Zum Beitrag"
The cards already had a hover-lift effect implying the whole thing
was clickable, but only the small arrow line was an actual link —
image and title had pointer-events-none/no href at all. The full
/blog listing page already wraps its own cards in one Link; this
brings the homepage teaser (Blog.tsx) in line with it. The former
inner CTA Link is now a plain span using group-hover for its color
change, since one <a> can't nest another.
2026-08-26 21:26:46 +00:00
Marco 0fea84f362 Fix homepage spotlight teaser dropping italic formatting
SpotlightText's minimal Lexical converter only checked format bit 1
(bold) — bit 2 (italic) was silently ignored, so Tasse "Die Pause"'s
italicized quote ("Nur noch schnell … Nein.") rendered as plain text
on the homepage despite showing correctly in the admin's richText
editor.
2026-08-26 21:24:26 +00:00
Marco 56fae6ff13 Product JSON-LD: add sku + gallery images, single-source the meta description
sku was tracked in Payload but never exposed to the frontend at all —
added to the Product type and wired into buildProductSchema (a real
Schema.org property Google's rich-result validator checks for).
image is now an array (main + gallery) when a product has gallery
photos, instead of always just the one main image.

/der-eine's page metadata description was a separately hardcoded
string that had drifted from Products.description (the one JSON-LD/
cart/checkout actually use) — now reads from the live product via
generateMetadata, same pattern /tasse-die-pause already uses. Updated
Payload's own description field to the more product-descriptive text
that used to live only in that hardcoded string.
2026-08-26 21:20:44 +00:00
Marco 39ebf2604d Hero block: make subline more prominent (was too muted) 2026-08-26 20:58:47 +00:00
Marco 2280a4a4ba Cut /ueber-mich over to the page builder
Content recreated as an 11-block Pages document — first real use of
HeroBlock's optional portrait image (about-author.jpg, uploaded to
Media fresh) alongside the headline. Checklist has no image (uses
ChecklistImageBlock's now-optional image field). Verified via a
temporary alternate slug before switching back and removing the
static file.
2026-08-26 20:51:50 +00:00
Marco b0d76c891f Frontend support for Hero block's optional portrait image 2026-08-26 20:50:25 +00:00
Marco 7fc7ee2c3a Cut /3x3-system over to the page builder
Content recreated as a 17-block Pages document (hero, prose sections,
the 2 key-line quotes, the pill list, the category step row, the
matrix example image — uploaded to Media fresh since the closest
existing media doc turned out to be a different file — and the
closing CTA card). Verified via a temporary alternate slug against
the live catch-all route (including the inline bold "Klarheit."
formatting) before switching back and removing the static file.
2026-08-26 20:45:08 +00:00
Marco 098ba79c8f Frontend support for the Hero block; H1 moves out of the hardcoded wrapper
app/[slug]/page.tsx and LivePageContent.tsx no longer render a
hardcoded H1 from page.title — that's now the Hero block's job (see
HeroBlock.ts's own comment on why). Breadcrumb stays, still built
from page.title.

Also backfilled /lebensuhr's already-migrated Pages document with a
Hero block (order 0) carrying its real headline — without it the
page would have lost its H1 entirely once the hardcoded wrapper was
removed.
2026-08-26 20:41:21 +00:00
Marco f4af0bc15c Cut /lebensuhr over to the page builder
Content recreated as a Pages document (11 blocks: intro prose, the
symbolic clock icon, the "24h" prose, the Alter/Uhrzeit table, more
prose, the 4-phase step row, the "Zinseszins" quote, more prose, the
3-question checklist, closing prose, the CTA card) — verified via a
temporary alternate slug against the live catch-all route before
switching it back to `lebensuhr` and removing the static file.

The static app/lebensuhr/page.tsx is now gone; /lebensuhr resolves
through app/[slug]/page.tsx.
2026-08-26 20:29:47 +00:00
Marco 2d9508324a Frontend support for stepRow subtitle + new icon block
Mirrors the backend schema additions (StepRowBlock.items.subtitle,
new IconBlock) needed for the /lebensuhr Pages migration: PageBlock
type + mapPayloadPageBlock in payload.ts, a "icon" case in
PageBlocks.tsx, and SYMBOLIC_ICONS (the clock face) added alongside
STEP_ICONS in StepIcons.tsx — a distinct registry since these icons
are standalone, not part of an icon-above-text row.
2026-08-26 20:26:46 +00:00
Marco 885389b300 Add the page-builder frontend: /[slug] catch-all + block renderer
Pages/getPageBySlug/mapPayloadPage in payload.ts follow the exact
getPostBySlug/mapPayloadPost pattern. PageBlocks.tsx renders a Pages
doc's `layout` field, reusing existing components (RichText,
StepArrow, STEP_ICONS, TestimonialsGrid) rather than reinventing per-
block styling — matches what /lebensuhr, /3x3-system, and
/7-tage-klarheits-check already hand-built. LivePageContent.tsx
mirrors LivePostContent.tsx's live-preview pattern, using a synchronous
subset of the block renderer (testimonialsRef needs an async fetch a
client component can't perform inline, so it's skipped in preview
only — same "editable subset" scope-cut LivePostContent.tsx already
makes). buildWebPageSchema in structuredData.ts is the generic
JSON-LD fallback for content types with no bespoke schema (Article/
Product don't fit a page-builder page).

Verified end-to-end: inserted a real Pages test document (SQL, since
no admin auth available here) covering richTextSection/quote/ctaCard,
confirmed /[slug] renders it correctly, confirmed notFound() after
deleting it, then cleaned up the test row.
2026-08-26 20:08:24 +00:00
Marco 940545888c Consolidate per-page icon duplicates into a shared registry
/lebensuhr, /3x3-system, and /7-tage-klarheits-check each defined
their own local copies of the same hand-drawn brand-line icons.
Moved them into app/components/icons/StepIcons.tsx as a keyed
registry (STEP_ICONS) — also what the new Payload Pages collection's
StepRowBlock select field maps onto by key, so a page-builder step
row can reference these same icons without duplicating SVG paths a
third time.
2026-08-26 20:03:57 +00:00
Marco e2d81fabc7 Add the trailing period to "einfach produktiv." wherever it was missing
The period is part of the brand name (see Footer.tsx's own comment on
the two-tone logo treatment) — fixed every place it was cleanly
appendable (string/heading endings, or before a dash, matching the
existing precedent in VertragspartnerBlock.tsx). Left the handful of
genuinely awkward mid-sentence spots alone (separable-verb endings,
"einfach produktiv-Konto" hyphenated compounds) rather than force a
period that breaks the sentence — flagging those separately.

Also: Über-mich's checklist now uses the same brand checkmark as
/lebensuhr and /7-tage-klarheits-check instead of plain bullets.
2026-08-26 19:25:27 +00:00
Marco a1c9a2127b Navbar: mark Werkzeuge active on /lebensuhr and /3x3-system 2026-08-26 19:06:06 +00:00
Marco c2d0b6e364 Rename /ueber-bjoern to /ueber-mich, link it in the nav
Nav's "Über Björn" entry pointed to the homepage's #ueber-bjoern
teaser section, not the new bio page — updated the label to "Über
mich" and the href to the real /ueber-mich page. The homepage's own
#ueber-bjoern anchor id (About.tsx) is untouched, it's a separate
section.
2026-08-26 18:55:49 +00:00
Marco 349bea5261 Add /ueber-bjoern bio page
Condensed from einfach-produktiv.com/ueber-bjoern/ — trimmed,
emoji-free, rewritten in the site's own voice rather than the
original's. Same hand-styled article treatment (H2/Quote/CTA-card)
as /lebensuhr and /3x3-system since it's not CMS-backed either.
Reuses the existing about-author.jpg portrait already used on blog
posts.
2026-08-26 18:53:56 +00:00
Marco 85478ff746 Lebensuhr: move the symbolic clock icon under its own heading, not the H1 2026-08-26 16:25:10 +00:00
Marco cf5d7f8739 3x3-system: pull the category row back into the article column
The full-width bg-bg-muted band made the three categories read as
a disconnected banner, separate from the paragraph right above it
introducing them. Now inline in the same 48rem column as the rest
of the article — still icon-above-text with StepArrow connectors,
just not broken out into its own section anymore.
2026-08-26 16:24:13 +00:00
Marco b26a44b979 Lebensuhr: add a purely symbolic clock icon above the headline
No numbers, no age/time data — just a clock silhouette (circle,
ticks, hands at 10:10). Every previous clock attempt tried to carry
actual data and kept going wrong; this one carries nothing, so
there's nothing to get wrong.
2026-08-26 16:23:26 +00:00
Marco 0fedb386b4 Remove the lebensuhr clock-face SVG
Went through several rounds (dot timeline, 24h dial, 12h dial,
decluttering) without landing right, and there's no way for me to
visually verify an SVG in this environment — kept guessing wrong.
Cutting it rather than continuing to iterate blind. The table stays
as the clear, working data source.
2026-08-26 16:17:58 +00:00
Marco 7b9e833560 Cut 3x3-system's prose volume, add a pill-list and a third Quote
Too much straight paragraph text in a row. Trimmed nearly every
section by merging/shortening paragraphs, turned the "everything
competes for priority" list into a tag/pill row instead of prose,
and promoted the closing "Zufall/du entscheidest" line into a Quote
callout (matching the two others already on the page) instead of a
plain paragraph.
2026-08-26 16:07:10 +00:00
Marco 3be8682e1a Simplify: strip clock clutter, drop 3x3-system's decorative dot-grid icon
Lebensuhr clock: each of the 8 points had 3 stacked text lines (age,
time, vorm./nachm.) plus a leader line — too much text crammed
around a small dial. Down to just the age number per point; the
matching time is one glance away in the table.

3x3-system: removed the IconGrid3x3 decorative mark per explicit
feedback that it added nothing.
2026-08-26 16:05:49 +00:00
Marco 5e48b67588 3x3-system: match icon-row/StepArrow pattern used site-wide
Category cards were a standalone 3-card grid with red/amber/green
accents (matching the original blog post) — out of step with how
every other icon-row on the site looks. Now the same icon-above-
text + StepArrow layout as /7-tage-klarheits-check's "So
funktioniert" and /lebensuhr's phase row, and the icons are a single
brand-orange line-icon style instead of per-category colors.
2026-08-26 15:55:17 +00:00
Marco 4326072fbe Lebensuhr clock: real 12-hour dial instead of a nonexistent 24-hour one
A clock face has 12 numbers, not 24 — the previous version invented
a 0-24 dial that doesn't exist on any real clock. Now a proper 1-12
face, with each age plotted at its 12h-mod position, labeled with
time + vorm./nachm. (every dial position is hit twice a day), and a
filled dot for vormittags vs. a hollow ring for nachmittags so the
two laps stay visually distinct.
2026-08-26 15:54:09 +00:00
Marco f0f5252d10 Lebensuhr clock: bigger, unclipped, every hour tick labeled
Sized the SVG up (viewBox 250->420) after labels clipped at the
edge, and gave all 12 ticks a real 24h-scale number (0,2,4…22, full
"HH:00" at the 4 cardinal points) instead of just 00/06/12/18 — with
only 4 numbers shown at those same 4 positions, the dial read like
an ordinary 12-hour clock face (implying two laps/day) rather than
the single 24h lap it actually represents.
2026-08-26 15:48:58 +00:00
Marco 0de2e1c1be 3x3-system: match /lebensuhr's structural style more closely
Category cards now break out into a full-width bg-bg-muted band
(same structural role as /lebensuhr's phase-row section), and the
page gets its own small decorative identity graphic — a literal 3x3
dot grid, paralleling that page's clock face.
2026-08-26 15:45:53 +00:00
Marco 76d59a5682 Lebensuhr clock: show the matching time under each age, plus 00/06/12/18 ring markers
The clock face only had age numbers — no way to tell which time on
the dial each one corresponded to. Added the matching time as a
second line under each age, and hour markers at 00/06/12/18 on the
ring itself so the dial reads as an actual 24h clock, not just an
abstract circle.
2026-08-26 15:44:24 +00:00
Marco af5956848b Lebensuhr: add age range to each life phase card
The four phases (Morgen/Vormittag/Nachmittag/Abend) had no explicit
tie back to age — readers had to infer it themselves from the clock
metaphor. Added an approximate age range per phase, using the same
age/90 = time/24 scale as the table (Vormittag ends exactly at 12:00
= age 45, which conveniently lines up with "Mittag").
2026-08-26 15:37:58 +00:00
Marco cdc779aebe 3x3-system: card layout for categories, matrix image, CTA card; lebensuhr fixes
/3x3-system:
- Categories now a 3-card grid (icon + colored top border) instead of
  a plain bulleted list.
- Embeds the original blog post's Arbeit/Familie/Persönlich matrix
  graphic, framed like the challenge page's image, with a caption
  clarifying it's one example application (the surrounding text only
  ever describes 3 categories, not a 3x3 grid).
- Closing CTA is now the same bordered-card link /lebensuhr uses.
- Tightened remaining prose.

/lebensuhr:
- Fixed missing side padding below 640px (the table card's
  full-bleed negative-margin trick ate the page's own padding).
- Added a small decorative clock-face SVG above the table (8 ages
  plotted at their proportional position) — company piece for the
  table, which stays the actual data source.
2026-08-26 15:36:47 +00:00
Marco 9af970587d Lebensuhr: revert dot-timeline back to a real, styled table
The dot-on-a-line version dropped the explicit "Alter"/"Uhrzeit"
column labels, making it ambiguous which value was which, and
forced a horizontal scroll on mobile for no real gain. Back to an
actual <table> with headers, just dressed up: brand-underlined
header row, zebra striping, bg-bg-muted card frame.
2026-08-26 15:29:50 +00:00
Marco fff2467d19 Lebensuhr: replace plain data table with a clock-scale timeline
A horizontal dotted line with each age/time pair on it reads as an
actual clock scale instead of a bare two-column table needing a
caption to explain itself. Same bg-bg-muted rounded-card treatment
already used by the AGB sidebar and the blog author box.
2026-08-26 15:26:36 +00:00
Marco e9a793db08 Lebensuhr: reuse morning icon for evening (rays stay above the sun) 2026-08-26 15:25:08 +00:00
Marco cfa658a210 Restyle /lebensuhr with shared step/checklist/CTA-card patterns
Reuses /7-tage-klarheits-check's StepArrow icon-row for the four life
phases (was plain H3 blocks), the checkmark-list pattern for the
closing three questions, and the blog's bordered "Passend dazu"
card treatment for the closing CTA. Also tightened the prose —
cut repeated points and filler transitions.
2026-08-26 15:22:14 +00:00
Marco 2dee2e7871 Add /3x3-system article page
Adapted from the einfach-produktiv.com blog post — condensed and
tightened, same hand-styled article treatment as /lebensuhr since
this isn't CMS-backed content either. CTA links to
/7-tage-klarheits-check, matching that page's own closing link.
2026-08-26 15:17:08 +00:00
Marco 790762979e Add /lebensuhr article page
Content page about the "life clock" concept, ending with an internal
link to /7-tage-klarheits-check. Styled by hand (headings/quote match
RichText.tsx's converter output) since it's not CMS-backed content.
2026-08-26 15:11:45 +00:00
Marco c64902c381 Rename /lebensuhr to /7-tage-klarheits-check, update CTA/content copy
Renames the route, its canonical/OG metadata, Navbar's active-route
list, and the testimonials page filter to match. Also updates CTA
copy ("Klarheits-Check starten"), the "So funktioniert" heading, the
"3. Umsetzen" step text, and the breadcrumb label.
2026-08-26 15:04:03 +00:00
Marco bf48a1561c Home Blog featured post: stack image-then-text through 1024px, not 640px
Side-by-side kicked in at sm (640px), squeezing the text column hard on
tablet widths. Moved the breakpoint to lg (1024px), matching the
secondary posts' grid below, which already used lg for the same reason.
2026-08-25 20:35:53 +00:00
Marco 168f17bcef Home Blog featured post: taller card on lg+ via min-h, image follows
The card's height was purely driven by the (short, fixed) text column's
content regardless of viewport width. lg:min-h-[26rem] gives it real
height on large screens; the image's sm:h-full and the text column's
justify-between both stretch to match.
2026-08-25 20:29:24 +00:00
Marco 7e00d51b89 Revert "Home Blog section: cap width at max-w-[75rem], same as the rest of the site"
This reverts commit 233a41f7c9.
2026-08-25 20:23:15 +00:00
Marco 233a41f7c9 Home Blog section: cap width at max-w-[75rem], same as the rest of the site
Was full-bleed w-full with no cap, so on very wide screens the featured
card (and its now-h-full image column) kept stretching wider while the
image's height stayed pinned to the text column's content height —
increasingly squashed the wider the viewport got.
2026-08-25 20:20:18 +00:00
Marco 24ae330037 Home Blog featured post: image stretches to full row height, not a fixed 220px
The 58%-wide image column at a hardcoded 220px tall produced a badly
elongated crop on wide viewports. sm:h-full lets the grid's natural
row-stretch (driven by the text column) size it instead, matching how
/blog's own featured card already does it.
2026-08-25 20:14:32 +00:00
Marco 821fbb0207 Blog: drop hero photo, plain text header like /shop's ShopHeader
Same breadcrumb/title/description block as every other page, no more
bespoke photo-bleed layout. Featured post card goes back to sitting
flush below the header (no more negative-margin overlap trick, which
only made sense with the photo's bottom edge to overlap).
2026-08-25 19:36:49 +00:00
Marco 194117c07d Der Eine.: add Testimonials + closing Pricing CTA, reorder PasstDazu last
Testimonials now pulled from Payload (page: "der-eine", same collection
as todo-cards/newsletter/challenge) instead of a hardcoded array. Pricing
adds a second, final buy CTA (same image+price+button card as the other
product pages' Pricing.tsx) for visitors who scroll past Hero's button.
PasstDazu moves to right before the Footer.
2026-08-25 19:28:47 +00:00