/*
  js-seite.css — justsoap, Demo.

  Farbwerte aus dem Kundenbriefing, Abschnitt 6.3 (dort ausdruecklich als
  Arbeitsgrundlage bezeichnet, nicht als festes Corporate Design).

  Harte Regeln aus dem Briefing, die hier verdrahtet sind:
    - vollflaechig dunkel, randlos, keine hellen Module zwischen dunklen
    - je Abschnitt hoechstens ZWEI Schriftgroessen: Display und Fliesstext
    - mindestens 40 % jeder Modulflaeche bleibt leer
    - keine Verlaeufe, keine Schlagschatten, keine Rahmen, keine runden Ecken
    - nie zwei Sortenfarben gleichzeitig in einem Modul

  Schrift: fuer die Demo Systemschriften. Der echte Bau braucht eine
  lizenzierte, selbst gehostete Schmalgrotesk — KEIN Google-CDN (DSGVO).

  --sorte wird je Abschnitt im HTML gesetzt. Beim Anhaengen von Sakura und
  Ocean aendert sich nur dieser eine Wert, keine Regel hier drin.
*/

:root {
  --tanne:     #1F2E26;
  /* Nach den Fahrten wird die Seite hell. Ein warmes Naturgrau, mehr Weiss
     als Grau — das Dunkle gehoert der Animation, nicht dem Lesen. */
  --hell:      #EDEBE6;
  --hellrand:  #DCD8D0;
  --tinte:     #1A1A18;
  --kalkweiss: #FFFDF8;
  --nebel:     #9AA79F;
  --tief:      #16211C;

  --sorte: #2C2C2A;                    /* wird je Abschnitt ueberschrieben */

  --display: ui-sans-serif, "Helvetica Neue", "Arial Narrow", system-ui, sans-serif;
  --text:    ui-sans-serif, system-ui, -apple-system, sans-serif;

  --rand: clamp(20px, 5vw, 72px);
  --luft: clamp(72px, 14vh, 190px);    /* Abstand zwischen Abschnitten */
}

* { box-sizing: border-box; }
html {
  scroll-behavior: auto;
  /* 🔴 NIEMALS `overflow-x: hidden` hierher. Das macht das Wurzelelement zum
     Scrollbehaelter, und damit hoert JEDES `position: sticky` auf der Seite
     auf zu kleben — Buehne, Reifung, Bahn, alles. Am 06.08.2026 selbst
     hineingelaufen und am gruenen leeren Bild gemerkt.
     Die 5 px Ueberbreite kamen vom Nebel und werden dort geklemmt, wo sie
     entstehen: `.js-fakten { overflow: clip }`. `clip` schneidet ab, ohne
     einen Scrollbehaelter aufzumachen. */
  overscroll-behavior-y: none;
}

