/**
 * app.css — Look & Token aus Master-Prompt TEIL E.
 * Farben und Maße stehen als Custom Properties; Zahlen, die auch das JS
 * braucht, stehen in config.js und werden hier gespiegelt (--track-vh).
 */

:root {
  --void:       #07080A;   /* Grund – fast schwarz, nie #000 (Referenz misst rgb(8,7,6)) */
  --deepblue:   #0B1622;   /* Grund-Verlauf zur Mitte */
  --star:       #D4EEFE;   /* hellste Sterne (gemessen) */
  --starcold:   #9ECDF9;   /* kalte Sterne */
  --nebula-warm:#C4552E;   /* Nebel Rost (Gate 3) */
  --nebula-hot: #F0A868;   /* Nebel Kern (Gate 3) */
  --dust:       #160B08;   /* Staubbahnen – subtraktiv (Gate 3) */
  --glow:       #8CF0E3;   /* UI-Licht */
  --brass:      #C9A45C;   /* Akzent, sparsam */
  --bone:       #E9E3D4;   /* Text — auf --void 15,8:1, Teil F verlangt ≥ 7:1 */

  --track-vh: 1200;        /* MUSS zu CONFIG.journey.trackVh passen; app.js prüft das.
                              [D1 28.08.2026] 700 → 950: die Bahn ist hinten um
                              250vh länger (das Finale). Die Reise davor behält
                              ihre 700vh, siehe CONFIG.finale.reiseVh.
                              [S1 30.08.2026] 950 → 1200: noch einmal 250vh
                              hinten, der Weg zum Sonnensystem (CONFIG.sonne.vh). Die
                              Bahn trägt jetzt drei Abschnitte — 700 + 250 + 250;
                              app.js prüft auch diese Summe. */
}

/* UI-Schrift nach Teil E. */
@font-face {
  font-family: 'Space Grotesk';
  src: url('./assets/fonts/Space_Grotesk.woff2') format('woff2');
  font-weight: 300 700;
  font-display: swap;
}

/* Display-Schnitt. Teil E nennt Cormorant Garamond; der liegt nicht in der
   lokalen Bibliothek (46 Schriften), Cinzel schon — auf Entscheid des Nutzers
   vom 25.08.2026 wird Cinzel genommen, statt eine Schrift nachzuladen.
   Verwendung ab Gate 5 (Planeten-Labels) und Gate 6 (Kammer-Titel). */
@font-face {
  font-family: 'Cinzel';
  src: url('./assets/fonts/Cinzel.woff2') format('woff2');
  font-weight: 400 700;
  font-display: swap;
}

/* ── [P2 26.08.2026 · Planeten_Interaktion_Spec §1.2] Display-Schnitt ────────
   NUTZERWUNSCH: „ich will auch die gleiche Schriftart wie beim Brain-Projekt."

   EHRLICHE LAGE, geprüft statt angenommen: der Brain-Prototyp
   (~/Downloads/brain-neon-prototype*.html, brain-command-center-prototype.html)
   deklariert in seiner CSS-Variablen `--font-display`
       "Cormorant Garamond","Iowan Old Style",Georgia,serif
   liefert aber KEINE Fontdatei mit und lädt auch keine (die Seiten machen
   keine externe Anfrage). Cormorant Garamond ist am Mini nicht installiert.
   Was der Prototyp dort also tatsächlich zeichnet, ist die zweite oder dritte
   Angabe der Liste — die macOS-Systemserife. „Die gleiche Schrift wie im
   Brain-Projekt" heißt damit gerade NICHT Cormorant.

   NUTZERENTSCHEID 26.08.2026: Option A der Spec — die Systemserife. Kein
   Download, keine externe Anfrage (font-src 'self' bleibt unangetastet), und
   das Bild stimmt mit dem Brain-Prototyp überein.

   Die Reihenfolge ist wörtlich die des Prototyps, damit beide Oberflächen auf
   demselben Rechner dieselbe Schrift finden: Cormorant zuerst — falls sie
   später doch einmal installiert oder self-hosted wird, greift sie in beiden
   Oberflächen zugleich; sonst Iowan Old Style (auf macOS vorhanden), sonst
   Georgia.

   CINZEL BLEIBT — Datei, @font-face und alles. Es ist nur nicht mehr die
   Voreinstellung. Wer den Stand vom 25.08.2026 zurückwill, setzt --display
   wieder auf 'Cinzel'. Damit ist auch die frühere Entscheidung „Cinzel statt
   Cormorant" nicht rückgängig gemacht, sondern durch eine neuere überholt:
   sie hieß „keine Schrift nachladen", und genau das gilt weiter. */
:root {
  --display: 'Cormorant Garamond', 'Iowan Old Style', Georgia, serif;
  --display-cinzel: 'Cinzel', Georgia, serif;   /* Stand 25.08.2026, abschaltbar */
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--void);
  color: var(--bone);
  font-family: 'Space Grotesk', system-ui, sans-serif;
  font-variant-numeric: tabular-nums;

  /* Kein horizontales Scrollen (Merker UEP 41) — aber `clip`, NICHT `hidden`.
     `overflow-x: hidden` zwingt overflow-y auf `auto`, macht html und body zu
     Scroll-Containern und bricht damit `position: sticky` still: die Bühne
     klebt nicht mehr, sondern scrollt aus dem Bild. In Gate 1 genau so
     passiert — gemessen stage.top = -1000 bei scrollY = 1000.
     `clip` erzeugt keinen Scroll-Container und lässt sticky intakt. */
  overflow-x: clip;
}

/* ── Die Bahn: sticky Bühne in einer langen Strecke ───────────────────────── */
#journey {
  position: relative;
  height: calc(var(--track-vh) * 1vh);
}

