/* ============================================================
   GEMEINSAMES STYLESHEET -- von index.html, beratung.html und
   profil.html per <link rel="stylesheet"> eingebunden. Enthaelt alle
   seitenuebergreifenden Regeln (Farb-Tokens, Reset, Body, Nav,
   Kopfleiste, Footer, Section-Farbsystem, Typografie-Basis) sowie
   Regeln, die nur auf index.html greifen, aber auf den Unterseiten
   schadlos ungenutzt bleiben (z.B. .portfolio-*, .lightbox-*, .poem-*).

   Eine Ausnahme bleibt bewusst NICHT hier: die Sichtbarkeits-Gating-
   Regeln von .site-title/.site-nav/.logo-bar (opacity:0 + Freigabe ueber
   html.nav-visible/html.hero-fixed) sind nur auf index.html relevant,
   wo diese Elemente hinter dem Klick-Uebergang des Gedichts versteckt
   starten. Auf den Unterseiten sollen sie von Anfang an sichtbar sein --
   die Basisregeln hier setzen daher bewusst KEIN opacity, wodurch der
   CSS-Startwert (1, sichtbar) automatisch greift. index.html ergaenzt die
   Gating-Logik in einem eigenen kleinen <style>-Block im <head>.
   ============================================================ */

/* ============================================================
   SELBST GEHOSTETE SCHRIFTEN (statt Google Fonts CDN)
   ============================================================
   War vorher ueber <link> zu fonts.googleapis.com/fonts.gstatic.com
   eingebunden -- aus Datenschutzgruenden (IP-Uebertragung an Google
   beim Laden, in Deutschland mehrfach abgemahnt/gerichtlich
   problematisiert) jetzt lokal in /fonts/ gehostet, keine externe
   Verbindung mehr beim Laden dieser Schriften.

   Nur der "latin"-Subset (deckt alle deutschen Sonderzeichen ab: ä ö ü
   ß liegen alle im Unicode-Bereich U+0000-00FF) -- die von Google
   zusaetzlich ausgelieferten Subsets (cyrillic, greek, vietnamese etc.)
   werden hier nicht gebraucht. Alle drei sind Variable Fonts: eine
   einzelne Datei deckt trotzdem alle im Projekt genutzten Schnitte ab
   (das ist auch genau das, was Googles eigene CSS bisher auslieferte --
   mehrere @font-face-Regeln mit unterschiedlichem font-weight, aber
   identischer Font-Datei je Familie). font-display: Inter/Caveat swap
   (Text wartet nicht auf den Ladevorgang), EB Garamond block -- siehe
   Begruendung an der Regel selbst. Alle Dateien werden zusaetzlich im
   <head> der Seiten per preload angestossen (Inter + EB Garamond
   ueberall, Caveat nur auf index.html), damit sie nicht erst nach dem
   CSS-Parsen entdeckt werden. */
@font-face {
  font-family: 'Inter';
  font-style: normal;
  font-weight: 400 900;
  font-display: swap;
  src: url('fonts/inter.woff2') format('woff2');
}

@font-face {
  font-family: 'EB Garamond';
  font-style: normal;
  font-weight: 400 500;
  /* block statt swap (Inter bleibt swap): EB Garamond traegt Gedicht
     und Seitentitel -- mit swap erschien der Gedichttext beim kalten
     Laden erst kurz in der Serifen-Ersatzschrift und sprang dann
     sichtbar auf EB Garamond um (vom Nutzer als "Ruckeln beim Laden"
     gemeldet). block haelt den Text stattdessen kurz unsichtbar, bis
     die Schrift da ist -- fuer den zeremoniellen Gedicht-Einstieg der
     ruhigere Kompromiss. Dank preload der Font-Datei im <head> aller
     Seiten (siehe dort) ist das Fenster ohnehin minimal. */
  font-display: block;
  src: url('fonts/eb-garamond.woff2') format('woff2');
}

/* Nur auf index.html tatsaechlich referenziert (Kritzel-Easteregg-
   Labels) -- Browser laden Font-Dateien erst bei tatsaechlichem Bedarf
   (passendem Text + font-family-Treffer), diese Regel kostet auf
   beratung.html/profil.html also keinen zusaetzlichen Request. */
@font-face {
  font-family: 'Caveat';
  font-style: normal;
  font-weight: 600 700;
  font-display: swap;
  src: url('fonts/caveat.woff2') format('woff2');
}

/* Fuer die Ermittlungswand-Beschriftungen auf Tape (profil.html) --
   bewusst NICHT Caveat: das ist elegante Schreibschrift, hier soll es
   nach grobem wasserfestem Marker auf Klebeband aussehen. Selbst
   gehostet wie die uebrigen Fonts (Datenschutz), von Google Fonts
   heruntergeladen statt live per CDN geladen. block statt swap (gleicher
   Grund wie bei EB Garamond, siehe dortiger Kommentar): die generische
   cursive-Ersatzschrift sieht Permanent Marker in Form/Breite ueberhaupt
   nicht aehnlich, der Sprung war besonders am grossen Tape-Titel
   (.wall-title-tape, breitenabhaengig von der Schriftbreite) deutlich
   sichtbar ("Fonts springen beim Klick auf Ueber", Nutzer-Meldung).
   Dank preload im <head> von profil.html bleibt das kurze Unsichtbar-
   Fenster minimal. */
@font-face {
  font-family: 'Permanent Marker';
  font-style: normal;
  font-weight: 400;
  font-display: block;
  src: url('fonts/permanent-marker.woff2') format('woff2');
}

/* ============================================================
   FARB-TOKENS & AKZENTFARBE
   ============================================================ */
:root {
  --bg-dark:    #0d0d0c;
  --text-dark:  #e8e6e1;
  --muted-dark: #6e6d69;
  /* Heller als --muted-dark: das ist fuer dezente Kontraste auf dem
     fest dunklen Gedicht-Hintergrund gedacht. Wird auch von der
     Lightbox genutzt, deren dunkler Zustand ebenfalls fest bleibt. */
  --muted-on-dark: #a9a8a3;

  /* Seiten-Grundton, sichtbar nur in den Luecken zwischen den
     Content-Sections -- --bg-light bleibt daneben bestehen, wird
     aber nur noch als explizite Section-Fuellfarbe verwendet, nicht
     mehr als Body-Hintergrund. Erzeugt den "Naht"-Abstand zwischen
     den Content-Bloecken, auf allen drei Seiten gleich. */
  --bg-page:     #fdfbf6;
  /* War #e5e9ea -- minimal kuehl (B/G leicht ueber R), verstaerkte den
     "monochrom/klinisch"-Eindruck neben dem S/W-Foto. Jetzt Richtung
     warmes, helles Beige-Grau statt Blau-Grau -- erst #e9e4da (gleiche
     Helligkeit wie vorher, nur Farbtemperatur gedreht), dann nochmal
     etwas aufgehellt. */
  --bg-light:    #eee9df;
  --text-light:  #121211;
  --muted-light: #706f6b;

  /* Fuer echten Fliesstext gedacht, der als Inhalt gelesen werden soll
     (z.B. Beratung/Profil-Absaetze) -- deutlich dunkler als
     --muted-light, das fuer wirkliche Nebensaechlichkeiten (Footer,
     Kicker-Labels, Genre-Tags) reserviert bleibt. Sonst tragen Inhalt
     und Fussnote optisch dasselbe Gewicht. */
  --text-secondary: rgba(18, 20, 17, 0.78);

  /* AKZENTFARBE (SIGNAL-ORANGE) -- exklusiv fuer Interaktion (Nav-Hover,
     Links, aktiver Zustand), nicht fuer statische Section-Kicker. */
  --accent:     #FF4D00;

  /* ZWEITE AKZENTFARBE (PETROL) -- exklusiv fuer rein informative,
     NICHT klickbare Elemente (Kicker-/Genre-Labels, Fussnoten-
     Markierung). Traegt keine Interaktionsbedeutung, damit "Orange =
     hier klicken" eindeutig bleibt. Zwei Varianten: --accent-2 fuer
     helle Hintergruende, --accent-2-on-dark (heller/entsaettigter) fuer
     die dauerhaft dunklen Flaechen (Lightbox, Portfolio-Vorschau-
     Overlay), wo das dunkle Petrol sonst nicht lesbar waere. */
  --accent-2:         #175A5E;
  --accent-2-on-dark: #6FC2C0;

  --line:  rgba(120, 120, 115, 0.2);

  --fade: 0.5s ease;

  /* .logo-bar's Hoehe und der Top-Abstand der obersten Content-Section
     (.main-intro/.page-intro) muessen so gekoppelt sein, dass Letzterer
     IMMER groesser bleibt -- sonst rutscht der Content unter die
     fixierte, teiltransparente Logo-Bar und wirkt oben angeschnitten.
     Als Variable statt unabhaengiger Werte, die bei einem Redesign
     leise auseinanderlaufen koennen. */
  /* War ein fixer Wert (7.25rem), unabhaengig vom Titel-Schriftgrad
     (.site-title: clamp(1.4rem, 2.5vw, 2.4rem)) -- beim Verkleinern des
     Browserfensters (noch VOR dem Mobil-Umbruch bei 768px) schrumpfte
     der Titeltext zwar mit, die Kopfleiste blieb aber immer gleich
     hoch, wodurch zunehmend Leerraum zwischen Titel/Nav und dem
     Leisten-Rand entstand. Jetzt an dieselbe vw-Kurve gekoppelt wie
     die Titel-Schriftgroesse (2.5vw * 1.3 Zeilenhoehe = 3.25vw), damit
     die Leiste in gleichem Tempo mitschrumpft. Rechnung siehe
     .site-nav (dieselbe clamp()-Teilformel fuer den Titel-Zeilenkasten). */
  --logo-bar-height: calc(4.25rem + clamp(1.82rem, 3.25vw, 3.12rem));
  --logo-bar-height-mobile: 7rem;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  background-color: var(--bg-page);
  color: var(--text-light);
  font-family: 'Inter', sans-serif;
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  /* Safari darf scrollY nicht eigenmaechtig nachjustieren, um einen
     Layout-Sprung (Font-Swap, spaet ladende Bilder) "auszugleichen" --
     das liess auf index.html kurz nach dem Gedicht-Uebergang die
     Hero-Section unter die fixierte Logo-Bar rutschen, obwohl JS
     scrollY bereits hart auf 0 gesetzt hatte. Schadlos auf Seiten ohne
     vergleichbaren Uebergang. */
  overflow-anchor: none;
  /* HINWEIS: Waehrend der iPhone-Scroll-Haenger-Jagd lag hier zeitweise
     touch-action: pan-y pinch-zoom (Gesten-Arbitrierung als Verdachts-
     Fix, kostete den Doppeltipp-Zoom). Nach Fund der echten Ursache
     (veraltete Gesten-Regionen durch den frueher dynamischen Gedicht-
     Scroll-Lock, siehe .poem-section) wieder entfernt. */
}

::selection { background: var(--accent); color: #fff; }

/* HINWEIS (bewusst KEIN user-select:none mehr): Ein frueherer Versuch
   gegen die iPhone-Scroll-Haenger legte -webkit-user-select:none auf
   den body (nur Touch-Geraete) -- danach war der Scroll-START ueber
   Hero-Text/Fussnote komplett tot, waehrend er ueber dem Hero-Foto
   funktionierte. Das ist eine dokumentierte WebKit-Regression: Touches
   ueber Textinhalten laufen auf iOS durch eine eigene Text-Gesten-
   Verarbeitung, und mit user-select:none kann diese den Scroll-Start
   ganz verschlucken (Bilder sind nicht betroffen, sie durchlaufen
   diese Verarbeitung nicht). Also: NIE wieder user-select:none
   flaechig auf Textbereiche legen. Die eigentlichen Haenger wurden
   stattdessen ueber die Beseitigung von Dauer-Repaints angegangen
   (Footnote-Sweep auf transform, Scroll-Hint-Stopp -- siehe dort). */

/* FILMKORN */
/* transform:translateZ(0) foerdert das Element auf eine eigene
   Compositor-Ebene -- ohne das kann mix-blend-mode auf einem fixierten,
   viewport-grossen Layer dazu fuehren, dass der Browser bei jedem
   Scroll-Frame die Ueberblendung mit dem gesamten darunterliegenden
   Inhalt neu berechnen muss (CPU statt GPU), was sich als kurze,
   komplette Scroll-Aussetzer bemerkbar macht -- v.a. in Safari. Rein
   performance-bedingt, keine optische Aenderung. */
/* Wieder auf ALLEN Geraeten sichtbar: das zeitweise Touch-Abschalten
   (hover/pointer-Media-Query) war ein Verdachts-Fix waehrend der
   iPhone-Scroll-Haenger-Jagd -- die echte Ursache war der dynamische
   Gedicht-Scroll-Lock (siehe .poem-section), das Korn war unschuldig. */
.film-grain {
  position: fixed;
  /* War inset:0 -- ein exakt am Viewport-Rand liegender, per
     translateZ(0) auf eine eigene Compositor-Ebene gehobener
     mix-blend-mode-Layer kann dort eine hauchduenne Kompositions-Naht
     zeigen (Rundungsfehler an der Layer-Kante), die durch "multiply"
     als kurzer dunkler/"negativer" Streifen auffaellt -- besonders bei
     Layer-Neuberechnungen wie waehrend des Gedicht-Uebergangs. 2px
     Ueberstand schiebt die Kante ausserhalb des sichtbaren Bereichs,
     ohne den Effekt selbst zu veraendern (Korn ist ohnehin nahtlos
     kachelbar). */
  inset: -2px;
  pointer-events: none;
  z-index: 90;
  opacity: 0.07;
  mix-blend-mode: multiply;
  transform: translateZ(0);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='240' height='240'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='240' height='240' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Skip-Link: fuer Tastatur-Nutzer, die sonst auf jeder Seite erst durch
   die komplette Nav tabben muessten, bevor sie zum eigentlichen Inhalt
   kommen. Fuer Maus-/Touch-Besucher unsichtbar (per Position ausserhalb
   des Bildschirms geschoben statt display:none/visibility:hidden, sonst
   waere er fuer Tastatur-Fokus gar nicht erst erreichbar) -- erscheint
   nur kurz, wenn tatsaechlich mit Tab reingesprungen wird. Erstes
   fokussierbares Element im DOM (siehe body-Anfang auf allen acht
   Seiten), zielt auf #inhalt (das <main>, dort per tabindex="-1"
   fokussierbar gemacht, sonst bekaeme nach dem Sprung nichts den Fokus). */
.skip-link {
  position: fixed;
  top: -4rem;
  left: 1rem;
  z-index: 120;
  background: var(--accent);
  color: #fff;
  padding: 0.7rem 1.2rem;
  border-radius: 0.4rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.95rem;
  text-decoration: none;
  transition: top 0.2s ease;
}

.skip-link:focus {
  top: 1rem;
}

/* KOPFLEISTE -- Titel + Nav + halbtransparente Logo-Bar, auf allen drei
   Seiten dieselbe Position/Optik. Auf index.html starten sie hinter dem
   Gedicht-Uebergang versteckt (siehe dortiger Gating-Block); hier ohne
   opacity-Angabe, also von Anfang an sichtbar -- passend fuer die
   Unterseiten ohne Uebergang. */
.site-title {
  position: fixed;
  top: 16px; /* war zuletzt 20px -- weiter verkleinert, siehe auch line-height unten */
  left: 50%;
  transform: translateX(-50%);
  z-index: 25;
  appearance: none;
  background: none;
  border: 0;
  padding: 0.15em 0;
  font-family: 'EB Garamond', serif;
  font-size: clamp(1.4rem, 2.5vw, 2.4rem);
  font-weight: 500;
  letter-spacing: 0.01em;
  /* War 1.6 -- fuer eine einzeilige Kopfzeile unnoetig viel eigener
     Leerraum ueber/unter dem Text, den die Kopfleiste insgesamt groesser
     wirken liess, als der sichtbare Titel selbst braucht. */
  line-height: 1.3;
  color: var(--text-light);
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}

/* Einzelne Buchstaben-Spans, per JS erzeugt (siehe <script> vor </body>
   auf allen drei Seiten) -- display:inline-block noetig, damit sich
   jeder Buchstabe per GSAP unabhaengig transformieren laesst. */
.site-title .letter {
  display: inline-block;
}

/* transform:translateZ(0) + will-change:backdrop-filter -- dieselbe
   Compositor-Layer-Foerderung wie bei .film-grain (siehe dortiger
   Kommentar), hier fuer backdrop-filter statt mix-blend-mode: ein
   fixierter, staendig ueber scrollendem Inhalt liegender Blur ist
   sonst eine der bekanntesten Ursachen fuer Scroll-Ruckler/-Aussetzer,
   weil der Blur bei jedem Frame neu ueber den sich aendernden Inhalt
   darunter berechnet wird. */
/* Blur wieder auf ALLEN Geraeten: das zeitweise Touch-Abschalten
   (hover/pointer-Media-Query mit deckenderem 95%-Fallback) war ein
   Verdachts-Fix waehrend der iPhone-Scroll-Haenger-Jagd -- die echte
   Ursache war der dynamische Gedicht-Scroll-Lock (siehe
   .poem-section), der Blur war unschuldig. */
/* --logo-bar-safe-extend: verlaengert die Leiste unsichtbar UEBER die
   Bildschirmoberkante hinaus (top negativ, Hoehe entsprechend groesser
   -- die sichtbare UNTERKANTE bleibt exakt bei var(--logo-bar-height)).
   Grund: iOS Safari laesst die Seite hinter der System-Statusleiste
   (Uhrzeit/Dynamic Island) durchscheinen, dieser Streifen liegt aber
   OBERHALB von top:0 des Layout-Viewports -- beim Hochscrollen wurde
   dort der vorbeiziehende Seitentext sichtbar, ueber der Kopfleiste.
   Die Verlaengerung deckt den Streifen mit ab; auf Geraeten ohne
   solchen Streifen liegt sie schlicht ausserhalb des Bildschirms. */
.logo-bar {
  --logo-bar-safe-extend: 100px;
  position: fixed;
  top: calc(-1 * var(--logo-bar-safe-extend));
  left: 0;
  width: 100%;
  height: calc(var(--logo-bar-height) + var(--logo-bar-safe-extend));
  z-index: 20;
  pointer-events: none;
  background-color: color-mix(in srgb, var(--bg-page) 82%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  transform: translateZ(0);
  /* War zusaetzlich will-change:backdrop-filter -- EXPERIMENTELL entfernt
     (siehe Chat: auf iPad im Querformat verschwand die Kopfleisten-
     Fuellung nach dem Hochscrollen komplett). transform:translateZ(0)
     foerdert allein bereits auf eine eigene Compositor-Ebene; das
     zusaetzliche will-change kann unter Speicherdruck (viele Bilder auf
     der Murder Wall, viele weitere transform-Kontexte durch die
     rotierten Polaroids) in WebKit dazu fuehren, dass die Ebene beim
     naechsten Scroll-Layout ganz fallengelassen wird statt nur die
     Blur-Optimierung zu verlieren -- ein dokumentiertes WebKit-
     Bug-Muster. Nicht auf echtem Safari verifizierbar (diese Umgebung
     hat kein WebKit), daher bitte nach dem naechsten Deploy auf dem
     iPad gegenpruefen. */
}

.site-nav {
  position: fixed;
  /* War ein fixer Wert (5rem) -- jetzt wie --logo-bar-height an die
     Titel-Schriftgroesse gekoppelt (1rem Abstand oben + Titel-Zeilen-
     kasten (clamp(1.4rem,2.5vw,2.4rem) * 1.3 Zeilenhoehe) + 1rem
     Abstand zur Nav), damit die Nav dem schrumpfenden Titel folgt statt
     bei kleineren Fensterbreiten zunehmend Luft darueber zu lassen. */
  top: calc(2rem + clamp(1.82rem, 3.25vw, 3.12rem));
  left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: center;
  align-items: baseline;
  gap: 0.6rem 2rem;
  list-style: none;
}

.site-nav a {
  position: relative;
  color: var(--text-light);
  text-decoration: none;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  font-weight: 400;
  letter-spacing: 0.02em;
  display: inline-block;
  transition: color 0.28s ease-out;
}

/* Unterstrich als eigenes Pseudo-Element statt text-decoration: laeuft
   bei Hover per scaleX ein statt hart einzublenden, mit leichtem
   Overshoot in der Easing-Kurve. bottom war -0.3em -- auf Nutzerwunsch
   naeher ans Wort geholt (hier wie bei allen anderen Link-Unterstrichen
   auf der Seite). */
.site-nav a::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.22em;
  height: 2px;
  background-color: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
  pointer-events: none;
}

.site-nav a:hover { color: var(--accent); }
.site-nav a:hover::after { transform: scaleX(1); }
.site-nav a[aria-current="page"] { color: var(--accent); }

/* GEDICHT SEKTION (nur index.html) -- einfacher Vollbild-Layer per
   position:fixed + inset:0, kein Scroll-gekoppeltes Pinnen/Messen mehr.
   Der Uebergang zur restlichen Seite wird per Klick ausgeloest (siehe
   JS: playIntroTransition), nicht durch Scroll-Fortschritt angetrieben.
   Auf Unterseiten ohne .poem-section im Markup wirkt das alles
   schlicht nie. */
.poem-section {
  position: fixed;
  inset: 0;
  z-index: 50;
  display: flex;
  align-items: center;
  color: var(--text-dark);
  /* Statischer Touch-Scroll-Lock der Gedicht-Phase: das Overlay deckt
     den ganzen Bildschirm ab, jeder Touch startet also hier und kann
     nicht pannen -- ersetzt das fruehere dynamische touch-action:none
     auf body.poem-active, das auf iOS 26 nach dem Entfernen der Klasse
     veraltete "nicht scrollbar"-Gesten-Regionen ueber den Text-Ebenen
     hinterliess (Scroll-Start ueber Fliesstext hing danach lange,
     ueber Bildern nicht -- siehe ausfuehrlichen Kommentar im Gating-
     <style>-Block von index.html). NICHT dynamisch machen. Nach dem
     Uebergang ist das Overlay visibility:hidden und faellt aus dem
     Hit-Testing, die Regel bleibt folgenlos. Swipe-Erkennung zum
     Weiterschalten des Gedichts (touchstart/touchend-Listener, siehe
     JS) funktioniert unveraendert -- touch-action unterbindet nur die
     Browser-Gesten (Pan/Zoom), nicht die Touch-Events selbst. */
  touch-action: none;
}

/* Eigenes, kindloses Element nur fuer die Hintergrundfarbe statt sie
   direkt auf .poem-section zu setzen: .poem-section enthaelt
   .hero-line, die waehrend der Klick-Timeline ihre eigene Landing-
   Animation durchlaeuft und dabei die ganze Zeit voll sichtbar bleiben
   muss -- ein opacity-Fade direkt auf .poem-section wuerde sie mit
   ausblenden. */
.poem-overlay-bg {
  position: absolute;
  inset: 0;
  background-color: var(--bg-dark);
}

.poem-block {
  position: absolute;
  left: 38vw;
  padding-right: 4vw;
  text-align: left;
  /* .poem-section ist ein Flex-Container mit align-items:center --
     .poem-block ist zwar per position:absolute aus dem Flex-Fluss
     genommen, bekommt aber (ohne eigenes top/bottom) ueber den
     Static-Position-Mechanismus fuer abspos-Kinder trotzdem eine an
     align-items:center orientierte Ausgangsposition. margin-top
     verschiebt von DORT aus nach oben -- weniger Kopfraum, die
     (jetzt groessere) Zeigehand unten bekommt dadurch mehr Luft, ohne
     dass sich am Zentrierungs-Mechanismus selbst etwas aendert.
     vh-basiert, damit die Verschiebung auf Mobile und Desktop
     gleichermassen (anteilig zur Bildschirmhoehe) wirkt. */
  margin-top: -9vh;
}

/* iPad (siehe html.is-ipad, per JS im <head> gesetzt -- echte
   Geraeteerkennung statt Breite/Seitenverhaeltnis zu raten). Frueher
   ueber @media(width/aspect-ratio) eingegrenzt -- brach auf echten
   Geraeten im Querformat: Safaris Adress-/Tableiste frisst sichtbare
   Bildschirmhoehe, das TATSAECHLICHE Seitenverhaeltnis rutscht dadurch
   je nach Chrome-Sichtbarkeit unvorhersehbar ueber jede feste Schwelle
   (in Playwright ohne Browser-Chrome nie reproduzierbar, siehe Chat).
   Hochformat: sehr grosse Bildschirmhoehe (oft 1100px+), ein fuer alle
   Seitenverhaeltnisse geteilter Wert (Desktop: -9vh) liess oben viel
   toten Raum stehen. NICHT einfach linear zum Basiswert skalierbar:
   bei einem abspos-Kind ohne eigenes "top", dessen Position nur ueber
   align-items:center des Eltern-Flex-Containers kommt, zentriert der
   Browser die MARGIN-BOX -- ein zusaetzliches vh an negativem
   margin-top verschiebt die sichtbare Position dadurch nur um die
   HAELFTE des erwarteten Betrags (empirisch verifiziert), daher ein
   deutlich groesserer Wert als naiv gedacht noetig. */
@media (orientation: portrait) {
  html.is-ipad .poem-block {
    margin-top: -38vh;
  }
}

/* Querformat -- eigene, kleinere Regel statt einfach beide Ausrichtungen
   gleich zu behandeln: hier ist die Bildschirmhoehe (oft 750-1050px,
   von Safaris Chrome noch weiter verkleinert) viel naeher an einem
   normalen Desktop-Fenster als im Hochformat, ein derart grosser Schub
   wie dort waere hier deutlich zu viel. Gleicher Halbierungs-Effekt wie
   oben beim margin-top-Wert. */
@media (orientation: landscape) {
  html.is-ipad .poem-block {
    margin-top: -22vh;
  }
}

.poem-line {
  font-family: 'EB Garamond', serif;
  font-size: clamp(1.4rem, 2.5vw, 2.4rem);
  font-weight: 400;
  line-height: 1.6;
  color: inherit;
  letter-spacing: 0.01em;
  white-space: nowrap;
}

.hero-line-wrapper { display: block; margin-bottom: 2rem; }

.hero-line {
  appearance: none;
  background: none;
  border: 0;
  /* Explizit statt "inherit": body ist Inter, aber das Gedicht/Logo
     bleibt bewusst bei der Serife. */
  font-family: 'EB Garamond', serif;
  line-height: inherit;
  color: inherit;
  font-weight: 500;
  display: inline-block;
  transform-origin: left top;
  cursor: pointer;
  pointer-events: auto;
  will-change: transform;
  padding: 0.15em 0;
}

/* .hero-line wandert waehrend des Klick-Uebergangs aus dem dunklen
   Gedicht-Bereich in die helle, fixierte Kopfleiste -- braucht dort
   also eine eigene Farb-Umschaltung, statt (wie die uebrigen
   Gedichtzeilen) einfach --text-dark von .poem-section zu erben. */
html.nav-visible .hero-line {
  color: var(--text-light);
}

/* Waehrend der Klick-Timeline positioniert GSAP .hero-line per
   Transform zur Logo-Position. Nach Abschluss uebernimmt .site-title
   dauerhaft die Rolle des Seitentitels -- .hero-line landet exakt an
   derselben Stelle (48px/zentriert), bevor sie verschwindet, der
   Wechsel ist deshalb unsichtbar. */
html.hero-fixed .hero-line-wrapper {
  display: none;
}

.poem-author {
  margin-top: 2.5rem;
  font-family: 'EB Garamond', serif;
  font-size: 0.75rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--text-dark);
}

/* Gedichttitel als eigene Zeile unter der Autor-Zeile -- dieselbe
   Typografie wie .poem-author (Groesse, Sperrung, Versalien, Farbe),
   nur knapperer margin-top, damit beide als zusammengehoeriges
   Zitat-Paar wirken statt wie zwei unterschiedlich gewichtete Angaben. */
.poem-title {
  margin-top: 0.5rem;
  font-family: 'EB Garamond', serif;
  font-size: 0.75rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: var(--text-dark);
}

.poem-ellipsis {
  color: var(--muted-dark);
}

/* Schliesst das Gedicht unten ab. Manicule/Zeigehand-Ornament (klass-
   isches Buchsatz-Schlusszeichen) statt Kreis+Chevron -- beide
   Ausloese-Wege (Klick UND Scroll/Wisch, siehe JS: window "wheel" +
   touchstart/touchend auf #poemSection) waren schon immer gleichzeitig
   aktiv, die Zeigehand ist also semantisch nicht weniger passend als
   der Chevron, hat aber mehr Charakter. image-neu.png ist eine
   Woodcut-Zeigehand mit echtem Alphakanal, per CSS-Maske eingesetzt
   (Farbe kommt von background-color, kein Freistellen mehr noetig). */
