/* =============================================================================
   XDR5 — das gesamte CSS des Projekts, an einer Stelle.

   Bis 27.07.2026 lag es in <style>-Bloecken auf neun Seiten verteilt; header.php
   trug den groessten Teil, die uebrigen acht ihre eigenen. Foerbs Ansage:
   "ich finde gar keine css-datei ... dann muessen wir einmal global saemtliches
   css aus den seiten nehmen und in einer css datei zusammenfuegen."

   AUFBAU
   ------
   1. FUNDAMENT — was auf jeder Seite gilt (Tokens, Bausteine, Menue, Layout).
   2. Danach je ein Abschnitt pro Seite, in der Reihenfolge, in der sie frueher
      geladen wurden. Wer eine Regel sucht, findet sie unter ihrer Seite.

   DIE EINE REGEL, DIE MAN KENNEN MUSS
   -----------------------------------
   Frueher wirkte seitenlokales CSS nur auf seiner Seite. In einer gemeinsamen
   Datei gilt alles ueberall — deshalb sind Regeln mit GENERISCHEN Selektoren
   (html, body, .seite, h1 …) an die Kennung des <body> gebunden:
   body.seite-start, body.seite-matryx und so weiter. header.php setzt die
   Kennung aus $seitenklasse, jede Seite setzt sie oben neben $seitentitel.

   Ohne diese Bindung waeren zwei Dinge sofort kaputt gewesen (beides gemessen):
   matryx.php setzt "html, body { overflow:hidden }" — jede Seite des Projekts
   waere unscrollbar geworden. Und start.php setzt ".seite h1 { text-align:left }"
   — jede Ueberschrift im Projekt haette sich verschoben.

   Regeln mit eigenen Klassennamen (.scan-buehne, .faq-tabelle, .intro-wrap …)
   brauchen die Bindung nicht: Sie greifen nur, wo ihre Klasse steht.
   ============================================================================= */



/* NICHT HIER, UND ZWAR ABSICHTLICH: .foerb-netzwerk
   Die Netzwerkleiste im Fuss kommt als fertiges Fragment von fabianbrose.de und
   liegt in _cache/foerb_netzwerk.html — mitsamt ihrem eigenen <style>. Sie wird
   dort periodisch erneuert und auf mehreren Seiten ausserhalb dieses Projekts
   eingebettet. Wer ihr CSS hierher zoege, haette es beim naechsten Cache-Refresh
   doppelt. Beim Zusammenfuehren am 27.07.2026 als einzige Regelgruppe bewusst
   ausgelassen (die Vollstaendigkeitspruefung zaehlt sie deshalb als "fehlend"). */

/* ===== 1. FUNDAMENT (gilt auf jeder Seite) ============================ */
@font-face {
  font-display: auto;
  font-family: 'Brose';
  font-style: normal;
  font-weight: 300;
  src: url('https://daten.fabianbrose.de/typo/font.php?f=Brose.woff2') format('woff2'),
       url('https://daten.fabianbrose.de/typo/font.php?f=Brose.woff') format('woff');
}

/* PIXELKREBS — die Ueberschriften-Schrift (Foerbs Auftrag 28.07.2026).
   Gleicher Weg wie Brose: EXTERN von daten.fabianbrose.de, nie lokal
   gebuendelt (CLAUDE.md) — nur so kommen Foerbs Zeichen-Updates an.
   Eingebunden wie auf pixelkrebs.de, von wo die Einbindung uebernommen ist.

   ACHTUNG, GEMESSEN AM 28.07.2026: Die Schrift hat nur 68 Zeichen —
   A-Z, a-z, 0-9 und , - . : plus Leerzeichen. Es FEHLEN Umlaute, ss,
   Anfuehrungszeichen, Gedankenstrich, Frage- und Ausrufezeichen.
   Deshalb steht Brose als Rueckfall dahinter: Ein fehlendes Zeichen faellt
   EINZELN auf Brose zurueck, die Ueberschrift bricht also nicht weg — sie
   wird an dieser Stelle gemischt. */
@font-face {
  font-display: auto;
  font-family: 'Pixelkrebs';
  font-style: normal;
  font-weight: 300;
  src: url('https://daten.fabianbrose.de/typo/font.php?f=Pixelkrebs.woff2') format('woff2'),
       url('https://daten.fabianbrose.de/typo/font.php?f=Pixelkrebs.woff') format('woff');
}

:root{
  --menu-h:48px;

  --c-white:#eee;
  --c-black:#111;

  --c-bg-body:#111;
  --c-bg-page:#eeeeeeee;
  --c-bg-overlay:rgba(0,0,0,0.7);
  --c-bg-dialog:#111;
  --c-bg-sidebar:#222222ee;
  --c-signalfarbe:#ff8800;
  --c-signal-dim:rgba(255,136,0,0.18);
  --c-signal-glow:rgba(255,136,0,0.5);
  --c-bg-tryx-hover:rgba(255,255,255,0.06);
  --c-bg-tryx-highlight:rgba(23,89,122,0.25);

  --c-line:#555;
  --c-line-soft:rgba(255,255,255,0.2);
  --c-line-tryx:rgba(255,255,255,0.15);

  /* FORM — drei Radien, mehr braucht das Projekt nicht.
     Gezählt vor der Aufräumaktion (27.07.2026): 28× --radius, dazu 3px (vier
     Stellen), 8px (.seite), 4px (ein Fortschrittsbalken) und ein
     "0 4px 4px 0". Die 4px waren von den 3px ununterscheidbar und sind jetzt
     --radius-klein; 8px war eine Einzelentscheidung für die große Seitenfläche
     und heißt jetzt so. */
  --radius:6px;
  --radius-klein:3px;      /* Badges, kleine Marken, Fortschrittsbalken */
  --radius-gross:8px;      /* die große Seitenfläche .seite */

  /* SCHRIFTGRÖSSEN — eine Leiter statt achtzehn Einzelfälle.
     Gezählt waren: 10, 11, 12, 13, 14, 15, 16, 17, 20, 22, 26, 27, 37 px.
     15/17/26 lagen so dicht an ihren Nachbarn, dass der Unterschied niemandem
     auffällt, aber jede neue Regel wieder raten muss. Sie sind eingeebnet. */
  --text-xs:10px;          /* Kleinstschrift: Marken, Fußzeilen */
  --text-s:11px;           /* Beschriftungen, Tabellen-Label */
  --text-m:12px;           /* Sekundärtext, Knopfbeschriftung im Umschalter */
  --text-basis:13px;       /* Fließtext */
  --text-l:14px;           /* hervorgehobener Fließtext, Blockköpfe */
  --text-xl:16px;          /* Kartenüberschrift, Beleg-Uhrzeit */
  --text-h3:20px;
  --text-zahl:22px;        /* große Kennzahl (Zahlenband, Sidebar-Knopf) */
  --text-h2:27px;
  --text-h1:37px;

  /* ÜBERSCHRIFTEN-FAKTOR (Foerbs Auftrag 28.07.2026: „alle Überschriften 30 %
     größer"). EINE Stellschraube statt acht Einzelwerte — wer die Größe noch
     einmal ändern will, dreht hier und nirgends sonst.

     Warum nicht einfach die Tokens erhöhen: Nur --text-h1 und --text-h2 gehören
     exklusiv den Überschriften. --text-h3 hängt zusätzlich am Sidebar-Knopf,
     --text-xl an .ergebnis-satz und der Beleg-Uhrzeit, --text-l an der
     User-Tabelle und dem Menü-Symbol (nachgemessen 28.07.2026). Ein Hochdrehen
     dieser Tokens hätte lauter Dinge mitvergrößert, die keine Überschrift sind.

     Die festen Zeilenhöhen von h1 und h2 wachsen deshalb mit — sonst stünde
     eine 30 % größere Schrift in einer unveränderten Zeilenbox und die Zeilen
     liefen ineinander.

     28.07.2026, zweiter Durchgang: Foerb wollte "nochmal 20 % größer".
     1,3 × 1,2 = 1,56 — der Faktor rechnet vom URSPRUNGSWERT, nicht vom schon
     vergrößerten, deshalb steht hier 1.56 und nicht 1.2.

     28.07.2026, dritter Durchgang: "nochmal um 30 % vergrößern", Begründung
     "gerade die drei h3 kann man kaum lesen, so klein".
     1,56 × 1,3 = 2,028. Damit steht die h1 bei 75 px — die Größe ist auf
     390 px Breite gerendert nachgeprüft worden, nicht geschätzt. */
  --text-h-faktor:2.028;

  /* ABSTÄNDE — vier Stufen für gap/Innenabstände in Rastern.
     Vorher: 8, 10, 12, 16, 18, 20, 24 px. Die Zwischenwerte 10, 18 und 20
     entstanden einzeln und sind jetzt auf die Leiter gerückt. */
  --luecke-s:8px;
  --luecke-m:12px;
  --luecke-l:16px;
  --luecke-xl:24px;

  /* DECKKRAFT — drei Stufen statt neun.
     Vorher lagen 0.6, 0.65, 0.7, 0.75, 0.8, 0.85, 0.9 nebeneinander, ohne dass
     jemand die Unterschiede benennen könnte. */
  --deckkraft-dezent:0.65;   /* Beschriftungen, Fußnoten */
  --deckkraft-halb:0.75;     /* zurückgenommener Text */
  --deckkraft-fast:0.9;      /* fast voll, z. B. Hover */

  /* KNÖPFE — ein Innenabstand für Aktions-Knöpfe, einer für kleine in Tabellen.
     Das Menü hat bewusst eigene Werte (größere Trefferfläche). */
  --knopf-padding:8px 10px;
  --knopf-padding-klein:4px 10px;

  --shadow:0 12px 30px rgba(0,0,0,0.6);
  --shadow-sidebar:0 0 14px rgba(0,0,0,0.6);
  --shadow-seite:0 0 40px rgba(0,0,0,0.7), inset 0 1px 0 rgba(255,255,255,0.04);
  --shadow-glow:0 0 18px var(--c-signal-glow);

  /* Weiß für Flächen, auf denen ein TriCode liegt. KEIN Deko-Wert: Der Leser
     schwellt per Otsu-Verfahren und braucht reines Weiß, sonst fällt der Code
     durch. Deshalb ein eigener Token statt dreimal #fff im Fließtext. */
  --flaeche-code:#fff;
  /* Reines Weiß für Text, der heller sein soll als der Grundton --c-white. */
  --c-white-klar:#fff;
  /* Überschriften-Töne unterhalb der Signalfarbe (h3, h4/h5). */
  --c-ueberschrift-3:rgba(255,200,100,0.9);
  --c-ueberschrift-4:rgba(255,220,150,0.8);

  /* TRYX-KLASSEN-FARBEN — seit 28.07.2026 aus dem Logo (Foerbs Auftrag).
     Die sieben Werte sind nicht geschaetzt, sondern aus logo_neu_2.svg
     ausgelesen: Die 22 Fuellfarben der Datei fallen in genau SIEBEN
     Farbfamilien, und es gibt genau sieben TRYX-Klassen. Je Familie steht hier
     der Mittelwert ihrer Formen.

     ZUORDNUNG entlang der ABHAENGIGKEITSKETTE, nicht nach Wert: GATE braucht
     5 TRYX, HIDDEN braucht 2 GATE, ANCHOR braucht GATE+LOCKED+HIDDEN, GLITCH
     braucht ANCHOR. In genau dieser Reihenfolge stehen die Badges auf der
     Startseite und die Klassenkaesten im FAQ — der Farbverlauf des Logos laeuft
     also mit dem Fortschritt mit (kalt am Anfang, warm am Ende):
       NODE   1983 Stueck, Wert 1-4   Cyan
       GATE     30 Stueck, Wert 2-3   Blau
       HIDDEN   80 Stueck, Wert 3-5   Violett
       LOCKED  120 Stueck, Wert 8-10  Magenta
       ANCHOR   10 Stueck, Wert 6-8   Pink
       GLITCH   80 Stueck, Wert 9-11  Orange
       CORE      1 Stueck, System     Rose

     ZUR KENNTNIS, NICHT DURCH DIESE AENDERUNG ENTSTANDEN: Die MATRYX-Karte
     (classColorStrong in matryx.php und karte.js) hat eine EIGENE Farbtabelle,
     die schon vorher voellig andere Werte trug — NODE dort dunkelblau statt
     gruen, GATE rot statt violett, LOCKED grau statt rot. Badge und Karte
     stimmten also nie ueberein. Das ist hier nicht mitgezogen worden, weil es
     ein eigener Auftrag waere. */
  --c-node:   #2AB8DE;
  --c-gate:   #4279DF;
  --c-hidden: #9053D4;
  --c-locked: #CD2AD8;
  --c-anchor: #E35096;
  --c-glitch: #FD7E5D;
  --c-core:   #E9607F;

  /* STATUSFARBEN — seit 28.07.2026 eigene Tokens.
     Vorher liehen sich die Statusmeldungen ihre Rahmenfarbe bei den Klassen:
     ok = --c-node (gruen), warn = --c-anchor (gold), fehler = --c-locked (rot).
     Mit den Logofarben waere daraus cyan/magenta/pink geworden — eine
     Erfolgsmeldung ohne Gruen und ein Fehler ohne Rot. Die alten Werte leben
     deshalb hier weiter, entkoppelt von der Spieloekonomie. */
  --c-ok:     #3dba5a;
  --c-warn:   #c8980a;
  --c-fehler: #c42020;

  /* Textfarben für Statusmeldungen: die Klassenfarben oben sind als SCHRIFT
     auf dunklem Grund zu satt. Diese aufgehellten Varianten tragen den Text,
     der Rahmen behält die kräftige Klassenfarbe. */
  --c-text-ok:     #c8f0d2;
  --c-text-warn:   #f2ddaa;
  --c-text-fehler: #f3c3c3;

  /* Neigung des Umschalters (Baustein .umschalter weiter unten). 30° sind
     genau die Flanke eines MATRYX-Dreiecks — die EINE Stellschraube für die
     Form aller Umschalter im Projekt. */
  --umschalter-neigung: -30deg;

  /* Blauverlauf ueber dem Hintergrundgif (27.07.2026, Foerbs Ansage):
     links oben hell und fast durchsichtig, nach rechts unten dunkelblau.
     Als Tokens, damit die beiden Enden an einer Stelle stehen und nicht als
     Hex-Werte mitten in einer Regel (§4 Regel 2 des Design-Vertrags). */
  /* Werte fuer die Lage UEBER der Karte (siehe #wrap::after). Dort wirken sie
     ganz anders als unter ihr: 0.97 am dunklen Ende wuerde die MATRYX rechts
     unten vollstaendig zudecken. Links oben nur ein Hauch, nach rechts unten
     kraeftig — die Karte bleibt ueberall sichtbar.
     GANZ DUNKLES BLAU, links oben voellig durchsichtig (Foerbs Ansage
     27.07.2026, nach Blau, Orange und Lila).

     BEIDE ENDEN HABEN DIESELBE FARBE, nur die Deckkraft laeuft von 0 auf 0.88.
     Das ist kein Zufall: Nimmt man zwei verschiedene Farben und laesst die
     eine auf Alpha 0 laufen, mischt der Browser auf halber Strecke ueber
     deren Farbwert mit — der Verlauf bekommt in der Mitte einen fremden,
     meist graustichigen Ton. Mit gleicher Farbe an beiden Enden bleibt es ein
     reines Aufblenden: oben links wirklich nichts, unten rechts sattes
     Dunkelblau. */
  --c-verlauf-hell:   rgba(6,18,56,0);
  --c-verlauf-dunkel: rgba(6,18,56,0.88);

  /* Zwei Flaechen fuer alles Kastenartige. Vorher gab es fuenf verschiedene
     Fuellungen (0.4 / 0.35 / 0.3 / 0.25 / weiss 0.03) fuer dieselbe Sache.
     hell = etwas heraus (Karten), tief = etwas hinein (Code, Eingabe, Buehne). */
  --flaeche-hell: rgba(255,255,255,0.03);
  --flaeche-tief: rgba(0,0,0,0.35);
  --akzent-balken: 3px;
}

