/* ==========================================================================
   TOMORROWS FILMWORK — Design System
   Farbwelt: Warm-Creme auf Tiefschwarz
   Typografie: Syne (Anzeige) / Archivo (Lesetext) / Azeret Mono (technisch)

   Zur Schriftwahl: Syne wurde für ein Kunstzentrum entworfen und läuft in
   den fetten Schnitten ungewöhnlich breit, während die mageren schmal
   bleiben. Genau dieser Sprung trägt hier die Gestaltung — das
   hervorgehobene Wort in einer Überschrift steht mager gegen den fetten
   Rest, statt schräg gegen aufrecht. Eine Kursive hat Syne nicht.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. SCHRIFTEN — selbst gehostet

   Die Dateien liegen in assets/fonts/, geladen von Google Fonts und hier
   unverändert abgelegt. Kein Aufruf mehr nach außen: Die IP-Adresse der
   Besucher bleibt beim eigenen Server, und die Seite lädt schneller, weil
   die zweite Verbindung wegfällt.

   Pro Familie zwei Zeichenvorräte: "latin" deckt Deutsch mit Umlauten ab,
   "latin-ext" die selteneren Zeichen (etwa im Namen eines isländischen
   Drehorts). Der Browser lädt nur, was auf der Seite tatsächlich vorkommt —
   unicode-range regelt das. Syne ist eine variable Schrift: eine Datei
   für alle Stärken von 400 bis 800.

   Neue Stärke nötig? Dieselben Familien unter fonts.google.com laden, die
   woff2-Datei hierher legen, einen @font-face-Block dazuschreiben.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/archivo-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/archivo-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/archivo-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/archivo-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Azeret Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/azeret-mono-400-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Azeret Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../fonts/azeret-mono-400-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Azeret Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/azeret-mono-500-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Azeret Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("../fonts/azeret-mono-500-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Syne";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/syne-400-800-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Syne";
  font-style: normal;
  font-weight: 400 800;
  font-display: swap;
  src: url("../fonts/syne-400-800-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* Farben — dunkle Basis */
  --bg:            #0a0908;
  --bg-raised:     #131110;
  --cream:         #f2e8d5;
  --cream-dim:     rgba(242, 232, 213, 0.62);   /* Fließtext — 6.2:1 */
  --cream-mute:    rgba(242, 232, 213, 0.50);   /* kleinste Textfarbe — 4.6:1 */
  --cream-faint:   rgba(242, 232, 213, 0.28);   /* 2.3:1 — NUR dekorativ, nie für Text */
  --line:          rgba(242, 232, 213, 0.14);
  --line-strong:   rgba(242, 232, 213, 0.28);

  /* Farben — helle Fläche (Split-Screen Detailseiten) */
  --paper:         #f7f4ec;
  --ink:           #12100e;
  --ink-dim:       rgba(18, 16, 14, 0.64);      /* Fließtext — 5.6:1 */
  --ink-faint:     rgba(18, 16, 14, 0.60);      /* kleinste Textfarbe — 4.6:1 */
  --line-ink:      rgba(18, 16, 14, 0.16);

  /* Schrift — abgeleitet aus der Wortmarke: fette, leicht schmal laufende
     Grotesk mit Neigung. Archivo hat eine Breitenachse und kommt dem Logo
     als Web-Schrift am nächsten. */
  --font-display: "Syne", "Helvetica Neue", Arial, sans-serif;
  --font-ui:      "Archivo", -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif;
  --font-mono:    "Azeret Mono", ui-monospace, "SFMono-Regular", "Consolas", monospace;

  /* Abstände — 8px-Rhythmus */
  --space-3xs: 4px;
  --space-2xs: 8px;
  --space-xs:  12px;
  --space-sm:  16px;
  --space-md:  24px;
  --space-lg:  40px;
  --space-xl:  64px;
  --space-2xl: 96px;
  --space-3xl: 144px;

  /* Layout */
  --gutter:      clamp(20px, 5vw, 64px);
  --max-width:   1600px;
  --header-h:    clamp(64px, 9vh, 96px);
  --footer-h:    clamp(52px, 7vh, 72px);

  /* Motion */
  --ease-out:    cubic-bezier(0.16, 1, 0.3, 1);
  --ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --dur-fast:   180ms;
  --dur-base:   420ms;
  --dur-slow:   760ms;

  /* Ebenen */
  --z-scale:   1;
  --z-content: 10;
  --z-header:  60;
  --z-menu:    55;   /* bewusst UNTER dem Header: der Schließen-Button muss klickbar bleiben */
  --z-overlay: 100;
}

/* --------------------------------------------------------------------------
   2. RESET
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

/* Das hidden-Attribut muss stärker sein als jedes display aus diesem
   Stylesheet. Sonst blieben Elemente sichtbar, die JavaScript ausblendet —
   etwa gefilterte Projektkarten oder der Play-Knopf über einem laufenden
   Video. Beides ist genau so passiert; deshalb steht die Regel hier oben. */
[hidden] { display: none !important; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--cream);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.6;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, video, svg { display: block; max-width: 100%; }
img, video { height: auto; }

a { color: inherit; text-decoration: none; }

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

::selection { background: var(--cream); color: var(--bg); }

/* --------------------------------------------------------------------------
   3. TYPOGRAFIE
   -------------------------------------------------------------------------- */
/* Überschriften in der Logo-Sprache: fett, geneigt, schmal laufend,
   in Versalien. Der Buchstabenabstand wird dabei bewusst zusammengezogen —
   sonst wirkt Versalsatz löchrig. */
.display {
  font-family: var(--font-display);
  font-weight: 800;
  line-height: 0.94;
  letter-spacing: -0.02em;
  text-transform: uppercase;
  text-wrap: balance;
}
/* Syne kennt keine Kursive. Der Akzent kommt deshalb aus dem Gewicht:
   Das hervorgehobene Wort steht mager gegen den fetten Rest. Das trägt
   weiter als eine Schrägstellung und ist typografisch sauberer als eine
   vom Browser errechnete Falschkursive. */
.display--italic {
  font-weight: 400;
  letter-spacing: 0;
}

.t-hero    { font-size: clamp(2.75rem, 9.5vw, 8.5rem); }
.t-section { font-size: clamp(2rem, 6vw, 5rem); }
.t-large   { font-size: clamp(1.75rem, 4.5vw, 3.5rem); }


.label {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream-dim);
}

.label--dot::before {
  content: "•";
  margin-right: 0.6em;
  color: var(--cream);
}

.body-text {
  font-size: clamp(1rem, 1.4vw, 1.125rem);
  line-height: 1.7;
  color: var(--cream-dim);
  max-width: 62ch;
}

.body-text + .body-text { margin-top: var(--space-md); }
/* Schlusssatz eines Textblocks: hell und mit etwas mehr Luft davor */
.body-text--schluss { color: var(--cream); }
.body-text + .body-text--schluss { margin-top: var(--space-lg); }

.tabular { font-variant-numeric: tabular-nums; }

/* Zeilen-Reveal (wird per JS aktiviert) */
.reveal-line { display: block; overflow: hidden; }
.reveal-line > span { display: block; will-change: transform; }

/* --------------------------------------------------------------------------
   4. LAYOUT-HILFEN
   -------------------------------------------------------------------------- */
.shell {
  width: 100%;
  max-width: var(--max-width);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  position: relative;
  padding-block: clamp(72px, 12vh, var(--space-3xl));
}

.section--divided { border-top: 1px solid var(--line); }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-md);
  flex-wrap: wrap;
  margin-bottom: var(--space-xl);
}

/* Nur für Screenreader */
.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link {
  position: absolute;
  top: -100px;
  left: var(--gutter);
  z-index: var(--z-overlay);
  padding: 12px 20px;
  background: var(--cream);
  color: var(--bg);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: 2px;
}
.skip-link:focus-visible { top: 12px; }

/* Sichtbarer Fokusring, global */
:focus-visible {
  outline: 2px solid var(--cream);
  outline-offset: 3px;
  border-radius: 2px;
}
.on-paper :focus-visible { outline-color: var(--ink); }

/* --------------------------------------------------------------------------
   5. HEADER
   -------------------------------------------------------------------------- */
.site-header {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: var(--z-header);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: start;
  gap: var(--space-md);
  padding: var(--space-md) var(--gutter) calc(var(--space-md) + 34px);
  pointer-events: none;
  /* Kein mix-blend-mode: Die Wortmarke ist ein weißes Bild und läge auf
     Projektseiten genau auf der Kante zwischen dunklem Foto und heller
     Fläche. Der Mischmodus liefert dort je nach Bildhelligkeit mal helle,
     mal dunkle Buchstaben. Ein eigener Verlauf ist berechenbar. */
  background: linear-gradient(180deg,
    rgba(10, 9, 8, 0.82) 0%,
    rgba(10, 9, 8, 0.45) 52%,
    rgba(10, 9, 8, 0) 100%);
}
.site-header > * { pointer-events: auto; }

/* Auf der Startseite und den Projektseiten liegt der Kopf über einem Bild —
   dort ist der weiche Verlauf richtig. Auf den scrollenden Seiten schoben
   sich dagegen die großen Titel darunter durch und lagen sichtbar über der
   Wortmarke. Dort ist der Streifen deshalb deckend. */
html:not(.reel-page):not(.project-page) .site-header {
  background: linear-gradient(180deg,
    var(--bg) 0%,
    var(--bg) 62%,
    rgba(10, 9, 8, 0) 100%);
}

/* Feste Spalten statt automatischer Platzierung: So steht der Name auf jeder
   Seite mittig — auch dort, wo er im Quelltext zuerst kommt, und auch wenn
   die Beschreibung ausgeblendet ist. */
/* grid-row muss mitgesetzt werden: Der Name steht im Quelltext vor der
   Beschreibung, liegt aber rechts von ihr. Ohne feste Zeile schiebt das
   automatische Platzieren die Beschreibung in eine zweite Zeile. */
.header-tag   { grid-column: 1; grid-row: 1; }
.wordmark     { grid-column: 2; grid-row: 1; }
.main-nav     { grid-column: 3; grid-row: 1; }
.menu-toggle  { grid-column: 3; grid-row: 1; }