.scroll-hint {
  display: block;
  /* position:fixed + left:50% + margin-left:-<halbe Breite>
     zentriert auf dem gesamten Bildschirm, unabhaengig von .poem-blocks
     eigener, asymmetrischer Position (left:38vw) -- "unter dem
     Gedichttext zentriert" ist NICHT dasselbe wie "auf dem Screen
     zentriert", da .poem-block selbst nicht bildschirmmittig sitzt.
     margin-left statt transform:translateX(-50%): .scroll-hint traegt
     bereits eine eigene transform-Animation (scroll-hint-pulse) -- ein
     zusaetzlicher statischer transform-Wert wuerde von der
     ueberschrieben, sobald sie laeuft. bottom statt
     Flow-Position, zusaetzlich per env() vor Safaris unterer
     Systemleiste geschuetzt, wie .poem-block selbst (Mobile-Regel). */
  position: fixed;
  left: 50%;
  /* Breite an die Bildschirmbreite gekoppelt statt fixem rem-Wert: auf
     schmalen Phones (< ~430px) wirkte die feste 3,45rem-Groesse (siehe
     Kommentar-Historie: war 1,5x der urspruenglichen Groesse) im
     Verhaeltnis zur Screenflaeche zu dominant. clamp() schrumpft sie auf
     schmalen Screens proportional, ab ~430px+ (groessere Phones, Tablet-
     Breiten, Desktop) bleibt sie exakt bei der bisherigen 3,45rem-
     Obergrenze -- dort also keine Verhaltensaenderung. Einzige Quelle
     als Custom Property, damit Breite/Hoehe/Zentrierung nie
     auseinanderlaufen koennen (Hoehe/margin-left leiten sich direkt
     daraus ab statt eigene clamp()-Formeln zu pflegen). */
  --scroll-hint-w: clamp(2.75rem, 16vw - 0.85rem, 3.45rem);
  margin-left: calc(var(--scroll-hint-w) / -2);
  bottom: calc(10vh + env(safe-area-inset-bottom));
  width: var(--scroll-hint-w);
  height: calc(var(--scroll-hint-w) * 1.913); /* Seitenverhaeltnis der bisherigen Fixgroesse 6.6rem/3.45rem */
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  pointer-events: auto;
  /* War opacity:0 + eigene scroll-hint-fade-in-Animation (Einblenden
     erst nach 1.2s) -- auf Nutzerwunsch steht die Zeigehand jetzt von
     Anfang an da, kein Einfaden mehr noetig. Direkt beim Startwert des
     Pulsierens (0.65, siehe scroll-hint-pulse) statt voller Deckkraft,
     damit der Animationsstart nahtlos ist statt mit einem Sprung. */
  opacity: 0.65;
  animation: scroll-hint-pulse 3.4s ease-in-out infinite;
}

.scroll-hint-hand {
  display: block;
  width: 100%;
  height: 100%;
  background-color: var(--muted-dark);
  -webkit-mask-image: url('image-neu.png');
  mask-image: url('image-neu.png');
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  transition: background-color var(--fade);
}

.scroll-hint:hover .scroll-hint-hand {
  background-color: var(--text-dark);
}

/* Kleinere Spannweite (0.55-1 Deckkraft, 0.94-1 Groesse) und laengere
   Dauer (war 2.2s) auf Nutzerwunsch -- wirkte vorher zu hektisch/
   aufgeregt fuer einen dezenten Dauerhinweis, der jetzt zudem von
   Anfang an sichtbar ist (siehe .scroll-hint) statt erst nach 1.2s
   einzufaden. */
@keyframes scroll-hint-pulse {
  0%, 100% { opacity: 0.65; transform: scale(0.97); }
  50%      { opacity: 0.9;  transform: scale(1); }
}

/* Nach der Gedicht-Phase (body.poem-active wird beim Uebergang bzw.
   beim Ueberspringen entfernt, siehe JS) lief das infinite Pulsieren
   sonst unsichtbar ewig weiter -- #poemSection wird nur per
   visibility:hidden ausgeblendet, was CSS-Animationen NICHT stoppt
   (dieselbe Fehlerklasse wie die frueher mitten im Uebergang sichtbare
   Footnote-Linie, siehe body.poem-active-Pausenregel dort). Unsichtbare
   Endlos-Animationen kosten trotzdem Frame-Arbeit -- unnoetige
   Dauerlast, gerade auf Mobilgeraeten. */
body:not(.poem-active) .scroll-hint {
  animation: none;
}

@media (prefers-reduced-motion: reduce) {
  .scroll-hint {
    animation: none;
    opacity: 1; /* sonst bliebe der Hint durch die neue Basis-opacity:0 dauerhaft unsichtbar, wenn keine Animation mehr laeuft */
  }
}

/* CONTENT STAGE -- gemeinsamer Wrapper auf allen drei Seiten. Kein
   horizontales Padding/max-width mehr: alle Content-Sections sind
   selbst randlos (100vw) und zentrieren ihren Inhalt per
   .section-inner. Nur noch als Stacking-Context (z-index) relevant.
   KEIN eigenes padding-bottom mehr: das erzeugte einen zusaetzlichen,
   unsichtbaren Abstand von 8rem NACH dem margin-bottom der letzten
   Section (.contact-section), der komplett ausserhalb von .site-footer
   lag -- deshalb blieb der Footer-Bereich trotz mehrfacher Reduktion
   von .site-footer's eigenem padding immer gleich hoch. Der Abstand
   zum Footer ergibt sich jetzt allein aus .contact-section's eigenem
   margin-bottom, genau wie zwischen allen anderen Sections. */
.scroll-container { position: relative; }

.content-stage {
  position: relative;
  z-index: 10;
}

/* Die Content-Bloecke bekommen explizit --bg-light als eigene
   Fuellfarbe plus Abstand zueinander -- der hellere --bg-page-Ton des
   Body wird dadurch als "Naht" zwischen den Sections sichtbar, statt
   dass Seite und Sections denselben Ton teilen und optisch
   verschmelzen. Gilt fuer alle drei Seiten: .main-intro/.intro-portrait
   (nur index.html) und .page-intro (nur beratung.html/profil.html)
   sind der jeweilige oberste Block, .portfolio-section nur index.html,
   .contact-section auf allen drei. .split-section (Beratung/Profil,
   nur index.html) gehoert bewusst NICHT in diese Liste -- sie bleibt
   selbst im --bg-page-Ton, die beiden .split-half-Kinder tragen ihr
   --bg-light einzeln (siehe dort), damit der Zwischenraum zwischen
   ihnen sichtbar bleibt statt einer durchgehenden Flaeche. */
.main-intro,
.page-intro,
.portfolio-section,
.contact-section {
  background-color: var(--bg-light);
  margin-bottom: 1.5rem;
  /* Randlose Hintergrundflaeche: die Section selbst waechst auf volle
     Bildschirmbreite. Ihr Inhalt bleibt trotzdem bei 1200px zentriert --
     siehe .section-inner.
     War width:100vw + negative Raender (klassischer Full-Bleed-Trick,
     um aus einem gepaddeten Elternelement auszubrechen) -- .content-
     stage/.scroll-container/body haben aber gar kein eigenes Padding
     oder max-width, die Section ist also schon ohne jeden vw-Trick
     randlos. 100vw war damit nicht nur unnoetig, sondern auf iOS Safari
     ein bekannter Performance-Fallstrick: vw-Einheiten koennen dort
     waehrend des Scrollens (u.a. durch die ein-/ausblendende
     Adressleiste) neu berechnet werden -- passt genau zu den kurzen,
     kompletten Scroll-Aussetzern auf dem iPhone, die Wolfram gemeldet
     hat (u.a. genau ueber dieser Section). width:100% braucht diese
     Neuberechnung nicht. */
  width: 100%;
}

/* Gemeinsamer Innen-Wrapper, der den Inhalt der randlosen Sections
   wieder auf die bekannte Content-Breite zentriert. Nur horizontales
   Padding als Longhand (padding-left/-right statt Shorthand padding):
   so kann eine Section, die zusaetzlich eigenes vertikales Padding
   braucht, das per padding-top/-bottom ergaenzen, ohne dass sich beide
   Regeln beim Zusammenspiel der beiden Klassen auf demselben Element
   gegenseitig ueberschreiben. */
.section-inner {
  max-width: 1200px;
  margin: 0 auto;
  padding-left: 5vw;
  padding-right: 5vw;
}

/* MAIN INTRO GRID (nur index.html) */
/* Kein padding-bottom: das Portrait sitzt (via align-self:stretch +
   object-fit:cover) direkt an der Unterkante der Section, jeder
   Bottom-Padding hier wuerde eine Luecke INNERHALB der Section
   erzeugen und den Sockel-Bezug zerstoeren. */
.main-intro {
  /* An --logo-bar-height gekoppelt statt eines unabhaengigen Werts --
     garantiert, dass der Content immer unterhalb der fixierten
     Logo-Bar beginnt. +3rem statt vormals +1.5rem: mehr Headroom
     zwischen Kopfleiste und Hero-Inhalt (Text/Portrait), unabhaengig
     von der kompakteren Kopfleiste selbst. */
  padding: calc(var(--logo-bar-height) + 3rem) 0 0 0;
  scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem); /* war hart 12rem -- veraltet seit die Kopfleiste verkleinert wurde, jetzt an --logo-bar-height gekoppelt wie die uebrigen Anker-Ziele */
  position: relative;
  /* Verhindert, dass .portrait-freistehends leichter Hover-Zoom
     (transform: scale(1.02)) an der Section-Kante ueber den Rand
     hinausragt. */
  overflow: hidden;
}

/* Oberster Content-Block auf beratung.html/profil.html -- dieselbe
   Farb-/Randlos-Behandlung wie .main-intro (siehe Selektor-Liste oben),
   aber mit normalem Bottom-Padding statt dem Portrait-Sockel-Sonderfall:
   hier steht nur Text/Kacheln, kein Bild, das buendig am unteren
   Section-Rand sitzen muss. */
.page-intro {
  padding: calc(var(--logo-bar-height) + 3rem) 0 4rem 0; /* +3rem statt +1.5rem, konsistent mit .main-intro */
  scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem); /* war hart 12rem -- veraltet seit die Kopfleiste verkleinert wurde, jetzt an --logo-bar-height gekoppelt wie die uebrigen Anker-Ziele */
}

/* .snapshots-section ist auf profil.html jetzt die oberste Section
   (die Wand ersetzt den frueheren separaten .page-intro-Textblock
   komplett) -- braucht daher dieselbe Kopfleisten-Frei-Raum-Behandlung
   wie .page-intro/.main-intro, obwohl sie selbst keine eigene
   .page-intro-Klasse traegt (die zieht per Selektor-Liste oben eine
   Hintergrundfarbe mit, die frueher hier bewusst nicht gewuenscht war).
   Testweise (siehe Chat) jetzt doch eine eigene, leicht waermere/dunklere
   Flaeche als --bg-page -- gibt den Fotos samt neuen engeren Schatten
   (siehe .snapshot-card) eine spuerbare Auflageflaeche, statt auf dem
   hellsten Seitenton zu "schweben". Bewusst nur ein kleiner Schritt
   (zwischen --bg-page und --bg-light), keine eigene Textur: die
   halbtransparente .logo-bar mischt sich beim Blur Richtung --bg-page
   ein, ein zu grosser Farbsprung darunter waere dort als sichtbare Naht
   aufgefallen. .film-grain (globaler Multiply-Layer) liest sich auf dem
   dunkleren Ton automatisch etwas praesenter, ganz ohne eigene Textur. */
.snapshots-section {
  padding-top: calc(var(--logo-bar-height) + 3rem);
  padding-bottom: 4rem;
  scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem);
  background-color: #f3ede0;
}

.main-intro-inner {
  display: grid;
  grid-template-columns: 1.15fr 0.85fr;
  gap: 1.5rem;
  align-items: end;
}

.intro-text {
  position: relative;
  z-index: 2;
  padding-bottom: 0.5rem;
  /* .main-intro-inner nutzt align-items:end -- dieses margin-bottom
     schiebt nur den Textblock nach oben, das Portrait daneben bleibt
     unten verankert. Schafft sichtbaren Abstand zur Section-Unterkante. */
  margin-bottom: 1.5rem;
}

.name-sub {
  font-family: 'Inter', sans-serif;
  font-size: clamp(1.3rem, 1.9vw, 1.75rem);
  font-weight: 400;
  color: var(--text-light);
  line-height: 1.35;
  letter-spacing: -0.01em;
}

.name-sub strong {
  font-weight: 700;
  color: var(--text-light);
}

/* Eigenstaendige Chip statt fliessender Fliesstext-Zeile -- liest sich
   dadurch als bewusst eingeschobener Status-Hinweis statt als weitere
   Absatzzeile, die den Lesefluss von Bio-Text zu Portraet unterbricht.
   display:inline-block, damit .name-sub-footnote (position:relative)
   der Hoehenanker fuer das Leuchtband (siehe ::after unten) bleibt --
   das geht nur, wenn der Textblock selbst positioniert ist. */
.name-sub-footnote {
  position: relative;
  margin-top: 1rem;
  display: inline-block;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem; /* statt 0.95rem, jetzt konsistent mit .portfolio-subline und anderen Fliesstext-Stellen */
  /* War --muted-light -- das war fuer die fruehere Chip-Optik (mit
     Hintergrund) gedacht; ohne eigenen Hintergrund (siehe oben) wirkte
     der Text zu blass. --text-secondary ist der Ton fuer echten
     Fliesstext (siehe Definition oben), die Fussnote soll mindestens
     so dunkel wie dieser sein, nicht wie ein Nebensaechlichkeits-Label. */
  color: var(--text-secondary);
}

/* Der Markierungsstrich (::before, senkrechter Strich links) ist raus
   -- stattdessen traegt jetzt allein das Leuchtband unten die
   "hier ist was Aktuelles"-Rolle.

   Echte Welle statt Sprung: vorherige Version animierte nur
   background-position mit einer langen Rest-Plateau-Phase (30%-100%
   auf demselben Wert) -- dabei blieb an beiden Enden des Zyklus ein
   Randstueck des Bands sichtbar (Hintergrundposition in % bezieht sich
   auf den UEBERSCHUSSRAUM, nicht die Containerbreite, -60%/160% bei
   55% Bandbreite reichten nicht aus, um es wirklich ausserhalb des
   sichtbaren Bereichs zu schieben) -- beim Zuruecksetzen auf den
   Startwert entstand dadurch ein sichtbarer Sprung. Jetzt zusaetzlich
   eine eigene Deckkraft-Huelle (0 -> Maximum -> 0), synchron zur
   durchgehenden Bewegung von -80% bis 180% (rechnerisch weit genug im
   Unsichtbaren fuer 35% Bandbreite) -- die Bewegung pausiert an
   keiner Stelle des Zyklus mehr. Deutlich gedaempfter (18% statt 38%
   Petrol-Anteil) und langsamer (14s statt 8s) fuer einen ruhigen,
   nie hektischen Puls. z-index:-1 setzt das Band hinter den normalen
   Inline-Textfluss (siehe Stacking-Reihenfolge), inset ueber die volle
   (ggf. mehrzeilige) Blockhoehe, wirkt bei mehreren Zeilen wie ein
   Lichtstreifen, der senkrecht durch den ganzen Absatz wandert. */
/* Nach zwei Anlaeufen mit einem Hintergrund-Fleck (erst linearer
   Streifen, dann weicher radialer Fleck mit/ohne Blur -- beide Male
   Kanten-Artefakte in Safari, die trotz mehrfacher Groessen-/Blur-
   Anpassungen nicht zuverlaessig weggingen) jetzt ein grundsaetzlich
   anderer, einfacherer Mechanismus: eine duenne Linie UNTER der
   Fussnote, die in derselben Geschwindigkeit/Choreografie (siehe
   footnote-sweep-bg) links nach rechts durchlaeuft, ein-/ausblendend.
   Keine Flaeche/kein Blur mehr -- nur ein 2px hoher linearer
   Verlauf, der randlos in Transparenz ausläuft (position:absolute mit
   left/right:0 statt inset auf allen Seiten, da hier keine Hoehe/Breite
   des Textblocks mehr nachgebildet werden muss).

   DESIGN-ANGLEICHUNG an die Portrait-Kritzel-Pfeile (statt des
   frueheren weichen Gradient-Streifens): jetzt ein handgezeichneter,
   leicht welliger SVG-Unterstrich in derselben Strichoptik/Farbe
   (Petrol, runde Kappen), der sich periodisch von links "hinkritzelt",
   kurz stehen bleibt, ausblendet und nach einer Pause wiederkommt --
   Markup siehe .footnote-underline in index.html.

   WICHTIG (iOS-Scroll-Performance, Lehre aus der Haenger-Jagd): KEINE
   endlose stroke-dashoffset-Animation wie bei den Pfeilen selbst (die
   laufen nur einmalig bei Hover/Tap) -- das waere dauerhafter Main-
   Thread-Paint pro Frame. Stattdessen der Vorhang-Trick, komplett auf
   dem Compositor (GPU): der aeussere Clip-Container (overflow:hidden)
   startet um -101% nach links verschoben, die innere Linie gegenlaeufig
   um +101% nach rechts -- die Linie steht dadurch optisch an Ort und
   Stelle, ist aber komplett aus dem Clip-Fenster geschoben. Animieren
   beide synchron (gleiches Keyframe-Segment, gleiche Dauer/Easing)
   auf 0, waechst das sichtbare Fenster von links nach rechts ueber die
   stillstehende Linie -- sieht aus wie Einzeichnen, ist aber nur
   transform+opacity. */
.footnote-underline {
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.6em;
  height: 9px;
  overflow: hidden;
  pointer-events: none;
  transform: translateX(-101%);
  animation: footnote-underline-clip 12s ease-in-out infinite;
}

.footnote-underline-line {
  display: block;
  width: 100%;
  height: 100%;
  transform: translateX(101%);
  animation: footnote-underline-draw 12s ease-in-out infinite;
}

/* Waehrend body.poem-active (Gedicht-Phase vor dem Klick-Uebergang,
   siehe JS) liefe die Animation sonst unsichtbar weiter, obwohl
   #introText (der Elternbereich der Fussnote) noch verborgen ist --
   CSS-Animationen pausieren nicht automatisch bei opacity:0. Pausiert
   waehrend der gesamten Gedicht-Phase (JS setzt die Klasse synchron
   vor dem ersten Paint), startet beim Entpausieren garantiert vom
   eingefrorenen ersten Frame (unsichtbar) aus sauber -- dieselbe
   Massnahme wie beim frueheren Gradient-Sweep. */
body.poem-active .footnote-underline,
body.poem-active .footnote-underline-line {
  animation-play-state: paused;
}

/* Choreografie (12s-Zyklus, bewusst mit langer Pause -- die Linie soll
   ein gelegentlicher Akzent sein, kein Dauerblinker): kurze Ruhe,
   Einzeichnen 5%->10% (~0.6s, flotter als die erste Fassung mit
   0.85s -- der ganze Durchlauf soll zuegig vorbeigehen), kurzes
   Stehenbleiben bis 19%, dann WEGWISCHEN 19%->24% in derselben
   Richtung und demselben Tempo wie das Einzeichnen (Clip faehrt auf
   +101%, die linke Fensterkante schiebt sich hinterher und radiert
   die Linie von links nach rechts weg), Rest = Pause. Sichtbarer
   Durchlauf insgesamt ~2.3s. Opacity dient nur als Schutz fuers
   UNSICHTBARE Zuruecksetzen der Transforms (25%->27%): die Rueckfahrt
   beider Elemente liefe sonst sichtbar durchs Fenster.
   Beide Keyframe-Saetze muessen an den Bewegungs-Segmenten dieselben
   Prozentmarken tragen, sonst laufen Clip-Fenster und Gegenschub
   auseinander und die Linie "schwimmt" statt sich einzuzeichnen. */
@keyframes footnote-underline-clip {
  0%   { transform: translateX(-101%); opacity: 1; }
  5%   { transform: translateX(-101%); }
  10%  { transform: translateX(0); }
  19%  { transform: translateX(0); }
  24%  { transform: translateX(101%); opacity: 1; }
  25%  { transform: translateX(101%); opacity: 0; }
  27%  { transform: translateX(-101%); opacity: 0; }
  28%  { transform: translateX(-101%); opacity: 1; }
  100% { transform: translateX(-101%); opacity: 1; }
}

@keyframes footnote-underline-draw {
  0%   { transform: translateX(101%); }
  5%   { transform: translateX(101%); }
  10%  { transform: translateX(0); }
  19%  { transform: translateX(0); }
  24%  { transform: translateX(-101%); }
  25%  { transform: translateX(-101%); }
  27%  { transform: translateX(101%); }
  100% { transform: translateX(101%); }
}

@media (prefers-reduced-motion: reduce) {
  .footnote-underline {
    display: none;
  }
}

.name-sub-footnote a {
  display: inline-block;
  position: relative;
  color: var(--text-light);
  text-decoration: none;
}

/* PORTRAIT INTEGRATION (nur index.html) */
.intro-portrait {
  position: relative;
  z-index: 1;
  display: flex;
  justify-content: flex-end;
  align-items: flex-end;
  align-self: stretch;
  /* Feste, echte Bezugsgroesse fuer .portrait-freistehend's height:100% --
     entspricht dem tatsaechlichen Seitenverhaeltnis von
     wolfram-huke-cutout.png (1872x1422, Hund+Mensch nebeneinander,
     daher deutlich breiter als das fruehere quadratische Portrait).
     Ersetzt das frühere min-height:32rem auf .main-intro-inner, das die
     ganze Grid-Zeile auf eine hoehere/schmalere Form zwang als das Foto
     hergibt und dadurch unnoetig stark beschnitt. */
  aspect-ratio: 1872 / 1422;
}

/* <picture> ist per Default nur inline und wuerde sich sonst als
   Flex-Kind auf seine Bildgroesse zusammenziehen statt wie zuvor das
   <img> direkt die volle Flaeche von .intro-portrait auszufuellen --
   ohne diese Regel wuerde die width:100%/height:100% von
   .portrait-freistehend gegen eine unbestimmte Groesse aufloesen. */
.intro-portrait picture {
  display: block;
  width: 100%;
  height: 100%;
}

.portrait-freistehend {
  display: block;
  width: 100%;
  height: 100%;
  min-height: 0;
  object-fit: cover;
  object-position: center top;
  opacity: 0.95;
  will-change: transform, filter;
  transition: filter 0.6s ease, transform 0.6s ease, opacity 0.6s ease;
  transform-origin: bottom center;
  mix-blend-mode: multiply;
  position: relative;
  z-index: 1;
}

/* KRITZEL-EASTEREGG: Filzstift-artige Pfeile + handschriftliche Labels
   ("Elmo"/"Wolfram"), die bei Hover auf das Hero-Portrait einblenden --
   rein spielerisch, kein Informationsgewinn (beide sind im Foto
   eindeutig erkennbar), deshalb bewusst NUR bei echtem Hover sichtbar
   (siehe Media-Query unten, gleiches Gate wie .portrait-freistehend
   oben) und nicht dauerhaft eingeblendet. Farben bewusst NICHT an die
   "Orange exklusiv fuer Interaktion"-Konvention gebunden -- das ist
   hier explizit ein dekorativer Sonderfall, kein UI-Element.

   Baut sich wie echtes Reinkritzeln auf statt nur einzublenden: die
   Linie "zeichnet" sich zuerst per stroke-dashoffset (SVG pathLength=1-
   Trick, siehe HTML -- dadurch reicht ein CSS-Wert von 0/1 unabhaengig
   von der tatsaechlichen Pfad-Laenge), dann zeichnet sich die
   Pfeilspitze (zwei kurze Striche, dieselbe Dashoffset-Technik --
   bewusst KEIN gefuelltes Dreieck, das saehe "ausgemalt" statt
   handgezeichnet aus), zuletzt wischt sich das Label per clip-path
   von links nach rechts frei (wie geschrieben, nicht nur eingeblendet)
   -- alles zeitversetzt per transition-delay, dieselbe Reihenfolge,
   in der man von Hand kritzeln wuerde. */
.portrait-scribble {
  position: absolute;
  z-index: 3;
  width: 7rem;
  pointer-events: none;
  /* Sicherheitsnetz gegen einen bekannten SVG-Rendering-Fall: bei
     stroke-dasharray/dashoffset-animierten Pfaden mit stroke-linecap:
     round kann am Off-Zustand (dashoffset:1, siehe .scribble-line/
     .scribble-arrowhead) ein winziger, aber sichtbarer Punkt am
     Pfad-Startpunkt uebrig bleiben, obwohl der "Strich" selbst
     unsichtbar sein sollte -- genau das war vor Hover als zwei kleine
     Farbpunkte (Teal/Orange) im Foto sichtbar. opacity:0 auf dem
     ganzen Kritzel-Container schliesst das zuverlaessig aus,
     unabhaengig von der Pfad-Animation selbst. transition ohne delay
     (im Gegensatz zu Linie/Spitze/Label, die weiter gestaffelt
     "reinkritzeln"), damit beim Hover-Start sofort alles sichtbar
     werden KANN -- der zeitliche Ablauf kommt weiterhin von den
     einzelnen dashoffset/clip-path-Transitions ihrer Kinder. */
  opacity: 0;
  /* Asymmetrisch: schnelles Einblenden (0.05s, siehe Kommentar oben --
     das Sicherheitsnetz gegen den Punkt-Bug braucht sofortiges
     opacity:1), aber langsames Ausblenden (0.65s, deckt die volle
     Ruecknahme-Sequenz von Label/Pfeilspitze/Linie ab, siehe deren
     delays), damit beim Verlassen des Hovers Zeit fuer eine echte
     Ruecknahme-Animation bleibt statt eines harten Abschneidens. Die
     schnelle Einblend-Dauer wird auf den Hover-/is-scribbling-Regeln
     unten per transition-duration ueberschrieben (die gelten nur beim
     Reingehen); ohne Hover greift wieder diese langsamere Basis-Dauer. */
  transition: opacity 0.65s ease;
}

/* War oben/rechts-buendig wie .portrait-scribble-wolfram -- jetzt
   umgedreht: Pfeil kommt von unten-links (darf per negativem left
   auch links aus dem Foto herauslaufen, .intro-portrait selbst hat
   kein overflow:hidden) und zeigt nach oben-rechts auf den Kopf des
   Hundes, statt das Foto von oben zu ueberdecken. */
.portrait-scribble-elmo   { top: 42%; left: -12%; }
.portrait-scribble-wolfram { top: 3%; right: 3%; text-align: right; }

.scribble-arrow {
  display: block;
  width: 100%;
  height: auto;
}

/* stroke-linecap hier bewusst auf "butt" ueberschrieben -- das HTML
   traegt noch stroke-linecap="round" (Praesentations-Attribut), diese
   Klassenregel gewinnt aber durchgehend, nicht nur waehrend der
   Animation. Grund: in Safari zeigten sich waehrend der dashoffset-
   Animation kurzzeitig kleine, farbige Punkte neben den Pfeilen (in
   Chrome nicht reproduzierbar) -- bekanntes WebKit-Verhalten bei
   runden Strichenden auf sehr kurzen/fast-Null-langen Dash-Segmenten
   waehrend der Animation: ein rundes Strichende rendert dort
   kurzzeitig als volle Kreisflaeche. "butt" (flaches Ende) hat dieses
   Problem nicht -- optisch bei 3.5px Strichbreite kaum ein
   Unterschied. */
/* transition-delay hier (0.25s) gilt nur fuers Zurueckziehen (Ausgang
   ohne Hover/is-scribbling): die Linie wurde zuerst gezeichnet, soll
   beim Verschwinden also zuletzt zurueckgezogen werden -- nach Label
   und Pfeilspitze (siehe deren kuerzere/keine Delays unten), fuer eine
   Ruecknahme in umgekehrter Reihenfolge statt eines gleichzeitigen
   Verschwindens aller drei. Die Hover-/is-scribbling-Regeln setzen für
   den Eingang explizit delay:0s (dort weiter unten), sonst wuerde
   dieser Exit-Delay auch das Reinzeichnen verzoegern. */
.scribble-line {
  stroke-linecap: butt;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 0.35s ease 0.25s;
}

/* War ein gefuelltes Dreieck (<polygon>) -- wirkte "ausgemalt" statt
   handgezeichnet. Jetzt zwei kurze Striche (wie die Hauptlinie per
   pathLength=1-Trick gezeichnet), die dieselbe Dashoffset-Technik
   nutzen -- konsequent derselbe "wird gerade gekritzelt"-Look wie bei
   Linie und Label, nicht nur ein Opacity-Pop. */
.scribble-arrowhead {
  stroke-linecap: butt;
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  /* delay 0.1s: zieht sich beim Ausgang nach dem Label, aber vor der
     Linie zurueck (umgekehrte Zeichenreihenfolge), siehe .scribble-line. */
  transition: stroke-dashoffset 0.15s ease 0.1s;
}