body {
  margin: 0;
  background: var(--tanne);
  color: var(--kalkweiss);
  font-family: var(--text);
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

/* ── Typografie ──────────────────────────────────────────────────────── */
.gross {
  font-family: var(--display);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -0.02em;
  line-height: 0.86;
  font-size: clamp(2.4rem, 10vw, 8rem);
  margin: 0;
}
.mittel {
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.01em;
  line-height: 0.95;
  font-size: clamp(1.5rem, 4.6vw, 3.2rem);
  margin: 0;
}
.lauf {
  font-size: clamp(1rem, 1.4vw, 1.12rem);
  line-height: 1.62;
  color: var(--nebel);
  max-width: 34em;
  margin: 0;
}
.klein {
  margin: 0;
  letter-spacing: 0.42em;
  text-transform: uppercase;
  font-size: clamp(0.62rem, 1.05vw, 0.8rem);
  color: var(--kalkweiss);
}
.zart { opacity: 0.62; }

/* ── Der Einzug ──────────────────────────────────────────────────────
   Alles mit data-einzug startet unsichtbar und leicht tiefer. js-einzug.js
   setzt `da`, sobald es ins Bild kommt. Ohne JavaScript bleibt alles
   sichtbar — `wartet` wird erst per Skript vergeben, nie im Grundzustand. */
[data-einzug].wartet {
  opacity: 0;
  transform: translateY(26px);
}
[data-einzug].wartet.da {
  opacity: 1;
  transform: none;
  transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.75s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ── Die Buehne ──────────────────────────────────────────────────────
   Eine klebende Flaeche, drei Lagen uebereinander. Man scrollt nicht zu
   einer neuen Stelle — die naechste Sorte wischt hier herein. */
.js-buehne { position: relative; }
.js-buehne .klebt {
  position: sticky;
  top: 0;
  height: 100svh;
  width: 100%;
  overflow: hidden;
  background: #0B0F0D;
}
.js-buehne .lagen { position: absolute; inset: 0; }
.js-buehne .lage {
  position: absolute;
  inset: 0;
  /* Der Wisch laeuft ueber clip-path, nicht ueber transform: so bleibt die
     Leinwand an ihrem Platz und muss nicht neu gemessen werden. */
  clip-path: inset(0 0 0 100%);
  will-change: clip-path;
}
.js-buehne .lage canvas { display: block; width: 100%; height: 100%; }

/* Das Sortenzeichen. Oben links, INNERHALB des Fensters, gross genug zum
   Lesen. Es wechselt mit der Sorte — die Tafeln stehen unten, sie kommen
   sich also nicht in die Quere. */
.js-buehne .zeichen {
  position: absolute;
  /* An der echten Bildkante, nicht am Fensterrand: --bildL/--bildO kommen
     aus js-kino.js und sagen, wo die Aufnahme tatsaechlich anfaengt. Plus
     etwas Innenabstand, damit das Zeichen nicht auf der Kante klebt. */
  left: calc(var(--bildL, 0px) + clamp(18px, 2.4vw, 42px));
  top: calc(var(--bildO, 0px) + clamp(18px, 2.4vw, 42px));
  width: clamp(96px, 12vw, 184px);
  z-index: 5;
  pointer-events: none;
  filter: drop-shadow(0 4px 18px rgba(0,0,0,0.5));
}

/* Immer nur EINE Tafel im Bild. --u ist der Fortschritt der gerade
   laufenden Fahrt; min() bildet Rampe hinauf und hinunter ab, und opacity
   kappt selbst auf 0 und 1 — dafuer braucht es keine Zeile JavaScript. */
.js-buehne .tafelsatz {
  position: absolute;
  left: calc(var(--bildL, 0px) + clamp(18px, 2.4vw, 42px));
  right: calc(var(--bildL, 0px) + clamp(18px, 2.4vw, 42px));
  bottom: calc(var(--bildO, 0px) + clamp(18px, 2.4vw, 42px) + 3.2rem);
  z-index: 5;
  pointer-events: none;
}
.js-buehne .tafelsatz[hidden] { display: none; }
/* Von wann bis wann eine Tafel steht. Frueher waren die vier Zeitfenster fest
   einprogrammiert — das passt fuer die drei Sortenfahrten, aber NICHT fuer die
   gemeinsame Fahrt durch alle drei Sorten: die schaltet nach gemessenen
   Bildern bei 0,17 und 0,56 um, und dort stand „Sakura Spirit" ueber der
   blauen Seife. Deshalb sind --von, --bis und --tempo jetzt einstellbar; im
   HTML steht bei der gemeinsamen Fahrt der gemessene Wert. */
.js-buehne .tafel {
  position: absolute; left: 0; bottom: 0; max-width: 18em;
  --von: 0.18; --bis: 0.44; --tempo: 12;
  opacity: min((var(--u,0) - var(--von)) * var(--tempo),
               (var(--bis) - var(--u,0)) * var(--tempo));
}
.js-buehne .tafel[data-beat="2"] { --von: 0.42; --bis: 0.66; }
.js-buehne .tafel[data-beat="3"] { --von: 0.64; --bis: 0.86; }
/* Die letzte Tafel bleibt bis zum Schluss stehen: --bis liegt hinter dem Ende. */
.js-buehne .tafel[data-beat="4"] { --von: 0.84; --bis: 9; }
.js-buehne .tafel b {
  display: block; font-family: var(--display); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.02em;
  font-size: clamp(1rem, 1.9vw, 1.5rem);
}
.js-buehne .tafel span {
  display: block; color: var(--nebel); margin-top: 3px;
  font-size: clamp(0.8rem, 1.15vw, 1rem);
}
.js-buehne .tafel::after {
  content: ""; display: block; height: 1px; background: var(--nebel);
  opacity: 0.5; margin-top: 12px; width: 100%;
}

.js-buehne .sortenname {
  position: absolute;
  right: calc(var(--bildL, 0px) + clamp(18px, 2.4vw, 42px));
  bottom: calc(var(--bildO, 0px) + clamp(18px, 2.4vw, 42px));
  margin: 0; z-index: 5; pointer-events: none;
  text-transform: uppercase; letter-spacing: 0.42em; color: var(--nebel);
  font-size: clamp(0.62rem, 1vw, 0.78rem);
}

/* ── Der Empfang ─────────────────────────────────────────────────────
   Die Marke liegt auf der Seife und sagt, was zu tun ist. Ohne diesen
   Moment weiss niemand, dass die Seite ueberhaupt etwas kann.
   Verschwindet beim ersten Scrollimpuls — --t ist der Fortschritt der
   ganzen Buehne, 0.015 davon sind rund eine halbe Umdrehung am Rad. */
.js-buehne .empfang {
  position: absolute;
  inset: 0;
  z-index: 8;
  display: grid;
  place-content: center;
  justify-items: center;
  text-align: center;
  gap: 20px;
  padding: var(--rand);
  pointer-events: none;
  background: rgba(8, 12, 10, 0.42);
  opacity: calc(1 - var(--t, 0) * 60);
}
.js-buehne .empfang img {
  width: clamp(160px, 26vw, 320px);
  filter: brightness(0) invert(1);
}
.js-buehne .empfang .scroll {
  margin: clamp(18px, 4vh, 44px) 0 0;
  font-family: var(--display); font-weight: 700;
  text-transform: uppercase; letter-spacing: 0.4em;
  font-size: clamp(0.7rem, 1.2vw, 0.9rem);
}
.js-buehne .empfang .pfeil {
  display: block; width: 1px; height: 46px; margin: 16px auto 0;
  background: var(--kalkweiss); opacity: 0.6;
  animation: pfeil-atmen 2.2s ease-in-out infinite;
  transform-origin: top;
}
@keyframes pfeil-atmen {
  0%, 100% { transform: scaleY(0.35); opacity: 0.25; }
  50%      { transform: scaleY(1);    opacity: 0.75; }
}


/* ── Die Fakten je Sorte ─────────────────────────────────────────────
   Zwei Kundenaufnahmen, die beim Scrollen ineinander uebergehen: erst die
   Verpackung, dann die Inhaltsstoffe. Sie werden NICHT freigestellt — sie
   haben einen gestalteten Hintergrund, und den nimmt der Abschnitt an.
   (Regel und Messwerkzeug: scripts/bilder_waehlen.py)

   Der Abschnitt faerbt sich MIT: --grundA ist der Grund der ersten
   Aufnahme, --grundB der der zweiten. Vorher lag die Bildfarbe nur zu 16 %
   ueber dem alten Gruen — deshalb blieb es gruen, obwohl das Bild rosa ist. */
.js-fakten {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 5vw, 90px);
  align-items: start;
  padding: var(--luft) var(--rand);
  background: var(--grundA);
  /* Der Nebel driftet um 5 % nach rechts aus dem Abschnitt heraus — genau die
     5 px Ueberbreite, die die ganze Seite am Handy waagerecht wackeln liessen.
     `clip` statt `hidden`: es schneidet ab, macht aber keinen Scrollbehaelter
     auf und laesst das klebende Produktbild in Ruhe. */
  overflow: clip;
}
/* Der Grund wechselt von der Farbe der ersten Aufnahme zu der der zweiten.
   Frueher lief das ueber `animation-timeline: view()` — das kann Chrome, aber
   nicht jedes iPhone. Jetzt liegt die Zielfarbe als Schicht darueber und wird
   ueber --p aufgeblendet; --p kommt aus js-scroll.js und laeuft ueberall. */
.js-fakten::before {
  content: "";
  position: absolute; inset: 0; z-index: 0;
  background: var(--grundB);
  opacity: clamp(0, (var(--p, 0) - 0.18) / 0.42, 1);
}
/* Auf hellem Grund (Sakura) muss die Schrift umschlagen. */
.js-fakten[data-hell] { color: var(--tinte); }
.js-fakten[data-hell] .lauf, .js-fakten[data-hell] .klein { color: #5A5A54; }

/* Der Nebel. Zwei sehr weiche Wolken, die langsam gegeneinander treiben —
   das Bild atmet, ohne dass etwas blinkt. Farbe kommt aus dem Grund des
   Bildes, aufgehellt; so passt er in jedem Abschnitt von selbst.
   Vorbild: die handbemalte Studiowand aus Ramons Ocean-Drive-Aufnahme. */
.js-fakten .nebel {
  position: absolute; inset: 0; pointer-events: none; z-index: 0;
  opacity: 0.5;
  background:
    radial-gradient(58vw 44vh at 22% 28%,
      color-mix(in oklab, var(--grundB) 40%, #FFFFFF) 0%, transparent 62%),
    radial-gradient(52vw 40vh at 78% 72%,
      color-mix(in oklab, var(--grundA) 55%, #FFFFFF) 0%, transparent 60%);
  animation: nebel-treiben 46s ease-in-out infinite alternate;
  will-change: transform;
}
@keyframes nebel-treiben {
  from { transform: translate3d(-4%, -2%, 0) scale(1.08); }
  to   { transform: translate3d(5%, 3%, 0)  scale(1.16); }
}
.js-fakten .produkt, .js-fakten .strang { position: relative; z-index: 1; }
.js-fakten .produkt { position: sticky; top: 12svh; }

/* Der Bildwechsel: das neue Bild kommt VON UNTEN und schiebt das alte nach
   oben hinaus. Ein Ueberblenden liest sich als Fehler — man sieht kurz zwei
   halbe Bilder. Ein Schub liest sich als Absicht. */
.js-fakten .bildpaar {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
}
/* --schub laeuft von 0 auf 1, waehrend der Block durch die Mitte des Fensters
   zieht. Beide Bilder haengen an derselben Zahl, deshalb koennen sie nie
   auseinanderlaufen — es ist IMMER genau eines im Kasten zu sehen.

   🔴 Genau hier lag das „am Handy doppelt": vorher stand die Bewegung in
   `animation-timeline: view()`, und wo das fehlte, standen beide Fotos
   untereinander. Ausserdem hat eine Handy-Regel die Bildbreite auf 66 %
   gesetzt — dann lagen zwei angeschnittene Bilder uebereinander. Beides raus. */
.js-fakten .bildpaar { --schub: clamp(0, (var(--p, 0) - 0.30) / 0.30, 1); }
.js-fakten .bildpaar img {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  will-change: transform;
}
.js-fakten .bildpaar img:first-child { transform: translateY(calc(var(--schub) * -100%)); }
.js-fakten .bildpaar img:last-child  { transform: translateY(calc((1 - var(--schub)) * 100%)); }
/* Steht nur EINE Aufnahme im Kasten, bewegt sich nichts. Sie ist zugleich
   erstes und letztes Kind — ohne diese Zeile griffe die Regel fuer das letzte
   und schoebe das einzige Bild aus dem Bild heraus.
   Ramon 06.08.2026: „Dieses erste Bild, wo diese Seife mit Karton ist, kannst
   du komplett weglassen. Da ist wichtig, dass diese Seife mit diesen ganzen
   Elementen drinnen ist." Der Wechsel sah am Handy unruhig aus; jetzt zeigt
   jede Sorte genau die Aufnahme, die er ausgesucht hat. */
.js-fakten .bildpaar img:only-child  { transform: none; }

.js-fakten .strang { display: grid; gap: clamp(56px, 12vh, 150px); }
.js-fakten .kopf { padding-bottom: 8px; }
.js-fakten .karte h3 { margin: 14px 0 16px; overflow-wrap: anywhere; hyphens: auto; }

/* Der Kaufteil zeigt jetzt Ramons Originalaufnahmen, nicht Freisteller. */
.js-kaufen article img { width: 100%; aspect-ratio: 1/1; object-fit: cover; }

/* ── Siegel ──────────────────────────────────────────────────────────── */
.js-siegel { padding: var(--luft) var(--rand); background: var(--hell); color: var(--tinte); }
.js-siegel .reihe {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: clamp(26px, 4vw, 56px);
  margin-top: clamp(36px, 6vh, 72px);
}
.js-siegel figure { margin: 0; text-align: center; }
.js-siegel img { width: clamp(64px, 7vw, 96px); margin: 0 auto 14px; }
.js-siegel figcaption {
  font-size: 0.72rem; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--nebel);
}
.fakten-liste {
  list-style: none; margin: clamp(40px, 7vh, 84px) 0 0; padding: 0;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 18px clamp(24px, 4vw, 60px);
}
.fakten-liste li {
  padding-top: 14px;
  border-top: 1px solid color-mix(in oklab, var(--nebel) 40%, transparent);
  color: var(--nebel); font-size: clamp(0.9rem, 1.2vw, 1rem);
}

/* ── Kaufen ──────────────────────────────────────────────────────────── */
.js-kaufen { padding: var(--luft) var(--rand); background: var(--hell); color: var(--tinte); }
.js-kaufen .reihe {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
  gap: clamp(24px, 3.5vw, 56px); margin-top: clamp(36px, 6vh, 72px);
}
.js-kaufen article {
  display: grid; justify-items: center; gap: 14px;
  padding: clamp(22px, 3vw, 42px);
  background: color-mix(in oklab, var(--sorte) 40%, var(--tief));
}
.js-kaufen article img { width: 76%; }
.js-kaufen .preis { font-family: var(--display); font-weight: 800; margin: 0;
  font-size: clamp(1.3rem, 3vw, 2rem); }
.js-kaufen .preis.offen { color: var(--nebel); }
.js-kaufen .preis.offen span {
  display: block; font-family: var(--text); font-weight: 400;
  font-size: 0.68rem; letter-spacing: 0.2em; text-transform: uppercase; margin-top: 6px;
}
.js-kaufen .knopf {
  display: inline-block; padding: 15px 34px; background: var(--kalkweiss);
  color: #121212; text-decoration: none; font-family: var(--display);
  font-weight: 700; letter-spacing: 0.1em; text-transform: uppercase; font-size: 0.85rem;
}

/* ── Der Fuss ────────────────────────────────────────────────────────── */
.js-fuss {
  display: grid; justify-items: center; gap: 16px;
  padding: var(--luft) var(--rand); background: var(--tief);
}
.js-fuss img { width: clamp(120px, 16vw, 200px); filter: brightness(0) invert(1); opacity: 0.92; }

/* ── Handy ───────────────────────────────────────────────────────────── */
@media (max-width: 900px) {
  .js-fakten { grid-template-columns: 1fr; }
  /* Eine Spalte: das Produkt kann nicht kleben, seine Gitterzelle ist nur so
     hoch wie es selbst. Es zieht also mit vorbei — und der Bildwechsel haengt
     dann an SEINEM eigenen Fortschritt (--pb), nicht am Abschnitt. Sonst
     waere der Wechsel schon durch, bevor das Bild ueberhaupt im Bild ist. */
  .js-fakten .produkt { position: static; }
  .js-fakten .bildpaar { --schub: clamp(0, (var(--pb, 0) - 0.34) / 0.30, 1); }
  .js-buehne .zeichen { width: 84px; }
  .js-buehne .tafelsatz { bottom: calc(var(--rand) + 4.4rem); }

  /* Der Nebel steht am Handy still. Drei bildschirmgrosse, weichgezeichnete
     Flaechen, die dauerhaft wandern, sind auf einem Telefon drei Ebenen, die
     der Browser laufend neu zeichnen muss — waehrend daneben die Bildfolge
     laeuft. Der Verlauf bleibt, nur die Bewegung geht. Sehen tut man den
     Unterschied nicht, spueren schon. */
  .js-fakten .nebel { animation: none; will-change: auto; }
}

/* ── Wer keine Bewegung will, bekommt trotzdem eine ganze Seite ──────── */
@media (prefers-reduced-motion: reduce) {
  .js-buehne .klebt { position: static; }
  .js-buehne .lage { clip-path: none; position: static; }
  .js-buehne .empfang { position: static; opacity: 1; }
  .js-buehne .tafelsatz { position: static; display: grid; gap: 22px; padding: var(--rand); }
  .js-buehne .tafelsatz[hidden] { display: grid; }
  .js-buehne .tafel { position: static; opacity: 1; }
  .js-buehne .zeichen, .js-buehne .sortenname { position: static; }
  .js-buehne .empfang .pfeil { animation: none; }
  /* Kein halber Bildwechsel: es steht genau EIN Foto im Kasten, das zweite. */
  .js-fakten .bildpaar { --schub: 1; }
  .js-fakten .nebel { animation: none; }
}

/* ── Die Reifung ─────────────────────────────────────────────────────
   Der Zaehler steigt beim Scrollen — Vorbild ist das Thermometer aus dem
   Doener-Kapitel „The Roast", nur mit Zeit statt Hitze.

   Frueher lief er ueber `@property` + `counter()` + `animation-timeline` —
   drei Neuheiten hintereinander, und am Handy stand er deshalb still auf 0.
   Jetzt schreibt js-scroll.js die Zahl direkt in das <span> und den
   Fortschritt als --p. Das laeuft in jedem Browser.
   42 bis 56 Tage sind 6 bis 8 Wochen: eine Zahl aus dem Material des Kunden. */
.js-reifung { position: relative; height: 260svh; background: var(--hell); color: var(--tinte); }
.js-reifung .klebt {
  position: sticky; top: 0; height: 100svh;
  display: grid; place-content: center; justify-items: center;
  gap: clamp(16px, 3vh, 34px); padding: var(--rand); text-align: center;
}

.js-reifung .zaehler {
  margin: 0; display: flex; align-items: baseline; gap: 0.3em;
  font-family: var(--display); font-weight: 800; line-height: 0.85;
}
.js-reifung .zahl { font-size: clamp(4rem, 18vw, 15rem); }
.js-reifung .einheit {
  font-size: clamp(1rem, 2.4vw, 2rem); color: var(--nebel);
  text-transform: uppercase; letter-spacing: 0.2em;
}

.js-reifung .skala {
  width: min(820px, 86vw);
  display: grid; grid-template-columns: repeat(5, 1fr);
  gap: 10px; margin-top: 8px; position: relative;
  padding-top: 26px;
}
/* Der Pegel steigt mit dem Zaehler. Volle Breite = 56 Tage. */
.js-reifung .pegel {
  position: absolute; left: 0; top: 0; height: 3px;
  width: calc(var(--p, 0) * 100%);
  background: var(--kalkweiss);
}
.js-reifung .skala::before {
  content: ""; position: absolute; left: 0; right: 0; top: 1px; height: 1px;
  background: var(--nebel); opacity: 0.35;
}
.js-reifung .skala span {
  font-size: clamp(0.58rem, 0.95vw, 0.74rem);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--nebel);
  /* Die Marke leuchtet auf, sobald der Pegel sie erreicht hat — und dann
     ganz. Die alte Formel endete bei 0,25: die letzte Marke „Woche 8 · fertig"
     blieb blass, obwohl der Pegel voll war. Genau die will man aber lesen. */
  opacity: clamp(0.25, (var(--p, 0) - var(--bei)) * 8 + 1, 1);
}
.js-reifung .mitte { margin: clamp(18px, 4vh, 40px) auto 0; text-align: center; }

@media (prefers-reduced-motion: reduce) {
  .js-reifung { height: auto; }
  .js-reifung .klebt { position: static; height: auto; }
}


/* ── Der helle Teil ──────────────────────────────────────────────────
   Nach den Fahrten wird die Seite hell. Alles, was fuer dunklen Grund
   gebaut war, muss hier umschlagen: Nebelgrau wird zu warmem Grau, die
   Siegel-Strichzeichnung von Kalkweiss zu Tinte. */
.js-siegel .lauf, .js-kaufen .lauf, .js-siegel figcaption, .js-reifung .klein, .js-reifung .lauf,
.js-siegel .klein, .js-kaufen .klein, .js-siegel h3, .js-kaufen h3, /* Die Siegel sind Kalkweiss auf Transparenz — auf hellem Grund unsichtbar.
   invert() dreht sie zu Tinte, ohne eine zweite Bilddatei. */
.js-siegel .reihe img { filter: invert(1) brightness(0.15); }
.fakten-liste li { color: #5A5A54; border-top-color: var(--hellrand); }
.js-reifung .skala::before { background: #B9B4AB; opacity: 1; }
.js-reifung .pegel { background: var(--tinte); }
.js-reifung .skala span { color: #5A5A54; }
.js-kaufen article { background: var(--sorte); color: var(--kalkweiss); }
.js-kaufen article .klein { color: var(--kalkweiss); }
.js-kaufen .preis.offen { color: rgba(255,253,248,0.7); }
.js-fuss { background: var(--tinte); }

@media (max-width: 900px) {
  /* Am Handy steht die Aufnahme in einem Band; die Aufschriften duerfen
     dort nicht mehr an der Bildkante haengen, sonst rutschen sie in die
     unscharfe Umgebung. Zurueck an den Fensterrand. */
  .js-buehne .zeichen { left: var(--rand); top: calc(var(--rand) + 4px); width: 76px; }
  .js-buehne .tafelsatz { left: var(--rand); right: var(--rand);
                          bottom: calc(var(--rand) + 3.6rem); }
  .js-buehne .sortenname { right: var(--rand); bottom: var(--rand); }
  .js-fakten .produkt { position: static; }
}

/* ── Kaufkarten: warme Toene aus den eigenen Aufnahmen ───────────────
   Vorher standen hier die rohen Markenfarben #993556 und #185FA5 — knallrosa
   und knallblau auf hellem Grau, ohne Bezug zu den Fotos darueber. Jetzt der
   gemessene Grund des jeweiligen Kaufbildes: #382922, #D5C6B5, #353C40. */
.js-kaufen article[data-hell] { color: var(--tinte); }
.js-kaufen article[data-hell] .klein { color: var(--tinte); }
.js-kaufen article[data-hell] .preis.offen { color: rgba(26,26,24,0.6); }
.js-kaufen article[data-hell] .knopf { background: var(--tinte); color: var(--hell); }

@media (max-width: 900px) {
  }

/* ── Die Bahn ────────────────────────────────────────────────────────
   Die sieben A++ Banner fahren am SCROLL entlang von rechts nach links.
   Man scrollt also weiter nach unten wie ueberall — und das Gleis bewegt
   sich waagerecht. Kein Wischen noetig, kein zweites Bediengeraet.

   Der Abschnitt ist so hoch wie die Bahn breit ist; die Buehne klebt und
   das Gleis wird ueber `animation-timeline: view()` verschoben. Damit
   haengt die Fahrt am Scrollbalken statt an einer Uhr und laeuft
   rueckwaerts von selbst rueckwaerts.

   Hell wie der Schlussteil — dunkel wirkte der Block hier wie ein Loch
   zwischen zwei hellen Flaechen. */
.js-bahn {
  position: relative;
  height: 330svh;
  background: var(--hell);
  color: var(--tinte);
}
.js-bahn .klebt {
  position: sticky; top: 0; height: 100svh;
  display: grid; align-content: center; gap: clamp(24px, 5vh, 56px);
  overflow: hidden;
}
.js-bahn h3 { padding: 0 var(--rand); }
.js-bahn .gleis {
  display: flex;
  /* Breite SELBST ausrechnen statt max-content. `-100%` bezieht sich sonst
     auf eine Breite, ueber die sich die Browser nicht einig sind: Chrome
     kommt auf 5285 px (Kachelbreite), WebKit auf 10493 px (natuerliche
     Bildbreite 1464 px). In Safari war die Bahn deshalb schon durch, bevor
     der Abschnitt zu Ende war — man sah nur noch die Ueberschrift. */
  --kachel: min(64vw, 720px);
  --luecke: clamp(14px, 2vw, 32px);
  --bahnbreite: calc(7 * var(--kachel) + 6 * var(--luecke) + var(--rand));
  --ende: 92vw;
  gap: var(--luecke);
  padding-left: var(--rand);
  width: var(--bahnbreite);
  /* Von der ersten Kachel bis zur letzten. --p kommt aus js-scroll.js und
     laeuft genau ueber die Strecke, in der der Abschnitt klebt — dieselbe
     Rechnung wie bei der grossen Buehne. Vorher hing das an
     `animation-timeline`, und wo das fehlte, war aus der Bahn eine
     wischbare Reihe geworden: am Handy also eine ganz andere Seite. */
  transform: translateX(calc(var(--p, 0) * (var(--ende) - var(--bahnbreite))));
  will-change: transform;
}
.js-bahn figure { margin: 0; flex: 0 0 var(--kachel); }
.js-bahn img { width: 100%; border-radius: 0; }

@media (max-width: 900px) {
  .js-bahn { height: 260svh; }
  .js-bahn figure { flex: 0 0 82vw; }
  .js-bahn .gleis { --kachel: 82vw; --ende: 88vw; }
}
@media (prefers-reduced-motion: reduce) {
  .js-bahn { height: auto; padding: var(--luft) 0; }
  .js-bahn .klebt { position: static; height: auto; }
  .js-bahn .gleis { transform: none; overflow-x: auto; width: auto;
                    padding-right: var(--rand); }
}
