/* ─────────────────────────────────────────────
   index.html — Styles der Werke-Seite
   (bis 06.08.2026 main.html; die Landing-Ebene darüber steht in landing.css)
   Abschnitte:
     1. Header
     2. Layout & Sidebar
     3. Bildbereich / Serien
     4. Lightbox
     5. Kontakt-Panel
     6. Hell-Modus
     7. Video-Einbettung
   ───────────────────────────────────────────── */


/* ── 1. Header ── */

/* AUSGERICHTET WIRD AN DER GRUNDLINIE, NICHT AN DER KASTENUNTERKANTE
   (07.08.2026). Vorher stand hier `align-items: flex-end`. Das legt die
   UNTERKANTEN der Textkästen auf eine Höhe — und die liegt bei jeder
   Schriftgröße unterschiedlich weit unter der Grundlinie, weil dort die
   Unterlängen Platz haben (bei UbuntuMono 0.17 der Schriftgröße). Der Name
   (1.5rem) und „Contact / CV" (0.7rem) standen dadurch um rund 2px versetzt.
   Ausgeglichen wurde das mit einem festen `top: -6px` am Kontakt-Link — was
   nur schlimmer wurde, sobald die Handy-Regeln unten BEIDE Größen ändern: dort
   waren es zuletzt gut 5px in die andere Richtung, dazu ein um 6px zu hoch
   sitzender Burger.

   `baseline` macht den Ausgleich von selbst und bleibt bei jeder Schriftgröße
   richtig. Damit die Zeile dabei nicht nach oben rutscht — Grundlinien-Kinder
   hängen am oberen Rand des Inhaltsbereichs, nicht am unteren —, wird der
   Inhaltsbereich per padding-top auf exakt eine Zeilenhöhe eingeschnürt: dann
   ist „oben bündig" dasselbe wie vorher „unten bündig", der Name steht also
   unverändert da, wo er stand.

   Dazu gehören zwingend:
   • `font-size` steht jetzt HIER, nicht mehr an .site-name — die 1em in der
     padding-Rechnung müssen die Größe des Namens meinen.
   • `line-height: 1` macht die Zeilenhöhe berechenbar — ohne das hängt sie an
     den Maßen der Schriftdatei, und die Rechnung oben ginge nicht auf.
     Nebenwirkung, gemessen: der Name rutscht dadurch um gut einen Pixel nach
     unten (bei 0.8rem 0.6px, bei 1.5rem 1.1px), weil UbuntuMonos „normal"
     etwas mehr als 1em ist. Das ist der Preis dafür, dass die Zeile nicht mehr
     von der Schriftdatei abhängt; .landing-name macht denselben Schritt mit,
     die beiden Namen liegen beim Übergang also weiter aufeinander.
   • Die 1px sind die Trennlinie unten, die bei border-box zur Höhe zählt. */
.site-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  height: 64px;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  padding: calc(64px - 1px - 0.8rem - 1em) 2rem 0.8rem 2rem;
  font-size: 1.5rem;
  line-height: 1;
  z-index: 200;
  border-bottom: 1px solid #111;
  background: black;
  transition: transform 0.35s ease, background-color 0.6s ease, border-color 0.6s ease;
}

.site-header.versteckt {
  transform: translateY(-100%);
}

/* Das <h1> um den Namen ist reine Auszeichnung und soll nichts aussehen —
   `font: inherit` nimmt ihm die Browser-Voreinstellung (2em, fett) ab, damit
   die 1.5rem des Headers gelten und .site-name darunter wie zuvor greift.
   Es ist jetzt das Flex-Kind an Stelle des Links; als Block mit einer Textzeile
   bringt es dieselbe Grundlinie mit, die Ausrichtung oben bleibt also heil. */
.site-titel {
  font: inherit;
}

.site-name {
  font-family: var(--font);
  font-weight: 300;
  /* Die Größe kommt vom Header (siehe dort) — sie ist dessen Bezugsmaß. */
  font-size: inherit;
  letter-spacing: 0.45em;
  color: white;
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
}

.contact-link {
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.7rem;
  letter-spacing: 0.35em;
  text-transform: uppercase;
  color: white;
  text-decoration: none;
  opacity: 0.35;
  transition: opacity 0.2s ease;
  /* Hier stand `position: relative; top: -6px` — der Handausgleich für die
     frühere Kastenausrichtung. Die Grundlinien-Ausrichtung im Header macht das
     jetzt rechnerisch; ein fester Versatz würde sie wieder verstellen. */
}

.contact-link:hover { opacity: 1; }


/* ── 2. Layout & Sidebar ── */

.layout {
  display: flex;
  height: 100vh;
  margin-top: 0;
  overflow: hidden;
  /* Bezugsrahmen für die eingeklappte Kategorien-Leiste und für den aus dem
     Fluss genommenen Bildbereich (beides in Abschnitt 8). */
  position: relative;
}

/* Sidebar */
.sidebar {
  /* Breite steht in base.css (--sidebar-breite). Sie war lange 190px — dabei
     brachen „Ultrasonic Theremin" und „Photography of an unknown object" auf
     drei gequetschte Zeilen um. */
  width: var(--sidebar-breite);
  min-width: var(--sidebar-breite);
  padding: 28vh 1.2rem 3rem 1.5rem;
  height: 100%;
  overflow-y: auto;
  border-right: 1px solid #1a1a1a;
  flex-shrink: 0;
}

/* Block um die Stationsliste. Trug bis 03.08.2026 die Überschrift „Works"
   darüber; die ist weg, der Block bleibt als Klammer und für den Abstand nach
   unten. Die Regeln der Überschrift liegen unter
   ../Archiv/with-Inter/stil-kategorie-titel.css. */
.kategorie-item {
  margin-bottom: 1.5rem;
}

/* 1. Ebene */
.unterkategorien {
  display: none;
  flex-direction: column;
  /* Kein border-left mehr — die „Fahrbahn" zeichnet jetzt jede Station selbst
     als Liniensegment (siehe .subkat-item::after). */
}

.unterkategorien.offen { display: flex; }

/* Subkategorie (2. Ebene) = Abzweig von einer durchgehenden Fahrbahn, wie in
   einer Navi-App: die Linie läuft ohne Unterbrechung senkrecht durch, und pro
   Station geht ein kurzer Stich nach rechts ab, der in einem Kopf endet —
   eine Abbiegemöglichkeit. Die Fahrbahn ist VOM ANFANG BIS ZUR AKTUELLEN
   Station farbig (var(--fahrbahn)), darunter dunkel.

   Drei Bauteile, drei Pseudo-Elemente:
     .subkat-item::after   → senkrechtes Fahrbahn-Stück (stoßen aneinander)
     .subkat-item::before  → waagerechter Stich nach rechts
     .subkat-titel::before → Kopf am Ende des Stichs (Rahmen + Punkt in der Mitte)
   Maße hängen zusammen: Fahrbahn liegt bei x=4..6, Stich 5..14, Kopf 14..26,
   deshalb padding-left 2.4rem (38px) für den Text — der Abstand zwischen Kopf
   und Beschriftung ist Absicht, vorher klebte beides aneinander. */