/* Base */

html, body {
  margin:0;
  padding:0;
  background:var(--c-bg-body) url('bg.gif') repeat;
  background-attachment:fixed;
  font-family:Brose;
  font-size:var(--text-basis);
  letter-spacing:0.7px;
  line-height:20px;
  overflow-x:hidden;
  min-height:100vh;
  color:var(--c-white);
  word-break: break-word; 
}

main {
  padding-top:var(--menu-h);
  padding-bottom:0;
}
hr{
  border:none;
  border-top: 1px solid var(--c-line-tryx);
  margin:20px 0;
}
a{
  color:var(--c-signalfarbe);
  text-decoration:underline;
  transition:opacity 0.15s;
}
a:hover {
  opacity:var(--deckkraft-halb);
  text-shadow:0 0 8px var(--c-signal-glow);
}

button, .btn-matryx {
  padding:var(--knopf-padding);
  border-radius:var(--radius);
  background-color:var(--c-signalfarbe);
  color:var(--c-white);
  border:none;
  margin:2px 4px 2px 0;
  text-decoration:none;
  transition:box-shadow 0.2s, transform 0.15s;
}
.btn-matryx {
  display:block;
  margin:20px auto;
  text-align:center;
  width:max-content;
}
/* .btn-matryx setzt display:block und uebersteuert damit das native
   [hidden]{display:none} — dieselbe Falle wie bei .feld (§4 Regel 6). Akut
   geworden am 27.07.2026: scan.php gibt dem Kamera-Startknopf .btn-matryx, und
   qr/scan.js blendet genau diesen Knopf per hidden aus, wenn das Kameramodul
   fehlt. Ohne diese Regel bliebe dort ein toter Knopf stehen. */
.btn-matryx[hidden] { display:none; }

button:hover, .btn-matryx:hover{
  cursor:pointer;
  box-shadow:0 0 12px var(--c-signal-glow);
}

/* margin-bottom:0 gilt seit dem 28.07.2026 nur noch fuer h1 — h2 bis h6 setzen
   weiter unten ihren eigenen Abstand nach unten (Foerbs Auflockerung). Stuende
   die 0 hier weiter fuer alle, muesste jede Ebene sie einzeln zuruecknehmen. */
h1, h2, h3, h4, h5, h6{
  font-weight:normal;
}
h1{
  margin-bottom:0;
}

/* ALLE Ueberschriften in Pixelkrebs (Foerbs Auftrag 28.07.2026). h6 steht mit
   in der Liste, obwohl das Projekt derzeit keine h6 benutzt — der Auftrag
   lautete h1 bis h6, und eine spaeter dazukommende h6 soll nicht herausfallen.
   Brose als Rueckfall fuer die Zeichen, die Pixelkrebs nicht hat (siehe
   @font-face oben). Nur die Schrift wird getauscht: Groessen, Farben,
   Zeilenhoehen und Abstaende bleiben unveraendert. */
h1, h2, h3, h4, h5, h6{
  font-family:'Pixelkrebs','Brose',sans-serif;
}

h1{
  text-align:center;
  font-size:calc(var(--text-h1) * var(--text-h-faktor));
  line-height:calc(40px * var(--text-h-faktor));
  text-shadow:0 0 30px var(--c-signal-glow), 0 2px 4px rgba(0,0,0,0.8);
  letter-spacing:2px;
}

/* ABSTAENDE UEBER UND UNTER DEN UEBERSCHRIFTEN (Foerbs Auftrag 28.07.2026:
   "alles sitzt ziemlich eng und undynamisch aufeinander, wir brauchen dringend
   viel mehr Raum zwischen Abschnitten, ueber alle h2-h6").

   ALLE WERTE IN EM, BEWUSST. em bezieht sich auf die Schriftgroesse der
   Ueberschrift SELBST. Damit gilt zweierlei:
     - Die Luft waechst automatisch mit --text-h-faktor mit. Vorher standen hier
       feste 28/20/14px, die nach zwei Vergroesserungen optisch zusammengefallen
       waren — genau das Gedraenge, das Foerb gemeldet hat.
     - Die Abstufung bleibt erhalten: Ein h2 bekommt mehr Luft als ein h4, weil
       es groesser ist, ohne dass das irgendwo doppelt gepflegt werden muss.

   NEU ist ausserdem der Abstand NACH UNTEN. Bis hierher stand ueberall
   margin-bottom:0, die Ueberschrift klebte also direkt auf ihrem Text. Ein
   kleiner Abstand (rund ein Drittel bis halbe Zeile) bindet sie sichtbar an
   ihren Absatz, ohne den Zusammenhang zu zerreissen. */
h2{
  font-size:calc(var(--text-h2) * var(--text-h-faktor));
  line-height:calc(30px * var(--text-h-faktor));
  color:var(--c-signalfarbe);
  text-shadow:0 0 14px rgba(255,136,0,0.35);
  margin-top:1.7em;
  margin-bottom:0.5em;
  padding-bottom:6px;
  border-bottom:1px solid var(--c-signal-dim);
}

h3{
  font-size:calc(var(--text-h3) * var(--text-h-faktor));
  /* ZEILENHÖHE OHNE EINHEIT — behoben 28.07.2026, gerendert nachgewiesen.
     h3 bis h6 hatten NIE eine eigene line-height und erbten die feste
     body{line-height:20px}. Solange die Überschriften klein waren, fiel das
     nicht auf. Bei Faktor 2,028 steht eine 40,6px hohe Schrift in einer 20px
     hohen Zeilenbox: Zwei Zeilen einer umbrechenden Überschrift lagen
     vollständig ineinander und waren unlesbar (gemessen auf faq.php bei
     390px, u. a. "Woran du siehst, was du in der Hand haeltst"; auf api.php
     waren 5 von 8 h3 betroffen).
     Einheitenlos statt calc(): Der Wert bezieht sich damit auf die eigene
     Schriftgröße und stimmt bei JEDEM künftigen --text-h-faktor, ohne dass
     hier noch einmal jemand nachrechnen muss. */
  line-height:1.2;
  color:var(--c-ueberschrift-3);
  margin-top:1.6em;
  margin-bottom:0.45em;
}
/* h6 steht seit dem 28.07.2026 mit hier: Foerbs Auftrag lautete "ueber alle
   h2-h6". Das Projekt benutzt derzeit keine h6 — eine spaeter dazukommende
   soll aber nicht als einzige ohne Groesse und ohne Luft dastehen. */
h4, h5, h6{
  font-size:calc(var(--text-xl) * var(--text-h-faktor));
  /* Siehe Begründung bei h3 — dasselbe Problem, dieselbe Lösung. */
  line-height:1.2;
  color:var(--c-ueberschrift-4);
  margin-top:1.5em;
  margin-bottom:0.4em;
}
strong {
  color:var(--c-white-klar);
  font-weight:600;
}
ul {
  margin:6px 0;
  padding-left:0;
  list-style:none;
}
ul li {
  padding-left:16px;
  position:relative;
}
ul li::before {
  content:'▸';
  position:absolute;
  left:0;
  color:var(--c-signalfarbe);
  font-size:0.75em;
  line-height:20px;
}

/* Die EINE Karte. Alles Kastenartige nimmt sie, statt sein eigenes Rezept zu
   erfinden: gleiche Flaeche, gleicher Rand, gleicher Radius.
   .karte--akzent setzt den Signalbalken links, wo ein Kasten etwas betont. */
/* KARTE — die abgesetzte Fläche für einen abgeschlossenen Gedanken.
   Zusammengeführt am 27.07.2026: Es gab sie zweimal, mit unterschiedlichem
   Innenabstand (14/16 gegen 16/18 px) — einmal aus dem Fundament (FAQ-Kästen)
   und einmal von der Startseite, wo ich den Namen unwissentlich ein zweites Mal
   belegt hatte. Im verteilten CSS fiel das niemandem auf; in einer Datei stehen
   sie zwei Bildschirme voneinander entfernt. Jetzt EINE Karte.

   Das Flex-Verhalten (Fußlink unten, gleich hohe Karten) gilt NUR im Raster:
   Auf einer freistehenden Karte würde display:flex das Zusammenfallen der
   Absatzabstände unterbinden und alle Abstände darin ändern. */
/* RAHMEN-FLÄCHE — der abgesetzte Kasten, der etwas als Einheit zusammenfasst.
   Zusammengeführt am 27.07.2026: .karte, #ergebnis, .scan-wissen und
   .ki-endpoint trugen alle vier dieselben zwei Zeilen (dünne Linie, Standard-
   radius) und unterschieden sich nur in Fläche und Innenabstand. Wer künftig
   einen solchen Kasten braucht, hängt seinen Selektor hier an, statt die zwei
   Zeilen ein fünftes Mal zu schreiben. */
.karte,
#ergebnis,
.scan-wissen,
.ki-endpoint {
  border:1px solid var(--c-line-tryx);
  border-radius:var(--radius);
}
.karte {
  background:var(--flaeche-hell);
  padding:16px 18px;
  margin:var(--luecke-l) 0;
}
.raster .karte { display:flex; flex-direction:column; margin:0; }
/* AKZENTBALKEN — die linke Kante in der Signalfarbe, die einen Kasten
   heraushebt, ohne ihm eine zweite Farbe zu geben. Stand an fünf Stellen
   wörtlich gleich; hier ist die eine Quelle. Wer ihn braucht, hängt sich an. */
.karte--akzent,
.scan-warnung,
.ki-note,
.ki-endpoint,
.trans-eigen td:first-child {
  border-left:var(--akzent-balken) solid var(--c-signalfarbe);
}

pre {
  background: var(--flaeche-tief);
  border: 1px solid var(--c-line-tryx);
  border-radius: var(--radius);
  padding: 12px 14px;
  overflow-x: auto;
  font-size:var(--text-m);
  line-height: 18px;
  white-space: pre;
}

code {
  background: var(--flaeche-tief);
  border-radius:var(--radius-klein);
  padding: 1px 5px;
  font-size: 0.9em;
}