/* Die Wortmarke steht mittig — die Beschreibung links, die Navigation
   rechts halten sie dort im Gleichgewicht.

   Sie ist jetzt gesetzter Text, kein Bild mehr. Das hat drei Vorteile: Sie
   ist scharf auf jedem Bildschirm, sie lässt sich markieren und vorlesen,
   und sie spart einen Ladevorgang. Der Preis: Sie sieht nicht mehr aus wie
   das alte Logo, sondern wie die Schrift der Seite. */
.wordmark {
  justify-self: center;
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(0.8rem, 1.15vw, 1rem);
  line-height: 1.02;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  text-align: center;
  color: var(--cream);
  white-space: nowrap;
}
/* Zweizeilig gesetzt wie im alten Signet: kurz, kompakt, mittig. */
.wordmark span { display: block; }
.wordmark .wordmark__zwei { margin-left: 0.08em; }

/* Der Name steht darunter wie eine Zeile im Vorspann: kleiner Schnitt,
   weit gesperrt, eine feine Linie dazwischen. Er soll die Wortmarke nicht
   verdoppeln, sondern sie unterschreiben.
   text-indent gleicht die Sperrung aus — sonst schiebt der Zwischenraum
   hinter dem letzten Buchstaben die Zeile sichtbar nach links. */
.wordmark__name {
  display: block;
  margin-top: 0.5em;
  padding-top: 0.5em;
  border-top: 1px solid var(--line);
  font-family: var(--font-mono);
  font-weight: 400;
  font-size: 0.625rem;
  letter-spacing: 0.26em;
  text-indent: 0.26em;
  text-transform: uppercase;
  color: var(--cream-mute);
}
@media (max-width: 719px) {
  /* Auf dem Telefon ist der Platz neben dem Menüknopf knapp */
  .wordmark__name { font-size: 0.5625rem; letter-spacing: 0.18em; text-indent: 0.18em; }
}

.wordmark--footer {
  justify-self: start;
  text-align: left;
  font-size: clamp(1.05rem, 1.8vw, 1.5rem);
  letter-spacing: -0.005em;
}
.wordmark--footer .wordmark__name {
  font-size: 0.6875rem;
  letter-spacing: 0.16em;
  text-indent: 0.16em;
}

.header-tag {
  justify-self: start;
  padding-top: 0.35em;
  white-space: nowrap;   /* einzeilig — sonst wirkt sie neben dem mittigen Namen unruhig */
}

/* Unter 1100px wird es neben dem mittigen Namen zu eng; die Beschreibung
   entfällt, bevor sie umbricht. */
@media (max-width: 1099px) {
  .header-tag { display: none; }
}

@media (max-width: 899px) {
  /* Es bleiben zwei Elemente: Name links, Menü rechts */
  .site-header { grid-template-columns: 1fr auto; }
  .wordmark    { grid-column: 1; grid-row: 1; justify-self: start; }
  .menu-toggle { grid-column: 2; grid-row: 1; }
}

.main-nav {
  justify-self: end;
  display: flex;
  gap: clamp(24px, 4vw, 72px);
}

.nav-group { display: flex; flex-direction: column; gap: 2px; }

.nav-link {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-height: 24px;
  padding-left: 14px;
  font-family: var(--font-ui);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--cream-dim);
  transition: color var(--dur-fast) var(--ease-out);
}
.nav-link::before {
  content: "";
  position: absolute;
  left: 0;
  top: 50%;
  width: 5px;
  height: 5px;
  margin-top: -2.5px;
  border-radius: 50%;
  background: var(--cream);
  opacity: 0;
  transform: scale(0.4);
  transition: opacity var(--dur-fast) var(--ease-out), transform var(--dur-fast) var(--ease-out);
}
.nav-link:hover,
.nav-link[aria-current="page"] { color: var(--cream); }
.nav-link:hover::before,
.nav-link[aria-current="page"]::before { opacity: 1; transform: scale(1); }

/* Mobiles Menü */
.menu-toggle {
  display: none;
  justify-self: end;
  align-items: center;
  gap: 10px;
  min-height: 44px;
  min-width: 44px;
  justify-content: flex-end;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream);
}
.menu-toggle-bars { display: block; width: 22px; height: 9px; position: relative; }
.menu-toggle-bars::before,
.menu-toggle-bars::after {
  content: "";
  position: absolute;
  left: 0;
  width: 100%;
  height: 1px;
  background: currentColor;
  transition: transform var(--dur-base) var(--ease-out);
}
.menu-toggle-bars::before { top: 0; }
.menu-toggle-bars::after  { bottom: 0; }
.menu-open .menu-toggle-bars::before { transform: translateY(4px) rotate(45deg); }
.menu-open .menu-toggle-bars::after  { transform: translateY(-4px) rotate(-45deg); }

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: var(--space-2xs);
  padding: var(--header-h) var(--gutter) var(--space-xl);
  background: var(--bg);
  transform: translateY(-100%);
  transition: transform var(--dur-slow) var(--ease-in-out);
  visibility: hidden;
}
.menu-open .mobile-menu { transform: translateY(0); visibility: visible; }

/* Nur die drei Menüpunkte sind groß — nicht jeder Link im Menü. Vorher
   galt die Regel für alle a-Elemente, und die E-Mail-Adresse unten lief in
   Versalien und 36 px rechts aus dem Bild. */
.mobile-menu nav a {
  display: block;
  padding-block: var(--space-2xs);
  font-family: var(--font-display);
  font-size: clamp(2.25rem, 11vw, 4rem);
  line-height: 1.1;
  color: var(--cream);
}
.mobile-menu nav a[aria-current="page"] { font-weight: 400; }
.mobile-menu-foot { margin-top: var(--space-xl); }
.mobile-menu-foot a {
  color: var(--cream-dim);
  text-decoration: none;
  overflow-wrap: anywhere;   /* Netz für sehr schmale Geräte: lieber umbrechen als abschneiden */
}

@media (max-width: 899px) {
  .main-nav { display: none; }
  .menu-toggle { display: inline-flex; }
}

/* --------------------------------------------------------------------------
   6. FOOTER-LEISTE (fix, Timecode)
   -------------------------------------------------------------------------- */
.status-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-header);
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
  padding: calc(var(--space-sm) + 26px) var(--gutter) var(--space-sm);
  pointer-events: none;
  /* gleicher Grund wie beim Kopfbereich: berechenbar statt Mischmodus */
  background: linear-gradient(0deg,
    rgba(10, 9, 8, 0.82) 0%,
    rgba(10, 9, 8, 0.40) 60%,
    rgba(10, 9, 8, 0) 100%);
}
.status-bar > * { pointer-events: auto; }

/* Auf der Startseite liegt die Leiste über einem Foto — dort ist der weiche
   Verlauf richtig. Auf den scrollenden Seiten schieben sich dagegen große
   Überschriften darunter durch, und zwei Texte lagen kurz übereinander.
   Deshalb ist der Streifen unter dem Text dort deckend. */
html:not(.reel-page) .status-bar {
  background: linear-gradient(0deg,
    var(--bg) 0%,
    var(--bg) 58%,
    rgba(10, 9, 8, 0) 100%);
}

/* Auf Projektseiten läuft die Leiste quer über die Trennkante zwischen Foto
   und heller Fläche — Mitte und rechte Seite wären dort nicht lesbar. Der
   Split-Screen kommt ohne dieses Overlay besser zur Geltung. */
.project-page .status-bar { display: none; }
.status-bar .label { color: var(--cream-mute); }
.status-bar__mid { justify-self: center; }
.status-bar__end { justify-self: end; }

/* Auf schmalen Geräten reicht der Platz für drei Spalten nicht — die Leiste
   löst sich aus der Fixierung und läuft als letzte Zeile im Dokument mit.
   Das spart Bildschirmhöhe und der Text kann nicht mehr überlappen. */
@media (max-width: 719px) {
  .status-bar {
    position: static;
    grid-template-columns: 1fr;
    justify-items: center;
    gap: var(--space-3xs);
    padding-top: var(--space-sm);
    padding-bottom: var(--space-lg);
    background: none;
  }
  .status-bar__mid,
  .status-bar__end { justify-self: center; }
  .status-bar__end { display: none; }  /* Timecode ist reine Desktop-Zierde */
}

/* --------------------------------------------------------------------------
   8. MEDIEN-RAHMEN (Bild-Platzhalter mit Fallback-Verlauf)
   -------------------------------------------------------------------------- */
.media {
  position: relative;
  overflow: hidden;
  background:
    radial-gradient(120% 120% at 30% 20%, #2a2622 0%, #14110f 55%, #0a0908 100%);
}
.media > img,
.media > picture > img,
.media > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* Ein picture-Element ist im Fluss nur ein Rahmen um sein Bild. Damit es
   die Fläche nicht um eine Zeilenhöhe verschiebt, verhält es sich wie das
   Bild selbst. */
picture { display: contents; }
.media--4x5  { aspect-ratio: 4 / 5; }
.media--16x9 { aspect-ratio: 16 / 9; }
.media--3x2  { aspect-ratio: 3 / 2; }

/* --------------------------------------------------------------------------
   9. HERO
   -------------------------------------------------------------------------- */
.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  isolation: isolate;
}

.hero__bg {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    radial-gradient(110% 90% at 50% 35%, #302a24 0%, #16130f 55%, #0a0908 100%);
  overflow: hidden;
}
.hero__bg img,
.hero__bg video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.72;
}
.hero__bg::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(180deg, rgba(10,9,8,0.72) 0%, rgba(10,9,8,0.30) 45%, rgba(10,9,8,0.92) 100%);
}

.hero__inner {
  position: relative;
  z-index: var(--z-content);
  width: 100%;
  text-align: center;
  padding-block: calc(var(--header-h) + var(--space-lg)) calc(var(--footer-h) + var(--space-lg));
}

/* Filmkorn — die Textur über der ganzen Seite.

   Ein Rauschen aus einem SVG-Filter, das viermal pro Sekunde um wenige
   Prozent springt: so entsteht das lebendige Korn einer Filmkopie, ohne dass
   irgendetwas fließend wandert (Verträglichkeit).

   Weiches Rauschen, bewusst zurückhaltend: Es soll die Fläche leben lassen,
   nicht als Effekt auffallen. Eine Fassung mit harten Sprenkeln (echtes
   Filmkorn, deutlich sichtbar) war zu viel — wer sie zurückwill, ersetzt
   das SVG durch eines mit feComponentTransfer, das die Punkte vereinzelt.

   Die Stärke steht in --korn: 0.06 ist die ruhige Fassung, ab 0.12 hebt
   das Rauschen den schwarzen Grund merklich ins Graue.

   Dazu eine Vignette (--vignette): Die Ränder des Fensters laufen leicht
   ins Dunkel, wie bei einem Objektiv mit offener Blende. Sie liegt als
   eigene Ebene darunter, weil das Korn springt und die Vignette nicht
   mitspringen darf. Beide lassen sich hier mit einer Zahl abschalten. */