#stage {
  position: sticky;
  top: 0;
  height: 100vh;
  /* Grundverlauf nach Teil E: fast schwarz außen, Tiefblau zur Mitte. */
  background: radial-gradient(ellipse 120% 80% at 50% 46%, var(--deepblue) 0%, var(--void) 70%);
}

#stage-canvas {
  display: block;
  width: 100%;
  height: 100%;
}

/* [P5/P6] Overlay für Impulsring und Sektionstext — deckungsgleich über der
   Szene. pointer-events: none ist wesentlich: der Zug für die 360-Grad-Ansicht
   muss durch diese Ebene hindurch auf #stage-canvas ankommen (Spec §6, Gate
   P8). Eine unsichtbare Fläche, die Klicks abfängt, wäre genau der Fehler, den
   man nicht sieht und stundenlang sucht. */
#sektion-canvas {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 2;
}

/* Papierkorn über allem — verbindet mit den anderen Achilles-Oberflächen. */
#grain {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 3;
  opacity: .05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='200' height='200'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.8' numOctaves='3'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* ── HUD ──────────────────────────────────────────────────────────────────── */
#hud {
  position: fixed;
  left: 1.25rem;
  bottom: 1.25rem;
  z-index: 2;
  display: flex;
  gap: 1.5rem;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--bone) 68%, transparent);
  pointer-events: none;                     /* kein Parallax, keine Interaktion (Teil I) */
}

/* [G7] „sonst soll kein Text sein": Akt und Zustand stehen nicht mehr im Bild.
   Sie bleiben im DOM und werden weiter beschrieben — __universe und die
   Gate-Messungen lesen sie. display: none, damit sie auch nicht vorgelesen
   werden; ein reines opacity: 0 stünde weiter in der Vorlesereihenfolge. */
#hud .nur-messung { display: none; }

/* [G7] „bisschen transparenter". Gemessen gegen den Grund (#07080A) mit
   tools/kontrast.py — die Zahl selbst bleibt über der 7:1-Grenze aus Teil F,
   die Beschriftung darunter ist bewusst schwächer: sie ist eine Marke, kein
   Inhalt, und der Wunsch war ausdrücklich zurückhaltender. */
#hud dt {
  color: color-mix(in srgb, var(--glow) 48%, transparent);
  margin-bottom: .35em;
}

#hud dd {
  margin: 0;
  color: color-mix(in srgb, var(--bone) 68%, transparent);
}

/* ── „Direkt zum System" — erstes fokussierbares Element (Teil F) ─────────── */
#skip-journey {
  position: fixed;
  right: 1.25rem;
  top: 1.25rem;
  z-index: 4;
  padding: .6rem 1rem;
  font: inherit;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone);
  background: color-mix(in srgb, var(--void) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--brass) 45%, transparent);
  border-radius: 2px;
  cursor: pointer;

  /* [G7] „sonst soll kein Text sein." Der Knopf verschwindet aus dem Bild,
     bleibt aber im Fokusbaum — Teil F verlangt ein erstes fokussierbares
     Element, mit dem die Reise ohne Maus und ohne Scroll übersprungen werden
     kann. opacity: 0 ist dafür der richtige Weg: display: none und
     visibility: hidden nehmen ein Element aus der Tabreihenfolge, ein
     durchsichtiges bleibt darin und wird beim Fokus wieder sichtbar.
     pointer-events: none, damit der unsichtbare Knopf keine Klicks abfängt,
     die dem Sternenfeld gelten. */
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s linear;
}

#skip-journey:hover {
  border-color: var(--brass);
}

#skip-journey:focus-visible {
  opacity: 1;
  pointer-events: auto;
  outline: 2px solid var(--glow);
  outline-offset: 2px;
}

/* ── [K1 27.08.2026] Musik an/aus ─────────────────────────────────────────── */
/* Rechts unten, spiegelbildlich zur ENTFERNUNG links unten, und in derselben
   zurückhaltenden Deckkraft wie deren Beschriftung — er soll auffindbar sein,
   nicht auffallen.

   z-index 6 statt 4 (skip) oder 5 (Kammer): der Ton muss in JEDEM Zustand
   abschaltbar bleiben, auch bei offener Kammer. Der Knopf ist das einzige
   Element, das über allem liegt; er ist 2 rem groß und steht in der Ecke, in
   der weder Kammerkopf noch Kennzahlen noch Sektionstext etwas ablegen. */
#klang-schalter {
  position: fixed;
  right: 1.25rem;
  bottom: 1.25rem;
  z-index: 6;
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  padding: 0;
  background: none;
  border: 0;
  color: color-mix(in srgb, var(--bone) 68%, transparent);   /* wie #hud dd */
  cursor: pointer;
  opacity: .5;
  transition: opacity .25s linear, color .25s linear;
}

#klang-schalter[hidden] { display: none; }   /* CONFIG.klang.aktiv = false */

#klang-schalter svg {
  width: 1.25rem;
  height: 1.25rem;
}

/* Das Horn ist eine Fläche, Wellen und Kreuz sind Striche — sonst müsste jeder
   Pfad seine Farbe selbst kennen und der Zustandswechsel unten liefe über zwei
   Eigenschaften statt über eine. */
#klang-schalter .klang-horn {
  fill: currentColor;
  stroke: none;
}

#klang-schalter .klang-welle,
#klang-schalter .klang-kreuz {
  fill: none;
  stroke: currentColor;
  stroke-width: 1.5;
  stroke-linecap: round;
}

#klang-schalter:hover,
#klang-schalter:focus-visible {
  opacity: 1;
  color: var(--bone);
}

#klang-schalter:focus-visible {
  outline: 2px solid var(--glow);
  outline-offset: 3px;
  border-radius: 2px;
}