/* War 'Permanent Marker' -- optisch eine Druckschrift im Filzstift-
   Look (gleichmaessige Blockbuchstaben), keine echte Handschrift.
   'Caveat' ist tatsaechlich kursiv/verbunden, liest sich naeher an
   von Hand geschrieben -- braucht dafuer eine groessere font-size,
   um bei gleicher optischer Praesenz wie vorher anzukommen (Caveats
   Buchstaben sind schmaler/duenner als die von Permanent Marker). */
/* Statt Einfaden ein Links-nach-rechts-Wisch per clip-path -- liest
   sich naeher an "wird gerade geschrieben" als ein reines Opacity-
   Fade. inset(0 100% 0 0) blendet den Text komplett rechts ab (0%
   sichtbare Breite), auf inset(0 0% 0 0) animiert zeigt ihn komplett.
   rotate bleibt bestehen: clip-path rechnet in der lokalen, noch
   unrotierten Box, der Wisch bleibt also relativ zur (leicht
   gekippten) Textzeile horizontal, nicht absolut zum Bildschirm. */
/* display:block liess das Label auf die volle Breite von
   .portrait-scribble (7rem) strecken -- bei 2rem Caveat war "Wolfram"
   breiter als das, der Text lief ueber die eigene Box hinaus. clip-path
   bezieht sich aber auf die Box selbst, nicht auf den ueberlaufenden
   Inhalt -- das ueberstehende Stueck (der letzte Buchstabe) wurde
   dadurch permanent abgeschnitten, nicht nur waehrend der Animation.
   inline-block + white-space:nowrap laesst die Box exakt am Text
   entlang wachsen, dann trifft clip-path die richtige Kante. */
.scribble-label {
  display: inline-block;
  white-space: nowrap;
  font-family: 'Caveat', cursive;
  font-weight: 700;
  font-size: 2rem;
  line-height: 1;
  transform: rotate(-2deg);
  clip-path: inset(0 100% 0 0);
  transition: clip-path 0.5s ease;
}

.portrait-scribble-wolfram .scribble-label {
  transform: rotate(2deg);
}

.scribble-label-elmo    { color: var(--accent-2); }
.scribble-label-wolfram { color: var(--accent); }

/* Zoom bleibt bei Hover, das Scribble-Kritzel-Easteregg gibt es jetzt
   NUR NOCH per Klick/Tap (.is-scribbling weiter unten) -- vorher loeste
   Hover beides zusammen aus, was Scribble zum Zufallstreffer bei jedem
   Drueberfahren machte statt zum bewussten Ueberraschungsmoment (siehe
   Chat). Reine :hover-Regel statt @media(hover:hover)/pointer:fine:
   die Media Query war ohnehin nur noetig, um Sticky-Hover-Effekte nach
   einem Tap auf Touch zu vermeiden -- bei diesem kleinen, kaum
   sichtbaren Zoom (1.02) ist ein etwaiges kurzes Haengenbleiben nach
   einem Tap vernachlaessigbar, anders als beim vorher deutlich
   auffaelligeren Scribble. Zugleich entfaellt damit die iPadOS-
   Eigenheit, dass (hover:hover) trotz angeschlossenem Trackpad immer
   false liefert (siehe Seitentitel-Kommentar) -- echtes :hover
   funktioniert dort ohnehin zuverlaessig, nur die Media Query luegt. */
.intro-portrait:hover .portrait-freistehend {
  opacity: 1;
  transform: scale(1.02);
}

/* Touch-Pendant zum Hover-Kritzel-Easteregg oben -- ohne echtes Hover
   (Touch-Geraete) passierte bisher gar nichts, das Easteregg existierte
   dort effektiv nicht. .is-scribbling wird per JS (siehe Skript vor
   </body>) beim Tap auf #introPortrait gesetzt und nach ein paar
   Sekunden wieder entfernt -- dasselbe Muster wie das fruehere
   Elmo-Peek-Easteregg (Tap toggelt sichtbar, kein Dauerzustand).
   Bewusst AUSSERHALB der hover:hover-Media-Query (die gilt nur fuer
   echtes :hover, um Sticky-Hover-Effekte nach einem Tap zu vermeiden),
   dieselben Endzustaende wie im Hover-Block oben, nur ueber die Klasse
   statt :hover ausgeloest. */
.intro-portrait.is-scribbling .portrait-scribble {
  opacity: 1;
  transition-duration: 0.05s;
}

.intro-portrait.is-scribbling .scribble-line {
  stroke-dashoffset: 0;
  transition-duration: 0.45s;
  transition-delay: 0s;
}

.intro-portrait.is-scribbling .scribble-arrowhead {
  stroke-dashoffset: 0;
  transition-duration: 0.18s;
  transition-delay: 0.4s;
}

.intro-portrait.is-scribbling .scribble-label {
  /* -15% statt 0% -- derselbe Sicherheitsabstand wie bei der Hover-
     Variante oben (siehe dortiger Kommentar), hier beim Tap-Pendant
     versehentlich vergessen. Genau DAS war der Grund, warum "Elmo" auf
     Touch-Geraeten (Tap loest .is-scribbling aus, nicht :hover) trotz
     des Hover-Fixes weiter angeschnitten blieb -- Mobile nutzt
     ausschliesslich diesen Pfad, nie :hover. */
  clip-path: inset(0 -15% 0 0);
  transition-delay: 0.5s;
}


/* BERATUNG/PROFIL (nur index.html) -- zwei eigenstaendige Flaechen mit
   echtem, sichtbarem Zwischenraum (--bg-page scheint durch), statt
   einer durchgehenden Box mit zwei Spalten wie beim frueheren
   .columns-grid. Kein separater max-width-Wrapper mehr (frueherer
   Fehler: dadurch reichte die Farbflaeche selbst nur bis 1200px statt
   randlos wie alle anderen Sections, plus verdoppeltes Padding durch
   Wrapper+Karte zusammen) -- .split-section traegt das Grid UND das
   randlose width:100vw direkt, exakt dieselbe Technik wie
   .main-intro/.portfolio-section. Jede .split-half traegt ihre eigene
   --bg-light-Flaeche bis zum jeweiligen Bildschirmrand, nur der
   3rem-Grid-Gap in der Mitte bleibt --bg-page (der Section-eigene
   Grundton scheint dort durch). */
/* width:100% statt 100vw + negativer Raender -- siehe ausfuehrlichen
   Kommentar bei .contact-section, dieselbe Ursache/derselbe Fix: der
   vw-Full-Bleed-Trick war hier unnoetig (kein Elternelement mit
   Padding/max-width zum Ausbrechen) und verursachte auf iOS Safari
   Scroll-Aussetzer, weil vw-Einheiten sich waehrend des Scrollens mit
   ein-/ausblendender Adressleiste laufend neu berechnen -- betraf laut
   Meldung genau die Beratung/Ueber-Kacheln dieser Section. */
.split-section {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.5rem;
  width: 100%;
  margin-bottom: 1.5rem;
  background-color: var(--bg-page);
}

.split-half {
  background-color: var(--bg-light);
  padding-top: 4rem;
  padding-bottom: 4rem;
}

/* Innenabstand zur Bildschirmmitte hin bleibt 5vw (Rhythmus wie
   .section-inner), aber zum jeweiligen Aussenrand hin waechst er mit,
   sobald der Screen breiter als 1200px wird -- so faengt der Text in
   beiden Haelften exakt auf Hoehe der 1200px-Content-Kante von
   .main-intro/.portfolio-section an/auf, statt bei sehr breiten Screens
   naeher am Rand zu stehen als der Content darueber/darunter.
   border-radius jetzt hier statt auf der Basisregel, und nur noch an
   der Seite zum GAP hin (nicht mehr rundum) -- .split-section ist
   randlos (siehe Kommentar dort), beide Haelften stossen also links/
   rechts direkt an den Bildschirmrand. Eine rundum gleiche Rundung
   liess dort (auf jeder Breite, nicht nur bestimmten Formaten wie
   iPad-Querformat) eine sichtbar abgeschnittene Ecke genau an der
   Fensterkante entstehen, statt schlicht buendig abzuschliessen. */
.split-half:first-child {
  padding-left: max(5vw, calc((100vw - 1200px) / 2 + 5vw));
  padding-right: 5vw;
  border-radius: 0 1rem 1rem 0;
}

.split-half:last-child {
  padding-left: 5vw;
  padding-right: max(5vw, calc((100vw - 1200px) / 2 + 5vw));
  border-radius: 1rem 0 0 1rem;
}

/* ÜBER (nur index.html) -- eigenstaendige Vollbreiten-Section statt
   Kachel in .split-section, seit Formatentwicklung deren Platz im
   Zwei-Spalten-Raster uebernommen hat. Gleiche --bg-light-Faerbung wie
   .split-half, aber randlos mit ueber .section-inner zentriertem Inhalt
   (wie .main-intro/.page-intro/.contact-section), statt der zweigeteilten
   Rand-Innenabstands-Formel von .split-half. */
.col-card-full {
  background-color: var(--bg-light);
  margin-bottom: 1.5rem;
  width: 100%;
  padding-top: 4rem;
  padding-bottom: 4rem;
}

/* Foto in der Ueber-Section: zweispaltig, Bild links/Text rechts --
   bewusst gespiegelt zur Hero-Section (.main-intro-inner: Text links,
   Bild rechts 1.15fr/0.85fr), damit beide Bloecke visuell aufeinander
   antworten statt identisch zu wirken. Kein "freistehend"-Cutout wie
   das Hero-Portrait, sondern ein normales Rechteckfoto (TV-Screenshot,
   16:9) -- deshalb vertikal zentriert statt bodenbuendig, und mit
   eigener Bildumrandung (border-radius) statt randlos ins Layout
   einzufliessen. */
.profil-grid {
  display: grid;
  grid-template-columns: 0.85fr 1.15fr;
  gap: 2.5rem;
  align-items: center;
}

.profil-image picture,
.profil-image img {
  display: block;
  width: 100%;
}

/* overflow:hidden hier (statt nur border-radius auf dem img) noetig,
   damit der leichte Hover-Zoom (siehe unten) nicht ueber die
   abgerundeten Ecken hinaus rechteckig herausragt. */
.profil-image picture {
  overflow: hidden;
  border-radius: 0.75rem;
}

/* Leicht entsaettigt im Ruhezustand, volle Farbe + dezenter Zoom bei
   Hover -- gleiches Prinzip wie die Murder-Wall-Fotos auf profil.html
   (siehe dortiger Kommentar zu .snapshot-card img), hier aber deutlich
   subtiler (35% statt deren 95%): das ist ein einzelnes redaktionelles
   Portraitfoto, kein "Noir"-Ermittlungswand-Look. */
.profil-image img {
  aspect-ratio: 16 / 9;
  object-fit: cover;
  filter: grayscale(35%) contrast(1.02);
  transition: filter 0.3s ease, transform 0.3s ease;
}

.profil-image:hover img,
.profil-image:focus-within img {
  filter: grayscale(0%) contrast(1);
  transform: scale(1.03);
}

/* An dieselbe Variable gekoppelt wie .main-intro/.page-intro's
   Top-Padding (--logo-bar-height), damit der Anker-Scroll-Versatz nie
   wieder von der tatsaechlichen Logo-Bar-Hoehe abweicht. Generisch auf
   .col-card selbst statt einzelner IDs (#beratung-section/
   #profil-section) -- gilt automatisch fuer jede neue Teaser-Kachel,
   ohne dass beide Werte auseinanderlaufen koennen. */
.col-card { scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem); }

/* Die ganze Kachel ist jetzt klickbar (siehe JS: leitet einen Klick
   irgendwo auf der Kachel an ihren echten .col-card-link weiter), nicht
   mehr nur der kleine "Mehr dazu"-Link unten -- sonst gab es mitten in
   einer visuell zusammenhaengenden Flaeche unsichtbare Bereiche, auf
   denen nichts passierte. cursor:pointer signalisiert das zusaetzlich.
   Als Hover-Highlight bewusst dieselbe innen liegende Kontur wie
   .nav-flash (siehe @keyframes nav-target-outline) statt eines eigenen
   Schatten/Anheben-Stils -- dieselbe visuelle Sprache fuer "hier ist
   etwas interaktiv" an beiden Stellen (siehe Chat: "waere konsistenter").
   Dauerhaft waehrend des Hovers statt nur kurz aufzublitzen: ein Klick
   auf die Kachel navigiert sofort weg, ein Flash-vorher waere kaum zu
   sehen -- bei .nav-flash dagegen bleibt man auf der Seite, dort macht
   der kurze Flash als Ankunfts-Bestaetigung Sinn. */
.col-card {
  cursor: pointer;
  position: relative;
}

/* Kontur ueber ein eigenes ::after statt direkt per box-shadow auf
   .col-card (Nutzerwunsch: an der Seite, wo eine Kachel an die echte
   Browserfensterkante stoesst, soll GAR KEINE Umrandung erscheinen --
   der Hintergrund soll dort optisch "weiterlaufen" statt eingerahmt zu
   wirken). box-shadow kennt keine einzelnen Seiten, border schon
   (border-*-width je Seite auf 0 setzbar) -- deshalb border statt
   box-shadow, transparent in Ruhe, damit border-radius:inherit (siehe
   unten) trotzdem schon die richtige Form fuer die spaetere
   Hover-Farbe vorbereitet. */
.col-card::after {
  content: '';
  position: absolute;
  inset: 0;
  pointer-events: none;
  /* Uebernimmt automatisch die (teils asymmetrische, siehe
     .split-half:first-child/:last-child) Eckenrundung der jeweiligen
     Kachel -- eine einzelne Ecke, die zufaellig an der Fensterkante
     liegt, ist dort ohnehin schon 0 (siehe dortiger Kommentar), passt
     also von selbst zusammen mit den unten abgeschalteten Seiten. */
  border-radius: inherit;
  border: 2px solid transparent;
  transition: border-color 0.2s ease;
}

/* Volles --accent (#FF4D00) rundum eine ganze grosse Kachel wirkte laut,
   eher wie eine Alarm-Umrandung als ein dezenter Hinweis (siehe Chat)
   -- reduzierte Deckkraft statt der vollen Akzentfarbe, liest sich mehr
   als "hier ist etwas interaktiv" statt als Ausrufezeichen. rgba statt
   var(--accent) direkt, da CSS Custom Properties keine eingebaute
   Opazitaets-Anpassung kennen; FF4D00 fest als rgb(255, 77, 0) notiert. */
.col-card:hover::after,
.col-card:focus-visible::after {
  border-color: rgba(255, 77, 0, 0.55);
}

/* Kommunikation stoesst links an die Fensterkante (.split-half:first-
   child), Ideen & Formate rechts (.split-half:last-child), Ueber an
   beide (.col-card-full, randlos nach beiden Seiten) -- dort jeweils
   die Kontur-Seite kappen, die die drei uebrigen Seiten bleiben aktiv. */
.split-half:first-child::after {
  border-left-width: 0;
}

.split-half:last-child::after {
  border-right-width: 0;
}

.col-card-full::after {
  border-left-width: 0;
  border-right-width: 0;
}

/* Kurzer Highlight-Flash beim Anspringen eines Nav-Ziels -- macht
   sichtbar, WELCHES Element gerade angesprungen wurde (jede Teaser-
   Kachel/Section traegt ihre ID direkt auf sich selbst, nie auf einem
   gemeinsamen Wrapper wie .split-section). Innen liegende Kontur statt
   Flaechen-Flash, damit die eigentliche --bg-light-Fuellfarbe der
   Section/Kachel unangetastet bleibt -- per JS nur fuer Beratung/
   Formatentwicklung/Ueber/Kontakt und nur auf Desktop-Breite ausgeloest
   (siehe dortiger Kommentar), Film & TV bleibt ohne Effekt. */
@keyframes nav-target-outline {
  0%   { box-shadow: inset 0 0 0 2px var(--accent); }
  100% { box-shadow: inset 0 0 0 2px transparent; }
}

.nav-flash {
  animation: nav-target-outline 1.1s ease-out;
}

@media (prefers-reduced-motion: reduce) {
  .nav-flash { animation: none; }
}

/* War rgba(20,16,13,0.55) -- beim ersten Petrol-Umbau uebersehen, da
   diese Regel (Homepage-Teaser-Karten) separat von .page-kicker (echte
   Unterseiten) existiert, obwohl beide optisch dieselbe Rolle spielen.
   Jetzt an --accent-2 angeglichen. */
.col-card h2 {
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-bottom: 1.5rem;
}

/* Dieselbe Typo-Ebene wie .name-sub (Hero-Text), damit keine Headline
   staerker hervorsticht. War eine reine Space-Grotesk-Regel, die fuer
   die existierenden Vorkommen wirkungslos war und als letzte Space-
   Grotesk-Referenz der einzige Grund, die Font ueberhaupt zu laden;
   deshalb samt Font-Einbindung entfernt. Jetzt generisch auf .col-card
   selbst statt auf einzelne IDs (#beratung-section/#profil-section)
   gemuenzt, damit jede neue Teaser-Kachel (z.B. Formatentwicklung)
   automatisch dieselbe Typografie bekommt. */
.col-card h3 {
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  font-size: clamp(1.3rem, 1.9vw, 1.75rem);
  letter-spacing: normal;
  margin-bottom: 1rem;
}

.col-card p,
.split-half p {
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  font-weight: 400;
  color: var(--text-secondary);
  line-height: 1.55;
}

/* Nicht Teil der Filmemacher/Journalist-Karten (die verlinken nirgendwo
   hin) -- nur die Beratung/Profil-Teaser haben einen CTA-Link zur
   jeweiligen Unterseite. Orange, weil er (anders als der neutrale
   Kicker/die Karten selbst) tatsaechlich interaktiv ist -- passend zur
   Regel "Orange bleibt exklusiv fuer Interaktion". */
.col-card-link {
  display: inline-block;
  position: relative;
  margin-top: 1.5rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
}

/* TEXTSEITEN (beratung.html, profil.html) -- Kicker/Headline/Lead.
   War rgba(20,16,13,0.55) (neutrales Grau) -- jetzt --accent-2 (Petrol):
   rein informatives Label, kein Link, daher NICHT --accent (Orange
   bleibt exklusiv fuer Interaktion). */
.page-kicker {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-bottom: 1.5rem;
}

/* Fluchtweg-Link zur klassischen Textversion (langweilige-version.html,
   siehe Chat) -- klein und rechtsbuendig, damit er neben dem grossen
   Tape-Titel nicht konkurriert, aber trotzdem als Erstes sichtbar ist.
   Kein eigener margin-top noetig (siehe .col-card-link-Basisregel: 1.5rem,
   gedacht fuer einen Link NACH einem Absatz -- hier steht er ganz am
   Seitenanfang), daher hier auf 0 zurueckgesetzt. */
.wall-boring-link {
  text-align: right;
  margin-bottom: 1.5rem;
}

.wall-boring-link .col-card-link {
  margin-top: 0;
  font-size: 0.85rem;
}

/* Grosses Tape statt klassischer <h1>-Textzeile ueber der Wand
   (profil.html) -- einziges echtes Gaffer-Tape-Element auf der Seite
   (die kleinen Beschriftungen auf den Polaroids selbst sind seit Kurzem
   direkt auf den Rand geschrieben statt auf einem eigenen Tape, siehe
   .snapshot-caption), daher hier bewusst als einziges auffaelliges
   Element. width:fit-content + margin:auto
   statt text-align:center auf dem Elternelement, damit nur das Tape
   selbst zentriert wird (der Rest von .section-inner bleibt linksbuendig).
   Traegt jetzt zwei Textzeilen (Name + "Wer ist er wirklich?") statt nur
   einer -- beide auf demselben Tape, per Nutzerwunsch: die Frage soll
   handschriftlich mit demselben Marker wirken, nicht als separate,
   andersartige Zeile darunter (die vorherige .wall-subtitle-Loesung).
   Kein eigenes font-* mehr direkt hier, das steht jetzt bei
   .wall-title-tape-name/-tagline, damit beide unterschiedliche Groessen
   bei gleicher Schrift bekommen koennen. */
.wall-title-tape {
  display: block;
  width: fit-content;
  /* War 2.5rem Abstand nach unten -- zusammen mit dem eigenen
     Innenabstand und der Wand-Polsterung (siehe .snapshot-wall) entstand
     dadurch besonders auf Mobile viel Leerraum bis zu den Post-its
     (siehe Chat). */
  margin: 0 auto 1.5rem;
  padding: 0.6rem 2.2rem;
  background:
    repeating-linear-gradient(124deg, rgba(0, 0, 0, 0.05) 0px, rgba(0, 0, 0, 0.05) 1px, transparent 1px, transparent 3px),
    linear-gradient(155deg, #d8dadd 0%, #a4a7ac 45%, #c3c5c9 75%, #aeb1b6 100%);
  /* Bewusst KEIN box-shadow: Tape klebt flach auf der Oberflaeche,
     anders als die Post-its/Polaroids (die wirken angepinnt/erhoben
     und werfen deshalb einen Schatten). */
  transform: rotate(-1.5deg);
  text-align: center;
}

.wall-title-tape-name,
.wall-title-tape-tagline {
  display: block;
  font-family: 'Permanent Marker', cursive;
  /* Nur font-weight:400 im @font-face hinterlegt -- ein hoeherer Wert
     wuerde vom Browser als (meist haessliches) synthetisches Fett
     nachgebaut, statt einfach zu greifen. */
  font-weight: 400;
  color: #202225;
}

/* War clamp(2rem, 4.6vw, 3.1rem) -- wirkte im Vergleich zu den
   restlichen Karten der Wand ueberdimensioniert (siehe Chat), gilt fuer
   alle Breiten gleichermassen. */
.wall-title-tape-name {
  font-size: clamp(1.7rem, 3.8vw, 2.5rem);
}

/* Deutlich kleiner als der Name (ca. 40%), aber gleiche Schrift/Farbe --
   liest sich wie eine zweite, kleiner geschriebene Zeile auf demselben
   Klebeband, nicht wie ein separates Element. Knapper margin-top, damit
   beide Zeilen optisch zusammengehoeren statt auseinanderzudriften. */
.wall-title-tape-tagline {
  margin-top: 0.1em;
  font-size: clamp(0.75rem, 1.55vw, 1.05rem);
}

.page-heading {
  font-size: clamp(2.4rem, 5vw, 4rem);
  font-weight: 400;
  margin-bottom: 2rem;
}

.page-lead {
  font-family: 'Inter', sans-serif;
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  font-weight: 400;
  color: var(--muted-light);
  line-height: 1.6;
  max-width: 640px;
}

/* .page-lead war urspruenglich als einzelner Absatz gedacht -- bei
   mehreren (z.B. formatentwicklung.html, ein Absatz pro Szenario)
   sitzen die Absaetze wegen des globalen margin:0-Resets sonst ohne
   jeden Abstand aufeinander. */
.page-lead + .page-lead {
  margin-top: 1rem;
}

/* Fliesstext-Bereich fuer Datenschutz/Impressum -- reine Rechtstexte
   mit mehreren Abschnitten (h2), Absaetzen, gelegentlich einer Liste
   oder Postanschrift. Eigener, schmalerer Block statt .page-lead
   (das ist fuer einen kurzen Einleitungssatz gedacht, nicht fuer
   mehrere hundert Woerter Fliesstext). */
.legal-content {
  max-width: 720px;
  margin-top: 2.5rem;
}

.legal-content h2 {
  font-family: 'Inter', sans-serif;
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--text-light);
  margin-top: 2.5rem;
  margin-bottom: 0.75rem;
}

.legal-content h2:first-child {
  margin-top: 0;
}

.legal-content p {
  font-family: 'Inter', sans-serif;
  font-size: 1rem;
  font-weight: 400;
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: 1rem;
}

.legal-content address {
  font-style: normal;
  font-family: 'Inter', sans-serif;
  color: var(--text-secondary);
  line-height: 1.6;
  margin-bottom: 1rem;
}

.legal-content ul {
  margin: 0 0 1rem 1.25rem;
  color: var(--text-secondary);
  line-height: 1.6;
}

.legal-content a {
  color: var(--accent);
}

/* Schnappschuss-Wand (profil.html) -- bewusst kein braves Bilder-Grid:
   die Fotos liegen leicht schief und ueberlappen sich stellenweise, wie
   ein loses Foto-Sammelsurium auf einem Tisch statt einer kuratierten
   Portfolio-Galerie. Passt zum "nahbar, viele Schnappschuesse"-Wunsch,
   ohne wie eine Bootstrap-Lightbox-Vorlage von 2010 zu wirken. */
/* Echtes Masonry statt Grid mit festen Zeilenhoehen: CSS-Spalten lassen
   jedes Foto in seinem EIGENEN Seitenverhaeltnis stehen (height:auto
   statt object-fit:cover in eine feste Zelle) -- vorher wurden breite
   Querformate in eine 120px-Zeile gepresst und dabei auf einen fast
   nichtssagenden schmalen Streifen zusammengeschnitten. Kleinere
   Basisbreite ergibt insgesamt deutlich kompaktere Kacheln als vorher.

   CSS GRID statt CSS-Spalten (columns) -- war zuerst als echtes Masonry
   ueber `columns` gebaut, aber WebKit/Safari hat einen bekannten Bug:
   jede Stil-Aenderung an einem Kind (u.a. :hover) kann die komplette
   Spaltenverteilung neu berechnen und sichtbar "springen" lassen, bis
   hinein in benachbarte Sections. Grid balanciert nicht dynamisch neu,
   also kein solches Risiko. Feste Spaltenbreite (kein auto-fill/1fr)
   UND ein feinkoerniges grid-auto-rows sind bewusst kein Zufall: jede
   Karte bekommt ihre grid-row-Spanne einzeln per Inline-Style (siehe
   HTML), berechnet aus dem ECHTEN Seitenverhaeltnis des jeweiligen
   Fotos bei GENAU dieser Spaltenbreite -- so passt height:auto exakt in
   die zugewiesenen Grid-Zeilen, ohne object-fit:cover/Zuschnitt und
   ohne Ueberlauf in die naechste Zeile. */
/* position:relative wird von den per JS erzeugten Faeden/Nadeln
   gebraucht (.wall-thread/.wall-thread-pin, siehe profil.html) -- die
   sitzen absolut positioniert relativ zu dieser Wand. Die Umriss-Kachel
   selbst braucht das nicht mehr, seit sie eine normale, am dichten
   Packing teilnehmende Grid-Kachel ist (siehe .snapshot-outline-card). */
.snapshot-wall {
  position: relative;
  display: grid;
  grid-template-columns: repeat(auto-fill, 160px);
  grid-auto-rows: 8px;
  grid-auto-flow: dense;
  justify-content: center;
  gap: 12px;
  padding: 2.5rem 1rem 1rem;
}

/* Polaroid-artiger Rahmen (dickerer unterer Rand) + leichte Drehung
   statt sauberem Grid-Alignment -- die Drehwerte wechseln ueber
   nth-child, damit es organisch wirkt statt einem einzigen fixen
   Winkel zu folgen. Negative margin-top auf ausgewaehlten Karten laesst
   sie ein Stueck in die vorherige Zeile hineinragen -- echte
   Ueberlappung statt sauber gestapelter Kacheln, wie ein loses Foto-
   Sammelsurium auf einem Tisch statt einer kuratierten Portfolio-
   Galerie. Anders als bei CSS-Spalten ist das mit Grid gefahrlos: die
   Zeilen-/Spaltenzuweisung selbst aendert sich dadurch nie, nur die
   sichtbare Position der einzelnen Karte verschiebt sich leicht. */
/* Schatten war ein einzelner grosser, weicher Blur (0 8px 20px) --
   klassische "Material Design Card Elevation", liest sich wie eine
   schwebende UI-Karte statt wie ein Foto, das nah auf der Wand liegt
   (siehe Chat: "altbacken 2010er"). Zwei enger gestaffelte Schichten
   (Kontaktschatte + kleinere Ambient-Schicht) statt einem grossen Halo
   wirken mehr nach "liegt/klebt auf einer Flaeche". */
.snapshot-card {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
  appearance: none;
  border: 0;
  padding: 0.4rem 0.4rem 1.4rem;
  background: #f7f4ee;
  box-shadow: 0 1px 2px rgba(20, 16, 13, 0.22), 0 4px 8px rgba(20, 16, 13, 0.16);
  cursor: zoom-in;
  transform: rotate(var(--r, -2deg));
  transition: transform 0.25s ease, box-shadow 0.25s ease;
}

/* Weniger Ueberlappung als zuvor (-1.75rem/-0.75rem) -- das "lose
   Sammelsurium auf dem Tisch"-Gefuehl bleibt, ohne dass Karten sich zu
   stark gegenseitig verdecken. */
.snapshot-card:nth-child(3n)   { margin-top: -0.9rem; }
.snapshot-card:nth-child(5n+2) { margin-top: -0.35rem; }