pre code {
  background: none;
  padding: 0;
  font-size: inherit;
}

.space32 {
  height: 32px;
  width: 100%;
}

canvas {
  width:100%;
  height:100%;
  display:block;
}

/* Layout / Content */

#wrap {
  position:fixed;
  inset:0;
  z-index:0;
  touch-action:none;
}

/* BLAUVERLAUF ÜBER DER MATRYX (27.07.2026, Foerbs Ansage).
   Der erste Versuch lag als zweite Ebene auf dem Hintergrundgif — und war
   unsichtbar. Foerbs Beobachtung "beim Laden leuchtet es kurz blau" war der
   Hinweis: Der Verlauf wurde gemalt und danach von der Karte zugedeckt.
   Nachgemessen ist das Canvas zu 84 % bei Alpha 192–239 und zu 16 % noch
   dichter — es deckt den Body-Hintergrund praktisch vollstaendig. (Meine
   erste Messung zaehlte alles ausser 0 und 255 als "teildurchsichtig" und
   war damit irrefuehrend.)

   Deshalb liegt der Verlauf JETZT ueber der Karte, aber unter dem Inhalt:
   ::after von #wrap erbt dessen z-index:0, die .seite steht auf 1.
   pointer-events:none, damit die Karte auf matryx.php anklickbar bleibt. */
#wrap::after {
  content:"";
  position:absolute;
  inset:0;
  background:linear-gradient(135deg,
              var(--c-verlauf-hell) 0%,
              var(--c-verlauf-dunkel) 100%);
  pointer-events:none;
}

.seite {
  position:relative;
  z-index:1;
  max-width:900px;
  margin:40px auto 80px;
  background:rgba(10,10,14,0.82);
  color:var(--c-white);
  padding:28px 28px 32px;
  box-sizing:border-box;
  border-radius:var(--radius-gross);
  border:1px solid rgba(255,136,0,0.2);
  border-top:2px solid var(--c-signalfarbe);
  box-shadow:var(--shadow-seite);
  backdrop-filter:blur(4px);
}
.seite h1 sup {
  font-size: 0.6em;
  vertical-align: super;
  /* line-height:0 ist hier kein Schoenheitsfehler, sondern der Kern der Sache
     (gemessen 27.07.2026, Foerbs Befund "die h1 muss ueberall auf gleicher
     Hoehe sitzen"): vertical-align:super dehnt die Zeilenbox nach OBEN, und
     das schiebt die ganze Ueberschrift nach unten. Gemessen auf 390px:
     userlist.php (mit XDR^5) Schriftoberkante 94px und Kastenhoehe 47px,
     scan.php (ohne sup) 87px und 40px — 7px Unterschied, obwohl h1 fest
     line-height:40px deklariert. Mit line-height:0 traegt das hochgestellte
     Zeichen nichts mehr zur Zeilenhoehe bei, die deklarierten 40px gelten
     wieder, und JEDE h1 sitzt gleich hoch. Zentral hier, damit scan.php keine
     Sonderregel braucht — die Ursache lag ohnehin auf den anderen Seiten. */
  line-height: 0;
}

/* LUFT VOR DER HOCHGESTELLTEN 5 (Foerbs Befund 28.07.2026 an der h1 von
   userlist.php: "da muss etwas Platz vor die 5"). In Pixelkrebs und bei der
   inzwischen deutlich groesseren Ueberschrift klebt die 5 sonst am R von XDR.

   Der Wert steht in em und bezieht sich damit auf die Groesse des sup SELBST —
   er waechst also mit jeder Aenderung von --text-h-faktor mit, statt bei der
   naechsten Groessenaenderung wieder falsch zu sein.

   Gilt fuer ALLE Ueberschriftenebenen, nicht nur h1: XDR^5 steht auch in h2
   ("Das XDR^5 Leitbild", "XDR^5-Bank-ID") und h3. Der Fliesstext bleibt
   bewusst unberuehrt — dort ist die Schrift klein und das Gedraenge kein
   Thema. */
.seite h1 sup, .seite h2 sup, .seite h3 sup,
.seite h4 sup, .seite h5 sup, .seite h6 sup {
  margin-left:0.2em;
}

/* Abstand zwischen den Abschnitten selbst (28.07.2026, von 20px erhoeht).
   Er greift dort, wo ein Abschnitt NICHT mit einer Ueberschrift beginnt — etwa
   bei den horizontalsumme-Bloecken mit Bild links. Beginnt ein Abschnitt mit
   einer Ueberschrift, faellt dieser Abstand mit deren groesserem margin-top
   zusammen (Margin Collapsing); dort gewinnt die Ueberschrift, und das ist so
   gewollt. */
.seite section {
  margin-top:44px;
}
.seite p {
  margin:10px 0 0;
  line-height:22px;
}
.seite ol {
  margin:8px 0;
  padding-left:22px;
}
.seite ol li {
  padding-left:4px;
}
.seite ol li::before {
  display:none;
}
/* Statusmeldung — EIN Baustein für alle Seiten (25.07.2026).
   Entstand doppelt auf scan.php und qr_gen.php; zusammengeführt, bevor sich das
   Muster festsetzt. Rahmen trägt die Bedeutung, der Text bleibt hell lesbar. */

.meldung {
  margin:12px 0;
  padding:9px 11px;
  border-radius:var(--radius);
  border:1px solid var(--c-line);
  background:var(--flaeche-tief);
  min-height:20px;
}
.meldung--ok     { border-color:var(--c-ok);     color:var(--c-text-ok); }
.meldung--warn   { border-color:var(--c-warn);   color:var(--c-text-warn); }
.meldung--fehler { border-color:var(--c-fehler); color:var(--c-text-fehler); }

/* Solange nichts zu melden ist, soll kein leerer Kasten herumstehen — der stand
   beim Laden von qr_gen.php als 40 px hohe Leerfläche da (gemessen 25.07.).
   BEWUSST NICHT display:none: Das Feld ist eine aria-live-Region. Nimmt man sie
   aus dem Baum, kündigen Screenreader den später erscheinenden Text
   unzuverlässig an. Sie bleibt also da und wird nur unsichtbar. */
.meldung:empty {
  border-color:transparent;
  background:none;
  min-height:0;
  padding:0;
  margin:0;
}

/* Formularfeld — Label-Wrapper mit dunklem Eingabefeld. Ebenfalls
   zusammengeführt aus scan.php/qr_gen.php. */

.feld { display:block; margin:9px 0; }
.feld input,
.feld select {
  display:block;
  width:100%;
  max-width:340px;
  box-sizing:border-box;
  padding:7px 8px;
  margin-top:3px;
  background:rgba(0,0,0,0.5);
  border:1px solid var(--c-line);
  border-radius:var(--radius);
  color:var(--c-white);
  font-family:inherit;
  font-size:var(--text-basis);
}
/* Mehrere Felder nebeneinander (z. B. "von"/"bis"). */
.feld--quer {
  display:flex;
  gap:var(--luecke-m);
  flex-wrap:wrap;
  max-width:340px;
}
.feld--quer label { flex:1 1 120px; }
.feld--quer input { max-width:none; }

/* hidden muss hidden bleiben. .feld und .feld--quer setzen display und würden
   das native [hidden]{display:none} sonst übersteuern — genau das ist am
   25.07. passiert: auf qr_gen.php standen alle Auswahlfelder gleichzeitig da,
   obwohl sie ausgeblendet waren. */
.feld[hidden], .feld--quer[hidden] { display:none; }

/* Umschalter — EIN Baustein für alle Seiten (27.07.2026, Foerbs Auftrag).
   Zwei Ansichten derselben Seite, genau eine sichtbar. Löst den seiteneigenen
   .faq-toggle ab. Benutzt wird er bisher NUR von faq.php; scan.php bekommt ihn
   im nächsten Schritt („einlesen | exportieren"). Er liegt trotzdem schon hier
   zentral, damit eine spätere Umgestaltung beide Seiten zugleich trifft und
   nicht zweimal erfunden wird (§4 Regel 5 des Design-Vertrags).

   FORM: ein Parallelogramm — die obere Kante nach rechts versetzt, beide
   Seitenkanten gleich schräg. 30° sind genau die Flanke eines MATRYX-Dreiecks
   (60° zur Waagerechten); der Schalter zitiert damit die Grundform des
   Projekts. --umschalter-neigung ist die EINZIGE Stellschraube dafür — eine
   Zahl ändern, und jeder Umschalter im Projekt kippt mit. Der Token steht bei
   den übrigen im :root ganz oben, damit es EINEN Token-Block gibt. Die
   Beschriftung wird gegengeneigt, sonst stünde sie schief. */

.umschalter {
  display:grid;
  /* Beide Segmente exakt gleich breit — der gleitende Reiter rechnet mit 50 %. */
  grid-template-columns:1fr 1fr;
  width:max-content;
  max-width:100%;
  margin:18px auto 24px;
  position:relative;
  border:1px solid var(--c-line);
  /* KEINE runden Ecken (Foerbs Ansage 27.07.2026). Bei der geneigten Form
     werden mitgeneigte Rundungen an den spitzen Ecken ungleich lang und der
     Reiter deckt den Aussenrahmen dort nicht mehr sauber ab. Ein
     Parallelogramm hat scharfe Ecken — deshalb hier bewusst kein --radius. */
  border-radius:0;
  transform:skewX(var(--umschalter-neigung));
}
/* display:grid würde das native [hidden]{display:none} übersteuern — §4 Regel 6.
   Dieselbe Falle wie bei .feld, #sidebar-toggle und #xdr-not-logged. */
.umschalter[hidden] { display:none; }

/* Die Segmente sind KEIN zweiter Button-Stil im Sinne von §4 Regel 1, sondern
   der Rahmen selbst trägt die Optik — der Knopf darin ist nur die Fläche. */
.umschalter button {
  position:relative;
  z-index:1;
  margin:0;
  padding:7px 24px;
  background:transparent;
  border:none;
  border-radius:0;
  color:var(--c-white);
  font-size:var(--text-m);
  letter-spacing:1px;
  white-space:nowrap;
  cursor:pointer;
  opacity:var(--deckkraft-dezent);
  transition:opacity 0.2s, color 0.2s;
}
/* box-shadow zurücknehmen: Das globale button:hover legt einen orangen Schein
   an, der hier den Reiter doppeln würde. */
.umschalter button:hover { opacity:var(--deckkraft-fast); box-shadow:none; }
.umschalter button[aria-selected="true"] { opacity:1; color:var(--c-signalfarbe); }
/* Der Schalter ist mit den Pfeiltasten bedienbar — der Fokusring darf nicht
   fehlen. Er wird mitgeneigt und liegt dadurch parallel zur Kante. */
.umschalter button:focus-visible {
  outline:2px solid var(--c-signalfarbe);
  outline-offset:2px;
}
.umschalter button > span {
  display:inline-block;
  transform:skewX(calc(-1 * var(--umschalter-neigung)));
}

/* Der gleitende Reiter. Liegt unter der Schrift und deckt den Außenrahmen des
   aktiven Segments genau ab (daher die -1px). Reine Optik: aria-hidden im
   Markup, pointer-events aus, damit er keine Klicks abfängt. */
.umschalter-reiter {
  position:absolute;
  top:-1px;
  bottom:-1px;
  left:-1px;
  width:calc(50% + 1px);
  /* ZWINGEND border-box (gemessen 27.07.2026, Foerbs Befund "der Rahmen stimmt
     nicht genau"): Ohne das meint width nur die INNENflaeche, die beiden
     1px-Raender kommen obendrauf — der Reiter wird 2px zu breit. Schlimmer
     noch verschiebt translateX(100%) um genau diese zu grosse Breite, dadurch
     stand im rechten Zustand die linke Kante 2px zu weit rechts und die rechte
     Kante ragte 4px ueber den Aussenrahmen hinaus. Mit border-box deckt der
     Reiter links wie rechts exakt den Rahmen des aktiven Segments. */
  box-sizing:border-box;
  border:1px solid var(--c-signalfarbe);
  border-radius:0;
  background:var(--c-signal-dim);
  box-shadow:0 0 10px var(--c-signal-dim);
  pointer-events:none;
  transition:transform 0.25s ease;
}
.umschalter[data-aktiv="1"] .umschalter-reiter { transform:translateX(100%); }

/* DREI SEGMENTE (27.07.2026, QR-PREIS): scan.php hat jetzt einlesen /
   exportieren / generieren. Der Baustein war auf zwei ausgelegt — beide Zahlen
   stecken in grid-template-columns und in der Reiterbreite, deshalb hier
   zusammen an EINER Stelle statt verstreut.

   Die Umschalt-Logik in JS musste dafuer nicht angefasst werden: Sie zaehlt die
   Knoepfe selbst und setzt data-aktiv auf deren Index. Die Regeln unten sind
   spezifischer als die Zwei-Segment-Regel darueber und gewinnen deshalb, ohne
   sie zu veraendern — faq.php und jeder andere Zweisegment-Schalter bleiben
   unberuehrt. */
