/* ═══════════════════════════════════════════════════════════
   BLACKCORE GROUP — Digitale Visitenkarte
   Dieselbe Sprache wie die Hauptseite: Papier statt Schwarz,
   Weight 400 durchgehend, Hierarchie über Größe und Versalien,
   Tracking negativ und mit der Größe wachsend.
   ═══════════════════════════════════════════════════════════ */

:root{
  /* Papier und Stein */
  --paper:#FFFFFF;
  --stone-1:#F0EFEE;
  --stone-2:#E6E4E2;
  --stone-3:#D2D2D2;

  /* Tinte */
  --ink:#0C0C0C;
  --ink-2:#55565A;
  --ink-3:#6E6F73;
  --line:rgba(12,12,12,.14);
  --line-2:rgba(12,12,12,.07);

  /* Auf dunklem Grund */
  --d-ink:#EDEDED;
  --d-ink-2:#9A958C;
  --d-line:rgba(255,255,255,.14);

  /* Champagner — die einzige Farbe */
  --acc:#C9B896;
  --acc-dark:#6E5F3E;
  --acc-soft:rgba(201,184,150,.16);

  --font:'Familjen Grotesk','Helvetica Neue',Arial,sans-serif;
  --mono:'Geist Mono','SFMono-Regular',Consolas,monospace;

  --ls-mega:-.08em;
  --ls-display:-.058em;
  --ls-lg:-.04em;
  --ls-md:-.028em;
  --ls-sm:-.02em;

  --e-out:cubic-bezier(.16,1,.3,1);
  --e-io:cubic-bezier(.76,0,.24,1);

  /* max() zieht den sicheren Bereich ein: ohne Aussparung sind die
     env()-Werte 0 und es bleibt beim clamp; im Querformat auf Geräten mit
     Aussparung wächst der Rand darauf an, statt Inhalt darunter zu schieben
     (das Dokument steht auf viewport-fit=cover). */
  --gut:max(clamp(18px,2.6vw,36px),env(safe-area-inset-left),env(safe-area-inset-right));
  --sec-y:clamp(72px,9vw,140px);

  /* Startwert für den Namen. Das Skript misst nach und füllt die
     Zeile exakt aus — ohne Skript bleibt dieser Wert stehen. */
  --name:clamp(52px,20vw,260px);
}

/* ── Schriften, selbst ausgeliefert ────────────────────────────
   Nicht ueber fonts.googleapis.com: die dynamische Einbindung
   uebertraegt die IP-Adresse der Besucher an Google und ist ohne
   Einwilligung abmahnbar (LG Muenchen I, 20.01.2022, 3 O 17493/20).
   Nebeneffekt: zwei Fremd-Roundtrips weniger im kritischen Pfad.
   Beide Familien stehen unter der SIL Open Font License 1.1,
   Eigenauslieferung ist ausdruecklich erlaubt. */