/* Faden-Zielkarten immer ueber der (absolut positionierten, in der
   Wandmitte schwebenden) Umriss-Kachel -- welche Karte im dichten
   Grid-Packing geometrisch genau dort landet, ist von der Fensterbreite
   abhaengig und nicht zuverlaessig vorhersehbar (siehe Chat: bei manchen
   Breiten geriet z.B. "Beweisstück B" versehentlich hinter den Umriss
   und war unsichtbar, obwohl ein Faden dorthin lief). z-index:5 schlaegt
   die Umriss-Kachel eindeutig, auch in deren Hover-Zustand (4) -- ein
   gleichstehender Wert waere bei gleichem z-index nur eine Frage der
   DOM-Reihenfolge gewesen, nicht zuverlaessig fuer alle Zielkarten. */
.wall-thread-target {
  z-index: 5;
}

.snapshot-card:nth-child(6n+1) { --r: -3deg; }
.snapshot-card:nth-child(6n+2) { --r: 2.5deg; }
.snapshot-card:nth-child(6n+3) { --r: -1.5deg; }
.snapshot-card:nth-child(6n+4) { --r: 3deg; }
.snapshot-card:nth-child(6n+5) { --r: -2.5deg; }
.snapshot-card:nth-child(6n)   { --r: 1.5deg; }

/* height:100% bezieht sich hier auf die CONTENT-Box von .snapshot-card
   (border-box abzueglich dessen Padding, siehe globales * { box-sizing:
   border-box }) -- entspricht also schon exakt der verfuegbaren Flaeche
   ohne Polaroid-Rand, kein zusaetzliches calc() noetig. */
/* <picture> ist per Default nur inline (siehe .intro-portrait picture
   fuer denselben Fall/dieselbe Ursache) -- ohne diese Regel wuerde sich
   der neue WebP/JPG-Wrapper (siehe Chat: -thumb-Varianten fuer die Wand)
   auf Bildgroesse zusammenziehen statt wie zuvor das <img> direkt die
   volle Kartenflaeche (bzw. bei markierten Fotos die volle
   .snapshot-mark-wrap-Flaeche) auszufuellen. Deckt beide Faelle ab, ohne
   .snapshot-mark-wrap picture separat zu brauchen (Nachfahren-Selektor). */
.snapshot-card picture {
  display: block;
  width: 100%;
  height: 100%;
}

.snapshot-card img {
  display: block;
  width: 100%;
  height: 100%;
  /* Reine Sicherheitsnetz-Rolle -- die Spanne pro Karte ist bereits aus
     dem echten Seitenverhaeltnis berechnet (siehe HTML), object-fit:
     contain faengt nur Rundungsungenauigkeiten ab (kein Verzerren, im
     Zweifel minimal Luft statt Zuschnitt). */
  object-fit: contain;
  /* War 28% -- auf Wunsch zu einem echten Noir-Look hochgezogen (siehe
     Chat), bei 28% wirkten die Fotos weder schwarzweiss noch farbig.
     Volle Farbe erst bei Hover/Fokus (siehe dort) bzw. sofort in der
     Grossansicht (.snapshot-zoom-image hat keinen eigenen Filter). */
  filter: grayscale(95%) contrast(1.02);
  transition: filter 0.25s ease;
}

/* War rotate(0deg) scale(1.08) -- jede Karte richtete sich beim Hover
   exakt gerade auf und wuchs gleichmaessig, die generische "Card lift"-
   Bewegung, die man von jedem Grid seit 2015 kennt (siehe Chat: "billig").
   Widerspricht eigentlich der Wand-Idee: lose gepinnte Fotos wuerden sich
   beim Anfassen eher leicht verschieben als sich brav geradezurichten.
   Jetzt nur ein Teil der eigenen Schraeglage abgebaut (30% von --r bleibt
   erhalten statt komplett auf 0), kleinerer Scale, dazu ein leichtes
   Anheben -- fuehlt sich mehr nach "kurz angehoben" an als nach
   UI-Hover. calc() statt fixem Wert, damit jede Karte (unterschiedliches
   --r via nth-child) ihre eigene Rest-Schraeglage behaelt statt alle auf
   denselben Zwischenwinkel zu vereinheitlichen. */
.snapshot-card:hover,
.snapshot-card:focus-visible {
  transform: rotate(calc(var(--r, -2deg) * 0.3)) scale(1.04) translateY(-3px);
  box-shadow: 0 3px 5px rgba(20, 16, 13, 0.3), 0 10px 16px rgba(20, 16, 13, 0.2);
  z-index: 2;
}

.snapshot-card:hover img,
.snapshot-card:focus-visible img {
  filter: grayscale(0%) contrast(1);
}

/* PROTOTYP Ermittlungswand-Post-its -- Gelbe Zettel als eigene Grid-
   Items in derselben Wand wie die Fotos (kein separater Absolut-
   Overlay): dadurch nie Kollisionen mit Fotos, die Dense-Packing-Logik
   von .snapshot-wall sorgt automatisch fuer Platz. Caveat statt Inter,
   damit es wie handschriftlich hingekritzelt wirkt (Font bereits auf
   der Seite geladen, siehe .portrait-scribble auf index.html). */
/* Echte Post-its sind quadratisch, nicht die breite Kachel, die hier
   herauskam, wenn man ihnen einfach eine ganze Grid-Spaltenbreite gibt
   (160px breit, aber nur ~88px hoch) -- feste Breite+aspect-ratio:1
   statt width:auto/stretch, dazu justify-self/align-self:center, damit
   das kleinere Quadrat mittig in seiner Grid-Zelle sitzt statt sie
   randlos auszufuellen. Insgesamt auch deutlich kleiner als vorher: bei
   nur 2-3 Stichworten Text braucht so ein Zettel nicht dieselbe Flaeche
   wie ein Foto daneben. */
/* Wrapper fuer die drei Post-its (siehe Chat): "ordentlich" nebeneinander
   geklebte Infos mit ganz schmalem Zwischenraum, im Unterschied zu den
   bewusst unordentlich verteilten/ueberlappenden Fotos. Der Wrapper
   selbst ist das einzelne Grid-Item (dieselbe Spannen-Groesse, die vorher
   die drei Post-its zusammen im dichten Packing beansprucht haetten --
   3 x 108px + 2 x 4px Gap = 332px, passt exakt in eine 2-Spalten-Breite
   bei 160px/12px-Gap-Spalten). grid-column/-row hier sind nur der
   Ausgangswert, aus dem positionAnchors() (siehe profil.html) die
   tatsaechliche, ueber die volle Wandbreite zentrierte Position berechnet
   -- die alten justify-self/align-self:center reichten dafuer nicht, die
   zentrierten nur INNERHALB der zufaellig vom dichten Packing zugewiesenen
   Zelle, nicht relativ zur ganzen Wand. */
.snapshot-postit-row {
  display: flex;
  justify-content: center;
  gap: 4px;
  grid-column: span 2;
  grid-row: span 6;
}

.snapshot-postit {
  position: relative;
  /* Muss IMMER ueber allem anderen auf der Wand liegen, auch ueber
     Fotos im Hover-/Fokus-Zustand (z-index:2) und ueber der Umriss-
     Kachel selbst im Hover (4) -- ein Zettel soll nie von einem
     ueberlappenden Foto verdeckt werden (siehe Chat). Hoeher auch als
     .wall-thread (5) und .wall-thread-pin (6): die roten Faeden duerfen
     zwar ueber Fotos laufen, aber nicht ueber einem Zettel liegen. */
  z-index: 7;
  display: flex;
  align-items: center;
  justify-content: center;
  /* War center -- linksbuendig wirkt naeher an echter Handschrift, die
     auf einem Zettel normalerweise am linken Rand ansetzt statt jede
     Zeile einzeln zu zentrieren (siehe Chat). */
  text-align: left;
  appearance: none;
  border: 0;
  width: 108px;
  aspect-ratio: 1 / 1;
  justify-self: center;
  align-self: center;
  /* War 0.7rem -- etwas knapper, wie von Hand eng an den Rand
     geschrieben (siehe Chat). Nicht weiter runter als das: der Caveat-
     Font hat sichtbaren Figuren-Ueberhang (siehe Kommentar bei
     .snapshot-postit-text), bei "Alles außer Fußball" reichte es zuvor
     schon knapp -- per Screenshot nach der Aenderung geprueft, dass die
     Zeile nicht ueber den Rand laeuft. */
  padding: 0.55rem;
  background: #fff6a8;
  box-shadow: 0 8px 16px rgba(20, 16, 13, 0.22);
  transform: rotate(var(--r, -3deg));
  font-family: 'Caveat', cursive;
  font-size: 1.15rem;
  line-height: 1.3;
  color: #3a2f0b;
  cursor: default;
  grid-row: span 6;
}

/* Keine eigene Groesse mehr noetig -- alle drei Zettel sind jetzt
   gleich gross (das quadratische Format oben), die "--tall"-Klasse
   bleibt nur im HTML als Namensrest bestehen (siehe Git-Historie),
   traegt hier aber keine eigenen Regeln mehr. */
.snapshot-postit--tall { grid-row: span 6; }

/* Der eigentliche Text ist EIN einzelnes Flex-Item (statt Textknoten +
   <br> + <em> direkt im Flex-Container, was als drei separate Flex-
   Items nebeneinander statt untereinander gerendert wurde) -- so
   funktioniert <br> wieder als normaler Zeilenumbruch innerhalb dieses
   einen Blocks. Leichter eigener Dreh gegen die Zettel-Rotation UND
   Buchstabenabstand fuer den "hingekritzelt statt gedruckt"-Effekt --
   nur 2-3 Stichworte pro Zettel, keine ganzen Saetze mehr. */
.snapshot-postit-text {
  display: block;
  /* KEINE eigene Gegen-Rotation mehr (frueher -1.5deg) -- bei den jetzt
     viel kleineren, randscharfen Zetteln (siehe .snapshot-postit) reicht
     schon eine kleine Winkelabweichung zwischen Text und Zettel, damit
     eine randbreite Zeile ueber die sichtbare (selbst gedrehte) Kante
     hinausragt: die Drehwinkel von Box und Text addieren/subtrahieren
     sich dann zu einem Rest-Winkel, der Ueberstand ist kein simples
     "Text ist breiter als Box"-Problem und laesst sich darum auch nicht
     einfach per Breite/Zeilenumbruch beheben (siehe Chat/Git-Historie:
     schmalerer Text hat hier nur zusaetzliche Zeilenumbrueche erzeugt
     und die Box dadurch ueber ihre eigentliche Hoehe hinauswachsen
     lassen). Ohne eigene Rotation dreht sich der Text exakt mit dem
     Zettel mit -- Zeilen bleiben parallel zu dessen Kanten, kein
     Rest-Winkel, kein Ueberstand mehr moeglich. */
  width: 100%;
  overflow-wrap: break-word;
  letter-spacing: 0.02em;
}

/* Schwungvolle Unterstreichung unter der Ueberschrift ("Indizien A/B/C")
   jedes Post-its -- statisches Pendant zu .footnote-underline (siehe
   dort): dort ist die Kritzel-Animation als Easteregg fuer einen ganzen
   Satz gedacht, hier reicht ein einzelnes, ruhiges Wort ohne eigene
   Animation. Eigener inline-block-Wrapper noetig, damit sich das SVG
   (position:absolute) nur auf DIESES Wort bezieht, nicht auf den
   gesamten mehrzeiligen Zettel-Text. currentColor statt fester Farbe --
   folgt automatisch der Zettel-eigenen Tinte, auch wenn die sich mal
   aendert. */
.postit-underline-word {
  position: relative;
  display: inline-block;
}

.postit-underline-swoosh {
  position: absolute;
  left: 0;
  bottom: -0.28em;
  width: 100%;
  height: 0.6em;
  overflow: visible;
}

/* Durchgestrichener Fussball unter "Themen" -- Weisses Papier per
   Python/PIL freigestellt (Helligkeits-Schwellwert -> Alphakanal) und
   auf die Zettel-Tinte eingefaerbt, wirkt dadurch wie direkt auf den
   Zettel gemalt statt als weisses Bildrechteck draufgeklebt. Breite in
   % statt fester rem-Werte, damit sie mit der jeweiligen Zettel-/
   Schriftgroesse jedes Breakpoints mitskaliert. */
.postit-doodle {
  display: block;
  width: 44%;
  height: auto;
  margin: 0.2em auto 0;
}

/* Individuelle Drehwinkel per data-title statt :nth-of-type -- seit
   auch die frueheren div-Zettel zu <button> wurden (Klick oeffnet jetzt
   das Text-Overlay), zaehlt :nth-of-type alle Buttons der Wand
   (Zettel UND Fotos zusammen, gleicher Tag-Name), nicht nur die
   Zettel -- traf dadurch nie die beabsichtigten Elemente. */
.snapshot-postit[data-title="Werdegang"]   { --r: -3deg; }
.snapshot-postit[data-title="Themen"]       { --r: 2deg; }
.snapshot-postit[data-title="Bester Beruf"] { --r: -1.5deg; }

/* Vierzeilige Liste (Kopfzeile + Unter-Ueberschrift + zwei/drei
   Stichpunkte) statt bisher zwei/drei Zeilen -- bei der Basis-
   Schriftgroesse brach "Journalismus"/"Temperament" trotz nbsp
   zwischen Gedankenstrich und Wort haesslich um (overflow-wrap:
   break-word bricht auch nicht-umbrechbare Einheiten, wenn sie sonst
   ueberliefen), und der Zettel wuchs ueber sein Quadrat hinaus.
   Kleinere Schrift fuer beide vierzeiligen Post-its, damit alle Zeilen
   sauber einzeln passen. */
.snapshot-postit[data-title="Werdegang"] .snapshot-postit-text,
.snapshot-postit[data-title="Themen"] .snapshot-postit-text {
  font-size: 0.95rem;
}

/* Alle drei Zettel sind <button> -- Klick oeffnet das Text-Overlay
   (siehe #postitOverlay/Skript), daher einheitlich Cursor/Hover statt
   frueher nur auf dem einen Zettel mit echtem Klick-Ziel. */
.snapshot-postit {
  cursor: pointer;
  transition: transform 0.2s ease;
}

.snapshot-postit:hover {
  transform: rotate(0deg) scale(1.05);
}

/* Klickbarkeits-Hinweis (siehe Chat: eine Nutzerin wusste nicht, dass
   die Zettel klickbar sind) -- ein wiederkehrendes kurzes Wackeln (siehe
   .is-inviting weiter unten, per JS ausgeloest). Die zweite, dauerhaft
   abstehende Ecke wurde wieder entfernt (siehe Chat: "Die Ecken der
   Post its können weg"). */

@keyframes postit-invite-wiggle {
  0%, 100% { transform: rotate(var(--r, -3deg)) scale(1); }
  22% { transform: rotate(calc(var(--r, -3deg) + 5deg)) scale(1.04); }
  44% { transform: rotate(calc(var(--r, -3deg) - 4deg)) scale(1.04); }
  66% { transform: rotate(calc(var(--r, -3deg) + 2deg)) scale(1.02); }
  88% { transform: rotate(var(--r, -3deg)) scale(1); }
}

.snapshot-postit.is-inviting {
  animation: postit-invite-wiggle 0.7s ease-in-out;
}

/* Sanft statt abrupt: wer eh schon reduzierte Bewegung bevorzugt, soll
   das Wackeln erst gar nicht sehen (kein Vestibular-Trigger fuer eine
   reine Aufmerksamkeits-Geste ohne Informationsgehalt). */
@media (prefers-reduced-motion: reduce) {
  .snapshot-postit.is-inviting {
    animation: none;
  }
}

/* Zusammenfassungs-Zettel ("Kurz gesagt") -- bewusst ein anderes Format
   als die Post-its (siehe Chat: die bleiben klickgated fuer Details,
   hier soll das Wichtigste sofort lesbar sein, ganz ohne Klick). Weisse
   Karteikarte statt gelber Klebezettel, echte Fliesstextschrift (Inter)
   statt Handschrift-Font (Caveat) -- setzt sich dadurch klar von den
   Post-its ab statt wie ein weiterer, nur groesserer Zettel zu wirken.
   Roter Pin oben (identische Optik wie .snapshot-outline-pin/
   .wall-thread-pin) haelt den Bezug zur Ermittlungswand. Kein <button>
   wie die uebrigen Wand-Elemente: nichts zum Anklicken, der Text steht
   ja bereits vollstaendig da. */
/* Linien wie in einem Schulheft -- rein dekorativ (siehe Chat), nicht an
   der Zeilenhoehe des echten Texts ausgerichtet (dafuer muesste jede
   Textzeile exakt auf einer Linie sitzen, hier reicht die blosse
   Anmutung). Sehr dezent (0.16 Deckkraft, gedecktes Blau -- klassische
   Heftlinie, kein kraeftiges Karo). background-color + eigenes
   background-image statt der bisherigen background-Kurzschreibweise,
   damit beide Ebenen unabhaengig voneinander bleiben. Padding zugleich
   etwas verkleinert (siehe Chat: Zettel duerfen enger an den Rand
   beschrieben sein, wie von Hand). */
.snapshot-index-card {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.6rem;
  width: 100%;
  height: 100%;
  padding: 1.3rem 1.1rem 1rem;
  background-color: #fdfbf5;
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent 26px,
    rgba(90, 120, 180, 0.16) 26px,
    rgba(90, 120, 180, 0.16) 27px
  );
  box-shadow: 0 8px 20px rgba(20, 16, 13, 0.18);
  transform: rotate(-1deg);
  grid-column: span 2;
  grid-row: span 11;
}

.snapshot-index-pin {
  position: absolute;
  top: -0.35rem;
  left: 50%;
  transform: translateX(-50%);
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ff6b5e, #a91d14);
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45);
}

.snapshot-index-title {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 0.72rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--accent-2);
}

.snapshot-index-text {
  font-family: 'Inter', sans-serif;
  font-size: 0.92rem;
  line-height: 1.55;
  color: var(--text-secondary);
}

/* Rote Schnuere: siehe .wall-thread weiter unten -- die Faeden sind
   kein festes CSS mehr (Winkel/Laengen wurden von Hand geschaetzt und
   trafen dadurch nie wirklich ein Foto/Post-it, siehe Chat), sondern
   werden per JS aus den echten Positionen von #wallThreadOrigin
   (Umriss-Nadel) und jedem .wall-thread-target (Post-it-Nadeln,
   Beweisstueck-Tapes) berechnet. */

/* "Umriss"-Zentrumskachel -- teilt sich .snapshot-card fuer Rahmen/
   Schatten/Klick/Hover-Zoom (echtes Foto, siehe HTML-Kommentar dort).
   War lange position:absolute + top/left:50% + translate(-50%,-50%),
   um sie unabhaengig von Spaltenzahl/Viewport-Breite exakt in der
   Wandmitte zu zentrieren -- das garantierte zwar echte Zentrierung,
   aber eben NUR die Position dieser einen Kachel: dem dichten Grid-
   Packing der uebrigen Karten war sie als reines Absolut-Element
   komplett unbekannt, konnte also von einer normalen Karte ueberdeckt
   werden (auf iPad beobachtet: eine echte Karte lag fast vollstaendig
   auf/hinter dem Umriss). z-index (siehe .wall-thread-target) verhinderte
   nur die UNSICHTBARKEIT dabei, nicht die Ueberlappung selbst -- sah
   weiterhin kaputt aus. Jetzt stattdessen eine normale, am dichten
   Packing teilnehmende Grid-Kachel (grid-column/-row: span N statt
   fester Breite/Hoehe) -- CSS Grid garantiert dadurch von sich aus
   ueberlappungsfreie Platzierung mit allen anderen Karten, genau wie
   zwischen zwei gewoehnlichen Karten. Preis: nicht mehr IMMER exakt
   pixelgenau in der Mitte (haengt jetzt von der Spaltenzahl/dichten
   Packing-Reihenfolge ab wie bei jeder anderen Karte), dafuer
   zuverlaessig kollisionsfrei -- der wichtigere Wert hier. Doppelte
   Klasse bei Selektoren unten, damit sie zuverlaessig gegen
   .snapshot-card selbst gewinnt (gleiche Spezifitaet bei einfacher
   Klasse waere sonst nur eine Frage der Regel-Reihenfolge). */
/* War kurzzeitig grid-column:1/-1 (eigene volle Zeile) + justify-self:
   center -- zuverlaessig zentriert, aber auf Kosten der ganzen Zeile
   (kein Foto konnte mehr danebenrutschen). Per Nutzerwunsch zurueck zu
   span 2, mittendrin zwischen den Fotos -- die Zentrierung uebernimmt
   jetzt JS (siehe Skript weiter unten: berechnet die tatsaechliche
   Spaltenzahl und setzt einen expliziten grid-column-start, bleibt aber
   ein normales, am dichten Packing teilnehmendes Grid-Item -- keine
   Kollisionsgefahr wie frueher bei position:absolute, der Browser haelt
   die Spalten trotz expliziter Platzierung weiterhin fuer alle anderen
   Karten korrekt frei/belegt). */
/* Kurzer Umweg auf 1 Spalte/Hochformat ueberall (siehe Chat) wieder
   zurueckgenommen: dabei ging die Karte als "Center Piece" optisch
   unter, blendete sich zu sehr unter die uebrigen Karten. Quadratisch
   wurde aus demselben Grund verworfen. Auf Desktop/Laptop/Tablet bleibt
   deshalb das urspruengliche Querformat -- 2 Spalten breit, faellt
   dadurch bewusst als groessere Karte auf, von der alle Faeden
   ausgehen. NUR auf Mobile (siehe Media Query weiter unten) kommt jetzt
   doch ein Hochformat-Zuschnitt zum Einsatz (mit Stativ ueber statt
   neben der Figur, siehe HTML-Kommentar) -- dort ist die Wand nur 3
   Spalten breit, ein 2-spaltiges Querformat liess dafuer zu wenig
   Seitenspalten fuer die Zielfotos rundherum uebrig. profil.html liest
   die jeweils aktuelle Spanne live aus dem CSS aus (siehe
   positionAnchors()), reagiert also automatisch auf diesen Wechsel. */
.snapshot-outline-card {
  grid-column: span 2;
  /* War Spanne 14, kalkuliert fuer das Bild MIT dem alten Polaroid-
     Innenabstand (0.4rem seitlich reduzierte die tatsaechliche
     Bildbreite). Ohne Padding (siehe .snapshot-card.snapshot-outline-
     card) ist das Bild jetzt minimal breiter, dadurch bei gleicher
     Spaltenbreite proportional niedriger -- Spanne auf 12 reduziert,
     sonst blieb unten ein sichtbarer dunkler Leerraum (der neue
     Rahmenhintergrund) stehen. */
  grid-row: span 12;
  z-index: 3;
}

/* cursor:default statt des geerbten zoom-in (siehe .snapshot-card) --
   die Karte ist seit Kurzem nicht mehr anklickbar (siehe Chat: dahinter
   steckt kein echter Inhalt zum Vergroessern, siehe JS-Kommentar bei
   .snapshot-outline-card-Ausnahme in der cards-Query), der Cursor soll
   das nicht mehr versprechen. Der leichte Hover-Tilt (siehe unten)
   bleibt trotzdem -- reine Wand-Haptik, kein Klick-Versprechen. */
/* Kein Polaroid-Rand (siehe Chat: "muss gar kein Polaroid sein") --
   randloses Bild bis zum Kartenrand statt des dicken weissen
   Papierrands der uebrigen Karten, wirkt eher wie ein angepinntes
   Tatort-/Beweisfoto als wie ein privates Polaroid unter den
   Schnappschuessen -- passt besser zur Rolle als "Center Piece", von
   dem alle Faeden ausgehen. Duenner dunkler Rahmen statt Papier-
   Hintergrund, damit sich die Kante trotzdem vom hellen Wandhintergrund
   abhebt (padding:0 + border statt background, das ohne umgebenden
   Papierrand unsichtbar waere). */
.snapshot-card.snapshot-outline-card {
  cursor: default;
  transform: rotate(-2deg);
  padding: 0;
  border: 3px solid #1a1714;
  background: #1a1714;
}

/* Kein translateY-Anheben wie bei den uebrigen Karten (siehe dortiger
   Kommentar): der Ausgangspunkt aller Faeden (#wallThreadOrigin) sitzt
   als Kind-Element mittig auf DIESER Karte und wuerde beim Hover mit
   ihr mitwandern -- die Faeden selbst sind aber separate, per JS EINMAL
   beim Laden/Resize (nicht bei Hover) berechnete Linien, die dann
   sichtbar an der alten Position "abreissen" wuerden, ausgerechnet am
   Punkt, an dem alle Faeden zusammenlaufen. Rotation/Scale sind dagegen
   unkritisch (die Faeden docken am Kartenrand an, nicht an einem festen
   Pixel-Offset). */
.snapshot-card.snapshot-outline-card:hover,
.snapshot-card.snapshot-outline-card:focus-visible {
  transform: rotate(-0.6deg) scale(1.03);
  z-index: 4;
}

/* Die Nadel, an der alle per JS gezeichneten Faeden zusammenlaufen
   (siehe .wall-thread) -- feste Position in der Kartenmitte, anders als
   die Zielnadeln (.wall-thread-pin, siehe dort), die dynamisch am
   Kartenrand sitzen: hier ist EIN gemeinsamer Knotenpunkt fuer mehrere
   Faeden gewuenscht, kein Rand-Ausweichen noetig. Kleiner als frueher
   (1rem -> 0.7rem), passend zu den ebenfalls verkleinerten Zielnadeln. */
.snapshot-outline-pin {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  z-index: 1;
  width: 0.7rem;
  height: 0.7rem;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ff6b5e, #a91d14);
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

/* Blut-Spritzer beim Klick auf den Umriss (siehe Chat: echtes Foto mit
   Alphakanal statt eigenem SVG-Nachbau, siehe Git-Historie fuer die
   beiden verworfenen Vorversuche). KEIN Kind der Umriss-Kachel mehr
   (siehe Chat: "die Schnüre verschwinden während des Blut Splashes")
   -- war es zunaechst, aber ein Hochsetzen der ganzen (undurchsichtigen)
   Kachel ueber die Faeden (z-index 5) hob auch deren Hintergrund ueber
   den Teil der Faeden, der die Kachel durchquert (jeder Faden startet
   ja genau in deren Mitte). Jetzt eigenstaendiges Geschwister-Element
   auf Wand-Ebene, links/oben/Breite kommen daher per JS bei jedem
   Klick frisch aus der aktuellen Kachel-Geometrie (siehe profil.html)
   statt aus %-Werten relativ zur Kachel selbst. --rot kommt ebenfalls
   zufaellig frisch per JS bei jedem Klick, aber nur ein kleiner Winkel
   (±20°) -- ein Foto-Spritzer soll nicht wie ein Sticker beliebig
   rotiert wirken, nur eine leichte Unregelmaessigkeit von Klick zu
   Klick. Breiter als die Kachel selbst (135%, siehe JS) -- soll
   bewusst ueber den Kartenrand auf die Nachbarfotos ueberschwappen
   statt sauber im Format zu bleiben (siehe Chat: "dürfte auch über das
   Outline hinaus splashen"), wirkt dadurch nach echtem Aufprall statt
   nach ordentlich zugeschnittenem Sticker. pointer-events:none
   verhindert, dass der ueberlappende Teil Klicks auf Nachbarkarten
   blockiert. z-index hoeher als alles andere auf der Wand (auch
   Post-its, 7, und Faeden/Nadeln, 5/6) -- der Spritzer soll sichtbar
   UEBER allem liegen, wie tatsaechlich verschuettetes Blut. */
.wall-splat {
  position: absolute;
  transform: translate(-50%, -50%) scale(1.5) rotate(var(--rot, 0deg));
  transform-origin: center;
  pointer-events: none;
  opacity: 0;
  z-index: 25;
}

.wall-splat img {
  display: block;
  width: 100%;
  height: auto;
}

.wall-splat.is-splashing {
  /* linear statt eigener cubic-bezier: die Wucht der Landung steckt
     schon in den eng gestaffelten Keyframes selbst (0/12/20/30/38%,
     siehe unten) -- eine zusaetzliche globale Timing-Funktion wuerde
     diese sorgfaeltig getakteten Prozentwerte auf der Zeitachse
     verzerren (getestet: mit eigener Kurve verblasste der Spritzer
     schon nach ~1,6s statt wie vorgesehen erst nach ~1,9s, weil die
     Kurve den "80%"-Keyframe zeitlich weit nach vorne zog). */
  animation: wall-splat-impact 2.8s linear forwards;
}

@keyframes wall-splat-impact {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.55) rotate(var(--rot, 0deg));
  }
  /* War danach noch mehrfach ueber/unter 1 geschwungen (scale 1.1,
     dann 0.96, dann erst 1) -- echtes Blut federt beim Aufprall aber
     nicht nach, es klatscht einmal hin und bleibt liegen (siehe Chat).
     Jetzt nur noch EIN kurzer Zusammenstauch-Moment (leicht unter 1),
     dann direkt die Endgroesse -- kein Ueberschwingen ueber 1 mehr. */
  14% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(0.92) rotate(var(--rot, 0deg));
  }
  24% {
    transform: translate(-50%, -50%) scale(1) rotate(var(--rot, 0deg));
  }
  /* War ein kurzer, fast abrupter Fade (80%->100% von 2.2s, ~440ms) --
     die Faeden, die der Spritzer teilweise verdeckt hatte, "sprangen"
     dadurch sichtbar zurueck ins Bild statt sanft wieder aufzutauchen
     (siehe Chat: "springen wieder direkt reinspringen ins Bild ...
     das ist nicht so schön"). Laengere Gesamtdauer plus ein
     Zwischenschritt bei 50% Deckkraft strecken das Ausblenden auf
     rund 1s -- wirkt dadurch als sanftes Verblassen statt als
     ploetzliches Verschwinden. */
  62% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1) rotate(var(--rot, 0deg));
  }
  82% {
    opacity: 0.5;
    transform: translate(-50%, -50%) scale(1) rotate(var(--rot, 0deg));
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1) rotate(var(--rot, 0deg));
  }
}