/* Die drei Zustände aus klang.js (data-klang am <html>). Ohne Attribut — also
   vor dem ersten Meldeschritt — gilt der obere Zweig: Wellen an, kein Kreuz. */
#klang-schalter .klang-kreuz { display: none; }

[data-klang="aus"] #klang-schalter .klang-welle,
[data-klang="fehler"] #klang-schalter .klang-welle { display: none; }

[data-klang="aus"] #klang-schalter .klang-kreuz,
[data-klang="fehler"] #klang-schalter .klang-kreuz { display: block; }

/* „wartet" heißt: der Browser hat den Autostart abgelehnt, der erste Klick
   irgendwohin startet die Musik. Die blassen Wellen sagen das, ohne einen
   Hinweistext ins Bild zu setzen (Nutzerentscheid G7).

   „fertig" [27.08.2026] ist der Zustand nach dem einen Durchlauf und trägt
   dasselbe Bild: kein Ton gerade, ein Druck bringt ihn. Ohne diese Zeile fiele
   der neue Zustand auf den obersten Zweig zurück — volle Wellen, kein Kreuz,
   also die Darstellung für „spielt". Ein neuer Zustandsname ohne CSS-Zeile
   sieht aus wie der Normalfall; das ist der Grund, warum sie hier steht. */
[data-klang="wartet"] #klang-schalter .klang-welle,
[data-klang="fertig"] #klang-schalter .klang-welle { opacity: .3; }
[data-klang="aus"] #klang-schalter,
[data-klang="fehler"] #klang-schalter { opacity: .32; }

/* ── [G6] Akt-Titel mit Schreibeffekt ─────────────────────────────────────── */
/* Links oben, wie in der Vorlage des Nutzers. Bewusst NICHT zentriert: das
   Ziel der Reise ist die Erde in der Bildmitte, ein Titel darüber läge auf ihr.
   Der Text kommt zeichenweise aus ui.js; hier steht nur der Cursor. */
#akt-titel {
  position: absolute;
  left: 2.5rem;
  top: 2.2rem;
  z-index: 2;
  pointer-events: none;
  max-width: min(60ch, 62vw);
  transition: opacity .25s linear;
}

#akt-titel h1 {
  margin: 0;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(2rem, 5.2vw, 3.6rem);
  line-height: 1.05;
  letter-spacing: .01em;
  color: var(--bone);
  /* Sehr dezenter Schein, damit die Schrift auf dem Sternenfeld steht und
     nicht darin verschwindet. Kein Neon (Teil I). */
  text-shadow: 0 0 26px color-mix(in srgb, var(--glow) 22%, transparent);
}

#akt-titel p {
  margin: .5rem 0 0;
  font-size: .8125rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--glow) 78%, transparent);
}

/* Der Cursor sitzt an der Zeile, die gerade geschrieben wird — data-cursor
   wird in ui.js umgeschaltet. Ein Block, kein Unterstrich: er muss auch am
   Zeilenende einer Versalzeile sichtbar bleiben. */
#akt-titel[data-cursor="titel"] h1 span::after,
#akt-titel[data-cursor="unter"] p span::after {
  content: '▌';
  margin-left: .06em;
  /* Kleiner als die Schrift: in Cinzel 3,6 rem ist ein Vollblock so breit wie
     ein halber Buchstabe und dominiert die ersten Zeichen, solange erst zwei
     dastehen. Am Bild bei p = 0,52 aufgefallen. */
  font-size: .62em;
  opacity: .8;
  color: var(--glow);
  animation: cursor-blink .625s steps(1, end) infinite;
}

@keyframes cursor-blink {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* ── [G6] Labels der zehn Knoten ──────────────────────────────────────────── */
#knoten-labels {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
}

.knoten-label {
  position: absolute;
  left: 0;
  top: 0;
  /* translate() kommt aus ui.js; -50% Y zentriert das Label auf dem Knoten. */
  transform-origin: left center;
  margin-top: -.7em;
  /* Das Polster steht bewusst in rem, nicht in em: es ist die Trefferfläche des
     Buttons und darf mit der Schrift NICHT mitschrumpfen. Bei halber Schrift
     trägt es allein rund 5 der 12 px Höhe. [P11] */
  padding: .15rem .35rem;
  border: 0;
  background: none;
  font: inherit;
  /* [P11 26.08.2026] NUTZERWUNSCH: „die Schriftgröße doppelt so klein bitte bei
     den Planeten." Also exakt die Hälfte: .75rem → .375rem (12 → 6 px bei
     16-px-Wurzel). Eine Zahl, kein clamp — der Wunsch ist ein Faktor, und ein
     clamp würde ihn auf schmalen Schirmen still wieder aufheben.
     Gilt für ALLE zehn Labels, auch für das der Erde (Memories): sie teilen
     sich einen Bildschirm und werden in ui.js gemeinsam gegen Kollisionen
     geprüft. Ein Name in doppelter Größe zwischen neun halben wäre keine
     Ausnahme, sondern ein Fehler im Bild. */
  font-size: .375rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--knoten-farbe, var(--bone));
  /* Zwei Schatten, nicht einer. Der farbige Schein war für eine Erde gedacht,
     die im zusammengesetzten Bild kaum zu sehen war (Befund und Behebung im
     Kopf von grund.js). Seit sie in voller Helligkeit steht, verschwindet ein
     Label über Sahara, Grönland oder Arabien — dort ist der Untergrund
     ausgebrannt. Der zusätzliche DUNKLE Saum trägt das Label auf jedem Grund;
     der farbige Schein bleibt darüber und macht weiterhin die Zugehörigkeit
     zum Knoten sichtbar. [G7] */
  text-shadow:
    0 0 4px color-mix(in srgb, var(--void) 92%, transparent),
    0 0 8px color-mix(in srgb, var(--void) 78%, transparent),
    0 0 12px color-mix(in srgb, var(--knoten-farbe, var(--glow)) 45%, transparent);
  cursor: pointer;
  pointer-events: auto;
  transition: opacity .2s linear;
}

