/* =============================================================================
   scanapp.css – Scan-App für den Einlass (www/scanapp/), einzige CSS-Datei der App
   -----------------------------------------------------------------------------
   Aufbau: 1 Schrift · 2 Variablen · 3 Grundlagen · 4 Bausteine (Knopf, Meldung, Kreisel)
           5 Kameravorschau · 6 Ergebnis grün/rot · 7 Fehler · 8 Statusleiste und Offline-Hinweis
   Jede Ansicht (<main class="ansicht">) füllt den ganzen Bildschirm. Sichtbar ist immer
   genau eine, die anderen haben das Attribut hidden (scanapp.js, zeige()).
   Farben wie im Admin-Bereich (backend.css), Grün und Rot kräftiger, damit man das
   Ergebnis auch aus dem Augenwinkel und im Dunkeln sofort erkennt.
   ============================================================================= */


/* 1 Schrift (selbst gehostet, nicht von Google laden: DSGVO) ------------------ */

@font-face { font-family: 'Inter'; font-style: normal; font-weight: 400; font-display: swap;
             src: url('../fonts/inter-v20-latin_latin-ext-regular.woff2') format('woff2'); }
@font-face { font-family: 'Inter'; font-style: normal; font-weight: 600; font-display: swap;
             src: url('../fonts/inter-v20-latin_latin-ext-600.woff2') format('woff2'); }
@font-face { font-family: 'DM Mono'; font-style: normal; font-weight: 500; font-display: swap;
             src: url('../fonts/dm-mono-v16-latin-500.woff2') format('woff2'); }


/* 2 Variablen ----------------------------------------------------------------- */

:root {
  --schrift:        Inter, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --schrift-betrag: "DM Mono", ui-monospace, monospace;      /* Ticket-Code, Bestellnummer */

  --farbe-grund:    #1d2430;   /* Hintergrund der neutralen Ansichten (wie die Kopfleiste im Admin) */
  --farbe-text:     #ffffff;
  --farbe-leise:    #aab4c2;
  --farbe-haupt:    #1f5fa8;   /* Knopf */
  --farbe-gruen:    #12843f;   /* gültig */
  --farbe-rot:      #c21f1f;   /* ungültig */
  --farbe-fehler:   #5a6475;   /* keine Verbindung: bewusst weder grün noch rot */
  --farbe-gelb:     #f5c400;   /* Testmodus: geprüft, aber nicht entwertet (dunkle Schrift darauf) */
  --farbe-info:     #1f5fa8;   /* Info: Gerät entwertet nicht, zeigt nur an (weiße Schrift darauf) */
  --farbe-info-hell:#6aa8ec;   /* dasselbe Blau als Schrift auf dunklem Grund */

  --abstand:        1.25rem;
  --radius:         12px;
}


/* 3 Grundlagen ---------------------------------------------------------------- */

*, *::before, *::after { box-sizing: border-box; }

html, body { height: 100%; margin: 0; }

/* Die App ist immer dunkel, egal ob das Handy auf hell oder dunkel steht.
   color-scheme: dark sagt dem Browser, dass die Seite schon dunkel ist: Er färbt nichts selbst um
   (automatischer Dunkelmodus mancher Browser) und zeichnet Haken und Felder in dunkler Optik.
   Dazu der dunkle Grund schon am <html>, damit auch beim Überziehen am Rand nichts Helles aufblitzt. */
:root { color-scheme: dark; background: var(--farbe-grund); }

body {
  font-family: var(--schrift);
  font-size: 1.1rem;
  line-height: 1.4;
  color: var(--farbe-text);
  background: var(--farbe-grund);
  -webkit-text-size-adjust: 100%;
  overscroll-behavior: none;              /* kein Nachladen durch Herunterziehen */
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
}

[hidden] { display: none !important; }

h1 { margin: 0; font-size: 1.8rem; font-weight: 600; line-height: 1.2; }
p  { margin: 0; }

/* Eine Ansicht füllt den Bildschirm. Der Rand lässt Platz für Notch und Home-Balken. */
.ansicht {
  position: fixed;
  inset: 0;
  padding: calc(var(--abstand) + env(safe-area-inset-top)) calc(var(--abstand) + env(safe-area-inset-right))
           calc(var(--abstand) + env(safe-area-inset-bottom)) calc(var(--abstand) + env(safe-area-inset-left));
  overflow: auto;
}

