Files
Anelia-Wolf/src/components/home/About.tsx
2026-09-06 17:21:52 +02:00

106 lines
4.2 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 { cn } from "@/lib/utils";
import { flags } from "@/lib/site";
import { about as aboutDe, type HomeContent } from "@/content/home";
import { Section } from "@/components/layout/Section";
import { Lines } from "@/components/motion/Lines";
import { Reveal } from "@/components/motion/Reveal";
import { Rule } from "@/components/motion/Rule";
import { Eyebrow } from "@/components/ui/Eyebrow";
import { Ledger } from "@/components/ui/Ledger";
import { Marginalia } from "@/components/ui/Marginalia";
import { Portrait } from "@/components/ui/Portrait";
export interface AboutProps {
/** `getPhotos().portrait` `null` renders the documented variant B (no photo). */
photo: string | null;
/** Inhalt der Sektion. Ohne Angabe die deutsche Fassung die englische reicht sie durch. */
content?: HomeContent["about"];
}
/* H2 on ivory: Playfair 400, max 14ch (spec §3.2 / §6.5). */
const H2 =
"font-display text-[clamp(2.25rem,1.4rem+3.2vw,4.75rem)] font-normal leading-[1.02] tracking-[-0.01em] text-fg [text-wrap:balance] max-w-[14ch]";
/* Body: Jost 400, 18 px, max 36rem, paragraph gap 1.25em, German hyphenation. */
const BODY = "max-w-[36rem] font-sans text-[1.125rem] leading-[1.6] text-fg [hyphens:auto]";
/**
* 04 · Über Anelia (ivory spec §6.5). First person, a portrait that stays
* in view while reading, the Werdegang as a ledger, marginalia with
* verifiable facts. No counters, no logos.
*
* With photo: Portrait cols 36 (sticky) · text + ledger cols 712 · marginalia cols 1012.
* Without: text cols 38 · ledger cols 39 · marginalia under the ledger
* (no monogram vignette see below).
*/
export function About({ photo, content: about = aboutDe }: AboutProps) {
const paragraphs = about.paragraphs.map((p, i) =>
i === about.paragraphs.length - 1 && flags.showTwins ? `${p} ${about.twins}` : p,
);
const hasPhoto = Boolean(photo);
return (
<div data-theme="light" className="relative bg-transparent text-fg">
<Rule variant="gold" />
<Section
id={about.id}
theme="light"
numeral={about.numeral}
label={about.eyebrow}
className="py-[clamp(4.5rem,10vh,8rem)] md:py-[clamp(7rem,16vh,14rem)]"
>
{hasPhoto && (
<div className="col-span-12 mb-14 lg:col-span-4 lg:col-start-3 lg:mb-0">
<Portrait
src={photo}
alt={about.alt}
aspect="4/5"
caption={about.caption}
sticky
parallax
sizes="(min-width: 1024px) 30vw, 100vw"
className="w-full max-w-[48svh] lg:max-w-none"
/>
</div>
)}
<div className={cn("col-span-12 lg:row-start-1", hasPhoto ? "lg:col-span-6 lg:col-start-7" : "lg:col-span-6 lg:col-start-3")}>
<Eyebrow className="mb-8">{about.eyebrow}</Eyebrow>
<Lines as="h2" className={H2}>
{about.h2.before}
{about.h2.em && <em className="italic">{about.h2.em}</em>}
{about.h2.after}
</Lines>
<div className="mt-10 flex flex-col gap-[1.25em]">
{paragraphs.map((text, i) => (
<Reveal key={i} as="p" delay={i * 0.08} className={BODY}>
{text}
</Reveal>
))}
</div>
</div>
{/* Werdegang ledger: cols 712 beside the photo, cols 39 without. */}
<div className={cn("col-span-12 mt-12 lg:row-start-2 lg:mt-16", hasPhoto ? "lg:col-span-6 lg:col-start-7" : "lg:col-span-7 lg:col-start-3")}>
<Ledger variant="werdegang" rows={about.werdegang} stagger={0.08} />
</div>
{/* No monogram vignette in variant B: the WOLF-Prinzip closer directly
above already ends on a full-height mark, and section 04 now carries
the sticky Bodoni numeral it was missing a third W in the same
viewport turned the signature into wallpaper. */}
<Reveal
className={cn(
"col-span-12 mt-12",
hasPhoto ? "lg:col-span-3 lg:col-start-10 lg:row-start-2" : "lg:col-span-7 lg:col-start-3 lg:row-start-3",
)}
>
<Marginalia items={about.marginalia} />
</Reveal>
</Section>
</div>
);
}
export default About;