first publish

This commit is contained in:
knso committed 2026-09-06 17:21:52 +02:00
commit b8e41b4d55
147 files changed
+19367

No files matched your search

+117
View File
@@ -0,0 +1,117 @@
import { cn } from "@/lib/utils";
import { Container } from "@/components/layout/Container";
import { Folio } from "@/components/layout/Folio";
import { GRID } from "@/components/layout/Section";
import { Lines } from "@/components/motion/Lines";
import { Reveal } from "@/components/motion/Reveal";
import type { Cta, FactItem, Headline } from "@/content/home";
import { Button } from "@/components/ui/Button";
import { Facts } from "@/components/ui/Facts";
import { Portrait } from "@/components/ui/Portrait";
/** The slice of `coaching` / `trainings` / `keynotes` the hero needs (structural). */
export interface ServiceHeroContent {
/** „01“ / „02“ / „03“ – mono, cols 1–2. */
numeral: string;
masthead: { center: string };
eyebrow: string;
h1: Headline;
/** The italic word may also carry `.gold-text` (keynotes – the page's one gradient word). */
h1GoldEm?: boolean;
subline: string;
cta: Cta;
facts: readonly FactItem[];
/** No fraction here – the page's own section counter is the only counter (see <Folio/>). */
folio: { next?: { label: string; href: string } };
/** Photo alt / caption (keynote stage photo). */
alt?: string;
caption?: string;
}
export interface ServiceHeroProps {
content: ServiceHeroContent;
/** Stage photo (keynotes) – `null`/undefined = no photo. */
photo?: string | null;
className?: string;
}
/**
* Service-page hero (ink, ≥ 60svh): mono numeral, H1 with one italic word,
* subline, the page's primary CTA, facts stack top-right, folio with the next
* section. Header monogram only – no hero draw.
*/
export function ServiceHero({ content, photo, className }: ServiceHeroProps) {
/* `masthead` und `eyebrow` stehen weiter in den Inhaltsdateien, werden aber seit
dem 06.09.2026 nicht mehr gerendert (Kundenwunsch): die Kopfzeile trug dieselben
Angaben doppelt, und über der H1 stand faktisch die Überschrift ein zweites Mal. */
const { numeral, masthead, h1, h1GoldEm, subline, cta, facts, folio, alt, caption } = content;
const hasPhoto = Boolean(photo);
return (
<section
data-theme="dark"
className={cn(
"relative flex min-h-[60svh] flex-col bg-transparent pt-[calc(var(--header-h,7.5rem)+1.5rem)] text-fg",
className,
)}
>
<Container className="flex flex-1 flex-col">
<div className={cn(GRID, "flex-1 gap-y-12 py-14 lg:py-20")}>
<Reveal
play="load"
delay={0.3}
as="p"
className="hidden font-mono text-xs tracking-[0.06em] tabular-nums text-fg-muted lg:col-span-2 lg:block lg:pt-1"
>
{numeral}
</Reveal>
<div className={cn("col-span-12 flex flex-col lg:col-start-3", hasPhoto ? "lg:col-span-5" : "lg:col-span-7")}>
<Lines
as="h1"
play="load"
delay={0.35}
className="max-w-[14ch] font-display text-[clamp(2.75rem,1.4rem+5.4vw,7rem)] font-normal leading-[0.96] tracking-[-0.015em] text-ivory [text-wrap:balance]"
>
{h1.before}
{h1.em && <em className={cn("italic", h1GoldEm && "gold-text")}>{h1.em}</em>}
{h1.after}
</Lines>
<Reveal play="load" delay={1.0} as="p" className="mt-10 max-w-[40ch] font-sans text-xl font-light leading-[1.45] text-fg-muted">
{subline}
</Reveal>
<Reveal play="load" delay={1.2} className="mt-8">
<Button {...cta} />
</Reveal>
</div>
<div
className={cn(
"col-span-12 flex flex-col gap-10",
hasPhoto ? "lg:col-span-5 lg:col-start-8" : "lg:col-span-3 lg:col-start-10",
)}
>
<Reveal play="load" delay={0.6} className={cn(hasPhoto && "lg:self-end")}>
<Facts items={facts} />
</Reveal>
{photo && (
<Portrait
src={photo}
alt={alt ?? masthead.center}
aspect="16/10"
caption={caption}
priority
frame={false}
sizes="(min-width: 1024px) 40vw, 100vw"
/>
)}
</div>
</div>
<Folio next={folio.next} play="load" delay={1.3} />
</Container>
</section>
);
}
export default ServiceHero;