/* Inhalt mittig untereinander */
.ansicht-mitte {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--abstand);
  text-align: center;
}

.leise   { color: var(--farbe-leise); font-size: .95rem; }
.geraet  { font-size: 1.2rem; }
.geraet strong { font-size: 1.6rem; }                     /* Gerätename größer als „Gerät:“ davor */
/* Datum und Location unter dem Eventnamen: grau, kleiner, dicht unter der Überschrift */
.event-info { margin-top: calc(var(--abstand) * -.6); font-size: .95rem; color: var(--farbe-leise); }
.zaehler { font-variant-numeric: tabular-nums; color: var(--farbe-leise); }


/* 4 Bausteine ----------------------------------------------------------------- */

.knopf {
  padding: .7rem 1.4rem;
  font: inherit;
  font-weight: 600;
  color: #fff;
  background: transparent;
  border: 2px solid rgba(255, 255, 255, .7);
  border-radius: var(--radius);
  cursor: pointer;
  touch-action: manipulation;             /* kein Doppeltipp-Zoom */
}
.knopf:active { transform: scale(.97); }
.knopf-gross {
  width: 100%;
  max-width: 24rem;
  padding: 1.1rem 1.4rem;
  font-size: 1.3rem;
  background: var(--farbe-haupt);
  border-color: var(--farbe-haupt);
}
.knopf-klein { padding: .35rem .9rem; font-size: .95rem; }
/* Knopf am unteren Rand (rote Ansicht): immer an derselben Stelle, mit dem Daumen erreichbar */
.knopf-unten { margin-top: auto; }
/* Inhalt trotzdem mittig: Ein unsichtbarer Platzhalter oben nimmt genauso viel freien Platz wie der Abstand über dem
   Knopf. Ohne ihn stünde wenig Inhalt (z. B. „Unbekanntes Ticket“ ohne Ticketdaten) oben am Bildschirm. */
.ansicht-mitte:has(> .knopf-unten:not([hidden]))::before { content: ''; margin-top: auto; }
/* „Gerät entkoppeln“: rot umrandet, damit man ihn nicht aus Versehen für einen normalen Knopf hält */
.knopf-gefahr { color: #ffb3b3; border-color: #ff8a8a; }

/* Zahnrad oben rechts auf der Startansicht: führt zu den Einstellungen */
.zahnrad {
  position: absolute;
  top: calc(.5rem + env(safe-area-inset-top));
  right: calc(.5rem + env(safe-area-inset-right));
  width: 3rem;                            /* groß genug zum Antippen */
  height: 3rem;
  padding: .6rem;
  color: var(--farbe-leise);
  background: transparent;
  border: 0;
  cursor: pointer;
  touch-action: manipulation;
}
.zahnrad svg { display: block; width: 100%; height: 100%; }
/* Einstellungen vor dem Koppeln: nur Handy-/PDA-Modus, alles zum Gerät ausgeblendet (scanapp.js, zeigeEinstellungen()) */
#ansicht-einstellungen.ungekoppelt [data-nur-gekoppelt] { display: none !important; }

/* Haken mit Erklärung (Einstellungen: Testmodus) */
.schalter {
  display: flex;
  align-items: flex-start;
  gap: .9rem;
  width: 100%;
  max-width: 24rem;
  padding: 1rem;
  text-align: left;
  border: 2px solid rgba(255, 255, 255, .3);
  border-radius: var(--radius);
}
.schalter input { flex: none; width: 1.7rem; height: 1.7rem; margin: .1rem 0 0; accent-color: var(--farbe-gelb); }
.schalter span { font-size: .95rem; color: var(--farbe-leise); }
.schalter strong { font-size: 1.15rem; color: var(--farbe-text); }
.schalter:has(input:checked) { border-color: var(--farbe-gelb); }

/* Auswahl mit runden Optionsfeldern (Einstellungen: Handy- oder PDA-Modus) */
.auswahl {
  display: flex;
  flex-direction: column;
  gap: .6rem;
  width: 100%;
  max-width: 24rem;
  margin: 0;
  padding: 0;
  border: 0;
}
.auswahl legend { padding: 0 0 .4rem; font-size: .95rem; color: var(--farbe-leise); }

/* Hinweis auf der Startansicht, dass der Testmodus an ist */
/* Nur Text, kein Kasten: Er soll nicht wie ein Knopf aussehen */
.testhinweis {
  font-size: 1.45rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--farbe-gelb);
}