.knoten-label:hover { color: var(--bone); }

/* ── [O4 28.08.2026] Die Namen der vier Sektor-Knoten ──────────────────────
   NUTZERAUFTRAG: „die vier Knotenpunkte beschriftet werden."

   Der Container trägt `pointer-events: none` und liegt mit `inset: 0` über
   dem Bild — die Kinder holen sich die Ereignisse einzeln zurück. Das ist
   Falle 37, die in diesem Projekt schon zweimal zugeschlagen hat (Kammer-Panel
   P8, Label-Container P11); ein dritter Container mit `auto` wäre der dritte
   tote Bildschirm.

   Größer als die zehn Bereichs-Labels (.5rem gegen .375rem) und das mit Grund:
   dort teilen sich zehn Namen die Erdkugel, hier stehen vier im leeren Raum
   des Sektors, und sie sind seit O4 die einzige Angabe, welcher Punkt was
   tut — die Linien zwischen ihnen sind weg. */
#sektor-labels {
  position: absolute;
  inset: 0;
  z-index: 3;             /* über #knoten-labels: im offenen Sektor sind das
                             hier die Bedienelemente, die anderen nur Kulisse */
  pointer-events: none;
}

.sektor-label {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: left center;
  margin-top: -.7em;
  padding: .2rem .4rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: .5rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--knoten-farbe, var(--bone));
  text-shadow:
    0 0 4px color-mix(in srgb, var(--void) 92%, transparent),
    0 0 8px color-mix(in srgb, var(--void) 78%, transparent),
    0 0 12px color-mix(in srgb, var(--knoten-farbe, var(--glow)) 45%, transparent);
  cursor: pointer;
  pointer-events: auto;
  transition: opacity .2s linear;
}

.sektor-label:hover { color: var(--bone); }

.sektor-label:focus-visible {
  outline: 1px solid var(--knoten-farbe, var(--glow));
  outline-offset: 2px;
}

/* Der Name des Knotens, dessen Sektion gerade offen steht, fällt weg — sein
   Name steht dann oben in der Kammer. Dieselbe Regel wie bei den Planeten
   (P6): jeder Name genau einmal im Bild. */
.sektor-label[data-offen="ja"] { opacity: 0; pointer-events: none; }

.knoten-label:focus-visible {
  outline: 1px solid var(--knoten-farbe, var(--glow));
  outline-offset: 2px;
}

/* ── [G6] Die Kammer ──────────────────────────────────────────────────────── */
#kammer[hidden] { display: none; }

#kammer {
  position: fixed;
  inset: 0;
  z-index: 5;
  display: grid;
  grid-template-columns: minmax(12rem, 22%) 1fr minmax(14rem, 28%);
  grid-template-rows: auto 1fr auto;
  gap: 1rem 2rem;
  padding: 2.2rem 2.5rem;
  /* Die Erde bleibt dahinter sichtbar und dreht weiter — das war die
     Entscheidung. Deshalb ein Verlauf mit Loch in der Mitte statt einer Decke:
     außen dicht genug für die Schrift, innen offen für das Bild. */
  background:
    radial-gradient(ellipse 70% 65% at 50% 52%,
      transparent 0%,
      color-mix(in srgb, var(--void) 55%, transparent) 62%,
      color-mix(in srgb, var(--void) 88%, transparent) 100%);
  backdrop-filter: blur(1.5px);
}

#kammer-kopf {
  grid-column: 1 / 3;
  grid-row: 1;
  max-width: 46ch;
}

#kammer-ort {
  margin: 0;
  font-size: .6875rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--glow) 85%, transparent);
}

#kammer-titel {
  margin: .5rem 0 .35rem;
  font-family: var(--display);
  font-weight: 400;
  font-size: clamp(1.8rem, 4vw, 3rem);
  line-height: 1;
  color: var(--bone);
  text-shadow: 0 0 30px color-mix(in srgb, var(--knoten-farbe, var(--glow)) 35%, transparent);
}

/* Cursor auch hier — die Kammer tippt ihren Titel über die Uhr. */
#kammer-titel::after {
  content: '▌';
  margin-left: .06em;
  color: var(--knoten-farbe, var(--glow));
  animation: cursor-blink .625s steps(1, end) infinite;
}

#kammer-unter {
  margin: 0;
  font-size: .6875rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--bone) 55%, transparent);
}

#kammer-zu {
  grid-column: 3;
  grid-row: 1;
  justify-self: end;
  align-self: start;
  padding: .6rem 1rem;
  font: inherit;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone);
  background: color-mix(in srgb, var(--void) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--knoten-farbe, var(--glow)) 55%, transparent);
  border-radius: 2px;
  cursor: pointer;
}

#kammer-zu:hover { border-color: var(--knoten-farbe, var(--glow)); }
#kammer-zu:focus-visible { outline: 2px solid var(--glow); outline-offset: 2px; }

#kammer-zahlen {
  grid-column: 1;
  grid-row: 2;
  align-self: center;
  display: flex;
  flex-direction: column;
  gap: 1.6rem;
}

.kammer-zahl { display: flex; flex-direction: column; }

.kammer-wert {
  font-family: var(--display);
  font-size: clamp(2rem, 4.5vw, 3.2rem);
  line-height: 1;
  color: var(--knoten-farbe, var(--glow));
}

.kammer-label {
  margin-top: .35rem;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--bone) 60%, transparent);
}

#kammer-liste {
  grid-column: 3;
  grid-row: 2;
  align-self: center;
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 1.1rem;
}

#kammer-liste li {
  display: flex;
  align-items: center;
  gap: .7rem;
  font-size: .9375rem;
  color: var(--bone);
}