@font-face{
  font-family:'Familjen Grotesk';font-style:normal;font-weight:400 500;font-display:swap;
  src:url('../assets/fonts/familjen-grotesk-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'Familjen Grotesk';font-style:normal;font-weight:400 500;font-display:swap;
  src:url('../assets/fonts/familjen-grotesk-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face{
  font-family:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/geist-mono-latin-ext.woff2') format('woff2');
  unicode-range:U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face{
  font-family:'Geist Mono';font-style:normal;font-weight:400;font-display:swap;
  src:url('../assets/fonts/geist-mono-latin.woff2') format('woff2');
  unicode-range:U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ── Reset ────────────────────────────────────────────────── */
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
html{-webkit-text-size-adjust:100%;overflow-x:clip;scroll-behavior:smooth}
/* Scrollbalken aus — über einem davon erschiene der Systemzeiger */
*{scrollbar-width:none;-ms-overflow-style:none}
*::-webkit-scrollbar{width:0;height:0;display:none}
body{
  font-family:var(--font);font-weight:400;font-size:16px;line-height:1.5;
  letter-spacing:var(--ls-sm);
  color:var(--ink);background:var(--paper);
  overflow-x:clip;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
}
/* Ohne diesen Reset erben h1–h6/strong das bold des Browsers und
   brechen das System, das ausschließlich über Größe hierarchisiert. */
h1,h2,h3,h4,h5,h6,strong,b{font-weight:400}
img,svg{display:block;max-width:100%}
a{color:inherit;text-decoration:none}
ul,ol{list-style:none}
button,input,textarea{font:inherit;color:inherit;background:none;border:none;outline:none}
::selection{background:var(--ink);color:var(--paper)}
:focus-visible{outline:2px solid var(--acc-dark);outline-offset:3px}
.inv :focus-visible,.foot :focus-visible{outline-color:var(--acc)}

/* Nur für Vorlesesoftware */
.vh{
  position:absolute;width:1px;height:1px;overflow:hidden;
  clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;
}
.skip{
  position:fixed;top:10px;left:10px;z-index:9500;padding:12px 20px;
  background:var(--ink);color:var(--paper);font-size:13px;text-transform:uppercase;
  transform:translateY(-200%);transition:transform .3s var(--e-out);
}
.skip:focus{transform:none}

/* ── Custom cursor ────────────────────────────────────────────
   An .js gebunden: der Systemzeiger darf erst verschwinden, wenn der
   Ersatz auch wirklich gezeichnet wird. Ungated bliebe bei einem
   Skriptfehler überhaupt kein Zeiger übrig. */
@media (hover:hover) and (pointer:fine){
  .js,.js body,.js *,.js *::before,.js *::after{cursor:none !important}
  .js a,.js button,.js input,.js textarea,.js label,.js summary,
  .js [role="button"]{cursor:none !important}
}
@media (hover:none),(pointer:coarse){.cursor__body,.cursor__halo{display:none !important}}

/* Kein fixierter Wrapper: position:fixed erzeugt einen Stapelkontext und
   kapselt das Blending darin ein — der Zeiger mischte dann nur mit dem
   leeren Container. Die Mischung muss auf dem fixierten Element selbst sitzen. */
.cursor__body{
  position:fixed;top:0;left:0;z-index:9999;pointer-events:none;
  display:grid;place-items:center;
  width:34px;height:34px;margin:-17px 0 0 -17px;
  border:1.5px solid #fff;border-radius:999px;
  will-change:transform;transform:translate3d(-50%,-50%,0);
  mix-blend-mode:difference;
  transition:width .4s var(--e-out),height .4s var(--e-out),
             margin .4s var(--e-out),background .3s var(--e-out),
             border-color .3s,border-radius .3s,opacity .25s;
}
.cursor__dot{
  width:4px;height:4px;border-radius:999px;background:#fff;
  transition:opacity .25s var(--e-out),transform .25s var(--e-out);
}
.cursor__label{
  position:absolute;
  font-size:11px;letter-spacing:var(--ls-sm);text-transform:uppercase;
  color:#000;opacity:0;white-space:nowrap;transition:opacity .25s var(--e-out);
}
/* Saum ohne Mischung. difference liefert 255 − Untergrund; bei mittleren
   Grautönen (~110–145) ergibt das exakt den Untergrund und der Zeiger
   verschwindet. Der Saum liegt darunter und trägt diese Lücke. */
.cursor__halo{
  position:fixed;top:0;left:0;z-index:9998;pointer-events:none;border-radius:999px;
  width:37px;height:37px;margin:-18.5px 0 0 -18.5px;
  border:1px solid rgba(0,0,0,.30);
  box-shadow:0 0 0 1px rgba(255,255,255,.26);
  will-change:transform;transform:translate3d(-50%,-50%,0);
  transition:width .4s var(--e-out),height .4s var(--e-out),
             margin .4s var(--e-out),border-radius .3s,opacity .25s;
}
html.cur-link .cursor__body{width:40px;height:40px;margin:-20px 0 0 -20px;background:#fff}
html.cur-link .cursor__halo{width:43px;height:43px;margin:-21.5px 0 0 -21.5px}
html.cur-link .cursor__dot{opacity:0;transform:scale(0)}

html.cur-label .cursor__body{width:78px;height:78px;margin:-39px 0 0 -39px;background:#fff}
html.cur-label .cursor__halo{width:81px;height:81px;margin:-40.5px 0 0 -40.5px}
html.cur-label .cursor__dot{opacity:0;transform:scale(0)}
html.cur-label .cursor__label{opacity:1}

html.cur-down .cursor__body,html.cur-down .cursor__halo{transform:translate3d(-50%,-50%,0) scale(.85)}
html.cur-out .cursor__body,html.cur-out .cursor__halo{opacity:0}

/* ── Fortschritt am oberen Rand ───────────────────────────── */
.progress{
  position:fixed;top:0;left:0;height:2px;width:100%;z-index:210;
  background:var(--acc);transform:scaleX(0);transform-origin:left;
  pointer-events:none;
}

/* ── Das Siegel ───────────────────────────────────────────────
   Der Auftakt: der Lorbeer zeichnet sich selbst, die Blätter setzen
   sich von unten nach oben, ein Champagnerring schließt ihn ein —
   dann öffnet sich das Blatt als Kreisblende auf die Karte.
   Ein echtes Siegel, das gebrochen wird. */
/* Ohne Skript gibt es kein Siegel. Es ist ein deckendes Vollbild und
   nur das Skript nimmt es wieder weg — ungated bliebe die Karte bei
   einem Skriptfehler hinter einer weißen Fläche verborgen. */
.seal{display:none}
.js .seal{
  position:fixed;inset:0;z-index:9000;
  display:grid;place-items:center;
  background:var(--paper);
  clip-path:circle(150% at 50% 46%);
  transition:clip-path 1.15s var(--e-io);
}
.seal__stage{position:relative;display:grid;place-items:center;width:min(52vw,250px);aspect-ratio:1}
/* Die echte Marke, nicht nachgebaut: eine blasse Spur und darüber
   dieselbe Datei, die per Beschnitt von unten nach oben freigegeben
   wird — genau die Auflösung, die auch der Lorbeer-Fortschritt der
   Website benutzt. */
.seal__mark{position:relative;width:56%;aspect-ratio:256/391}
.seal__track,.seal__fill{position:absolute;inset:0;width:100%;height:100%;object-fit:contain}
.seal__track{opacity:.13}
.seal__fill{clip-path:inset(100% 0 0 0)}
.seal__ring{position:absolute;inset:0;width:100%;height:100%;overflow:visible}
.seal__ring circle{
  fill:none;stroke:var(--acc);stroke-width:1.5;
  stroke-dasharray:var(--c,880);stroke-dashoffset:var(--c,880);
  transform:rotate(-90deg);transform-origin:center;
}
.seal__cap{
  position:absolute;left:50%;bottom:-14%;transform:translateX(-50%);
  font-family:var(--mono);font-size:10px;letter-spacing:.28em;text-transform:uppercase;
  color:var(--acc-dark);white-space:nowrap;opacity:0;
}

/* Der Ablauf. Nur .js startet ihn — ohne Skript ist das Siegel
   von vornherein entfernt und die Karte sichtbar. */
.js .seal.is-run .seal__fill{animation:steigen 1.2s var(--e-io) .1s forwards}
.js .seal.is-run .seal__ring circle{animation:ring 1.35s var(--e-io) .12s forwards}
.js .seal.is-run .seal__cap{animation:cap .7s var(--e-out) .85s forwards}
@keyframes steigen{to{clip-path:inset(0 0 0 0)}}
@keyframes ring{to{stroke-dashoffset:0}}
@keyframes cap{to{opacity:1}}

/* Die Blende: der Kreis zieht sich auf den Lorbeer zusammen und
   springt dann auf — die Karte erscheint durch das Siegel hindurch. */
.js .seal.is-open{clip-path:circle(0% at 50% 46%);pointer-events:none}
.js .seal.is-gone{display:none}

/* ── Nav ──────────────────────────────────────────────────────
   mix-blend-difference invertiert selbsttätig über jedem Untergrund. */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:200;
  display:flex;align-items:center;justify-content:space-between;gap:20px;
  padding:20px var(--gut);
  color:#fff;mix-blend-mode:difference;
  transition:padding .5s var(--e-out),transform .5s var(--e-out);
  /* Die Leiste ist durchsichtig, aber randbreit und rund 80px hoch. Ohne
     dies finge sie jede Berührung im oberen Streifen der Seite ab — auf
     dem Telefon liegt dort der Anfang der Karte. Die Kinder nehmen die
     Eingaben wieder an. */
  pointer-events:none;
}
.nav__brand,.nav__actions{pointer-events:auto}
/* Der Fokusring würde von difference mitinvertiert und käme auf Papier
   nur auf 2.6:1. Weiß kehrt sich zum Gegenwert des Untergrunds um —
   dieselbe Mechanik, die die Schrift der Leiste lesbar hält. */
.nav :focus-visible{outline-color:#fff}
.nav.is-stuck{padding:13px var(--gut)}
.nav.is-hidden{transform:translateY(-105%)}
.nav__brand{display:flex;align-items:center;gap:10px}
/* Das Zeichen ist hochformatig (0.655) — Höhe führen, Breite folgt,
   sonst quetscht die quadratische Box es zusammen. */
.nav__mark{height:28px;width:auto;transition:transform .6s var(--e-out)}
.nav__brand:hover .nav__mark{transform:rotate(-8deg)}
.nav__name{font-size:13px;letter-spacing:var(--ls-sm);text-transform:uppercase}
.nav__name em{font-style:normal;opacity:.55}
.nav__actions{display:flex;align-items:center;gap:18px}
@media(max-width:520px){.nav__name{display:none}}

/* ── Typografie ───────────────────────────────────────────── */
.eyebrow{
  display:flex;align-items:center;gap:12px;padding-top:14px;
  border-top:2px solid var(--acc);
  font-size:13px;letter-spacing:var(--ls-sm);text-transform:uppercase;color:var(--acc-dark);
}
.eyebrow::before{content:'';width:6px;height:6px;border-radius:999px;background:var(--acc);flex:none}
.foot .eyebrow{color:var(--acc)}
.h-sec{font-size:clamp(30px,4.6vw,66px);line-height:.98;letter-spacing:var(--ls-display);text-transform:uppercase}
.h-item{font-size:clamp(20px,2.3vw,32px);line-height:1.02;letter-spacing:var(--ls-lg)}
.body-lg{font-size:clamp(16px,1.3vw,19px);line-height:1.5;letter-spacing:var(--ls-sm);color:var(--ink-2);max-width:52ch}
.label{font-family:var(--mono);font-size:11px;letter-spacing:.06em;text-transform:uppercase;color:var(--acc-dark)}

/* ── Zeilenaufstieg ───────────────────────────────────────────
   Kein overflow auf dem Block — die Maskierung macht die Wortkapsel.
   Die bekommt auf allen vier Seiten Luft: unten für die Unterlängen,
   oben für die Umlautpunkte, seitlich weil das negative Tracking die
   Vorschubbreite des letzten Zeichens kürzt und die Glyphe sonst über
   den Layoutkasten ragt und abgeschnitten wird. Die negativen Ränder
   heben die Reserve im Layout wieder auf. */
[data-split]{overflow:visible}
.sw{
  display:inline-block;overflow:hidden;vertical-align:top;
  padding:.12em .16em .16em;margin:-.12em -.16em -.16em;
}
.sw>i,.sw>em{font-style:normal}
.js .sw>i,.js .sw>em{
  display:inline-block;transform:translateY(106%);
  transition:transform .9s var(--e-out);transition-delay:calc(var(--d) * 24ms);
}
.js [data-split].in .sw>i,.js [data-split].in .sw>em{transform:translateY(0)}
.js .reveal{opacity:0;transform:translateY(22px);transition:opacity .85s var(--e-out),transform .85s var(--e-out)}
.js .reveal.in{opacity:1;transform:none}

/* ── Buttons ──────────────────────────────────────────────────
   Raster statt Flex: beide Labelkopien liegen in DERSELBEN Zelle und
   damit exakt übereinander. Absolut positioniert spränge die zweite
   beim Hover in die obere linke Ecke. */
.btn{
  position:relative;display:inline-grid;grid-template-areas:"t";place-items:center;
  min-height:44px;padding:0 2px;overflow:hidden;
  font-size:13px;letter-spacing:var(--ls-sm);text-transform:uppercase;
}
/* Beide Kopien sind exakt eine Zeile hoch. Ohne feste Höhe ist der
   Container so hoch wie der Button — translateY(100%) schöbe den Text
   dann nicht aus dem Sichtfeld und beide Label stünden übereinander. */
.btn__t{grid-area:t;position:relative;display:block;overflow:hidden;height:1.6em;line-height:1.6;white-space:nowrap}
.btn__t span{display:inline-block;white-space:pre}
.btn__t--b span{transform:translateY(100%)}
.js .btn__t span{transition:transform .5s var(--e-out);transition-delay:calc(var(--i) * 16ms)}
.btn:hover .btn__t--a span{transform:translateY(-110%)}
.btn:hover .btn__t--b span{transform:translateY(0)}
.btn__u{position:absolute;left:2px;right:2px;bottom:10px;height:1px}
.btn__u i{position:absolute;inset:0;background:currentColor;transition:transform .45s var(--e-out)}
.btn__u .u-r{transform-origin:right center}
.btn__u .u-l{transform-origin:left center;transform:scaleX(0)}
.btn:hover .u-r{transform:scaleX(0)}
.btn:hover .u-l{transform:scaleX(1);transition-delay:.2s}

.btn--solid{
  min-height:58px;padding:0 32px;border-radius:999px;
  background:var(--ink);color:var(--paper);
}
.btn--solid .btn__u{display:none}
.btn--solid::before{
  content:'';position:absolute;inset:0;background:var(--acc);
  transform:translateY(101%);transition:transform .55s var(--e-out);
}
.btn--solid:hover{color:var(--ink)}
.btn--solid .btn__t{z-index:1}
.btn--solid:hover::before{transform:translateY(0)}

.btn--line{
  min-height:58px;padding:0 32px;border-radius:999px;
  border:1px solid var(--line);color:var(--ink);
}
.btn--line .btn__u{display:none}
.btn--line::before{
  content:'';position:absolute;inset:0;background:var(--ink);
  transform:translateY(101%);transition:transform .55s var(--e-out);
}
.btn--line:hover{color:var(--paper);border-color:var(--ink)}
.btn--line .btn__t{z-index:1}
.btn--line:hover::before{transform:translateY(0)}

.btn--block{width:100%;justify-content:center}

/* Bestätigung: die Fläche bleibt geflutet, gut zwei Sekunden lang.
   Beide Labelkopien tragen denselben Text — der Aufrollen-Effekt ist
   Textur, keine Zustandsanzeige. Stünde in der zweiten Kopie „Gespeichert",
   verspräche der Knopf das schon beim bloßen Überfahren. Was tatsächlich
   passiert ist, sagt die Meldung. */
.btn.is-ok::before{transform:translateY(0)}
.btn.is-ok{color:var(--ink)}
/* Der Umriss-Knopf flutet im Hover mit Tinte — als Quittung muss es
   Champagner sein, sonst stünde Tinte auf Tinte. */
.btn--line.is-ok{border-color:var(--acc)}
.btn--line.is-ok::before{background:var(--acc)}

/* ── Karte / Hero ─────────────────────────────────────────── */
.card{
  position:relative;min-height:100svh;
  display:grid;align-content:space-between;
  grid-template-columns:minmax(0,1fr) clamp(240px,30vw,420px);
  grid-template-areas:
    "head fig"
    "name fig"
    "foot fig";
  column-gap:clamp(24px,4vw,64px);
  padding:calc(88px + env(safe-area-inset-top)) 0 var(--gut);
  overflow:hidden;
}
.card__head{grid-area:head;padding-left:var(--gut);padding-right:var(--gut)}
.card__eyebrow{display:inline-flex}

/* Der Name führt die Seite: Vorname klein und gemischt, Nachname
   riesig und in Versalien — Hierarchie ohne einen einzigen fetten Schnitt. */
.card__name{grid-area:name;padding-inline:var(--gut);margin:clamp(20px,4vw,52px) 0}
.card__first{
  display:block;font-size:clamp(20px,2.6vw,38px);line-height:1;
  letter-spacing:var(--ls-lg);color:var(--ink-2);margin-bottom:.12em;
}
/* Die Größe füllt die Zeile bis an beide Ränder. Das Skript misst die
   tatsächliche Glyphenbreite und setzt --name exakt nach; der clamp()
   darüber ist nur der Startwert für den ersten Frame und den Fall ohne JS. */
/* max-content ist Voraussetzung fürs Messen: als Block wäre die Breite
   immer die des Elternteils und die Rechnung ginge stets auf 1 auf. */
.card__last{
  display:block;width:max-content;max-width:none;
  font-size:var(--name);line-height:.86;
  letter-spacing:var(--ls-display);text-transform:uppercase;
  white-space:nowrap;
}
.card__foot{
  grid-area:foot;display:flex;align-items:flex-end;justify-content:space-between;
  flex-wrap:wrap;gap:18px;padding-inline:var(--gut);
}
.card__ident{display:grid;gap:clamp(12px,1.4vw,18px)}
.card__role{font-size:clamp(15px,1.5vw,20px);line-height:1.35;letter-spacing:var(--ls-md);color:var(--ink-2);max-width:34ch}
.card__role b{color:var(--ink)}
/* Champagner-Kante links, dieselbe Auszeichnung wie im Team der Website */
.card__task{
  max-width:34ch;padding-left:12px;border-left:2px solid var(--acc);
  font-size:14px;line-height:1.45;letter-spacing:var(--ls-md);color:var(--ink-2);
}
.card__task span{
  display:block;margin-bottom:4px;
  font-family:var(--mono);font-size:10px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--acc-dark);
}
.card__hint{
  display:flex;align-items:center;gap:10px;
  font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--acc-dark);
}
.card__hint i{font-style:normal;animation:sink 1.8s var(--e-io) infinite}
@keyframes sink{0%,100%{transform:translateY(0)}50%{transform:translateY(5px)}}

/* ── Das Bildnis ──────────────────────────────────────────────
   Der Lorbeer schließt sich beim Scrollen um die Person: --ring läuft
   von 0 auf 1 und gibt den Kreisbogen frei. Dieselbe Auflösung wie der
   Fortschrittskranz der Hauptseite. */
.fig{
  grid-area:fig;align-self:stretch;margin-right:var(--gut);
  display:flex;flex-direction:column;gap:10px;
}
/* Eigener Kasten für Rahmen und Kranz: der Kranz sitzt außerhalb der
   Bildkante, der Rahmen selbst beschneidet aber (overflow:hidden) —
   lägen beide in einem Element, schnitte es den Kranz weg. */
.fig__box{position:relative;flex:1;min-height:clamp(320px,52vh,620px)}
.fig__frame{position:absolute;inset:0;background:var(--stone-1);overflow:hidden}
.fig__img{
  width:100%;height:100%;object-fit:cover;object-position:50% 20%;
  /* Aufzug: das Bild fährt hinter der Unterkante hervor */
  clip-path:inset(0 0 100% 0);transform:scale(1.08);
}
.js .fig.in .fig__img{
  clip-path:inset(0 0 0 0);transform:scale(1);
  transition:clip-path 1.25s var(--e-io) .15s,transform 1.6s var(--e-out) .15s;
}
/* Der Kranz schließt sich beim Scrollen: --ring läuft von 0 auf 1 und
   gibt den Strich frei. Die Maße setzt das Skript in echten Pixeln,
   damit die Strichlänge über die ganze Kante gleichmäßig bleibt. */
/* Maße ausrechnen statt über inset aufspannen: ein <svg> ist ein ersetztes
   Element und zieht seine Größe aus dem viewBox-Verhältnis — bei inset
   allein würde der rechte und untere Wert verworfen und der Kranz käme
   quadratisch heraus statt der Bildkante zu folgen. */
.fig__ring{
  --o:clamp(8px,1.4vw,14px);
  position:absolute;top:calc(-1 * var(--o));left:calc(-1 * var(--o));
  width:calc(100% + 2 * var(--o));height:calc(100% + 2 * var(--o));
  /* Der Reset setzt svg auf max-width:100% — das kappte die Breite genau
     um den Überstand, während die Höhe frei blieb, und der Kranz saß
     rechts auf der Bildkante statt daneben. */
  max-width:none;
  pointer-events:none;overflow:visible;
}
.fig__ring rect{
  fill:none;stroke:var(--acc);stroke-width:1.5;
  stroke-dasharray:var(--len,1200);
  stroke-dashoffset:calc(var(--len,1200) * (1 - var(--ring,0)));
}
.fig__cap{
  font-family:var(--mono);font-size:10px;letter-spacing:.18em;
  text-transform:uppercase;color:var(--ink-3);
}

/* Ersatzdarstellung, solange kein Bildnis vorliegt: dieselbe Geometrie,
   derselbe Kranz — nur das Zeichen statt der Person. Kein Loch im Layout. */
/* Alles im Verhältnis zum Rahmen, nicht in festen Mindestmaßen: auf
   kleinen Geräten schrumpft der Rahmen auf 40% der Kartenbreite, ein
   Sockelwert von 84px für das Zeichen ließ den Inhalt dort herauslaufen. */
.fig__mono{
  position:absolute;inset:0;display:grid;place-content:center;justify-items:center;
  gap:clamp(6px,4%,18px);
  background:
    radial-gradient(120% 90% at 50% 18%,rgba(201,184,150,.20) 0%,transparent 62%),
    var(--stone-1);
}
.fig__mono-mark{width:38%;min-width:40px;max-width:150px;height:auto;color:var(--acc-dark);opacity:.85}
.fig__mono-ini{
  font-size:clamp(16px,3.4vw,42px);line-height:1;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink-2);
}
/* --ink-3 käme auf Stein nur auf 4.4:1 und verfehlte AA für Kleintext */
.fig__mono-note{
  font-family:var(--mono);font-size:clamp(9px,1.4vw,10px);letter-spacing:.2em;
  text-transform:uppercase;color:var(--ink-2);
}

@media(max-width:900px){
  .card{
    /* minmax(0,…) ist Pflicht: eine blanke 1fr-Spur nimmt als Mindestmaß
       den Inhalt, und .card__last steht auf width:max-content — die Spur
       würde damit breiter als die Karte und der Name liefe seitlich raus. */
    grid-template-columns:minmax(0,1fr);
    grid-template-areas:"head" "fig" "name" "foot";
    align-content:start;row-gap:clamp(18px,4vw,32px);
    padding-top:calc(84px + env(safe-area-inset-top));
    min-height:100svh;
  }
  /* Schmales Hochformat statt voller Breite: sonst schiebt das Bildnis
     den Namen unter die Falz und die Karte zeigt zuerst nur ein Foto. */
  .fig{margin:0 var(--gut);align-self:start}
  .fig__box{flex:none;width:min(58%,300px);min-height:0;aspect-ratio:4/5}
  .fig__img{object-position:50% 16%}
  .card__name{margin:0}
}
@media(max-width:900px) and (max-height:680px){
  .fig__box{width:min(40%,220px)}
}
@media(max-width:520px){
  .card__foot{flex-direction:column;align-items:flex-start;gap:14px}
}

/* ── Abschnitte ───────────────────────────────────────────── */
.sec{padding-block:var(--sec-y);position:relative}
.pad{padding-inline:var(--gut)}
.stone{background:var(--stone-2)}

/* ── Kontaktzeilen ────────────────────────────────────────── */
.rows{border-top:2px solid var(--acc)}
/* isolation ist Pflicht: ohne eigenen Stapelkontext fiele die Füllung
   mit z-index:-1 hinter den Abschnittsgrund und bliebe unsichtbar —
   die Schrift wechselte trotzdem auf Weiß und verschwände auf Stein. */
.row{
  position:relative;isolation:isolate;
  display:grid;grid-template-columns:minmax(0,22ch) minmax(0,1fr) auto;
  gap:clamp(14px,3vw,44px);align-items:baseline;
  padding:clamp(20px,2.6vw,32px) 0;border-bottom:1px solid var(--line);
  transition:padding-inline .55s var(--e-out),color .35s var(--e-out);
}
/* --from bestimmt, von welcher Seite die Füllung wächst. Das Skript setzt
   es beim Ein- und Austritt auf die Seite, an der der Zeiger die Zeile
   kreuzt — die Fläche folgt damit der Bewegung statt immer von oben zu
   kommen. Ohne Skript bleibt der Vorgabewert. */
.row::before{
  content:'';position:absolute;inset:0;background:var(--ink);z-index:-1;
  transform:scaleY(0);transform-origin:center var(--from,top);
  transition:transform .5s var(--e-io);
}
.row:hover,.row:focus-visible{padding-inline:clamp(12px,2vw,26px);color:var(--d-ink)}
.row:hover::before,.row:focus-visible::before{transform:scaleY(1)}
.row:hover .row__meta,.row:focus-visible .row__meta{color:var(--acc)}
.row:hover .row__go,.row:focus-visible .row__go{opacity:1;transform:none}
.row__meta{font-size:13px;letter-spacing:var(--ls-sm);text-transform:uppercase;color:var(--acc-dark);transition:color .35s}
.row__v{font-size:clamp(17px,1.9vw,24px);line-height:1.15;letter-spacing:var(--ls-md);word-break:break-word}
.row__go{
  font-size:13px;letter-spacing:var(--ls-sm);text-transform:uppercase;
  opacity:0;transform:translateX(-8px);transition:opacity .4s var(--e-out),transform .4s var(--e-out);
}
/* Ohne Zeiger gibt es kein Hover — die Berührung übernimmt die Rückmeldung */
@media(hover:none){
  .row:active{color:var(--d-ink)}
  .row:active::before{transform:scaleY(1);transition-duration:.2s}
  .row:active .row__meta{color:var(--acc)}
}
@media(max-width:860px){
  .row{grid-template-columns:1fr;gap:6px}
  .row__go{display:none}
}

/* ── Aktionen ─────────────────────────────────────────────── */
.act__grid{
  display:flex;flex-wrap:wrap;gap:14px;
  margin-top:clamp(26px,4vw,40px);
}
/* --ink-3 reicht auf Papier (5.0:1), auf Stein nur noch 4.0:1 — dieser
   Absatz steht auf Stein und braucht deshalb die dunklere Stufe. */
.act__note{margin-top:20px;font-size:13px;line-height:1.5;color:var(--ink-2);max-width:56ch}
@media(max-width:560px){
  .act__grid{flex-direction:column;align-items:stretch}
  .act__grid .btn{width:100%}
}

/* ── Meldung ──────────────────────────────────────────────────
   Quittung für „gespeichert" und „kopiert". Sie parkt außerhalb des
   Bildes. visibility trägt das ab, nicht die Verschiebung allein: die
   Leiste steht über dem unteren Rand, ein reines translate ließ einen
   leeren schwarzen Streifen stehen. */
.toast{
  position:fixed;left:50%;z-index:9600;
  bottom:calc(22px + env(safe-area-inset-bottom));
  transform:translate(-50%,200%);
  opacity:0;visibility:hidden;
  max-width:min(560px,calc(100vw - 32px));
  padding:13px 22px;background:var(--ink);color:var(--d-ink);
  font-family:var(--mono);font-size:11px;line-height:1.5;letter-spacing:.06em;
  text-transform:uppercase;text-align:center;
  /* Umbruch statt Beschnitt: die Rückfallmeldung trägt eine ganze Adresse,
     die abgeschnitten nutzlos wäre. */
  overflow-wrap:anywhere;
  transition:transform .6s var(--e-out),opacity .3s var(--e-out),visibility 0s .6s;
}
.toast.is-on{
  transform:translate(-50%,0);opacity:1;visibility:visible;
  transition:transform .6s var(--e-out),opacity .3s var(--e-out),visibility 0s;
}

/* ── Footer — voller Viewport ─────────────────────────────── */
.foot{
  position:relative;isolation:isolate;overflow:hidden;
  min-height:100svh;display:flex;
  background:var(--ink);color:var(--d-ink);
}
.foot__bg{
  position:absolute;inset:0;z-index:-2;
  width:100%;height:100%;object-fit:cover;object-position:62% 50%;
}
/* Verlauf nach links: dort steht der Text, rechts bleibt das Relief frei */
.foot::before{
  content:'';position:absolute;inset:0;z-index:-1;
  background:linear-gradient(90deg,rgba(12,12,12,.94) 0%,rgba(12,12,12,.86) 42%,rgba(12,12,12,.30) 100%);
}
.foot__inner{
  /* min-width:0 ist Pflicht. Als Flex-Element gilt sonst min-width:auto,
     also die Mindestbreite des Inhalts — und .foot__word steht auf
     max-content mit nowrap. Der Fußbereich wüchse damit auf die Breite des
     Schriftzugs, statt ihn zu begrenzen: auf schmalen Geräten maß das
     Skript gegen einen bereits zu breiten Kasten und der Zug lief rechts
     aus dem Bild. */
  min-width:0;
  position:relative;flex:1;display:flex;flex-direction:column;
  gap:clamp(28px,4.5vw,56px);
  padding:clamp(64px,8vw,110px) var(--gut) calc(var(--gut) + env(safe-area-inset-bottom));
}
.foot__top{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);gap:clamp(28px,5vw,64px)}
.foot__lead .eyebrow{border-top-color:var(--acc);margin-bottom:20px}
.foot__mail,.foot__tel{
  display:block;font-size:clamp(17px,2.1vw,28px);line-height:1.25;
  letter-spacing:var(--ls-lg);transition:color .3s;word-break:break-word;
}
.foot__mail:hover,.foot__tel:hover{color:var(--acc)}
.foot__tel{margin-top:6px;color:var(--d-ink-2)}
.foot__adr{margin-top:18px;font-size:14px;color:var(--d-ink-2)}
.foot__cols{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:24px}
.foot__col{display:flex;flex-direction:column;gap:10px}
.foot__col span{
  font-family:var(--mono);font-size:10px;letter-spacing:.18em;text-transform:uppercase;
  color:var(--acc);margin-bottom:4px;
}
.foot__col a{font-size:14px;color:var(--d-ink-2);transition:color .3s,transform .35s var(--e-out)}
.foot__col a:hover{color:var(--d-ink);transform:translateX(5px)}
@media(max-width:900px){.foot__top{grid-template-columns:1fr;gap:34px}}

/* Der Abschluss: der Nachname flutet beim Scrollen von oben mit
   Champagner voll — dieselbe Auflösung wie der Kranz am Bildnis. */
.foot__word{
  margin:auto 0;width:max-content;
  font-size:var(--footname,clamp(42px,13.6vw,236px));line-height:.86;
  letter-spacing:-.012em;font-kerning:none;font-variant-ligatures:none;
  text-transform:uppercase;white-space:nowrap;user-select:none;
  background-image:linear-gradient(180deg,
    var(--acc) 0%, var(--acc) var(--fill,0%),
    rgba(237,237,237,.13) var(--fill,0%), rgba(237,237,237,.13) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;
}
.foot__base{
  display:flex;justify-content:space-between;flex-wrap:wrap;gap:10px;
  padding-top:20px;border-top:1px solid rgba(255,255,255,.12);
  font-family:var(--mono);font-size:11px;letter-spacing:.04em;color:var(--d-ink-2);
}
.foot__base b{font-weight:400}
.foot__base a{color:var(--acc);transition:opacity .3s}
.foot__base a:hover{opacity:.7}

/* ── Reduzierte Bewegung ──────────────────────────────────── */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms !important;animation-iteration-count:1 !important;
    transition-duration:.01ms !important;scroll-behavior:auto !important;
  }
  .reveal{opacity:1;transform:none}
  .sw>i,.sw>em{transform:none}
  .fig__img{clip-path:none;transform:none}
  .fig__ring rect{stroke-dashoffset:0}
  .foot__word{--fill:100%}
  .seal{display:none}
}