.meldung {
  width: 100%;
  max-width: 24rem;
  padding: .7rem 1rem;
  color: #fff;
  background: var(--farbe-rot);
  border-radius: var(--radius);
}

/* Drehender Ring: lädt bzw. prüft */
.kreisel {
  width: 3.5rem;
  height: 3.5rem;
  border: .4rem solid rgba(255, 255, 255, .25);
  border-top-color: #fff;
  border-radius: 50%;
  animation: drehen .8s linear infinite;
}
@keyframes drehen { to { transform: rotate(360deg); } }

/* Wartekreis über der ganzen Anzeige, solange eine Anfrage an den Server läuft (scanapp.js, rufe()):
   halbdunkle Fläche, in der Mitte der drehende Ring, ohne Text. Fängt auch Antippen ab, damit
   währenddessen nichts doppelt ausgelöst wird. */
.warten {
  position: fixed;
  inset: 0;
  z-index: 10;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, .55);
}
.warten .kreisel { width: 4.5rem; height: 4.5rem; }

/* Zwischenseite „Prüfe Ticket …“ (nur Text, den Kreis bringt bei Bedarf .warten) */
.pruefen-text { font-size: 1.3rem; font-weight: 600; }

/* Fußzeile der Start- und der Koppelansicht: „Ticketsystem von“ und darunter das Lightics-Logo, klein am unteren Rand.
   Sie liegt über dem unteren Rand der Ansicht (absolut), damit der Inhalt darüber mittig bleibt;
   beide Ansichten halten dafür unten Platz frei. */
#ansicht-start, #ansicht-koppeln { padding-bottom: calc(5.5rem + env(safe-area-inset-bottom)); }
.hersteller {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(1rem + env(safe-area-inset-bottom));
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: .35rem;
  font-size: .75rem;
  color: var(--farbe-leise);
}
.hersteller img { display: block; width: 9rem; height: auto; }

/* App-Symbol (icon.svg) groß oben auf der Start- und der Koppelansicht; bei niedrigen Bildschirmen etwas kleiner */
.app-symbol { display: block; width: min(7rem, 16vh); height: auto; flex: none; }


/* 5 Kameravorschau ------------------------------------------------------------ */