.kammer-punkt {
  width: .45rem;
  height: .45rem;
  border-radius: 50%;
  background: var(--knoten-farbe, var(--glow));
  box-shadow: 0 0 10px var(--knoten-farbe, var(--glow));
  flex: none;
}

#kammer-fuss {
  grid-column: 1 / 4;
  grid-row: 3;
  margin: 0;
  font-size: .6875rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--bone) 42%, transparent);
}

/* ── [F3 27.08.2026] Das Farbpanel ─────────────────────────────────────────
   Sitzt in derselben Spalte wie die Sektionsliste und darf scrollen: die Zahl
   der Farben wächst mit jeder neuen Lage, und eine feste Höhe würde die
   unteren Einträge irgendwann unerreichbar machen — ohne dass man es sieht.

   Die Farben dieses Panels stammen bewusst aus denselben Custom Properties,
   die es selbst verstellt (--bone, --glow, --void). Das ist kein Versehen: wer
   die Textfarbe ändert, soll das sofort am Panel selbst sehen. */
#kammer-panel {
  grid-column: 2 / 4;
  grid-row: 2;
  overflow-y: auto;
  max-height: min(56vh, 32rem);
  padding-right: .5rem;
}
#kammer-panel[hidden] { display: none; }

.farbpanel h3 {
  margin: 0 0 .5rem;
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--bone) 52%, transparent);
}

.farbpanel-gesamt {
  padding-bottom: .9rem;
  margin-bottom: .9rem;
  border-bottom: 1px solid color-mix(in srgb, var(--bone) 14%, transparent);
}

.farbpanel-regler {
  display: grid;
  grid-template-columns: 6.5rem 1fr 3.2rem;
  align-items: center;
  gap: .6rem;
  margin-bottom: .45rem;
  font-size: .8125rem;
  color: var(--bone);
}

.farbpanel-reglername { white-space: nowrap; }

.farbpanel-wert {
  font-variant-numeric: tabular-nums;   /* sonst springt die Zeile beim Ziehen */
  text-align: right;
  font-size: .75rem;
  color: color-mix(in srgb, var(--bone) 62%, transparent);
}

.farbpanel-regler input[type="range"] {
  width: 100%;
  accent-color: var(--knoten-farbe, var(--glow));
}

.farbpanel-gruppe { margin-bottom: .9rem; }

.farbpanel-zeile {
  display: grid;
  grid-template-columns: 1.6rem 1fr 1.6rem;
  align-items: center;
  gap: .6rem;
  padding: .18rem 0;
  font-size: .8125rem;
  color: var(--bone);
}

.farbpanel-zeile input[type="color"] {
  inline-size: 1.6rem;
  block-size: 1.35rem;
  padding: 0;
  border: 1px solid color-mix(in srgb, var(--bone) 22%, transparent);
  border-radius: 3px;
  background: none;
  cursor: pointer;
}
.farbpanel-zeile input[type="color"]:disabled { cursor: not-allowed; opacity: .38; }

/* Eine veränderte Farbe wird markiert — sonst weiß man nach zehn Zügen nicht
   mehr, was man angefasst hat und was noch original ist. */
.farbpanel-zeile[data-veraendert="ja"] .farbpanel-name::after {
  content: '•';
  margin-left: .4rem;
  color: var(--knoten-farbe, var(--glow));
}
.farbpanel-zeile[data-attribut="ja"] .farbpanel-name,
.farbpanel-zeile[data-ohnewirkung="ja"] .farbpanel-name {
  color: color-mix(in srgb, var(--bone) 45%, transparent);
}
/* Die Klammer sagt WARUM eine Zeile blass ist. Ohne sie sieht „abgeschaltet"
   aus wie „kaputt", und man sucht einen Fehler, den es nicht gibt. */
.farbpanel-zeile[data-attribut="ja"] .farbpanel-name::before { content: '⌾ '; }
.farbpanel-zeile[data-ohnewirkung="ja"] .farbpanel-name::after {
  content: ' (Lage aus)';
  font-size: .6875rem;
  color: color-mix(in srgb, var(--bone) 35%, transparent);
}

.farbpanel-zurueck,
.farbpanel-zurueck-alles {
  font: inherit;
  color: var(--bone);
  background: none;
  border: 1px solid color-mix(in srgb, var(--bone) 22%, transparent);
  border-radius: 3px;
  cursor: pointer;
}
.farbpanel-zurueck {
  inline-size: 1.6rem;
  block-size: 1.35rem;
  line-height: 1;
  font-size: .75rem;
}
.farbpanel-zurueck:disabled { opacity: .3; cursor: not-allowed; }
.farbpanel-zurueck-alles {
  margin-top: .6rem;
  padding: .35rem .7rem;
  font-size: .75rem;
  letter-spacing: .06em;
}
.farbpanel-zurueck:hover:not(:disabled),
.farbpanel-zurueck-alles:hover {
  border-color: var(--knoten-farbe, var(--glow));
  color: var(--knoten-farbe, var(--glow));
}

/* Solange die Kammer offen ist, treten Reise-Titel, Labels und HUD zurück.
   Ohne das stehen zwei Überschriften übereinander (der Akt-Titel schien durch
   den Kammer-Titel hindurch) und zehn Labels flimmern hinter dem Text. */
/* display, NICHT opacity: ui.js setzt die Deckkraft des Akt-Titels in jedem
   Frame als Inline-Style, und ein Inline-Style schlägt jede Regel hier. Genau
   das stand im Bild — „Deine Welt" schien durch den Kammer-Titel hindurch. */
[data-kammer="offen"] #akt-titel { display: none; }
[data-kammer="offen"] #knoten-labels { opacity: 0; pointer-events: none; }
[data-kammer="offen"] #hud { opacity: .25; }
[data-kammer="offen"] #skip-journey { display: none; }