.subkat-item {
  position: relative;
  margin-bottom: 0.7rem;
  padding-left: 2.4rem;

  /* FLACHER LOOK (Stand 27.07.2026). Der zwischenzeitliche Leiterplatten-/
     Stahl-Versuch mit Querverläufen und Lichtkanten ist auf Noahs Wunsch
     wieder raus („entferne diesen metallenen look") — Linien und Köpfe sind
     wieder einfarbig, nur der Punkt im Kopf ist jetzt deutlich größer.

     Alle vier Leitungs-Werte sind trotzdem als `linear-gradient` einer einzigen
     Farbe geschrieben, NICHT als reine Farbe: die aktive Station legt zwei
     Ebenen per `background-image` übereinander (siehe unten), und dort sind
     nur Bilder erlaubt — eine nackte Farbe wäre ungültig und die Fahrbahn
     verschwände. */
  --leitung-v: linear-gradient(#333, #333);
  --leitung-h: linear-gradient(#333, #333);
  /* Erreichte Strecke im Ton von --fahrbahn */
  --leitung-v-aktiv: linear-gradient(var(--fahrbahn), var(--fahrbahn));
  --leitung-h-aktiv: linear-gradient(var(--fahrbahn), var(--fahrbahn));
  --kopf-fuellung: #161412;
  --kopf-rand: #55504a;
  --kopf-punkt: #8d867d;
  --kopf-glanz: none;
}

/* Senkrechtes Fahrbahn-Stück. Es ragt oben und unten 6px über das Element
   hinaus (Abstand zum Nachbarn ist 8,8px), damit die Stücke überlappen und
   die Linie durchgehend ist. */
.subkat-item::after {
  content: '';
  position: absolute;
  left: 4px;
  top: -6px;
  bottom: -6px;
  width: 2px;
  background: var(--leitung-v);
  transition: background 0.2s ease;
  /* Reine Deko — sie gehört zum .subkat-item und würde als oberstes Element
     sonst Klicks abfangen, die der Station darunter gelten (in der schmalen
     Leiste liegen Linie und Stich genau dort, wo man hinzielt). */
  pointer-events: none;
}

/* Der Stich nach rechts (Abzweig). Liegt auf Höhe der ersten Textzeile. */
.subkat-item::before {
  content: '';
  position: absolute;
  left: 6px;
  top: 12px;
  width: 9px;
  height: 2px;
  background: var(--leitung-h);
  z-index: 1;
  transition: background 0.2s ease;
  pointer-events: none;   /* Deko, siehe ::after oben */
}

/* Kopf am Ende des Stichs: ECKIGE Station mit einem RUNDEN Punkt in der Mitte
   (28.07.2026 auf Noahs Wunsch — die Fassung davor war Kreis mit Raute).
   Dafür braucht es zwei Pseudo-Elemente des Titels: ::before ist das Quadrat,
   ::after der Kreis. Sie liegen konzentrisch — das Quadrat nimmt 14..28 × 7..21
   ein (samt 1px Rand), seine Mitte ist also (21|14), und der Punkt wird darauf
   zentriert. Wer eine der beiden Größen ändert, muss die andere nachrechnen. */
.subkat-titel::before {
  content: '';
  position: absolute;
  left: 14px;
  top: 7px;
  width: 12px;
  height: 12px;
  background: var(--kopf-fuellung);
  border: 1px solid var(--kopf-rand);
  box-shadow: var(--kopf-glanz);
  z-index: 1;
  transition: border-color 0.2s ease, background-color 0.2s ease;
}

/* Der runde Punkt in der Mitte. 6.5px Durchmesser füllen das 12px-Quadrat
   deutlich aus, ohne seinen Rand zu berühren. Mitte = (21|14), also
   left/top = 21/14 − 3.25. */
.subkat-titel::after {
  content: '';
  position: absolute;
  left: 17.75px;
  top: 10.75px;
  width: 6.5px;
  height: 6.5px;
  border-radius: 50%;
  background: var(--kopf-punkt);
  z-index: 2;
  pointer-events: none;
  transition: background-color 0.2s ease;
}

/* Bereits passierte Stationen: ihre ganze Leitung ist die polierte Fassung.
   `:has(~ …)` = „hat weiter unten eine aktive Station" — trifft also genau
   alles OBERHALB der aktuellen. */
.subkat-item:has(~ .subkat-item .subkat-titel.aktiv)::after {
  background: var(--leitung-v-aktiv);
}

/* Aktuelle Station: die Fahrbahn ist nur BIS zu ihrem Abzweig poliert und
   darunter wieder matt. 19px = Abstand von der Oberkante des Stücks (-6px)
   bis zur Mitte des Stichs (13px).
   ZWEI Ebenen statt eines Verlaufs: der Verlauf QUER (Rundung der Stange) und
   der Wechsel ENTLANG der Stange lassen sich nicht in einem unterbringen —
   die obere Ebene wird deshalb einfach nur 19px hoch eingeblendet. */
.subkat-item:has(.subkat-titel.aktiv)::after {
  background-image: var(--leitung-v-aktiv), var(--leitung-v);
  background-size: 100% 19px, 100% 100%;
  background-position: 0 0, 0 0;
  background-repeat: no-repeat, no-repeat;
}

/* Aktuelle Station: Abzweig und Lötauge poliert. */
.subkat-item:has(.subkat-titel.aktiv)::before {
  background: var(--leitung-h-aktiv);
}
/* Aktuelle Station auf dunklem Grund: das Quadrat ist hell gefüllt, der Punkt
   darin wird zum dunklen „Loch" — so bleibt er auf dem gefüllten Kopf sichtbar. */
.subkat-item:has(.subkat-titel.aktiv) {
  --kopf-fuellung: var(--fahrbahn);
  --kopf-rand: var(--fahrbahn);
  --kopf-punkt: rgba(0, 0, 0, 0.72);
}

/* Themenwechsel (vor „Photography of an unknown object" und vor „Mesh
   Deform"): nur mehr Luft zwischen den Stationen — die Fahrbahn selbst
   läuft durch (Noahs Wunsch: durchgezogener Strich). Die Stellen sind
   dieselben wie die `absatz`-Absätze im Bildbereich. */
.subkat-item.mit-trenner {
  margin-top: 2.4rem;
}
.subkat-item.mit-trenner::after {
  /* Überbrückt den größeren Abstand nach oben. ACHTUNG: .unterkategorien ist
     ein Flex-Container, dort kollabieren Ränder NICHT — die Lücke ist also
     0.7rem + 2.4rem = 49,6px. Wer margin-top oben ändert, MUSS hier
     nachziehen, sonst reisst die Fahrbahn auf. */
  top: -3.3rem;
}

/* Auf hellem Grund (oberer, weißer Teil der Seite) müssen Fahrbahn und Köpfe
   heller sitzen, damit sie sichtbar bleiben. */
body.main-hell .subkat-item {
  /* Auf Weiß muss die Linie dunkler sein als der Grund, sonst verschwindet
     sie. Gleicher Aufbau, nur die Helligkeiten sind umgedreht — und weil es
     seit dem 28.07.2026 keine Akzentfarbe mehr gibt, sondern nur Schwarz-Grau-
     Weiß, ist „erreicht/aktiv" hier fast SCHWARZ statt fast weiß. Dazu setzt
     dieser Block einfach --fahrbahn für seinen Seitenteil um; alles andere
     leitet sich wie gehabt daraus ab. */
  --fahrbahn: var(--fahrbahn-auf-hell);
  --leitung-v: linear-gradient(#c2bfba, #c2bfba);
  --leitung-h: linear-gradient(#c2bfba, #c2bfba);
  /* Noah 27.07.2026: auf Weiß gefiel ihm die UMKEHRUNG des dunklen Teils
     besser. Also hier: leeres Quadrat mit schwarzem Rahmen (der Punkt ist so
     weiß wie die Füllung und damit unsichtbar) — die Station ist erst dann
     „markiert", wenn sie aktiv ist. */
  --kopf-fuellung: #ffffff;
  --kopf-rand: #14120f;
  --kopf-punkt: #ffffff;
  --kopf-glanz: none;
}
/* Aktive Station auf Weiß: schwarz gefüllt, Punkt darin WEISS. */
body.main-hell .subkat-item:has(.subkat-titel.aktiv) {
  --kopf-fuellung: var(--fahrbahn);
  --kopf-rand: #14120f;
  --kopf-punkt: #ffffff;
}

.subkat-titel {
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.74rem;
  /* 0.2em waren bei den langen Namen zu viel — mit der breiteren Spalte und
     etwas Zeilenhöhe brechen sie jetzt höchstens zweizeilig statt dreizeilig. */
  letter-spacing: 0.14em;
  line-height: 1.5;
  color: #777;
  cursor: pointer;
  transition: color 0.2s ease;
  user-select: none;
  padding: 0.25rem 0;
}

.subkat-titel:hover,
.subkat-titel.aktiv { color: #ddd; }

/* Römische Zahlen (3. Ebene) */
.subsubkat {
  display: none;
  flex-direction: column;
  gap: 0.3rem;
  /* Rueckt unter der Mitte der Kategorie ein statt an ihrer linken Kante zu
     kleben. ACHTUNG beim Nachjustieren: die Textspalte ist 133px breit
     (215px Leiste − 1.5/1.2rem Innenrand − 2.4rem .subkat-item), davon geht
     dieser Einzug ab. Bei UbuntuMono kostet ein Zeichen 0.5em + letter-
     spacing, „Point Cloud" sind 11 Zeichen — mehr als hier passt nicht, sonst
     bricht der laengste Link um. */
  /* 1.6rem waren es, solange die Unterpunkte „I" und „II" hiessen. Mit
     „Material II (still)" braucht die Textspalte jeden Millimeter: „Material II"
     ist 101px breit, bei 1.6rem Einzug blieben nur 94px und der Name haette
     ueberstanden. 1.1rem lassen 116px und halten den Einzug trotzdem sichtbar. */
  padding-left: 1.1rem;
  margin-top: 0.15rem;
  border-left: 1px solid #2e2e2e;
}

.subsubkat.offen { display: flex; }

.unter-link {
  font-family: var(--font);
  font-weight: 400;
  /* Fast so gross wie die Kategorie darueber (0.74rem) — die roemischen
     Zahlen waren als Winzlinge kaum zu treffen. Sperrung von 0.2em auf
     0.14em zurueck, sonst passt „Point Cloud" nicht mehr in eine Zeile. */
  font-size: 0.9rem;
  letter-spacing: 0.14em;
  color: #666;
  text-decoration: none;
  transition: color 0.2s ease;
  padding: 0.2rem 0;
}

.unter-link:hover,
.unter-link.aktiv { color: white; }


/* ── 3. Bildbereich / Serien ── */

.bildbereich {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 5.5rem 3rem 6rem 3rem;
  /* KEIN padding-top im Übergang mehr — siehe .bildbereich.header-versteckt */
  transition: opacity 0.26s ease;
  opacity: 1;

  /* BEZUGSBREITE ALLER BILDMASSE.
     Normal einfach 100 % — dann heißt calc(0.62 * var(--breite)) schlicht 62 %
     wie früher. Ist das 3D-Panel offen, bleibt sie auf der Breite stehen, die
     der Bildbereich OHNE Panel hätte. Dadurch behalten die Bilder ihre Größe
     und rücken nur nach links in den Platz, den die ausgeblendeten Kategorien
     frei machen — statt mitzuschrumpfen (Noah: „das Bild wird nur kleiner
     skaliert statt in selber Größe verdrängt").
     Alle Maße stehen zusätzlich in min(100%, …): das Panel nimmt mehr Breite
     weg (28vw) als die Kategorien freigeben (--sidebar-breite), für die
     breitesten Blöcke (86 %, Vierer-Reihen) reicht es deshalb nicht ganz. Die
     weichen dann auf den verfügbaren Platz aus, statt hinter dem Panel zu
     verschwinden. */
  --breite: 100%;
}

/* --breite-zu misst main-3d.js: die tatsächliche Innenbreite des
   Bildbereichs bei geschlossenem Panel. Gerechnet aus 100vw käme man daneben,
   weil Scrollbalken und Rahmen mit hineinspielen — der Fallback ist nur da,
   falls das Skript nicht läuft. */
body.dreid-offen .bildbereich {
  --breite: var(--breite-zu, calc(100vw - var(--sidebar-breite) - 6rem));
}

.bildbereich.fade-aus {
  opacity: 0;
}

/* ENTSCHÄRFT 27.07.2026 (Noah: „das Scrollen auf der Mainpage ist auch etwas
   ruckelig"). Hier stand `padding-top: 1.5rem` — beim Ausblenden des Headers
   rückte also der gesamte, sehr lange Inhalt um 64px nach oben, und zwar als
   0,35s-Übergang. Padding zu animieren heißt: der Browser MUSS in jedem Bild
   dieser Animation die ganze Seite neu umbrechen (rund 50 Bilder, alle
   Sektionen) — und das jedes Mal, wenn man die Scrollrichtung wechselt.
   Genau das war das Ruckeln.

   Jetzt bleibt der Innenabstand fest; der Header fährt weiterhin weg, nur der
   Inhalt springt nicht mehr nach. Nebenwirkung, die uns entgegenkommt: die im
   3D-Panel gemessene Abweichung von konstant ±64px zwischen beiden Spuren
   fällt damit ebenfalls weg.
   (Regel bewusst stehen gelassen, damit klar ist, dass sie leer ist.) */
.bildbereich.header-versteckt {
  /* absichtlich leer — siehe Kommentar oben */
}

.portfolio-seite { display: none; }
.portfolio-seite.aktiv { display: block; }

/* Der Abstand NACH einem Werk. Er trennt Werk von Werk — die Bilder INNERHALB
   eines Werks stehen enger (gap der .bilder / .bild-grid) und bleiben davon
   unberührt. Am 10.09.2026 von 5+4 auf 6+8 rem erhöht (144 -> 224px): darunter
   standen Jahreszahl und Werkangabe des einen Werks fast auf der Kopfzeile des
   nächsten (Noah: „damit infotexte nicht so gequetscht sind"). */
.serie {
  margin-bottom: 6rem;
  padding-top: 8rem;
}

.serie:first-child {
  padding-top: 0;
}

/* Größere Absätze zwischen Werkgruppen — die Luft trennt Kapitel im
   Bildbereich, so wie der Strich in der Sidebar es andeutet.
   `absatz` = neues Thema (vor Photography of an unknown object),
   `absatz-gross` = eigener Werkkomplex (vor Mesh Deform). */
/* In vh, damit die Pause auf jedem Schirm als „hier ist Schluss" liest und
   nicht auf grossen Monitoren zur Randnotiz schrumpft. clamp() haelt sie
   auf flachen Fenstern brauchbar und auf sehr hohen im Rahmen. */
.serie.absatz { padding-top: clamp(24rem, 70vh, 44rem); }
.serie.absatz-gross { padding-top: clamp(30rem, 95vh, 58rem); }

.serie-header {
  display: flex;
  align-items: baseline;
  gap: 1.2rem;
  margin-bottom: 2rem;
  padding-bottom: 0.6rem;
  border-bottom: 1px solid #1a1a1a;
}

.serie-pfad {
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.65rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #444;
}

.serie-zahl {
  font-family: var(--font);
  font-weight: 500;
  font-size: 0.85rem;
  letter-spacing: 0.15em;
  color: #777;
}

.bilder {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.bild-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1.2rem;
  /* Bei offenem 3D-Panel so breit bleiben wie ohne (siehe --breite) */
  width: min(100%, var(--breite));
  margin: 0 auto;
}

/* Vier Bilder in EINER Reihe (Point Cloud, Mesh Deform I + II).
   Sie werden dabei kaum kleiner: im 2×2-Raster nahm ein Bild 62 % einer
   halben Zeile ein (≈31 % der Breite), hier füllt es seine Viertel-Spalte
   ganz aus (≈24 %). Deshalb hier width:100% statt der sonstigen 62 %. */
.bild-grid.reihe {
  grid-template-columns: repeat(4, 1fr);
  gap: 0.8rem;
  align-items: center;
}

.bild-grid.reihe .bild-item img { width: 100%; }

/* Raster, das links und rechts bündig mit den Querformaten der Serie steht.
   Zurzeit von keiner Serie benutzt — die einzige („with Inter / @Rosselquell")
   liegt seit 03.08.2026 im Archiv unter ../Archiv/with-Inter/. Die Regel bleibt
   stehen, damit gemischte Hoch-/Querformat-Serien sie wieder aufgreifen können.
   Ohne sie ragen die Hochformate aus der Flucht:
   sie waren 62 % ihrer halben Rasterzelle breit, die Querformate 68 % der
   ganzen Zeile — zwei Maße, die nichts miteinander zu tun haben. Jetzt ist das
   Raster selbst 68 % breit und die Bilder füllen ihre Zelle ganz aus, damit
   die Außenkanten aller Bilder der Serie auf einer Linie liegen. */
.bild-grid.buendig {
  width: min(100%, calc(0.68 * var(--breite)));
  margin: 0 auto;
}

.bild-grid.buendig .bild-item img,
.bild-grid.buendig .bild-item.quer img { width: 100%; }

/* Querformat-Bild in Kartoffel-16-Größe (68% statt 62% Breite) — im Grid
   (z.B. Kartoffel 16) über beide Spalten, so dass die Höhe etwa den
   Hochformat-Bildern entspricht; auch für Einzelbilder (z.B. INTER RS1).
   margin-top = Luft zum Inhalt darüber. */
.bild-item.quer {
  margin-top: 2rem;
}

.bild-item.quer img {
  width: min(100%, calc(0.68 * var(--breite)));
}

.bild-grid .bild-item.quer {
  grid-column: 1 / -1;
}

/* Zwei Bilder gleicher Höhe nebeneinander (z.B. Säule4 + Säule2).
   Das Paar ist genau so breit wie ein normales Bild (62 %) und steht damit
   links und rechts bündig mit dem Querformat darunter.
   Früher stand hier eine feste Bildhöhe (50vh) bei automatischer Breite —
   dann ergab sich die Gesamtbreite aus den Bildern und passte zu nichts.
   Jetzt andersherum: die Breite steht fest, und damit die Bilder trotzdem
   gleich hoch werden, wächst jedes im Verhältnis seiner eigenen Bildbreite
   zur Bildhöhe (--seite, steht am Element im HTML). Bei gleichem
   Breite/Höhe-Faktor sind zwangsläufig auch die Höhen gleich. */
.bild-paar {
  display: flex;
  justify-content: center;
  align-items: flex-start;
  gap: 2.5rem;
  width: min(100%, calc(0.62 * var(--breite)));
  margin: 0 auto;
}

.bild-paar .bild-item {
  flex: var(--seite, 1) 1 0;
  min-width: 0;          /* sonst verhindert die Mindestbreite des Bildes das Schrumpfen */
  width: auto;
}

.bild-paar .bild-item img {
  width: 100%;
  height: auto;
  margin: 0;
}

.bild-item {
  width: 100%;
  background: transparent;
  overflow: visible;
}

.bild-datum {
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.65rem;
  letter-spacing: 0.15em;
  color: #999999;
  text-align: right;
  margin-top: 0.8rem;
}

/* Bildunterschrift / Foto-Credit direkt unter einem Bild */
.bild-credit {
  display: block;
  /* = Bildbreite → Credit endet an der Bildkante */
  width: min(100%, calc(0.62 * var(--breite)));
  margin: 0.5rem auto 0;
  font-family: var(--font);
  font-weight: 200;
  font-style: italic;
  font-size: 0.6rem;
  letter-spacing: 0.12em;
  color: #777;
  text-align: right;
}

/* Im Säulen-Paar füllt das bild-item die Bildbreite → Credit = volle Breite */
.bild-paar .bild-credit {
  width: 100%;
}

/* Serien-Credit: einmalig am Ende einer Serie, etwas größer */
.credit-serie {
  display: block;
  font-family: var(--font);
  font-weight: 200;
  font-style: italic;
  font-size: 0.78rem;
  letter-spacing: 0.1em;
  color: #888;
  text-align: right;
  margin-top: 1.2rem;
}

/* Größere, hervorgehobene Einzelwerke — bei Mesh Deform I das Video, bei II
   die 3. Aufnahme. Der große Abstand setzt sie bewusst von der
   Vierer-Reihe darüber ab. */
.bild-item.gross {
  margin-top: 5rem;
}

/* Zu den Prozentwerten und --breite siehe .bildbereich in Abschnitt 3:
   ohne offenes 3D-Panel ist --breite 100 %, calc(0.86 * var(--breite)) heißt
   dann also einfach 86 %. */
.bild-item.gross img,
.bild-item.gross video {
  width: min(100%, calc(0.86 * var(--breite)));
}
.bild-item img {
  width: min(100%, calc(0.62 * var(--breite)));
  height: auto;
  display: block;
  margin: 0 auto;
  cursor: pointer;
  transition: opacity 0.3s ease;
}

/* ── Platzhalter und Aufbau (Noah 02.08.2026) ────────────────────────────────
   Der Platz stand schon immer korrekt: jedes <img> trägt width/height, woraus
   der Browser das Seitenverhältnis ableitet und den Kasten VOR dem Laden
   freihält (deshalb hüpft hier nichts). Neu ist nur, dass dieser Kasten jetzt
   etwas ZEIGT statt weiß zu bleiben.

   Gezeichnet wird auf dem <img> selbst, nicht in einem zusätzlichen Element:
   ein leeres <img> malt seinen CSS-Hintergrund ganz normal, und sobald das
   Bild da ist, deckt es ihn zu. Das erspart eine Umbauaktion am DOM — die
   Lightbox und die Vermessung der 3D-Spur hängen an genau dieser Struktur. */
/* Der Platzhalter ist eine STILLE Fläche. Ein wandernder Lichtstreifen stand
   hier bis 02.08.2026 — Noah: „ohne großes Effektgetue". Er zog die
   Aufmerksamkeit auf das Warten statt auf das Werk. Der Ton liegt dicht am
   Weiß der Seite: gerade genug, dass man den Kasten als „hier kommt etwas"
   liest, zu wenig, um als grauer Block aufzufallen. */
.bild-item img.laedt,
.plakat img.laedt {
  background-color: #f4f2f0;
}

/* Der Aufbau, und sonst nichts. `clip-path: inset(0 100% 0 0)` blendet alles
   bis auf die linke Kante aus; der rechte Wert läuft auf 0 und gibt das Bild
   spaltenweise nach rechts frei. Es wischt also aus dem Nichts herein, ohne
   Blenden, ohne Schieben, ohne Unschärfe.

   Die Kurve läuft gleichmäßig aus (ease-out) statt mit Schwung anzusetzen —
   ein Beschleunigen wäre wieder eine Geste, die auf sich zeigt. */
.bild-item img.fertig,
.plakat img.fertig {
  animation: bild-aufbau 0.62s cubic-bezier(0.33, 1, 0.68, 1) both;
}

@keyframes bild-aufbau {
  from { clip-path: inset(0 100% 0 0); }
  to   { clip-path: inset(0 0 0 0); }
}

/* Ein Bild, das auch nach allen Versuchen nicht kommt, behält schlicht seine
   Platzhalterfläche: bilder.js setzt bei `error` die Klasse `laedt` zurück.
   Damit steht dort eine ruhige Lücke in Bildgröße statt des Browser-
   Fragezeichens, und die Seite rutscht nicht. (Die frühere Sonderklasse
   `bild-fehlt` ist beim Entflechten am 02.08.2026 entfallen — sie tat
   dasselbe, brauchte aber eine zweite Stelle, die mitzählen musste.) */

/* Wer „Bewegung reduzieren" eingestellt hat, bekommt kein Wischen, sondern ein
   kurzes Aufblenden. Der Platzhalter steht ohnehin still, für ihn ist hier
   nichts zurückzunehmen. */
@media (prefers-reduced-motion: reduce) {
  .bild-item img.fertig,
  .plakat img.fertig {
    animation: bild-aufblenden 0.35s ease both;
  }

  @keyframes bild-aufblenden {
    from { opacity: 0; }
    to   { opacity: 1; }
  }
}

/* Video-Werke liegen im selben Raster wie die Bilder, sind aber nicht
   anklickbar (die Lightbox sammelt nur .bild-item img) — deshalb kein
   Zeiger-Cursor. */
.bild-item video {
  width: min(100%, calc(0.62 * var(--breite)));
  height: auto;
  display: block;
  margin: 0 auto;
}


/* ── 4. Lightbox ── */

.lightbox {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.96);
  z-index: 500;
  align-items: center;
  justify-content: center;
}

.lightbox.offen { display: flex; }

.lightbox-bild {
  max-width: 90vw;
  max-height: 90vh;
  object-fit: contain;
  display: block;
  cursor: default;
}

.lightbox-close {
  position: fixed;
  top: 1.8rem;
  right: 2rem;
  font-family: var(--font);
  font-weight: 200;
  font-size: 1.1rem;
  color: white;
  opacity: 0.25;
  cursor: pointer;
  transition: opacity 0.2s ease;
  user-select: none;
  z-index: 501;
}

.lightbox-close:hover { opacity: 0.8; }

.lightbox-pfeil {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  font-size: 2rem;
  font-weight: 100;
  color: white;
  opacity: 0.2;
  cursor: pointer;
  transition: opacity 0.2s ease;
  user-select: none;
  padding: 1rem;
  z-index: 501;
  line-height: 1;
}

.lightbox-pfeil:hover { opacity: 0.7; }
.lightbox-pfeil.links { left: 1.5rem; }
.lightbox-pfeil.rechts { right: 1.5rem; }


/* ── 5. Kontakt-Panel ── */

/* ── CV/Contact-Panel ──
   Flex-Kind in .layout, rechts vom 3D-Panel; Breite 0 → 28vw (wie das 3D-Panel).
   SCHALTET SEIT 10.09.2026 MIT (Noah: „der hintergrund ist immer weiss, er
   soll auch invertieren wenn ich im 3D view offen war oder zu mesh deform
   gescrollt habe"). Bis dahin war das Panel fest weiss. Alle Farben liegen
   deshalb als Variablen hier und werden unten fuer den dunklen Zustand
   einmal umgeschrieben — `body.main-hell` ist genau dann gesetzt, wenn die
   Seite hell ist, und faellt sowohl beim Oeffnen des 3D-Fensters als auch ab
   Mesh Deform weg (siehe pruefeThema/darfHellSein in main.js). */
.cv-panel {
  --cv-grund:  #ffffff;
  --cv-kante:  #ddd9d2;
  --cv-stark:  #14120f;   /* Name, Abschnitts-Ueberschriften */
  --cv-text:   #3a352f;   /* Werte und Listentexte */
  --cv-leise:  #8a857e;   /* Feldbeschriftungen, Jahreszahlen */
  --cv-rahmen: #d6d1c9;   /* Kaestchen des Plakat-Aufklappers */
  --cv-hover:  #000000;

  width: 0;
  flex-shrink: 0;
  height: 100%;
  overflow: hidden;
  background-color: var(--cv-grund);
  transition: background-color 0.6s ease, border-color 0.6s ease;
  padding-top: 64px;   /* unter den fixed Header */
  transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

body.cv-offen .cv-panel {
  width: 28vw;
  border-left: 1px solid var(--cv-kante);
  overflow-y: auto;   /* aufgeklappte Ausstellungen scrollen im Panel */
}

/* Dunkel: gilt, sobald die Seite dunkel ist — 3D-Fenster offen ODER ab
   Mesh Deform. Nur die Variablen werden getauscht, keine einzige Regel
   doppelt geschrieben. */
body:not(.main-hell) .cv-panel {
  --cv-grund:  #0d0c0b;
  --cv-kante:  #232019;
  --cv-stark:  #f3f0ea;
  --cv-text:   #cec8bf;
  --cv-leise:  #8a857e;
  --cv-rahmen: #3d382f;
  --cv-hover:  #ffffff;
}

.kontakt-panel {
  position: relative;
  padding: 2.5rem 2rem 3rem;
  width: 100%;
}

/* CV offen: die Kategorien werden NICHT mehr ausgeblendet (Noah 27.07.2026:
   „du hast das sehr cool geregelt, dass die Kategorien so sliden, aber noch
   anhebbar sind wenn man drauf hovert — dasselbe bitte, wenn CV aktiviert
   ist"). Sie werden also zur selben schmalen Fahrbahn-Leiste wie bei offenem
   3D-Panel; die Regeln dafür stehen weiter unten und gelten für BEIDE
   Zustände. Hier bleibt nur der Platz, den der Bildbereich einrückt. */
body.cv-offen .bildbereich { margin-left: var(--rail-breite); }

/* War das 3D-Panel offen, wächst es auf Hauptseiten-Größe und die Hauptseite
   (Bildbereich) weicht ganz. War es zu, verdrängt CV einfach 28vw der
   Hauptseite (Bildbereich bleibt flex:1 und wird nur schmaler).

   ACHTUNG, teuer gelernt (Noah 27.07.2026: „wenn cv und 3d view offen ist
   liegen die views komisch übereinander"): Der Bildbereich darf dabei NICHT
   auf Breite 0 zusammenfallen. Die 3D-Spur misst ihre Blöcke an den
   offsetTop-Werten der Sektionen im Bildbereich — bei Breite 0 bricht dort
   jedes Bild um, alle Werte werden Unsinn und die Blöcke landen übereinander.
   Außerdem klemmt dann sein Scrollweg auf fast 0 und reißt die gekoppelte
   Spur mit nach oben.

   Deshalb: aus dem Fluss nehmen statt zusammenfallen lassen. Er behält per
   left/right exakt die Breite, die er als Flex-Kind hatte, bleibt vollständig
   vermessbar und verschwindet nur hinter dem (deckenden) 3D-Panel, das im
   Fluss an seine Stelle rückt.

   NACHTRAG 30.07.2026 — DAS RUCKELN BEIM CV-KLICK (Noah: „wenn 3D-Info offen
   und Klick auf Contact/CV und noch einmal fürs Schließen — Animation
   ruckelig"). Hier stand `left: 0; right: 28vw`, also 1152px statt der 1030px,
   die der Bildbereich als Flex-Kind hat. Damit änderte sich beim CV-Klick doch
   seine Breite, und zwar zweimal:

     · Beim ÖFFNEN sprang er in EINEM Bild von 1030 auf 1152 px (links von 48
       auf 0) — die ganze Galerie brach einmal komplett um, gemessen
       scrollHeight 13406 → 13564.
     · Beim SCHLIESSEN war es viel schlimmer: der Bildbereich fällt in dem
       Augenblick zurück in den Fluss, in dem 3D-Panel (1152) und CV-Panel
       (448) noch ihre volle Breite haben — als Flex-Rest blieben ihm gemessene
       96 px. Über die folgende halbe Sekunde wuchs er 96 → 166 → 347 → 533 →
       … → 1030 px, und die ganze Galerie wurde in JEDEM DIESER BILDER neu
       umgebrochen (scrollHeight 4213 → 13406). Weil die 3D-Spur ihre Blöcke an
       den offsetTop-Werten dieser Sektionen ausrichtet, sprang sie die ganze
       Zeit mit.

   Die Lösung ist, dem Bildbereich außerhalb des Flusses GENAU DIE GEOMETRIE zu
   geben, die er im Fluss auch hat (links die Fahrbahn-Leiste, rechts 3D-Panel
   + 3D-Leiste). Dann ändert sich beim CV-Klick an ihm überhaupt nichts mehr —
   kein Umbruch, kein Sprung, in keiner Richtung. Er wird einfach vom breiter
   werdenden 3D-Panel überdeckt (das deckt 0…1152, er liegt bei 48…1078).

   `.cv-schliesst` hält ihn die Dauer der Animation über weiter draußen; die
   Klasse setzt und entfernt `schliesseKontakt()` in main.js. Ohne sie käme das
   Zusammenfallen auf 96 px zurück. */
body:is(.cv-offen, .cv-schliesst).dreid-offen .bildbereich {
  position: absolute;
  top: 0; bottom: 0;
  left: var(--rail-breite);
  right: calc(28vw + var(--leiste-breite));
  /* left übernimmt, was sonst der Rand tat */
  margin-left: 0;
}
body.cv-offen.dreid-offen .dreid-panel {
  width: calc(100vw - 28vw);
}

/* `.cv-schliesst` ist bei der BREITE bewusst NICHT dabei — sie soll ja gerade
   von 72vw auf 28vw zurückfahren. Nur die rechte Verankerung gilt für beide
   Zustände: während der Schließ-Animation liegt der Bildbereich weiterhin
   außerhalb des Flusses (Regel oben), das Panel wäre ohne sie also wieder das
   erste Flex-Kind und spränge nach links. */
body:is(.cv-offen, .cv-schliesst).dreid-offen .dreid-panel {
  /* RECHTS VERANKERT — sonst läuft die Animation rückwärts (Noah 28.07.2026:
     „slided von der falschen Seite rein").
     Der Bildbereich verlässt beim CV-Klick SCHLAGARTIG den Fluss (Regel oben,
     position: absolute). Ohne diese Zeile ist das 3D-Panel damit im selben
     Augenblick das erste Kind im Fluss und springt an den linken Rand — die
     linke Kante rutscht in EINEM Bild von 1078 auf 0 und das Panel rollt
     danach nach rechts auf, wirkt also, als käme es von links herein.
     `margin-left: auto` schiebt es stattdessen weiter nach rechts: seine
     rechte Kante bleibt am CV-Panel kleben und wandert mit dessen Aufziehen
     sanft nach links, während es zugleich breiter wird. Gemessen: linke Kante
     durchgehend 1078 → 0, kein Sprung. */
  margin-left: auto;
}

/* Weites Panel: 360°-Ansicht und Modell NEBENEINANDER.
   Übereinander (wie in der schmalen Spalte) geht hier nicht auf: das
   16:9-Feld wäre bei ~72vw Breite höher als der Bildschirm und schöbe Modell
   und Werkangabe aus dem Block heraus. Genau das sah Noah als „komisch
   übereinander". */
body.cv-offen.dreid-offen .dreid-halt {
  display: grid;
  grid-template-columns: 1.6fr 1fr;
  grid-template-rows: 1fr auto;
  column-gap: 1.5rem;
  padding: 2vh 1.5rem 0;
}
body.cv-offen.dreid-offen .dreid-360 {
  grid-column: 1;
  grid-row: 1;
  margin-top: 0;
  aspect-ratio: auto;
  border-bottom: none;
  border-right: 1px solid #1f1c18;
}
body.cv-offen.dreid-offen .dreid-360.leer { border-right: none; }
body.cv-offen.dreid-offen .dreid-buehne { grid-column: 2; grid-row: 1; }
body.cv-offen.dreid-offen .dreid-fuss { grid-column: 1 / -1; grid-row: 2; }
/* Der Fließtext bekommt eine eigene dritte Zeile unter der Werkangabe. Ohne
   diese Zuweisung setzte ihn das Raster selbsttätig in die nächste freie
   Zelle — also neben die 360°-Ansicht. Die Breite wird begrenzt: über die
   vollen ~72vw gezogen wären es weit über hundert Zeichen je Zeile. */
body.cv-offen.dreid-offen .dreid-werktext {
  grid-column: 1 / -1;
  grid-row: 3;
  max-width: 62ch;
  margin: 0 auto;
}

/* Solange CV offen ist, ist die 3D-Leiste weg — zurück geht es über den Knopf. */
/* Sie gibt ihre 74px dabei frei — anders als beim `[hidden]`-Fall (siehe
   base.css), wo der Platz stehen bleiben MUSS. Aber sie gibt ihn in derselben
   halben Sekunde frei, in der die Panels fahren: mit `display: none` wäre der
   Platz schlagartig weg und das rechts verankerte 3D-Panel machte im ersten
   Bild einen Satz um 74px nach rechts, bevor es nach links wächst. */
body.cv-offen .dreid-leiste {
  width: 0;
  border-left-width: 0;
  opacity: 0;
  visibility: hidden;
  transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              border-left-width 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.25s ease,
              visibility 0s linear 0.5s;
}

/* Zurück-/Home-Knopf an der Stelle der Kategorien (oben links). Sichtbar,
   sobald irgendein Zusatzfenster offen ist — er ist der Weg zurück auf die
   reine Hauptseite, auch wenn diese gerade ganz verdrängt ist. */
.cv-zurueck {
  position: fixed;
  top: 88px;
  left: 2rem;
  z-index: 210;   /* über dem CV-Panel (auch dem Handy-Overlay) und dem Header */
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.68rem;
  letter-spacing: 0.25em;
  text-transform: uppercase;
  color: #999;
  padding: 0;
  transition: color 0.2s ease;
}
.cv-zurueck:hover { color: #fff; }
/* Nur bei offenem CV — dann ist die Hauptseite verdrängt und es gibt sonst
   keinen Weg zurück. Ist NUR das 3D-Panel offen, ist die Hauptseite sichtbar
   und anklickbar (Klick auf eine freie Stelle schließt alles, siehe
   main.js), und der Knopf säße genau auf dem Serien-Pfad „Works". */
body.cv-offen .cv-zurueck { display: block; }

.kontakt-name {
  font-family: var(--font);
  font-weight: 300;
  font-size: 1rem;
  letter-spacing: 0.3em;
  color: var(--cv-stark);
  margin-bottom: 2rem;
}

.kontakt-zeile {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin-bottom: 1.4rem;
}

.kontakt-zeile:last-child { margin-bottom: 0; }

/* „Mail: …" und „Instagram: …" als eine gewoehnliche Zeile. .kontakt-zeile
   ist sonst ein Spaltenstapel (Beschriftung ueber dem Wert); hier stehen
   beide nebeneinander auf einer Grundlinie und duerfen auf schmalen Fenstern
   umbrechen. */
.kontakt-zeile--satz {
  flex-direction: row;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.45em;
  margin-bottom: 0.6rem;
}

.kontakt-satzlabel {
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;      /* wie .kontakt-wert, es ist derselbe Satz */
  letter-spacing: 0.05em;
  color: var(--cv-text);
}

.kontakt-label {
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.6rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--cv-leise);
}

/* „CV", „Exhibitions" und „Other" sind UEBERSCHRIFTEN ihres Abschnitts, nicht
   Feldbeschriftungen wie „Mail" und „Instagram" — die bleiben klein und grau.
   Groesse liegt bewusst knapp UNTER .kontakt-name (1rem), damit der Name die
   oberste Ebene bleibt. Sperrung von 0.3em zurueck auf 0.22em: was in 0.6rem
   luftig wirkt, faellt in fast 1rem auseinander. */
.kontakt-cv > .kontakt-label,
.kontakt-ausstell > .kontakt-label,
.kontakt-sonstiges > .kontakt-label {
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.22em;
  color: var(--cv-stark);
  margin-bottom: 0.7rem;
}

.kontakt-wert {
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  color: var(--cv-text);
  text-decoration: none;
  transition: color 0.2s ease;
}

a.kontakt-wert:hover { color: var(--cv-hover); }

/* Wert MIT vorangestelltem Zeichen (Instagram). `baseline` statt `center`:
   das Zeichen soll auf derselben Linie sitzen wie die Schrift daneben, sonst
   hinge es je nach Schriftgröße leicht zu hoch. */
.kontakt-wert-mit-zeichen {
  display: inline-flex;
  align-items: baseline;
  gap: 0.5rem;
}

/* Etwas kleiner als die Zeilenhöhe, damit das Zeichen den Namen begleitet und
   ihn nicht überragt. `translateY` setzt es auf die Schriftlinie — ein SVG
   richtet sich sonst mit seiner UNTERKANTE an der Grundlinie aus. */
.insta-zeichen {
  flex: none;
  width: 0.95rem;
  height: 0.95rem;
  transform: translateY(0.14rem);
  fill: none;
  stroke: currentColor;
  stroke-width: 1.7;
}

/* Der kleine Punkt oben rechts ist im Original gefüllt, nicht umrandet */
.insta-zeichen .insta-punkt {
  fill: currentColor;
  stroke: none;
}

/* ── Lebenslauf ──────────────────────────────────────────────────────────
   Steht mit deutlichem Abstand unter den Kontaktangaben, der Ausstellungs-
   Knopf folgt darunter als Letztes. */
.kontakt-cv { margin-top: 3.2rem; }

/* Zeit links, Text rechts als zweispaltiges Raster. `max-content` gibt der
   ersten Spalte genau die Breite der LÄNGSTEN Zeitangabe — dadurch beginnen
   alle Texte auf derselben senkrechten Linie und nichts springt von Zeile zu
   Zeile. Ein zu langer Text bricht innerhalb SEINER Spalte um (hängender
   Einzug), läuft also nie unter die Jahreszahl. */
.cv-liste {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 1.2rem;
  row-gap: 0.7rem;
  margin: 0.5rem 0 0;
}

.cv-liste dt {
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  color: var(--cv-leise);   /* wie die Labels: die Zeit tritt hinter den Text zurück */
  white-space: nowrap; /* „2021 – Now“ bleibt in einem Stück */
}

.cv-liste dd {
  margin: 0;
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  line-height: 1.45;
  color: var(--cv-text);
}

/* Sehr schmales Panel (Handy-Overlay): untereinander statt nebeneinander,
   sonst bliebe für den Text kaum Breite. Die Zeit steht dann als Zeile
   darüber, der Text bündig darunter — auch das springt nicht. */
@media (max-width: 800px) {
  .cv-liste {
    grid-template-columns: 1fr;
    row-gap: 0.25rem;
  }
  .cv-liste dd { margin-bottom: 0.9rem; }
  .cv-liste dd:last-child { margin-bottom: 0; }
}

/* ── Ausstellungen ───────────────────────────────────────────────────────
   Dasselbe Raster wie der CV darüber (Jahr links, Text rechts), nur mit mehr
   Luft ZWISCHEN den Jahren: ein Jahr trägt bis zu fünf Einträge, die als Block
   zusammengehören und sich vom nächsten Jahr absetzen müssen. */
/* „Other" teilt sich Abstand und Raster mit den Ausstellungen — siehe main.html. */
.kontakt-ausstell,
.kontakt-sonstiges { margin-top: 3.2rem; }

.ausstell-liste {
  display: grid;
  grid-template-columns: max-content 1fr;
  column-gap: 1.2rem;
  row-gap: 1.5rem;
  margin: 0.5rem 0 0;
}

.ausstell-liste dt {
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  color: var(--cv-leise);   /* wie im CV: die Jahreszahl tritt hinter den Text zurück */
  white-space: nowrap;
}

/* Die Einträge eines Jahres stehen untereinander — enger als der Abstand
   zwischen den Jahren, damit die Gruppierung sichtbar bleibt. */
.ausstell-liste dd {
  display: flex;
  flex-direction: column;
  gap: 0.45rem;
  margin: 0;
  font-family: var(--font);
  font-weight: 300;
  font-size: 0.85rem;
  letter-spacing: 0.05em;
  line-height: 1.45;
  color: var(--cv-text);
}

/* Handy: wie beim CV untereinander statt nebeneinander — zweispaltig bliebe
   für die langen Ausstellungstitel kaum Breite. */
@media (max-width: 800px) {
  .ausstell-liste {
    grid-template-columns: 1fr;
    row-gap: 0.3rem;
  }
  .ausstell-liste dt { margin-top: 0.5rem; }
  .ausstell-liste dt:first-child { margin-top: 0; }
  .ausstell-liste dd { margin-bottom: 1.2rem; }
  .ausstell-liste dd:last-child { margin-bottom: 0; }
}

/* ── Plakat-Aufklapper an der einzelnen Ausstellung (10.09.2026) ──────────
   Der Pfeil ist mit Rändern gezeichnet statt als Schriftzeichen (▾) gesetzt:
   Zeichensätze bilden solche Pfeile unterschiedlich breit und hoch ab, und in
   UbuntuMono säße er sichtbar zu tief. Zwei Kanten eines Quadrats, um 45°
   gedreht, sehen überall gleich aus und drehen sich beim Aufklappen mit. */
.plakat-schalter {
  appearance: none;
  /* Sichtbarer Kreis statt eines nackten Pfeils: als blosser Strich neben dem
     Text war er zu leicht zu uebersehen (Noah 10.09.2026: „auch nicht bei der
     ausstellungsliste als jeweiliges dropdown"). */
  background: none;
  border: 1px solid var(--cv-rahmen);
  /* Eckig, nicht rund (Noah 10.09.2026: „hässlich rund") — und im selben
     Rechteckmass wie die Stationsköpfe der Fahrbahn. */
  border-radius: 0;
  /* inline-flex zentriert den Pfeil im Kästchen, ohne dass man ihn per
     translate zurechtschieben muss. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.25em;
  height: 1.25em;
  margin-left: 0.5em;
  padding: 0;
  cursor: pointer;
  color: var(--cv-leise);
  /* Mittig AUF der Textzeile. Nachgemessen im Browser: Mitte des Kästchens
     gegen Mitte der Zeile. Mit -0.3em sass es 5,2px zu tief; bei 13,6px
     Schrift sind das 0,38em, also -0.3 + 0.38 = 0.08em. Wer die Kästchengrösse
     oder den Schriftgrad der Liste ändert, misst das neu. */
  vertical-align: 0.08em;
  transition: color 0.2s ease, border-color 0.2s ease, background-color 0.2s ease;
}

.plakat-schalter:hover,
.plakat-schalter[aria-expanded="true"] {
  color: var(--cv-stark);
  border-color: var(--cv-stark);
}

.plakat-schalter::before {
  content: '';
  display: inline-block;
  width: 0.42em;
  height: 0.42em;
  border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor;
  /* Die Drehung kippt das Quadrat auf die Spitze; das Verschieben bringt die
     Spitze zurück auf die Schriftlinie, sonst hinge sie zu hoch. */
  /* Nur die halbe Kantenhoehe nach oben: das gedrehte Quadrat ist optisch
     tieferliegend als sein Rechteck, sonst haengt die Spitze zu weit unten. */
  transform: translateY(-0.1em) rotate(45deg);
  transition: transform 0.25s ease;
}

.plakat-schalter[aria-expanded="true"]::before {
  transform: translateY(0.1em) rotate(225deg);
}

/* Das Plakat selbst. KEIN eigener Scrollkasten: die Höhe ist gedeckelt, damit
   es ganz ins Fenster passt — dann muss auch niemand darin scrollen und das
   Mausrad bleibt beim Panel. (Der Sammelkasten davor war 60vh hoch und
   scrollte in sich, was das Rad über der halben Spalte schluckte.) */
.plakat {
  display: flex;
  flex-direction: column;
  gap: 1rem;
  margin: 0.9rem 0 0.4rem;
}

.plakat[hidden] { display: none; }

.plakat img {
  width: 100%;
  max-height: 62vh;
  object-fit: contain;
  object-position: left top;
  display: block;
}

/* Solange ein Plakat offen ist, bekommt das Panel mehr Breite — bei 28vw wäre
   ein Plakat nur rund 350px breit und seine Beschriftung nicht mehr lesbar.
   main.js setzt dafür `.breit` auf .kontakt-panel (siehe panelBreite()).
   ACHTUNG: die alte Fassung hatte hier NUR `max-width: 760px` auf dem inneren
   .kontakt-panel — das ist bei `width: 100 %` in einer 28vw-Spalte aber bloss
   ein Deckel und hat nie verbreitert. Die Breite muss an die Spalte selbst.
   Die Klasse sitzt zusätzlich auf <body> (main.js: panelBreite), damit hier
   kein `:has()` nötig ist — das kennt Firefox erst ab 121 und liesse die Regel
   sonst lautlos ausfallen. In der eigenen min-width-Abfrage, weil am Handy
   (ab 800px abwärts) das Panel fest über die ganze Seite liegt. */
@media (min-width: 801px) {
  body.plakat-offen.cv-offen .cv-panel { width: 44vw; }
}

.kontakt-panel.breit { max-width: 760px; }

/* (Das CV-Panel ist IMMER weiß, deshalb stehen alle hellen Werte oben direkt
   an den Grundregeln — es gibt hier keine Dunkel-Umkehrung nachzuziehen.
   Der Sammelknopf „Ausstellungen" samt seinem 60vh-Scrollkasten ist am
   10.09.2026 entfallen, seine fünf Plakate hängen jetzt einzeln an ihrer
   Ausstellung.) */


/* ── Weicher Hell↔Dunkel-Übergang ──
   Beim Wechsel des Farbschemas (Works ↔ Envoirements/Mesh Deform) gleiten
   Hintergrund, Ränder und Schriftfarben statt hart umzuspringen. Der Inhalt
   selbst cross-fadet weiterhin über .bildbereich.fade-aus. */

body {
  transition: background-color 0.6s ease, color 0.6s ease;
}

.sidebar,
.unterkategorien,
.subsubkat,
.serie-header {
  transition: border-color 0.6s ease;
}

.site-name,
.serie-pfad,
.serie-zahl,
.bild-credit,
.credit-serie {
  transition: color 0.6s ease;
}

.burger span {
  transition: background-color 0.6s ease;
}

/* Nav-Texte haben fürs Hover eine schnelle Farb-Transition (0.2s) — während
   des Schema-Wechsels (body.theme-fade, per JS gesetzt) laufen sie im
   Hintergrund-Tempo mit, damit nichts kurz unlesbar aufblitzt. */
body.theme-fade .subkat-titel,
body.theme-fade .unter-link,
body.theme-fade .contact-link {
  transition: color 0.6s ease, opacity 0.2s ease;
}

/* Dasselbe fuers CV-Panel. Ohne diese Regel sprang der Text hart um, waehrend
   der Hintergrund weich lief — die Farben kommen dort aus --cv-*, und eine
   Custom Property selbst animiert NICHT. Was animiert, ist die `color`, die
   sie benutzt; sie braucht nur eine Transition. `.kontakt-wert` und
   `.plakat-schalter` haben fuers Hover eine schnelle (0.2s) — waehrend des
   Schemawechsels laufen sie hier im Tempo des Hintergrunds mit. */
body.theme-fade .cv-panel,
body.theme-fade .kontakt-name,
body.theme-fade .kontakt-label,
body.theme-fade .kontakt-satzlabel,
body.theme-fade .kontakt-wert,
body.theme-fade .cv-liste dt,
body.theme-fade .cv-liste dd,
body.theme-fade .ausstell-liste dt,
body.theme-fade .ausstell-liste dd,
body.theme-fade .plakat-schalter {
  transition: color 0.6s ease, background-color 0.6s ease, border-color 0.6s ease;
}


/* ── 6. Hell-Modus (schwarze Schrift, weißer Hintergrund) ── */

body.main-hell {
  background: #ffffff;
  color: #111111;
}

body.main-hell .site-header {
  background: #ffffff;
  border-bottom-color: #ddd;
}

body.main-hell .site-name {
  color: #111111;
}

body.main-hell .contact-link {
  color: #111111;
  opacity: 0.45;
}

body.main-hell .sidebar {
  border-right-color: #ddd;
}

body.main-hell .unterkategorien {
  border-left-color: #b5b5b5;
}

body.main-hell .subkat-titel {
  color: #888888;
}

body.main-hell .subkat-titel:hover,
body.main-hell .subkat-titel.aktiv {
  color: #222222;
}

body.main-hell .subsubkat {
  border-left-color: #c8c8c8;
}

body.main-hell .unter-link {
  color: #999999;
}

body.main-hell .unter-link:hover,
body.main-hell .unter-link.aktiv {
  color: #111111;
}

/* ── Großes 3D-Fenster: Kategorienleiste bleibt DUNKEL ──
   Ist zusätzlich zum 3D-Panel das CV offen, füllt das schwarze Panel fast die
   ganze Seite. Eine weiße Fahrbahnleiste davor sähe wie ein Fremdkörper aus
   (Noah 27.07.2026: „wenn 3D-View groß ist, wird die Kategoriespalte nicht
   schwarz — sie sollte auch schwarz sein und der Rest invertiert, genau wie
   bei 3D Mesh Deform").

   NACHTRAG 29.07.2026: Seit offenes 3D die GANZE Seite dunkel schaltet
   (`darfHellSein()` in main.js), können .main-hell und .dreid-offen gar nicht
   mehr gleichzeitig am body stehen — dieser Block greift also nicht mehr. Er
   bleibt als Sicherung stehen, falls das Gatter je wieder gelockert wird.
   Deshalb hier für den hellen Seitenteil ausdrücklich die dunklen Werte
   zurückholen. Im dunklen Teil der Seite gelten sie ohnehin. */
body.main-hell.cv-offen.dreid-offen .sidebar {
  background-color: #000000;
  border-right-color: #1a1a1a;
}
body.main-hell.cv-offen.dreid-offen .subkat-titel { color: #777; }
body.main-hell.cv-offen.dreid-offen .subkat-titel:hover,
body.main-hell.cv-offen.dreid-offen .subkat-titel.aktiv { color: #ddd; }
body.main-hell.cv-offen.dreid-offen .unter-link { color: #666; }
body.main-hell.cv-offen.dreid-offen .unter-link:hover,
body.main-hell.cv-offen.dreid-offen .unter-link.aktiv { color: #fff; }
/* Fahrbahn und Stationsköpfe zurück auf die dunklen Werte (siehe .subkat-item).
   Wichtig: --fahrbahn muss hier ebenfalls zurückgeholt werden — der helle
   Seitenteil setzt es auf fast Schwarz, und das wäre auf der schwarzen Spalte
   unsichtbar. */
body.main-hell.cv-offen.dreid-offen .subkat-item {
  --fahrbahn: var(--fahrbahn-auf-dunkel);
  --leitung-v: linear-gradient(#333, #333);
  --leitung-h: linear-gradient(#333, #333);
  --kopf-fuellung: #161412;
  --kopf-rand: #55504a;
  --kopf-punkt: #8d867d;
}
body.main-hell.cv-offen.dreid-offen .subkat-item:has(.subkat-titel.aktiv) {
  --kopf-fuellung: var(--fahrbahn);
  --kopf-rand: var(--fahrbahn);
  --kopf-punkt: rgba(0, 0, 0, 0.72);
}

body.main-hell .serie-header {
  border-bottom-color: #ddd;
}

body.main-hell .serie-pfad {
  color: #999999;
}

body.main-hell .serie-zahl {
  color: #555555;
}

body.main-hell .bild-credit,
body.main-hell .credit-serie {
  color: #777777;
}

body.main-hell .bild-item {
  background: transparent;
}

/* Das CV-Panel ist immer dunkel (eigener Panel-Hintergrund), sein Inhalt macht
   den Hell/Dunkel-Wechsel der Seite deshalb NICHT mit. */

/* Copyright-Zeile auf hellem Grund dunkel statt hell (Grundfarbe in base.css) */
body.main-hell .seiten-fuss {
  color: rgba(0, 0, 0, 0.38);
}


/* ── 7. Video-Einbettung ── */

.video-wrapper {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  background: #000;
}

.video-wrapper iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: none;
}


/* ── 8. 3D-Panel: eine zweite Spur, die mit der Hauptseite mitscrollt ──
   Die Leiste „3D & Info" steht am rechten Rand (Aussehen in base.css, weil die
   Startseite dieselbe Leiste hat). Beim Klick bekommt der body die Klasse
   .dreid-offen und das Panel fährt auf 28vw auf.

   AUFBAU (gefüllt von main-3d.js):
     .dreid-panel            Spalte, Breite 0 → 28vw
       canvas.dreid-canvas   EIN WebGL-Canvas für alle Modelle, liegt still
                             hinter der Spur; jedes Modell bekommt daraus per
                             Scissor-Rechteck seinen Ausschnitt
       .dreid-spur           scrollt — und zwar exakt im Gleichtakt mit
                             .bildbereich (Zweiwege-Spiegelung im JS)
         .dreid-gleis        so hoch wie die Hauptseite lang ist
           .dreid-block      je Werk mit 3D, an genau der Höhe seiner Sektion
             .dreid-halt     position: sticky — das Objekt bleibt stehen,
                             solange seine Sektion durchläuft, und wird dann
                             vom nächsten hinausgeschoben

   Vorher zeigte das Panel immer nur EIN Werk und blendete beim Scrollen auf das
   nächste über. Das war der Grund für Noahs „faded nur aus und ein statt zu
   scrollen" — die Spur ersetzt den Crossfade vollständig.

   Das Panel ist ein Flex-Kind des Layouts und nimmt beim Öffnen echte Breite
   weg. Damit die Bilder der Hauptseite dabei NICHT kleiner werden, hängen ihre
   Breiten an --breite (siehe Abschnitt 3) statt an Prozent des geschrumpften
   Bildbereichs: sie behalten ihre Größe und rücken nur nach links, in den
   Platz, den die ausgeblendeten Kategorien frei machen.

   Ein reines Overlay (position: fixed) wurde ausprobiert und verworfen: der
   Inhaltsblock behält dabei seine volle Breite, und schiebt man ihn per
   transform nach links, wandert die Sektionsüberschrift aus dem Bild. */

.dreid-panel {
  position: relative;
  width: 0;
  flex-shrink: 0;
  height: 100%;
  overflow: hidden;
  background-color: #0a0905;
  transition: width 0.5s cubic-bezier(0.4, 0, 0.2, 1);
}

body.dreid-offen .dreid-panel {
  width: 28vw;
  border-left: 1px solid #1f1c18;
}

/* Canvas und Spur beginnen unter dem fixierten Header (64px). Beide sind
   absolut positioniert und decken sich deckungsgleich — das Canvas darf nicht
   mitscrollen, sonst wanderten die Modelle mit dem Scissor-Rechteck davon. */
.dreid-canvas {
  position: absolute;
  top: 64px; left: 0; right: 0;
  display: block;
  width: 100%;
  /* Höhe MUSS explizit stehen. Ein <canvas> ist ein ersetztes Element: bei
     height:auto gewinnt seine eigene Pixelhöhe (die three.js setzt), und
     `bottom` wird stillschweigend ignoriert — das Canvas war dadurch nur
     222px hoch und die Modelle lagen außerhalb. */
  height: calc(100% - 64px);
  z-index: 0;
  pointer-events: none;   /* Ziehen geht an die .dreid-buehne darüber */
}

.dreid-spur {
  position: absolute;
  top: 64px; left: 0; right: 0; bottom: 0;
  z-index: 1;
  /* KEIN eigener Scrollweg mehr (10.09.2026). Die Spur wird von main-3d.js per
     transform gesetzt, weil zwei echte Scroll-Container sich nie exakt
     gleichzeitig bewegen koennen — der Nachlauf und das gegenseitige
     Ausbremsen kamen genau daher. Rad und Finger ueber der Spur leitet
     main-3d.js an den Bildbereich weiter. */
  overflow: hidden;
  /* Kein eigener Scrollbalken — die Spur ist keine zweite Bedienoberfläche,
     sie läuft mit. Scrollen darin ist trotzdem echt (Rad und Finger), und die
     Hauptseite folgt. */
  scrollbar-width: none;
  overscroll-behavior: contain;
  /* Firefox würde beim Spiegeln sonst versuchen, die Position an einem Element
     zu verankern, und meldet „Scroll anchoring disabled". Hier führt die
     Hauptseite, die Spur folgt — Verankerung wäre nur ein Gegenspieler. */
  overflow-anchor: none;
}
.dreid-spur::-webkit-scrollbar { width: 0; height: 0; }

.dreid-gleis {
  position: relative;
  width: 100%;
  /* Höhe setzt main-3d.js: so, dass der maximale Scrollweg exakt dem des
     Bildbereichs entspricht. Sonst klemmt eine der beiden Spuren am Ende
     früher und die Spiegelung würde dort hin- und herspringen. */
}

.dreid-block {
  position: absolute;
  left: 0;
  right: 0;
  /* top und height kommen von der zugehörigen Sektion der Hauptseite */
}

.dreid-block[hidden] { display: none; }

/* Bleibt stehen, solange seine Sektion durchläuft. Die Höhe setzt das Skript
   auf min(Blockhöhe, Sichthöhe) — wäre sie größer als der Block, ragte der
   Inhalt in den Nachbarn hinein; wäre sie größer als das Sichtfeld, klebte
   nichts mehr. */
/* Kein `position: sticky` mehr (10.09.2026): der Inhalt eines Blocks laeuft
   jetzt einfach mit seinem Werk vorbei, wie die Bilder in der Hauptspalte
   auch. Das Festhalten am oberen Rand war das Stocken beim Durchscrollen. */
.dreid-halt {
  position: relative;
  display: flex;
  flex-direction: column;
}

/* ── Kategorien bei offenem Panel: schmale Fahrbahn-Leiste ──
   Früher wurde die ganze Spalte nach links hinausgeschoben und stillgelegt.
   Das war richtig, solange das Panel eine kurze Einzelansicht war — seit es
   eine dauerhaft mitlaufende Spur ist, hat man es fast immer offen und kommt
   dann an die Navigation nicht mehr heran (Noahs Rückmeldung 27.07.2026).

   Jetzt bleibt die Fahrbahn stehen: eine 48px schmale Leiste mit der Linie und
   ihren Stationsköpfen. Jeder Kopf ist weiterhin sein Werk und anklickbar; die
   Beschriftungen liegen einfach außerhalb und werden abgeschnitten. Fährt man
   mit der Maus hinein (oder landet mit der Tastatur darin), klappt sie weich
   auf die volle Breite auf.

   Sie ist dabei absolut positioniert, liegt also ÜBER dem Bildbereich statt
   neben ihm — das Aufklappen verschiebt deshalb kein einziges Bild. Der
   Bildbereich rückt nur um die 48px der eingeklappten Leiste ein. */
.sidebar {
  transition: margin-left 0.5s cubic-bezier(0.4, 0, 0.2, 1),
              width 0.4s cubic-bezier(0.4, 0, 0.2, 1),
              opacity 0.35s ease,
              box-shadow 0.4s ease,
              border-color 0.6s ease;
}

body:is(.dreid-offen, .cv-offen) .sidebar {
  position: absolute;
  top: 0; left: 0; bottom: 0;
  z-index: 130;
  width: var(--rail-breite);
  min-width: 0;
  margin-left: 0;
  /* Innenabstand in BEIDEN Zuständen gleich — sonst würde die Fahrbahn beim
     Aufklappen seitlich verrutschen statt nur breiter zu werden. */
  padding-left: 0.6rem;
  padding-right: 0;
  overflow: hidden;
  /* Deckend, weil sie über dem Bildbereich liegt. */
  background-color: #000000;
}

body.main-hell:is(.dreid-offen, .cv-offen) .sidebar { background-color: #ffffff; }

/* Aufgeklappt EXTRA BREIT (nicht --sidebar-breite). Grund steht bei den
   .subkat-titel-Regeln weiter unten: bei 215px braeche der laengste Name um,
   die Station wuerde zweizeilig und alles darunter rutschte sichtbar nach.
   Die Leiste liegt als Overlay ueber dem Bildbereich, Breite kostet hier also
   kein Layout. */
body:is(.dreid-offen, .cv-offen) .sidebar:hover,
body:is(.dreid-offen, .cv-offen) .sidebar:focus-within {
  width: var(--rail-auf-breite);
  padding-right: 1.2rem;
  overflow-y: auto;
  box-shadow: 12px 0 34px rgba(0, 0, 0, 0.5);
}

body.main-hell:is(.dreid-offen, .cv-offen) .sidebar:hover,
body.main-hell:is(.dreid-offen, .cv-offen) .sidebar:focus-within {
  box-shadow: 12px 0 34px rgba(0, 0, 0, 0.12);
}

/* Die Klickfläche einer Station bis an den linken Rand ziehen. Ohne das
   beginnt sie erst hinter dem Innenabstand (2.4rem) — also GENAU außerhalb
   der schmalen Leiste, und man träfe nur noch den 14px kleinen Stationskopf.
   Der Text bleibt durch das gleich große padding an seinem Platz. */
body:is(.dreid-offen, .cv-offen) .subkat-titel {
  margin-left: -2.4rem;
  padding-left: 2.4rem;
  /* EINGEKLAPPT ZAEHLT NUR DER ABSTAND DER KOEPFE. Ohne die feste Zeilenhoehe
     haengt er an der Textlaenge: „Photography of an unknown object" und
     „Ultrasonic Theremin" brechen zweizeilig um, und weil der Kopf oben an
     der ERSTEN Zeile sitzt, entsteht die Luecke UNTER ihnen — also mitten im
     Paket statt an dessen Grenze. Genau das sah aus wie ein Themenwechsel
     zwischen Photography und Untitled.
     1.5em ist exakt eine Zeile (line-height 1.5 der eigenen Schriftgroesse).
     Der Text wird ohnehin von `overflow: hidden` der Leiste abgeschnitten.
     Damit bleiben als einzige Luecken die von .mit-trenner — und die sitzen
     an den zwei Paketgrenzen. */
  height: 1.5em;
  /* base.css setzt border-box; ohne content-box waeren die 0.5rem Innenabstand
     IN der Zeilenhoehe und der 12px-Stationskopf (::before, top 7px) wuerde
     unten angeschnitten. */
  box-sizing: content-box;
  overflow: hidden;
}

/* Aufgeklappt bleibt die Station EINZEILIG — `height` von oben gilt weiter,
   dazu nowrap. Damit stehen die Koepfe eingeklappt und aufgeklappt an exakt
   derselben Stelle und die Formation wackelt beim Drueberfahren nicht mehr.
   (Fassung vom 10.09.2026 setzte hier `height: auto`; dann brachen die langen
   Namen wieder um und alles darunter schob sich sichtbar nach oben zur ersten
   Zeile — Noah: „wirkt wie ein bug, ist haesslich".)
   Der Preis ist die Breite: --rail-auf-breite muss den laengsten Namen in eine
   Zeile lassen, sonst schneidet `overflow: hidden` ihn ab. */
body:is(.dreid-offen, .cv-offen) .sidebar:hover .subkat-titel,
body:is(.dreid-offen, .cv-offen) .sidebar:focus-within .subkat-titel {
  white-space: nowrap;
}

/* Die römischen Zahlen (3. Ebene) nur unsichtbar machen, nicht ausblenden —
   ihr Platz muss erhalten bleiben, sonst rutschen die Stationen darunter beim
   Aufklappen nach unten. */
body:is(.dreid-offen, .cv-offen) .subsubkat.offen { visibility: hidden; }
body:is(.dreid-offen, .cv-offen) .sidebar:hover .subsubkat.offen,
body:is(.dreid-offen, .cv-offen) .sidebar:focus-within .subsubkat.offen { visibility: visible; }

/* Platz für die eingeklappte Leiste. Die Bilder behalten ihre Größe
   (--breite), sie rücken nur nach. */
body.dreid-offen .bildbereich { margin-left: var(--rail-breite); }

/* Oben die 360°-Ansicht, darunter das drehbare Modell.
   Fehlt die 360°-Ansicht, bleibt ihr Platz frei (.leer, siehe main-3d.js)
   — damit jeder Block gleich aufgebaut ist und das Modell nicht bei manchen
   Werken auf volle Höhe wächst. */
.dreid-360,
.dreid-buehne {
  min-height: 0;
  position: relative;
}

.dreid-360[hidden],
.dreid-buehne[hidden] { display: none; }

/* Die Orbit-Frames sind 16:9. Genau dieses Verhältnis geben — sonst bliebe
   über und unter dem Bild toter schwarzer Raum, der dem Modell fehlt.
   Das padding legt den schmalen Rahmen um das Bild — object-fit: contain
   passt es darin ein, ohne es zu verzerren. */
.dreid-360 {
  flex: none;
  aspect-ratio: 16 / 9;
  padding: 14px;
  /* Die 360°-Ansicht sitzt tiefer, nicht direkt unter dem Header — damit sie
     und das Modell darunter optisch gleich viel Gewicht bekommen. */
  margin-top: calc(7vh + var(--tiefer, 0px));
  border-bottom: 1px solid #1f1c18;
  overflow: hidden;
}

/* 360°-Ansicht UND Modell einzelner Werke tiefer setzen (Noah 17.09.2026:
   „schiebe mal die 360 und 3d von visualizer weiter nach unten, daten bleiben
   erstmal gleich. selbiges gilt für Teremin"). Das Modell zieht mit, weil
   main-3d.js (richteAus) den Versatz der 360°-Ansicht misst und seine
   Unterkante um genau diesen Betrag tiefer setzt. Werkangabe und Text haengen
   an den Bildern und bleiben davon unberuehrt. Groesser = tiefer.
   `--modell-tiefer` rueckt NUR das Modell zusaetzlich ab, fuer mehr Abstand zur
   360°-Ansicht („zieh die 3d views noch weiter runter damit mehr abstand zu
   360 entsteht", Noah 17.09.2026).
   Achtung: `--modell-tiefer` kommt ZUSAETZLICH zu `--tiefer` — wer die
   360°-Ansicht hoeher setzt, zieht das Modell mit hoch, sofern man
   `--modell-tiefer` nicht um denselben Betrag erhoeht. */
.dreid-block[data-fuer="skulp-provider"]   { --tiefer: 10vh; --modell-tiefer: 30vh; }
.dreid-block[data-fuer="skulp-theremin"]   { --tiefer: 12vh; --modell-tiefer: 22vh; }
.dreid-block[data-fuer="skulp-visualizer"] { --tiefer: 14vh; --modell-tiefer: 18vh; }
.dreid-block[data-fuer="skulp-screen"]     { --tiefer: 10vh; --modell-tiefer: 28vh; }

/* Objekte ohne Orbit-Frames: der Platz bleibt reserviert, aber ohne Rahmen
   und Trennlinie, damit dort kein leerer Kasten steht. */
.dreid-360.leer {
  padding: 0;
  border-bottom: none;
}

/* ── Dreh-Hinweis „drag to rotate" ──
   Kleiner englischer Hinweis über 360°-Ansicht und Modell. pointer-events:none,
   damit er das Ziehen nicht abfängt; verschwindet (.weg) beim ersten Anfassen. */
.dreh-hinweis {
  position: absolute;
  bottom: 12px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 3;
  pointer-events: none;
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.56rem;
  letter-spacing: 0.22em;
  color: rgba(255, 255, 255, 0.55);
  background: rgba(10, 9, 5, 0.5);
  padding: 3px 9px;
  border-radius: 2px;
  white-space: nowrap;
  transition: opacity 0.5s ease;
}
.dreh-hinweis.weg { opacity: 0; }

/* ── Werk-Info unten im Block (//Name//Jahr//Material//Maße//) ──
   Jeder Block trägt seine eigene; Werke ohne Angaben bekommen gar keinen Fuß. */
.dreid-fuss {
  flex: none;
  border-top: 1px solid #1f1c18;
  padding: 0.85rem 14px 1.05rem;
}

/* Steht die Angabe allein im Block (Werke ohne Modell und ohne Fließtext:
   Photography, Untitled, Sandstein, drei), wäre die Linie ein Strich über
   einer einzelnen Zeile, ohne etwas zu trennen. */
.dreid-halt.nur-text .dreid-fuss { border-top: none; }

.dreid-info {
  font-family: var(--font);
  font-weight: 200;
  /* 27.07.2026 um rund 5px vergrößert (0.62rem ≈ 10px → 0.94rem ≈ 15px) und
     zentriert — Noah: „Material- und Titelangabe von den ganzen 3D-Modellen
     größer, außerdem mittig zentriert an der Stelle, wo sie gerade ist." */
  font-size: 0.94rem;
  letter-spacing: 0.05em;
  line-height: 1.55;
  text-align: center;
  color: rgba(255, 255, 255, 0.62);
  word-break: break-word;
}
.dreid-info[hidden] { display: none; }

/* ── Fließtext zum Werk (aus .werk-text im Bildbereich kopiert) ──
   Auf der Seite selbst ist die Vorlage ausgeblendet; hier im Fenster ist sie
   der Inhalt. Sektionen ohne 3D-Objekt bestehen NUR daraus — dort bekommt der
   Text den ganzen Block und steht mittig. */
.dreid-werktext {
  /* Darf SCHRUMPFEN und dann in sich scrollen (flex-shrink 1 + min-height 0).
     Ohne das ragte der Text oben aus dem Block heraus und seine ersten Zeilen
     verschwanden hinter dem Kopf der Seite — im Screenshot fing er mitten im
     Satz an. */
  flex: 0 1 auto;
  min-height: 0;
  padding: 1.4rem 18px 0.6rem;
  font-family: var(--font);
  font-weight: 200;
  font-size: 0.76rem;
  line-height: 1.8;
  color: rgba(255, 255, 255, 0.6);
  overflow-y: auto;
}

/* Bilder IM Info-Fenster (Point Cloud, seit 10.09.2026). Die Spalte ist rund
   390px breit — die vier Aufnahmen nebeneinander waeren dort briefmarkengross,
   also zwei mal zwei. Die `--spalten`-Gewichte der Galerie gelten hier nicht,
   deshalb wird `.bild-grid.reihe` bewusst ueberschrieben. */
.dreid-werktext .bild-grid.reihe {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0.5rem;
  width: 100%;
  margin: 0 0 1.6rem;
}

.dreid-werktext .bild-item img {
  width: 100%;
  height: auto;
  display: block;
  cursor: zoom-in;
}

.dreid-werktext p { margin-bottom: 1rem; }
.dreid-werktext p:last-child { margin-bottom: 0; }

/* Folgt der Text auf die Werkangabe, stoßen deren Unterkante und sein eigener
   Vorlauf aufeinander — zusammen 2,45rem, ein Loch mitten im Block. Der Fuß
   bringt den Abstand schon mit, hier genügt ein Rest. */
.dreid-fuss + .dreid-werktext { padding-top: 0.55rem; }

.dreid-werktext .werk-text-titel {
  color: rgba(255, 255, 255, 0.85);
  line-height: 1.6;
  margin-bottom: 1.3rem;
}

.dreid-halt.nur-text {
  justify-content: center;
  padding: 0 0.5rem;
}

/* MESH DEFORM: Fliesstext groesser (Noah 10.09.2026). Nur im Panel des
   Rechners — am Handy haengen die Bloecke in `.werk-klapp` und behalten dort
   Groesse und Stand, ausdruecklich so gewuenscht.

   SEINE POSITION STEHT NICHT HIER. Der Block haengt seit 10.09.2026 an der
   MITTE der ganzen Mesh-Deform-Gruppe statt an seiner eigenen Sektion; das
   rechnet main-3d.js aus (Konstante TEXT_ANTEIL, siehe misstGleis). Innerhalb
   seines Blocks steht der Text mittig, wie jeder andere reine Textblock. */
.dreid-panel .dreid-werktext {
  font-size: 0.95rem;
  /* 1.65 statt 1.75: der Text ist 68 % der Blockhoehe hoch, jede Zeilenhoehe
     mehr nimmt ihm genau so viel Tiefe weg. Die Schriftgroesse bleibt. */
  line-height: 1.65;
}
.dreid-halt.nur-text .dreid-werktext { max-height: 100%; }

/* Im weiten Panel (CV offen) ist .dreid-halt sonst ein zweispaltiges Raster für
   360°-Ansicht und Modell. Ein reiner Textblock hat davon nichts — er stünde
   gequetscht in der ersten Spalte. Also zurück auf eine einzelne, mittige
   Spalte mit ruhiger Zeilenlänge. */
body.cv-offen.dreid-offen .dreid-halt.nur-text {
  display: flex;
  /* padding-inline, NICHT die Kurzform `padding: 0 12%`. Die setzte auch oben
     und unten auf 0 und warf damit den Vorlauf des Point-Cloud-Texts weg —
     bei offenem CV sprang er wieder ganz nach oben. */
  padding-inline: 12%;
}

/* Das Modell. FESTE Hoehe statt `flex: 1` (10.09.2026): seit der Block die
   volle Werkhoehe hat, wuerde `flex: 1` es ueber mehrere Bildschirme strecken.
   `flex-basis` mit Schrumpfen erlaubt, damit kurze Werke nicht ueberlaufen.
   Der Vorlauf setzt es unter die 360°-Ansicht ab — die bleibt, wo sie ist:
   ihre Oberkante bildet mit den Bildern der Hauptspalte eine Linie, und genau
   das soll so bleiben (Noah 10.09.2026). */
.dreid-buehne {
  flex: 0 1 38vh;
  min-height: 140px;
  /* „einen Ticken weiter nach unten, mehr Abstand zur 360-Ansicht, aber nicht
     viel" (Noah 10.09.2026) — von 7vh auf 10vh. */
  margin-top: calc(10vh + var(--modell-tiefer, 0px));
}

/* Werkangabe ans UNTERE Ende des Werks. `margin-top: auto` schiebt sie in
   einem Flex-Stapel so weit nach unten wie moeglich — bei einem langen Werk
   also weit, bei einem kurzen (Dispenser: ein einziges Bild) gar nicht. Damit
   regelt sich „mehr Luft" von selbst nach der Laenge der Arbeit, ohne feste
   Zahl je Werk. */
.dreid-fuss { margin-top: auto; }

/* ── Block mit Modell UND Fliesstext (Ultrasonic Theremin) ────────────────
   Der Text folgt der Werkangabe und rutscht mit ihr nach unten. Ein Deckel auf
   seine Hoehe (bis 10.09.2026 32 %) ist nicht mehr noetig: seit der Block die
   volle Werkhoehe hat, ist Platz genug. */
.dreid-halt.mit-text .dreid-werktext {
  flex: 0 0 auto;
  /* Fester Abstand zur Werkangabe darueber. Die Angabe wird an der Unterkante
     des 2. Bildes ausgerichtet (main-3d.js, richteAus), der Text zieht hier
     einfach mit — er haengt an ihr, nicht an einem eigenen Bild. */
  margin-top: 4vh;
}

/* Der Spinner selbst kommt aus spinner.js — hier nur die Einpassung */
.dreid-360 .spinner {
  width: 100%;
  height: 100%;
  cursor: grab;
  touch-action: pan-y;   /* senkrecht wischen scrollt weiter die Seite */
}

.dreid-360 .spinner.greifen { cursor: grabbing; }

/* Früher stand hier ein `transform: scale(1.32) translateY(7%)`, um das Objekt
   im Orbit-Render größer und mittiger erscheinen zu lassen. Das war der Grund
   für die abgeschnittenen Ränder: der Zoom vergrößert über das Feld hinaus,
   die Verschiebung schiebt zusätzlich nach unten heraus — und das
   overflow:hidden des Feldes hat den Überstand weggeschnitten.
   Jetzt bleibt das Bild vollständig sichtbar und bekommt über das padding des
   Feldes ringsum einen schmalen Rahmen. */
.dreid-360 .spinner-img,
.dreid-360 .spinner-video {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
  user-select: none;
  -webkit-user-drag: none;
}

.dreid-buehne {
  cursor: grab;
  /* Nur die Grundeinstellung fürs Stylesheet. Den geltenden Wert setzt
     main-3d.js INLINE — OrbitControls schreibt im Konstruktor selbst inline
     `touch-action: none` hinein, und dagegen kommt keine Regel von hier an
     (siehe Notiz vom 27.07.2026). Am Handy wechselt der Wert zwischen `auto`
     (Modell schläft, Wisch gehört der Seite) und `none` (Modell ist wach). */
  touch-action: pan-y;
}

.dreid-buehne:active { cursor: grabbing; }

/* ── HANDY: das wache Modell (Noah 31.07.2026, „erst antippen, dann drehen") ──
   Solange ein Modell wach ist, gehört jeder Wisch über ihm dem Objekt und die
   Seite steht still. Das MUSS man sehen, sonst wirkt genau das wieder wie ein
   Fehler. Ein feiner heller Rahmen und ein Hauch Aufhellung reichen — die
   Seite kennt sonst keine Farbe, also bekommt auch dieser Zustand keine. */
.dreid-buehne.wach {
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.28);
  background: rgba(255, 255, 255, 0.025);
}

.dreid-buehne.wach .dreh-hinweis {
  color: rgba(255, 255, 255, 0.85);
  background: rgba(10, 9, 5, 0.75);
}

/* Ladehinweis, solange DAS MODELL DIESES BLOCKS geholt wird. Das Canvas ist
   für alle Blöcke gemeinsam, also muss der Hinweis am einzelnen Block hängen. */
.dreid-buehne.laedt::after {
  content: 'loading';
  position: absolute;
  top: calc(50% - 14px); left: 50%;
  transform: translate(-50%, -50%);
  font-size: 0.6rem;
  letter-spacing: 0.24em;
  color: #6a635a;
  pointer-events: none;
}

/* Ladebalken (Three.js + Modell laden). Unbestimmt animiert — der Import der
   Bibliothek liefert keinen Byte-Fortschritt, deshalb ein laufender statt eines
   prozentgenauen Balkens. Sichtbar nur, solange das Panel .laedt trägt. */
.dreid-ladebalken {
  position: absolute;
  left: 20%; right: 20%;
  top: 50%;
  height: 3px;
  background: rgba(255, 255, 255, 0.1);
  border-radius: 2px;
  overflow: hidden;
  opacity: 0;
  transition: opacity 0.25s ease;
  pointer-events: none;
  z-index: 4;
}
.dreid-panel.laedt .dreid-ladebalken { opacity: 1; }
.dreid-ladebalken span {
  position: absolute;
  top: 0; bottom: 0;
  width: 38%;
  background: var(--fahrbahn);
  border-radius: 2px;
  animation: dreid-laden 1.1s ease-in-out infinite;
}
@keyframes dreid-laden {
  0%   { left: -38%; }
  100% { left: 100%; }
}

/* ── 9. Mobil (Handy): Sidebar hinter Burger-Menü ── */

/* Burger-Knopf im Header — auf dem Desktop unsichtbar */
.burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 30px;
  height: 30px;
  /* Ein Zeichen hat eine Grundlinie, drei Striche nicht — der Knopf kann in der
     Grundlinien-Reihe des Headers also nicht mitmachen und wird stattdessen
     mittig in den Inhaltsbereich gesetzt. Der ist dort genau eine Zeile hoch,
     seine Mitte liegt damit rechnerisch 0.33em über der Grundlinie; die
     optische Mitte der Schrift (halbe Versalhöhe, 0.35em) fällt bis auf einen
     Fünftelpixel damit zusammen. Der 30px-Kasten ist absichtlich größer als
     die Striche — er ist die Tippfläche und darf ruhig überstehen. */
  align-self: center;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

.burger span {
  display: block;
  width: 22px;
  height: 1px;
  background: #eee;
}

body.main-hell .burger span { background: #111; }

/* Abdunkelung hinter der offenen Sidebar */
.sidebar-schleier {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  z-index: 250;
}

@media (max-width: 800px) {
  .burger { display: flex; }

  /* font-size gehört an den Header, nicht an .site-name: die padding-Rechnung
     oben (`… - 1em`) muss der Namensgröße folgen, sonst sitzt die ganze Zeile
     hier falsch. Der Wert selbst ist unverändert. */
  .site-header {
    padding: calc(64px - 1px - 0.8rem - 1em) 1rem 0.8rem;
    gap: 0.8rem;
    font-size: 0.8rem;
  }
  .site-name { letter-spacing: 0.15em; white-space: nowrap; }
  .contact-link { font-size: 0.6rem; letter-spacing: 0.2em; white-space: nowrap; }

  /* Sidebar wird zum Overlay: schiebt sich von links über die Seite */
  .sidebar {
    position: fixed;
    top: 0; left: 0; bottom: 0;
    height: auto;
    width: min(75vw, 300px);
    z-index: 300;
    background: #050505;
    padding: 4.5rem 1.5rem 3rem;
    transform: translateX(-100%);
    transition: transform 0.3s ease, background-color 0.6s ease;
    /* Auf kleinen Geräten kann die Kategorienliste länger sein als der
       Bildschirm (GEMESSEN 360×640: 800px Inhalt in 640px Höhe).
       Bei genug Platz ändert `auto` nichts. */
    overflow-y: auto;
    /* NUR SENKRECHT (Noah 31.07.2026: „warum ist Kategorieliste seitlich
       scrollbar, sie soll einfach nur nach unten scrollbar sein").
       CSS koppelt die beiden Achsen: steht eine auf `auto`, wird die andere
       aus `visible` still zu `auto` gemacht — die Liste war also seitlich
       scrollbar, sobald IRGENDETWAS über den rechten Rand ragte (damals der
       Tropfen des „3D & Info"-Knopfes, der bis 01.08.2026 hier unten im Menü
       saß; gemessen 390px: scrollWidth 487 statt 292). Diese Zeile ist die
       Zusage, dass es auch bei künftigen Überhängen beim reinen
       Hoch-/Runterscrollen bleibt. */
    overflow-x: hidden;
  }

  .sidebar.offen { transform: translateX(0); }

  body.main-hell .sidebar { background: #ffffff; }


  /* CV/Contact am Handy: als fixes Overlay unter dem Header über die ganze
     Seite legen (die 28vw wären zu schmal; als Flex-Kind bliebe außerdem ein
     Streifen der Hauptseite stehen). Der Zurück-Knopf liegt darüber und
     schließt es. */
  body.cv-offen .cv-panel {
    position: fixed;
    top: 64px;
    left: 0;
    right: 0;
    bottom: 0;
    width: auto;
    z-index: 150;   /* über dem Inhalt, unter dem Header (200) */
    border-left: none;
    padding-top: 1.5rem;
  }

  /* Der Aufklapp-Knopf für die Werk-Info ist entfallen: seit jedes Werk einen
     eigenen Block in der Spur hat, steht die Angabe unten in ihrem Block und
     stört dort nicht — sie scrollt mit dem Werk weg. */

  .sidebar-schleier.offen { display: block; }

  /* Mehr Platz für die Bilder: volle Breite statt 62% */
  .bildbereich { padding: 5.5rem 1.2rem 4rem; }

  .bild-item img,
  .bild-item.gross img,
  .bild-item.quer img,
  .bild-item video,
  .bild-item.gross video { width: 100%; }

  /* Vierer-Reihe bricht auf 2×2 um — vier nebeneinander wären auf dem Handy
     nur noch briefmarkengroß */
  .bild-grid.reihe { grid-template-columns: 1fr 1fr; }

  /* Mobil ist die 68%-Flucht hinfällig: dort sind alle Bilder 100% breit */
  .bild-grid.buendig { width: 100%; }

  /* Kapitel-Absätze auf dem Handy kleiner — sonst scrollt man durch Leere */
  .serie.absatz { padding-top: clamp(13rem, 42vh, 22rem); }
  .serie.absatz-gross { padding-top: clamp(17rem, 58vh, 30rem); }

  /* Bildpaare untereinander statt nebeneinander */
  .bild-paar {
    flex-direction: column;
    align-items: center;
    gap: 2rem;
    width: 100%;
  }

  .bild-paar .bild-item img {
    width: 100%;
    height: auto;
  }
}


/* ═══════════════════════════════════════════════════════════════════════════
   10. HANDY: 3D & INFO KLAPPEN AM WERK SELBST AUS
   (Noah 01.08.2026: „3D und Info der jeweiligen Objekte sollen verschoben
   werden zu ihrem passenden Partner auf der Hauptseite … mit Drei-Punkte-Knopf
   zum Ausklappen, somit wird der Button hinfällig und alles ist auf einer
   Seite.")

   Am Rechner bleibt alles wie gehabt: die zweite Scrollspur rechts, die
   „3D & Info"-Leiste mit ihrem Tropfen, der gekoppelte Gleichlauf. Knopf und
   Klappfach stehen dort zwar im HTML, sind aber aus.

   Am Handy hängt stattdessen jeder Block in seiner eigenen Sektion, unter dem
   letzten Bild, und der „3D & Info"-Knopf dort klappt ihn nach unten auf. Es
   ist immer nur EINER offen — das gemeinsame
   Canvas wird schlicht in das gerade offene Fach umgehängt (main-3d.js,
   zeichneInline). Dadurch scrollt das Modell nativ mit seinem Kasten mit;
   ein nachgeführtes Scissor-Rechteck könnte beim Scrollen hinterherhinken.
   ═══════════════════════════════════════════════════════════════════════════ */

/* Am Rechner beide unsichtbar — dort führt die Leiste ins Panel. */
.werk-mehr,
.werk-klapp { display: none; }

/* Im Klappfach ist das Canvas ein gewöhnliches Kind der Bühne und füllt sie
   ganz. Die Grundregel weiter oben (position:absolute, top:64px) gilt für das
   Panel des Rechners und muss hier weichen. */
.dreid-buehne > .dreid-canvas {
  position: absolute;
  inset: 0;
  top: 0;
  width: 100%;
  height: 100%;
  z-index: 0;
}

@media (max-width: 800px) {

  /* ── Panel und Leiste sind am Handy vollständig abgemeldet ──
     Ihre alten Handy-Regeln (Panel als volles Overlay mit translateX, die
     Leiste als breiter Knopf unten im Menü, der waagerechte Tropfen und die
     Rücknahmen für `body.dreid-offen`) sind am 01.08.2026 restlos gelöscht
     worden — rund 200 Zeilen. Der Rechner-Teil weiter oben ist unberührt. */
  .dreid-panel,
  .dreid-leiste,
  .dreid-leiste[hidden] { display: none; }

  /* ── Der „3D & Info"-Knopf am Fuß des Werks ──
     Bis 01.08.2026 waren das drei Punkte oben in der Kopfzeile. Die Zeichen
     (Würfel mit Achsen = das Modell, i = der Text) sagen jetzt vorab, WAS im
     Fach liegt, und der Knopf steht unter dem letzten Bild statt darüber
     (main-3d.js hängt ihn ans Ende der Sektion).

     Gezeichnet statt geschrieben, damit die Zeichen auf jedem Gerät gleich
     aussehen — die Pfade stehen in main-3d.js. */
  /* ── Der Kasten (Noah 02.08.2026, zurückgenommen am selben Tag: „ist viel zu
     groß … unauffälliger, aber trotzdem so dass man drauf aufmerksam wird") ──

     Der erste Wurf spannte über den ganzen Bildschirm mit 0,5 cm Rand und war
     56 px hoch — ein Balken, der mit den Bildern um Aufmerksamkeit stritt. Jetzt
     zieht er sich auf die Breite seines Inhalts zusammen und steht rechts unter
     der Jahreszahl, wo das Auge nach dem letzten Bild ohnehin ankommt.

     Auffällig wird er nicht mehr über die Größe, sondern über BEWEGUNG: einmal,
     wenn er zum ersten Mal in den Blick scrollt, atmet der Rahmen kurz auf
     (.werk-mehr.winkt, gesetzt von main-3d.js). Ein einzelner Wink an genau der
     Stelle, an der man ohnehin hinsieht, trägt weiter als ein Dauerbalken —
     und er stört beim zweiten Mal nicht mehr, weil er nur einmal läuft. */
  .werk-mehr {
    /* `flex`, NICHT `inline-flex`: das `margin-left: auto` weiter unten schiebt
       den Kasten nach rechts unter die Jahreszahl — und automatische Ränder
       wirken nur an Boxen auf Blockebene. Als `inline-flex` rechnet der Browser
       sie zu 0 und der Kasten klebte links. */
    display: flex;
    align-items: center;
    /* Rechtsbündig unter der Jahreszahl (.bild-datum) */
    justify-content: center;
    gap: 9px;
    box-sizing: border-box;
    width: fit-content;
    margin: 0.8rem 0 0 auto;
    padding: 7px 11px;
    background: none;
    /* Dunkles Schema (falls es zurückkommt) — ein heller Rahmen wie in
       .main-hell wäre dort ein leuchtender Kasten. */
    border: 1px solid #2b2825;
    border-radius: 2px;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
    /* Die Zeichen erben die Farbe (stroke: currentColor). */
    color: #8d8d8d;
    transition: color 0.25s ease, border-color 0.25s ease;
  }

  /* Die beiden Zeichen bleiben als Paar zusammen — enger beieinander als der
     Abstand zum Strich, damit sie als EIN Gegenstand gelesen werden. */
  .werk-mehr-mitte {
    display: flex;
    align-items: center;
    gap: 8px;
  }

  /* Der Trennstrich zwischen Inhalt und Funktion. Bewusst kürzer als die
     Zeichen hoch sind (14px gegen 19px) und deutlich blasser als sie: er soll
     gliedern, nicht als drittes Zeichen mitgelesen werden. */
  .werk-mehr-strich {
    flex: none;
    width: 1px;
    height: 14px;
    background: currentColor;
    opacity: 0.45;
  }

  .werk-mehr-mitte svg {
    display: block;
    width: 19px;
    height: 19px;
  }

  /* Flach und schmal: der Pfeil begleitet, er führt nicht. */
  .werk-mehr .werk-pfeil {
    display: block;
    flex: none;
    width: 14px;
    height: 7px;
    transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
  }

  /* Offen zeigen beide nach oben — dieselbe Geste rückwärts heißt „zuklappen". */
  .werk-mehr[aria-expanded="true"] .werk-pfeil { transform: rotate(180deg); }

  body.main-hell .werk-mehr {
    color: #9a9a9a;
    border-color: #dcdcdc;
  }

  /* Der einmalige Wink. Rahmen und Zeichen laufen zweimal von ruhig nach
     deutlich und zurück — kein Blinken, sondern ein Atemzug. Danach nimmt
     main-3d.js die Klasse wieder ab, damit später nichts nachzuckt. */
  @keyframes werk-wink {
    0%, 100% { border-color: #dcdcdc; color: #9a9a9a; }
    50%      { border-color: #8a8a8a; color: #3a3a3a; }
  }

  body.main-hell .werk-mehr.winkt {
    animation: werk-wink 1.15s ease-in-out 2;
  }

  /* Wer im Betriebssystem „Bewegung reduzieren" eingestellt hat, bekommt keinen
     Wink, sondern von Anfang an den kräftigeren Rahmen — dasselbe Ziel, ohne
     Animation. */
  @media (prefers-reduced-motion: reduce) {
    body.main-hell .werk-mehr.winkt {
      animation: none;
      border-color: #b0b0b0;
      color: #6a6a6a;
    }
  }

  /* Offen: die Zeichen werden hell — dieselbe Sprache wie die gefüllte Station
     in der Kategorien-Fahrbahn. Die Strichstärke bleibt, ein dickerer Strich
     ließe die Mitte des Würfels zulaufen. */
  .werk-mehr[aria-expanded="true"] {
    color: #ddd;
    border-color: #4a453e;
  }
  body.main-hell .werk-mehr[aria-expanded="true"] {
    color: #111;
    border-color: #999;
  }

  /* ── Das Klappfach ──
     BEWUSST OHNE HÖHEN-ANIMATION. Eine Höhe über 0,4 s zu animieren hieße, die
     sehr lange Seite in jedem Bild dieser Animation neu umzubrechen — genau das
     war am 27.07.2026 die Ursache des Ruckelns beim Header (siehe .bildbereich).
     Die Höhe springt deshalb, und nur der Inhalt blendet weich auf. */
  .werk-klapp { display: none; }

  .werk-klapp.offen {
    display: block;
    /* Kein Abstand nach unten mehr: das Fach ist jetzt das LETZTE Element der
       Sektion, darunter liegen schon deren 5rem (.serie margin-bottom). Oben
       genügen 0,6rem, weil die 14px Polster des Knopfes darüber mitzählen. */
    margin: 0.6rem 0 0;
  }

  .werk-klapp.offen .dreid-block {
    animation: werk-auf 0.34s cubic-bezier(0.4, 0, 0.2, 1) both;
  }

  @keyframes werk-auf {
    from { opacity: 0; transform: translateY(-10px); }
    to   { opacity: 1; transform: none; }
  }

  /* Im Fach gelten weder die absolute Lage der Spur noch das Kleben (sticky);
     die Maße, die messe() für die Spur setzt, räumt platziereBloecke() weg. */
  .werk-klapp .dreid-block {
    position: static;
    left: auto;
    right: auto;
  }

  .werk-klapp .dreid-halt {
    position: static;
    display: flex;
    flex-direction: column;
    /* Eigener dunkler Kasten: die Seite ist im oberen Teil weiß, das 3D-Fenster
       war aber immer schon dunkel. Es liest sich als Schaukasten im Text. */
    background-color: #0a0905;
    border: 1px solid #1f1c18;
  }

  body.main-hell .werk-klapp .dreid-halt { border-color: #ddd9d2; }

  /* Der 7vh-Vorlauf gehört zum Panel, das oben am Header beginnt. Im Fach sitzt
     die 360°-Ansicht direkt an der Oberkante. */
  .werk-klapp .dreid-360 {
    margin-top: 0;
    border-bottom: 1px solid #1f1c18;
  }
  /* Werke ohne 360°-Ansicht: IM FACH FÄLLT DER PLATZ WEG.
     Im Panel blieb er reserviert, damit der Aufbau beim Durchscrollen nicht
     von Werk zu Werk springt. Hier klappt jedes Werk für sich auf — ein
     leeres schwarzes Feld über dem Modell wäre nur eine Lücke (gesehen bei
     „Screen": 197px Schwarz vor dem Objekt). */
  .werk-klapp .dreid-360.leer { display: none; }

  /* Feste Bühnenhöhe: im Panel bekam das Modell den Rest der Bildschirmhöhe,
     hier gibt es keinen Rest — das Fach ist so hoch wie sein Inhalt. Rund
     70 % der Breite ist etwa so viel Platz wie die 360°-Ansicht darüber
     (16:9 = 56 %), das Modell bleibt damit das größere von beiden. */
  .werk-klapp .dreid-buehne {
    flex: none;
    height: min(70vw, 42vh);
    min-height: 200px;
  }

  /* Reine Textblöcke (Point Cloud) haben keine Bühne — dort ist der Text der
     Inhalt und braucht nur Luft. Im Panel stand er mittig in voller Höhe. */
  .werk-klapp .dreid-halt.nur-text {
    justify-content: flex-start;
    padding: 0.4rem 0.2rem;
  }
  .werk-klapp .dreid-werktext {
    overflow-y: visible;
    max-height: none;
  }

  .werk-klapp .dreid-fuss { padding: 0.75rem 14px 0.85rem; }

  /* Werkangabe (//Titel//Jahr//Material//Maße) am Handy KLEINER.
     Die 0.94rem stammen vom 27.07.2026, als die Angabe nur im breiten
     Panel des Rechners stand — im schmalen Klappfach wirkt derselbe Grad zu
     groß und die Zeile bricht mehrfach um (Noah 02.08.2026: „die Maße, Jahr,
     Titel usw. sind am Handy zu groß"). Am Rechner bleibt es bei 0.94rem. */
  .werk-klapp .dreid-info {
    font-size: 0.7rem;
    letter-spacing: 0.04em;
    line-height: 1.5;
  }

  /* Der Fließtext daneben zieht mit, sonst stünde er plötzlich größer da als
     die Werkangabe darunter. */
  .werk-klapp .dreid-werktext { font-size: 0.72rem; line-height: 1.7; }

  /* Der schwebende „tap to rotate“-Hinweis über der Bühne im selben Maß */
  .werk-klapp .dreh-hinweis { font-size: 0.5rem; letter-spacing: 0.18em; }
}