.ansicht-scan { padding: 0; overflow: hidden; background: #000; }
/* PDA-Modus: keine Kamera, also weder Vorschau noch Rahmen */
.ansicht-scan.ohne-kamera video,
.ansicht-scan.ohne-kamera .scan-rahmen { display: none; }

/* PDA-Modus: Mitte mit QR-Symbol und „Bitte scannen“. Feld und „Prüfen“ sind unsichtbar (der Scanner tippt, die App
   nimmt die Tasten auch ohne sichtbares Feld an); unten bleiben „Letztes Ergebnis“ und die Fußzeile. */
.pda-mitte { display: none; }
.ansicht-scan.ohne-kamera .pda-mitte {
  position: absolute;
  inset: 0 0 12rem;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1.5rem;
}
.pda-qr { width: min(45vw, 11rem); height: auto; overflow: visible; }
.pda-qr-punkte { fill: rgba(255, 255, 255, .85); }
.pda-text { margin: 0; font-size: 2rem; font-weight: 600; }
/* Feld im PDA-Modus unsichtbar, aber vorhanden: Es sammelt die Zeichen des Scanners (scanapp.js, keydown) */
.ansicht-scan.ohne-kamera .scan-eingabe {
  position: absolute; width: 1px; height: 1px; overflow: hidden; opacity: 0; pointer-events: none;
}

/* Das Kamerabild füllt den Bildschirm (Ränder werden abgeschnitten) */
#kamera {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Rahmen in der Mitte: hierhin gehört der QR-Code. Der breite Schatten dunkelt den Rest ab. */
.scan-rahmen {
  position: absolute;
  top: 50%;
  left: 50%;
  width: min(70vw, 55vh);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  border: 4px solid rgba(255, 255, 255, .9);
  border-radius: 24px;
  box-shadow: 0 0 0 100vmax rgba(0, 0, 0, .45);
}

/* Oben in der Vorschau: im Testmodus ein gelber Balken, darunter der Hinweistext */
.scan-kopf {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  display: flex;
  flex-direction: column;
  gap: .7rem;
  padding-top: env(safe-area-inset-top);
  text-align: center;
}
.scan-hinweis {
  padding: var(--abstand) var(--abstand) 0;
  font-weight: 600;
  text-shadow: 0 1px 4px #000;
}
/* Gelber Balken über die ganze Breite: Niemand soll den Testmodus beim Einlass übersehen */
.scan-test {
  padding: .6rem var(--abstand);
  font-weight: 600;
  letter-spacing: .03em;
  color: var(--farbe-grund);
  background: var(--farbe-gelb);
}
.scan-test:not([hidden]) + .scan-hinweis { padding-top: 0; }

.scan-fuss {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 1.4rem;      /* oben Eingabefeld, in der Mitte der runde Knopf „Letztes Ergebnis“, unten die Fußzeile;
                       zusammen ein großer Abstand zwischen „Prüfen“ und „Beenden“, damit man sich nicht vertippt */
  padding: .8rem calc(var(--abstand) + env(safe-area-inset-right)) calc(.8rem + env(safe-area-inset-bottom))
           calc(var(--abstand) + env(safe-area-inset-left));
  font-size: .95rem;
  font-variant-numeric: tabular-nums;
  background: rgba(0, 0, 0, .55);
}
.scan-fuss-zeile {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--abstand);
}
/* Mit Eingabefeld darüber: feine Trennlinie, damit Eingabe und „Beenden“ klar getrennt wirken */
.scan-eingabe:not([hidden]) ~ .scan-fuss-zeile {
  padding-top: .8rem;
  border-top: 1px solid rgba(255, 255, 255, .25);
}

/* Code von Hand eingeben: Feld und „Prüfen“ in einer Zeile.
   Schriftgröße mind. 16px, sonst zoomt das iPhone beim Antippen in die Seite. */
.scan-eingabe {
  display: flex;
  gap: .5rem;
}
.scan-eingabe[hidden] { display: none; }
.scan-eingabe input {
  flex: 1;
  min-width: 0;
  padding: .6rem .8rem;
  font: inherit;
  font-size: 1.1rem;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: #fff;
  background: rgba(255, 255, 255, .12);
  border: 1px solid rgba(255, 255, 255, .35);
  border-radius: .5rem;
}
.scan-eingabe input::placeholder {
  color: rgba(255, 255, 255, .6);
  letter-spacing: normal;
  text-transform: none;
}
.scan-eingabe input:focus {
  outline: none;
  border-color: #fff;
  background: rgba(255, 255, 255, .2);
}


/* 6 Ergebnis: Vollbild grün bzw. rot ------------------------------------------ */

.ansicht-gruen { background: var(--farbe-gruen); }
.ansicht-rot   { background: var(--farbe-rot); }
/* Entwertbares Extra (neubau_123) in Grün, Rot oder Gelb: dicker blauer Rahmen (scanapp.js, zeigeErgebnis()) */
.extra-rahmen { border: 28px solid var(--farbe-info); }   /* Rahmen statt Schatten: der Inhalt (Knopf unten) rückt nach innen */

/* Testmodus: Vollbild gelb mit dunkler Schrift. „Ticket wurde nicht entwertet“ groß, darunter
   die Ticketdaten. Unten ein rundes Symbol, wie der Scanner im echten Betrieb reagiert hätte:
   grün mit Haken oder rot mit X, darunter der Statustext („Gültig“, „Noch nicht freigegeben“ …).
   Der Block hat die Klasse „gut“, wenn das Ticket gültig wäre; danach richtet sich das Symbol. */