/* ── [P6 26.08.2026] Die Kammer im SEKTIONS-Modus ───────────────────────────
   Seit der Planeten-Interaktion steht der Inhalt gezeichnet um den Ring herum
   (sektion.js), nicht mehr als Vollbild-Panel. Das DOM bleibt trotzdem
   vollständig bestehen und wird weiter befüllt — es ist die Fassung für die
   Vorlesehilfe und der Tastaturweg (Teil F). Es wird nur nicht mehr angezeigt.

   NUTZERENTSCHEID 26.08.2026: „ohne Menü und Leiste, nur Zurück-Option."
   Sichtbar bleibt deshalb genau ein Element: der Zurück-Knopf.

   pointer-events: none auf dem Panel ist die Bedingung für Gate P8 — ohne das
   fängt das unsichtbare Vollbild-Overlay jeden Zug ab und die 360-Grad-Ansicht
   ist in der Sektion tot. Der Knopf holt sich die Ereignisse einzeln zurück. */
#kammer[data-modus="sektion"] {
  background: none;
  backdrop-filter: none;
  pointer-events: none;
}
#kammer[data-modus="sektion"] #kammer-kopf,
#kammer[data-modus="sektion"] #kammer-zahlen,
#kammer[data-modus="sektion"] #kammer-liste,
#kammer[data-modus="sektion"] #kammer-fuss {
  /* Wie .visually-hidden: für die Vorlesehilfe da, im Bild nicht. Nicht
     display: none — das nähme sie auch der Vorlesehilfe. */
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
#kammer[data-modus="sektion"] #kammer-zu { pointer-events: auto; }

/* [F3-NACHTRAG 28.08.2026, NUTZERBEFUND „die Farben kann ich nicht umstellen"]
   Das Farbpanel braucht dieselbe Rückholung wie der Zurück-Knopf.

   Gemessen im laufenden Bild, bevor diese Zeile stand: das Panel war offen und
   sichtbar (display: block, opacity 1, 1403 × 512 px, 48 Bedienelemente), aber
   `getComputedStyle(input[type=range]).pointerEvents` war `none` — geerbt von
   `#kammer[data-modus="sektion"]` drei Zeilen weiter oben. Kein Regler, kein
   Wähler, kein Knopf war anklickbar. Das Panel war eine Anzeige, keine
   Bedienung.

   Warum das hier KEIN Widerspruch zu Falle 37 ist (dort: „ein Element mit
   inset: 0 bekommt nie pointer-events: auto"): Diese Falle gilt für
   UNSICHTBARE VOLLBILD-Overlays, die man nicht sieht und deshalb nicht als
   Ursache erkennt. `#kammer-panel` ist ein begrenzter, sichtbarer Kasten in
   Spalte 2/3 — was er abfängt, steht im Bild und ist erklärbar.

   Und es gilt nur, wenn er offen ist: für die neun Planeten und die drei
   übrigen Sektor-Knoten trägt er `hidden` (display: none), fängt also nichts
   ab. Gate P8 (Ziehen und Drehen in der Sektion) bleibt damit unberührt —
   überall dort, wo es bisher galt. */
#kammer[data-modus="sektion"] #kammer-panel { pointer-events: auto; }

/* Die Labels der übrigen Planeten bleiben in der Sektion stehen: sie sind der
   Tastaturweg zu den anderen Bereichen, und ohne Menü (Nutzerentscheid) der
   einzige. Nur schwächer, damit sie den Text nicht stören.

   [P11 26.08.2026 NUTZERBEFUND „sobald ich auf einen Planeten klicke hängt sich
   bis auf das Zoomen alles auf"] Hier stand zusätzlich `pointer-events: auto` —
   und zwar auf dem CONTAINER. `#knoten-labels` liegt mit `inset: 0` über dem
   ganzen Bild: damit war bei offener Sektion der komplette Bildschirm eine
   unsichtbare Fangschicht vor dem Canvas. Kein Zug, kein Klick, kein Drehen —
   nur das Rad kam durch, weil Scrollen nicht an `pointer-events` hängt. Genau
   das Bild, das der Befund beschreibt; der einzige Ausweg war, so weit
   herauszuscrollen, bis der Notausgang in stelleBlick() die Sektion abbricht.

   Das ist DERSELBE Fehler, vor dem der Kommentar 25 Zeilen weiter oben bei
   `#kammer[data-modus="sektion"]` ausdrücklich warnt („ohne das fängt das
   unsichtbare Vollbild-Overlay jeden Zug ab") — nur eine Ebene tiefer noch
   einmal gemacht.

   Gemeint waren nie der Container, sondern die BUTTONS. Die brauchen hier gar
   nichts: `.knoten-label` trägt sein eigenes `pointer-events: auto`, und ein
   Kind mit `auto` ist auch unter einem Elternteil mit `none` anklickbar. Es
   bleibt deshalb allein bei der Deckkraft. */
[data-kammer="offen"][data-sektion="an"] #knoten-labels { opacity: .35; }

/* Auf schmalen Schirmen aber gar nicht: AM BILD AUFGEFLOGEN im 390-px-iframe —
   dort lagen PROJEKTE, SECURITY, KALENDER, CRONJOBS und DASHBOARD quer über
   dem Sektionstext, weil zehn Körper und ihre Namen auf 390 px keinen Platz
   haben (dieselbe Enge wie bei der Kollisionsauflösung in ui.js). Damit gilt
   dort wieder Brain-Master §7 wörtlich: „Labels aus".
   Die Buttons bleiben im DOM und tastaturerreichbar — nur unsichtbar. */
@media (max-width: 640px) {
  [data-kammer="offen"][data-sektion="an"] #knoten-labels { opacity: 0; pointer-events: none; }
}