.umschalter[data-segmente="3"] { grid-template-columns:1fr 1fr 1fr; }
.umschalter[data-segmente="3"] .umschalter-reiter { width:calc(33.3333% + 1px); }
.umschalter[data-segmente="3"][data-aktiv="1"] .umschalter-reiter { transform:translateX(100%); }
.umschalter[data-segmente="3"][data-aktiv="2"] .umschalter-reiter { transform:translateX(200%); }
/* Wer im Betriebssystem "Bewegung reduzieren" gesetzt hat, bekommt den Wechsel
   ohne Gleiten — dasselbe Zugeständnis wie beim Sprung zum Ergebnis in
   qr/scan.js. */
@media (prefers-reduced-motion: reduce) {
  .umschalter-reiter { transition:none; }
}
/* Derselbe EINE Breakpoint wie im übrigen Projekt (§3), nur hier beim Baustein
   statt unten im Responsive-Abschnitt, damit er vollständig an einer Stelle
   steht. Die schrägen Kanten ragen bei 30° und ~34px Höhe rund 10px über die
   Fläche hinaus — dafür der Seitenabstand. */
@media (max-width:600px) {
  .umschalter { width:auto; margin-left:12px; margin-right:12px; }
  .umschalter button { padding:7px 10px; }
}

/* ---------------------------------------------------------------------------
   TABELLEN AUF SCHMALEN SCHIRMEN — EIN VERHALTEN FUER ALLE (27.07.2026)

   Foerbs Ansage: "die tabellen muessen in der mobilen ansicht einheitlich
   umbrechen." Vorher taten sie drei verschiedene Dinge, gemessen bei 375 px:

     .user-table (ID-Liste)   Breite 343 px, bricht um, Zeile 77 px hoch  — gut
     .trans-table             Breite 478 px, ragt hinaus und wird ABGESCHNITTEN;
                              die ID-Spalte auf 0 px gequetscht, dadurch eine
                              einzige Zeile 657 px hoch                  — kaputt
     .faq-tabelle             scrollt waagerecht (min-width:520px)       — anders

   Einheitlich heisst hier: umbrechen wie die ID-Liste. Also KEINE festen
   Pixelbreiten und KEINE Mindestbreite mehr auf schmalen Schirmen — die
   Spaltenanteile werden prozentual, und was nicht passt, bricht innerhalb
   seiner Zelle um.

   Damit ist das waagerechte Scrollen der FAQ-Tabelle bewusst aufgegeben. Der
   alte Kommentar dort begruendete es damit, dass die Spalte "Wozu" sonst auf
   81 px faellt — das stimmt, ist aber Foerbs Entscheidung: lieber eine hohe
   Tabelle als eine, die man wischen muss, und vor allem ueberall dieselbe.

   overflow-wrap:anywhere statt word-break:break-all: break-all zerhackt JEDES
   Wort mitten drin, auch wenn daneben Platz waere. anywhere bricht erst, wenn
   es sein muss — deshalb bleibt "Preisschild" ganz und nur die 32-stellige ID
   wird umbrochen. */
.user-table,
.faq-tabelle {
  table-layout:fixed;
  width:100%;
}
/* WIE gebrochen wird, ist der Unterschied zwischen "bricht um" und "ist
   zerhackt" (Foerbs Befund am ersten Versuch: aus "Betrag" wurde "Bet rag",
   aus "Schild" "Sch ild"):

   - hyphens:auto trennt deutsche Woerter an ihren Silben MIT Bindestrich —
     "Preis-schild" statt "Preissch ild". Das setzt lang="de" am <html> voraus,
     und das steht oben.
   - overflow-wrap:anywhere ist nur der Rueckfall fuer Zeichenketten, die keine
     Woerter sind: die 32-stelligen IDs und Pfade wie
     api/api_confirm_payment_intent.php. Es greift erst, wenn die Silbentrennung
     nicht weiterhilft.
   - KOPFZEILEN werden nie gebrochen. Sie sind ein bis zwei Woerter lang; passt
     eine nicht, ist die Spalte zu schmal — das gehoert dort behoben und nicht
     durch Zerhacken kaschiert. */
.user-table td,
.faq-tabelle td {
  /* break-word, NICHT anywhere: anywhere erlaubt den Bruch an jeder Stelle und
     der Browser nimmt ihn, BEVOR er die Silbentrennung anwendet — aus
     "Preisschild" wurde dadurch "Preis schild" ohne Bindestrich (gemessen bei
     375 px). break-word bricht ein Wort erst, wenn es sonst ueberlaeuft; bis
     dahin darf hyphens:auto sauber trennen. Fuer die 32-stelligen IDs greift
     es genauso, dort gibt es nur keine Silben zu finden. */
  overflow-wrap:break-word;
  hyphens:auto;
}
.user-table th,
.faq-tabelle th {
  overflow-wrap:normal;
  word-break:normal;
  hyphens:none;
}

/* AUF SCHMALEN SCHIRMEN WIRD GESTAPELT, NICHT GEQUETSCHT (Foerbs Entscheid
   27.07.2026, nachdem eine Messung die Grenze gezeigt hat).

   Warum keine Tabelle mehr: Die Transaktionsliste hat sechs Spalten. Damit
   nichts mitten im Wort bricht, brauchen sie zusammen mindestens 331 px
   ("27.07.2026" 79, "Betrag" 51, "Preisschild" 78, "Schild" 49, Knopf
   "anzeigen" 74) — bei 343 px nutzbarer Breite blieben der ID-Spalte 12 px.
   Sechs Spalten passen auf ein Handy nicht, egal wie man die Prozente verteilt.
   Zwei Versuche mit Prozentbreiten endeten in "Bet rag" und "Preissc hild".

   Also wird jede Zeile ein Block: Beschriftung links, Wert rechts, nichts
   verschwindet, nichts wird zerhackt. Die Beschriftung kommt aus data-label an
   der Zelle — deshalb MUSS jede neue Tabellenzelle im Projekt eines tragen,
   sonst steht dort mobil eine leere Spalte. Die erste Zelle jeder Zeile ist der
   Blockkopf und bekommt keine Beschriftung (sie traegt Datum bzw. Kennung).

   Es gilt fuer ALLE drei Tabellen des Projekts — das war der Auftrag:
   ID-Liste, Transaktionen und die FAQ-Tabellen verhalten sich gleich. */
@media (max-width:600px) {
  .user-table,
  .faq-tabelle {
    display:block;
    font-size:var(--text-basis);
    background:none;
  }
  /* Kopfzeilen weg: Ihre Aufgabe uebernimmt .td-label in jeder Zelle.
     .user-table hat ein <thead>, die FAQ-Tabellen ihre Kopfzeile als erstes <tr>. */
  .user-table thead,
  .faq-tabelle tr:first-child { display:none; }
  .user-table tbody,
  .faq-tabelle tbody { display:block; }
  .user-table tr,
  .faq-tabelle tr {
    display:block;
    border:1px solid var(--c-line-tryx);
    border-radius:var(--radius);
    background:var(--flaeche-hell);
    margin:0 0 10px;
    padding:8px 10px;
  }
  /* KEIN display:grid hier — das war der erste Versuch und er zerreisst Zellen:
     Ein Grid macht JEDES Kind zu einem eigenen Item, also stand ein Satz wie
     "<code>id</code> + <code>key</code> in jedem Aufruf" mobil in fuenf
     untereinanderliegenden Kaestchen. Stattdessen: die Beschriftung absolut
     nach links, der Wert mit Einzug. Damit bleibt der Zellinhalt ein
     zusammenhaengender Textfluss, und mehrzeilige Werte bleiben eingerueckt. */
  .user-table td,
  .faq-tabelle td {
    display:block;
    position:relative;
    padding:3px 0 3px 92px;
    min-height:20px;
    border-bottom:none;
  }
  /* DIE BESCHRIFTUNG IST ECHTER TEXT, kein ::before mit content:attr().
     Grund (barrierefreiheit-agent, 27.07.2026, MUSS-Befund): Generierter
     CSS-Inhalt landet nicht in jeder Browser/Screenreader-Kombination im
     Accessibility-Baum. Weil die Kopfzeile hier gleichzeitig ausgeblendet ist,
     haetten Screenreader-Nutzer im schlechtesten Fall reine Werte ohne jede
     Spaltenzuordnung bekommen — also weniger als sehende Nutzer. Ein echtes
     <span> ist immer im Baum und braucht kein aria-label als zweite Wahrheit. */
  .td-label {
    display:block;
    position:absolute;
    left:0;
    top:5px;
    width:84px;
    opacity:var(--deckkraft-dezent);
    font-size:var(--text-s);
    letter-spacing:1px;
    text-transform:uppercase;
    overflow-wrap:break-word;
  }
  /* Der Blockkopf: erste Zelle, volle Breite, ohne Einzug — sie traegt das
     Datum bzw. die Kennung und hat deshalb auch kein .td-label. */
  .user-table td:first-child,
  .faq-tabelle td:first-child {
    padding:0 0 6px;
    margin-bottom:4px;
    font-size:var(--text-l);
    color:var(--c-signalfarbe);
    border-bottom:1px solid var(--c-line-tryx);
  }
  /* Eine Zelle ueber die ganze Zeile (colspan) ist kein Wertepaar, sondern ein
     eigener Bereich — etwa die aufgeklappte Beleg-Zeile. */
  .user-table td[colspan],
  .faq-tabelle td[colspan] { padding-left:0; }
}

/* Auf breiten Schirmen ist die Beschriftung ueberfluessig — dort steht sie in
   der Kopfzeile. Sie wird VERSTECKT, nicht weggelassen: derselbe Text zweimal
   im Baum waere fuer Screenreader Doppelung, und display:none nimmt ihn sauber
   heraus, waehrend die Kopfzeile ihre Aufgabe erfuellt. */
.td-label { display:none; }

/* WAS DIE SEITEN SELBST REGELN MUESSEN: die Spaltenanteile. Die haengen am
   Inhalt (Codepfade brauchen anders viel Platz als Zahlenspalten) und stehen
   deshalb bei der jeweiligen Tabelle. Die EINE Regel dabei, an der .trans-table
   gescheitert ist: auf schmalen Schirmen PROZENTUAL, nie in Pixeln. Feste
   Pixelbreiten summieren sich bei table-layout:fixed ueber die Schirmbreite
   hinaus, ohne gestaucht zu werden — die flexible Spalte bekommt dann 0 px. */

/* ---------------------------------------------------------------------------
   CODE-FLAECHE UND BELEG (27.07.2026, QR-PREIS)

   Gebraucht von ZWEI Seiten: scan.php (erzeugtes Preisschild, Beleg nach der
   Zahlung) und userlist.php (Beleg je Zeile der Transaktionsliste). Steht
   deshalb hier und nicht zweimal in den Seiten — §4 Regel 5 des
   Design-Vertrags, und die Regel hat sofort gegriffen: Beim Bau lagen die
   Klassen kurzzeitig in beiden Styleblöcken und waren schon auseinandergelaufen
   (margin 10px gegen 6px), noch bevor sie einmal live waren.

   WEISS IST HIER PFLICHT, kein Gestaltungswunsch: Der Leser schwellt das Bild
   per Otsu-Verfahren und braucht hellen Grund, sonst fällt der Code durch.
   Dieselbe begründete Token-Ausnahme wie bei .gen-vorschau. */
/* CODE-FLÄCHE — der weiße Grund, auf dem ein gedrucktes Dreieck liegt.
   Zusammengeführt am 27.07.2026: Es gab dreimal dieselbe Regel unter drei
   Namen (.preis-bild auf scan.php/userlist.php, .qr-schau-bild im FAQ-Kasten,
   .gen-vorschau im Betreiber-Generator) — gleiche Farbe, gleicher Radius,
   gleiches Innenmaß, und dreimal identisch "svg { width:100%; height:auto }".
   Verschieden ist nur die GRÖSSE, und die bleibt bei den Einzelklassen. */
.preis-bild,
.qr-schau-bild,
.gen-vorschau {
  background:var(--flaeche-code);
  padding:10px;
  border-radius:var(--radius);
  box-shadow:var(--shadow);
}
.preis-bild svg,
.qr-schau-bild svg,
.gen-vorschau svg { width:100%; height:auto; display:block; }
.preis-bild { flex:0 0 220px; width:220px; }
/* Frisch erzeugtes Preisschild: groesser als ein Beleg, weil man es vor dem
   Drucken wirklich ansehen will. */
.preis-bild--gross { flex:none; width:min(300px, 80vw); margin:12px 0; }
/* Code links, Daten rechts. */
.beleg-block {
  display:flex;
  gap:var(--luecke-l);
  align-items:flex-start;
  flex-wrap:wrap;
  margin:10px 0;
}
.beleg-daten { flex:1 1 220px; min-width:0; }
/* WERTEPAAR-LISTE — Beschriftung klein und zurückgenommen, Wert darunter.
   Zusammengeführt am 27.07.2026: .scan-treffer (was in einem Code steht) und
   .beleg-daten (was auf einem Beleg steht) hatten Zeichen für Zeichen dieselbe
   Optik unter zwei Namen. Sie bleiben zwei Klassen, weil sie zwei Dinge
   benennen — aber sie sehen an EINER Stelle gleich aus. */