.ansicht-gelb  { color: var(--farbe-grund); background: var(--farbe-gelb); }
.ansicht-gelb h1 { font-size: 2.2rem; }
.gelb-text { font-size: 1.3rem; font-weight: 600; }
.gelb-echt { display: flex; flex-direction: column; align-items: center; gap: .5rem; font-weight: 600; }
.gelb-zeichen { width: 5rem; height: 5rem; }
.gelb-zeichen-gut circle      { fill: var(--farbe-gruen); }
.gelb-zeichen-schlecht circle { fill: var(--farbe-rot); }
.gelb-echt .gelb-zeichen-gut,
.gelb-echt.gut .gelb-zeichen-schlecht { display: none; }
.gelb-echt.gut .gelb-zeichen-gut      { display: block; }
.ansicht-gelb .knopf-gross { background: var(--farbe-grund); border-color: var(--farbe-grund); }

/* Kategorie nicht freigeschaltet (neubau_118): ebenfalls gelb mit dunkler Schrift, aber mit Sperrzeichen statt
   „nicht entwertet“, damit es nicht wie der Testmodus aussieht. Groß die Kategorie, darunter Tisch und Ticketdaten. */
.ansicht-kategorie { color: var(--farbe-grund); background: var(--farbe-gelb); }
.ansicht-kategorie h1 { font-size: 1.8rem; }
.kategorie-gross { font-size: 2.6rem; font-weight: 700; line-height: 1.1; }
.ansicht-kategorie .knopf-gross { background: var(--farbe-grund); border-color: var(--farbe-grund); }

/* Info (Gerät entwertet nicht, neubau_102): alles, was nur informiert, ist blau.
   Aufbau wie Gelb: Überschrift, Ticketdaten, unten das runde Symbol mit dem Stand vom Server. */
.ansicht-blau  { background: var(--farbe-info); }
.ansicht-blau h1 { font-size: 2.2rem; }
.ansicht-blau .zeichen { width: min(26vw, 13vh); }   /* i kleiner als bei Grün/Rot: unten steht noch das Ergebnis-Symbol */
#blau-zeichen-gueltig { width: min(45vw, 30vh); }   /* Haken eines gültigen Extras: so groß wie bei Grün, unten ist dann kein Symbol */
.blau-text { font-size: 1.3rem; font-weight: 600; }
/* Kleines Symbol hinter dem Zustand eines entwertbaren Extras: grüner Haken = noch nicht entwertet, rotes X = bereits entwertet */
.mini-zeichen { width: 1.6em; height: 1.6em; margin-left: .35em; vertical-align: -.45em; }
.mini-gut circle      { fill: var(--farbe-gruen); }
.mini-schlecht circle { fill: var(--farbe-rot); }
.ansicht-blau .knopf-gross { background: var(--farbe-grund); border-color: var(--farbe-grund); }
/* Hinweis „Nur Anzeige“ auf der Startansicht und blauer Balken in der Vorschau (wie beim Testmodus) */
.infohinweis { font-size: 1.45rem; font-weight: 700; line-height: 1.25; color: var(--farbe-info-hell); }
.scan-info   { color: #fff; background: var(--farbe-info); }
.scan-info:not([hidden]) + .scan-hinweis { padding-top: 0; }

/* „Check!“ (Ticket braucht besondere Aufmerksamkeit, neubau_74): dickes gelbes Banner oben im grünen Bildschirm,
   dazu ein gelber Knopf zum Bestätigen. Im gelben Testmodus-Bildschirm dunkel mit gelber Schrift,
   sonst wäre es dort nicht zu sehen. */
.ausweis-banner {
  width: 100%;
  max-width: 26rem;
  padding: .9rem 1rem;
  font-size: 2.1rem;
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--farbe-grund);
  background: var(--farbe-gelb);
  border-radius: var(--radius);
  box-shadow: 0 0 0 4px rgba(0, 0, 0, .25);
}
.ansicht-gelb .ausweis-banner { color: var(--farbe-gelb); background: var(--farbe-grund); }
.ansicht-gruen .knopf-ausweis { color: var(--farbe-grund); background: var(--farbe-gelb); border-color: var(--farbe-gelb); }

/* Letztes Ergebnis noch einmal: runder Knopf mittig unten im Scan-Bildschirm (zwischen Zähler und „Beenden“),
   oben ein Hinweis, dass es eine Wiederholung ist, und in Grün ein dunkler Knopf „Weiter scannen“ */