#hud, #knoten-labels { transition: opacity .25s linear; }

/* Schmale Schirme: aus drei Spalten wird eine (Teil G — auf 390 px binden
   die Kennzahlen sonst die halbe Breite und die Liste bricht um). */
@media (max-width: 820px) {
  #kammer {
    grid-template-columns: 1fr;
    grid-template-rows: auto auto 1fr auto;
    padding: 1.5rem 1.25rem;
  }
  #kammer-kopf { grid-column: 1; }
  #kammer-zu { grid-column: 1; grid-row: 1; justify-self: end; }
  #kammer-zahlen { grid-column: 1; grid-row: 2; flex-direction: row; gap: 1.4rem; }
  #kammer-liste { grid-column: 1; grid-row: 3; align-self: start; }
  #kammer-fuss { grid-column: 1; grid-row: 4; }
  #akt-titel { left: 1.25rem; top: 1.5rem; }
}

/* Ganz am ENDE der Bahn darf ein Zug auf dem Canvas nicht mehr die Seite
   scrollen — sonst dreht sich auf dem Touchgerät nichts, sondern die Bahn
   läuft zurück. Das Attribut setzt app.js ab CONFIG.orbit.touchSperreAbP.
   [P11 26.08.2026] Das ist NICHT mehr dieselbe Schwelle, ab der gedreht wird:
   der Orbit ist schon bei Entfernung 0.009 scharf (abP = 0,845), geschluckt
   wird der senkrechte Zug aber erst bei 0,985. Dazwischen dreht der waagerechte
   Zug und der senkrechte scrollt weiter — sonst käme ein Touchgerät die
   restlichen 14 % der Bahn nicht mehr herunter. */
#stage-canvas { touch-action: pan-y; }
[data-orbit="an"] #stage-canvas { touch-action: none; }

/* [G7] Am STARTPUNKT ist der Blick ebenfalls beweglich, aber touch-action
   bleibt bei pan-y: dort ist der senkrechte Zug das Scrollen, mit dem die
   Reise überhaupt erst losgeht. Wer sie mit einem Griff sperrte, käme auf
   einem Touchgerät nicht vom Fleck. Also nur der Zeiger wechselt — umsehen
   geht dort waagerecht, kippen mit den Pfeiltasten.
   [P11] Derselbe Zeiger gilt jetzt für den ganzen Ankunftsbereich: er hängt am
   BLICK (drehbar ja/nein), nicht mehr an der Touch-Sperre. */
[data-blick="orbit"] #stage-canvas,
[data-blick="frei"] #stage-canvas { cursor: grab; }
[data-blick="orbit"] #stage-canvas:active,
[data-blick="frei"] #stage-canvas:active { cursor: grabbing; }

/* ── 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;
}

/* ── TEIL F: die Reise entfällt vollständig ───────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  #journey { height: 100vh; }               /* Bahn schrumpft, sofort Akt 6 */
  #skip-journey { display: none; }          /* das Ziel ist bereits zu sehen */

  /* [G6] Kein blinkender Cursor. Er steht still da — der Schreibeffekt selbst
     entfällt ohnehin, weil die Bahn auf 100vh schrumpft und p sofort 1 ist. */
  #akt-titel[data-cursor="titel"] h1 span::after,
  #akt-titel[data-cursor="unter"] p span::after,
  #kammer-titel::after {
    animation: none;
    opacity: .5;
  }
}

/* ═══ [B1/B2 28.08.2026] Die Endstation: Titel oben links und das Gehirn ════
   NUTZERAUFTRAG: „links oben, im selben Stil wie der Buchi-Schriftzug, alles in
   Großbuchstaben — PROJECT ONE, darunter BUCHI ARTIST APP, bisschen kleiner.
   Dann will ich, wenn ich auf den Buchi-Schriftzug klicke, dass wir in seinen
   Kopf zoomen und das Gehirn geöffnet wird."                                */

/* Der Titel. Fest im Bild, nicht auf der Bühne: er gehört zum Ort, nicht zur
   Szene, und darf beim Drehen nicht mitwandern.

   pointer-events: none auf dem CONTAINER — die Regel aus Befund 37 („ein
   Element, das über dem Canvas liegt, bekommt nie pointer-events: auto; die
   Kinder holen sich die Ereignisse einzeln zurück"). Hier ist der Container zwar
   klein statt inset: 0, aber er liegt genau dort, wo der Nutzer den Schriftzug
   anklicken könnte, wenn er quer über dem Bild steht.

   Die FARBE kommt aus JS (--finale-farbe, gesetzt in ui.js aus CONFIG.buchi.farbe).
   Sie steht bewusst nicht als Hexwert hier: die Figur und ihr Titel sollen
   dieselbe Farbe tragen, und zwei Stellen für einen Ton laufen auseinander —
   genau das hat das Farbinventar (F3) im ganzen Projekt aufgeräumt. */
#finale-titel {
  position: fixed;
  left: 1.25rem;
  top: 1.25rem;
  z-index: 3;
  pointer-events: none;
  opacity: 0;
  transition: opacity .25s linear;
}

#finale-titel[hidden] { display: none; }

#finale-titel p {
  margin: 0;
  font-family: var(--display-cinzel);
  font-weight: 600;
  text-transform: uppercase;
  color: var(--finale-farbe, var(--bone));
  white-space: nowrap;

  /* Dasselbe additive Leuchten wie die Punktwolke — nur eben in CSS: ein enger
     dunkler Saum gegen den Sternhintergrund, darüber zwei weiche Höfe in der
     eigenen Farbe. Ohne den dunklen Saum steht der Titel über hellen Sternen
     ohne Zeichnung da (derselbe Grund wie bei den Knoten-Labels seit Falle 23). */
  text-shadow:
    0 0 5px color-mix(in srgb, var(--void) 90%, transparent),
    0 0 14px color-mix(in srgb, var(--finale-farbe, var(--glow)) 55%, transparent),
    0 0 30px color-mix(in srgb, var(--finale-farbe, var(--glow)) 30%, transparent);
}