.scan-treffer dt,
.beleg-daten dt {
  opacity:var(--deckkraft-dezent);
  font-size:var(--text-s);
  letter-spacing:1px;
  text-transform:uppercase;
  margin-top:var(--luecke-s);
}
.scan-treffer dd,
.beleg-daten dd { margin:2px 0 0; word-break:break-all; }
/* Die Uhrzeit ist das wichtigste Feld eines Belegs — sie allein unterscheidet
   einen frischen von einem alten. Deshalb tritt sie hervor. */
.beleg-daten .beleg-zeit { font-size:var(--text-xl); }
.beleg-alt { color:var(--c-signalfarbe); }
.beleg-hinweis { margin-top:0; font-size:var(--text-basis); }
@media (max-width:600px) {
  .preis-bild { flex-basis:min(200px, 60vw); width:auto; }
  .preis-bild--gross { width:min(300px, 80vw); }
}

/* TRYX-Klassen-Badges */

.tryx-badge {
  display:inline-block;
  padding:1px 7px;
  border-radius:var(--radius-klein);
  font-size:var(--text-s);
  letter-spacing:1px;
  font-weight:600;
  vertical-align:middle;
  text-transform:uppercase;
}
.tryx-badge-core   { background:rgba(233,96,127,0.15);  color:var(--c-core);   border:1px solid var(--c-core);   }
.tryx-badge-node   { background:rgba(42,184,222,0.15);  color:var(--c-node);   border:1px solid var(--c-node);   }
.tryx-badge-gate   { background:rgba(66,121,223,0.15);  color:var(--c-gate);   border:1px solid var(--c-gate);   }
.tryx-badge-hidden { background:rgba(144,83,212,0.15);  color:var(--c-hidden); border:1px solid var(--c-hidden); }
.tryx-badge-locked { background:rgba(205,42,216,0.15);  color:var(--c-locked); border:1px solid var(--c-locked); }
.tryx-badge-anchor { background:rgba(227,80,150,0.15);  color:var(--c-anchor); border:1px solid var(--c-anchor); }
.tryx-badge-glitch { background:rgba(253,126,93,0.15);  color:var(--c-glitch); border:1px solid var(--c-glitch); }

/* Horizontalsumme class image left column */
.horizontalsumme .horiz_links img {
  border-radius:var(--radius);
  border:1px solid var(--c-line-tryx);
  box-shadow:0 4px 16px rgba(0,0,0,0.6);
  filter:brightness(0.92) saturate(1.1);
}

/* Top-Menü */

#menu {
  position:fixed;
  left:0;
  right:0;
  top:0;
  background:rgba(8,8,10,0.97);
  border-bottom:1px solid rgba(255,136,0,0.25);
  z-index:2000;
}

#menu nav {
  display:flex;
  flex-wrap:wrap;
  align-items:center;
  justify-content:center;
  text-align:center;
}

/* Menuelink UND Aufklapp-Knopf teilen sich die Typografie: Der Knopf ist kein
   zweiter Baustein, sondern derselbe Menuepunkt in anderer Rolle. Deshalb EIN
   Selektor statt zweier gleichlautender Bloecke. */
#menu a,
#menu-knopf {
  color:var(--c-signalfarbe);
  padding:11px 14px;
  text-transform:uppercase;
  letter-spacing:1.5px;
  font-size:var(--text-m);
}
#menu a {
  white-space:nowrap;
  text-decoration:none;
  transition:color 0.15s, text-shadow 0.15s;
}
#menu a:hover,
#menu-knopf:hover {
  text-shadow:0 0 10px var(--c-signal-glow);
}
#menu a:hover { opacity:1; }

/* Nur unter 600px sichtbar (siehe Breakpoint weiter unten). margin und
   border-radius muessen ausdruecklich zurueck: Die globale button-Regel gibt
   jedem Knopf 2px/4px Versatz und eine Eckenrundung mit — der Knopf staende
   sonst minimal anders als die Links, mit denen er eine Zeile bildet. */
#menu-knopf {
  display:none;
  background:none;
  border:none;
  border-radius:0;
  margin:0;
  cursor:pointer;
  font-family:inherit;
  line-height:inherit;
}
#menu-knopf #menu-knopf-icon { font-size:var(--text-l); }

/* Info-Bubble */

#info {
  position:absolute;
  padding:6px 10px;
  background:rgba(0,0,0,0.8);
  pointer-events:none;
  white-space:pre-wrap;
  overflow-wrap:break-word;
  max-width:260px;
  border-radius:var(--radius);
  border:1px solid var(--c-line-soft);
  display:none;
  box-sizing:border-box;
  font-size:var(--text-xs);
  line-height:12px;
}

/* Sidebar / Wallet */

#sidebar-toggle {
  position:fixed;
  top:calc(var(--menu-h) + 10px);
  left:10px;
  width:40px;
  height:40px;
  z-index:1500;
  border-radius:var(--radius);
  /* Orange wie jedes andere Bedienelement (Foerbs Ansage 27.07.2026) — er war
     als einziger Knopf grau abgesetzt. */
  background:var(--c-signalfarbe);
  color:var(--c-white);
  border:none;
  font-size:var(--text-zahl);
  cursor:pointer;
  display:flex;
  align-items:center;
  justify-content:center;
}
#sidebar-toggle:hover {
  box-shadow:0 0 12px var(--c-signal-glow);
}

/* Ohne diese Zeile bliebe der Knopf trotz hidden sichtbar: Das display:flex
   oben ist spezifischer als die Browser-Grundregel für [hidden]. Genau diese
   Falle steckt schon in .feld[hidden] — hier gemessen am 26.07.2026, als der
   Knopf im ausgeloggten Zustand einfach stehenblieb. */
#sidebar-toggle[hidden] {
  display:none;
}

#wallet-sidebar {
  position:fixed;
  top:var(--menu-h);
  left:0;
  height:calc(100vh - var(--menu-h));
  width:300px;
  background-color:var(--c-bg-sidebar);
  box-shadow:var(--shadow-sidebar);
  z-index:1400;
  transform:translateX(-100%);
  transition:transform 0.3s ease;
  display:flex;
  flex-direction:column;
}

#wallet-sidebar.open {
  transform:translateX(0);
}

#wallet-inner {
  padding:60px 16px 16px 16px;
  overflow-y:auto;
  height:100%;
  box-sizing:border-box;

  word-break:break-all;
}

/* Einstieg für Nicht-Angemeldete (26.07.2026, Foerbs Vorgabe).
   Steht seit heute NICHT mehr in der Seitenleiste, sondern fest auf der Karte:
   Wer zum ersten Mal herkommt, muss die beiden Knöpfe sofort sehen, ohne das ☰
   zu finden. Dieselbe Ecke wie das ☰, das im ausgeloggten Zustand verborgen ist
   — es gibt also keine Überschneidung.
   Die Breite ist begrenzt und der Block scrollt in sich, damit das Anmelde-
   bzw. Registrierformular (wird per JS hineingeschrieben) die Karte nicht
   überdeckt und auf dem Handy nicht aus dem Bild läuft. */
/* Wie beim ☰: display:flex ist spezifischer als die Grundregel fuer [hidden]. */
#xdr-not-logged[hidden] {
  display:none;
}

#xdr-not-logged {
  position:fixed;
  top:calc(var(--menu-h) + 10px);
  left:10px;
  z-index:1500;
  display:flex;
  flex-wrap:wrap;
  gap:var(--luecke-s);
  align-items:flex-start;
  max-width:min(340px, calc(100vw - 20px));
  max-height:calc(100vh - var(--menu-h) - 20px);
  overflow-y:auto;
  padding:10px;
  border-radius:var(--radius);
  background:var(--c-bg-sidebar);
  box-shadow:var(--shadow-sidebar);
}

/* Die beiden Bereiche werden erst per JS gefüllt; leer sollen sie keine
   Lücke in die Knopfreihe reißen. */
#xdr-login-area,
#xdr-register-area {
  flex-basis:100%;
  min-width:0;
}
#xdr-login-area:empty,
#xdr-register-area:empty {
  display:none;
}

/* Die Formularfelder kommen per innerHTML aus dem Skript und bringen ihre
   eigene Breite mit. In der Seitenleiste (300px) fiel das nicht auf; im neuen
   Kasten lief das längere Feld auf dem Handy über den Rand hinaus — gemessen
   bei 390px. */
#xdr-login-area input,
#xdr-register-area input {
  max-width:100%;
  box-sizing:border-box;
}
.tryx-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  width: 100%;
}

.tryx-header-arrow {
  font-size: 0.8em;
  opacity:var(--deckkraft-halb);
}

/* Dialog / XDR */

#xdr-dialog {
  position:fixed;
  inset:0;
  background:var(--c-bg-overlay);
  display:none;
  align-items:center;
  justify-content:center;
  z-index:1700;
}

#xdr-dialog-inner {
  background:var(--c-bg-dialog);
  padding:18px 18px 12px;
  max-width:360px;
  width:calc(100% - 40px);
  border-radius:var(--radius);
  border:1px solid var(--c-line);
  
  box-shadow:var(--shadow);
}

#xdr-dialog-message {
  margin-bottom:14px;
  white-space:pre-wrap;
}

#xdr-dialog-buttons {
  display:flex;
  justify-content:flex-end;
  gap:var(--luecke-s);
}

#xdr-dialog-buttons button {
  min-width:80px;
}

#xdr-logged p,
#xdr-not-logged p {
  margin:0 0 8px 0;
}

#xdr-tryx-list {
  padding:7px;
  border: 1px solid var(--c-line-tryx);
}

/* Tryx-Liste */

.tryx-entry {
  border-bottom:1px solid var(--c-line-tryx);
  padding:4px 0;
}

.tryx-header {
  width:100%;
  text-align:left;
  background:transparent;
  border:none;
  padding:7px;
  cursor:pointer;
  font-weight:600;
  word-break:break-all;
}

.tryx-header:hover {
  background:var(--c-bg-tryx-hover);
}

.tryx-details {
  margin-top:4px;
}

.tryx-details .tryx-info {
  margin-bottom:4px;
  word-break:break-all;
}

.tryx-entry.highlight {
  background:var(--c-bg-tryx-highlight);
  padding:7px;
}

.tryx-entry.highlight .tryx-header {
  background:var(--c-bg-tryx-highlight);
  padding:7px;
}
#xdr-current-id, .clustergilde-entry{
  font-size:var(--text-xs);
}
.clustergilde-entry button{
  margin-left:7px;
}
/* seitenleiste tabelle */
#xdr-tryx-limits table,
  #xdr-cluster-share table {
    width: 100%;
    border-collapse: collapse;
  }
  #xdr-tryx-limits table{
    margin-bottom:10px;
  }

  #xdr-tryx-limits th,
  #xdr-tryx-limits td,
  #xdr-cluster-share th,
  #xdr-cluster-share td {
    border: 1px solid var(--c-white);
    padding: 2px 4px;
    text-align: left;
    box-sizing: border-box;
  }

  #xdr-tryx-limits th:last-child,
  #xdr-tryx-limits td:last-child,
  #xdr-cluster-share th:last-child,
  #xdr-cluster-share td:last-child {
    width: 53px;
    text-align: right;
  }

  .xdr-table-title {
    text-align: left;
    margin-bottom: 2px;
    margin-top:9px;
  }

/* Claim-Popup */

#claim-popup {
  position:fixed;
  inset:0;
  background:var(--c-bg-overlay);
  display:none;
  align-items:center;
  justify-content:center;
  z-index:1600;
}

#claim-popup-inner {
  background:var(--c-bg-dialog);
  padding:16px;
  max-width:320px;
  border:1px solid var(--c-line);
  border-radius:var(--radius);
  
}

#claim-popup-inner button {
  margin-top:8px;
}
#credential {
  position:fixed;
  right:0px;
  bottom:0px;

  display:flex;
  gap:var(--luecke-m);
  z-index:2100;
  background:var(--c-bg-dialog);
  padding:0px 5px;
}

#credential a {
  text-decoration:none;
  font-size:var(--text-xs);
}

#credential a:hover {
  opacity:1;
  text-decoration:underline;
}

/* Keyframes — derzeit keine. @keyframes pulseGlow stand hier bis zum
   27.07.2026 und wurde von keiner Regel und keinem Skript benutzt; im
   verteilten CSS ist so etwas unsichtbar, in einer Datei fällt es auf. */

/* Responsive */