:root {
  --korn: 0.06;
  --vignette: 0.42;
}
.grain {
  position: fixed;
  inset: -50%;
  z-index: 90;
  pointer-events: none;
  opacity: var(--korn);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  animation: grain-shift 900ms steps(4) infinite;
}
/* Die Vignette als eigene, ruhende Ebene direkt unter dem Korn. Sie hängt
   am body, damit sie auf jeder Seite da ist — auch auf denen ohne
   .grain-Element. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  z-index: 89;
  pointer-events: none;
  background: radial-gradient(120% 90% at 50% 45%, rgba(10, 9, 8, 0) 55%, rgba(10, 9, 8, var(--vignette)) 100%);
}
@keyframes grain-shift {
  0%   { transform: translate(0, 0); }
  25%  { transform: translate(-3%, 2%); }
  50%  { transform: translate(2%, -3%); }
  75%  { transform: translate(-2%, -2%); }
  100% { transform: translate(0, 0); }
}

/* --------------------------------------------------------------------------
   8b. LADEBILDSCHIRM

   Erscheint beim Betreten und beim Wechsel zwischen Seiten. Bewusst nur
   sichtbar, wenn JavaScript läuft (.js): Ohne Skript könnte ihn niemand
   wieder ausblenden, und der Inhalt bliebe für immer verdeckt.
   -------------------------------------------------------------------------- */
.loader { display: none; }

.js .loader {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  background: var(--bg);
  transition: opacity 620ms var(--ease-out), visibility 0s linear 620ms;
}

/* Ausgeblendet: unsichtbar UND nicht mehr im Weg */
.js .loader.is-done {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Wieder eingeblendet beim Verlassen der Seite */
.js .loader.is-leaving {
  opacity: 1;
  visibility: visible;
  transition: opacity 340ms var(--ease-out);
}

/* Auch der Ladebildschirm zeigt jetzt den Namen als Text — sonst hätte man
   beim Wechsel vom Laden zur Seite zwei verschiedene Marken gesehen. */
.loader__mark {
  display: block;
  font-family: var(--font-display);
  font-weight: 800;
  font-size: clamp(1.4rem, 3.6vw, 2.6rem);
  line-height: 1.02;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-align: center;
  color: var(--cream);
  opacity: 0;
  animation: loader-mark 1600ms var(--ease-out) infinite;
}
.loader__mark span { display: block; }
@keyframes loader-mark {
  0%   { opacity: 0.28; }
  50%  { opacity: 1; }
  100% { opacity: 0.28; }
}

/* Feiner Balken darunter, der einmal durchläuft */
.loader__bar {
  position: absolute;
  left: 50%;
  bottom: clamp(64px, 14vh, 120px);
  width: clamp(120px, 18vw, 200px);
  height: 1px;
  transform: translateX(-50%);
  background: var(--line);
  overflow: hidden;
}
.loader__bar::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--cream);
  transform: translateX(-100%);
  animation: loader-sweep 1200ms var(--ease-in-out) infinite;
}
@keyframes loader-sweep {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}

@media (prefers-reduced-motion: reduce) {
  .loader__mark { opacity: 1; animation: none; }
  .loader__bar { display: none; }
}

/* --------------------------------------------------------------------------
   9b. PROJEKT-REEL — horizontales Vollbild-Karussell (Startseite)

   Bewusst als natives Scroll-Snap-Karussell gebaut, nicht als
   Scroll-Entführung: Touch-Wischen, Tastatur, Trackpad und Bildlaufleiste
   funktionieren dadurch von selbst, und der Tab-Fokus springt automatisch
   zum richtigen Panel. Das Mausrad wird nur zusätzlich umgelenkt.
   -------------------------------------------------------------------------- */
.reel-page,
.reel-page body {
  height: 100%;
  overflow: hidden;          /* die Seite selbst scrollt nicht — nur das Reel */
  overscroll-behavior: none;
}

.reel {
  position: relative;
  height: 100svh;
  width: 100%;
}

.reel__track {
  display: flex;
  height: 100%;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* Bewusst KEIN scroll-behavior: smooth — das würde auch jede Bewegung des
     Mausrads animieren und träge machen. Die weiche Bewegung setzt das
     Skript gezielt nur bei Buttons und Tastatur ein. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;             /* Firefox */
  -ms-overflow-style: none;
}
.reel__track::-webkit-scrollbar { display: none; }

.reel__panel {
  position: relative;
  flex: 0 0 100%;
  height: 100%;
  scroll-snap-align: start;
  scroll-snap-stop: always;          /* nie zwei Projekte auf einmal überspringen */
  overflow: hidden;
  background: radial-gradient(110% 90% at 50% 40%, #2c2721 0%, #16130f 55%, #0a0908 100%);
}

.reel__link {
  position: absolute;
  inset: 0;
  display: block;
}

/* Die Bühne trägt die Bewegung; Bild und (optionales) Video liegen darin
   übereinander. Beim Blättern wird sie im Raum gedreht, sodass die Projekte
   auf einem Zylinder zu stehen scheinen — das aktive frontal, die Nachbarn
   nach hinten weggeklappt. Die Werte setzt main.js in paintMotion. */
.reel__stage {
  position: absolute;
  inset: 0;
  transform: scale(1.06);
  transform-style: preserve-3d;
  backface-visibility: hidden;
  will-change: transform;
}
/* Kein CSS-Filter und keine Übergangszeit auf filter: Die Schärfentiefe
   setzt main.js bei jedem Bildaufbau direkt — eine Transition würde sie
   hinterherhinken lassen. */

/* Ohne Skript bleibt es beim flachen Nebeneinander — auch dann vollständig
   benutzbar, nur ohne die Drehung. */
.no-js .reel__stage,
.reel__stage:not([style*="transform"]) { transform: scale(1.06); }

.reel__media {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Das Video liegt über dem Standbild und wird erst sichtbar, wenn es
   tatsächlich läuft — so gibt es keinen schwarzen Moment beim Laden. */
video.reel__media {
  opacity: 0;
  transition: opacity 700ms var(--ease-out);
}
video.reel__media.is-playing { opacity: 1; }

/* Der Bildausschnitt und die Beschriftung werden beim Blättern per Skript
   unterschiedlich schnell verschoben (siehe initReel). Das erzeugt die Tiefe
   beim Projektwechsel. Die Werte hier sind nur die Ruhelage. */
.reel__caption { will-change: translate, opacity; }

/* Die Abdunklung muss auch vor hellen Tagaufnahmen tragen, nicht nur vor
   dunklen Motiven. Zwei Ebenen: ein weicher Kern hinter der Beschriftung,
   darunter ein Verlauf, der Kopf- und Fußzeile absichert. */
.reel__scrim {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(78% 52% at 50% 54%, rgba(10,9,8,0.66) 0%, rgba(10,9,8,0.22) 62%, rgba(10,9,8,0) 100%),
    linear-gradient(180deg,
      rgba(10,9,8,0.72) 0%,
      rgba(10,9,8,0.34) 22%,
      rgba(10,9,8,0.34) 62%,
      rgba(10,9,8,0.88) 100%);
}

/* Der Projekttitel steht mittig im Bild */
.reel__caption {
  position: absolute;
  inset: 0;
  z-index: var(--z-content);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  /* Der seitliche Abstand hält die Blätterpfeile frei — sie liegen auf
     derselben Höhe wie der Titel und würden sonst bei längeren
     Projektnamen darüberlaufen. */
  padding: calc(var(--header-h) + var(--space-lg))
           clamp(58px, 9vw, 170px)
           calc(var(--footer-h) + var(--space-xl));
  pointer-events: none;
}

.reel__index {
  display: block;
  margin-bottom: var(--space-md);
  color: var(--cream-mute);
}

.reel__title {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 8vw, 7rem);
  line-height: 0.94;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-md);
  text-wrap: balance;
  /* zusätzliche Sicherheit über hellen Bildstellen */
  text-shadow: 0 2px 40px rgba(10, 9, 8, 0.55);
  transition: transform var(--dur-slow) var(--ease-out);
}
.reel__index,
.reel__meta { text-shadow: 0 1px 16px rgba(10, 9, 8, 0.75); }

/* --- Titel, der beim Projektwechsel buchstabenweise einfliegt ---
   Die Wörter bleiben umbruchfähig, die Buchstaben darin werden einzeln
   bewegt. Der Titel behält per aria-label seinen Wortlaut, damit
   Vorlesewerkzeuge nicht Buchstabe für Buchstabe ausgeben. */
.reel__title .w {
  display: inline-block;
  white-space: nowrap;
}
.reel__title .l {
  display: inline-block;
  will-change: transform, opacity;
}
/* Ausgangslage nur, wenn das Skript die Steuerung übernommen hat */
.js .reel__title.is-split .l {
  opacity: 0;
  transform: translateY(0.5em) rotate(4deg);
}


@media (prefers-reduced-motion: reduce) {
  .js .reel__title.is-split .l { opacity: 1; transform: none; }
}
.reel__panel:hover .reel__title { transform: scale(1.02); }

.reel__meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-sm) var(--space-lg);
  align-items: baseline;
}
/* Auf dem Telefon je Angabe eine Zeile — Kunde, Jahr, Rolle, Hinweis
   untereinander. Vorher brach die Zeile je nach Textlänge irgendwo um, und
   jedes Projekt sah anders aus: mal Kunde und Jahr nebeneinander, mal der
   Kundenname selbst über zwei Zeilen. */
@media (max-width: 599px) {
  .reel__meta {
    flex-direction: column;
    align-items: center;
    gap: var(--space-sm);
  }
  .reel__meta > .label { text-wrap: balance; }
}

/* Die eigene Rolle im Projekt — das entscheidende Merkmal, wenn die Seite
   auch die Arbeit als Director of Photography zeigen soll. Deshalb
   hervorgehoben statt gleichrangig neben Kunde und Jahr. */