.knopf-letztes {
  flex: none;
  display: grid;
  place-items: center;
  width: 3.2rem;
  height: 3.2rem;
  padding: 0;
  border: 1px solid rgba(255, 255, 255, .35);
  border-radius: 50%;
  background: rgba(255, 255, 255, .12);
  color: var(--farbe-text);
  cursor: pointer;
}
.knopf-letztes svg { width: 1.9rem; height: 1.9rem; }
/* eigene Zeile mittig zwischen Eingabefeld und Fußzeile; die Höhe bleibt auch ohne Knopf (vor dem ersten Scan),
   damit das Feld nicht springt. Beim Koppeln (ohne Eingabefeld) fällt die Zeile weg. */
.scan-letztes { display: flex; justify-content: center; align-items: center; height: 3.2rem; }
.scan-eingabe[hidden] + .scan-letztes { display: none; }
.letztes-hinweis {
  position: fixed;
  top: calc(var(--leiste) + .5rem);
  left: 50%;
  z-index: 21;
  transform: translateX(-50%);
  margin: 0;
  padding: .35rem .9rem;
  border-radius: 999px;
  background: rgba(0, 0, 0, .75);
  color: #fff;
  font-size: .9rem;
  font-weight: 600;
  white-space: nowrap;
  pointer-events: none;
}
#knopf-weiter-gruen { background: var(--farbe-grund); border-color: var(--farbe-grund); }

/* Haken bzw. X */
.zeichen { width: min(45vw, 30vh); height: auto; flex: none; }

/* Tischnummer groß (nur grün): Das ist die Angabe, die der Helfer dem Gast sagt */
.tisch { font-size: 2.6rem; font-weight: 600; line-height: 1.1; font-variant-numeric: tabular-nums; }

/* Ticket-Code, Bestellnummer, Name, Ticketart als zweispaltige Liste */
.ticketdaten {
  display: grid;
  grid-template-columns: auto auto;
  gap: .3rem 1rem;
  margin: 0;
  text-align: left;
  font-size: 1.15rem;
}
.ticketdaten:empty { display: none; }
.ticketdaten dt { opacity: .8; }
.ticketdaten dd { margin: 0; font-weight: 600; overflow-wrap: anywhere; }
/* Zusatzleistung mit „INFO“ (neubau_103): nur der Text über beide Spalten, mittig. width 0 + min-width 100 %: ein langer Text
   macht den Block nicht breiter (sonst rutschen die Zeilen darüber nach links), er bricht innerhalb der Breite um */
.ticketdaten dd.ganz { grid-column: 1 / -1; width: 0; min-width: 100%; margin-top: .4rem; text-align: center; }
.ticketdaten .code { font-family: var(--schrift-betrag); font-weight: 500; letter-spacing: .04em; }
/* Gästeliste (neubau_125): weiße Schrift auf Blau an der Stelle des Namens; heller Rand, damit sie auch auf Blau (Info) auffällt */
.gaesteliste {
  display: inline-block;
  margin-right: .5rem;
  padding: .05rem .55rem;
  color: #ffffff;
  background: var(--farbe-haupt);
  border: 1px solid rgba(255, 255, 255, .75);
  border-radius: 6px;
  font-weight: 700;
}
/* Plakette „Extra“ ganz oben über dem Symbol, bei jedem Scan eines Extras (scanapp.js, zeigeErgebnis()) */
.extra-plakette {
  margin: 0;
  padding: .2rem 1.1rem;
  color: #ffffff;
  background: var(--farbe-haupt);
  border: 2px solid rgba(255, 255, 255, .85);
  border-radius: 8px;
  font-size: 1.5rem;
  font-weight: 700;
  letter-spacing: .04em;
}


/* 7 Fehler: keine Verbindung -------------------------------------------------- */

.ansicht-fehler { background: var(--farbe-fehler); }


/* 8 Einlass ohne Netz: Statusleiste und Offline-Hinweis (neubau_116) ---------- */

/* Leiste ganz oben, eine Zeile, schmal wie die Statuszeile des Handys: Server, Lokal, Master/Slave, offen, Uhr.
   Den Einlass-Modus zeigen die Details. Mit Leiste (body.mit-leiste) beginnen alle Ansichten darunter;
   den Rand für die Notch übernimmt dann die Leiste. */