@media (max-width:600px) {
  /* AUFKLAPPMENUE (Foerbs Auftrag 26.07.2026). Sechs Punkte brachen hier auf
     zwei Zeilen um und fraßen die halbe Bildschirmhoehe.

     Das offene Menue LEGT SICH UEBER den Inhalt, statt die Leiste hoeher zu
     machen. Grund: --menu-h wird per JS aus der Leistenhoehe berechnet und
     steuert padding-top von main, die Position der Sidebar und deren Hoehe —
     eine waschsende Leiste wuerde bei jedem Aufklappen die ganze Seite
     verschieben. So bleibt die Leiste immer eine Zeile hoch. */
  #menu-knopf:not([hidden]) { display:block; }

  /* Lage und Aussehen der aufklappbaren Liste — gilt in BEIDEN Zustaenden.
     `:not([hidden])` haengt daran, dass JS den Knopf freigeschaltet hat: Ohne
     JS greift keine dieser Regeln und alles bleibt beim Alten. */
  #menu-knopf:not([hidden]) ~ #menu-liste {
    position:absolute;
    left:0;
    right:0;
    top:100%;
    flex-direction:column;
    align-items:stretch;
    background:rgba(8,8,10,0.98);
    border-bottom:1px solid rgba(255,136,0,0.25);
  }
  /* Auf und Zu ausdruecklich DISJUNKT ("true" gegen "false") statt Basis +
     Ueberschreibung. Beide Selektoren haetten dieselbe Spezifitaet — welcher
     gewinnt, haenge sonst allein an der Reihenfolge im Stylesheet, und ein
     spaeteres Verschieben kippte das Verhalten lautlos. */
  #menu-knopf:not([hidden])[aria-expanded="false"] ~ #menu-liste { display:none; }
  #menu-knopf:not([hidden])[aria-expanded="true"]  ~ #menu-liste { display:flex; }
  #menu-knopf:not([hidden]) ~ #menu-liste a {
    padding:13px 16px;
    text-align:left;
    border-top:1px solid rgba(255,136,0,0.12);
  }

  .seite {
    max-width:100%;
    margin:0 auto 80px;
    padding:18px 16px 22px;
    border-radius:0;
    border-left:none;
    border-right:none;
  }
  #sidebar-toggle {
    width:34px;
    height:34px;
    font-size:var(--text-h3);
  }
  #wallet-sidebar {
    width:100%;
    height:calc(100% - var(--menu-h));
  }
}


/* ===== start.php  (body.seite-start) ================================= */
/* ---------------------------------------------------------------------------
   STARTSEITE — seiteneigene Gestaltung (§3 des Design-Vertrags: eigene
   <style>-Blöcke sind die Norm). Wiederverwendbares steckt in header.php:
   .btn-matryx, .tryx-badge-*, .preis-bild, .meldung, --Tokens.

   Der EINE Breakpoint des Projekts (600px) gilt auch hier. Das Raster darunter
   arbeitet mit auto-fit statt fester Spaltenzahl — dann fällt es von selbst von
   drei auf zwei auf eine Spalte, ohne einen zweiten Breakpoint zu erfinden. */

/* Abstaende 28.07.2026 auf Foerbs Ansage: oben 30px mehr (32 -> 62),
   unten 100px mehr (8 -> 108). Das neue Logo traegt die Ringe und braucht
   mehr Luft um sich als das flache weisse davor.
   Der untere Rand faellt mit dem margin-top:44px des folgenden <section>
   zusammen (Margin Collapsing unter Geschwistern) — es gewinnt der groessere,
   der Abstand betraegt also 108px und nicht 152px. */
.logo { display:block; margin:62px auto 108px; max-width:400px; width:100%; }

body.seite-start .seite h1 { text-align:left; }
body.seite-start .seite h2 { text-align:center; }
/* Das Projekt setzt global word-break:break-word. Bei einer Überschrift bricht
   das mitten im Wort — auf 375 px stand "Währungsuniversu" / "m" (gemessen).
   Überschriften brechen deshalb hier an Silben oder gar nicht; für den Fließtext
   bleibt die globale Regel unangetastet. */
body.seite-start .seite h1,
body.seite-start .seite h2,
body.seite-start .karte h3 { word-break:normal; hyphens:auto; }

/* ---- Kopf: Video links, Anspruch rechts ---- */
.intro-wrap { display:flex; gap:var(--luecke-xl); }
.intro-left { flex:0 0 40%; }
.intro-right { flex:0 0 60%; display:flex; flex-direction:column; justify-content:center; }
.intro-video { width:100%; height:auto; display:block; border-radius:var(--radius); }
/* margin-top 28.07.2026 auf Foerbs Ansage ("über den Buttons 30px Platz"):
   Die Knopfreihe stand direkt unter dem Einleitungstext. */
.intro-knoepfe { display:flex; gap:var(--luecke-m); flex-wrap:wrap; align-items:center; margin-top:30px; }
/* Der zentrale Baustein .btn-matryx trägt margin:20px auto — als einzelnes
   Element auf einer Seite zentriert ihn das, in einem Flex-Container aber
   schiebt das auto-margin ihn in die Mitte und den Link daneben an den rechten
   Rand (gemessen: Knopf bei 726 px in einem 505 px breiten Kasten, der bei
   617 px beginnt). Hier soll die Reihe links stehen, deshalb der auto-Rand
   zurückgenommen — der Baustein selbst bleibt unberührt. */
.intro-knoepfe .btn-matryx { margin:0; }

/* ---- Zahlenband: was gerade wirklich los ist ---- */
.zahlen {
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(120px, 1fr));
  gap:var(--luecke-m);
  margin:26px 0 6px;
}
.zahl {
  background:var(--flaeche-hell);
  border:1px solid var(--c-line-tryx);
  border-radius:var(--radius);
  padding:12px 10px;
  text-align:center;
}
.zahl b {
  display:block;
  font-size:var(--text-zahl);
  line-height:1.1;
  color:var(--c-signalfarbe);
  text-shadow:0 0 12px var(--c-signal-glow);
}
.zahl span {
  display:block;
  margin-top:4px;
  font-size:var(--text-s);
  letter-spacing:1px;
  text-transform:uppercase;
  opacity:var(--deckkraft-halb);
}

/* ---- Das Raster für Karten (Schritte, Codearten, Wege) ---- */
.raster {
  display:grid;
  grid-template-columns:repeat(auto-fit, minmax(240px, 1fr));
  gap:var(--luecke-l);
  margin:18px 0;
}
.raster--zwei { grid-template-columns:repeat(auto-fit, minmax(280px, 1fr)); }
.karte h3 {
  margin:0 0 8px;
  font-size:calc(var(--text-xl) * var(--text-h-faktor));
  color:var(--c-signalfarbe);
}
.karte p { margin:0 0 10px; }
.karte p:last-child { margin-bottom:0; }
/* Der Link am Fuß einer Karte sitzt immer unten, damit die Karten einer Reihe
   gleich aussehen, auch wenn die Texte verschieden lang sind. */
.karte .karte-fuss { margin-top:auto; padding-top:10px; }

/* ÜBERSCHRIFT UND SCHRITTNUMMER AUF EINER MITTELLINIE (Foerbs Ansage
   28.07.2026: die drei h3 unter "In drei Schritten dabei" sollen vertikal
   mittig zum Zahlen-Badge stehen).

   Vorher lagen beide als inline-block auf derselben Schriftlinie. Solange
   Badge und Text ähnlich groß waren, fiel das nicht auf — seit die
   Überschriften über --text-h-faktor deutlich gewachsen sind, sitzt der kleine
   Badge sichtbar zu tief.

   Die h3 wird deshalb zum Flex-Kasten: align-items:center legt beide auf eine
   gemeinsame Mittellinie, unabhängig davon, wie weit die Größen künftig noch
   auseinandergehen. Der Abstand kommt weiter aus margin-right des Badge.
   Eng auf #so-gehts begrenzt — nur diese drei Karten haben eine Schrittnummer,
   alle anderen .karte h3 sollen normale Textüberschriften bleiben. */
#so-gehts .karte h3 {
  display:flex;
  align-items:center;
}

/* Die Schrittnummer — reine Auszeichnung, kein zweiter Button-Stil. */
.schritt-nr {
  display:inline-block;
  /* flex:none, seit die h3 darüber ein Flex-Kasten ist: Ohne das dürfte der
     Badge schrumpfen, sobald der Titel daneben eng wird. */
  flex:none;
  /* GROESSE AN DER UEBERSCHRIFT (Foerbs Ansage 28.07.2026: die Zahlen muessen
     mindestens so gross sein wie die h3). font-size:1em heisst: genau die
     Schriftgroesse der h3, in der der Badge steht — er wandert damit bei jeder
     kuenftigen Aenderung von --text-h-faktor automatisch mit, statt wie die
     festen 13px (--text-basis) zurueckzubleiben.
     Auch Innenabstand, Mindestbreite und der Abstand zum Titel stehen deshalb
     in em: Sonst waere die Zahl gross und ihr Kasten daumennagelgross. */
  font-size:1em;
  min-width:1.5em;
  /* INNENABSTAND OBEN GROESSER ALS UNTEN — und das ist kein Schoenheitsfehler,
     sondern der Kern der Sache (Foerbs Befund 28.07.2026: "die Zahlen kleben
     ja oben"). Pixelkrebs hat KEINE Unterlaengen (gemessen:
     actualBoundingBoxDescent = 0), die Zeilenbox reserviert den Platz darunter
     trotzdem. Bei gleichem Innenabstand standen deshalb 7,26 px Luft ueber der
     Ziffer und 14,27 px darunter — die Ziffer sass oben im Kasten.
     0,256 em oben gegen 0,04 em unten gleicht die 7,01 px Differenz aus: die
     Ziffer steht damit mittig IM Rahmen. */
  padding:0.256em 0.22em 0.04em;
  margin-right:0.35em;
  /* ZWEITE STELLSCHRAUBE: die Ziffer auf die Hoehe der Ueberschrift daneben.
     Der ungleiche Innenabstand oben stellt die Ziffer mittig in den Rahmen —
     macht den Kasten dabei aber hoeher als die Zeilenbox des Titels. Weil
     align-items:center beide Flex-Elemente auf ihre MITTE legt und die Tinte
     des Titels 3,5 px ueber dessen Mitte sitzt (auch hier: keine
     Unterlaengen), stuende die Ziffer sonst 3,5 px zu tief.
     position/top statt margin, damit die Verschiebung rein optisch bleibt und
     Flex-Layout und Kartenhoehe unberuehrt laesst. In em, damit sie bei jeder
     kuenftigen Groessenaenderung stimmt. */
  position:relative;
  top:-0.108em;
  color:var(--c-signalfarbe);
  border:1px solid var(--c-signalfarbe);
  border-radius:var(--radius);
  text-align:center;
}

/* ---- Die drei Codearten nebeneinander ---- */
.code-karte { align-items:center; text-align:center; }
.code-karte .preis-bild { margin:0 0 12px; }
.code-karte h3 { text-align:center; }

/* FARBVERLAUF HINTER DEN DREI CODES (Foerbs Auftrag 28.07.2026).
   Drei verschiedene, aber verwandte Verlaeufe: blau-gruen, gelb-rot, rot-blau.

   WARUM multiply UND NICHT EINFACH EIN HINTERGRUND: Die SVG bringt ihren
   eigenen deckend weissen Grund mit (`<rect fill="#ffffff">` in qr/svg.js,
   Zeile 253) — ein Verlauf dahinter waere schlicht unsichtbar. Diesen weissen
   Grund anzutasten verbietet sich: Dieselbe Funktion erzeugt die GEDRUCKTEN
   Codes, und die muessen weiss bleiben.
   `mix-blend-mode:multiply` loest das ohne Eingriff in die Codeerzeugung:
   Weiss mal Verlauf ergibt den Verlauf, Schwarz mal Verlauf bleibt schwarz.
   Faellt die Blend-Unterstuetzung aus, steht wieder der weisse Code da — der
   Rueckfall ist also der bisherige Zustand.

   SCANBARKEIT: Die Codes sind reines #000000 (HAUSFARBE in qr/svg.js). Die
   Staerke der Verlaeufe ist NICHT geschaetzt, sondern ausgemessen — vier
   Stufen wurden gerendert, als Bild durch den echten Leser
   TriCode.dekodiereBild() geschickt und verglichen (28.07.2026, gegen die
   Live-Seite). Alle vier blieben lesbar:

     Stufe          hellster Wert   Otsu-Schwelle
     sehr hell        233 - 243       119 - 122
     mittel           222 - 236       112 - 117
     kraeftig         205 - 224       103 - 109
     sehr kraeftig    189 - 211        94 - 101   <- steht unten

   Der dunkelste Wert bleibt in jeder Stufe 0, die Trennung ist also selbst
   bei der kraeftigsten Stufe sehr deutlich. Foerbs Ansage war "kraeftiger,
   die sind kaum sichtbar" — deshalb die unterste Zeile. Wer es zurueckdrehen
   will, findet in der Tabelle die naechstschwaechere Stufe.

   isolation:isolate haelt die Mischung im Kasten: Ohne das mischte die SVG
   mit allem, was hinter der Karte liegt (Kartenhintergrund, Matryx-Canvas). */