.reel__role { color: var(--cream); }

/* Auf Touchgeräten ersetzt dieser Hinweis den Cursor-Kreis, den es dort
   nicht geben kann. Am Zeigegerät übernimmt der Kreis diese Aufgabe. */
.reel__cue {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--cream);
}
@media (hover: hover) and (pointer: fine) {
  .reel__cue { display: none; }
}

/* --- Cursor-Kreis beim Überfahren eines Projekts ---
   Runde Fläche mit feiner Umrandung, die dem Zeiger weich nachläuft. Die
   Kante gibt ihm den Halt, den ein reiner Schein über wechselnden Motiven
   nicht liefert. */
.reel-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 75;
  display: grid;
  place-items: center;
  width: clamp(92px, 8vw, 124px);
  aspect-ratio: 1;
  border: 1px solid rgba(242, 232, 213, 0.55);
  border-radius: 50%;
  background: rgba(10, 9, 8, 0.20);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
  color: var(--cream);
  text-align: center;
  pointer-events: none;
  opacity: 0;
  transform: translate(-50%, -50%) scale(0.4);
  transition: opacity 300ms var(--ease-out), transform 480ms var(--ease-out);
}
.reel-cursor.is-on      { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.reel-cursor.is-pressed { transform: translate(-50%, -50%) scale(0.88); }
.reel-cursor span { display: block; line-height: 1.3; }

@media (hover: none), (pointer: coarse) { .reel-cursor { display: none; } }

/* Das Bild hellt beim Überfahren leicht auf */
.reel__panel .reel__scrim { transition: opacity var(--dur-slow) var(--ease-out); }
/* nur leicht aufhellen — sonst wird die Beschriftung vor hellen Motiven unlesbar */
.reel__panel:hover .reel__scrim { opacity: 0.88; }

/* Steuerleiste unten */
/* Auf der Startseite reicht der Verlauf etwas weiter herunter, weil dort
   ständig wechselnde Motive darunter liegen. */
.reel-page .site-header { padding-bottom: calc(var(--space-md) + 44px); }

/* Projektseiten: Links liegt das Foto, rechts die helle Fläche. Ein Verlauf
   käme über dem Hellen nie auf lesbaren Kontrast, ohne wie ein Balken zu
   wirken. Stattdessen wandert die Wortmarke auf die dunkle Seite und die
   Navigation bekommt dort, wo sie hell unterlegt ist, dunkle Schrift. */
@media (min-width: 900px) {
  .project-page .site-header { background: none; }
  .project-page .wordmark { grid-column: 1; justify-self: start; }
  /* Die Beschreibung läge mittig genau auf der Trennkante und wäre dort
     unlesbar. Sie entfällt hier — Kategorie und Jahr stehen ohnehin groß
     im Textteil. */
  .project-page .header-tag { display: none; }
  .project-page .main-nav .nav-link { color: var(--ink-dim); }
  .project-page .main-nav .nav-link:hover,
  .project-page .main-nav .nav-link[aria-current="page"] { color: var(--ink); }
  .project-page .main-nav .nav-link::before { background: var(--ink); }
}

.reel-bar {
  position: fixed;
  inset: auto 0 0 0;
  z-index: var(--z-header);
  /* spiegelt den Kopfbereich: sichert Zähler und Marker nach unten ab */
  background: linear-gradient(0deg,
    rgba(10, 9, 8, 0.80) 0%,
    rgba(10, 9, 8, 0.42) 55%,
    rgba(10, 9, 8, 0) 100%);
  padding-top: calc(var(--space-sm) + 34px);
  display: grid;
  /* 1fr auto 1fr hält die Marker exakt in der Seitenmitte, unabhängig
     davon, wie breit der Zähler links gerade ist */
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-md);
  padding-inline: var(--gutter);
  padding-bottom: calc(var(--space-sm) + env(safe-area-inset-bottom, 0px));
}

/* Projekt-Marker statt Fortschrittsbalken.
   Jeder Strich ist ein Projekt und direkt anwählbar; der aktive dehnt sich
   aus und wandert dadurch sichtbar mit. Das zeigt, wo man ist, ohne die
   Arbeiten zu einer abzuarbeitenden Strecke zu erklären. */
.reel-ticks {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
}

.reel-tick {
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  width: clamp(15px, 2.2vw, 30px);
  height: 44px;                       /* volle Grifffläche, unsichtbar */
  padding: 0;
  transition: width 560ms var(--ease-out);
}
.reel-tick::before {
  content: "";
  display: block;
  width: 100%;
  height: 1px;
  background: var(--line-strong);
  transition: background-color var(--dur-base) var(--ease-out),
              height var(--dur-base) var(--ease-out);
}
.reel-tick:hover::before { background: var(--cream-dim); height: 2px; }
.reel-tick[aria-current="true"] { width: clamp(38px, 6.5vw, 84px); }
.reel-tick[aria-current="true"]::before { background: var(--cream); height: 2px; }
.reel-tick:focus-visible { outline-offset: -6px; }

@media (max-width: 719px) {
  .reel-tick { width: clamp(12px, 4vw, 22px); height: 40px; }
  .reel-tick[aria-current="true"] { width: clamp(30px, 11vw, 52px); }
}

/* Pfeile mittig an den Seitenrändern, ohne Rahmen — dort, wo die Hand beim
   Blättern ohnehin hinfährt. Die Grifffläche bleibt großzügig, sichtbar ist
   nur der Pfeil. */
.reel-arrow {
  position: fixed;
  top: 50%;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: center;
  width: clamp(56px, 6vw, 84px);
  height: clamp(56px, 6vw, 84px);
  margin-top: calc(clamp(56px, 6vw, 84px) / -2);
  color: var(--cream);
  transition: opacity var(--dur-base) var(--ease-out);
}
.reel-arrow--prev { left: max(4px, calc(var(--gutter) - 28px)); }
.reel-arrow--next { right: max(4px, calc(var(--gutter) - 28px)); }

.reel-arrow svg {
  transition: transform var(--dur-base) var(--ease-out),
              opacity var(--dur-base) var(--ease-out);
  opacity: 0.72;
  /* hebt den hellen Pfeil auch vor hellen Bildstellen ab */
  filter: drop-shadow(0 1px 6px rgba(10, 9, 8, 0.85));
}
.reel-arrow:hover:not(:disabled) svg { opacity: 1; }
.reel-arrow--prev:hover:not(:disabled) svg { transform: translateX(-5px); }
.reel-arrow--next:hover:not(:disabled) svg { transform: translateX(5px); }
.reel-arrow:disabled { opacity: 0.22; cursor: default; }

@media (max-width: 719px) {
  .reel-arrow { width: 46px; height: 46px; margin-top: -23px; }  /* Touch-Mindestmaß */
  .reel-arrow--prev { left: 0; }
  .reel-arrow--next { right: 0; }
  .reel-arrow svg { width: 20px; }
}

/* Der Zähler springt beim Wechsel um wie ein Zählwerk — die Reaktion sitzt
   dort, wo die Änderung ohnehin abzulesen ist, statt als Effekt über dem
   Bild zu liegen. */
.reel-count {
  white-space: nowrap;
  display: inline-block;
}
.reel-count.is-rolling-up   { animation: count-up 460ms var(--ease-out); }
.reel-count.is-rolling-down { animation: count-down 460ms var(--ease-out); }

@keyframes count-up {
  0%   { transform: translateY(0.85em); opacity: 0; }
  100% { transform: translateY(0);      opacity: 1; }
}
@keyframes count-down {
  0%   { transform: translateY(-0.85em); opacity: 0; }
  100% { transform: translateY(0);       opacity: 1; }
}

/* Während des Blätterns dunkelt das Bild kurz nach — wie eine Blende, die
   sich beim Schwenk schließt und wieder öffnet. Gesteuert über eine einzige
   Variable am Track, deshalb nur eine Zuweisung pro Bild. */
.reel__scrim::after {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: var(--reel-tempo, 0);
}

@media (max-width: 719px) {
  .reel-bar { grid-template-columns: auto 1fr auto; gap: var(--space-sm); }
}

/* Der Hinweis „seitwärts scrollen“ verschwindet nach der ersten Bewegung */
.reel-hint {
  position: absolute;
  left: 50%;
  bottom: clamp(96px, 15vh, 150px);
  transform: translateX(-50%);
  z-index: var(--z-content);
  display: inline-flex;
  align-items: center;
  gap: 10px;
  color: var(--cream-mute);
  pointer-events: none;
  transition: opacity var(--dur-slow) var(--ease-out);
}
.reel-hint.is-gone { opacity: 0; }
@media (max-width: 899px) { .reel-hint { display: none; } }

/* --------------------------------------------------------------------------
   10. WERKLISTE (Ghost-Index mit Cursor-Vorschau)
   -------------------------------------------------------------------------- */
.work-index {
  border-top: 1px solid var(--line);
  margin: 0;
  padding: 0;
  list-style: none;   /* die Nummern stehen sichtbar in der Zeile */
}

.work-row {
  position: relative;
  display: grid;
  grid-template-columns: 3.5rem 1fr auto;
  align-items: center;
  gap: var(--space-md);
  padding-block: clamp(20px, 3.2vw, 38px);
  border-bottom: 1px solid var(--line);
  transition: border-color var(--dur-base) var(--ease-out);
}
.work-row:hover { border-bottom-color: var(--line-strong); }

.work-row__num {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  color: var(--cream-mute);
  transition: color var(--dur-base) var(--ease-out);
}
.work-row:hover .work-row__num { color: var(--cream-dim); }

.work-row__title {
  font-family: var(--font-display);
  font-size: clamp(1.6rem, 4.4vw, 3.25rem);
  line-height: 1.02;
  color: var(--cream-mute);
  transition: color var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.work-row:hover .work-row__title {
  color: var(--cream);
  transform: translateX(clamp(6px, 1.2vw, 18px));
}

.work-row__meta {
  margin: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: var(--space-md);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-mute);
  transition: color var(--dur-base) var(--ease-out);
  text-align: right;
}
.work-row:hover .work-row__meta { color: var(--cream-dim); }
/* Die Spalte rechts nimmt sich sonst so viel Breite wie ihr längster
   Inhalt — ein langer Kundenname („Emilia | Fineline Tattoo Artist")
   drückte den Titel bei 768 px aus seiner Spalte. Ab 40 % der Zeile bricht
   die Meta-Zeile stattdessen um. */
