diff --git a/public/zertifikate/greator-siegel.webp b/public/zertifikate/greator-siegel.webp
new file mode 100644
index 0000000..1623100
Binary files /dev/null and b/public/zertifikate/greator-siegel.webp differ
diff --git a/public/zertifikate/greator-speaker.jpg b/public/zertifikate/greator-speaker.jpg
new file mode 100644
index 0000000..192acaa
Binary files /dev/null and b/public/zertifikate/greator-speaker.jpg differ
diff --git a/public/zertifikate/greator-speaker.pdf b/public/zertifikate/greator-speaker.pdf
new file mode 100644
index 0000000..b3912ef
Binary files /dev/null and b/public/zertifikate/greator-speaker.pdf differ
diff --git a/src/components/home/About.tsx b/src/components/home/About.tsx
index bcfe24d..e338ae5 100644
--- a/src/components/home/About.tsx
+++ b/src/components/home/About.tsx
@@ -1,5 +1,6 @@
import { cn } from "@/lib/utils";
import { flags } from "@/lib/site";
+import type { CertificateAsset } from "@/lib/assets";
import { about as aboutDe, type HomeContent } from "@/content/home";
import { Section } from "@/components/layout/Section";
import { Lines } from "@/components/motion/Lines";
@@ -9,12 +10,15 @@ import { Eyebrow } from "@/components/ui/Eyebrow";
import { Ledger } from "@/components/ui/Ledger";
import { Marginalia } from "@/components/ui/Marginalia";
import { Portrait } from "@/components/ui/Portrait";
+import { Certificate } from "@/components/ui/Certificate";
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"];
+ /** `getCertificate()` – `null` lässt die Sektion wie vorher rendern. */
+ certificate?: CertificateAsset | null;
}
/* H2 on ivory: Playfair 400, max 14ch (spec §3.2 / §6.5). */
@@ -29,11 +33,13 @@ const BODY = "max-w-[36rem] font-sans text-[1.125rem] leading-[1.6] text-fg [hyp
* in view while reading, the Werdegang as a ledger, marginalia with
* verifiable facts. No counters, no logos.
*
- * With photo: Portrait cols 3–6 (sticky) · text + ledger cols 7–12 · marginalia cols 10–12.
- * Without: text cols 3–8 · ledger cols 3–9 · marginalia under the ledger
- * (no monogram vignette – see below).
+ * With photo: Portrait cols 3–6 (sticky) · text + ledger cols 7–12 · marginalia and
+ * certificate stacked under the ledger (cols 7–12) – the text already
+ * owns the outer columns there, so nothing can stick to the right edge.
+ * Without: text cols 3–8 · ledger cols 3–9 · marginalia under the ledger ·
+ * certificate sticky in cols 10–12 (no monogram vignette – see below).
*/
-export function About({ photo, content: about = aboutDe }: AboutProps) {
+export function About({ photo, content: about = aboutDe, certificate = null }: AboutProps) {
const paragraphs = about.paragraphs.map((p, i) =>
i === about.paragraphs.length - 1 && flags.showTwins ? `${p} ${about.twins}` : p,
);
@@ -92,11 +98,30 @@ export function About({ photo, content: about = aboutDe }: AboutProps) {
+
+ {/* Das Zertifikat ist das Gegenstück zur klebenden „04“: gleiche Höhe
+ (`calc(50vh − .45em)`), gleiche Zeilen, nur am anderen Rand. Mit Foto
+ reicht der Text bis Spalte 12 – dort gäbe es keine freie Gutter, also
+ steht es dann still unter dem Werdegang. */}
+ {certificate && (
+
+
+
+
+
+ )}
);
diff --git a/src/components/pages/HomeBody.tsx b/src/components/pages/HomeBody.tsx
index 8794059..6f9177b 100644
--- a/src/components/pages/HomeBody.tsx
+++ b/src/components/pages/HomeBody.tsx
@@ -7,7 +7,7 @@ import { About } from "@/components/home/About";
import { Audience } from "@/components/home/Audience";
import { ProcessContact } from "@/components/home/ProcessContact";
import type { HomeContent } from "@/content/home";
-import { getPhotos } from "@/lib/assets";
+import { getCertificate, getPhotos } from "@/lib/assets";
import { routesByLang, type Lang } from "@/lib/i18n";
import { rootCopy } from "@/lib/metadata";
import { site } from "@/lib/site";
@@ -37,7 +37,7 @@ export function HomeBody({ lang, content }: { lang: Lang; content: HomeContent }
} content={content.principle} />
-
+
>
diff --git a/src/components/ui/Certificate.tsx b/src/components/ui/Certificate.tsx
new file mode 100644
index 0000000..2429c59
--- /dev/null
+++ b/src/components/ui/Certificate.tsx
@@ -0,0 +1,97 @@
+"use client";
+
+import { useState, type MouseEvent } from "react";
+import { cn } from "@/lib/utils";
+import type { CertificateAsset } from "@/lib/assets";
+import type { CertificateCopy } from "@/content/home";
+import { LaurelWreath } from "./LaurelWreath";
+import { CertificateDialog } from "./CertificateDialog";
+
+export interface CertificateProps {
+ content: CertificateCopy;
+ asset: CertificateAsset;
+ className?: string;
+}
+
+/**
+ * Das Zertifikats-Abzeichen der Sektion „Über Anelia“ – Lorbeerkranz in Gold,
+ * darunter Haarlinie, Beschreibung, Datum. Kein Fettdruck, Radius 0, kein
+ * Schatten; die Display-Schrift kommt hier gar nicht vor, weil sie unter 28 px
+ * nicht gesetzt werden darf und in diese Spalte nichts Größeres passt.
+ *
+ * Ausgeliefert wird ein echter Link auf das PDF: ohne JavaScript, beim
+ * Rechtsklick und beim Öffnen im neuen Tab landet man beim Dokument. Erst das
+ * JS wertet den Klick zum Overlay auf.
+ */
+export function Certificate({ content, asset, className }: CertificateProps) {
+ const [open, setOpen] = useState(false);
+
+ const onClick = (e: MouseEvent) => {
+ /* Neues Fenster/Tab bleibt neues Fenster/Tab – nur der einfache Klick
+ wird abgefangen. */
+ if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey || e.button !== 0) return;
+ e.preventDefault();
+ setOpen(true);
+ };
+
+ return (
+
+ );
+}
+
+export default Certificate;
diff --git a/src/components/ui/CertificateDialog.tsx b/src/components/ui/CertificateDialog.tsx
new file mode 100644
index 0000000..e1904aa
--- /dev/null
+++ b/src/components/ui/CertificateDialog.tsx
@@ -0,0 +1,204 @@
+"use client";
+
+import { useEffect, useLayoutEffect, useRef, useState } from "react";
+import { createPortal } from "react-dom";
+import type { CertificateAsset } from "@/lib/assets";
+import type { CertificateCopy } from "@/content/home";
+import { gsap, DUR, EASE, MM, getLenis, refreshSoon } from "@/lib/gsap";
+
+/** Die Überschrift benennt den Dialog (`aria-labelledby`) – es gibt genau einen. */
+const TITLE_ID = "zertifikat-titel";
+
+/**
+ * Kopf, Blatt und Fußzeile teilen sich eine Breite, sonst stehen sie versetzt.
+ * Auf dem Desktop richtet sie sich nach der Höhe: das Blatt soll ganz ins Bild
+ * passen (0,706 = 989 ÷ 1400, das Seitenverhältnis des Scans).
+ */
+const COLUMN = "w-full max-w-[46rem] lg:max-w-[min(46rem,calc((100svh-16rem)*0.706))]";
+
+export interface CertificateDialogProps {
+ open: boolean;
+ onClose: () => void;
+ content: CertificateCopy;
+ asset: CertificateAsset;
+}
+
+/**
+ * Vorschau des Zertifikats: dunkler Grund, Seite 1 als Bild, darunter der Weg
+ * zum PDF. Bewusst ein Bild und kein eingebettetes PDF – ein `