/* Kurzer heller Blitz im Aufprallmoment (siehe Chat: "als wuerde es
   draufklatschen") -- eigenes, sehr kurzlebiges Element statt Teil der
   Spritzer-Animation, damit Timing/Ostkurve unabhaengig vom (viel
   laenger sichtbaren) Spritzer selbst bleiben. */
.wall-splat-flash {
  position: absolute;
  aspect-ratio: 1 / 1;
  border-radius: 50%;
  background: radial-gradient(circle, rgba(255, 255, 255, 0.9) 0%, rgba(255, 200, 190, 0.55) 40%, rgba(255, 200, 190, 0) 72%);
  transform: translate(-50%, -50%) scale(0);
  pointer-events: none;
  opacity: 0;
  z-index: 24;
}

.wall-splat-flash.is-splashing {
  animation: wall-splat-flash 0.32s ease-out forwards;
}

@keyframes wall-splat-flash {
  0% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.3);
  }
  35% {
    opacity: 1;
    transform: translate(-50%, -50%) scale(1.15);
  }
  100% {
    opacity: 0;
    transform: translate(-50%, -50%) scale(1.6);
  }
}

@media (prefers-reduced-motion: reduce) {
  .wall-splat.is-splashing,
  .wall-splat-flash.is-splashing {
    animation: none;
  }
}

/* Per JS erzeugte/positionierte Fadenlinie (siehe Skript weiter unten
   in profil.html) -- ein <div> pro Verbindung von #wallThreadOrigin zu
   einem .wall-thread-target, aus deren tatsaechlichen Positionen
   berechnet (left/top/width/rotate), statt wie zuvor aus geschaetzten
   CSS-Winkeln. Dadurch automatisch korrekt bei jeder Fensterbreite/
   jedem Layout, ohne eigene Mobile-Anpassung. transform-origin:0 0,
   damit rotate() um den Startpunkt (den Punkt bei #wallThreadOrigin)
   dreht, nicht um die Fadenmitte.
   z-index hoeher als jede Karte (auch die Umriss-Kachel im Hover,
   z-index:4) -- die Schnur soll sichtbar UEBER dem zentralen Foto
   verlaufen, wie auf einem echten Ermittlungs-Board, nicht dahinter
   verschwinden. Kein box-shadow mehr: auf dem Foto selbst braucht die
   Schnur keinen Leucht-Rand zur Abgrenzung, direkt auf dem Bild wirkt
   ein scharfer Rand realistischer als ein Glow. */
.wall-thread {
  position: absolute;
  height: 2px;
  background: #c81e1e;
  transform-origin: 0 0;
  pointer-events: none;
  z-index: 5;
}

/* Zielnadel eines Fadens -- per JS erzeugt/positioniert (siehe Skript
   weiter unten in profil.html), an dem Punkt auf dem Kartenrand, der
   dem Ursprung am naechsten liegt (nicht die Kartenmitte -- sonst
   wuerde der Faden quer ueber das ganze Foto/Post-it laufen statt nur
   bis zum Rand). z-index hoeher als .wall-thread, damit die Nadel
   sichtbar auf dem Fadenende sitzt statt darunter. */
.wall-thread-pin {
  position: absolute;
  width: 0.55rem;
  height: 0.55rem;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ff6b5e, #a91d14);
  box-shadow: 0 2px 3px rgba(0, 0, 0, 0.45);
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 6;
}

/* Unsichtbare Platzhalter-Kachel -- per JS erzeugt/positioniert (siehe
   positionAnchors() in profil.html), sperrt in "entfernten" Spalten die
   Post-it-Zeile fuer das dichte Packing, damit dort nichts vor der
   Umriss-Hoehe startet (siehe dortiger Kommentar). Rein layouttechnisch,
   nie sichtbar -- visibility:hidden statt display:none, damit die Zelle
   trotzdem als Grid-Platz belegt zaehlt. */
.wall-row-spacer {
  visibility: hidden;
}

/* Beschriftung direkt im unteren Polaroid-Rand statt auf einem
   aufgeklebten Tape-Etikett -- schlichter, wie mit einem Folienstift
   auf den weissen Rand geschrieben. Sitzt in der ohnehin vorhandenen
   unteren Kartenpolsterung (padding-bottom von .snapshot-card), braucht
   daher kein eigenes overflow:visible/z-index-Gegenmittel mehr (anders
   als das fruehere Tape, das oben ueber die Karte hinausragte). */
.snapshot-caption {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.2rem;
  text-align: center;
  font-family: 'Permanent Marker', cursive;
  font-size: 0.7rem;
  /* War #3a372f (warmes Braunschwarz) -- Test auf einen "tintigen" Ton
     statt sattem Kugelschreiber-Blau (siehe Chat: Blau haette die
     disziplinierte Farbsprache der Wand gesprengt). Sehr dunkles,
     entsaettigtes Blauschwarz wie echte (Fueller-)Tinte, bei dieser
     Groesse (0.7rem) noch nah an Schwarz, aber mit kuehlem statt
     warmem Unterton. */
  color: #1e2233;
  pointer-events: none;
}

/* AKTUELL UNGENUTZT (siehe Chat): die drei Markierungen auf der Murder
   Wall (Kreis um sich selbst, Kreis um ein Teammitglied, Unterstrich im
   FAZ-Ausschnitt) waren "random" positioniert und sind erstmal wieder
   raus. Klassen bleiben stehen, falls sie spaeter praeziser gesetzt
   zurueckkommen (gleiches Prinzip wie bei .snapshot-index-card).
   Wrapper NUR bei Fotos mit Kreis-/Unterstreichungs-Markierung (siehe
   .wall-mark-circle/.wall-mark-underline) -- exakt so gross wie das
   <img> selbst (100% der Karten-Innenflaeche, kein eigenes Padding),
   damit die per Prozentwert positionierten Markierungen sich auf das
   BILD beziehen, nicht auf die Karte samt ihrem ungleichen Polaroid-
   Rand (0.4rem oben/seitlich, 1.4rem unten) -- letzteres haette die
   Prozentrechnung fuer jede Karte unterschiedlich verschoben. */
.snapshot-mark-wrap {
  position: relative;
  display: block;
  width: 100%;
  height: 100%;
}

/* Handschriftlich wirkender Kreis/Ellipse in Fettstift-Rot, wie ein
   Ermittler, der ein Detail im Foto markiert -- unregelmaessiger
   border-radius statt echtem Kreis fuer den "aus freier Hand
   gezogen"-Effekt, leichte Rotation zusaetzlich zur Formunregel-
   maessigkeit. mix-blend-mode:multiply statt reinem Rot obendrauf,
   damit es wie Stift AUF dem Foto wirkt statt wie ein aufgeklebter
   Vektor-Ring. Position/Groesse/Rotation kommen als Inline-Style pro
   Einsatzstelle (siehe HTML), da jede Markierung ein anderes Detail an
   anderer Stelle trifft. */
.wall-mark-circle {
  position: absolute;
  border: 3px solid #c81e1e;
  border-radius: 48% 52% 45% 55% / 55% 45% 58% 42%;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 2;
}

/* Kurzer, leicht gewellter Unterstreichungs-Strich -- fuer markierte
   Textstellen in den Zeitungsausschnitten. Zwei leicht versetzte
   Randlinien (border-bottom + box-shadow) statt einer einzelnen
   geraden Linie, damit sie nicht wie ein cleaner CSS-Rahmen, sondern
   wie zwei ungefaehr uebereinander gezogene Stiftstriche wirkt. */
.wall-mark-underline {
  position: absolute;
  border-bottom: 3px solid #c81e1e;
  box-shadow: 0 3px 0 -1.5px #c81e1e;
  mix-blend-mode: multiply;
  pointer-events: none;
  z-index: 2;
}

/* Zeitungsausschnitt-Kachel -- echte gescannte Presse-Ausrisse (siehe
   HTML-Kommentar) als Bild statt gestaltetem Zitat-Text. Teilt sich
   .snapshot-card fuer Rahmen/Schatten/Dreh/Klick/Hover-Zoom wie jedes
   andere Foto, bekommt zusaetzlich einen gezackten Riss per clip-path
   (faellt auch auf den weissen Polaroid-Rand mit -- wirkt wie ein
   angerissenes Stueck Papier statt eines sauber ausgeschnittenen
   Fotos). Keine eigene --r/nth-of-type-Rotation mehr noetig: erbt die
   von .snapshot-card selbst (nth-child, zaehlt echte Positionen in der
   Wand -- :nth-of-type haette hier denselben Bug wie einst bei
   .snapshot-postit, da alle Karten denselben Tag-Namen teilen). */
.snapshot-card.snapshot-clipping {
  clip-path: polygon(
    0% 3%, 6% 0%, 13% 2%, 20% 0%, 28% 2%, 35% 0%, 43% 2%, 50% 0%, 58% 2%, 65% 0%, 73% 2%, 80% 0%, 88% 2%, 95% 0%, 100% 3%,
    100% 97%, 94% 100%, 87% 98%, 80% 100%, 72% 98%, 65% 100%, 57% 98%, 50% 100%, 42% 98%, 35% 100%, 27% 98%, 20% 100%, 12% 98%, 5% 100%, 0% 97%
  );
}

/* Grossansicht -- bewusst minimal (siehe HTML-Kommentar), analog zu
   .poster-zoom im Aufbau (fixes dunkles Overlay, zoom-out-Cursor),
   aber eigenstaendig statt mitbenutzt, da .poster-zoom nur in
   index.html existiert. */
.snapshot-zoom {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: none;
  align-items: center;
  justify-content: center;
  background-color: rgba(8, 8, 7, 0.96);
  padding: 4rem 5.5rem;
  cursor: zoom-out;
  /* Sicherheitsnetz fuer sehr kurze Fenster (z.B. Handy im Querformat)
     mit einem Hochkantfoto UND langer Anekdote -- .has-story oben
     verhindert das im Normalfall schon, aber ganz ausschliessen laesst
     sich eine zu knappe Fensterhoehe damit nicht. Ohne dies waere der
     untere Teil (Caption/Anekdote) sonst schlicht unerreichbar. */
  overflow-y: auto;
  /* Ohne dies "durchbricht" das Scrollen auf iOS Safari die Grenze
     des Overlays, sobald man am Ende des Inhalts weiterscrollt (Scroll-
     Chaining/Rubber-Banding) -- die Seite DAHINTER scrollt dann sichtbar
     mit, obwohl das Overlay position:fixed/inset:0 ist und sie eigentlich
     komplett abdecken sollte. contain haelt das Scrollen dort, wo es
     hingehoert. BEWUSST kein manuelles overflow:hidden/touch-action:none
     auf body als Alternative -- genau das fuehrte an anderer Stelle
     (siehe body.poem-active weiter oben in index.html) auf iOS zu einem
     eigenen, schwerer zu findenden Folgefehler (staled Scroll-Gesten-
     Regionen). overscroll-behavior ist die dafuer vorgesehene, rein
     deklarative Loesung ohne Seiteneffekte auf body. */
  overscroll-behavior-y: contain;
}

.snapshot-zoom.is-open {
  display: flex;
}

/* Sichtbarer Scroll-Hinweis, analog zum Vorbild in der Portfolio-
   Lightbox (.lightbox-scroll-hint) -- position:fixed statt Teil des
   scrollenden Inhalts (siehe HTML-Kommentar dort): der ganze Dialog
   scrollt hier als ein Block, ein Kind darin wuerde selbst mitwandern
   statt am Fensterrand zu bleiben. is-scrollable/is-at-bottom werden
   per JS gesetzt (siehe updateZoomScrollState()) -- ohne echten
   Ueberlauf (der Normalfall) bleibt der Pfeil unsichtbar. */
.snapshot-zoom-scroll-hint {
  display: none;
  position: fixed;
  left: 50%;
  bottom: 1rem;
  transform: translateX(-50%);
  font-size: 1.5rem;
  line-height: 1;
  color: var(--accent-2-on-dark);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
  pointer-events: none;
  z-index: 111;
  animation: snapshotZoomScrollHintBounce 1.4s ease-in-out infinite;
}

.snapshot-zoom.is-scrollable .snapshot-zoom-scroll-hint {
  display: block;
}

/* visibility statt display:none -- reserviert damit keinen neuen
   Sprung/Repaint, verschwindet einfach an Ort und Stelle, sobald
   wirklich zu Ende gescrollt wurde (gleiches Muster wie beim Lightbox-
   Vorbild). */
.snapshot-zoom.is-scrollable.is-at-bottom .snapshot-zoom-scroll-hint {
  visibility: hidden;
}

@keyframes snapshotZoomScrollHintBounce {
  0%, 100% { transform: translateX(-50%) translateY(0); opacity: 0.5; }
  50% { transform: translateX(-50%) translateY(3px); opacity: 1; }
}

/* Bild + Bildzeile als eigener Block -- cursor:default statt des
   zoom-out-Cursors vom Hintergrund, klar als eigenstaendiger Inhalt statt
   Teil der schliessbaren Flaeche (schliessen bleibt trotzdem moeglich,
   siehe JS: nur ein Klick exakt auf den Hintergrund selbst schliesst). */
.snapshot-zoom-figure {
  display: flex;
  flex-direction: column;
  align-items: center;
  max-width: 100%;
  max-height: 100%;
  cursor: default;
  /* Nicht ueber das Padding von .snapshot-zoom geloest -- Padding am
     Ende eines scrollenden Flex-Containers wird browseruebergreifend
     nicht immer zuverlaessig respektiert (der Inhalt sass beim
     Herunterscrollen streckenweise direkt am unteren Fensterrand statt
     mit Luft davor). Margin auf dem Flex-ITEM selbst wird dagegen in
     Flex-Layouts nie mit dem Container-Rand kollabiert, zaehlt also
     zuverlaessig zur Scroll-Hoehe -- betrifft nur den Fall, dass
     ueberhaupt gescrollt werden muss (siehe .snapshot-zoom
     overflow-y:auto), im Normalfall unsichtbar. Auf dem iPhone war
     selbst 2rem noch knapp (siehe Chat-Screenshot: der Text sass fast
     auf der Safari-Werkzeugleiste auf) -- deutlich grosszuegiger plus
     env(safe-area-inset-bottom) obendrauf, das eigentliche Geraete-
     Sicherheitspolster unterhalb der Home-Indicator-Geste auf neueren
     iPhones (0px auf Geraeten ohne Notch/Home-Indicator, dort bleibt es
     einfach bei den 4rem). */
  margin-bottom: calc(4rem + env(safe-area-inset-bottom, 0px));
}

.snapshot-zoom-image {
  display: block;
  max-width: 100%;
  max-height: calc(100vh - 11rem);
  border-radius: 0.25rem;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
  /* Beim Foto-Wechsel (Vor/Zurueck) wird kurz ausgeblendet, bevor Groesse
     und src wechseln, und danach wieder eingeblendet -- sonst verzerrt der
     Browser das noch sichtbare ALTE Foto in die neuen Box-Masse, waehrend
     das neue Bild laedt (gleiches Muster wie .portfolio-image in der
     Portfolio-Slideshow, siehe dortiges Skript). */
  opacity: 1;
  transition: opacity 0.2s ease;
}

/* Bei Fotos MIT Anekdote (siehe JS: .has-story wird nur dann gesetzt)
   bleibt sonst bei Hochkantfotos kaum Platz fuer den zusaetzlichen
   Text -- das Bild fuellt die normale Hoehengrenze fast komplett aus.
   Knappere Grenze nur in diesem Fall, damit unter dem Bild sichtbar
   Raum fuer Caption + Anekdote bleibt. Wirkt sich bei Querformat-Fotos
   nicht sichtbar aus (die werden schon vorher durch max-width statt
   max-height begrenzt). */
.snapshot-zoom-image.has-story {
  max-height: calc(100vh - 17rem);
}

/* Erklaerende Bildzeile zum jeweiligen Foto -- EB Garamond kursiv wie
   die Presse-Zitate in der Portfolio-Lightbox, analoge Rolle (kurzer,
   erzaehlender Begleittext statt reiner Beschriftung). Knapper Abstand
   zum Bild darueber, OHNE eigenen Trennsteg -- der Steg sitzt
   stattdessen erst NACH der Caption (siehe .snapshot-zoom-story unten):
   Bild und Bildzeile sollen als ein zusammengehoeriger Block wirken,
   der Steg trennt nur die kurze Bildzeile von der laengeren Anekdote
   darunter (wie bei einem Museumsschild: Titel direkt unterm Exponat,
   erst danach ein Steg vor dem erklaerenden Fliesstext). */
.snapshot-zoom-caption {
  margin-top: 0.6rem;
  max-width: 34rem;
  text-align: center;
  font-family: 'EB Garamond', serif;
  font-style: italic;
  font-size: 1.05rem;
  line-height: 1.5;
  color: #d8d6d0;
}

/* Laengere Anekdote zu einzelnen Fotos (data-story, optional -- die
   meisten Fotos haben nur die kurze Caption oben) -- bewusst NICHT
   kursiv wie die Caption: Kursivschrift ist fuer eine kurze Bildzeile
   gedacht, bei mehreren Saetzen Fliesstext wird sie schnell anstrengend
   zu lesen. Aus demselben Grund auch nicht die Serife (EB Garamond) der
   Caption, sondern Inter -- exakt dasselbe Muster wie beim Post-it-
   Overlay (.postit-overlay-note, siehe dort): kurzer Titel bleibt bei
   der Serife, laengerer Fliesstext wechselt auf die normale Seiten-
   schrift, Lesbarkeit vor Immersion. Groesse/Zeilenhoehe entsprechen
   der Fliesstext-Norm der Seite (siehe .page-section p). Liest sich wie
   ein Museumsschild: kursiver Bildtitel oben, ruhiger Erklaertext
   darunter -- der Trennsteg (border-top) davor gehoert automatisch nur
   dazu, wenn wirklich eine Anekdote da ist, das [hidden]-Attribut blendet
   ihn bei den uebrigen Fotos mit aus. Kraeftiger als der erste Versuch
   (0.16 Alpha wirkte kaum sichtbar). */
.snapshot-zoom-story {
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid rgba(255, 255, 255, 0.32);
  max-width: 30rem;
  /* Linksbuendig statt zentriert -- bei laengerem Fliesstext (mehrere
     Zeilen) erschwert eine zentrierte Ausrichtung das Scannen der
     einzelnen Zeilen (kein fester Zeilenanfang). Die kurze Caption
     darueber bleibt bewusst zentriert, dort stoert das bei nur einer
     Zeile nicht. */
  text-align: left;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  line-height: 1.6;
  color: #b5b2ac;
}

/* Absaetze innerhalb einer Anekdote (siehe JS: story.split(/\n\s*\n/))
   -- gleiches Prinzip wie .postit-overlay-para, nur schlichter (kein
   Nadel-Icon zwischen den Absaetzen, das passt zum gelben Zettel dort,
   nicht zum dunklen Foto-Overlay hier). margin:0 statt des Browser-
   eigenen <p>-Abstands, der sich sonst mit dem naechsten Regel-Abstand
   summiert haette (doppelter Zwischenraum, zusaetzlicher Rand vor dem
   ersten/nach dem letzten Absatz). */
.snapshot-zoom-story-para {
  margin: 0;
}

.snapshot-zoom-story-para + .snapshot-zoom-story-para {
  margin-top: 1rem;
}

.snapshot-zoom-close {
  position: fixed;
  top: 1.5rem;
  right: 1.5rem;
  z-index: 111;
  appearance: none;
  /* War halbtransparentes Weiss, dann halbtransparentes Dunkel -- beides
     faerbt sich je nach Untergrund unterschiedlich stark ein (Postit-
     Overlay: gelber Zettel: Lightbox: schwarzer Hintergrund, siehe
     Chat: "Siehst du den Close Button hier? Ich nicht so" / "Button der
     vor hell und Dunkel gleichermassen funktioniert"). Jetzt VOLL
     deckend statt transparent -- sieht dadurch auf Gelb und auf Schwarz
     exakt gleich aus, unabhaengig vom Untergrund darunter. Der helle
     Ring sorgt zusaetzlich fuer eine klare Kante, falls der Button mal
     vor einem aehnlich dunklen Bildbereich der Lightbox landet. */
  background: #1a1512;
  border: 1px solid rgba(255, 255, 255, 0.4);
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.45);
  color: #fff;
  width: 2.75rem;
  height: 2.75rem;
  border-radius: 50%;
  font-size: 1.5rem;
  line-height: 1;
  cursor: pointer;
}

/* Vor/Zurueck durch die Wand, ohne die Grossansicht zu verlassen --
   analog zu den Portfolio-Lightbox-Pfeilen, aber ohne deren JS-
   Positionsberechnung: die Grossansicht ist immer die volle
   Viewport-Flaeche, position:fixed an den Raendern reicht per CSS. */
.snapshot-zoom-nav {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 111;
  appearance: none;
  background: none;
  border: none;
  color: #e8e6e1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  font-size: 2.5rem;
  line-height: 1;
  transition: color 0.2s, transform 0.2s;
}

.snapshot-zoom-prev { left: 0.5rem; }
.snapshot-zoom-next { right: 0.5rem; }

.snapshot-zoom-nav:hover {
  color: var(--accent);
}

/* Post-it-Overlay (nur profil.html): eigene Grossansicht fuer die
   Ermittlungswand-Zettel, analog zu .snapshot-zoom im Aufbau (fixes
   dunkles Overlay, .snapshot-zoom-close mitbenutzt fuer identischen
   Schliessen-Button), aber ohne Bild -- die "Grossansicht" ist hier
   der Zettel selbst, nur groesser und mit mehr Text. */
.postit-overlay {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: none;
  align-items: center;
  justify-content: center;
  background-color: rgba(8, 8, 7, 0.96);
  padding: 4rem 1.5rem;
  cursor: zoom-out;
}

.postit-overlay.is-open {
  display: flex;
}

.postit-overlay-note {
  position: relative;
  max-width: 30rem;
  width: 100%;
  /* Eigenes Scrollen statt ueber den Viewport-Rand hinauszuwachsen --
     der laengere, in Absaetze gegliederte Werdegang-Text (siehe Chat)
     ragte auf Mobile sonst oben UND unten aus dem Bildschirm heraus,
     Anfang und Ende des Texts waren nicht mehr erreichbar. 8rem
     entspricht dem Overlay-eigenen Padding (4rem oben/unten, siehe
     .postit-overlay), damit der Zettel bei kurzen Texten weiterhin
     genauso aussieht wie zuvor (kein Scroll noetig). */
  max-height: calc(100vh - 8rem);
  overflow-y: auto;
  /* Gleicher Grund wie bei .snapshot-zoom (siehe dortiger Kommentar):
     ohne dies kann Scrollen auf iOS Safari am Ende des Zettel-Texts in
     die dahinterliegende Seite "durchbrechen". */
  overscroll-behavior-y: contain;
  background: #fff6a8;
  box-shadow: 0 20px 50px rgba(0, 0, 0, 0.5);
  padding: 2.5rem 2.25rem;
  transform: rotate(-1.2deg);
  cursor: default;
  /* War Caveat (Skript-Handschrift) fuer die ganze Grossansicht -- gut
     lesbar fuer ein, zwei Stichworte auf dem kleinen Zettel selbst, aber
     bei laengerem Fliesstext (siehe Werdegang) geht Lesbarkeit vor
     Immersion (siehe Chat). Gelb/Drehung/Schatten bleiben als
     "Zettel"-Signal, nur die Schrift wechselt auf die normale
     Seiten-Schrift. */
  font-family: 'Inter', sans-serif;
  color: #3a2f0b;
}

/* War Permanent Marker -- neben der jetzt normalen Inter-Fliesstext-
   Schrift wirkte die Comic-/Filzstift-Optik eher nach Comic Sans als
   nach Handschrift (siehe Chat). EB Garamond kursiv (dieselbe Schrift
   wie Gedicht/Zitate an anderer Stelle der Seite) liest sich eher wie
   ein Dokumenttitel -- passt zum "Akte"-Charakter des Overlays, ohne
   kindlich zu wirken. */
.postit-overlay-title {
  font-family: 'EB Garamond', serif;
  font-style: italic;
  font-size: 1.9rem;
  font-weight: 500;
  margin-bottom: 0.9rem;
}

/* Manche Overlays brauchen keine eigene Ueberschrift (siehe Chat: "das
   Overlay selbst braucht keine Ueberschrift, das ist selbsterklärend")
   -- #postitOverlayTitle bleibt dann als leerer <p> im DOM (siehe
   Skript), :empty blendet ihn samt Abstand komplett aus statt einer
   sichtbaren Luecke. */
.postit-overlay-title:empty {
  display: none;
}

.postit-overlay-text {
  position: relative;
  font-size: 1.05rem;
  /* War 1.6 -- auf Wunsch enger gesetzt (siehe Chat). */
  line-height: 1.4;
}

/* Rote Schnur zwischen den Pins (siehe Chat: "verbinde die roten Nadeln
   doch einfach vertikal mit roter Schnur, dann wiederholt sich das,
   auch wenn es wenig Sinn ergibt") -- eine durchgehende Linie im
   Nadel-Gutter, laeuft HINTER den Pins (siehe deren z-index unten), da
   sie ja an jeder Nadel "vorbeifuehrt". top/bottom 0.5em spart die
   erste/letzte Nadel selbst aus, die Linie beginnt/endet an deren
   Mittelpunkt statt am Textrand. */
.postit-overlay-text::before {
  content: '';
  position: absolute;
  left: 0.25rem;
  top: 0.5em;
  bottom: 0.5em;
  width: 2px;
  background: #a91d14;
  z-index: 0;
}

/* Absaetze im Klick-Overlay (per JS aus \n\n im data-full-Attribut
   erzeugt, siehe Skript) bekommen statt einer Label-Ueberschrift (siehe
   Chat: "wie ein Kommentar zu meinem Lebenslauf", nicht gewuenscht) nur
   eine rein formale rote Pin-Markierung -- derselbe Farbverlauf wie
   .wall-thread-pin auf der Wand, bindet das Overlay optisch an die
   Ermittler-Aesthetik der Seite, ohne den Inhalt zu deuten. */
.postit-overlay-para {
  position: relative;
  z-index: 1;
  padding-left: 1.3rem;
}

.postit-overlay-para + .postit-overlay-para {
  margin-top: 1rem;
}