@media (min-width: 720px)  { .work-row__meta { max-width: 13rem; } }
@media (min-width: 900px)  { .work-row__meta { max-width: 19rem; } }
@media (min-width: 1100px) { .work-row__meta { max-width: 24rem; } }

/* Ganze Zeile klickbar, Linkfläche deckt Zeile ab */
.work-row__link {
  /* Ohne min-width: 0 kann die 1fr-Spalte nicht unter die Breite des
     längsten Wortes schrumpfen — „Sachsenrallye" sprengte damit auf dem
     Telefon die Seite um 26 Pixel nach rechts. */
  min-width: 0;
}
.work-row__title { overflow-wrap: break-word; }

.work-row__link::after {
  content: "";
  position: absolute;
  inset: 0;
}

@media (max-width: 719px) {
  .work-row { grid-template-columns: 2.25rem 1fr; row-gap: var(--space-2xs); }
  .work-row__meta {
    grid-column: 2;
    justify-content: flex-start;
    text-align: left;
    gap: var(--space-sm);
  }
  .work-row:hover .work-row__title { transform: none; }
}

/* Das kleine Bild in der Zeile — nur dort, wo es keine Zeigervorschau gibt.
   Am Desktop zeigt die Vorschau am Zeiger das Bild; auf Touch-Geräten gab
   es bis dahin gar keins, und ein Archiv aus acht Titeln ohne ein einziges
   Bild ist für eine Filmseite zu wenig. Standard: ausgeblendet. Ein
   Bild mit loading="lazy", das nie angezeigt wird, lädt der Browser auch
   nicht — am Desktop kostet es also nichts. */
.work-row__bild { display: none; }

@media (hover: none), (pointer: coarse), (max-width: 899px) {
  .work-row { grid-template-columns: 3.5rem minmax(0, 7.5rem) 1fr auto; }
  .work-row__bild {
    display: block;
    aspect-ratio: 16 / 9;
    overflow: hidden;
    border-radius: 3px;
    background: #14110f;
  }
  .work-row__bild img { width: 100%; height: 100%; object-fit: cover; }
}
@media (max-width: 719px) {
  /* Drei Spalten auf 335 px: Nummer, Bild, Titel. Der Titel braucht Platz
     für „Meerane" in Syne 800 — darum engerer Spaltenabstand, schmaleres
     Bild und ein Grad weniger als ohne Bild. */
  .work-row { grid-template-columns: 2.25rem 5rem 1fr; column-gap: var(--space-sm); }
  /* Der Schriftgrad dafür steht in Abschnitt 16b — dort, wo die Grade der
     Titel gesetzt werden; eine Regel hier verlöre gegen die spätere. */
  .work-row__num, .work-row__bild { grid-row: 1 / span 2; }
  .work-row__link { grid-column: 3; }
  .work-row__meta { grid-column: 3; }
}

/* Cursor-Vorschau — nur bei echtem Zeigegerät */
.hover-preview {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 70;
  width: clamp(280px, 27vw, 480px);
  /* 16:9 wie die Vorschaubilder selbst. Vorher stand hier 4:5 — ein
     Hochformat, in das ein Querformat hineingeschnitten wurde. Genau das
     war der komische Anschnitt: Von jedem Bild blieb ein Mittelstreifen. */
  aspect-ratio: 16 / 9;
  overflow: hidden;
  pointer-events: none;
  opacity: 0;
  transform: translate3d(-50%, -50%, 0) scale(0.94);
  transition:
    opacity 320ms var(--ease-out),
    transform 520ms var(--ease-out);
  background: #14110f;
  box-shadow: 0 40px 90px rgba(0, 0, 0, 0.6);
}
.hover-preview.is-visible {
  opacity: 1;
  transform: translate3d(-50%, -50%, 0) scale(1);
  /* Nur solange sie zu sehen ist — eine dauerhafte Ebene kostet Speicher,
     auch wenn nichts passiert. */
  will-change: translate;
}

/* Zwei Bilder übereinander: Beim Wechsel der Zeile wird überblendet statt
   umgeschaltet. Ein harter Bildwechsel unter dem Zeiger wirkt wie ein
   Fehler, eine Blende wie ein Schnitt. */
.hover-preview img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 260ms var(--ease-out);
}
.hover-preview img.is-on { opacity: 1; }

/* Der Loop liegt über den Bildern und wird erst sichtbar, wenn er wirklich
   läuft (main.js setzt is-on beim playing-Ereignis). Bis dahin trägt das
   Standbild darunter — die Vorschau ist damit nie leer. Zeilen ohne Film,
   etwa Elbsandstein, bleiben schlicht beim Bild. */
.hover-preview__film {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity 300ms var(--ease-out);
}
.hover-preview__film.is-on { opacity: 1; }

/* Feine Kante und ein Hauch Vignette — das Bild soll wie ein Kader wirken,
   nicht wie ein aufgeklebtes Rechteck. */
.hover-preview::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  box-shadow:
    inset 0 0 0 1px rgba(242, 232, 213, 0.16),
    inset 0 0 70px rgba(0, 0, 0, 0.5);
}
@media (hover: none), (pointer: coarse), (max-width: 899px) {
  .hover-preview { display: none; }
}

/* Filter-Chips */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2xs);
  margin-bottom: var(--space-lg);
}
.chip {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  padding: 0 var(--space-sm);
  border: 1px solid var(--line);
  border-radius: 999px;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-dim);
  transition: color var(--dur-fast) var(--ease-out),
              border-color var(--dur-fast) var(--ease-out),
              background-color var(--dur-fast) var(--ease-out);
}
.chip:hover { color: var(--cream); border-color: var(--line-strong); }
.chip[aria-pressed="true"] {
  background: var(--cream);
  border-color: var(--cream);
  color: var(--bg);
}
.chip__count { margin-left: 0.5em; opacity: 0.6; }

/* --------------------------------------------------------------------------
   13. LITE-YOUTUBE (Poster statt iframe bis zum Klick)
   -------------------------------------------------------------------------- */
.lite-yt {
  position: relative;
  display: block;
  width: 100%;
  /* Bei 16:9 wäre der Player auf breiten Monitoren höher als der Bildschirm.
     Die Breitenbegrenzung hält ihn vollständig sichtbar — ohne das Seitenverhältnis
     zu verbiegen. */
  max-width: calc(80svh * 16 / 9);
  margin-inline: auto;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: #14110f center / cover no-repeat;
  cursor: pointer;
}
.lite-yt::after {
  content: "";
  position: absolute;
  inset: 0;
  background: rgba(10, 9, 8, 0.35);
  transition: background var(--dur-base) var(--ease-out);
}
.lite-yt:hover::after { background: rgba(10, 9, 8, 0.18); }

.lite-yt__btn {
  position: absolute;
  inset: 0;
  z-index: 2;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}
.lite-yt__btn span {
  display: inline-flex;
  align-items: center;
  gap: 12px;
  padding: 14px 26px;
  border: 1px solid rgba(242, 232, 213, 0.5);
  border-radius: 999px;
  backdrop-filter: blur(6px);
  background: rgba(10, 9, 8, 0.28);
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.lite-yt__btn:hover span {
  background: var(--cream);
  border-color: var(--cream);
  color: var(--ink);
}
.lite-yt iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  z-index: 3;
}

/* --- Selbst gehosteter Film (projekte.json → film) ---
   Gleiche Bühne wie der YouTube-Player, aber im Seitenverhältnis des Films:
   --film-ratio kommt aus der Vorlage. Die Breitenbegrenzung hält auch ein
   5:4-Bild auf breiten Monitoren vollständig sichtbar. */
.film {
  --film-ratio: 16 / 9;
  width: 100%;
  max-width: calc(80svh * (var(--film-ratio)));
  margin-inline: auto;
  aspect-ratio: var(--film-ratio);
  background: #14110f;
  overflow: hidden;
}
.film__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  background: #14110f;
}

/* --------------------------------------------------------------------------
   14. PROJEKT-DETAIL — Split-Screen
   -------------------------------------------------------------------------- */
.project-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100svh;
}

.project-split__visual {
  position: relative;
  /* clip, nicht hidden: overflow: hidden macht die Spalte zum eigenen
     Scrollbereich, und darin hat position: sticky nichts zu heften — das
     Bild scrollte einfach weg, und bei langen Seiten (CCL) blieb unten
     schwarze Spalte. */
  overflow: clip;
  background: var(--bg);
}
/* Das Bild füllt die halbe Seite in voller Höhe und bleibt beim Scrollen
   stehen. Ein 16:9-Kader wird dafür seitlich beschnitten — welcher Teil
   sichtbar bleibt, steuert je Projekt das Feld „fokus" in projekte.json
   (object-position, Standard: Mitte). */
.project-split__visual .sticky-media {
  position: sticky;
  top: 0;
  height: 100svh;
}
.project-split__visual img,
.project-split__visual video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.project-split__panel {
  background: var(--paper);
  color: var(--ink);
  padding: calc(var(--header-h) + var(--space-xl)) clamp(24px, 4vw, 72px) var(--space-2xl);
}

.project-title {
  font-family: var(--font-ui);
  font-weight: 800;
  font-size: clamp(2.5rem, 5.2vw, 5.5rem);
  line-height: 0.92;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  text-wrap: balance;
  margin-bottom: var(--space-md);
}

.project-intro {
  font-size: clamp(1rem, 1.3vw, 1.125rem);
  line-height: 1.7;
  color: var(--ink-dim);
  max-width: 54ch;
  margin-bottom: var(--space-xl);
}

/* Credits-Tabelle */
.credits { width: 100%; border-collapse: collapse; text-align: left; }
.credits caption {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink);
  text-align: left;
  padding-bottom: var(--space-sm);
  border-bottom: 1px solid var(--line-ink);
}
.credits th,
.credits td {
  padding: var(--space-xs) 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  line-height: 1.6;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  vertical-align: top;
  border-bottom: 1px solid var(--line-ink);
}
.credits th {
  width: 42%;
  font-weight: 400;
  color: var(--ink-faint);
  padding-right: var(--space-sm);
}
.credits td { color: var(--ink); font-weight: 500; text-align: right; }

.on-paper .label { color: var(--ink-faint); }
.on-paper .label--dot::before { color: var(--ink); }