.code-karte .preis-bild { isolation:isolate; }
.code-karte .preis-bild svg { mix-blend-mode:multiply; }
#code-tryx  { background:linear-gradient(140deg, #8ccbf2 0%, #8ce6b8 100%); }
#code-preis { background:linear-gradient(140deg, #f9d271 0%, #f9a184 100%); }
#code-beleg { background:linear-gradient(140deg, #f99fb0 0%, #94b3f2 100%); }
.code-karte p { text-align:left; }
/* Was unter dem Dreieck steht: die Beschriftung, die IM Code steht. */
.code-marke {
  display:inline-block;
  margin-bottom:10px;
  padding:2px 8px;
  border:1px solid var(--c-line-tryx);
  border-radius:var(--radius);
  font-size:var(--text-m);
  opacity:var(--deckkraft-halb);
}

/* ---- Klassen-Übersicht ---- */
.klassen-liste { display:flex; flex-wrap:wrap; gap:var(--luecke-s); margin:12px 0 0; }

@media (max-width:600px) {
  .intro-wrap { flex-direction:column; }
  .intro-left, .intro-right { flex:0 0 100%; }
  body.seite-start .seite h1 { text-align:center; }
  .zahl b { font-size:var(--text-zahl); }
}


/* ===== scan.php  (body.seite-scan) =================================== */
/* QR-TRYX Scan-Seite */

/* ── Die Buehne ──────────────────────────────────────────────────────────
   RUHEZUSTAND IST EIN FLACHER STREIFEN (27.07.2026). Vorher stand hier
   dauerhaft ein leeres 360x360-Quadrat — das groesste Element der ganzen
   Seite, bevor ueberhaupt etwas passiert war. Der Knopf "kamera starten"
   rutschte dadurch auf einem 390x844-Schirm auf y≈700, also praktisch an den
   unteren Bildrand: Die Hauptaktion war das am schlechtesten sichtbare
   Element (gemessen 27.07.2026). Jetzt waechst die Buehne erst, wenn sie
   wirklich etwas zeigt — die Klasse .aktiv setzt qr/scan.js.

   QUADRATISCH bleibt sie im aktiven Zustand (Foerbs Befund 26.07.2026):
   Ausgewertet wird ohnehin nur der zentrale Quadrat-Ausschnitt des
   Kamerabildes (qr/kamera.js) — also muss der Nutzer auch ein Quadrat sehen,
   sonst haelt er den Code in einen Bereich, der gar nicht analysiert wird. */
.scan-buehne {
  position:relative;
  margin:14px auto;
  border:1px solid var(--c-line);
  border-radius:var(--radius);
  background:var(--flaeche-tief);
  width:100%;
  max-width:420px;
  height:150px;
  display:flex;
  align-items:center;
  justify-content:center;
  overflow:hidden;
}
.scan-buehne.aktiv {
  height:auto;
  aspect-ratio:1;
}
/* VIDEO (Live-Kamera): cover. Das Quadrat wird gefuellt und dabei genau das
   weggeschnitten, was die Auswertung auch wegschneidet — Sucher und Analyse
   zeigen dasselbe. */
.scan-buehne video {
  width:100%;
  height:100%;
  object-fit:cover;
  display:block;
}
/* [hidden] muss trotz display:block wirken. Ohne diese Regel gaebe es das
   Attribut nur noch auf dem Papier: Eine Autoren-Regel mit display schlaegt
   das eingebaute [hidden]{display:none} — dann stuende das leere Video vor dem
   Kamerastart als schwarzer Block in der Buehne. Dieselbe Falle ist im Projekt
   schon dreimal aufgetreten (Sidebar-Schalter, #xdr-not-logged, .feld), siehe
   xdr5_design.md §4 Regel 6. */
.scan-buehne video[hidden] { display:none; }

/* CANVAS (Standbild nach dem Erkennen): contain, NICHT cover. Beim Kamera-Weg
   ist das Bild ohnehin quadratisch, da macht es keinen Unterschied. Beim
   FOTO-Weg wird aber das Originalformat gezeichnet — mit cover schnitte ein
   Hochkantbild oben und unten weg, und genau dort kann der eingezeichnete
   Rahmen um den erkannten Code liegen. Der Rahmen ist der Beleg, dass wirklich
   das Dreieck gefunden wurde; er darf nie aus dem Bild fallen. */
.scan-buehne canvas {
  max-width:100%;
  max-height:100%;
  object-fit:contain;
  display:block;
}
/* DIESELBE [hidden]-REGEL FUER DEN CANVAS — und hier war sie ueberfaellig:
   Ein Canvas mit width="0" height="0" verschwindet NICHT von selbst. Gemessen
   am 27.07.2026 auf der Live-Seite belegte der leere Canvas 183 von 345 px
   Buehnenbreite und schob den Hinweistext nach rechts (Textkante x=200 statt
   53) — der Kasten sah dadurch verrutscht aus. qr/scan.js setzt jetzt
   leinwand.hidden, solange nichts gezeichnet ist. */
.scan-buehne canvas[hidden] { display:none; }

/* SUCHER ALS DREIECK, nicht als Quadrat (27.07.2026, Foerbs Ansage).
   Der Rahmen sagt ohne Worte, wohin der Code gehoert — und ein QR-TRYX ist
   ein Dreieck. Ein quadratischer Rahmen forderte zum Ausfuellen einer Form
   auf, die der Code gar nicht hat: Man haelt ihn dann kleiner, als noetig
   waere. Genau das ist der Fall, der an der Messstrecke kippte (Code auf
   30 % der Bildkante). Das Dreieck fordert dagegen genau die Haltung, die
   die Auswertung braucht.
   Als SVG statt CSS-Rahmen, weil ein UMRISS gebraucht wird: Eine
   clip-path-Flaeche waere gefuellt und verdeckte das Kamerabild. */
.scan-sucher {
  position:absolute;
  inset:0;
  width:100%;
  height:100%;
  opacity:0;
  transition:opacity 0.2s;
  pointer-events:none;
}
.scan-buehne.sucht .scan-sucher { opacity:var(--deckkraft-fast); }
.scan-sucher polygon {
  fill:none;
  stroke:var(--c-signalfarbe);
  stroke-width:2;
  stroke-linejoin:round;
  vector-effect:non-scaling-stroke;
}
.scan-buehne .scan-leer {
  padding:14px 16px;
  text-align:center;
  opacity:var(--deckkraft-dezent);
  font-size:var(--text-m);
}
/* "Kamera stoppen" liegt IN der Buehne statt als zweiter Knopf daneben
   (27.07.2026). Vorher standen "kamera starten" und "kamera stoppen" als zwei
   gleich grosse orange Knoepfe nebeneinander, der Stopp nur disabled — drei
   gleichrangige Angebote ohne erkennbaren Hauptweg. Jetzt gibt es genau einen
   Startknopf; der Stopp erscheint dort, wo das laufende Bild ist, und nur
   solange es laeuft. Kein neuer Baustein: das globale button, nur kleiner. */
.buehne-stop {
  position:absolute;
  top:8px;
  right:8px;
  z-index:2;
  margin:0;
  padding:var(--knopf-padding-klein);
  font-size:var(--text-s);
  /* Dunkler 1px-Ring: Der Knopf liegt ueber dem LAUFENDEN Kamerabild, also
     ueber einem Untergrund, den niemand vorhersagen kann. Zeigt die Kamera
     zufaellig etwas Oranges, verschwimmt seine Silhouette mit dem Bild
     (WCAG 1.4.11). Der Ring haelt die Kontur unabhaengig vom Bildinhalt. */
  box-shadow:0 0 0 1px rgba(0,0,0,0.75);
}
.scan-ablage {
  border:1px dashed var(--c-line);
  border-radius:var(--radius);
  padding:12px 14px;
  text-align:center;
  cursor:pointer;
  background:var(--flaeche-hell);
  transition:border-color 0.2s, background 0.2s;
}
.scan-ablage:hover,
.scan-ablage:focus,
.scan-ablage.aktiv {
  border-color:var(--c-signalfarbe);
  background:var(--c-signal-dim);
  outline:none;
}
/* Statusmeldung (.meldung) und Formularfeld (.feld) kommen aus header.php —
   sie werden auch auf qr_gen.php gebraucht und stehen deshalb zentral. */
.scan-warnung {
  padding-left:10px;
  margin:14px 0;
}
/* Der eine Satz, der sagt was passiert ist. Kein neuer Melde-Baustein —
   .meldung bleibt fuer Status und Fehler zustaendig; das hier ist die
   Ueberschrift des Ergebnisses und steht deshalb dort, wo das Ergebnis steht. */
.ergebnis-satz {
  font-size:var(--text-xl);
  line-height:1.4;
  margin:14px 0 4px;
}
/* KEIN outline:none hier. Dieses Element wird nach jedem Scan gezielt
   fokussiert (zeigeErgebnis in qr/scan.js) — ihm den Fokusring zu nehmen,
   traefe genau die Nutzer, die dem Fokus mit den Augen folgen. Stattdessen ein
   deutlicher eigener Ring in der Signalfarbe. */
.ergebnis-satz:focus {
  outline:2px solid var(--c-signalfarbe);
  outline-offset:3px;
  border-radius:var(--radius);
}
/* Der Ergebnis-Block fasst zusammen, was nach dem Lesen kommt: erst der Satz,
   dann die Daten, dann die Folgeaktion. Ein Rahmen, damit er als eine Einheit
   zu erkennen ist und nicht als vier lose Absaetze. */
#ergebnis {
  background:var(--flaeche-hell);
  padding:2px 14px 14px;
  margin:14px 0;
}
/* Aufklappbare Erklaerungen unter der Handlung, gebuendelt in EINER Zone.
   Vorher standen die beiden <details> als nackte Trennlinien untereinander und
   sahen aus wie Fussnoten. */
.scan-wissen {
  padding:4px 14px 10px;
  margin:22px 0 0;
}
.scan-wissen > h2 {
  font-size:calc(var(--text-xl) * var(--text-h-faktor));
  color:var(--c-white);
  border-bottom:none;
  text-shadow:none;
  /* 28.07.2026 von festen 10px auf em umgestellt, damit diese Ueberschrift die
     Auflockerung mitmacht statt als einzige eng zu bleiben. Bleibt bewusst
     knapper als eine normale h2: Sie leitet keinen neuen Abschnitt ein,
     sondern beschriftet die Klappkasten-Zone darunter. */
  margin-top:1.2em;
  opacity:var(--deckkraft-halb);
}
.scan-info + .scan-info { border-top:1px solid var(--c-line-tryx); }
.scan-info > summary {
  cursor:pointer;
  padding:8px 0;
  opacity:var(--deckkraft-fast);
}
.scan-info > summary:hover { opacity:1; }
/* Anschauungs-Code */
.qr-schau {
  display:flex;
  gap:var(--luecke-l);
  align-items:flex-start;
  flex-wrap:wrap;
  margin:12px 0 6px;
}
.qr-schau-bild {
  /* 190px waren zu klein: Der Schriftzug "xdr5.de" in der untersten Zeile war
     kaum noch zu erkennen (Foerbs Befund 25.07.2026). 300px zeigen den Code so,
     wie er gedruckt wirkt, ohne die Textspalte zu erdruecken.
     Farbe/Radius/Schatten stehen im gemeinsamen Baustein weiter oben. */
  flex:0 0 300px;
}
.qr-schau-text { flex:1 1 260px; min-width:0; }
.qr-schau-text p:first-child { margin-top:0; }
.qr-schau-fuss { font-size:var(--text-s); opacity:var(--deckkraft-halb); }
@media (max-width:600px) {
  .qr-schau-bild { flex-basis:min(240px, 70vw); margin:0 auto; }
}
/* Liste der eigenen TRYX beim Export */
.export-zeile {
  display:flex;
  gap:var(--luecke-m);
  align-items:center;
  flex-wrap:wrap;
  padding:6px 0;
  border-bottom:1px solid var(--c-line-tryx);
}
.export-zeile .export-id {
  flex:1 1 220px;
  word-break:break-all;
  font-size:var(--text-m);
}
.export-zeile button { margin:0; }
/* .preis-bild, .beleg-block, .beleg-daten, .beleg-zeit, .beleg-alt stehen
   ZENTRAL in header.php — sie werden auch von userlist.php gebraucht (§4
   Regel 5 des Design-Vertrags). */


/* ===== faq.php  (body.seite-faq) ===================================== */
/* Der Umschalter "Für Menschen / Für KI" ist seit 27.07.2026 der zentrale
   Baustein .umschalter aus header.php — dieselbe Komponente wie auf scan.php.
   Das frühere seiteneigene .faq-toggle samt faqSwitch() ist damit entfallen;
   die Optik lebt weiter, nur an einer Stelle für alle Seiten. */

/* TABELLEN DER FAQ (27.07.2026, Foerbs Befund).
   Sie hatten bis hierher GAR KEINE Gestaltung — nachgemessen: keine einzige
   Regel traf sie, es war reiner Browser-Standard. Folge: keine Trennlinien,
   kein Innenabstand, und die Spalten teilten sich nach Textmenge auf. In der
   Endpunkt-Tabelle bekam "Wozu" 501 px, waehrend die Spalte "Abschnitt" auf
   38 px zusammenfiel und ihre eigene Ueberschrift zu "Absc/hnitt" umbrach.
   Dazu schien die bunte MATRYX-Karte durch die Zeilen.

   Die Optik folgt bewusst der Tabelle auf userlist.php (feste Spalten,
   duenne Trennlinie, Innenabstand) — das ist die Tabellensprache des
   Projekts. EIGENE Spaltenbreiten hat jede der beiden Tabellen, weil sie
   verschiedene Inhalte tragen: Die eine haelt Codepfade und Kopfzeilen, die
   andere drei kurze Zahlenspalten. */