.postit-overlay-para::before {
  content: '';
  position: absolute;
  left: 0;
  top: 0.5em;
  width: 0.5rem;
  height: 0.5rem;
  border-radius: 50%;
  background: radial-gradient(circle at 30% 30%, #ff6b5e, #a91d14);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.4);
}

/* Optionale kleine Illustrationen ueber/unter dem Text in manchen
   Post-it-Overlays (Fussball unten auf "Themen", Nuckel oben/Sarg
   unten auf "Werdegang", siehe data-overlay-doodle-top/-bottom in
   profil.html) -- deutlich kleiner als der eigentliche Zettel-Text,
   bewusst als kleiner Gag statt als gleichwertiges Element. Fixe
   rem-Breite statt % wie bei den Wand-Postits: das Overlay ist um ein
   Vielfaches groesser als ein Zettel, ein %-Wert relativ zur Notiz
   waere hier unpassend riesig. Geschwister von .postit-overlay-text
   (siehe JS), NICHT dessen Kinder -- sonst liefe die rote Schnur
   zwischen den Absatz-Pins (.postit-overlay-text::before) quer durchs
   Bild statt nur zwischen den Pins zu verlaufen. */
.postit-overlay-doodle {
  display: block;
  width: 3.4rem;
}

.postit-overlay-doodle--top {
  margin: 0 auto 1.2rem;
}

.postit-overlay-doodle--bottom {
  margin: 1.4rem auto 0;
}

.postit-overlay-doodle img {
  display: block;
  width: 100%;
  height: auto;
}

.page-sections {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 4rem;
  margin-top: 6rem;
  padding-top: 6rem;
  border-top: 1px solid var(--line);
}

/* Modifier fuer Seiten mit ungleich langen Abschnitten (aktuell nur
   langweilige-version.html/Werdegang) -- die Standard-Mehrspalten-
   Regel oben ist fuer mehrere AEHNLICH KURZE Bloecke nebeneinander
   gedacht (siehe beratung.html/formatentwicklung.html: 2-3 Saetze pro
   Karte). Sobald ein Abschnitt deutlich laenger ist als seine
   Zeilen-Nachbarn (hier: der mehrere Absaetze lange Werdegang-Text neben
   kurzen Kurz-gesagt-/Ausbildung-Blöcken), reisst grid-auto-flow eine
   hohe Zeile auf, in der die kuerzeren Spalten wie leere Luecken
   wirken -- siehe Chat: "zwei fast leere Spalten, eine volle rechts".
   Einspaltig statt dessen: liest sich wie ein durchgehender Artikel,
   unabhaengig davon, wie lang ein einzelner Abschnitt gerade ist. */
.page-sections--article {
  grid-template-columns: 1fr;
  max-width: 42rem;
  gap: 3rem;
}

/* War rgba(20,16,13,0.55) -- dieselbe Kicker-Rolle wie .page-kicker/
   .col-card h2/.contact-section h2, daher ebenfalls --accent-2 (Petrol). */
.page-section h2 {
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-bottom: 1.5rem;
}

/* War 0.95rem/--muted-light -- der einzige Ausreisser gegenueber
   .col-card p/.split-half p (identische Rolle: echter Absatztext einer
   Content-Card), obwohl --text-secondary laut eigener Definition (siehe
   :root) explizit fuer "Beratung/Profil-Absaetze" gedacht ist. Jetzt
   auf beides vereinheitlicht. */
.page-section p {
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  font-weight: 400;
  color: var(--text-secondary);
  line-height: 1.6;
}

/* Bisher hatte jeder .page-section immer nur einen einzigen Absatz --
   ohne Abstand fielen mehrere <p> (siehe langweilige-version.html:
   Werdegang) sonst ohne Trennung ineinander. */
.page-section p + p {
  margin-top: 1rem;
}

/* Bisher kein einziger .page-section-Absatz hatte einen Inline-Link
   (der Verweis auf Film & TV auf langweilige-version.html ist der
   erste) -- ohne diese Regel fiel er auf Browser-Standardblau zurueck.
   Gleiche Rolle/Farbe wie .legal-content a. */
.page-section p a {
  color: var(--accent);
}

/* Gleiche Rolle wie .legal-content ul (siehe dort), nur an .page-section
   ps Schriftgroesse (1.05rem statt 1rem) angeglichen -- fuer knappe aufzaehlende
   Listen (z.B. Preise/Festivals) innerhalb eines Fliesstext-Abschnitts,
   kein eigenstaendiges Tabellen-/CV-Layout. */
.page-section ul {
  margin: 0.5rem 0 0 1.25rem;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  color: var(--text-secondary);
  line-height: 1.7;
}

/* Redaktionelles Einzelfoto innerhalb eines .page-section-Fliesstexts
   (z.B. langweilige-version.html) -- bewusst kein Polaroid/Wand-Look
   wie auf profil.html (das ist dort die eigene Bildsprache der
   "Ermittlungswand"), sondern ein schlichtes, ruhiges Editorial-Bild mit
   Bildunterschrift, passend zum eher nuechternen Ton dieser Seite.
   EB-Garamond-kursive Caption wiederverwendet dieselbe Rolle wie
   .snapshot-zoom-caption (siehe dort), nur hell statt dunkel (heller
   Hintergrund hier, kein Overlay). */
.page-photo {
  margin: 2.5rem 0;
  max-width: 26rem;
}

.page-photo img {
  display: block;
  width: 100%;
  height: auto;
  border-radius: 0.25rem;
  box-shadow: 0 10px 24px rgba(20, 16, 13, 0.18);
  filter: grayscale(20%) contrast(1.02);
}

.page-photo-caption {
  margin-top: 0.85rem;
  font-family: 'EB Garamond', serif;
  font-style: italic;
  font-size: 0.95rem;
  line-height: 1.5;
  color: var(--text-secondary);
}

/* PORTFOLIO: Bildvorschau + fließende Titelliste (nur index.html) */
.portfolio-section {
  padding: 7rem 0;
  scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem); /* war hart 12rem -- veraltet seit die Kopfleiste verkleinert wurde, jetzt an --logo-bar-height gekoppelt wie die uebrigen Anker-Ziele */
}

/* .portfolio-list bleibt auf voller Sektionsbreite (wie der Rest der
   Seite, z.B. .page-section) -- das war der eigentliche Fix fuer "faellt
   aus dem Rahmen" (vorherige Variante zwang mit gleicher max-width auch
   die Liste auf Bildbreite zusammen). .portfolio-preview bekommt eine
   eigene, schmalere max-width und wird darueber zentriert (margin:auto)
   statt linksbuendig -- linksbuendig liess das schmalere Bild neben viel
   Leerraum "an den Rand geschoben" wirken, zentriert ist ausgewogener,
   auch wenn die Bildkante dadurch nicht mehr mit der Listenkante darunter
   fluchtet. */
.portfolio-preview {
  /* War static -- jetzt Bezugsrahmen fuer .portfolio-preview-nav
     (position:absolute), die Pfeile sitzen seitlich ausserhalb des
     schmaleren Vorschaubilds im umgebenden Leerraum der Section. */
  position: relative;
  width: 100%;
  max-width: min(100%, calc(42vh * 16 / 9));
  margin: 0 auto 3rem;
}

/* Blaettert direkt im Vorschaubild zum vorigen/naechsten Projekt, ohne
   die Lightbox zu oeffnen (siehe JS). Anders als die Lightbox-Pfeile
   reicht hier reines CSS (top:50%/transform) -- .portfolio-media hat
   ein festes 16:9-Seitenverhaeltnis ohne variablen Inhalt darunter,
   kein JS-Neuberechnen der Position noetig. */
.portfolio-preview-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  appearance: none;
  background: none;
  border: none;
  color: var(--muted-light);
  cursor: pointer;
  padding: 0.5rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: color 0.2s;
}

.portfolio-preview-nav:hover {
  color: var(--accent);
}

.portfolio-preview-prev {
  right: 100%;
  margin-right: 0.5rem;
}

.portfolio-preview-next {
  left: 100%;
  margin-left: 0.5rem;
}

.portfolio-preview-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-size: 1.9rem;
  line-height: 1;
}

.portfolio-list {
  width: 100%;
}

.portfolio-media {
  position: relative;
  aspect-ratio: 16 / 9;
  /* Breite/Hoehen-Deckel sitzt jetzt am Elternelement .portfolio-preview
     (siehe dort) -- hier einfach volle Breite der bereits begrenzten,
     zentrierten Box fuellen. */
  width: 100%;
  overflow: hidden;
  background-color: var(--line);
  cursor: pointer;
}

.portfolio-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.35s ease, transform 0.4s ease, filter 0.4s ease;
}

.portfolio-media:hover .portfolio-image {
  transform: scale(1.04);
  filter: brightness(0.92);
}

/* Klick-Signal statt separater Pfeil-Buttons/Hinweis-Pill: Titel und
   Genre liegen dauerhaft als Overlay auf dem Bild, unten-mittig -- war
   oben, verdeckte dort haeufiger wichtige Bildinhalte (Gesichter etc.),
   der untere Rand ist in der Regel unkritischer. Gradient von "to
   bottom" auf "to top" gedreht (Farbstopps unveraendert), damit der
   dunkle Verlauf weiterhin an der Text-Seite (jetzt unten) am
   staerksten ist und nach oben ausblendet -- sonst haette Text auf
   halbtransparentem Bild ohne Kontrast gestanden. Padding ebenfalls
   gespiegelt: die engere 1,75rem-Seite war die Anker-Kante (Bildrand),
   sitzt jetzt unten statt oben. */
.portfolio-media-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2.5rem 2rem 1.75rem;
  background: linear-gradient(to top, rgba(20, 16, 13, 0.78) 0%, rgba(20, 16, 13, 0.5) 55%, transparent 100%);
  pointer-events: none;
}

.portfolio-media-title-row {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.35rem;
}

.portfolio-media-genre {
  display: inline-flex;
  align-items: center;
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  font-size: 0.8rem; /* war 0.75rem -- reiht sich jetzt bei den uebrigen Uppercase-Labels (Kicker, Genre-Tags) ein statt knapp danebenzuliegen */
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* Zurueck zu rgba(255,255,255,0.8): --accent-2-on-dark (Petrol) sah
     hier auf dem echten Foto-Overlay wie ein Farbfehler aus, nicht wie
     ein bewusstes Label -- anders als auf den flachen Hintergruenden
     der Kicker/Fussnote wirkt Petrol direkt auf einem Foto nicht wie
     Gestaltung, sondern wie ein Rendering-Bug. */
  color: rgba(255, 255, 255, 0.8);
}

.portfolio-media-title {
  font-family: 'Inter', sans-serif;
  font-weight: 700;
  letter-spacing: normal;
  font-size: clamp(1.3rem, 1.9vw, 1.75rem);
  color: #fff;
  line-height: 1.15;
  position: relative;
}

/* bottom war -0.15em -- naeher ans Wort geholt (siehe .site-nav a::after). */
.portfolio-media-title::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.1em;
  height: 2px;
  background-color: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.portfolio-media:hover .portfolio-media-title::after {
  transform: scaleX(1);
}

/* Dieselbe wachsende Unterstrich-Animation wie beim Portfolio-Titel
   oben, hier fuer echte Text-Links -- currentColor statt --accent, da
   jeder dieser Links seine eigene Farbe traegt (Orange, geerbte
   Footer-Farbe, geerbte Kontakt-Farbe, geerbte Hero-Footnote-Farbe).
   .portfolio-item/.portfolio-sep bleiben bewusst aussen vor (kein
   ::after dort). */
/* bottom war -0.1em -- naeher ans Wort geholt (siehe .site-nav a::after). */
.col-card-link::after,
.footer-meta a::after,
.contact-email::after,
.name-sub-footnote a::after,
.lightbox-external-link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: -0.07em;
  height: 2px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform 0.28s cubic-bezier(0.34, 1.56, 0.64, 1);
}

.col-card-link:hover::after,
.footer-meta a:hover::after,
.contact-email:hover::after,
.name-sub-footnote a:hover::after,
.lightbox-external-link:hover::after {
  transform: scaleX(1);
}

/* Sender/namhafte Produktionsfirma/HFF -- nur bei Projekten mit
   belegtem meta-Feld befuellt (siehe portfolioItems/activatePreview()),
   :empty blendet die Zeile sonst komplett aus statt eine Luecke durch
   ihren eigenen margin-top zu hinterlassen. */
.portfolio-media-meta {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  font-size: 0.75rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  /* War rgba(255,255,255,0.55) (neutrales Grau) -- jetzt --accent-2-on-dark
     (helles Petrol), analog zu .portfolio-media-genre. */
  color: var(--accent-2-on-dark);
  margin-top: 0.5rem;
}

.portfolio-media-meta:empty {
  display: none;
}

.portfolio-headline {
  display: block;
  font-family: 'Inter', sans-serif;
  font-weight: 400;
  letter-spacing: normal;
  font-size: clamp(1.3rem, 1.9vw, 1.75rem);
  margin-bottom: 0.6rem;
  text-align: center;
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}

.portfolio-subline {
  display: block;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  line-height: 1.55;
  font-weight: 400;
  color: var(--text-secondary);
  margin-bottom: 2rem;
  text-align: center;
  max-width: 640px;
  margin-left: auto;
  margin-right: auto;
}

.portfolio-filters {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 1.5rem;
}

.portfolio-filter {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted-light);
  transition: color 0.2s;
}

.portfolio-filter:hover {
  color: var(--text-light);
}

.portfolio-filter.is-active {
  color: var(--accent);
}

.portfolio-flow {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  /* Zentriert statt linksbuendig/ragged-right: .portfolio-preview
     darueber und die Filter-Buttons darunter sind beide zentriert, die
     Liste wirkte dazwischen linksbuendig unausgewogen. Nur Desktop --
     auf Mobile steht ohnehin jeder Titel einzeln untereinander (siehe
     dortige Ueberschreibung), dort waere Zentrieren pro Zeile nur ein
     einzelner mittig stehender Titel, kein klassischer linksbuendiger
     Listen-Look mehr. */
  justify-content: center;
  column-gap: 0.5em;
  row-gap: 0.7em;
  margin-bottom: 2rem;
}

.portfolio-item {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  font-family: 'Inter', sans-serif;
  font-size: 1.3rem;
  font-weight: 400;
  color: var(--muted-light);
  transition: color 0.2s;
}

.portfolio-item:hover {
  color: var(--text-light);
}

.portfolio-item.is-active {
  color: var(--accent);
}

.portfolio-sep {
  font-family: 'Inter', sans-serif;
  font-size: 1.3rem;
  color: var(--muted-light);
}

/* Auf Desktop nie sichtbar -- ohne diese Regel stand hinter jedem
   Titel der fortlaufenden Liste dauerhaft sein Genre, was die Liste
   sehr unuebersichtlich machte. Auf Mobile bleibt das bestehende
   Verhalten (nur beim aufgeklappten Titel sichtbar, siehe
   .portfolio-item-genre.is-open in der Media Query) unveraendert --
   die dortige eigene display:none/is-open-Logik ueberschreibt dies
   dort ohnehin wieder. */
.portfolio-item-genre {
  display: none;
}

.portfolio-panel {
  display: none;
}

/* PORTFOLIO-LIGHTBOX (nur index.html): eigener, dauerhaft dunkler
   Vollbild-Layer -- bewusst immer dunkel, unabhängig vom Rest der
   Seite, wie bei Bildbetrachtern üblich. */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: none;
  background-color: rgba(13, 13, 12, 1);
}

.lightbox.is-open {
  display: block;
}

/* display:flex/min-height:0 sind der Kern des Einzel-Scroll-Fixes:
   .lightbox-content verteilt seine Hoehe jetzt auf Bild+Filmstrip (feste
   Groesse, flex:0 0 auto, siehe dort) und .lightbox-info (flex:1 1 auto)
   -- passt alles hinein, bleibt alles bei natuerlicher Groesse und wird
   von .lightbox-frames justify-content:center zentriert, wie bisher.
   Wird es zu hoch, schrumpft NUR noch .lightbox-info (und darin, ueber
   dieselbe Flex-Kette, letztlich nur .lightbox-desc-wrap) bis alles
   wieder in den Rahmen passt -- min-height:0 hebt Flexbox' Standard-
   Untergrenze auf, die sonst am Inhalt kleben bleibt und das Schrumpfen
   verhindert. Siehe .lightbox-frame: dessen frueheres eigenes
   overflow-y:auto entfaellt dadurch komplett -- es gibt jetzt nur noch
   EINEN Scroll-Bereich (.lightbox-desc-wrap), nicht mehr zwei
   verschiedene je nach Mauszeiger-Position ueber Bild oder Text. */
.lightbox-content {
  width: 100%;
  max-width: 1100px;
  max-height: 100%;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
}

/* War 58vh, dann 50vh -- bei Projekten mit vielen Credits/Zitaten (z.B.
   Love Alien) reichte der verbleibende Platz darunter oft nicht. Noch
   kleineres Bild schafft noch mehr Luft; die eigentliche Absicherung
   gegen zu wenig Platz ist aber jetzt struktureller Art, siehe
   .lightbox-content oben. flex:0 0 auto haelt Bild/Filmstrip auf
   fester Groesse -- nur .lightbox-info darf sich noch stauchen. Auf
   niedrigen Desktop-Fenstern (z.B. 1366x768-Laptops) frass das feste
   Bild bislang fast das gesamte Hoehenbudget auf, das Textfeld blieb ein
   paar Pixel hoher Streifen -- 44vh statt 50vh gibt ihm spuerbar mehr
   Raum, siehe auch .lightbox-filmstrip (dieselbe Formel, fuer buendige
   Breite) und .lightbox-info (gap-Verkleinerung, gleicher Zweck). */
.lightbox-media {
  position: relative;
  flex: 0 0 auto;
  width: min(100%, 1100px, calc(44vh * 16 / 9));
  aspect-ratio: 16 / 9;
  overflow: hidden;
  margin: 0 auto;
}

.lightbox-image {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: opacity 0.35s ease, transform 0.35s ease;
  transform: scale(1);
}

.lightbox-image.is-opening {
  transform: scale(1.05);
  transition: none;
}

.lightbox-image.is-kenburns {
  transition: opacity 0.35s ease, transform 4s linear;
  transform: scale(1.06);
}

/* Nur bei EINEM Still (siehe applyKenBurns() in JS) -- der einmalige
   Zoom von is-kenburns wuerde hier nach 4s einfach einfrieren, da kein
   naechstes Still folgt, das ihn zuruecksetzt. Endlos-Loop stattdessen:
   sanft rein- und rauszoomen statt Stillstand. */
@keyframes lightboxKenBurnsLoop {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.06); }
}

.lightbox-image.is-kenburns-loop {
  animation: lightboxKenBurnsLoop 8s ease-in-out infinite;
}

/* Waehrend der Trailer laeuft, bleibt das Hauptbild im DOM (Zustand
   fuer den naechsten Aufruf), wird aber ausgeblendet -- das iframe
   selbst uebernimmt sichtbar den ganzen .lightbox-media-Bereich. */
.lightbox-media.is-playing-trailer .lightbox-image {
  visibility: hidden;
}

.lightbox-trailer-frame {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

/* Wieder dieselbe vh-basierte Formel wie .lightbox-media/.lightbox-
   filmstrip (war zwischenzeitlich breiter/entkoppelt, siehe Git-
   Historie) -- auf Nutzerwunsch zurueckgesetzt: alle drei sollen buendig
   untereinanderstehen. Mehr Raum fuers Textfeld kommt stattdessen aus der
   Hoehe (Titel/Genre als Overlay ueber dem Bild statt eigene Zeile hier,
   siehe .lightbox-title-row; Poster jetzt neben dem Fliesstext statt in
   eigener Zeile, siehe .lightbox-poster-btn), nicht mehr aus der Breite. */
.lightbox-info {
  width: min(100%, 1100px, calc(44vh * 16 / 9));
  margin: 0 auto;
  text-align: left;
  padding: 0.75rem 1.5rem 1rem;
  margin-top: 0.75rem;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}

/* Poster/DVD-Cover, nur sichtbar bei Projekten mit eigenem poster-Feld
   (siehe JS: fillLightbox). Feste Breite + auto-Hoehe statt aspect-ratio/
   object-fit wie beim Hauptbild/Filmstreifen -- die sind bewusst auf
   16:9 gecovert, ein Hochformat-Poster darin wuerde oben/unten
   weggeschnitten. So bleibt das Poster komplett sichtbar, in seinem
   eigenen Seitenverhaeltnis. */
.lightbox-poster {
  width: 3.75rem;
  height: auto;
  border-radius: 0.25rem;
  display: block;
}

/* Steht jetzt als erstes Kind direkt im Beschreibungstext (siehe HTML-
   Kommentar bei #lightboxDescWrap), nicht mehr in einer eigenen Zeile
   neben dem (jetzt ausgelagerten) Titel. float:left auf Desktop laesst
   Zitat/Fliesstext daneben umbrechen, statt Poster und Text sich das
   knappe Hoehenbudget nacheinander teilen zu lassen -- direkter Gewinn
   fuers "Textfeld optimal ausnutzen". overflow-y:auto auf dem
   umgebenden .lightbox-desc-wrap (siehe dort) enthaellt den Float
   automatisch, kein eigener Clearfix noetig. Auf Mobile bewusst kein
   Float mehr (siehe Media Query) -- bei der dortigen Textbreite bliebe
   neben dem Poster kaum noch Platz zum Lesen. */
.lightbox-poster-btn {
  appearance: none;
  background: none;
  border: 0;
  padding: 0;
  cursor: zoom-in;
  float: left;
  margin: 0 1rem 0.75rem 0;
  transition: transform 0.2s;
}

.lightbox-poster-btn:hover {
  transform: scale(1.04);
}

/* Zweite Overlay-Ebene UEBER der Lightbox (siehe z-index), zeigt das
   Poster gross im echten Hochformat -- .lightbox-media/.lightbox-content
   sind selbst fest auf 16:9 ausgelegt, dort haette ein Hochformat-Poster
   keinen sauberen Platz. Eigener, dunklerer Hintergrund statt
   transparent, damit die Lightbox dahinter nicht durchscheint/ablenkt. */
.poster-zoom {
  position: fixed;
  inset: 0;
  z-index: 110;
  display: none;
  align-items: center;
  justify-content: center;
  background-color: rgba(8, 8, 7, 0.96);
  padding: 4rem 1.5rem;
  cursor: zoom-out;
}

.poster-zoom.is-open {
  display: flex;
}

.poster-zoom-image {
  display: block;
  max-width: min(90vw, 520px);
  max-height: 100%;
  width: auto;
  height: auto;
  border-radius: 0.4rem;
  box-shadow: 0 2rem 4rem rgba(0, 0, 0, 0.5);
  cursor: default;
}

/* #posterZoomClose nutzt die .lightbox-close-Klasse fuer identische
   Optik -- hier nur die Position korrigiert (.lightbox-close ist
   eigentlich relativ zu .lightbox-frame positioniert, hier aber direkt
   Kind von .poster-zoom selbst). */
#posterZoomClose {
  position: fixed;
}

/* Titel + Genre in einer Zeile, Genre hinter dem Titel (DOM-Reihenfolge:
   Titel zuerst -- semantisch sinnvoller, Screenreader lesen den Titel vor
   seinem Metadaten-Tag). align-items:baseline statt center, damit die
   kleinere Genre-Schrift auf derselben Grundlinie wie der Titel sitzt,
   nicht mittig dazu.
   Auf Desktop als Gradient-Overlay unten aufs Vorschaubild gelegt (siehe
   HTML-Kommentar bei .lightbox-media) -- position:absolute nimmt die
   Zeile komplett aus dem Fluss von .lightbox-info, die eigene Zeile
   Hoehe, die Titel/Genre vorher dort gekostet haben, kommt so direkt der
   Beschreibung darunter zugute. Der Verlauf laeuft von deckendem Schwarz
   unten zu transparent oben (nur ueber dem unteren Drittel des Bilds),
   text-shadow zusaetzlich als Sicherheitsnetz fuer den Rand des
   Verlaufs. Auf Mobile bewusst noch normaler Fluss statt Overlay (siehe
   Media Query) -- eigener Wunsch, erstmal nur auf Desktop auszuprobieren. */
.lightbox-title-row {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 1;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  column-gap: 0.6em;
  padding: 2.5rem 1.25rem 0.85rem;
  background: linear-gradient(to top, rgba(0, 0, 0, 0.8), rgba(0, 0, 0, 0) 100%);
}