@media (max-width: 899px) {
  .project-split { grid-template-columns: 1fr; }
  /* Wie beim .two-col: Ohne min-width: 0 wächst die Spalte auf die Breite
     ihres längsten Wortes. „Sachsenrallye" hat die Seite so auf 484 px
     aufgezogen, und die Silbentrennung konnte gar nicht greifen — es gab
     ja keine zu enge Zeile, gegen die sie hätte arbeiten können. */
  .project-split > * { min-width: 0; }
  .project-split__visual .sticky-media { position: relative; height: 62svh; }
  .project-split__panel { padding-top: var(--space-xl); }
}

/* Stills-Galerie */
.stills {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
  gap: var(--space-2xs);
}
.stills .media--tall { grid-row: span 2; }

/* --------------------------------------------------------------------------
   BEHIND THE SCENES

   Zwei Zustände aus einem Markup:

   Bis 899 px  seitwärts laufender Streifen. Kein Skript nötig, natives
               Scrollen mit weichem Einrasten.
   Ab 900 px   Bildwolke — die Aufnahmen stehen in einem Raum verteilt,
               dazwischen Messpunkte wie in einer Punktwolke. Der Raum kippt
               sanft mit dem Mauszeiger und mit dem Scrollen mit.

   Warum die Wolke keinen Schwindel macht: Sie dreht sich um höchstens neun
   Grad, sie bewegt sich nie auf einen zu, und die Grundbewegung läuft über
   eine halbe Minute pro Schwingung. Was zoomt, ist nur das Bild unter dem
   Zeiger — und das nur, weil man selbst darauf zeigt.
   -------------------------------------------------------------------------- */
.bts {
  margin-top: var(--space-lg);
  padding-bottom: var(--space-xs);
  overflow-x: auto;
  overflow-y: hidden;
  overscroll-behavior-x: contain;
  scroll-snap-type: x proximity;
  /* Ohne dieses Polster würde das erste Bild beim Einrasten hart am linken
     Rand kleben statt in einer Flucht mit der Überschrift zu stehen. */
  scroll-padding-left: var(--gutter);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--line-strong) transparent;
}
.bts::-webkit-scrollbar { height: 4px; }
.bts::-webkit-scrollbar-track { background: transparent; }
.bts::-webkit-scrollbar-thumb { background: var(--line-strong); border-radius: 2px; }

.bts__track {
  display: flex;
  width: max-content;
  gap: var(--space-2xs);
  margin: 0;
  padding: 0 var(--gutter);
  list-style: none;
}

