diff --git a/README.md b/README.md
index a030126..15e7ece 100644
--- a/README.md
+++ b/README.md
@@ -1479,6 +1479,40 @@ while auditing every page's metadata were fixed in the same pass: 3
route (now uses the real order number), and `/shop`/`/blog` were missing an
Open Graph image.
+## Tablet responsive fixes (2026-07-24)
+
+A follow-up to the Mobile pass above — testing at real Tablet widths
+(768-1023px) surfaced a few spots where the Mobile-focused fixes had left
+Tablet worse off than before, or where a component's own structural
+breakpoint no longer needed to be as conservative as originally set:
+
+- **`Hero.tsx` reverted from `lg:` back to `md:` as its structural
+ breakpoint.** It was moved to `lg:` earlier (see the figma-to-nextjs
+ skill's Gotcha #5) because at `md:col-span-5` the text column was only
+ ~283px at 768px and the then-full-size CTA/subtitle text made the whole
+ row wrap to 3 cramped lines. The smaller fixed CTA/subtitle/icon sizes
+ added during the Mobile pass (for true phone widths) are well under half
+ that column's width, so the original wrapping problem doesn't recur —
+ moving back to `md:` means Tablet gets the real 5/7 grid (image beside
+ text) again instead of a phone-style stacked layout. Every `lg:`-gated
+ size override in that file moved to `md:` to match.
+- **`Tools.tsx`'s card icon** — full 56px size pushed from `md:` to `lg:`;
+ at Tablet the title/description text is still close to its own fluid
+ floor, and the full-size icon read as too big next to it.
+- **`About.tsx`'s text/photo columns** — the Desktop overlap layout
+ (`flex-[1.4_0_0]` on the photo, `-ml-48`, gradient) kicked in from `md:`
+ already, giving the text column (fixed against the smaller 1:1.4 share)
+ too little room for its fixed-width statement + quote/bio row at Tablet.
+ The ratio is swapped at Tablet (text gets the bigger 1.4 share, photo the
+ smaller one, no overlap) and reverts to the original ratio + overlap only
+ from `lg:` up, where it was designed for.
+- **`Newsletter.tsx`'s input+submit row** and **`Footer.tsx`'s logo/handle/
+ legal-links row** — both went side-by-side at `md:`, but the columns
+ around them (the newsletter card's fixed-width copy column; the footer's
+ 3 groups sharing one `justify-between` row) didn't leave enough width at
+ 768px. Both pushed from `md:flex-row` to `lg:flex-row` — stacked through
+ the whole Tablet range, side by side again once there's real room.
+
## Tests
`npm run test:unit` (Vitest, `node` environment, no jsdom/Next.js runtime
diff --git a/app/components/About.tsx b/app/components/About.tsx
index 362fb89..9dd0f89 100644
--- a/app/components/About.tsx
+++ b/app/components/About.tsx
@@ -6,9 +6,16 @@ export function About() {
{/* 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). */}
-
+ photo at lg+. Comes first in DOM at every breakpoint (no reorder
+ here — unlike Hero, there's no conversion CTA at stake).
+ md:flex-[1.4_0_0] lg:flex-[1_0_0] — at Tablet the text column got
+ the narrower 1:1.4 share meant for Desktop's overlap layout,
+ leaving it too cramped for the fixed-width statement + quote/bio
+ row. Widened at Tablet (text gets the bigger share, image the
+ smaller one, no overlap yet) and reverted to the original ratio
+ from lg: up, where the overlap trick actually needs the image to
+ have more room. */}
+
{/* Large serif statement — width-constrained as per design */}
- {/* 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. */}
+ {/* Author photo — overlaps the text column via -ml-48 from lg+ only
+ (that overlap trick has nothing to blend into once stacked, and
+ at Tablet it would eat back into the extra width the text column
+ above just gained); plain full-width photo below the text on
+ Mobile, plain side-by-side (no overlap) at Tablet. */}
@@ -80,11 +89,11 @@ export function About() {
alt="Björn"
src="/about-author.jpg"
fill
- sizes="(min-width: 768px) 58vw, 100vw"
+ sizes="(min-width: 1024px) 58vw, (min-width: 768px) 42vw, 100vw"
className="object-cover object-center pointer-events-none"
/>
- {/* Left gradient: wide enough to cover the text-column overlap — md+ only */}
-
+ {/* Left gradient: wide enough to cover the text-column overlap — lg+ only */}
+
diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx
index 848a7ad..3dee45e 100644
--- a/app/components/Footer.tsx
+++ b/app/components/Footer.tsx
@@ -18,8 +18,12 @@ export function Footer() {
{/* Footer inner — max-width 1280px, centered */}
- {/* Three groups: logo | @handle | links — stacked + centered below md */}
-
+ {/* Three groups: logo | @handle | links — stacked + centered below
+ lg: (was md:). Logo + handle + 5 legal links all side by side
+ with justify-between read too cramped on Tablet — stacked
+ through that range instead, side by side again once there's
+ real room at lg:. */}
+
{/* Logo: "einfach produktiv" white + "." gold */}
diff --git a/app/components/Hero.tsx b/app/components/Hero.tsx
index 1a4dfc6..bb97bf8 100644
--- a/app/components/Hero.tsx
+++ b/app/components/Hero.tsx
@@ -11,7 +11,7 @@ function HeroImage() {
alt=""
fill
priority
- sizes="(min-width: 1024px) 58vw, 100vw"
+ sizes="(min-width: 768px) 58vw, 100vw"
className="object-cover"
style={{
WebkitMaskImage:
@@ -28,36 +28,39 @@ function HeroImage() {
export function Hero() {
return (
- {/* Structural breakpoint is lg: (1024px) here, not the site-wide md:
- (768px) — a documented exception (see Gotcha in the figma-to-nextjs
- skill). At md:col-span-5 the text column was only ~320px at
- 768-1023px viewports, too narrow for the heading/CTA/social-proof
- row (which wrapped to 3 cramped lines). Staying stacked full-width
- through the whole Tablet range and only splitting into the 5/7
- grid once there's real room (≥1024px) fixes that without touching
- the 5/7 ratio itself, which is fine once it has space. */}
-
+ {/* Structural breakpoint is md: (768px), same as the site-wide
+ convention — briefly moved to lg: (see the figma-to-nextjs
+ skill's Gotcha #5) because at md:col-span-5 the text column was
+ only ~283px at 768px and the full-size CTA/subtitle text made the
+ heading/CTA/social-proof row wrap to 3 cramped lines. Reverted
+ 2026-07-24: the CTA/subtitle/icon sizing added since then for
+ true mobile widths (see each one's own comment below) uses fixed
+ sizes well under half that column width, so the original problem
+ doesn't recur — moving back to md: means Tablet gets the real
+ 5/7 grid (image beside text) instead of stacking full-width like
+ a phone. */}
+
{/* Text content — first in DOM/visual order at every breakpoint so
the CTA stays above the fold on Mobile (deliberate exception to
the "keep DOM order" default, see Hero decision in the plan).
Reveal fires ~immediately since Hero is already in the initial
viewport — this doubles as the page's entrance animation. */}
-
+
- {/* Heading — forced break after "darf" only in the sm-lg tablet
- range (Hero stacked full-width, natural wrap there landed
+ {/* Heading — forced break after "darf" only in the sm-md tablet
+ range (Hero stacked full-width there, natural wrap landed
awkwardly). Removed at true mobile widths (below sm:) 2026-07-24
— narrower still, natural wrap there reads fine, and the
forced break made "darf" the whole first line, leaving very
- little text above the CTA. Natural wrap from lg: up too, once
+ little text above the CTA. Natural wrap from md: up too, once
the 5/12 grid gives the column real width again. */}
- Produktivität darf
sich leicht anfühlen
+ Produktivität darf
sich leicht anfühlen
{/* Brand's signature orange dot (also in the logo/footer) —
bouncy pop-in once the heading scrolls into view, timed to
@@ -70,10 +73,10 @@ export function Hero() {
- {/* Subheading — smaller fixed size below lg:, text-h-emphasis's
+ {/* Subheading — smaller fixed size below md:, text-h-emphasis's
own 20px floor read too large next to the now-smaller mobile
CTA text. leading shrinks to match, not just font-size. */}
-
+
Für Menschen mit Familie, Verantwortung und zu wenig Zeit
@@ -82,28 +85,28 @@ export function Hero() {
href="/challenge"
className="flex gap-4 items-center justify-center overflow-clip px-6 py-3 rounded-sm shrink-0 max-w-full bg-brand hover:brightness-95 active:scale-[0.97] transition-all"
>
- {/* Letting this wrap to two lines below lg: (tried 2026-07-24)
+ {/* Letting this wrap to two lines below md: (tried 2026-07-24)
put the icon beside a two-line text block, which read as
broken rather than intentional. Smaller fixed size below
- lg: instead, so the full phrase fits on one line within
+ md: instead, so the full phrase fits on one line within
the stacked mobile column's width — text-h3's own 19px
floor was still too wide for that at ~236px of available
text budget on a 375px phone. */}
-
+
Starte mit der 7-Tage-Challenge
{/* Scaled down to match the smaller mobile CTA text (same
- ~0.76 aspect ratio as the lg: size), full size again from
- lg: up alongside text-h3. */}
-
-
+ ~0.76 aspect ratio as the md: size), full size again from
+ md: up alongside text-h3. */}
+
+
{/* Social proof — always avatars-then-text on two lines below
- lg: (not just when it happens to overflow), single row again
- from lg: up where the real column width fits it fine. */}
-
+ md: (not just when it happens to overflow), single row again
+ from md: up where the real column width fits it fine. */}
+
{/* Avatars — gap 2px, not overlapping */}
{["/avatar-1.jpg", "/avatar-2.jpg", "/avatar-3.jpg"].map((src, i) => (
@@ -123,33 +126,29 @@ export function Hero() {
{/* Image — bleeds to the true edge at every breakpoint (never
- padded). Below lg: (stacked layout) the full 887:583 aspect
+ padded). Below md: (stacked layout) the full 887:583 aspect
ratio at 100vw would make the image ~600-900px tall and
dominate the page, so height is capped and object-cover crops
- it into a supporting banner instead; at lg:+ (grid, image only
+ it into a supporting banner instead; at md:+ (grid, image only
58% width) the full aspect ratio looks right again, so the cap
- is lifted. A small negative top margin below lg: pulls it up to
- slightly tuck under the text block (deliberately less than the
- social-proof row's height, so it never covers the avatars/text).
- Scoped to md:-only (mt-0 at base and again at lg:) — it's a
- Tablet-specific touch, not a permanent effect. No shadow: a
- plain box-shadow reads as a hard rectangular edge against the
- existing corner/right/bottom mask-gradient fade below, which
- looked worse than no shadow at all — tried and reverted. */}
- {/* No Reveal (fade-in-on-scroll) below lg: — whileInView's -80px
+ is lifted. No shadow: a plain box-shadow reads as a hard
+ rectangular edge against the existing corner/right/bottom
+ mask-gradient fade below, which looked worse than no shadow at
+ all — tried and reverted. */}
+ {/* No Reveal (fade-in-on-scroll) below md: — whileInView's -80px
viewport margin means the image doesn't fade in until scrolled
that much further into view; on a short mobile viewport this
image sits right at the initial fold, so it stayed at
opacity:0 (a white gap, matching the section's own bg-bg-base)
above the fold until the user scrolled (reported 2026-07-24).
- Plain, always-visible image below lg: instead; Reveal's fade
- kept from lg: up, where the image is beside the text with
+ Plain, always-visible image below md: instead; Reveal's fade
+ kept from md: up, where the image is beside the text with
plenty of room and this was never an issue. */}
-
+
diff --git a/app/components/Newsletter.tsx b/app/components/Newsletter.tsx
index 810dea0..2e83b4f 100644
--- a/app/components/Newsletter.tsx
+++ b/app/components/Newsletter.tsx
@@ -98,8 +98,14 @@ export function Newsletter({
) : (