.lightbox-genre {
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 400;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* War --muted-on-dark (neutrales Grau) -- jetzt --accent-2-on-dark
     (helles Petrol), analog zu .portfolio-media-genre. */
  color: var(--accent-2-on-dark);
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

.lightbox-title {
  font-family: 'Inter', sans-serif;
  font-size: clamp(1.3rem, 1.9vw, 1.75rem);
  font-weight: 400;
  letter-spacing: normal;
  color: #e8e6e1;
  text-shadow: 0 1px 4px rgba(0, 0, 0, 0.6);
}

/* Scrollbarer Block statt mitwachsender Lightbox -- laengere Texte
   (data-long-desc, siehe JS) wuerden sonst ueber den Rahmen hinaus-
   wachsen und Navigation/Pfeile aus dem sichtbaren Bereich schieben.
   Die eigentliche Groesse ist jetzt kein fester vh-Wert mehr, sondern
   flex:1 1 auto -- der Wrapper nimmt genau den Platz, der nach Bild/
   Filmstrip/Titel im Rahmen noch uebrig ist (siehe die Flex-Kette ab
   .lightbox-content), und schrumpft erst dann auf sich selbst, wenn
   selbst das nicht reicht. Fliesstext + optionale Produktionsdaten-
   Liste teilen sich diesen einen Scroll-Bereich statt zwei unabhaengige
   zu haben, Bild/Titel/Nav bleiben an fester Position. mask-image
   blendet die letzte Zeile sanft aus, als Hinweis, dass mehr folgt,
   wenn tatsaechlich gescrollt werden kann (bei kurzem Inhalt ohne
   Overflow unsichtbar, da nichts abgeschnitten wird). */
/* Wrapper um #lightboxDescWrap + den eigenen Scroll-Indikator (siehe
   .lightbox-scrollbar) -- position:relative haengt den Indikator an
   diese Box statt an ein weiter aussen liegendes Element, display:flex
   (Row, Standardrichtung) laesst #lightboxDescWrap per flex:1 1 auto
   sowohl Breite als auch (per align-items:stretch, Standard) Hoehe der
   Box fuellen. */
.lightbox-desc-scroll-area {
  position: relative;
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
}

.lightbox-desc-wrap {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  /* Gleicher Grund wie bei .snapshot-zoom (siehe dortiger Kommentar):
     ohne dies kann Scrollen auf iOS Safari am Textende in die
     dahinterliegende Seite "durchbrechen". */
  overscroll-behavior-y: contain;
  background: rgba(255, 255, 255, 0.045);
  border-radius: 0.5rem;
  padding: 0.75rem 0.75rem 0.75rem 0.9rem;
}

/* Eigener, per JS aus scrollTop/scrollHeight/clientHeight berechneter
   Scroll-Indikator (siehe fillLightbox/lbDescWrap-Scroll-Listener in
   index.html) statt native Browser-Scrollbar -- die laesst sich nicht
   zuverlaessig dauerhaft sichtbar erzwingen (macOS/Safari blenden sie
   je nach Systemeinstellung trotz ::-webkit-scrollbar-CSS wieder aus).
   is-visible wird wie is-scrollable auf #lightboxDescWrap gesetzt.
   pointer-events:none: rein optisches Feedback, kein eigenes Drag-
   Handle -- gescrollt wird weiterhin ganz normal ueber den Inhalt. */
.lightbox-scrollbar {
  display: none;
  position: absolute;
  top: 0;
  bottom: 0;
  right: 2px;
  width: 4px;
  border-radius: 3px;
  background: rgba(255, 255, 255, 0.1);
  pointer-events: none;
}

.lightbox-scrollbar.is-visible {
  display: block;
}

.lightbox-scrollbar-thumb {
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  border-radius: 3px;
  background: var(--accent-2-on-dark);
}

/* Faedet bis zu einer Rest-Deckkraft aus (nicht bis 0) -- bei ganz
   transparent verschwand die letzte Zeile komplett im dunklen
   Lightbox-Hintergrund (sah aus wie vom Filmstrip darunter verdeckt,
   war aber nur der Verlauf selbst -- besonders auf Mobile auffaellig,
   wo die 16vh-Box ohnehin wenig Platz hat und der Fade-Bereich
   dadurch einen groesseren Anteil des sichtbaren Texts betraf). */
.lightbox-desc-wrap.is-scrollable {
  mask-image: linear-gradient(to bottom, #000 calc(100% - 1.4em), rgba(0, 0, 0, 0.45) 100%);
}

/* Deutlicherer Scroll-Hinweis als der Fade-Verlauf allein -- besonders
   auf Mobile gewuenscht. Sitzt als Geschwister AUSSERHALB von
   #lightboxDescWrap (siehe HTML-Kommentar dort), Sichtbarkeit an
   .is-scrollable des Wrappers gekoppelt statt eigenem JS-Toggle.
   Verschwindet zusaetzlich, sobald tatsaechlich zu Ende gescrollt
   wurde (.is-at-bottom, per Scroll-Listener in fillLightbox/selectStill
   -- siehe JS).
   BEWUSST normaler Blockfluss statt position:absolute -- ein negativer
   bottom-Versatz (um nicht mit der letzten, nur abgedunkelten statt
   ausgeblendeten Textzeile zu kollidieren) schob den Pfeil bei manchen
   Viewport-Hoehen ueber die Kante von .lightbox-frame hinaus, dessen
   overflow-y:auto ihn dort abschnitt (Inhalt fuellte den Rahmen exakt
   aus, kein Platz fuer absolut positionierte Elemente ausserhalb des
   natuerlichen Fluss-Endes). Im normalen Fluss zaehlt seine Hoehe zum
   Elternelement, kein Clipping-Risiko mehr. */
.lightbox-scroll-hint {
  display: none;
  flex: 0 0 auto;
  text-align: center;
  margin-top: 0.4rem;
  font-size: 1.1rem;
  line-height: 1;
  color: var(--accent-2-on-dark);
  pointer-events: none;
  animation: lightboxScrollHintBounce 1.4s ease-in-out infinite;
}

/* Nur noch auf Mobile gezeigt (siehe Media Query weiter unten) -- auf
   Desktop faellt der Pfeil komplett weg, der dadurch frei werdende
   Platz geht an .lightbox-desc-scroll-area (flex:1 1 auto zieht ihn
   sich automatisch). HTML-Element/JS bleiben bestehen, nur diese
   Sichtbarkeits-Regel ist jetzt mobile-exklusiv statt global. */

/* visibility statt display:none -- ein display:none haette hier den
   Platz des Hinweises aus dem Fluss genommen, .lightbox-content dadurch
   kuerzer gemacht und wegen .lightbox-frames justify-content:center
   den GESAMTEN Inhalt (inkl. Vorschaubild) sichtbar neu zentriert,
   sobald man beim Scrollen das Ende des Textblocks erreichte -- genau
   das gemeldete "Bild verrutscht beim Scrollen". visibility:hidden
   haelt den reservierten Platz, keine Hoehenaenderung mehr. */
.lightbox-desc-wrap.is-scrollable.is-at-bottom ~ .lightbox-scroll-hint {
  visibility: hidden;
}

@keyframes lightboxScrollHintBounce {
  0%, 100% { transform: translateY(0); opacity: 0.5; }
  50% { transform: translateY(3px); opacity: 1; }
}

.lightbox-desc {
  margin-top: 0.4rem;
  font-family: 'Inter', sans-serif;
  font-size: 1.05rem;
  font-weight: 400;
  /* Bleibt hell: liegt in der dunklen Lightbox (--muted-on-dark), nicht
     auf hellem --bg-light -- --text-secondary waere hier fast
     unlesbar. */
  color: #a9a8a3;
  line-height: 1.55;
  /* KEIN max-width mehr (war 640px) -- seit die Beschreibung nicht mehr
     Teil derselben Flex-Zeile wie das Poster ist (siehe .lightbox-info-
     top-Kommentar), war dieser feste Wert der einzige verbleibende
     Grund fuer eine schmale Spalte: bei einem breiten/hohen Viewport
     blieb ein grosser leerer Rand rechts neben dem Text stehen statt
     die vorhandene Boxbreite zu nutzen. */
}

/* Kurzfakten (Jahr/Länge/Format) als kompakte Metazeile, abgetrennt von
   den eigentlichen Personen-/Firmen-Credits (siehe .lightbox-credits-
   line weiter unten) -- selbe Farbrolle wie .lightbox-genre
   (accent-2-on-dark), damit beide "strukturierten Metadaten"-Zeilen
   (Overlay-Genre oben, Fakten hier unten) optisch zusammengehoeren.
   War Teil derselben Label/Wert-Liste wie alle anderen Credits (siehe
   Git-Historie) -- wirkte dort wie ein Datenblatt statt einer
   Filmbeschreibung (siehe Chat: "Credits ziemlich langweilig"). */
.lightbox-facts {
  margin-top: 0.9rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--accent-2-on-dark);
}

/* Alle uebrigen Credits (Regie, Kamera, Schnitt, Produktion, Förderung,
   Festival etc.) als fliessende Zeile im Stil eines Filmplakat-
   Abspanns statt tabellarischer Liste: LABEL in Kapitaelchen-Optik
   (uppercase + letter-spacing, siehe .lightbox-credits-label), Wert
   direkt dahinter in normalem Fliesstext, Eintraege durch " · "
   getrennt (siehe JS: fillLightbox()). Bricht bei Bedarf einfach um
   (kein Grid mehr noetig), da alles derselbe Textfluss ist. */
.lightbox-credits-line {
  margin-top: 0.9rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.85rem;
  line-height: 1.7;
  color: #a9a8a3;
}

.lightbox-credits-label {
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: #e8e6e1;
}

.lightbox-credits-sep {
  color: #7d7c78;
}

/* Echte Pressezitate mit Quelle, nur bei Projekten mit belegtem
   quotes-Feld (siehe portfolioItems/fillLightbox) -- leer und
   unsichtbar sonst. Eigener Block statt in .lightbox-credits-line:
   Zitate sind Fliesstext-Saetze, keine kompakten Label/Wert-Paare.
   Steht nach .lightbox-facts im Markup (Pressezitat wie auf einem
   Filmposter, Kurzfakten direkt darueber), margin-top entsprechend
   klein -- der groessere Abstand nach oben kommt schon von
   .lightbox-facts selbst (0.9rem), bzw. direkt vom Poster/Bild, wenn
   ein Projekt keine Kurzfakten hat. */
.lightbox-quotes {
  margin-top: 0.4rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

/* :empty statt bedingtem JS-hidden-Attribut -- lbFacts/lbQuotes/
   lbCreditsLine werden in fillLightbox() immer erst geleert, ohne
   diese Regel wuerde ein Projekt ohne Kurzfakten/Zitate/Credits
   trotzdem den margin-top des leeren Blocks als Luecke zeigen. */
.lightbox-facts:empty,
.lightbox-quotes:empty,
.lightbox-credits-line:empty {
  display: none;
}

/* Ausgehender Link (z.B. Mediathek-Beitrag), wenn sich ein Video nicht
   einbetten laesst -- nur bei Projekten mit belegtem link-Feld
   sichtbar (siehe portfolioItems/fillLightbox). */
.lightbox-external-link {
  display: inline-block;
  position: relative;
  margin-top: 0.9rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--accent-2-on-dark);
  text-decoration: none;
}

/* display:inline-block oben ist eine Autoren-Regel und schlaegt sonst
   das display:none des hidden-Attributs (nur aus dem UA-Stylesheet,
   verliert gegen Autoren-Regeln unabhaengig von der Spezifitaet --
   siehe derselbe Fix bei .lightbox-trailer-btn/.filmstrip-thumb). */
.lightbox-external-link[hidden] {
  display: none;
}

.lightbox-quote-text {
  font-family: 'EB Garamond', serif;
  font-style: italic;
  font-size: 1rem;
  color: #d8d6d0;
  line-height: 1.5;
}

.lightbox-quotes cite {
  display: block;
  margin-top: 0.15rem;
  font-family: 'Inter', sans-serif;
  font-style: normal;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  /* War --accent-2-on-dark (Petrol) -- Presse-Quellen sind eine reine
     Herkunftsangabe, kein hervorzuhebendes Label wie Genre/Sender, daher
     neutrales Grau wie die uebrigen Nebensaechlichkeiten auf dunklem
     Grund (siehe --muted-on-dark). */
  color: var(--muted-on-dark);
}

/* Eigene Breite/Zentrierung noetig, seit der Filmstrip direkt unters
   Bild gewandert ist (vorher erbte er die Breite von .lightbox-info als
   dessen Kind) -- dieselbe Formel wie .lightbox-media (44vh, siehe
   dortigen Kommentar), damit beide buendig untereinanderstehen.
   .lightbox-info ist seit Kurzem NICHT mehr Teil dieser Formel (siehe
   dortigen Kommentar) -- das Textfeld darf breiter sein als Bild/
   Filmstrip. */
.lightbox-filmstrip {
  flex: 0 0 auto;
  width: min(100%, 1100px, calc(44vh * 16 / 9));
  margin: 1.25rem auto 0;
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  padding-bottom: 0.25rem;
}

.filmstrip-thumb {
  flex: 0 0 auto;
  width: 6rem;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  appearance: none;
  background: none;
  border: 2px solid transparent;
  padding: 0;
  cursor: pointer;
  opacity: 0.55;
  transition: opacity 0.2s, border-color 0.2s;
}

.filmstrip-thumb:hover {
  opacity: 0.85;
}

.filmstrip-thumb.is-active {
  opacity: 1;
  border-color: var(--accent);
}

.filmstrip-thumb-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: grayscale(100%);
  transition: filter 0.2s;
}

.filmstrip-thumb.is-active .filmstrip-thumb-image {
  filter: grayscale(0%);
}

/* Trailer-Thumb: eigener Filmstrip-Eintrag statt Play-Button ueber dem
   Hauptbild (fruehere Umsetzung deckte dabei das komplette Vorschau-
   bild ab) -- nutzt dasselbe "Thumb anklicken zeigt etwas anderes"-
   Muster, oeffnet aber den Vimeo-Trailer statt ein Still. Volle Opacity
   von Anfang an (statt der 0.55 der Foto-Thumbs): repraesentiert keine
   Still-Auswahl, die von "aktiv" unterschieden werden muesste, sondern
   einen dauerhaft gleich wichtigen Aktions-Button. */
.filmstrip-thumb.is-trailer {
  position: relative;
  opacity: 0.9;
}

.filmstrip-thumb.is-trailer:hover {
  opacity: 1;
}

/* Abgedunkeltes Thumb-Foto als Hintergrund, Play-Kreis mittig darueber. */
.filmstrip-thumb.is-trailer .filmstrip-thumb-image {
  filter: grayscale(60%) brightness(0.55);
}

.filmstrip-thumb.is-trailer:hover .filmstrip-thumb-image {
  filter: grayscale(30%) brightness(0.65);
}

/* Icon + Text statt nur Icon -- bei der kleinen Thumb-Groesse (6rem
   breit, 16:9) ist ein blosses Play-Dreieck zu wenig eindeutig, koennte
   auch fuer "noch ein Still" gehalten werden. Eigene Zeile statt neben
   dem Icon in einer Reihe: bei 96px Thumb-Breite ist fuer "▶ Trailer"
   nebeneinander kaum Luft, untereinander bleibt beides gut lesbar. */
.filmstrip-trailer-badge {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.2rem;
  color: #f5f4f0;
  pointer-events: none;
}

.filmstrip-trailer-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.4rem;
  height: 1.4rem;
  border-radius: 50%;
  background: var(--accent);
  font-size: 0.6rem;
  /* Play-Dreieck optisch zentrieren -- ▶ hat selbst etwas Luft rechts. */
  padding-left: 0.12rem;
}

.filmstrip-trailer-label {
  font-family: 'Inter', sans-serif;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.6);
}

/* War position:absolute (relativ zu .lightbox-frame, das selbst
   overflow-y:auto traegt) -- scrollte .lightbox-frame bei laengerem
   Inhalt (siehe .lightbox-media-Kommentar), verschwand der Button mit
   dem Bild aus dem sichtbaren Bereich, kein Weg mehr zum Schliessen
   ohne Escape/Klick daneben. position:fixed haelt ihn unabhaengig vom
   Scroll-Zustand am Viewport -- deckt sich jetzt mit dem, was auf
   Mobile ohnehin schon galt (siehe dortige, jetzt ueberfluessige
   eigene Regel). */
.lightbox-close {
  appearance: none;
  background: none;
  border: 1px solid rgba(232, 230, 225, 0.22);
  border-radius: 0.65rem;
  color: #e8e6e1;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  outline: none;
  transition: color 0.2s, text-shadow 0.2s, transform 0.2s, border-color 0.2s;
  position: fixed;
  z-index: 2;
  top: 1.5rem;
  right: 1.5rem;
  width: 3.25rem;
  height: 3.25rem;
  font-size: 2rem;
}

.lightbox-close:hover {
  color: var(--accent);
  border-color: var(--accent);
  transform: scale(1.12);
}

.lightbox-close:focus-visible {
  outline: 1px solid var(--muted-on-dark);
  outline-offset: 3px;
}

/* overflow-y:auto als Sicherheitsnetz zusaetzlich zum internen Scroll
   in .lightbox-desc: dessen max-height allein reicht nicht immer --
   Bild + Titel + (gedeckelte) Beschreibung + Filmstreifen + Navigation
   koennen zusammen trotzdem hoeher sein als der Rahmen, v.a. auf
   niedrigeren Viewports. justify-content bleibt center fuer den
   Normalfall (Inhalt passt, keine Ueberlaufsituation); bei Overflow
   scrollt der ganze Rahmen statt Inhalt am Rand abzuschneiden. */
/* War overflow-y:auto -- der Rahmen konnte dadurch selbst scrollen,
   zusaetzlich zum eigenen Scroll-Bereich von .lightbox-desc-wrap. Zwei
   verschiedene Scroll-Ziele je nach Mauszeiger-Position (Bild vs. Text)
   waren verwirrend, und ein Frame-Scroll konnte Bild/Schliessen-Button/
   Pfeile aus dem sichtbaren Bereich schieben. Jetzt uebernimmt
   .lightbox-content per Flexbox die gesamte Platzverteilung (siehe
   dortiger Kommentar) -- der Rahmen selbst muss nie mehr scrollen, nur
   noch zentrieren/clippen, falls durch Rundungsfehler minimal etwas
   ueberstehen sollte. */
.lightbox-frame {
  position: absolute;
  inset: 4rem 2rem;
  max-width: 1200px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  justify-content: center;
  overflow: hidden;
}

/* Auf Desktop wieder seitlich vom Vorschaubild statt darunter -- bei
   laengerem Inhalt (viele Credits/Filmstrip) macht .lightbox-frames
   eigenes overflow-y:auto die Pfeile sonst erst nach dem Scrollen
   sichtbar. position:fixed statt absolute: JS (positionNavArrows())
   berechnet centerY/left ueber die tatsaechliche Position von
   .lightbox-media und setzt sie direkt als Viewport-Koordinaten, kein
   eigener Platz im Textfluss noetig -- .lightbox-nav-row bleibt daher
   ohne eigene Groesse/Position. Auf Mobile explizit auf static
   zurueckgesetzt (siehe Media Query), dort greift wieder die
   Blockfluss-Reihe wie zuvor. */
.lightbox-nav-row {
  margin: 0;
  padding: 0;
}

.lightbox-nav {
  appearance: none;
  background: none;
  border: none;
  color: inherit;
  cursor: pointer;
  outline: none;
  position: fixed;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.5rem;
  z-index: 5;
}

.lightbox-nav:focus-visible {
  outline: 1px solid var(--muted-on-dark);
  outline-offset: 3px;
}

.lightbox-arrow {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.75rem;
  height: 2.75rem;
  font-size: 1.9rem;
  color: #e8e6e1;
  line-height: 1;
  transition: color 0.2s, text-shadow 0.2s, transform 0.2s;
}

/* Wie mobil: keine Beschriftung neben dem Pfeil -- aria-label auf
   .lightbox-nav traegt die Bedeutung weiterhin fuer Screenreader. */
.lightbox-nav-label {
  display: none;
}

.lightbox-nav:hover .lightbox-arrow {
  color: var(--accent);
  transform: scale(1.12);
}

/* KONTAKT (alle drei Seiten) */
.contact-section {
  padding: 4rem 0 2rem;
  scroll-margin-top: calc(var(--logo-bar-height) + 1.5rem); /* war hart 12rem -- veraltet seit die Kopfleiste verkleinert wurde, jetzt an --logo-bar-height gekoppelt wie die uebrigen Anker-Ziele */
}

/* War rgba(20,16,13,0.55) -- dieselbe Kicker-Rolle wie .page-kicker/
   .col-card h2, daher ebenfalls --accent-2 (Petrol). */
.contact-section h2 {
  font-family: 'Inter', sans-serif;
  font-size: 0.8rem;
  font-weight: 900;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--accent-2);
  margin-bottom: 2rem;
}

/* --text-secondary statt --muted-light: der Kontakt-Satz ist inhaltlich
   ein normaler Absatz (wie .col-card p/.split-half p), keine grosse
   Seiten-Einleitung unter einem H1 wie .page-lead (dessen Farbrolle
   hier faelschlich mitkopiert wurde) -- dafuer ist --muted-light
   eigentlich reserviert und wirkte dadurch heller als der uebrige
   Fliesstext der Seite. */
.contact-lead {
  font-family: 'Inter', sans-serif;
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  font-weight: 400;
  color: var(--text-secondary);
  line-height: 1.6;
  max-width: 640px;
  margin-bottom: 1.5rem;
}

.contact-email {
  font-family: 'Inter', sans-serif;
  font-size: clamp(2rem, 4vw, 3.5rem);
  color: inherit;
  text-decoration: none;
  display: inline-block;
  position: relative;
}

/* Elmo-Easteregg: Popo + Schwanz unten rechts in der Sektion, per Klick/
   Tap ausgeloest (siehe JS-Kommentar dort). .contact-section braucht
   dafuer position:relative als Bezugsrahmen -- war vorher nicht noetig,
   da sie bislang keine absolut positionierten Kinder hatte. */
.contact-section { position: relative; }

/* Holt den Kontakt-Text explizit vor .contact-elmo-wrap (siehe unten):
   ohne eigenen z-index wuerde das spaeter im DOM stehende, ebenfalls
   positionierte Video sonst gewinnen und den Text bei Ueberlappung
   optisch verdecken (dessen --bg-light-Hintergrund ist blickdicht,
   siehe Chat -- "hallo@etwasistimmer.de" wurde dadurch sichtbar
   abgeschnitten). position:relative ohne Versatz aendert am Layout
   nichts, macht die Elemente aber z-index-faehig. */
.contact-section h2,
.contact-lead,
.contact-email {
  position: relative;
  z-index: 1;
}

/* top:0 bezieht sich auf .contact-section's Rahmen (den echten
   Sektionsrand VOR deren eigenem oberem Padding), genau das war mit
   "bis an die Sektionsgrenze" gemeint. Hoehe wird immer per JS gesetzt
   (siehe Skript nach </footer>) -- der Wert hier ist nur der Fallback,
   bevor/falls JS greift.
   right: 1.5rem bleibt auf Tablet/Desktop-Breiten (siehe JS-Kommentar,
   ">= 600px") die tatsaechlich wirksame Regel, NICHT nur ein Fallback --
   bewusst relativ zu .contact-section selbst (dem randlosen Element auf
   voller Bildschirmbreite), nicht zu .section-inner (der auf 1200px
   begrenzten Innenspalte). Auf Handy-Breiten ueberschreibt JS right mit
   einem dynamisch berechneten Wert relativ zur Innenspalte (dort gibt
   es keinen Text daneben, an dem sich "nach rechts wandern" orientieren
   koennte). */
.contact-elmo-wrap {
  position: absolute;
  top: 0;
  right: 1.5rem;
  height: 220px;
  /* Zuschnitt zeigt bewusst nur Huefte+Schwanz, oben hart abgeschnitten
     (siehe Chat: "Hund an der Huefte abschneiden" -- der Hund wirkt so,
     als sitze er hinter/unter der Sektion und nur der untere Teil ragt
     hinein). Mit top:0/Hoehe bis zur Email-Unterkante (siehe JS) wird
     GENAU dieser abgeschnittene Ausschnitt hochskaliert, bis seine
     (bereits gekappte) Oberkante an der Sektionsgrenze ankommt -- nicht
     der ganze Hund samt Kopf verkleinert eingepasst. */
  aspect-ratio: 424 / 206;
  /* Selbst nicht klickbar -- nur .contact-elmo (die "sichere" mittlere
     Zone ohne Schwanz-Ausschlag) bekommt pointer-events wieder zurueck.
     Ausserhalb dieser Zone (Video UND das, was vom Wrap ausserhalb des
     Videos uebrig bleibt) fallen Klicks so immer zum darunterliegenden
     Element durch, z.B. dem Email-Link, siehe Chat/Kommentar in
     index.html. */
  pointer-events: none;
}