.bts__item {
  flex: none;
  height: clamp(200px, 34vh, 380px);
  overflow: hidden;
  scroll-snap-align: start;
  background: radial-gradient(120% 120% at 30% 20%, #2a2622 0%, #14110f 55%, #0a0908 100%);
}
.bts__item img {
  display: block;
  height: 100%;
  width: auto;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.bts__item:hover img { transform: scale(1.03); }

/* Die Messpunkte gibt es nur in der Wolke. pointer-events muss aus sein:
   Die Ebene liegt über den Bildern und würde den Zeiger sonst abfangen —
   dann ließe sich kein Bild mehr scharfstellen. */
.bts__punkte { display: none; pointer-events: none; }

/* Im Streifen ist die Strecke nur eine Hülle ohne eigene Wirkung. */
.bts-strecke { display: contents; }
.bts-nav-halter { display: none; }

@media (prefers-reduced-motion: reduce) {
  .bts { scroll-snap-type: none; }
  .bts__item img { transition: none; }
  .bts__item:hover img { transform: none; }
}


/* Der Knopf um jedes Bild: macht die Wolke mit der Tastatur bedienbar.
   Sichtbar soll er nicht sein — nur das Bild. */
.bts__knopf {
  display: block;
  /* Muss positioniert sein. Die visually-hidden-Beschriftung darin liegt
     absolut; ohne einen positionierten Vorfahren hängt sie am Seitenanfang
     statt am Knopf, entkommt dem Scrollrahmen des Streifens und zieht die
     ganze Seite auf 1900 Pixel Breite auf. */
  position: relative;
  /* Im Streifen gibt die Kachel die Höhe vor, das Bild darin füllt sie aus.
     Ohne diese Zeile hinge das height: 100% des Bildes an einer Höhe, die
     es nicht gibt — die Bilder liefen dann in Originalgröße auf und der
     Streifen wäre achttausend Pixel breit. Genau das ist passiert. */
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  color: inherit;
  font: inherit;
}
.bts__knopf:focus-visible {
  outline: 2px solid var(--cream);
  outline-offset: 3px;
}

/* Steuerung unter der Wolke */
.bts-nav {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
  margin-top: var(--space-sm);
}
.bts-nav__stand {
  min-width: 12ch;
  color: var(--cream-mute);
}
.bts-nav__zurueck {
  margin-left: auto;
  border: 0;
  background: none;
  padding: 0;
  color: var(--cream-mute);
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  transition: color var(--dur-fast) var(--ease-out);
}
.bts-nav__zurueck:hover { color: var(--cream); }

/* ---------- Ab hier: die Wolke ---------- */
@media (min-width: 900px) {

  .bts {
    /* 1400 px Brennweite: lang genug, dass die Perspektive ruhig bleibt und
       die Bilder an den Rändern nicht auseinandergezogen wirken. */
    perspective: 1400px;
    /* Genau die Mitte, damit ein angewähltes Bild auch in der Mitte landet. */
    perspective-origin: 50% 50%;
    overflow: hidden;
    padding-bottom: 0;
    scroll-snap-type: none;
    /* Ein Hauch Licht in der Mitte. Ohne das wäre der Raum ein schwarzes
       Loch und die Wolke schwebte im Nichts. */
    background:
      radial-gradient(58% 68% at 50% 48%, rgba(242, 232, 213, 0.045), transparent 72%);
    /* Die Fläche ist größer als das Fenster und wird gezogen. */
    cursor: grab;
    touch-action: none;
  }
  .bts.is-greifend { cursor: grabbing; }

  /* Weiche Kanten, damit angeschnittene Bilder am Rand nicht abgehackt
     wirken. Ein eigenes Element statt mask auf .bts — eine Maske würde die
     räumliche Staffelung darunter flachlegen. */
  .bts::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 4;
    pointer-events: none;
    background:
      linear-gradient(90deg, var(--bg) 0%, transparent 9%, transparent 91%, var(--bg) 100%),
      linear-gradient(180deg, var(--bg) 0%, transparent 7%, transparent 93%, var(--bg) 100%);
  }

  .bts__track,
  .bts__punkte {
    position: absolute;
    inset: 0;
    display: block;
    width: auto;
    margin: 0;
    padding: 0;
    transform-style: preserve-3d;
    /* Das Skript schreibt hier jede Bildwiederholung eine Drehung hinein. */
    will-change: transform;
  }
  /* ---------- Die Flugstrecke ----------
     Die Bilder liegen nicht nebeneinander, sondern hintereinander — über
     rund zweieinhalbtausend Pixel Tiefe. Die Strecke ist ein hoher, leerer
     Kasten; die Bühne darin klebt am oberen Rand. Wer scrollt, fährt durch
     den Raum: Jede Aufnahme kommt einmal von hinten heran, zieht groß
     vorbei und verschwindet.

     Die Höhe der Strecke setzt main.js nach der Anzahl der Bilder. Der Wert
     hier ist nur der Ausgangspunkt für den Fall, dass kein Skript läuft. */
  .bts-strecke {
    display: block;
    position: relative;
    height: 280vh;
  }
  .bts {
    position: sticky;
    top: 0;
    height: 100vh;
  }
  .bts__punkte { display: block; }

  .bts__item {
    position: absolute;
    left:   calc(50% + var(--x, 0) * 1%);
    top:    calc(50% + var(--y, 0) * 1%);
    width:  calc(var(--w, 18) * 1%);
    height: auto;
    flex: initial;
    /* Kein Kippen um die Bildachse: Die Aufnahmen stehen gerade im Raum.
       Schräge Bilder sähen nach Schnappschusshaufen aus, nicht nach
       Punktwolke — die Tiefe macht die Staffelung, nicht die Neigung. */
    transform:
      translate(-50%, -50%)
      translateZ(calc(var(--z, 0) * 1px));
    /* Unschärfe und Blässe kommen aus der Tiefe und stehen fest im HTML.
       Nichts davon wird pro Bild neu gerechnet. */
    filter: blur(calc(var(--b, 0) * 1px)) saturate(calc(1 - var(--b, 0) * 0.14));
    opacity: calc(1 - var(--b, 0) * 0.11);
    box-shadow: 0 30px 70px rgba(0, 0, 0, 0.55);
    transition:
      transform 620ms var(--ease-out),
      filter    420ms var(--ease-out),
      opacity   420ms var(--ease-out);
  }
  .bts__knopf { width: 100%; height: auto; }
  .bts__item img { width: 100%; height: auto; }

  /* Ein Bild anfahren heißt: es kommt nach vorn und wird scharf.
     Derselbe Gedanke wie beim Fokuspunkt im Reel. */
  .bts:not(.is-schau) .bts__item:hover,
  .bts:not(.is-schau) .bts__item:focus-within {
    transform:
      translate(-50%, -50%)
      translateZ(calc((var(--z, 0) + 150) * 1px));
    filter: none;
    opacity: 1;
  }
  .bts__item:hover img { transform: none; }

  /* ---------- Schaumodus: ein Bild groß in der Mitte ----------
     Das gewählte Bild fährt auf 320 px nach vorn — vor alles andere — und
     landet dank perspective-origin 50% 50% genau in der Bühnenmitte.
     Das transform dafür schreibt main.js direkt ins Element: es braucht
     die Maße der Bühne, und ein var() als Argument einer Transform-Funktion
     wird von manchen Browsern stillschweigend verworfen. */
  .bts__item.is-schau {
    filter: none;
    opacity: 1;
  }
  /* Der Rest der Wolke tritt zurück — ohne sich zu bewegen. Nur Helligkeit
     und Schärfe gehen herunter, damit nichts auf den Betrachter zufährt.
     Anklickbar bleiben die anderen aber: So springt man direkt zum nächsten
     Bild, statt erst schließen zu müssen. */
  .bts__item.is-beiseite {
    filter: blur(3px) saturate(0.5);
    opacity: 0.16;
  }
  .bts__item.is-beiseite:hover { opacity: 0.4; }
  /* Die Punkte einzeln dämpfen, nicht die ganze Ebene: Ein opacity unter 1
     auf einem preserve-3d-Element würde dessen Inhalt platt auf eine Fläche
     legen und die Tiefe der Punkte zerstören. */
  .bts.is-schau .bts__punkt { opacity: calc(var(--o, 0.2) * 0.45); }
  .bts__punkt { transition: opacity 420ms var(--ease-out); }

  .bts__punkt {
    position: absolute;
    left: calc(50% + var(--x, 0) * 1%);
    top:  calc(50% + var(--y, 0) * 1%);
    width: 3px;
    height: 3px;
    border-radius: 50%;
    background: var(--cream);
    opacity: var(--o, 0.2);
    transform: translate(-50%, -50%) translateZ(calc(var(--z, 0) * 1px));
  }

  /* Der Zähler liegt in der Bühne und fliegt deshalb nicht mit. */
  .bts-nav-halter {
    display: block;
    position: absolute;
    left: 0;
    right: 0;
    /* Hoch genug, dass die feste Statusleiste am Fensterrand nicht
       darüberliegt — die ist rund 70 Pixel hoch. */
    bottom: clamp(78px, 10vh, 120px);
    z-index: 5;
    pointer-events: none;
  }
  .bts-nav-halter .bts-nav { margin-top: 0; pointer-events: auto; }
}

@media (min-width: 900px) and (prefers-reduced-motion: reduce) {
  /* Die Wolke bleibt stehen — die Anordnung allein trägt das Bild. */
  .bts__item { transition: none; }
}

/* Porträt im DoP-Abschnitt der Kontaktseite */
.portrait {
  margin: var(--space-xl) 0 0;
  max-width: 340px;
}
.portrait figcaption {
  margin-top: var(--space-2xs);
  color: var(--cream-mute);
}
@media (max-width: 899px) {
  .portrait { max-width: 260px; }
}

/* Nächstes Projekt */
.next-project {
  position: relative;
  min-height: 46svh;
  display: grid;
  /* minmax(0, 1fr): Eine auto-Spalte wäre so breit wie das längste Wort im
     Titel und ragte bei schmalen Fenstern über den Rand. */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  text-align: center;
  overflow: hidden;
  border-top: 1px solid var(--line);
}
.next-project__bg {
  position: absolute;
  inset: 0;
  background: radial-gradient(90% 90% at 50% 50%, #2a2622 0%, #0a0908 100%);
  transform: scale(1.02);
  transition: transform var(--dur-slow) var(--ease-out);
}
.next-project__bg img { width: 100%; height: 100%; object-fit: cover; opacity: 0.5; }
.next-project:hover .next-project__bg { transform: scale(1.06); }
.next-project__inner {
  position: relative;
  z-index: 2;
  /* width: 100% — als Rasterkind wuchs der Block sonst auf die Breite
     seines längsten Wortes („Sachsenrallye" bei 360 px) über den Rand
     hinaus und wurde rechts abgeschnitten. */
  width: 100%;
  padding: var(--space-xl) var(--gutter);
}
/* Der Titel bekommt bei schmalen Fenstern eine Stufe weniger, damit auch
   „Sachsenrallye" als ein Wort in die Zeile passt statt zu brechen. */
@media (max-width: 479px) {
  .next-project__inner .t-section { font-size: clamp(1.1rem, 5.6vw, 1.5rem); }
}

/* --------------------------------------------------------------------------
   14a. LASS UNS REDEN — der Abbinder auf jeder Projektseite

   Zwei Wörter stehen übereinander. Fährt man mit dem Zeiger darüber, fahren
   sie auseinander, und dazwischen öffnet sich eine Kachel mit einem Bild vom
   Set und dem Kontaktknopf; links und rechts schieben sich Name und Rolle
   heran. Verlässt der Zeiger den Abschnitt, schließt er sich wieder. Ohne
   Zeiger — Telefon, Tastatur, „Bewegung reduzieren" — steht er offen.

   Alles über transform und opacity: Die Mittelzeile hat immer ihre volle
   Höhe. Im geschlossenen Zustand sind die Wörter nur um die halbe Zeile in
   die Mitte verschoben und die Kachel ist auf Null gestaucht — beim Öffnen
   gehen beide Bewegungen zurück auf Null. Kein Layoutsprung, kein Reflow.
   -------------------------------------------------------------------------- */
.talk {
  --talk-h: clamp(150px, 21vw, 280px);
  --talk-dauer: 900ms;
  position: relative;
  padding: clamp(72px, 12vh, var(--space-3xl)) var(--gutter);
  border-top: 1px solid var(--line);
  text-align: center;
  overflow: hidden;
}

.talk__wort {
  display: block;
  font-size: clamp(2rem, 6vw, 5rem);
  transition: transform var(--talk-dauer) var(--ease-out);
}
.talk__wort--unten { color: var(--cream); }

.talk__mitte {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  gap: var(--space-md);
  height: var(--talk-h);
}

.talk__seite {
  color: var(--cream-dim);
  transition:
    transform var(--talk-dauer) var(--ease-out) 260ms,
    opacity   var(--talk-dauer) var(--ease-out) 260ms;
}
.talk__seite--links  { text-align: right; }
.talk__seite--rechts { text-align: left; }

.talk__kachel {
  position: relative;
  display: block;
  width: calc(var(--talk-h) * 16 / 9);
  max-width: 100%;
  height: var(--talk-h);
  overflow: hidden;
  border-radius: 6px;
  background: #14110f;
  transform-origin: 50% 50%;
  transition: transform var(--talk-dauer) var(--ease-out) 80ms;
}
.talk__kachel img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
.talk__kachel:hover img { transform: scale(1.04); }

/* Der runde Knopf in der Mitte der Kachel. Dunkel und leicht durchscheinend,
   damit er auf jedem Motiv sitzt; beim Überfahren wird er zur Creme-Fläche. */
.talk__knopf {
  position: absolute;
  top: 50%;
  left: 50%;
  display: grid;
  place-items: center;
  width: clamp(88px, 9vw, 120px);
  height: clamp(88px, 9vw, 120px);
  border-radius: 50%;
  background: rgba(20, 17, 15, 0.82);
  backdrop-filter: blur(6px);
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transform: translate(-50%, -50%);
  /* Das Wachsen läuft über die eigenständige scale-Eigenschaft, nicht über
     transform: transform gehört dem Skript, das den Knopf am Zeigegerät dem
     Zeiger nachführen lässt (main.js, initTalk). Beides in einer Eigenschaft
     würde sich gegenseitig überschreiben. */
  transition:
    background-color var(--dur-fast) var(--ease-out),
    color var(--dur-fast) var(--ease-out),
    scale var(--dur-base) var(--ease-out);
}
.talk__kachel:hover .talk__knopf,
.talk__kachel:focus-visible .talk__knopf {
  background: var(--cream);
  color: var(--bg);
  scale: 1.06;
}
.talk__kachel:focus-visible { outline: 2px solid var(--cream); outline-offset: 4px; }

/* Geschlossener Zustand: solange der Zeiger nicht über dem Abschnitt steht
   und nichts darin den Fokus hat. is-offen setzt main.js dort, wo es keinen
   Zeiger gibt — dann gilt die Regel nie. Ohne JavaScript steht alles offen. */
.js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__wort--oben  { transform: translateY(calc(var(--talk-h) / 2)); }
.js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__wort--unten { transform: translateY(calc(var(--talk-h) / -2)); }
.js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__kachel      { transform: scaleY(0); transition-delay: 0ms; }
.js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__seite--links  { opacity: 0; transform: translateX(32px); transition-delay: 0ms; }
.js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__seite--rechts { opacity: 0; transform: translateX(-32px); transition-delay: 0ms; }

@media (max-width: 699px) {
  .talk { --talk-h: clamp(140px, 38vw, 220px); }
  /* Name und Rolle passen neben die Kachel nicht mehr — sie rücken darunter,
     nebeneinander. Die Mittelzeile wächst um diese eine Zeile. */
  .talk__mitte {
    grid-template-columns: auto auto;
    grid-template-rows: var(--talk-h) auto;
    justify-content: center;
    column-gap: var(--space-md);
    row-gap: var(--space-sm);
    height: auto;
  }
  .talk__kachel { grid-column: 1 / -1; grid-row: 1; justify-self: center; }
  .talk__seite--links, .talk__seite--rechts { grid-row: 2; text-align: center; }
  .js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__wort--oben  { transform: translateY(calc(var(--talk-h) / 2 + 20px)); }
  .js .talk:not(.is-offen):not(:hover):not(:focus-within) .talk__wort--unten { transform: translateY(calc(var(--talk-h) / -2 - 20px)); }
}

@media (prefers-reduced-motion: reduce) {
  .talk__wort, .talk__kachel, .talk__seite, .talk__kachel img { transition: none; }
}

/* --------------------------------------------------------------------------
   15. INHALTSSEITEN (About / Kontakt)
   -------------------------------------------------------------------------- */
.page-head {
  padding-top: calc(var(--header-h) + clamp(48px, 12vh, 140px));
  padding-bottom: clamp(40px, 8vh, 96px);
}

.two-col {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.35fr);
  gap: clamp(32px, 6vw, 96px);
  align-items: start;
}
@media (max-width: 899px) { .two-col { grid-template-columns: 1fr; } }
/* Ohne min-width: 0 kann eine 1fr-Spalte nicht unter die Mindestbreite
   ihres Inhalts schrumpfen. Die Zahlenreihe im DoP-Abschnitt verlangte
   398 px, verfügbar waren 320 — die Spalte lief nach rechts aus dem Bild,
   und nur das overflow-x: hidden am body hat es verdeckt. */
.two-col > * { min-width: 0; }
/* Spalte, die sich an der Höhe der Nachbarspalte ausrichtet — der Text neben
   dem Porträt sitzt so auf dessen Mitte statt oben an der Kante. */
.two-col__mitte { align-self: center; }
/* Die Überschrift links hat nur gut 40 % der Breite. .t-large ist für die
   volle Breite bemessen — in der Spalte lief „ANGABEN" bei 1156 px um 30
   Pixel in den Spaltenzwischenraum und die Überschrift brach in sieben
   Zeilen. Hier eine Stufe kleiner, mit dem gleichen Verlauf. */
.two-col .t-large { font-size: clamp(1.6rem, 2.9vw, 2.9rem); }   /* 2.9vw: „GESCHICHTE" passt so auch bei 1024 px in die Spalte */
@media (max-width: 899px) { .two-col .t-large { font-size: clamp(1.75rem, 4.5vw, 3.5rem); } }


/* Kontaktspalten */
.contact-cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
  gap: clamp(32px, 4vw, 64px);
}
.contact-col__title {
  font-family: var(--font-display);
  font-weight: 700;
  /* 2.3vw statt 3.5vw: Bei drei Spalten ab 900 px hat jede rund ein
     Drittel der Breite, und „ANSPRECH-" in Syne 800 läuft breit — mit
     3.5vw lief der Titel bis 1300 px in die Nachbarspalten hinein. */
  font-size: clamp(1.4rem, 2.3vw, 2.5rem);
  line-height: 1.05;
  margin-bottom: var(--space-md);
}
.contact-col__list { font-family: var(--font-mono); font-size: 0.8125rem; line-height: 2.1; }
.contact-col__list dt { color: var(--cream-mute); letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.6875rem; }
.contact-col__list dd { margin: 0 0 var(--space-sm); }
.contact-col__list a {
  display: inline-flex;
  align-items: center;
  min-height: 32px;
  color: var(--cream);
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--dur-fast) var(--ease-out);
}
.contact-col__list a:hover { border-bottom-color: var(--cream); }