#finale-titel-1 {
  font-size: 1.15rem;
  letter-spacing: .34em;
}

/* „bisschen kleiner" — 0,72 der ersten Zeile. Die Sperrung wächst dabei
   leicht: bei kleinerer Schrift wirkt derselbe relative Abstand enger. */
#finale-titel-2 {
  font-size: .82rem;
  letter-spacing: .38em;
  margin-top: .45em;
  opacity: .82;
}

/* Der Weg in den Kopf ohne Maus. Genau wie #skip-journey: unsichtbar, aber im
   Fokusbaum — und beim Fokus wieder da. */
#gehirn-auf {
  display: block;
  margin-top: .8rem;
  padding: .5rem .9rem;
  font: inherit;
  font-size: .625rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--bone);
  background: color-mix(in srgb, var(--void) 82%, transparent);
  border: 1px solid color-mix(in srgb, var(--finale-farbe, var(--brass)) 45%, transparent);
  border-radius: 2px;
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity .18s linear;
}

#gehirn-auf:focus-visible {
  opacity: 1;
  pointer-events: auto;
  outline: 1px solid var(--finale-farbe, var(--glow));
  outline-offset: 2px;
}

/* Steht das Gehirn offen, tritt der Titel zurück (CONFIG.finaleTitel.imGehirn)
   — aber nicht auf 0: er ist der einzige Ort, an dem die Seite sagt, wo man
   ist. Die Deckkraft schreibt ui.js inline, diese Regel ist deshalb bewusst
   ohne opacity: ein Inline-Stil schlüge sie ohnehin (Falle 21/F-028). */
:root[data-gehirn="an"] #finale-titel { letter-spacing: normal; }

/* Der Zeiger über dem Schriftzug: er ist anklickbar, also muss man das sehen.
   Gesetzt wird das Attribut in app.js beim Bewegen des Zeigers — dieselbe
   Lösung wie data-blick für den Orbit. */
:root[data-schriftzug="ziel"] #stage-canvas { cursor: pointer; }

/* ── Die Labels der neun Gehirnknoten ─────────────────────────────────────── */
#gehirn-labels {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;     /* Befund 37: die Kinder holen sich die Ereignisse */
}

.gehirn-label {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: left center;
  margin-top: -.7em;
  padding: .2rem .45rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: .5rem;
  letter-spacing: .16em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--knoten-farbe, var(--bone));
  text-shadow:
    0 0 4px color-mix(in srgb, var(--void) 92%, transparent),
    0 0 8px color-mix(in srgb, var(--void) 78%, transparent),
    0 0 12px color-mix(in srgb, var(--knoten-farbe, var(--glow)) 45%, transparent);
  cursor: pointer;
  pointer-events: auto;
  transition: opacity .2s linear;
}

.gehirn-label:hover { color: var(--bone); }

.gehirn-label:focus-visible {
  outline: 1px solid var(--knoten-farbe, var(--glow));
  outline-offset: 2px;
}

/* Jeder Name genau einmal im Bild: der Knoten, dessen Sektion offen steht,
   trägt seinen Namen oben in der Kammer (dieselbe Regel wie P6 und N2). */
.gehirn-label[data-offen="ja"] { opacity: 0; pointer-events: none; }

/* ── [S4 31.08.2026] Die Namen der acht Planeten ────────────────────────────
   Dieselbe Klasse wie die Gehirn- und Sektor-Knoten (ui.SektorLabels), also
   auch dieselbe Gestalt — mit zwei Unterschieden, beide begründet:

   1. `pointer-events: none`. Die acht öffnen keine Kammer (das täte
      sektion.js, und das wäre eine zweite Interaktionskette auf derselben
      Seite — in Teil 298 ausdrücklich als „nicht beauftragt" vermerkt). Ein
      Element, das wie ein Knopf aussieht und keiner ist, ist schlimmer als
      eines, das keiner sein will. Der Zeigefinger bleibt deshalb aus, und
      der Zug darunter erreicht den Canvas — sonst hinge der Orbit fest,
      sobald man einen Namen erwischt.
   2. Etwas größer als die Gehirnknoten (.55 gegen .5rem): dort stehen neun
      Namen auf engem Raum im Kopf, hier acht über das ganze Bild. */
#planet-labels {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

.planet-label {
  position: absolute;
  left: 0;
  top: 0;
  transform-origin: left center;
  margin-top: -.7em;
  padding: .2rem .45rem;
  border: 0;
  background: none;
  font: inherit;
  font-size: .55rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--knoten-farbe, var(--bone));
  text-shadow:
    0 0 4px color-mix(in srgb, var(--void) 92%, transparent),
    0 0 8px color-mix(in srgb, var(--void) 78%, transparent),
    0 0 12px color-mix(in srgb, var(--knoten-farbe, var(--glow)) 45%, transparent);
  cursor: default;
  pointer-events: none;
  transition: opacity .2s linear;
}

/* Auf schmalen Schirmen wird der Titel kleiner — sonst steht „BUCHI ARTIST APP"
   mit 0,38em Sperrung bei 390 px über die halbe Bildbreite. Gerechnet, nicht
   geschätzt: 16 Zeichen × (0,82rem × 0,62 + 0,38 × 0,82rem) ≈ 213 px von 390. */
@media (max-width: 640px) {
  #finale-titel { left: .8rem; top: .8rem; }
  #finale-titel-1 { font-size: .9rem; letter-spacing: .28em; }
  #finale-titel-2 { font-size: .66rem; letter-spacing: .3em; }
}