:root { --leiste: calc(2rem + env(safe-area-inset-top)); }
.statusleiste {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: .9rem;
  height: var(--leiste);
  padding: env(safe-area-inset-top) calc(.7rem + env(safe-area-inset-right)) 0 calc(.7rem + env(safe-area-inset-left));
  background: #000;
  color: var(--farbe-text);
  font-size: .8rem;
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  user-select: none;
}
/* Schmale Handys: eine Zeile bleibt, nur enger und etwas kleiner */
@media (max-width: 560px) {
  .statusleiste { gap: .55rem; font-size: .72rem; padding-left: calc(.5rem + env(safe-area-inset-left));
                  padding-right: calc(.5rem + env(safe-area-inset-right)); }
  .status-punkt { gap: .25rem; }
}
/* Unter der Leiste etwas mehr Luft (doppelter Rand), damit nichts an ihr klebt */
body.mit-leiste .ansicht { top: var(--leiste); padding-top: calc(var(--abstand) * 2); }
body.mit-leiste .ansicht-scan { padding-top: 0; }
body.mit-leiste .scan-kopf { padding-top: .75rem; }
body.mit-leiste .zahnrad { top: 1rem; }

.status-punkt { display: inline-flex; align-items: center; gap: .35rem; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* Server und Lokal: Symbol eingefärbt nach Zustand (data-zustand, scanapp.js leistenPunkt()); getrennt bzw. Fehler grau mit rotem X */
.status-symbol { flex: none; width: 1.25rem; height: 1.25rem; color: #8a919c; }
.status-punkt[data-zustand="gut"] .status-symbol     { color: #2bd46a; }
.status-punkt[data-zustand="warnung"] .status-symbol { color: var(--farbe-gelb); }
.status-x { display: none; fill: none; stroke: #ff4d4d; stroke-width: 3.2; stroke-linecap: round; }
.status-punkt[data-zustand="schlecht"] .status-x { display: inline; }
.status-master {
  flex: none;
  padding: .05rem .45rem;
  border-radius: 4px;
  background: var(--farbe-gelb);
  color: var(--farbe-grund);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
}
.status-master[data-slave] { background: #4a5260; color: #c9ced6; }   /* Slave: dasselbe Schild in Grau */
/* Einlass-Modus als Wolke: blau = Offline erlaubt, gelb und durchgestrichen = nur online */
.status-modus { flex: none; width: 1.25rem; height: 1.25rem; color: var(--farbe-info-hell); }
.status-modus-strich { display: none; }
.status-modus[data-offline="0"] { color: var(--farbe-gelb); }
.status-modus[data-offline="0"] .status-modus-strich { display: inline; }
/* Hinweis „Einlass-Modus geändert“: groß unter der Leiste, über allen Ansichten */
.modus-hinweis {
  position: fixed;
  top: calc(var(--leiste) + .5rem);
  left: .75rem;
  right: .75rem;
  z-index: 21;
  padding: .9rem 1rem;
  border-radius: var(--radius);
  background: var(--farbe-gelb);
  color: var(--farbe-grund);
  font-size: 1.1rem;
  font-weight: 600;
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
}
.status-sperre {                                  /* wartet auf den Master (neubau_117) */
  flex: none;
  padding: .05rem .45rem;
  border-radius: 4px;
  background: var(--farbe-rot);
  font-weight: 700;
}
.status-offen { flex: none; font-weight: 700; color: var(--farbe-gelb); font-variant-numeric: tabular-nums; }
.status-uhr   { flex: none; font-variant-numeric: tabular-nums; }
/* Server und Lokal: der Text bleibt nur für Vorleseprogramme (unsichtbar) */
.status-text {
  position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Netz: Balken (hell = aktiv), grau mit rotem X ohne Verbindung. Schiebt Netz, Akku und Uhr nach rechts */
.status-netz { flex: none; width: 1.1rem; height: 1.1rem; margin-left: auto; color: #d7dbe2; }
.status-netz .balken { fill: currentColor; opacity: .25; }
.status-netz[data-balken="1"] .balken-1,
.status-netz[data-balken="2"] :is(.balken-1, .balken-2),
.status-netz[data-balken="3"] :is(.balken-1, .balken-2, .balken-3),
.status-netz[data-balken="4"] .balken { opacity: 1; }
.status-netz[data-balken="1"] { color: var(--farbe-gelb); }   /* sehr langsam: gelb */
.status-netz[data-balken="0"] { color: #8a919c; }                 /* kein Netz bzw. Server nicht erreichbar: grau mit rotem X */
.status-netz[data-balken="0"] .balken { opacity: .55; }
.status-netz[data-balken="0"] .status-x { display: inline; }
/* Akku: Umriss mit Füllung nach Ladestand und Prozent; rot ab 20 %, grün beim Laden */
.status-akku { flex: none; display: inline-flex; align-items: center; gap: .2rem; font-variant-numeric: tabular-nums; }
.status-akku svg { width: 1.45rem; height: 1.25rem; color: #d7dbe2; }
.akku-fuellung { fill: currentColor; }
.status-akku[data-zustand="leer"] svg { color: #ff4d4d; }
.status-akku[data-zustand="laedt"] svg { color: #2bd46a; }

/* Details: klappen unter der Leiste auf */
.status-details {
  position: fixed;
  top: var(--leiste);
  left: 0;
  right: 0;
  z-index: 19;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: .8rem;
  padding: var(--abstand) calc(var(--abstand) + env(safe-area-inset-right)) var(--abstand) calc(var(--abstand) + env(safe-area-inset-left));
  background: var(--farbe-grund);
  border-bottom: 1px solid var(--farbe-leise);
  box-shadow: 0 6px 18px rgba(0, 0, 0, .5);
}
.status-details dl { display: grid; grid-template-columns: auto auto; gap: .3rem 1rem; margin: 0; font-size: .95rem; }
.status-details dt { color: var(--farbe-leise); }
.status-details dd { margin: 0; font-weight: 600; font-variant-numeric: tabular-nums; overflow-wrap: anywhere; }
.status-details { max-height: calc(100vh - var(--leiste)); overflow-y: auto; }   /* mit dem Tastentest länger als der Bildschirm */
/* Tastentest im Infomenü: oben die empfangenen Tasten als Text, darunter die letzten Ereignisse (Schreibmaschinenschrift) */
.tastentest { align-self: stretch; display: flex; flex-direction: column; gap: .4rem; padding-top: .6rem; border-top: 1px solid var(--farbe-leise); }
.tastentest-titel { margin: 0; font-weight: 700; }
.tastentest-hinweis { margin: 0; font-size: .85rem; color: var(--farbe-leise); }
.tastentest-text { min-height: 2.6rem; padding: .45rem .6rem; border-radius: 6px; background: #000; font-family: ui-monospace, monospace;
                   font-size: 1rem; overflow-wrap: anywhere; white-space: pre-wrap; }
.tastentest-liste { margin: 0; padding-left: 1.4rem; font-family: ui-monospace, monospace; font-size: .75rem; color: var(--farbe-leise); }
.tastentest-liste li { overflow-wrap: anywhere; }
.tastentest .knopf { align-self: flex-start; }

/* „offline geprüft“ bzw. Hinweis unter dem Ergebnis: Kasten auf der farbigen Fläche */
/* „gescannt vor 2:05 Minuten“ unter „Bereits eingelassen um …“ (zählt jede Sekunde weiter) */
.vor-text { margin: 0; font-size: 1.2rem; font-weight: 600; font-variant-numeric: tabular-nums; }

.offline-hinweis {
  max-width: 26rem;
  padding: .45rem .9rem;
  border: 2px solid currentColor;
  border-radius: var(--radius);
  font-size: 1.05rem;
  font-weight: 600;
}


/* Wer „Bewegung reduzieren“ eingestellt hat, bekommt den Ring ohne Drehung */
@media (prefers-reduced-motion: reduce) {
  .kreisel { animation: none; }
}

/* Weißer Blitz: nächster Scan im PDA-Modus, während noch ein Ergebnis steht (scanapp.js, naechsterScan()).
   Kurz weiß, dann ausblenden; darunter erscheint schon das neue Ergebnis */
.blitz { position: fixed; inset: 0; z-index: 30; background: #fff; pointer-events: none; opacity: 0; }
.blitz.an { animation: blitz .1s ease-out; }
@keyframes blitz { 0% { opacity: 1; } 40% { opacity: 1; } 100% { opacity: 0; } }   /* kurz weiß, dann ausblenden */