/* Zwischen 560 und 899 px passen zwei Spalten, aber nicht drei. Dann standen
   „Anfragen" und „Ansprechpartner" nebeneinander und „Social" allein in
   der zweiten Reihe unter „Anfragen" — mit einem Loch daneben. Jetzt nimmt
   Social die ganze zweite Reihe und legt seine drei Einträge nebeneinander:
   Beschriftungen in der ersten Zeile, Namen in der zweiten. */
@media (min-width: 560px) and (max-width: 899px) {
  /* minmax(0, 1fr): sonst darf eine Spalte nicht schmaler werden als ihr
     längstes Wort — „ANFRAGEN" in Syne 800 hielt beide Spalten auf 258 px
     und die Reihe lief bei 560 px um 48 px aus dem Fenster. */
  .contact-cols { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .contact-col { min-width: 0; }
  .contact-col__title { font-size: clamp(1.4rem, 4vw, 2.1rem); }
  .contact-col:nth-child(3) { grid-column: 1 / -1; }
}
/* Die drei Social-Einträge nebeneinander brauchen gut 520 px — erst ab 660
   passen sie; darunter bleibt die Liste untereinander, aber mittig. */
@media (min-width: 660px) and (max-width: 899px) {
  .contact-col:nth-child(3) .contact-col__list {
    display: grid;
    grid-template-columns: repeat(3, auto);
    justify-content: center;
    column-gap: clamp(24px, 5vw, 56px);
  }
  .contact-col:nth-child(3) .contact-col__list dt { grid-row: 1; }
  .contact-col:nth-child(3) .contact-col__list dd { grid-row: 2; margin-bottom: 0; }
}

/* Großer Text-Link / CTA */
.cta-link {
  display: inline-flex;
  align-items: center;
  gap: var(--space-sm);
  min-height: 48px;
  font-family: var(--font-display);
  font-size: clamp(1.75rem, 5vw, 3.5rem);
  line-height: 1.1;
  color: var(--cream);
}
.cta-link__arrow {
  display: inline-block;
  transition: transform var(--dur-base) var(--ease-out);
}
.cta-link:hover .cta-link__arrow { transform: translateX(10px); }

/* Textlink inline */
.text-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  min-height: 44px;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--cream);
  border-bottom: 1px solid var(--line-strong);
  transition: border-color var(--dur-fast) var(--ease-out), gap var(--dur-fast) var(--ease-out);
}
.text-link:hover { border-bottom-color: var(--cream); gap: 14px; }
.on-paper .text-link { color: var(--ink); border-bottom-color: var(--line-ink); }
.on-paper .text-link:hover { border-bottom-color: var(--ink); }

/* Fließtext-Seiten (Impressum, Datenschutz) */
.prose { max-width: 68ch; }
.prose h2 {
  margin: var(--space-xl) 0 var(--space-sm);
  font-family: var(--font-ui);
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--cream);
}
.prose h3 {
  margin: var(--space-lg) 0 var(--space-2xs);
  font-family: var(--font-mono);
  font-size: 0.75rem;
  font-weight: 500;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-dim);
}
.prose p,
.prose li {
  font-size: 1rem;
  line-height: 1.75;
  color: var(--cream-dim);
}
.prose p + p { margin-top: var(--space-sm); }
.prose ul {
  margin: var(--space-sm) 0;
  padding-left: 1.2em;
  list-style: disc;
}
.prose li { margin-bottom: var(--space-3xs); }
.prose a {
  color: var(--cream);
  border-bottom: 1px solid var(--line-strong);
}
.prose a:hover { border-bottom-color: var(--cream); }
.prose address { font-style: normal; }

/* Seiten-Footer */
.site-footer {
  border-top: 1px solid var(--line);
  padding: var(--space-xl) 0 calc(var(--footer-h) + var(--space-lg));
}
/* Die Statusleiste ist hier nicht mehr fixiert — kein Platzhalter nötig */
@media (max-width: 719px) {
  .site-footer { padding-bottom: var(--space-xl); }
}
.site-footer__grid {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-md) var(--space-xl);
  justify-content: space-between;
  align-items: flex-end;
}
.site-footer nav { display: flex; flex-wrap: wrap; gap: var(--space-md); }
.site-footer nav a {
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--cream-dim);
  transition: color var(--dur-fast) var(--ease-out);
}
.site-footer nav a:hover { color: var(--cream); }

/* --------------------------------------------------------------------------
   16. SCROLL-REVEAL — ohne JS bleibt alles sichtbar
   -------------------------------------------------------------------------- */
.js .anim-up {
  opacity: 0;
  transform: translateY(28px);
}
.js .anim-fade { opacity: 0; }

/* --------------------------------------------------------------------------
   16b. LOGO-SPRACHE FÜR ALLE ÜBERSCHRIFTEN

   Die Wortmarke ist eine fette, geneigte, schmal laufende Grotesk in
   Versalien. Damit die Seite nicht wie ein anderes Unternehmen aussieht als
   das Logo, tragen alle Überschriften dieselben Merkmale. Steht bewusst weit
   unten in der Datei, damit sie die einzelnen Bausteine überschreibt.
   -------------------------------------------------------------------------- */
.reel__title,
.work-row__title,
.contact-col__title,
.cta-link,
.next-project .display,
.mobile-menu nav a {
  font-family: var(--font-display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.022em;
  line-height: 0.98;
}

/* Versalien tragen weiter als Gemischtsatz — die Grade werden entsprechend
   etwas zurückgenommen, damit lange Titel nicht die Zeile sprengen. */
.reel__title      { font-size: clamp(2.2rem, 6.8vw, 5.8rem); line-height: 0.9; }
.work-row__title  { font-size: clamp(1.4rem, 3.8vw, 2.7rem); }
/* Sobald das Zeilenbild neben dem Titel steht (unter 900 px), wird die
   Titelspalte schmaler; „Meerane" brach sonst mitten im Wort. Muss hier
   stehen und nicht bei .work-row: gleiche Spezifität, die spätere Regel
   gewinnt. */
@media (max-width: 899px) {
  .work-row__title { font-size: clamp(1.1rem, 3.1vw, 2rem); }
}
@media (max-width: 719px) {
  .work-row__title { font-size: clamp(1rem, 5vw, 1.5rem); }
}
.mobile-menu nav a { font-size: clamp(2rem, 9.5vw, 3.4rem); line-height: 1.06; }
.cta-link         { font-size: clamp(1.5rem, 4vw, 2.9rem); }

/* Der Projekttitel auf heller Fläche folgt derselben Sprache */
.project-title {
  font-family: var(--font-display);
  font-weight: 800;
  letter-spacing: -0.028em;
}

/* --------------------------------------------------------------------------
   16c. SCHRIFTGRADE AUF SCHMALEN GERÄTEN

   Syne läuft in 800 deutlich breiter als die vorherige Schrift. „Produktionen."
   maß auf dem Telefon 398 px bei 335 px Platz, „Sachsenrallye" sogar 616 px —
   beides lief rechts aus dem Bild.

   Dieser Block steht bewusst ganz unten: Die Grade für .project-title und
   .reel__title werden weiter oben gesetzt. Stünde die Abfrage davor, gewönne
   trotz Media Query die spätere Regel — gleiche Spezifität, und dann
   entscheidet die Reihenfolge.
   -------------------------------------------------------------------------- */
@media (max-width: 479px) {
  .t-hero        { font-size: clamp(2rem, 8.6vw, 2.75rem); }
  .t-section     { font-size: clamp(1.5rem, 6.6vw, 2rem); }
  .t-large       { font-size: clamp(1.3rem, 5.8vw, 1.75rem); }
  .reel__title   { font-size: clamp(1.5rem, 6.6vw, 2.3rem); }
  .project-title { font-size: clamp(1.9rem, 8.2vw, 3rem); }

  /* Deutsche Wörter wie „Sachsenrallye" oder „Elbsandsteingebirge" passen in
     keiner vernünftigen Größe in 335 Pixel. Statt die Schrift noch weiter zu
     verkleinern, wird getrennt: Der Browser kennt die Trennstellen, weil in
     jeder Datei lang="de" steht. break-word ist nur das Netz darunter, falls
     kein Wörterbuch vorhanden ist. */
  .display,
  .reel__title,
  .project-title,
  .work-row__title,
  .contact-col__title {
    hyphens: auto;
    overflow-wrap: break-word;
  }
}

/* --------------------------------------------------------------------------
   17. REDUZIERTE BEWEGUNG
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .grain { animation: none; }
  .js .anim-up,
  .js .anim-fade { opacity: 1; transform: none; }
  .hover-preview { display: none; }
  .reel__track { scroll-behavior: auto; }
  .reel__media { transform: none; }
  .reel__panel:hover .reel__media { transform: none; }
  .reel__panel:hover .reel__title { transform: none; }
  .reel-cursor { display: none; }
}