/* width:100% und table-layout:fixed stehen ZENTRAL in header.php (gemeinsam mit
   .user-table) — hier stünden sie doppelt. */
.faq-tabelle {
  border-collapse:collapse;
  margin:16px 0;
  font-size:var(--text-basis);
  /* Leichte Aufhellung, damit die Zeilen sich vom Hintergrund abheben —
     dasselbe Mittel wie bei .horizontalsumme. */
  background:var(--flaeche-hell);
  border-radius:var(--radius);
  overflow:hidden;
}
.faq-tabelle th,
.faq-tabelle td {
  border-bottom:1px solid var(--c-line-tryx);
  padding:7px 9px;
  text-align:left;
  vertical-align:top;
  /* KEIN overflow-wrap hier: Das Umbruchverhalten steht zentral in
     header.php (Silbentrennung fuer Woerter, harter Bruch nur fuer Pfade und
     IDs). Diese Zeile stand hier noch aus der Zeit davor und hat die zentrale
     Regel ueberschrieben, weil der Styleblock der Seite spaeter kommt —
     dadurch brachen die FAQ-Tabellen wieder mitten im Wort (Befund css-agent
     27.07.2026). */
}
.faq-tabelle th {
  color:var(--c-signalfarbe);
  font-weight:normal;
  border-bottom-color:var(--c-signal-dim);
}
.faq-tabelle tr:last-child th,
.faq-tabelle tr:last-child td { border-bottom:none; }

/* Spaltenbreiten NUR auf breiten Schirmen — unter 600px wird gestapelt
   (zentral in header.php). Die Kapselung ersetzt das frühere !important:
   Wo keine konkurrierende Regel entsteht, muss auch keine überstimmt werden. */
@media (min-width:601px){
  /* Endpunkte (KI-Ansicht): Pfad · Zweck · Anmeldung · Abschnitt.
     "Wozu" bekommt den Rest, die drei anderen feste Anteile — sonst frisst der
     laengste Fliesstext den Platz der kurzen Spalten. */
  .faq-tabelle--endpunkte th:nth-child(1),
  .faq-tabelle--endpunkte td:nth-child(1) { width:26%; }
  .faq-tabelle--endpunkte th:nth-child(3),
  .faq-tabelle--endpunkte td:nth-child(3) { width:20%; }
  .faq-tabelle--endpunkte th:nth-child(4),
  .faq-tabelle--endpunkte td:nth-child(4) { width:11%; }

  /* Klassen-Limits (Publikums-Ansicht): drei kurze Spalten, zwei davon Zahlen. */
  .faq-tabelle--limits th:nth-child(1),
  .faq-tabelle--limits td:nth-child(1) { width:28%; }
  .faq-tabelle--limits th:nth-child(2),
  .faq-tabelle--limits td:nth-child(2) { width:26%; }
}

/* Der Rahmen bleibt scrollbar — er greift nur nicht mehr, seit die Tabelle
   keine Mindestbreite mehr hat. Er ist die Rueckfalllinie, falls je ein Inhalt
   doch breiter wird als der Schirm; die SEITE darf nie waagerecht laufen. */
.faq-tabelle-rahmen { overflow-x:auto; }

/* KEIN min-width mehr auf schmalen Schirmen (Foerbs Ansage 27.07.2026:
   "die tabellen muessen in der mobilen ansicht einheitlich umbrechen").
   Hier stand bis dahin `min-width:520px` mit der Begruendung, die Spalte
   "Wozu" falle sonst auf 81 px und die Tabelle werde ellenlang. Das stimmt
   weiterhin — es ist der bewusst gezahlte Preis dafuer, dass alle drei
   Tabellen des Projekts (ID-Liste, Transaktionen, FAQ) sich gleich verhalten,
   statt dass eine gewischt und eine gebrochen wird.
   Grundverhalten und Schriftgroesse stehen jetzt zentral in header.php. */

/* Horizontale Klassen-Einträge */
.horizontalsumme {
  display:flex;
  align-items:flex-start;
  gap:var(--luecke-xl);
  margin:18px 0;
  padding:14px;
  background:var(--flaeche-hell);
  border-radius:var(--radius);
  border-left:var(--akzent-balken) solid var(--c-signal-dim);
}
.horizontalsumme.cls-core   { border-left-color:var(--c-core); }
.horizontalsumme.cls-node   { border-left-color:var(--c-node); }
.horizontalsumme.cls-gate   { border-left-color:var(--c-gate); }
.horizontalsumme.cls-hidden { border-left-color:var(--c-hidden); }
.horizontalsumme.cls-locked { border-left-color:var(--c-locked); }
.horizontalsumme.cls-anchor { border-left-color:var(--c-anchor); }
.horizontalsumme.cls-glitch { border-left-color:var(--c-glitch); }

.horiz_links {
  width:180px;
  flex:0 0 180px;
  display:flex;
  align-items:center;
  justify-content:center;
}
.horiz_links img {
  max-width:100%;
  height:auto;
}
.horiz_rechts {
  flex:1 1 auto;
}
.horiz_rechts h2,
.horiz_rechts h4 {
  text-align:left;
  margin-top:0;
  border-bottom:none;
}

/* KI API Docs */
.ki-endpoint {
  /* Rahmen, Radius und Akzentbalken kommen aus den Bausteinen weiter oben. */
  background:var(--flaeche-tief);
  padding:14px 16px;
  margin:16px 0;
}
.ki-endpoint .ep-method {
  display:inline-block;
  font-size:var(--text-xs);
  letter-spacing:1px;
  padding:2px 8px;
  border-radius:var(--radius-klein);
  background:var(--c-signalfarbe);
  color:#000;
  font-weight:600;
  margin-right:8px;
}
.ki-endpoint .ep-method.get {
  /* GET bleibt gruen (REST-Konvention) und folgt deshalb der Statusfarbe,
     nicht mehr der Klassenfarbe NODE. */
  background:var(--c-ok);
  color:var(--c-white-klar);
}
.ki-endpoint .ep-url {
  font-family:ui-monospace,monospace;
  font-size:var(--text-basis);
  color:#ddd;
}
.ki-endpoint h4 {
  margin:0 0 8px;
  color:var(--c-white-klar);
  font-size:calc(var(--text-l) * var(--text-h-faktor));
}
.ki-note {
  /* Akzentbalken aus dem Baustein weiter oben. */
  background:rgba(255,136,0,0.06);
  padding:10px 14px;
  margin:14px 0;
  border-radius:0 var(--radius-klein) var(--radius-klein) 0;
  font-size:var(--text-m);
  line-height:18px;
}

@media (max-width:600px) {
  .horizontalsumme {
    flex-direction:column;
  }
  .horiz_links {
    width:100%;
    flex:none;
  }
}


/* ===== userlist.php  (body.seite-userlist) =========================== */
/* width:100% und table-layout:fixed stehen ZENTRAL in header.php. */
.user-table{
  border-collapse:collapse;
  margin-top:20px;
  font-size:var(--text-l);
}
.user-table th,
.user-table td{
  border-bottom:1px solid var(--c-line-tryx);
  padding:6px 8px;
  text-align:left;
  vertical-align:top;
}
/* Spaltenbreiten gelten NUR auf breiten Schirmen. Unter 600px wird gestapelt
   (zentral in header.php) — dort gibt es keine Spalten, und eine feste Breite
   machte die Zelle schmaler als ihre Beschriftung. Die Kapselung in
   min-width:601px ist der saubere Weg dahin: Sie laesst gar keine
   konkurrierende Regel entstehen, statt sie mit !important zu ueberstimmen
   (Befund css-agent 27.07.2026). Es ist derselbe EINE Breakpoint, nur seine
   andere Seite. */
@media (min-width:601px){
  .user-table th:nth-child(1),
  .user-table td:nth-child(1){ width:20px; }
  .user-table th:nth-child(3),
  .user-table td:nth-child(3){ width:40px; }
  .user-table th:nth-child(4),
  .user-table td:nth-child(4){ width:40px; }
  .user-table th:nth-child(5),
  .user-table td:nth-child(5){ width:60px; }
}

.badge{
  display:inline-block;
  max-width:100%;
  padding:2px 6px;
  border-radius:var(--radius-klein);
  background:var(--flaeche-tief);
  font-size:var(--text-m);
  white-space:normal;
  word-break:break-all;
}
.cleanup-wrap{
  margin-top:20px;
  text-align:right;
}

/* ---- Transaktionen (27.07.2026) ----
   Dieselbe Tabelle wie oben, aber mit anderen Spalten. Auch hier gelten die
   Breiten NUR auf breiten Schirmen — unter 600px wird gestapelt. */
@media (min-width:601px){
  .user-table.trans-table th:nth-child(1),
  .user-table.trans-table td:nth-child(1){ width:130px; }
  .user-table.trans-table th:nth-child(2),
  .user-table.trans-table td:nth-child(2){ width:60px; }
  .user-table.trans-table th:nth-child(3),
  .user-table.trans-table td:nth-child(3){ width:auto; }
  .user-table.trans-table th:nth-child(4),
  .user-table.trans-table td:nth-child(4){ width:90px; }
  .user-table.trans-table th:nth-child(5),
  .user-table.trans-table td:nth-child(5){ width:60px; }
  .user-table.trans-table th:nth-child(6),
  .user-table.trans-table td:nth-child(6){ width:100px; }
}
.trans-table button{ margin:0; font-size:var(--text-m); padding:var(--knopf-padding-klein); }
/* Auf schmalen Schirmen greifen KEINE Spaltenbreiten mehr — dort wird
   gestapelt (zentral in header.php). Die Breiten oben gelten nur für breite
   Schirme. */
@media (max-width:600px){
  /* Die aufgeklappte Beleg-Zeile schließt direkt an ihren Block an, statt als
     zweiter Kasten mit Abstand darunter zu stehen. */
  .trans-beleg{ margin-top:-8px; }
  .trans-table button{ white-space:nowrap; }
}
/* Die eigene Zeile hervorheben — reine Anzeige, die Liste ist fuer jeden
   dieselbe. Nicht nur Farbe: der Balken traegt die Information auch dann, wenn
   Farben nicht unterscheidbar sind. */
.trans-du{
  font-size:var(--text-xs);
  letter-spacing:1px;
  text-transform:uppercase;
  color:var(--c-signalfarbe);
}
.trans-beleg[hidden]{ display:none; }
.trans-beleg > td{ background:var(--flaeche-hell); }
.trans-fuss{ font-size:var(--text-m); opacity:var(--deckkraft-halb); }
/* .beleg-block, .preis-bild, .beleg-daten und .beleg-hinweis stehen ZENTRAL in
   header.php — dieselben Bausteine benutzt scan.php (§4 Regel 5 des
   Design-Vertrags). */


/* ===== qr_gen.php  (body.seite-gen) ================================== */
/* QR-TRYX Generator (interne Seite).
   Statusmeldung (.meldung) und Formularfeld (.feld/.feld--quer) kommen aus
   header.php — sie werden auch auf scan.php gebraucht. */
.gen-schalter { display:flex; align-items:center; gap:var(--luecke-s); margin:10px 0; }
.gen-schalter input { width:auto; }
.gen-vorschau { margin:14px 0; max-width:320px; }
.gen-fortschritt {
  width:100%;
  max-width:340px;
  height:8px;
  border:1px solid var(--c-line);
  border-radius:var(--radius-klein);
  overflow:hidden;
  margin:10px 0;
  background:rgba(0,0,0,0.4);
}
.gen-fortschritt div {
  height:100%;
  width:0;
  background:var(--c-signalfarbe);
  transition:width 0.2s;
}


/* ===== matryx.php  (body.seite-matryx) =============================== */
body.seite-matryx { margin:0; height:100%; overflow:hidden; }
html:has(body.seite-matryx) { margin:0; height:100%; overflow:hidden; }

  /* KEIN Hintergrundverlauf auf dieser Seite (27.07.2026, Foerbs Ansage).
     Auf allen anderen Seiten liegt die MATRYX nur als Hintergrund hinter dem
     Inhalt und darf getoent werden. HIER IST SIE DER INHALT — man arbeitet auf
     ihr, klickt Felder an und liest Klassenfarben ab. Ein Verlauf darueber
     verfaelschte genau die Farben, auf die es ankommt. */
  body.seite-matryx #wrap::after { content:none; }


/* ===== impressum.php  (body.seite-impressum) ========================= */
body.seite-impressum .seite { max-width:1000px; }


/* ===== datenschutz.php  (body.seite-datenschutz) ===================== */
body.seite-datenschutz .seite { max-width:1000px; }