.contact-elmo-video {
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* Liegt exakt auf dem <video>, bis das 'playing'-Event bestaetigt, dass
   wirklich ein Frame gerendert wird (siehe Skript) -- auf iOS sonst ein
   kurzer Leerblitz: der Browser blendet den poster schon beim Aufruf von
   play() aus, bevor das erste Bild tatsaechlich dekodiert/gemalt ist
   (siehe Chat: "es gibt einen Leer-Blitz vom Still -- dann verschwindet
   das Bild kurz -- dann beginnt die Animation"). Dasselbe poster-Bild
   als eigenstaendiges <img> ueberdeckt genau diese Luecke. */
.contact-elmo-poster-overlay {
  position: absolute;
  top: 0;
  left: 0;
}

/* Deckt nur die mittlere ~44%-Zone ab (Huefte/Beine), nicht die
   aeusseren Raender, in denen der Schwanz beim Wedeln ausschlaegt --
   Klicks in diesem Rand (leere, auf --bg-light gebackene Videoflaeche,
   kein sichtbarer Hund) sollen nicht den Easteregg ausloesen. Masse per
   Pixelmessung ueber alle 96 Animationsframes von elmo-wag.mp4 (424px
   breiter Zuschnitt, rechts bereits um den ungenutzten Rand gekuerzt,
   siehe Kommentar bei ASPECT_RATIO im Skript): Huefte/Pfoten liegen bei
   32-76% der Breite, mit etwas Sicherheitsabstand nach innen. */
.contact-elmo {
  display: block;
  position: absolute;
  top: 0;
  left: 30%;
  width: 44%;
  height: 100%;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
  pointer-events: auto;
}

/* Kein fester Breakpoint mehr zum Ausblenden auf schmalen Bildschirmen
   -- das Skript nach </footer> blendet .contact-elmo-wrap stattdessen
   per JS aus, sobald die tatsaechlich berechnete Groesse zu klein wird
   (MIN_VISIBLE_HEIGHT_PX), statt anhand einer geschaetzten
   Viewport-Breite zu raten. */

/* Eigenstaendiger Footer statt Teil von .contact-section: die Seite
   kann so mehrfach denselben Footer verwenden (index/beratung/profil),
   ohne dass er an den Kontaktbereich gebunden ist. Heller Grundton wie
   Header/Nav-Zone (--bg-page), nicht --bg-light wie die Content-Bloecke,
   damit er sich sichtbar davon absetzt. */
.site-footer {
  background-color: var(--bg-page);
  padding: 1rem 0;
}

.site-footer-inner {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 2rem;
}

.footer-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 2rem;
  font-family: 'Inter', sans-serif;
  font-size: 0.85rem;
  color: var(--muted-light);
}
.footer-meta a { color: inherit; text-decoration: none; position: relative; }
.footer-meta a:hover { color: var(--text-light); }

/* Kein min-height: bleibt leer, bis tatsaechlich Icons/Links darin
   stehen -- sonst zieht der leere Platzhalter unnoetig Footer-Hoehe. */
.footer-social {
  display: flex;
  gap: 1rem;
  min-height: 0;
}

/* MOBIL RESPONSIVE -- ein einziger Breakpoint-Block statt mehrerer
   verstreuter @media-Regeln, gilt fuer alle drei Seiten gemeinsam. */
@media (max-width: 768px) {
  :root {
    --logo-bar-height-mobile: 4rem; /* statt 7rem -- Nav ist bereits nach unten gewandert, oben wird nur noch der Titel gebraucht */
    /* Entspricht der real gerenderten Hoehe von .site-nav OHNE Safe-
       Area -- .site-nav bekommt darauf eine min-height, .site-footer
       eine gleich hohe zusaetzliche Bottom-Padding, damit beide an
       derselben Zahl haengen statt zwei unabhaengige Werte zu pflegen.
       War 3rem (~47.8px, eine Zeile bei 4 Nav-Punkten) -- seit
       Formatentwicklung als fuenfter Punkt dazukam, umbricht die Leiste
       auf normalen Telefonbreiten (<= ~430px) auf zwei Zeilen (gemessen:
       ~76.2px = 4.76rem), auf breiteren Mobile-Breakpoint-Groessen
       (Tablet-Portrait, ~600px+) bleibt sie einzeilig. Auf den
       haeufigeren Telefon-Fall ausgelegt (sonst waere der Footer-Inhalt
       dort unerreichbar unter der Leiste), auf den breiteren Groessen
       bleibt dadurch etwas ungenutzter Puffer -- unkritisch. */
    --bottom-nav-height-mobile: 4.9rem;
  }

  .poem-block {
    left: 6vw; right: 6vw; padding-right: 0;
    /* Kein eigenes padding-bottom mehr fuer den Scroll-Hint noetig --
       der ist jetzt position:fixed und schuetzt sich per env() selbst
       vor Safaris unterer Systemleiste (siehe .scroll-hint). */
  }

  /* Mobile: die (jetzt groessere) Zeigehand ruecke sonst zu nah an
     "KURT TUCHOLSKY" heran -- ein groesserer bottom-Wert schiebt sie
     (falsch herum gedacht im ersten Versuch) naeher an den Text statt
     weiter weg, da "bottom" von der Bildschirmunterkante aus zaehlt.
     Kleinerer Wert als die 10vh-Basis (die selbst schon knapp war,
     seit die Hand vergroessert wurde) schafft mehr Abstand nach oben. */
  .scroll-hint {
    bottom: calc(6vh + env(safe-area-inset-bottom));
  }
  .poem-line { font-size: 1.35rem; white-space: normal; }
  .site-title {
    /* Vertikal mittig im (jetzt kompakteren) Kopfbereich statt fixem
       top-Wert -- haengt sich an --logo-bar-height-mobile statt an einem
       geschaetzten px-Wert, bleibt also automatisch mittig, egal wie
       hoch die Kopfleiste gerade eingestellt ist. */
    top: calc(var(--logo-bar-height-mobile) / 2);
    transform: translate(-50%, -50%);
    font-size: 1.35rem;
  }

  /* + --logo-bar-safe-extend wie in der Basisregel (siehe dort) -- ohne
     das wuerde diese Mobile-Hoehe die Statusleisten-Abdeckung wieder
     wegkuerzen, die Unterkante saesse 100px zu hoch. */
  .logo-bar { height: calc(var(--logo-bar-height-mobile) + var(--logo-bar-safe-extend)); }

  .site-nav {
    top: auto;
    bottom: 0; left: 0;
    transform: none;
    width: 100%;
    min-height: var(--bottom-nav-height-mobile); /* Referenzwert fuer .site-footers Bottom-Puffer, siehe dort */
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem 1.4rem;
    padding: 0.9rem 6vw calc(0.9rem + env(safe-area-inset-bottom));
    /* Blur wieder aktiv: das zeitweise Abschalten (deckender 94%-
       Fallback ohne backdrop-filter) war ein Verdachts-Fix waehrend
       der iPhone-Scroll-Haenger-Jagd -- echte Ursache war der
       dynamische Gedicht-Scroll-Lock (siehe .poem-section). */
    background-color: color-mix(in srgb, var(--bg-page) 88%, transparent);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-top: 1px solid var(--line);
    transform: translateZ(0);
    will-change: backdrop-filter;
  }
  .site-nav a {
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--accent); /* dauerhaft orange auf Mobile, nicht erst bei Hover/Touch -- untere Leiste als App-Tableiste erkennbar */
  }

  /* Kein eigenes padding-bottom hier: das war dieselbe Ursache, die auf
     Desktop schon einmal behoben wurde (siehe Kommentar bei der
     .content-stage-Basisregel) -- ein zusaetzlicher, unsichtbarer
     Abstand von 10rem NACH dem margin-bottom der letzten Section, der
     komplett ausserhalb von .site-footer lag und den Footer-Bereich
     unabhaengig von dessen eigenem Padding aufgeblaeht hat. */
  .section-inner { padding-left: 6vw; padding-right: 6vw; }
  .main-intro {
    padding: calc(var(--logo-bar-height-mobile) + 1.5rem) 0 2.5rem 0;
    /* War hart auf 7rem kodiert -- ein Leftover aus der Zeit vor dem
       --logo-bar-height-mobile-Fix (7rem -> 4rem), dadurch reservierte
       der Scroll-Sprung deutlich mehr Puffer, als die kleinere Kopf-
       leiste tatsaechlich braucht. */
    scroll-margin-top: calc(var(--logo-bar-height-mobile) + 1rem);
  }
  .page-intro {
    padding: calc(var(--logo-bar-height-mobile) + 1.5rem) 0 4rem 0;
    scroll-margin-top: calc(var(--logo-bar-height-mobile) + 1rem);
  }
  .snapshots-section {
    padding-top: calc(var(--logo-bar-height-mobile) + 1.5rem);
    scroll-margin-top: calc(var(--logo-bar-height-mobile) + 1rem);
  }

  /* Restliche Anker-Ziele, deren scroll-margin-top desktop-seitig an
     --logo-bar-height (10.5rem) gekoppelt ist bzw. hart auf 12rem
     kodiert war -- auf Mobile bisher ungekoppelt, dadurch reservierte
     der Scroll-Sprung auf dem iPhone deutlich mehr Puffer, als die
     kleinere Kopfleiste braucht (Section rutschte sichtbar zu weit
     unter den Header). .col-card deckt Beratung/Formatentwicklung/Ueber
     generisch ab (jede traegt die Klasse), einzelne IDs braucht es
     dafuer nicht mehr. */
  .col-card,
  #portfolio,
  #kontakt {
    scroll-margin-top: calc(var(--logo-bar-height-mobile) + 1rem);
  }

  .main-intro-inner {
    grid-template-columns: 1fr;
    gap: 1.25rem;
    align-items: center;
  }
  .intro-text {
    padding-top: 1rem; /* Raum vor dem Hero-Text, aber knapper als frueher (2.5rem wirkte zu weit weg von der Kopfleiste) */
    padding-bottom: 0;
  }
  /* Die feste Desktop-Aspect-Ratio (siehe dortige Regel) ist nur fuer die
     feste Grid-Spaltenbreite gedacht -- auf Mobile faellt die Spalte weg
     (main-intro-inner wird 1fr einspaltig), das Bild schrumpft eigen-
     staendig auf max-width:300px. Ohne diesen Reset zwingt aspect-ratio
     den Container trotzdem in ein quadratisches Format, waehrend das
     Bild selbst kleiner bleibt -- dazwischen blieb eine leere graue
     Flaeche stehen. */
  .intro-portrait {
    justify-content: center;
    align-self: auto;
    margin-top: 0;
    aspect-ratio: auto;
    padding: 0.5rem 0 2rem 0; /* wenig Abstand oben zum Text (natuerliche Lesehierarchie: Bild als dichte Fortsetzung), normaler Abstand nach unten */
    /* width:fit-content + margin-inline:auto statt margin-left/right:0
       auf voller Flex-Breite: sonst ist .intro-portrait (der
       Bezugsrahmen fuer die prozentual positionierten .portrait-
       scribble-Kritzeleien, siehe dort) auf Mobile deutlich breiter
       als das tatsaechliche Foto (das per max-width:300px +
       object-fit:contain kleiner bleibt und zentriert wird) -- die
       Prozentwerte liefen dadurch komplett am Foto vorbei. Damit deckt
       sich .intro-portrait wieder exakt mit der Fotoflaeche, wie auf
       Desktop. */
    width: fit-content;
    margin-left: auto;
    margin-right: auto;
  }
  /* Etwas kleiner als auf Desktop -- bei 300px statt ~440px Fotobreite
     wirkten Pfeile/Labels in Originalgroesse arg dominant. */
  .portrait-scribble {
    width: 5.5rem;
  }
  .scribble-label {
    font-size: 1.6rem;
  }
  .portrait-freistehend {
    height: auto; width: 100%; max-width: 300px; max-height: none; object-fit: contain;
    /* .intro-portrait picture (Desktop-Regel, width:100%) fuellt als Flex-Item
       bereits die volle Containerbreite aus -- justify-content:center auf
       .intro-portrait hat dadurch keinen Spielraum mehr. Ohne margin:auto
       haengt das <img> als Block-Element innerhalb von <picture> links statt
       zentriert. */
    margin: 0 auto;
  }

  .page-sections { gap: 2.5rem; margin-top: 4rem; padding-top: 4rem; }

  /* .portfolio-section: 7rem oben/unten war der Desktop-Wert ohne
     eigene mobile Reduzierung -- auf einem kleinen Screen unverhaeltnis-
     maessig viel Leerraum am Section-Anfang/-Ende. */
  .portfolio-section { padding: 3.5rem 0; }

  .split-section { grid-template-columns: 1fr; }

  /* Auf Mobile faellt die 1200px-Content-Kanten-Ausrichtung weg (keine
     Spalte nebeneinander mehr, die sich daran orientieren muesste) --
     beide Seiten bekommen denselben einfachen Innenabstand. Vertikales
     Padding ebenfalls reduziert (4rem war der unveraenderte Desktop-
     Wert). */
  .split-half:first-child,
  .split-half:last-child {
    padding-left: 6vw;
    padding-right: 6vw;
  }
  .split-half {
    padding-top: 2.5rem;
    padding-bottom: 2.5rem;
  }

  /* Die Eck-Rundung (Desktop: sichtbar zwischen den beiden Kacheln mit
     echtem Zwischenraum, jetzt nur noch an der Gap-Seite, siehe
     Basisregel) ergibt auf Mobile keinen Sinn mehr -- dort stehen die
     Kacheln randlos volle Breite untereinander, die Rundung wirkte dort
     nur wie zufaellig gekappte Ecken statt wie eine eigene Karte. Eckig
     statt gerundet, passend zu den uebrigen randlosen Mobile-Sections
     (.col-card-full/.contact-section/etc.). Eigene, gleich spezifische
     Selektoren noetig (nicht mehr nur .split-half): die Basisregel
     setzt border-radius jetzt auf .split-half:first-child/:last-child,
     eine einfache .split-half-Regel haette dagegen verloren. */
  .split-half:first-child,
  .split-half:last-child {
    border-radius: 0;
  }

  /* Auf Mobile stehen beide Kacheln volle Breite untereinander (siehe
     .split-section-Reset oben) -- dadurch stoesst jetzt jede Kachel mit
     BEIDEN Seiten an die Fensterkante, nicht mehr nur mit einer wie im
     Desktop-Zweispalter (siehe .col-card::after-Basisregel). Ohne
     diesen Reset bliebe die Desktop-Zuordnung (nur links bzw. nur
     rechts gekappt) stehen und die Kontur wuerde auf der jeweils
     falschen Seite trotzdem an der Fensterkante erscheinen. */
  .split-half:first-child::after,
  .split-half:last-child::after {
    border-left-width: 0;
    border-right-width: 0;
  }

  .col-card-full {
    padding-top: 2.5rem;
    padding-bottom: 2.5rem;
  }

  .profil-grid {
    grid-template-columns: 1fr;
    gap: 1.5rem;
  }

  .portfolio-preview {
    display: none;
  }

  .portfolio-flow {
    row-gap: 0;
    justify-content: flex-start; /* Desktop-Zentrierung zurueckgesetzt -- siehe Basisregel */
  }

  /* Auf Mobile steht jeder Titel in seiner eigenen Zeile -- die
     Schraegstrich-Trenner sind ein Ueberbleibsel des Desktop-Layouts
     (dort fliessen die Titel in einer Zeile hintereinander) und
     hingen hier funktionslos hinter jedem Titel. */
  .portfolio-sep {
    display: none;
  }

  .portfolio-panel {
    display: block;
    flex-basis: 100%;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.35s ease;
  }

  .portfolio-panel.is-open {
    max-height: 32rem;
  }

  .portfolio-panel-inner {
    padding: 1rem 0 1.5rem;
  }

  .portfolio-panel-media {
    width: 100%;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    cursor: pointer; /* oeffnet dieselbe Lightbox wie das "Mehr Infos"-Overlay, siehe JS: openMorePictures() */
  }

  .portfolio-panel-image {
    width: 100%;
    height: 100%;
    object-fit: cover;
  }

  /* Steht jetzt direkt hinter dem Titel in der Liste statt im Panel
     beim Bild -- nur sichtbar, wenn dieser Titel gerade aufgeklappt ist
     (.is-open, von JS gesetzt: togglePanel()). display:none an dieser
     Stelle (Media Query) reicht als Basiszustand, da .portfolio-flow
     ohnehin nur auf Mobile ueberhaupt sichtbar ist. */
  .portfolio-item-genre {
    display: none;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    font-weight: 400;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    /* War --muted-light -- jetzt --accent-2, analog zu den uebrigen
       Genre-/Kicker-Labels. */
    color: var(--accent-2);
  }

  .portfolio-item-genre.is-open {
    display: inline;
  }

  /* Sender/namhafte Produktionsfirma/HFF -- nur erzeugt, wenn ein
     Projekt ein meta-Feld hat (siehe renderPortfolioItems()), daher
     kein :empty-Fallback noetig wie bei .portfolio-media-meta. */
  .portfolio-panel-meta {
    margin-top: 0.75rem;
    font-family: 'Inter', sans-serif;
    font-size: 0.75rem;
    font-weight: 400;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    /* War --muted-light (neutrales Grau) -- jetzt --accent-2, analog
       zu .portfolio-item-genre auf demselben hellen Hintergrund. */
    color: var(--accent-2);
  }

  /* Letztes Element im Panel, unter der Kurzbeschreibung -- Sichtbarkeit
     haengt wieder direkt an der max-height-Transition des Panels, keine
     eigene .is-open-Steuerung mehr noetig (siehe JS: togglePanel()).
     Zentriert statt an den Panel-Raendern verteilt: die Pfeile zeigen
     jetzt hoch/runter (siehe .portfolio-panel-arrow-icon-Glyphen in
     renderPortfolioItems()), passend dazu gehören sie als ein Paar
     zusammen, statt als klassische Links/Rechts-Paging-Navigation. */
  .portfolio-panel-actions {
    display: flex;
    justify-content: center;
    gap: 1.5rem;
    margin-top: 1.25rem;
  }

  /* Reine Pfeil-Glyphen statt Textlabels ("↑ Zurück"/"Vor ↓") -- ohne
     die Text-Anker groesser skaliert (1.4rem statt vormals 0.85rem),
     damit sie trotz fehlendem Wortlaut gut sichtbar/greifbar bleiben,
     aber ohne die 900er-Grossschreibung der ganz fruehen Version, die
     schwerer wirkte als die Titelliste darueber. */
  .portfolio-panel-arrow-icon {
    appearance: none;
    background: none;
    border: none;
    color: var(--muted-light);
    font-family: 'Inter', sans-serif;
    font-size: 1.4rem;
    font-weight: 600;
    line-height: 1;
    padding: 0.4rem 0.5rem;
  }

  .portfolio-panel-arrow-icon:active {
    color: var(--accent);
  }

  /* Ersetzt den frueheren separaten "Mehr Bilder ansehen"-Button:
     blendet zeitverzoegert direkt auf dem Bild ein (siehe JS:
     revealPanelOverlay()), statt permanent als eigene Zeile darunter
     zu stehen. Oeffnet dieselbe Lightbox wie ein Tap aufs Bild selbst. */
  .portfolio-panel-overlay {
    position: absolute;
    left: 50%;
    bottom: 0.75rem;
    transform: translateX(-50%);
    appearance: none;
    background-color: rgba(13, 13, 12, 0.65);
    border: 1px solid rgba(255, 255, 255, 0.4);
    border-radius: 999px;
    padding: 0.5rem 1rem;
    color: #fff;
    font-family: 'Inter', sans-serif;
    font-size: 0.8rem;
    font-weight: 600;
    opacity: 0;
    transition: opacity 0.4s ease;
    pointer-events: none; /* erst aktiv, sobald sichtbar */
  }

  .portfolio-panel-overlay.is-visible {
    opacity: 1;
    pointer-events: auto;
  }

  /* War eine eigene Zeile im Dokumentfluss ganz am Ende (nach Bild/
     Filmstrip/Info) -- kostete auf niedrigen Viewports sichtbare Hoehe,
     die dem Textfeld gefehlt hat. Jetzt wie der Close-Button fest am
     Viewport verankert, oben ueber dem Bild -- .lightbox-nav-row nimmt
     dadurch keinen Platz mehr im Fluss ein, .lightbox-content braucht
     also auch keinen eigenen order-Wert mehr dafuer. JS
     (positionNavArrows()) ueberspringt Mobile ohnehin komplett, siehe
     dortigen Kommentar -- reine CSS-Positionierung reicht hier.
     left/right (statt nur left) + justify-content:center spannen die
     Zeile ueber dieselbe Breite wie .lightbox-media (0.75rem Rand, siehe
     .lightbox-frame-Inset) und zentrieren die beiden Pfeile darin, statt
     sie links anzukleben -- "zentriert ueber dem Bild", nicht neben dem
     Schliessen-Kreuz. Groesserer gap (2.5rem statt 0.5rem) sorgt fuer
     spuerbaren Abstand zwischen den Pfeilen selbst; zum Schliessen-Kreuz
     ergibt sich der Abstand automatisch daraus, dass die Zeile mittig
     sitzt statt in der rechten Ecke daneben zu kleben. */

  .lightbox-content {
    display: flex;
    flex-direction: column;
  }

  .lightbox-nav-row {
    position: fixed;
    top: 1.5rem;
    left: 0.75rem;
    right: 0.75rem;
    z-index: 2;
    display: flex;
    justify-content: center;
    gap: 2.5rem;
    padding: 0;
    margin: 0;
    /* Die Zeile spannt jetzt fast die volle Breite auf (fuer die
       Zentrierung, siehe oben), reicht dadurch bis ueber den Bereich
       des Schliessen-Kreuzes rechts. Ohne pointer-events:none faengt
       dieser leere Flex-Zwischenraum -- selbst ohne sichtbaren
       Hintergrund -- Klicks ab, die eigentlich dem darunterliegenden
       .lightbox-close galten (gleicher z-index, aber spaeter im DOM,
       gewinnt daher den Stapelkontext): das Kreuz liess sich dadurch
       nicht mehr schliessen. pointer-events:auto auf .lightbox-nav
       selbst (siehe dort) macht die beiden Pfeile trotzdem klickbar. */
    pointer-events: none;
  }

  /* Kein Rahmen/Hintergrund (anders als ein frueherer Versuch, siehe
     Git-Historie) -- wirkte neben dem randlosen Schliessen-Kreuz wie
     zwei verschiedene Bedienelement-Stile in derselben Zeile. Feste
     Groesse (2.75rem, identisch zu .lightbox-close) + padding:0 statt
     sich aus Pfeilgroesse+Padding+Rahmen zu ergeben -- nur so haben
     beide Buttons wirklich dieselbe Hoehe und sitzen auf derselben
     Mittelachse, nicht nur an derselben oberen Kante. Lesbarkeit ueber
     beliebigen Bildinhalten kommt jetzt vom text-shadow auf
     .lightbox-arrow (siehe dort) statt von einer Box drumherum.
     pointer-events:auto hebt das pointer-events:none der Elternzeile
     (siehe .lightbox-nav-row) fuer die Buttons selbst wieder auf. */
  .lightbox-nav {
    pointer-events: auto;
    position: static;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
  }

  .lightbox-media {
    order: 1;
    width: min(100%, 1100px, calc(48vh * 16 / 9));
  }

  /* Kein Overlay auf Mobile (siehe Basisregel/Kommentar) -- normaler
     Fluss, wieder wie vor dem Desktop-Overlay als erstes Kind von
     .lightbox-info (vor der Beschreibung), nach dem Filmstrip. JS
     (updateTitleRowParent()) haengt die Zeile bei diesem Breakpoint
     tatsaechlich dorthin um: als Kind von .lightbox-media (fix per
     aspect-ratio + overflow:hidden, siehe dort) wuerde reiner
     position:static-Fluss die Zeile schlicht abschneiden. */
  .lightbox-title-row {
    position: static;
    background: none;
    padding: 0.85rem 0 1rem;
  }

  /* order:2, zwischen Bild (1) und Info-Block (3) -- dieselbe DOM-
     Reihenfolge wie auf Desktop, hier per order nur fuer den
     abweichenden Nav-Row-Platz am Ende nochmal explizit gesetzt (siehe
     Kommentar oben zu .lightbox-content als Flex-Container). */
  .lightbox-filmstrip {
    order: 2;
    width: min(100%, 1100px, calc(48vh * 16 / 9));
    margin: 1rem auto 0;
  }

  .lightbox-info {
    order: 3;
    width: min(100%, 1100px, calc(48vh * 16 / 9));
    padding: 0.6rem 1.1rem 1.15rem;
    margin-top: 0.6rem;
  }

  /* Kleiner als Desktop (3.75rem) -- .lightbox-info ist auf Mobile selbst
     schon schmaler, ein gleich breites Poster liesse dem Textblock
     daneben zu wenig Platz. */
  .lightbox-poster {
    width: 3.5rem;
  }

  /* Kein Umfluss auf Mobile -- bei der dortigen Textbreite bliebe neben
     einem schwebenden Poster kaum noch Platz zum Lesen (siehe Basisregel/
     Kommentar bei .lightbox-poster-btn, dort nur fuer Desktop gedacht). */
  .lightbox-poster-btn {
    float: none;
    display: block;
    margin: 0 0 0.75rem;
  }

  /* Genre nach dem Titel, wie auf Desktop (dortige Regel: DOM-
     Reihenfolge Titel-dann-Genre, keine Umsortierung) -- frueher stand
     hier per row-reverse/order:-1 extra Genre-vor-Titel fuer Mobile,
     ein Leftover aus der Zeit vor dem Desktop-Redesign (siehe
     Git-Historie), das seither ein Mismatch zwischen den Breakpoints
     war. Keine eigene Regel mehr noetig -- Mobile erbt jetzt einfach
     die Desktop-Reihenfolge. */
  .lightbox-genre {
    font-weight: 400;
  }

  /* Unten deutlich weniger Abstand als oben (1.25rem statt 4rem) --
     die 4rem oben bleiben fuer Notch/Statusleiste noetig, unten gab es
     dafuer eigentlich keinen Grund: die fixe untere .site-nav-Leiste
     (siehe dort) liegt bei z-index:30 UNTER der Lightbox (z-index:100,
     komplett blickdichter Hintergrund) und ist bei offener Lightbox
     ohnehin voll verdeckt, kann also gar nicht ueberlappt werden. Der
     alte 4rem-Wert liess dadurch nur ungenutzten Platz am unteren
     Bildschirmrand stehen, den der Beschreibungstext (flex:1 1 auto,
     siehe .lightbox-desc-scroll-area) haette fuellen und so mehr Text
     ohne Scrollen zeigen koennen. env(safe-area-inset-bottom) haelt den
     Rahmen trotzdem oberhalb der Home-Indicator-Geste auf iPhones ohne
     Home-Button. */
  .lightbox-frame { inset: 4rem 0.75rem calc(1.25rem + env(safe-area-inset-bottom)) 0.75rem; }
  /* war 0.85rem -- reiht sich jetzt bei der uebrigen "sekundaerer
     Fliesstext"-Stufe ein (Nav-Links, Panel-Beschreibungen) statt
     zwischen den Stufen zu liegen. Kein eigenes max-height mehr auf
     .lightbox-desc-wrap -- die Basisregel (flex:1 1 auto) passt sich
     bereits dynamisch an, wie viel Platz nach Bild/Filmstrip/Titel/
     Nav-Zeile auf Mobile tatsaechlich noch uebrig ist. */
  .lightbox-desc { font-size: 0.95rem; }
  /* Kleinerer Fade-Bereich als Desktop -- bei wenig Boxhoehe frisst
     1.4em (Desktop-Wert) proportional deutlich mehr vom sichtbaren
     Text weg. */
  .lightbox-desc-wrap.is-scrollable { mask-image: linear-gradient(to bottom, #000 calc(100% - 1em), rgba(0, 0, 0, 0.45) 100%); }
  /* Etwas groesser/praesenter als Desktop -- auf Mobile ist der Hinweis
     besonders gewuenscht. display:block hierher verschoben (siehe
     Kommentar bei der Basisregel) -- auf Desktop bleibt der Pfeil
     komplett weg, auf Mobile weiterhin sichtbar sobald scrollbar. */
  .lightbox-desc-wrap.is-scrollable ~ .lightbox-scroll-hint { display: block; }
  .lightbox-scroll-hint { font-size: 1.3rem; }
  .lightbox-credits { font-size: 0.85rem; }
  .filmstrip-thumb { width: 4.5rem; }
  /* Bei 4.5rem Thumb-Breite (16:9 -> ~2.5rem hoch) ist fuer Icon+Text
     untereinander weniger Platz als auf Desktop -- etwas kompakter. */
  .filmstrip-trailer-icon { width: 1.1rem; height: 1.1rem; font-size: 0.5rem; }
  .filmstrip-trailer-label { font-size: 0.5rem; }
  .lightbox-close { width: 2.75rem; height: 2.75rem; font-size: 1.6rem; }
  /* text-shadow statt Box-Hintergrund (siehe .lightbox-nav-Kommentar) --
     haelt den Pfeil auch auf hellen Bildstellen lesbar, ohne einen
     sichtbaren Rahmen um ihn herum zu ziehen. */
  .lightbox-arrow {
    width: 2.25rem;
    height: 2.25rem;
    font-size: 1.6rem;
    text-shadow: 0 1px 6px rgba(0, 0, 0, 0.85), 0 1px 3px rgba(0, 0, 0, 0.9);
  }

  /* Ausnahme: .snapshot-index-card braucht auf Mobile trotzdem mehr
     Zeilen als die Basisregel (span 11) vorgibt -- anders als bei Fotos
     schrumpft echter Fliesstext nicht proportional mit der kleineren
     Zeilenhoehe mit (Buchstaben bleiben lesbar gross), bei unveraenderter
     Spannenzahl reichte die Kartenhoehe auf schmalen Screens nicht mehr
     fuer den (unveraendert langen) Zusammenfassungstext -- gemessen:
     Text lief unten aus der Karte heraus. (Zettel selbst aktuell nicht
     mehr im HTML verwendet, Regel bleibt fuer eine eventuelle
     Rueckkehr stehen.) */
  .snapshot-index-card {
    grid-row: span 16;
  }

  /* Weniger Seitenpadding, kleinere Pfeile -- auf Telefonbreite braucht
     die Grossansicht den Platz fuers Bild selbst, nicht fuer breite
     Randabstaende zu den Pfeilen. */
  .snapshot-zoom {
    padding: 4.5rem 3rem 2rem;
  }

  .snapshot-zoom-nav {
    width: 2.25rem;
    height: 2.25rem;
    font-size: 2rem;
  }

  .snapshot-zoom-prev { left: 0.15rem; }
  .snapshot-zoom-next { right: 0.15rem; }

  /* Die eigentliche Ursache fuer den weitlaeufig wirkenden Footer war
     .content-stage's padding-bottom (siehe oben) -- das hier ist eine
     eigenstaendige Ergaenzung, kein zusaetzlicher Kuerzungs-Hebel.
     Zusaetzlich zur Home-Indicator-Sicherheitszone (env(safe-area-
     inset-bottom)) braucht der Footer auf Mobile Puffer in Hoehe der
     fixierten unteren Nav-Leiste selbst (--bottom-nav-height-mobile,
     siehe :root oben) -- sonst landet der komplette Footer-Inhalt am
     Seitenende genau in dem Bereich, den die Leiste ueberdeckt und
     ist dort nie erreichbar, egal wie weit man scrollt. */
  .site-footer {
    padding: 1rem 0 calc(var(--bottom-nav-height-mobile) + env(safe-area-inset-bottom) + 0.5rem);
  }
  .site-footer-inner {
    gap: 1rem;
  }
}

/* Eigene, ENGERE Schwelle statt der allgemeinen 768px-Mobile-Grenze
   oben (siehe Chat: bei 768px -- Tablet-Breite, noch "horizontal" --
   erschien faelschlich schon der Hochformat-Zuschnitt der Umriss-Kachel,
   der eigentlich nur echten Telefonbreiten vorbehalten sein soll). 480px
   deckt gaengige Handy-Breiten (~375-430px) sicher ab, laesst 768px-
   Tablets aber beim Querformat wie Desktop/Laptop. Muss synchron zum
   media-Attribut der <source>-Elemente in profil.html bleiben (siehe
   dortiger Kommentar).
   ALLE Wand-bezogenen Groessen-Regeln (Spaltenbreite, Post-it-Spanne/
   -Groesse, Umriss-Format) sitzen bewusst gemeinsam HIER statt verteilt
   auf diese und die 768px-Schwelle oben -- vorher lag die Post-it-Zeile
   (grid-column:span 3) noch bei 768px, waehrend die Umriss-Kachel schon
   bei dieser engeren Schwelle blieb: zwischen 481-768px hatten Post-its
   und Umriss dadurch UNTERSCHIEDLICHE Spannen (3 vs. 2) und liessen sich
   nicht auf denselben Mittelpunkt zentrieren (siehe Chat: "Post-Its
   zentriert, Umriss darunter nicht"). Eine einzige gemeinsame Schwelle
   fuer alle drei verhindert diese Kombination strukturell. */
@media (max-width: 480px) {
  /* Kleinere feste Spaltenbreite auf Mobile, sonst passen auf
     Telefonbreite oft nur zwei Spalten mit viel Leerraum dazwischen.
     grid-auto-rows/gap sind NICHT einfach uebernommen, sondern auf die
     schmalere Spalte (100px statt 160px, Faktor ~0.625) herunter-
     skaliert: die grid-row-Spannen selbst kommen als Inline-Style aus
     dem HTML und bleiben dieselben Zahlen wie auf Desktop, nur die
     Zeilenhoehe je Einheit ist hier kleiner, damit "Spanne x Einheit"
     wieder zur tatsaechlich benoetigten Bildhoehe bei 100px passt. */
  .snapshot-wall {
    grid-template-columns: repeat(auto-fill, 100px);
    grid-auto-rows: 5px;
    gap: 0.5rem;
    /* War 2rem oben -- zusammen mit dem Tape-Abstand viel Leerraum bis
       zu den Post-its auf Mobile (siehe Chat). */
    padding: 1.1rem 0 1rem;
  }

  .snapshot-card {
    padding: 0.3rem 0.3rem 1rem;
  }

  /* 3 x 70px + 2 x 3px Gap = 216px passt nicht mehr in 2 Mobile-Spalten
     (100px/8px-Gap = 208px) wie auf Desktop -- braucht hier 3 Spalten
     (100*3+8*2=316px), mit etwas Luft rechts/links dank
     justify-content:center auf der Basisregel. */
  .snapshot-postit-row {
    gap: 3px;
    grid-column: span 3;
  }

  /* Gleiches quadratisches Format wie Desktop, nur kleiner (84px statt
     108px) -- passend zur insgesamt schmaleren Wand (100px-Spalten statt
     160px). War 70px -- auf dem iPhone kaum lesbar, daher etwas
     grosszuegiger skaliert (Faktor 1.2 auf Breite/Schrift/Padding), ohne
     an Anordnung oder Zentrierung etwas zu aendern -- passt weiterhin
     locker in die 3-Spalten-Breite (3 x 84px + 2 x 3px Gap = 258px von
     316px verfuegbar). */
  .snapshot-postit {
    width: 84px;
    /* Etwas kleiner als der erste Versuch (0.85rem * 1.2) -- der Caveat-
       Schreibschrift-Font hat sichtbaren Figuren-Ueberhang (Schwuenge/
       Serifen ragen ueber die reine Textmetrik-Box hinaus, siehe Chat),
       der bei einer bis auf ~1px an die Boxbreite ausgereizten Zeile
       ("Alles außer") sichtbar ueber den Zettelrand ragte, obwohl die
       Text-Layout-Box selbst noch reingepasst haette. */
    font-size: 0.9rem;
    padding: 0.6rem;
    grid-row: span 6;
  }

  /* Die Fussball-Illustration sitzt ZUSAETZLICH zu mehrzeiligem Text im
     selben, jetzt viel kleineren 70px-Zettel -- der normale %-Wert
     (siehe .postit-doodle oben) reicht dafuer allein nicht aus, der
     Zettel wuchs auf Mobile ueber sein Quadrat hinaus. Deutlich
     kleinerer Anteil nur hier, wo Text UND Bild um denselben knappen
     Platz konkurrieren. */
  .postit-doodle {
    width: 42%;
    margin-top: 0.15em;
  }

  /* Die vierzeiligen "Werdegang"- und "Themen"-Listen brauchen auch auf
     Mobile eine eigens verkleinerte Schrift (siehe Desktop-Regel) --
     ohne diesen Mobile-eigenen Wert haette die Desktop-Regel (0.95rem,
     ausserhalb dieser Media Query, aber per Attribut-Selektor
     spezifischer als die obige .snapshot-postit-Basisregel) hier sonst
     die kleinere Mobile-Basisgroesse (0.75rem) wieder ueberschrieben und
     den Text GROESSER statt kleiner gemacht. Gleicher
     Verkleinerungsfaktor wie Desktop (1.15rem -> 0.95rem) auf die
     Mobile-Basis angewendet. */
  .snapshot-postit[data-title="Werdegang"] .snapshot-postit-text,
  .snapshot-postit[data-title="Themen"] .snapshot-postit-text {
    font-size: 0.74rem;
  }

  .snapshot-postit--tall { grid-row: span 6; }

  /* Die Nadelgroessen sind fest in rem, skalieren also nicht mit der
     viel schmaleren Kartenbreite auf Mobile -- wirkten dort ueberdimensioniert.
     Kleiner statt Desktop-Groesse. */
  .wall-thread-pin {
    width: 0.42rem;
    height: 0.42rem;
  }

  .snapshot-outline-pin {
    width: 0.5rem;
    height: 0.5rem;
  }

  .snapshot-caption {
    font-size: 0.55rem;
  }

  /* Hochformat-Zuschnitt statt Querformat, dadurch 1 statt 2 Spalten
     breit -- bei nur 3 Mobile-Spalten insgesamt bleiben so 2 statt nur 1
     Seitenspalte fuer die Zielfotos rundherum uebrig. 848x1264 bei 100px
     Spaltenbreite ergibt ~149px Bildhoehe zzgl. 6px Rahmen (siehe
     .snapshot-card.snapshot-outline-card, padding:0 + border statt
     Polaroid-Innenabstand) -- Spanne 12 passt (12*5 + 11*8 = 148px).
     War kurzzeitig Spanne 15 (bewusst groesser, "einen Ticken groesser,
     um es von den Polaroids besser abzusetzen") -- seit object-fit:
     contain durch einen Bugfix (siehe Chat: liegengebliebenes
     height:auto aus einer viel frueheren Kartenversion) jetzt
     tatsaechlich zentriert statt nur oben buendig, wirkte der
     ueberstehende Rand oben UND unten sichtbar wie unnoetiges Padding
     statt wie eine breitere Montur -- zurueck auf die exakt passende
     Spanne 12. */
  .snapshot-outline-card {
    grid-column: span 1;
    grid-row: span 12;
  }
}
