Files
einfach-produktiv/app/lebensuhr/page.tsx
T
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

410 lines
20 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import Link from "next/link";
import { Reveal, RevealGroup, RevealItem } from "../components/Reveal";
import { Footer } from "../components/Footer";
import { StepArrow } from "../components/StepArrow";
const title = "Die Lebensuhr: Wie spät ist es in deinem Leben?";
const description =
"Wie spät ist es eigentlich in deinem Leben? Die Lebensuhr zeigt dir, wo du gerade stehst und was in dieser Phase wirklich zählt.";
export const metadata: Metadata = {
title,
description,
alternates: { canonical: "/lebensuhr" },
openGraph: {
title,
description,
url: "/lebensuhr",
type: "article",
},
twitter: {
title,
description,
},
};
// Same "H2 with brand underline" treatment RichText.tsx's heading
// converter uses for CMS-driven content — this page is hand-written (no
// Payload post backs it, it lives at the plain /lebensuhr slug, not
// /blog/…), so the styling is copied by hand instead of shared.
function H2({ children }: { children: React.ReactNode }) {
return (
<h2
className="font-semibold text-h-small text-text-primary mt-4 first:mt-0"
style={{ fontFamily: "var(--font-lora)" }}
>
{children}
<span className="block h-[0.125rem] w-8 bg-brand mt-2" aria-hidden />
</h2>
);
}
function P({ children }: { children: React.ReactNode }) {
return <p className="text-body text-text-body">{children}</p>;
}
// Same visual language as RichText.tsx's Quote component (Caveat script +
// brand divider) — copied rather than imported since that component is
// tied to the CMS quoteLabel prop this static page has no use for.
function Quote({ children }: { children: React.ReactNode }) {
return (
<div className="relative flex items-start gap-6 w-full my-2">
<div className="w-px self-stretch bg-brand shrink-0" />
<p className="text-text-primary text-[1.75rem] leading-[1.1] flex-1" style={{ fontFamily: "var(--font-caveat)" }}>
{children}
</p>
</div>
);
}
// Same "icon-above-text, StepArrow-connected" row /7-tage-klarheits-check's
// "So funktioniert" section and /todo-cards's/newsletter's own "How it
// works" sections use — reused here for the four life phases instead of
// stacking them as plain H3 blocks.
function IconSunrise() {
return (
<svg width="44" height="40" viewBox="0 0 44 40" fill="none">
<line x1="4" y1="30" x2="40" y2="30" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<path d="M13 30a9 9 0 0 1 18 0" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="22" y1="4" x2="22" y2="10" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="9" y1="10" x2="13" y2="14" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="35" y1="10" x2="31" y2="14" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}
function IconGrowthBars() {
return (
<svg width="44" height="40" viewBox="0 0 44 40" fill="none">
<line x1="4" y1="36" x2="40" y2="36" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<rect x="7" y="25" width="7" height="11" rx="1" stroke="#f6a701" strokeWidth="2" />
<rect x="18.5" y="16" width="7" height="20" rx="1" stroke="#f6a701" strokeWidth="2" />
<rect x="30" y="6" width="7" height="30" rx="1" stroke="#f6a701" strokeWidth="2" />
</svg>
);
}
function IconSunHigh() {
return (
<svg width="40" height="40" viewBox="0 0 40 40" fill="none">
<circle cx="20" cy="20" r="8" stroke="#f6a701" strokeWidth="2" />
<line x1="20" y1="2" x2="20" y2="7" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="20" y1="33" x2="20" y2="38" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="2" y1="20" x2="7" y2="20" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="33" y1="20" x2="38" y2="20" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="7" y1="7" x2="10.5" y2="10.5" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="29.5" y1="29.5" x2="33" y2="33" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="33" y1="7" x2="29.5" y2="10.5" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
<line x1="10.5" y1="29.5" x2="7" y2="33" stroke="#f6a701" strokeWidth="2" strokeLinecap="round" />
</svg>
);
}
// Same checkmark used by /7-tage-klarheits-check's "Das erwartet dich"
// checklist.
function Check() {
return (
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" className="shrink-0 mt-1">
<path d="M3 9.5l4 4L15 4" stroke="#f6a701" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
);
}
// Age boundaries — same 90-year scale as lebensuhrRows below (age/90 =
// time/24), picked so "Vormittag" ends exactly at 12:00 (age 45), which
// also happens to line up with "Mittag" as the literal midpoint between
// Vormittag and Nachmittag. Independent of lebensuhrRows (different
// breakpoints), not derived from it.
const phases = [
{
icon: <IconSunrise />,
title: "Der Morgen: Ausprobieren",
ageRange: "ca. 025 Jahre",
desc: "Man probiert aus, lernt, macht Fehler, entdeckt Interessen. Man muss noch nicht alles perfekt können.",
},
{
icon: <IconGrowthBars />,
title: "Der Vormittag: Aufbauen",
ageRange: "ca. 2545 Jahre",
desc: "Jetzt wird gebaut Gewohnheiten, Beziehungen, Fähigkeiten. Ein Leben, das sich nach dem eigenen anfühlt.",
},
{
icon: <IconSunHigh />,
title: "Der Nachmittag: Stärken",
ageRange: "ca. 4565 Jahre",
desc: "Nicht alles neu, sondern das Bestehende wird stärker: Erfahrung zahlt sich aus, Entscheidungen fallen leichter.",
},
{
// Same icon as the morning — a rising and a setting sun both show as
// the same arc-above-the-horizon silhouette, rays above it.
icon: <IconSunrise />,
title: "Der Abend: Weitergeben",
ageRange: "ca. 6590 Jahre",
desc: "Weniger Wachstum, mehr Bedeutung weiterzugeben, was man aufgebaut hat.",
},
];
const questions = [
"Wie spät ist es gerade in meinem Leben?",
"Was passt eigentlich zu dieser Tageszeit?",
"Und welche Aufgabe schleppe ich noch mit mir herum, obwohl sie zu einer früheren Stunde gehört?",
];
const lebensuhrRows: [string, string][] = [
["10", "02:40 Uhr"],
["20", "05:20 Uhr"],
["30", "08:00 Uhr"],
["40", "10:40 Uhr"],
["50", "13:20 Uhr"],
["60", "16:00 Uhr"],
["70", "18:40 Uhr"],
["80", "21:20 Uhr"],
];
export default function LebensuhrPage() {
return (
<>
<main className="flex flex-col flex-1 bg-bg-base">
<Reveal className="flex flex-col gap-4 items-start pt-10 pb-8 px-[var(--layout-padding-x)] w-full max-w-[48rem] mx-auto">
<p className="flex items-center gap-2 text-body-sm text-text-muted">
<Link href="/" className="hover:text-brand transition-colors">Startseite</Link>
<span></span>
<span className="text-text-primary">Lebensuhr</span>
</p>
<p
className="font-semibold text-[clamp(2.25rem,1.393rem+1.786vw,3rem)] text-text-primary leading-[1.15]"
style={{ fontFamily: "var(--font-playfair)" }}
>
Die Lebensuhr: Wie spät ist es eigentlich in deinem Leben?
</p>
</Reveal>
<Reveal delay={0.1} className="w-full max-w-[48rem] mx-auto px-[var(--layout-padding-x)] pb-14 flex flex-col gap-5">
<P>Wie spät ist es eigentlich in deinem Leben?</P>
<P>Keine philosophische Frage. Eine überraschend praktische.</P>
<P>
Wir planen unsere Tage bis auf die Stunde. Wir blocken Meetings, setzen Erinnerungen und
schreiben To-do-Listen. Aber kaum jemand weiß, wie er seine aktuelle Lebensphase eigentlich
nutzen möchte.
</P>
<H2>Die Lebensuhr: Dein Leben in 24 Stunden</H2>
<P>
Stell dir vor, dein ganzes Leben wäre auf einen einzigen Tag mit 24 Stunden komprimiert.
Wirst du 90 Jahre alt, sieht das ungefähr so aus:
</P>
{/* Decorative companion to the table below, not a replacement for
it — a real 12-hour dial (112, like an actual clock face)
with the 8 ages plotted at their 12-hour position. Only the
age is labeled here (a version that also spelled out
time + vorm./nachm. on every point — 3 lines × 8 points —
read as clutter); the exact time for each age is one glance
down, in the table. Coordinates are hand-computed from
lebensuhrRows's own age→time pairs (hour mod 12, -90° so 12
sits at the top) — if those rows ever change, these need
updating too. */}
<div className="flex justify-center py-2">
<svg viewBox="-10 -10 440 440" className="w-full max-w-[22rem] h-auto" aria-hidden="true">
<circle cx="210" cy="210" r="150" stroke="var(--color-border)" strokeWidth="1.5" fill="none" />
{[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11].map((h) => {
const deg = (h / 12) * 360 - 90;
const rad = (deg * Math.PI) / 180;
const major = h % 3 === 0;
const rIn = major ? 140 : 145;
const x1 = 210 + rIn * Math.cos(rad), y1 = 210 + rIn * Math.sin(rad);
const x2 = 210 + 150 * Math.cos(rad), y2 = 210 + 150 * Math.sin(rad);
return <line key={h} x1={x1} y1={y1} x2={x2} y2={y2} stroke="var(--color-border)" strokeWidth={major ? 2 : 1.5} />;
})}
{/* The dial's own numerals, 112, same as a real clock face. */}
{([
[1, 292.5, 67.1, "start"],
[2, 352.9, 127.5, "start"],
[3, 375, 210, "start"],
[4, 352.9, 292.5, "start"],
[5, 292.5, 352.9, "start"],
[6, 210, 375, "middle"],
[7, 127.5, 352.9, "end"],
[8, 67.1, 292.5, "end"],
[9, 45, 210, "end"],
[10, 67.1, 127.5, "end"],
[11, 127.5, 67.1, "end"],
[12, 210, 45, "middle"],
] as [number, number, number, "start" | "middle" | "end"][]).map(([h, x, y, anchor]) => (
<text key={h} x={x} y={y} textAnchor={anchor} dominantBaseline="middle" className="fill-text-muted" style={{ fontSize: 12, fontWeight: 600 }}>
{h}
</text>
))}
{/* One dot per lebensuhrRows entry, at its 12h-dial position
(hour mod 12), labeled with just the age. */}
{([
[10, 308.5, 192.6, 332.1, 188.5, "start"],
[20, 244.2, 304.0, 252.4, 326.5, "start"],
[30, 123.4, 260.0, 102.6, 272.0, "end"],
[40, 145.7, 133.4, 130.3, 115.0, "end"],
[50, 274.3, 133.4, 289.7, 115.0, "start"],
[60, 296.6, 260.0, 317.4, 272.0, "start"],
[70, 175.8, 304.0, 167.6, 326.5, "end"],
[80, 111.5, 192.6, 87.9, 188.5, "end"],
] as [number, number, number, number, number, "start" | "end"][]).map(([age, dx, dy, lx, ly, anchor]) => (
<g key={age}>
<circle cx={dx} cy={dy} r="5" fill="var(--color-brand)" />
<text x={lx} y={ly} textAnchor={anchor} dominantBaseline="middle" className="fill-text-primary" style={{ fontSize: 15, fontWeight: 700 }}>
{age}
</text>
</g>
))}
<text x="210" y="196" textAnchor="middle" className="fill-text-primary" style={{ fontFamily: "var(--font-caveat)", fontSize: 30 }}>
dein
</text>
<text x="210" y="228" textAnchor="middle" className="fill-text-primary" style={{ fontFamily: "var(--font-caveat)", fontSize: 30 }}>
Leben
</text>
</svg>
</div>
<p className="text-center text-body-sm text-text-muted -mt-1">Dein Alter auf dem Zifferblatt. Die genaue Uhrzeit dazu steht unten.</p>
{/* Still a real table (explicit "Alter"/"Uhrzeit" headers — a
dot-on-a-line version tried here first read ambiguous
without them, and forced a horizontal scroll on mobile for
no gain). Dressed up with the same bg-bg-muted rounded card
the AGB sidebar and blog author box use, brand-colored
header, and zebra rows instead of a bare bordered table. */}
<div className="bg-bg-muted rounded-xl overflow-hidden">
<table className="w-full text-left border-collapse">
<thead>
<tr className="border-b-2 border-brand">
<th className="py-3 pl-6 pr-4 font-semibold text-body-sm text-text-primary uppercase tracking-wide">Alter</th>
<th className="py-3 pr-6 font-semibold text-body-sm text-text-primary uppercase tracking-wide">Uhrzeit</th>
</tr>
</thead>
<tbody>
{lebensuhrRows.map(([alter, uhrzeit], i) => (
<tr key={alter} className={i % 2 === 1 ? "bg-bg-base/60" : undefined}>
<td className="py-2.5 pl-6 pr-4 text-body text-text-body">{alter}</td>
<td className="py-2.5 pr-6 font-semibold text-body text-text-primary">{uhrzeit}</td>
</tr>
))}
</tbody>
</table>
</div>
<P>Und? Wie spät ist es gerade bei dir?</P>
<H2>Warum die Lebensuhr mehr zeigt als dein Alter</H2>
<P>
Viele Menschen haben gar nicht das Gefühl, zu wenig Zeit zu haben. Sie haben das Gefühl,
nicht dort zu sein, wo sie sein wollten. Zwei verschiedene Dinge. Man kann morgens schon
erschöpft sein oder abends voller Energie die Uhr sagt nichts darüber, wie du dich fühlst,
nur wie viel Tag noch vor dir liegt.
</P>
<P>Die spannendere Frage lautet deshalb: Lebst du gerade so, wie es zu deiner Tageszeit passt?</P>
<P>
Die Lebensuhr ist dabei kein Fahrplan. Sie schreibt dir nichts vor, nur weil du an einem
bestimmten Punkt stehst manche fangen mit 45 nochmal komplett neu an, manche bauen mit 60
ihr erstes eigenes Ding auf. Sie zeigt eine Tendenz, keine Vorschrift, und sie bewertet auch
nicht, ob du "weiter" bist als andere. Sie zeigt nur, welche Art von Aufgabe in deiner
aktuellen Phase gut zu dir passen könnte. Ein Vormittag ist schließlich auch nicht besser als
ein Abend nur anders.
</P>
<H2>Was zu welcher Lebensphase passt</H2>
<P>
Diese Zuordnungen halten sich nicht strikt an die Uhr manchmal probiert man am Nachmittag
nochmal etwas komplett Neues aus, manchmal gibt man schon morgens etwas weiter. Sie sind ein
Muster, kein Gesetz: Nimm dir davon, was zu deiner Situation passt.
</P>
</Reveal>
{/* Full-width StepArrow row — same pattern as /7-tage-klarheits-check's
"So funktioniert" section, reused here for the four life phases
instead of stacking them as plain headings. */}
<section className="bg-bg-muted w-full py-10 lg:py-14">
<div className="px-8 lg:px-[5rem] max-w-[1280px] mx-auto">
<RevealGroup className="flex flex-col lg:flex-row items-center lg:items-start gap-8 lg:gap-2 w-full">
{phases.flatMap((phase, i) => [
<RevealItem key={phase.title} className="group flex flex-col items-center gap-4 lg:gap-5 flex-1 min-w-0">
<div className="flex items-center justify-center w-16 h-14 shrink-0 transition-transform duration-300 group-hover:scale-110">
{phase.icon}
</div>
<div className="flex flex-col gap-1 text-center">
<p className="font-semibold text-text-primary text-[1rem]">{phase.title}</p>
<p className="font-semibold text-[0.75rem] text-brand">{phase.ageRange}</p>
<p className="text-[0.875rem] text-text-muted leading-[1.5]">{phase.desc}</p>
</div>
</RevealItem>,
i < phases.length - 1 ? (
<div key={`arrow-${i}`} className="flex items-center justify-center shrink-0 lg:mt-5">
<StepArrow className="w-8 h-8 rotate-90 lg:w-10 lg:h-4 lg:rotate-0" />
</div>
) : null,
])}
</RevealGroup>
</div>
</section>
<Reveal delay={0.1} className="w-full max-w-[48rem] mx-auto px-[var(--layout-padding-x)] pt-10 pb-14 flex flex-col gap-5">
<Quote>
Wer am Nachmittag immer wieder bei null anfängt, verschenkt oft das Wertvollste, was er
besitzt den Zinseszins des eigenen Lebens.
</Quote>
<H2>Produktivität beginnt mit der richtigen Lebensphase</H2>
<P>
Wir behandeln oft jede Lebensphase gleich immer schneller, immer mehr, immer weiter. Dabei
erwarten wir von einem Abend dieselbe Dynamik wie von einem frühen Morgen. Das funktioniert
weder an einem einzigen Tag noch in einem ganzen Leben.
</P>
<P>
Produktivität bedeutet für mich deshalb etwas anderes: das Richtige zur richtigen Zeit zu
tun. Nicht ständig neu anzufangen, wenn es Zeit wäre, etwas wachsen zu lassen. Nicht
krampfhaft festzuhalten, wenn es Zeit wäre, loszulassen. Und nicht auf "den richtigen
Moment" warten du lebst bereits in ihm.
</P>
<H2>Drei Fragen für deine persönliche Lebensuhr</H2>
<P>Schau heute Abend noch mal auf deine Uhr. Nicht die am Handgelenk deine Lebensuhr.</P>
<ul className="flex flex-col gap-3">
{questions.map((q) => (
<li key={q} className="flex items-start gap-3">
<Check />
<p className="text-body text-text-body">{q}</p>
</li>
))}
</ul>
<P>
Vielleicht zeigt dir die Lebensuhr heute nicht, dass du dich beeilen musst sondern dass
genau jetzt die richtige Zeit für das ist, was vor dir liegt. Manchmal reicht das schon, um
den nächsten Schritt klarer zu gehen.
</P>
{/* Same bordered CTA-card treatment as the blog's "Passend dazu"
link — more click surface and visibility than a plain inline
text link. */}
<Link
href="/7-tage-klarheits-check"
className="group flex items-center justify-between gap-4 border border-border rounded-md px-6 py-5 mt-2 hover:border-brand transition-colors"
>
<div className="flex flex-col gap-1">
<p className="font-bold text-[0.8125rem] text-brand">Nächster Schritt:</p>
<p className="font-semibold text-body text-text-primary" style={{ fontFamily: "var(--font-lora)" }}>
7-Tage-Klarheits-Check
</p>
<p className="text-body-sm text-text-muted">Ein ruhiger Einstieg, sieben Tage lang.</p>
</div>
<svg
viewBox="0 0 20 20"
className="size-4 shrink-0 text-text-primary transition-transform duration-200 group-hover:translate-x-1"
fill="none"
aria-hidden="true"
>
<path d="M4 10h12m0 0-5-5m5 5-5 5" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</Link>
</Reveal>
</main>
<Footer />
</>
);
}