/* ==========================================================================
   Check-in — Kanal-Stylesheet (V1.0.813)
   ==========================================================================

   Bis V1.0.812 stand das hier als <style>-Block in `checkin-d1.html`: 186
   Zeilen, 75 Farbliterale, kein Bezug zum Tenant-Design. Der Check-in war der
   einzige Gastkanal, den das Design gar nicht erreichte.

   Der Inhalt ist beim Herausloesen ZEICHENGENAU uebernommen worden. Was sich
   aendert, steht ausschliesslich im Tenant-Layer weiter unten — so bleibt
   nachvollziehbar, was Umzug und was Aenderung ist.

   Schichten (wie bei der Bewertungsseite):
     1. :root                          Defaults — der bisherige Stand
     2. [data-checkin-theme="magnolia"] liest aus --app-* (app-tokens.css /
                                       /api/tenant/design.css)
     3. Komponenten                    unveraendert

   Rueckweg: `data-checkin-theme` in checkin-d1.html entfernen.
   Fahrplan: docs/architecture/theme-system-einschaetzung-2026-07-30.md
   ========================================================================== */

/* ── 1. Defaults + Komponenten (unveraendert uebernommen) ─────────────── */

/* Seitenweiter [hidden]-Schutz (V1.0.1243): Jede Autorenregel mit display
   (grid/flex) schlaegt sonst das hidden-Attribut — dreimal zugeschnappt
   (guest-guide-d1, review-public, SMS-Seite), zuletzt punktuell geflickt
   (.idcap-huelle, .land-zeile, .land-gruppe). Eine Regel je Fundstelle ist
   ein Wettlauf; diese eine gilt fuer alles, was das Stylesheet laedt. */
body [hidden]{display:none !important}

/* ── iOS rechnet an der Schriftgroesse mit, wenn man es laesst (V1.0.1305)
   `-webkit-text-size-adjust` steht auf iOS von Haus aus auf `auto`: Safari
   vergroessert Text in schmalen Spalten eigenmaechtig. Fuer DIESE Seite ist
   das kein Schoenheitsfehler: Die Lage der Codezeilen im Kamerarahmen ist in
   Millimetern gerechnet (zeichenzeilenBand), und eine eigenmaechtig
   vergroesserte Schrift verschoebe die Schablone gegen das, was gemessen
   wird. `100%` heisst: unsere Zahlen gelten, und nur sie.

   Was diese Zeile NICHT tut (nachgemessen 18.8.2026): den Fokus-Zoom
   verhindern. Sie kam in V1.0.1305 unter genau dieser Hoffnung hinein und
   hat nichts geaendert — die Ursache lag beim SEITENZOOM des Geraetes, den
   eine Seite weder sehen noch ueberschreiben darf und auch nicht soll.
   Siehe docs/gedaechtnis/zoom-lag-am-seitenzoom.md. Die Zeile bleibt wegen
   der Millimeter oben, nicht wegen des Zooms. */
html{-webkit-text-size-adjust:100%;text-size-adjust:100%}

:root{
  --idc-brand:#17294A; --idc-ink:#0f172a; --idc-muted:#64748b; --idc-line:#e2e8f0;
  /* V1.0.813: Flaechen als Tokens statt als Literale — vorher stand #fff an
     dreizehn Stellen und der Verlauf direkt in der body-Regel. */
  --idc-surface:#fff; --idc-brand-ink:#fff;
  --idc-page-bg:linear-gradient(180deg,#fafbfe 0%,#eef2f9 100%);
  --warn-border:#f59e0b; --warn-bg:#fffbeb; --warn-ink:#92400e;
  --ok:#15803d; --ok-bg:#dcfce7; --err:#b91c1c; --err-bg:#fef2f2; --err-line:#fecaca;
  --info-bg:#eff6ff; --info-line:#bfdbfe; --info-ink:#1e3a5f;
}
*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  background:var(--idc-page-bg);
  min-height:100vh;color:var(--idc-ink);
  font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,sans-serif;
  -webkit-font-smoothing:antialiased;
}
a{color:var(--idc-brand)} a:hover{color:#0f1c36}
.wrap{max-width:640px;margin:0 auto;padding:calc(16px + env(safe-area-inset-top)) 16px calc(120px + env(safe-area-inset-bottom))}
header.app{display:flex;align-items:center;justify-content:space-between;gap:10px;margin-bottom:14px}
.brandmark{display:flex;align-items:center;gap:9px;font-weight:600;color:var(--idc-brand);font-size:15px;min-width:0;background:none;border:0;padding:0;font-family:inherit;text-align:left;cursor:pointer}
.brandmark:focus-visible{outline:2px solid var(--idc-brand);outline-offset:4px;border-radius:10px}
.brandmark .dot{flex:none;width:28px;height:28px;border-radius:9px;background:var(--idc-brand);color:var(--idc-brand-ink);display:grid;place-items:center;font-size:14px}
.brandmark span.nm{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.hdr-tools{display:flex;align-items:center;gap:6px;flex:none}
/* Die Fassung oben rechts (V1.0.1308) — nur im Testbetrieb sichtbar.
   Schreibmaschinenschrift, damit sich zwei Nummern auf einen Blick
   vergleichen lassen; `uneinig` faellt auf, ohne dass man hinsehen muss. */
.fassung{font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;
         font-weight:700;letter-spacing:.02em;color:var(--idc-muted);
         white-space:nowrap;align-self:center}
.fassung.uneinig{color:#b91c1c;background:#fef2f2;border:1px solid #fca5a5;
                 border-radius:6px;padding:2px 6px}
.lang-btn{display:flex;align-items:center;gap:6px;background:var(--idc-surface);border:1px solid var(--idc-line);border-radius:99px;padding:8px 12px;min-height:44px;font-weight:600;font-size:14px;line-height:1;font-family:inherit;color:var(--idc-ink);cursor:pointer}
.lang-btn:hover{background:#f8fafc}
.reset-btn{background:none;border:none;color:var(--idc-muted);font-size:13px;text-decoration:underline;cursor:pointer;padding:8px;min-height:44px;font-family:inherit}
.progress{margin:0 0 20px}
.ptext{font-size:13px;color:var(--idc-muted);margin-bottom:6px;display:flex;justify-content:space-between;gap:8px}
.pbar{height:6px;border-radius:99px;background:var(--idc-line);overflow:hidden}
.pbar>div{height:100%;border-radius:99px;background:var(--idc-brand);transition:width .35s ease}
h1{font-size:24px;line-height:1.25;margin:0 0 8px;outline:none;text-wrap:pretty}
h1:focus-visible{outline:2px solid var(--idc-brand);outline-offset:4px;border-radius:6px}
/* Programmatischer Fokus zeichnet keinen Rahmen (V1.0.1160).
   Die Ueberschrift bekommt bei jedem Schritt den Fokus, damit Screenreader
   den neuen Abschnitt vorlesen. iOS Safari wertet auch diesen Fokus als
   ":focus-visible" — und der Gast sah einen Kasten um die Ueberschrift, bis
   er irgendwo hin tippte. Die Marke faellt beim ersten Tastendruck weg,
   Tastaturnutzer bekommen den Rahmen also sofort zurueck. */
h1.ohne-rahmen:focus-visible{outline:none}
.lead{color:var(--idc-muted);font-size:15px;line-height:1.55;margin:0 0 22px;text-wrap:pretty}
.card{background:var(--idc-surface);border:1px solid var(--idc-line);border-radius:18px;padding:20px;margin-bottom:16px;box-shadow:0 1px 2px rgba(15,23,42,.04)}
.kv{display:flex;justify-content:space-between;gap:12px;padding:10px 0;border-bottom:1px solid var(--idc-line);font-size:15px}
.kv:last-child{border-bottom:none}
.kv .k{color:var(--idc-muted)} .kv .v{font-weight:600;text-align:right}
.btn{display:flex;align-items:center;justify-content:center;gap:8px;width:100%;min-height:52px;padding:14px 20px;border-radius:14px;font-weight:600;font-size:16px;line-height:1.25;font-family:inherit;cursor:pointer;border:1px solid transparent;text-align:center}
.btn:focus-visible{outline:3px solid #93b4e8;outline-offset:2px}
.btn[disabled]{opacity:.45;cursor:not-allowed}
/* V1.0.790 — „reist nicht mit": ein Textlink, kein Knopf. Er soll nicht mit dem
   Weiter-Knopf konkurrieren; der uebliche Weg ist, die Person zu erfassen. */
.link-btn{background:none;border:0;padding:0;font:inherit;color:var(--idc-muted,#64748b);text-decoration:underline;cursor:pointer}
.link-btn:hover{color:inherit}
.btn-primary{background:var(--idc-brand);color:var(--idc-brand-ink)}
.btn-primary:hover:not([disabled]){background:#122240}
.btn-secondary{background:var(--idc-surface);color:var(--idc-ink);border-color:#cbd5e1}
.btn-secondary:hover:not([disabled]){background:#f8fafc}
.btn-ghost{background:none;border:none;color:var(--idc-muted);font-weight:500;font-size:14px;line-height:1.4;font-family:inherit;text-decoration:underline;min-height:44px;cursor:pointer}
.btn-ghost:hover{color:var(--idc-ink)}
.actions{display:flex;flex-direction:column;gap:10px;margin-top:20px}
.backrow{margin:0 0 10px}
.back-btn{display:inline-flex;align-items:center;gap:6px;background:none;border:none;color:var(--idc-brand);font-weight:600;font-size:15px;line-height:1;font-family:inherit;cursor:pointer;padding:10px 8px 10px 0;min-height:44px}
label{display:block;font-size:14px;font-weight:600;margin:0 0 6px}
.hint{font-size:13px;color:var(--idc-muted);margin:8px 0 0;line-height:1.5}
input,select,textarea{width:100%;min-height:52px;padding:12px 14px;border:1px solid #cbd5e1;border-radius:12px;font-size:16px;font-family:inherit;color:var(--idc-ink);background:var(--idc-surface)}
/* ── Die Untergrenze, unter die kein Eingabefeld darf (V1.0.1302) ─────────
   iOS-Safari zoomt beim Antippen in JEDES Eingabefeld hinein, dessen
   BERECHNETE Schriftgroesse unter 16px liegt. Es gibt keine Einstellung
   dagegen und keinen Weg zurueck: Der Gast steht danach in einem
   vergroesserten Ausschnitt und muss von Hand herauswischen.

   Die Zeile darueber setzt 16px — und das hat drei Versionen lang nicht
   gereicht, weil eine Grundregel jederzeit ueberschrieben werden kann:
   von einer spaeteren Regel im selben Blatt, von `/api/tenant/design.css`
   (wird NACH diesem Blatt geladen und gewinnt bei gleicher Spezifitaet),
   oder von einer ungueltigen `font`-Kurzform, die der Browser ganz
   verwirft und durch seine eigenen 11px ersetzt (siehe unten).

   Deshalb hier eine feste 16 mit `!important`. Das folgt derselben
   Ueberlegung wie `body [hidden]{display:none!important}` am Kopf dieser
   Datei — eine Zusage, die eine spaetere Regel aushebeln kann, ist keine
   Zusage.

   ── Warum PLATT 16px und nicht `max(16px, 1em)` (V1.0.1305) ─────────────

   Die Untergrenze mit `max()` war die elegantere Fassung und stand eine
   Version lang hier. Sie ist wieder weg, und der Grund ist Vorsicht statt
   Beweis: WebKit entscheidet den Fokus-Zoom in einem Codepfad, der den
   ANGEGEBENEN Wert liest, und ob er dabei eine Rechenfunktion auswertet,
   ist nicht zugesichert. Am 17.8.2026 zoomte das Geraet des Betreibers
   weiterhin, obwohl beide Fassungen — die alte 16px und die neue
   Untergrenze — rechnerisch 16 ergeben. Damit ist `max()` nicht als
   Ursache erwiesen, aber als Unbekannte ausgeschlossen. Eine Zahl, die
   dasteht, kann kein Browser missverstehen.

   Ein Feld GROESSER als 16px braucht diese Seite nirgends. Sollte das
   einmal noetig werden, gehoert dort eine eigene Regel MIT `!important`
   hin — nicht eine Aufweichung dieser.

   Gemessen wird das in `scripts/zoomProbe.mjs` (npm run pruefe:zoom) an
   der laufenden Seite in Chromium UND WebKit — nicht am Quelltext. Ein
   Textpruefer sieht genau die Bauart nicht, die ihn aushebelt. */
/* ── Die eine Zeile, die es wirklich war (V1.0.1311) ──────────────────────
   Gemessen auf dem Geraet des Betreibers, 18.8.2026:

       email 8px · html 16px · body 8px
       adjust 100% / body 50%
       KEINE Regel setzt body

   Die Wurzel traegt die 100 % aus Zeile 46 — `body` traegt 50 %, und KEINE
   Regel setzt eine Schriftgroesse. Es war also nie eine Frage der Kaskade:
   `-webkit-text-size-adjust` ist ein MULTIPLIKATOR, der NACH ihr auf die
   berechnete Groesse wirkt und vererbt wird. Die Felder erbten die 50 % von
   `body`, aus 16 wurden 8 — und iOS zoomte voellig richtig um 16/8 = 2.
   Deshalb half `!important` nicht: Es gewinnt die Kaskade und verliert
   gegen einen Faktor, der danach kommt.

   Woher die 50 % an `body` kommen, setzt nicht diese Seite (im ganzen
   Bestand steht `text-size-adjust` genau einmal, in Zeile 46, und dort auf
   `html`). Es ist eine Einstellung des Geraets — auf iOS traegt Safari
   seinen Seitenzoom genau so ein.

   Die Antwort darauf ist deshalb bewusst KLEIN: Nur die Formularelemente
   bekommen ihren eigenen Faktor zurueck. Der uebrige Text der Seite behaelt
   die Groesse, die der Gast eingestellt hat — wer die Seite kleiner haben
   will, bekommt sie kleiner. Nur die Felder bleiben bei den 16 px, unter
   denen iOS hineinzoomt, und genau dieses Hineinzoomen faellt damit weg.
   Kein Viewport-Riegel, kein `maximum-scale`, keine Zoomsperre. */
input,select,textarea{
  font-size:16px !important;
  -webkit-text-size-adjust:100%;
  text-size-adjust:100%;
}
textarea{min-height:104px;line-height:1.5;resize:vertical}
input:focus,select:focus,textarea:focus{outline:2px solid var(--idc-brand);border-color:var(--idc-brand)}
/* ── Datumsfelder: Safari legt ihnen eine EIGENE Breite unter (V1.0.1285) ──
   `width:100%` steht in der Regel darueber und gilt auch fuer sie. Trotzdem
   waren Geburts- und Ablaufdatum auf dem Bildschirmfoto vom 17.8.2026 die
   einzigen beiden Felder, die weiter rechts standen als alle anderen: iOS-
   Safari gibt dem Datumsfeld aus seinem Schattenbaum eine Mindestbreite, und
   die schlaegt die Prozentangabe.

   Die Folge war nicht nur ein schiefes Formular. Das zu breite Feld sprengt
   die Karte, damit wird die Seite breiter als das Fenster — und beim
   Antippen eines Feldes zoomt Safari auf den Ausschnitt, in dem das Feld
   ganz hineinpasst. Genau das war der „er zoomt ins Feld"-Befund; auf
   demselben Bild ist der Zurueck-Pfeil links abgeschnitten, die Seite stand
   also schon quer verschoben. Der Schriftgrad war nie die Ursache — 16 px
   stehen ueberall, und darunter zoomt Safari gar nicht erst.

   Chromium tut das alles nicht. Ein Playwright-Lauf haette den Fehler also
   nie gesehen; gefunden hat ihn das Bildschirmfoto. Deshalb haelt
   `tests/oberflaeche/datumsfeldRegel.test.js` fest, dass diese Regel bleibt.

   `appearance:none` nimmt die Eigenbreite, `min-width:0` das implizite
   Minimum. Den Wert selbst stellt Safari mittig dar — die zweite Zeile
   holt ihn nach links zu den anderen Feldern. Die dritte haelt das
   Kalendersymbol sichtbar, das `appearance:none` in Chromium sonst
   mitnimmt (der Gast darf auch am Rechner sitzen).

   Ein LEERES Datumsfeld zeigt weiterhin `tt.mm.jjjj` statt „bitte manuell
   eingeben": `placeholder` ist bei `type=date` in jedem Browser wirkungslos.
   Die Aussage tragen dort der rote Rahmen und der offen stehende Hilfetext
   darunter — siehe `.field.fehlt`. */
input[type=date]{appearance:none;-webkit-appearance:none;min-width:0;max-width:100%}
input[type=date]::-webkit-date-and-time-value{text-align:left;margin:0;min-width:0}
input[type=date]::-webkit-calendar-picker-indicator{opacity:.55}
.field{margin-bottom:16px} .field:last-child{margin-bottom:0}
/* Prüfmaske — zwei Zustände, zwei Farben (V1.0.1273).
   Gelb heisst „gelesen, aber ungedeckt", rot heisst „nichts gelesen". Bis
   V1.0.1272 gab es nur Gelb, und die Regel griff nur auf `input`: Bei den
   Auswahlfeldern (Anrede, Nationalität, Ausstellungsland, Ausweisart) blieb
   der Rahmen unauffällig, obwohl die Marke danebenstand. */
.field.unsure input,.field.unsure select,.field.unsure .land-trigger{border-color:var(--warn-border);background:var(--warn-bg)}
.field.fehlt input,.field.fehlt select,.field.fehlt .land-trigger{border-color:var(--err);background:var(--err-bg)}
.field.fehlt input::placeholder{color:var(--err);opacity:.9}
.error-msg{display:flex;gap:9px;align-items:flex-start;color:var(--err);background:var(--err-bg);border:1px solid var(--err-line);border-radius:12px;padding:12px 14px;font-size:14px;line-height:1.5;margin-top:10px}
.note-box{display:flex;gap:10px;background:var(--info-bg);border:1px solid var(--info-line);border-radius:14px;padding:14px 16px;font-size:14px;line-height:1.55;color:var(--info-ink);margin-top:16px}
.note-box.calm{background:#f8fafc;border-color:var(--idc-line);color:var(--idc-muted)}
.choice-cards{display:flex;flex-direction:column;gap:12px}
/* `font-size` ausdruecklich (V1.0.1302): Ohne sie nimmt ein <button> die
   Vorgabe der Maschine — 13,3px in Chromium, 11px in WebKit. Die beiden
   Textkinder (.cc-title, .cc-text) setzen ihre eigene Groesse, blanker Text
   in der Karte stand deshalb still auf 11px. */
.choice-card{display:flex;gap:14px;align-items:flex-start;text-align:left;width:100%;background:var(--idc-surface);border:2px solid var(--idc-line);border-radius:18px;padding:18px;cursor:pointer;font-family:inherit;font-size:16px;min-height:52px}
.choice-card:hover{border-color:#94a3b8}
.choice-card:focus-visible{outline:3px solid #93b4e8;outline-offset:2px}
.choice-card[aria-pressed="true"]{border-color:var(--idc-brand);background:#f4f8ff}
.cc-title{display:block;font-size:16px;font-weight:700;margin:0 0 4px;color:var(--idc-ink)}
.cc-text{display:block;font-size:14px;color:var(--idc-muted);line-height:1.5}
.cc-icon{flex:none;width:44px;height:44px;border-radius:12px;background:#f1f5f9;display:grid;place-items:center;color:var(--idc-brand)}
.choice-card[aria-pressed="true"] .cc-icon{background:#e0eaff}
/* ── Ausweiswahl: eine Zeile je Art (V1.0.1287) ───────────────────────────
   Vorher drei gleich breite Kacheln nebeneinander. Auf 320 px blieb je
   Kachel ein Drittel der Breite — „Aufenthaltstitel" passte dort nur mit
   Umbruch, und warum man das eine oder andere waehlt, stand nirgends.
   Jetzt volle Breite je Art, mit dem Satz darunter, der sagt, welche Seite
   danach gebraucht wird. */
.doc-zeilen{display:flex;flex-direction:column;gap:10px}
.doc-zeile{display:flex;align-items:center;gap:14px;width:100%;text-align:left;
           background:var(--idc-surface);border:2px solid var(--idc-line);border-radius:16px;
           padding:14px 16px;cursor:pointer;color:var(--idc-ink);font:inherit;min-height:52px}
.doc-zeile:focus-visible{outline:3px solid #93b4e8;outline-offset:2px}
.doc-zeile[aria-pressed="true"]{border-color:var(--idc-brand);background:#ebf0fb}
/* Die Kachel um das Systemzeichen. `line-height:1` verhindert, dass die
   Schriftmetrik des Emoji die Kachel unrund macht. */
.doc-marke{flex:none;display:grid;place-items:center;width:50px;height:50px;border-radius:15px;
           background:#efede7;font-size:26px;line-height:1}
/* Die Kachel bleibt beige, auch wenn die Zeile ausgewaehlt ist. Faerbte sie
   mit, waere die Auswahl zweimal gesagt (Rahmen UND Kachel) und das
   Bildzeichen verloere den ruhigen Grund, auf dem es erkennbar ist. */
.doc-text{display:flex;flex-direction:column;gap:3px;min-width:0}
.doc-name{font-weight:700;font-size:16px;line-height:1.25}
.doc-sinn{font-size:13px;line-height:1.45;color:var(--idc-muted)}

/* Die Vorzeile ueber der Ueberschrift — sie sagt, WORUM es gerade geht
   (die Person, die Ausweisart), damit die Ueberschrift selbst den Handgriff
   sagen kann statt den Gegenstand. */
.vorzeile{margin:0 0 4px;font-size:12.5px;font-weight:700;letter-spacing:.08em;
          text-transform:uppercase;color:#8a6a24}
/* ---- Stufen-Anzeige ---- */
.ladder{background:var(--idc-surface);border:1px solid var(--idc-line);border-radius:18px;padding:16px 18px 15px;margin-bottom:16px}
.ladder-track{display:grid;grid-template-columns:repeat(3,1fr);gap:0;position:relative;margin-bottom:12px}
.lstep{display:flex;flex-direction:column;align-items:center;gap:7px;position:relative;text-align:center}
.lstep::before{content:"";position:absolute;top:13px;left:-50%;width:100%;height:2px;background:var(--idc-line)}
.lstep:first-child::before{display:none}
.lstep.done::before,.lstep.now::before{background:var(--idc-brand)}
.lnode{width:28px;height:28px;border-radius:50%;display:grid;place-items:center;background:var(--idc-surface);border:2px solid var(--idc-line);color:#94a3b8;font-size:13px;font-weight:700;position:relative;z-index:1}
.lstep.done .lnode{background:var(--idc-brand);border-color:var(--idc-brand);color:var(--idc-brand-ink)}
.lstep.now .lnode{border-color:var(--idc-brand);color:var(--idc-brand);box-shadow:0 0 0 4px #e6edf9}
.lstep.now.last .lnode{border-color:var(--warn-border);color:var(--warn-ink);box-shadow:0 0 0 4px var(--warn-bg)}
.lstep .lname{font-size:12.5px;font-weight:600;color:#94a3b8;letter-spacing:.01em}
.lstep.done .lname,.lstep.now .lname{color:var(--idc-ink)}
.ldots{display:flex;gap:4px;justify-content:center;min-height:8px}
.ldot{width:7px;height:7px;border-radius:50%;background:var(--idc-line)}
.ldot.left{background:var(--idc-brand)}
.lstep.now.last .ldot.left{background:var(--warn-border)}
.lmsg{font-size:13.5px;line-height:1.5;color:var(--idc-muted);text-align:center;border-top:1px solid var(--idc-line);padding-top:11px}
.lmsg.urgent{color:var(--warn-ink);font-weight:600}
/* ---- Scan / Foto ---- */
.scanbox{background:#0b1220;border-radius:18px;aspect-ratio:3/4;position:relative;overflow:hidden;display:grid;place-items:center;margin-bottom:14px}
/* Live-Kamerabild liegt unter dem Rahmen; erst sichtbar, wenn der Stream läuft. */
.cam-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;opacity:0;transition:opacity .25s}
.cam-video.on{opacity:1}
.scanbox.photo{aspect-ratio:4/3}
.docframe{width:88%;aspect-ratio:1.45;border:3px solid rgba(255,255,255,.92);border-radius:12px;position:relative;transition:border-color .3s}
.docframe.ok{border-color:#22c55e} .docframe.err{border-color:#ef4444}
.mrz{position:absolute;left:6%;right:6%;bottom:8%;height:24%;border:2px dashed rgba(255,255,255,.55);border-radius:6px;display:grid;place-items:center;color:rgba(255,255,255,.65);font-size:11px;letter-spacing:.04em;text-align:center;padding:0 6px}
.docframe.ok .mrz{border-color:rgba(134,239,172,.8);color:rgba(187,247,208,.9)}
.corner{position:absolute;width:22px;height:22px;border:3px solid rgba(255,255,255,.9)}
.corner.tl{top:-3px;left:-3px;border-right:0;border-bottom:0;border-radius:10px 0 0 0}
.corner.tr{top:-3px;right:-3px;border-left:0;border-bottom:0;border-radius:0 10px 0 0}
.corner.bl{bottom:-3px;left:-3px;border-right:0;border-top:0;border-radius:0 0 0 10px}
.corner.br{bottom:-3px;right:-3px;border-left:0;border-top:0;border-radius:0 0 10px 0}
.scanline{position:absolute;left:4%;right:4%;height:3px;background:linear-gradient(90deg,transparent,#7dd3fc,transparent);border-radius:99px;top:8%;opacity:0}
.scanning .scanline{opacity:1;animation:scanmove 1.6s ease-in-out infinite}
@keyframes scanmove{0%{top:8%}50%{top:88%}100%{top:8%}}
@media (prefers-reduced-motion:reduce){.scanning .scanline{animation:none;top:50%}.pbar>div{transition:none}.sheet{transition:none}}
.scan-status{display:flex;align-items:center;gap:8px;justify-content:center;font-size:15px;font-weight:600;min-height:28px;margin-bottom:12px;text-align:center;color:var(--idc-muted)}
.scan-status.ok{color:var(--ok)} .scan-status.err{color:var(--err)}
.tiplist{display:flex;flex-direction:column;gap:8px;margin:0;padding:0;list-style:none}
.tiplist li{display:flex;gap:9px;font-size:14px;line-height:1.5;color:var(--idc-muted)}
.tiplist .b{flex:none;width:20px;height:20px;border-radius:50%;background:#f1f5f9;color:var(--idc-brand);display:grid;place-items:center;font-size:12px;font-weight:700;margin-top:1px}
/* ---- Abweichungen ---- */
.diff{border:1px solid var(--idc-line);border-radius:16px;padding:16px;margin-bottom:12px;background:var(--idc-surface)}
.diff-head{display:flex;align-items:center;gap:8px;margin-bottom:12px}
.diff-badge{font-size:11.5px;font-weight:700;letter-spacing:.03em;text-transform:uppercase;padding:3px 8px;border-radius:7px;background:#f1f5f9;color:var(--idc-muted)}
.diff.med .diff-badge{background:var(--warn-bg);color:var(--warn-ink)}
.diff.info .diff-badge{background:var(--info-bg);color:var(--info-ink)}
.diff-field{font-size:15px;font-weight:700}
.diff-vals{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-bottom:12px}
.dv{border:1px solid var(--idc-line);border-radius:12px;padding:10px 12px;background:#f8fafc}
.dv .src{font-size:11.5px;text-transform:uppercase;letter-spacing:.04em;color:var(--idc-muted);font-weight:700;margin-bottom:3px}
.dv .val{font-size:15.5px;font-weight:600;word-break:break-word}
.diff-opts{display:flex;flex-direction:column;gap:8px}
.opt{display:flex;align-items:center;gap:10px;width:100%;text-align:left;background:var(--idc-surface);border:2px solid var(--idc-line);border-radius:12px;padding:12px 14px;min-height:52px;font-weight:500;font-size:15px;line-height:1.35;font-family:inherit;color:var(--idc-ink);cursor:pointer}
.opt:hover{border-color:#94a3b8}
.opt:focus-visible{outline:3px solid #93b4e8;outline-offset:2px}
.opt[aria-pressed="true"]{border-color:var(--idc-brand);background:#f4f8ff;font-weight:600}
.opt .mark{flex:none;width:20px;height:20px;border-radius:50%;border:2px solid #cbd5e1;display:grid;place-items:center;font-size:11px;color:var(--idc-brand-ink)}
.opt[aria-pressed="true"] .mark{background:var(--idc-brand);border-color:var(--idc-brand)}
/* ---- Sonstiges ---- */
.addr-card{background:#f8fafc;border:1px solid var(--idc-line);border-radius:14px;padding:16px;font-size:15px;line-height:1.6}
.summary-section{margin-bottom:14px}
.summary-head{display:flex;justify-content:space-between;align-items:center;gap:10px;margin-bottom:8px}
.summary-head h2{font-weight:700;font-size:13px;line-height:1.3;font-family:inherit;margin:0;color:var(--idc-muted);text-transform:uppercase;letter-spacing:.05em}
.edit-btn{background:var(--idc-surface);border:1px solid #cbd5e1;border-radius:10px;padding:8px 14px;font-weight:600;font-size:14px;line-height:1;font-family:inherit;color:var(--idc-brand);cursor:pointer;min-height:44px;flex:none}
.edit-btn:hover{background:#f8fafc}
.person-sum{display:flex;gap:12px;align-items:flex-start}
.person-sum .st{flex:none;width:28px;height:28px;border-radius:50%;background:var(--ok-bg);color:var(--ok);display:grid;place-items:center;font-size:15px;font-weight:700}
.person-sum .pl{font-size:14.5px;line-height:1.6;color:var(--idc-muted)}
.person-sum .pl strong{color:var(--idc-ink);font-size:15.5px}
.success-circle{width:88px;height:88px;border-radius:50%;background:var(--ok-bg);border:3px solid #22c55e;color:var(--ok);display:grid;place-items:center;font-size:40px;margin:24px auto}
.demo-note{background:#f1f5f9;border:1px dashed #94a3b8;border-radius:12px;padding:12px 14px;font-size:13px;color:var(--idc-muted);line-height:1.5;margin-top:18px}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:0 12px}
.sr-only{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.offline-bar{position:sticky;top:0;z-index:30;display:none;align-items:flex-start;gap:9px;background:#fff7ed;border:1px solid #fed7aa;color:#9a3412;border-radius:14px;padding:12px 14px;font-size:14px;line-height:1.5;margin-bottom:14px}
body.is-offline .offline-bar{display:flex}
/* ---- Testbetrieb (V1.0.1255, umgebaut V1.0.1312) ----
   Hier stand ein rotes Band ueber der ganzen Seite, mit der Begruendung:
   „Wer die Seite oeffnet, soll in der ersten Sekunde wissen, dass er im
   Testbetrieb ist." Das galt — solange niemand die Seite ansehen wollte,
   WIE SIE DER GAST SIEHT. Genau das ist ihr Zweck geworden: Ein rotes Feld
   ueber allem verfaelscht jedes Urteil ueber Abstaende, Farben und den
   ersten Blick.

   Die Marke ist deshalb nicht weg, sondern leiser: In der Kopfzeile stehen
   die Fassungsnummer und der Ruecksetzknopf, beide NUR im Testbetrieb. Wer
   sie sieht, ist im Testbetrieb; wer sie nicht sieht, in einer echten
   Buchung. Dass das ein Rueckschritt an Deutlichkeit ist, steht hier
   ausdruecklich — es ist eine Abwaegung, kein Versehen. Der Schutz gegen
   echte Wirkung liegt ohnehin nicht am Band, sondern am Server (keine
   Meldung, keine Nachricht, nichts an Smoobu). */
/* ── Ruecksetzen im Testbetrieb (V1.0.1312) ───────────────────────────────
   Bis V1.0.1311 sass der Knopf in einem roten Hinweisband ueber der ganzen
   Seite. Das Band ist weg: Der Betreiber will die Gastseite so sehen, wie
   der Gast sie sieht, und ein rotes Feld ueber allem verfaelscht jedes
   Urteil ueber Abstaende, Farben und den ersten Blick.

   Der Knopf bleibt — aber still. Er steht in der Kopfzeile links neben der
   Fassungsnummer, in derselben kleinen Schrift, ohne Farbe und ohne Rahmen:
   erreichbar, aber kein Blickfang. Die Rueckfrage vor dem Loeschen bleibt,
   und sichtbar ist er nur im Testbetrieb — dieselbe EINE Bedingung wie die
   Fassung daneben, gesetzt in `zeigeFassung()`.

   Die Zielflaeche ist trotz der kleinen Schrift 44 px hoch: Ein Knopf, den
   man nicht trifft, ist keiner. */
.tb-reset{flex:none;background:none;border:0;padding:6px 4px;min-height:44px;
          font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:11px;
          font-weight:700;letter-spacing:.02em;color:var(--idc-muted);
          text-decoration:underline;cursor:pointer;white-space:nowrap}
.tb-reset:hover:not([disabled]){color:var(--idc-ink)}
.tb-reset[disabled]{opacity:.6;cursor:default;text-decoration:none}
.tb-reset:focus-visible{outline:2px solid var(--idc-brand);outline-offset:2px;border-radius:6px}
/* ---- Sprach-Sheet ---- */
.sheet-back{position:fixed;inset:0;background:rgba(15,23,42,.35);z-index:60;display:none}
.sheet-back.open{display:block}
.sheet{position:fixed;left:0;right:0;bottom:0;z-index:61;background:var(--idc-surface);border-radius:22px 22px 0 0;padding:18px 16px calc(20px + env(safe-area-inset-bottom));box-shadow:0 -8px 30px rgba(15,23,42,.18);transform:translateY(100%);transition:transform .25s ease;max-width:640px;margin:0 auto}
.sheet.open{transform:translateY(0)}
.sheet h2{font-size:17px;margin:0 0 4px}
.sheet p{font-size:13.5px;color:var(--idc-muted);margin:0 0 14px;line-height:1.5}
.lang-list{display:grid;gap:8px}
.lang-row{display:flex;align-items:center;justify-content:space-between;gap:10px;width:100%;min-height:52px;padding:13px 16px;border:2px solid var(--idc-line);border-radius:14px;background:var(--idc-surface);font-weight:500;font-size:16px;line-height:1.2;font-family:inherit;color:var(--idc-ink);cursor:pointer}
.lang-row[aria-pressed="true"]{border-color:var(--idc-brand);background:#f4f8ff;font-weight:700}
.lang-row .code{font-size:12.5px;font-weight:700;color:var(--idc-muted);letter-spacing:.06em}
/* Flagge: Emoji-Font erzwingen, damit sie nicht in der Textfarbe monochrom
   gerendert wird. Der Sprachname steht immer daneben – auf Plattformen ohne
   Flaggen-Glyphen (Windows) bleibt die Zeile also verständlich. */
.lang-flag{display:inline-flex;align-items:center;line-height:1}
/* Einwilligungen — ganze Zeile ist Klickfläche, Mindesthöhe 44px (Apple HIG).
   Der Link darin darf den Klick nicht ans Label weiterreichen, sonst würde
   das Öffnen der AGB zugleich das Häkchen setzen.

   Zwei Spalten statt Reihe (V1.0.1273): Das Kästchen sitzt in einer Spalte
   fester Breite, der Text daneben — damit fluchten alle vier Zeilen, auch
   wenn ein Text zweizeilig umbricht.

   Die drei Rücknahmen darunter sind kein Feinschliff, sondern der Grund für
   die schiefe Zeile: Die Formularregel weiter oben gibt JEDEM input
   `min-height:52px`, `padding:12px 14px` und einen Rahmen. Auf einem
   Kästchen ergibt das einen 52px hohen Klotz mit Innenabstand — `height:24px`
   verliert gegen `min-height`. Und `label{font-weight:600;margin:0 0 6px}`
   trifft die Zeile ebenfalls: fetter Fliesstext und ein zusätzlicher
   Abstand NUR nach unten, also ungleiche Lücken zwischen den vier Punkten. */
.consent-row{display:grid;grid-template-columns:24px 1fr;align-items:start;column-gap:14px;
             margin:0;padding:14px 2px;min-height:44px;cursor:pointer;
             font-size:15px;font-weight:400;border-bottom:1px solid var(--idc-line)}
.consent-row:last-of-type{border-bottom:0}
.consent-row input[type=checkbox]{width:24px;min-width:24px;height:24px;min-height:24px;
             padding:0;border:0;border-radius:0;accent-color:var(--idc-brand);cursor:pointer;
             /* auf die Mitte der ERSTEN Textzeile: (15px × 1.5 − 24px) ÷ 2 */
             margin:calc((1.5em - 24px) / 2) 0 0}
.consent-text{font-size:15px;font-weight:400;line-height:1.5}
.consent-text a{color:var(--idc-brand);text-decoration:underline;text-underline-offset:2px}
/* Beanstandetes Feld: roter Rahmen, damit die Meldung darunter zuordenbar ist. */
.field.invalid input,.field.invalid select,.field.invalid textarea{border-color:var(--err);box-shadow:0 0 0 3px rgba(239,68,68,.14)}
.lang-row .lang-flag{margin-right:2px}
.lang-row span:nth-child(2){flex:1}
/* ---- Mockup-Navigation ---- */
.dm-label{font-size:10.5px;text-transform:uppercase;letter-spacing:.06em;color:#94a3b8;padding:8px 8px 5px;font-weight:700}
@media(min-width:700px){.wrap{padding-top:40px}h1{font-size:28px}}

/* ── 2. Tenant-Layer: liest aus der Plattform-Ebene (V1.0.813) ─────────────
   Nur die markentragenden Toene. Die neutralen Grauabstufungen (#94a3b8,
   #cbd5e1, #f1f5f9 …) und die Info-Blautoene bleiben, wo sie sind — sie
   passen zu jeder Palette und ihr Umzug waere Aufwand ohne Wirkung.
   `--idc-brand` ist bereits heute #17294A und damit identisch mit
   --app-c-primary: Kopf, Fortschritt und Buttons aendern sich nicht.
   ────────────────────────────────────────────────────────────────────────── */
[data-checkin-theme="magnolia"] {
  --idc-brand: var(--app-c-primary);
  --idc-ink: var(--app-c-text);
  --idc-muted: var(--app-c-muted);
  --idc-line: var(--app-c-border);

  --ok: var(--app-c-success);
  --ok-bg: var(--app-c-success-bg);
  --err: var(--app-c-danger);
  --err-bg: var(--app-c-danger-bg);
  --warn-ink: var(--app-c-warning);
  --warn-bg: var(--app-c-warning-bg);

  /* Flaechen: der Verlauf weicht dem flachen Hintergrund der beiden anderen
     Kanaele. Drei Gastseiten, ein Untergrund. */
  --idc-page-bg: var(--app-c-bg);
  --idc-surface: var(--app-c-surface);
}

/* Vorschau des hochgeladenen Ausweisbilds (V1.0.1114) — der Rahmen war
   vorher immer schwarz und sah aus wie eine Kamera, die es nie gab. */
.scanbox.photo { position: relative; overflow: hidden; }
.photo-preview { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; background: var(--idc-scan-bg, #0b1220); }
/* ── Die Vorschau stellt ein quer liegendes Dokument auf (V1.0.1303) ──────
   Nur die ANZEIGE. Was zu Microsoft geht, sind unveraendert die
   Originalbytes der Datei — die Begruendung steht im Kopf von bildLage.js,
   und `fotoExifStrip.test.js` friert sie byteweise ein.

   Bei 90 und 270 Grad tauschen Breite und Hoehe die Rollen: Das Bild muss
   deshalb VOR der Drehung so gross sein wie der Kasten hoch ist, sonst
   ragt es hinaus. `width:100%;height:100%` gilt weiter dem ungedrehten
   Bild, die Drehung dreht das fertige Ergebnis um die eigene Mitte — bei
   `object-fit:contain` bleibt das Seitenverhaeltnis dabei erhalten und das
   Bild passt in jeden Fall in den Kasten hinein.

   Die Bewegung ist kurz und beruhigt: Wer den Drehknopf drueckt, soll
   sehen, DASS sich etwas dreht, statt ein neues Bild vorgesetzt zu
   bekommen. */
.photo-preview.gedreht-90  { transform: rotate(90deg);  }
.photo-preview.gedreht-180 { transform: rotate(180deg); }
.photo-preview.gedreht-270 { transform: rotate(270deg); }
@media (prefers-reduced-motion: no-preference) {
  .photo-preview { transition: transform .22s ease; }
}
/* Der Drehknopf sitzt in der Ecke des Kastens, ueber dem Bild und ueber der
   Rahmenschablone — er muss mit einem Daumen erreichbar sein (44 px). */
.photo-drehen {
  position: absolute; right: 10px; bottom: 10px; z-index: 3;
  width: 44px; height: 44px; border-radius: 50%;
  border: 1px solid rgba(255,255,255,.35); background: rgba(15,23,42,.55);
  color: #fff; font-size: 20px; line-height: 1; cursor: pointer;
  display: grid; place-items: center;
}
.photo-drehen:hover { background: rgba(15,23,42,.75); }
.photo-drehen:focus-visible { outline: 3px solid #93b4e8; outline-offset: 2px; }

/* Gezeichnete Flaggen (V1.0.1120) — Unicode-Flaggen stellt Windows nicht dar. */
.flagge { display:inline-flex; width:22px; height:16px; border-radius:2px; overflow:hidden;
          box-shadow:0 0 0 1px rgba(15,23,42,.12); flex:0 0 auto; vertical-align:middle; }
.flagge svg, .flagge img { width:100%; height:100%; display:block; object-fit:cover; }

/* Länderfeld mit Favoritenreihe und Tippfilter (V1.0.1120) */
.land-favoriten { display:flex; flex-wrap:wrap; gap:6px; margin:0 0 8px; }
.land-fav { display:inline-flex; align-items:center; gap:6px; padding:6px 10px; border-radius:999px;
            border:1px solid var(--idc-line); background:var(--idc-surface); font:inherit; font-size:13px;
            font-weight:700; color:var(--idc-ink); cursor:pointer; }
.land-fav:hover { border-color:var(--idc-brand); }
.land-fav.ist-gewaehlt { background:var(--idc-brand); border-color:var(--idc-brand); color:var(--idc-brand-ink); }
.land-suche { margin-bottom:6px; }

/* Zusatzwünsche im D1 (V1.0.1120) */
.zw-karte.ist-gewaehlt { border-color:var(--idc-brand); box-shadow:0 0 0 2px rgba(22,35,60,.12); }
.zw-karte.ist-gewaehlt .cc-icon { background:var(--idc-brand); color:var(--idc-brand-ink); }
.zw-preis { display:block; margin-top:4px; font-weight:800; color:var(--idc-brand); }

/* ── Länderfeld als Aufklappliste (V1.0.1122) ────────────────────────────
   Ein natives <select> kann kein Bild tragen — für Flaggen in jeder Zeile
   braucht es ein eigenes Element. Das Select bleibt darunter und trägt
   weiterhin den Wert; es ist nur nicht mehr zu sehen. */
.land-feld { position: relative; }
.land-feld select.land-nativ { display: none; }
.land-trigger {
  align-items: center; background: var(--idc-surface); border: 1px solid var(--idc-line);
  border-radius: 12px; color: var(--idc-ink); cursor: pointer; display: flex; gap: 10px;
  /* 16px verhindern, dass iOS beim Antippen hineinzoomt. */
  font: inherit; font-size: 16px; min-height: 52px; padding: 12px 14px; text-align: left; width: 100%;
}
.land-trigger:focus-visible { outline: 2px solid var(--idc-brand); outline-offset: 2px; }
.land-trigger-text { flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.land-pfeil { color: var(--idc-muted); flex: 0 0 auto; }
.land-panel {
  background: var(--idc-surface); border: 1px solid var(--idc-line); border-radius: 14px;
  box-shadow: 0 12px 32px rgba(15,23,42,.16); left: 0; padding: 10px; position: absolute;
  right: 0; top: calc(100% + 4px); z-index: 30;
}
.land-liste { max-height: min(52vh, 360px); overflow-y: auto; -webkit-overflow-scrolling: touch; }
.land-zeile {
  align-items: center; border-radius: 8px; cursor: pointer; display: grid; gap: 10px;
  /* 48px ist das Fingermass — darunter trifft man auf dem Handy daneben. */
  grid-template-columns: 22px 1fr; min-height: 48px; padding: 8px 10px;
}
.land-zeile.ist-aktiv { background: var(--idc-soft, #f4f8ff); }
.land-zeile[aria-selected="true"] { font-weight: 700; }
.land-leer { color: var(--idc-muted); padding: 12px 10px; margin: 0; }

@media (max-width: 699px) {
  /* Auf dem Handy von unten einfahren — wie das Sprachblatt. */
  .land-panel {
    border-radius: 22px 22px 0 0; bottom: 0; left: 0; max-height: 75vh; position: fixed;
    right: 0; top: auto; padding-bottom: calc(16px + env(safe-area-inset-bottom));
  }
}

/* Zusatzleistungen im Check-in (V1.0.1122) */
.zw-block { font-size: 15px; margin: 22px 0 8px; }
.so-liste { display: grid; gap: 10px; }
.so-karte { background: var(--idc-surface); border: 1px solid var(--idc-line); border-radius: 14px; padding: 14px; }
.so-kopf { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline; }
.so-stand { color: var(--idc-muted); font-size: 13px; }
.so-leer { color: var(--idc-muted); font-size: 13px; margin: 6px 0 0; }
.so-stufen { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }
.so-stufe { align-items: center; background: var(--idc-surface); border: 1px solid var(--idc-line);
            border-radius: 999px; color: var(--idc-ink); cursor: pointer; display: inline-flex;
            font: inherit; font-size: 14px; gap: 8px; min-height: 44px; padding: 8px 14px; }
.so-stufe.ist-gewaehlt { background: var(--idc-brand); border-color: var(--idc-brand); color: var(--idc-brand-ink); }
.so-stufe b { font-weight: 800; }

/* Gruppentitel in der Länderliste (V1.0.1127) */
.land-gruppe { color: var(--idc-muted); font-size: 12px; font-weight: 700; letter-spacing: .04em;
               margin: 8px 0 2px; padding: 0 10px; text-transform: uppercase; }
.land-gruppe:first-child { margin-top: 0; }

/* Weiter am Handy (V1.0.1136) */
.handy-karte { text-align: center; }
.qr-feld { display: inline-block; max-width: 240px; width: 100%; }
.qr-feld svg { width: 100%; height: auto; display: block; }
.qr-feld.qr-nur-text { font-family: ui-monospace, monospace; font-size: 13px; word-break: break-all;
                       max-width: none; text-align: left; }
.wartet-karte { align-items: center; display: flex; gap: 10px; }
.wartet-punkt { background: var(--idc-brand); border-radius: 50%; flex: 0 0 auto; height: 10px; width: 10px;
                animation: wartetPuls 1.4s ease-in-out infinite; }
@keyframes wartetPuls { 0%,100% { opacity: .25; transform: scale(.85); } 50% { opacity: 1; transform: scale(1); } }
@media (prefers-reduced-motion: reduce) { .wartet-punkt { animation: none; opacity: 1; } }


/* ── Ausweiserfassung (V1.0.1207) ────────────────────────────────────────
   Dieser Bereich gehoert dem CameraController: Videoelement, Rahmen und
   Statustext werden von ihm erzeugt und direkt beschrieben. render() fasst
   ihn nicht an.

   Sichtbarkeit haengt hier an KEINER Zustandsklasse. Genau das war dreimal
   der schwarze Kasten: `.cam-video` trug `opacity:0` und wurde nur in einer
   bestimmten Phase eingeblendet — wer eine Phase ergaenzte, machte das Bild
   in jeder anderen unsichtbar. */
.idcap-wirt{margin:16px 0}

/* ── Die Gestaltungskonstanten des Scanners (V1.0.1280) ───────────────────
   Alle Farben und die eine Übergangsdauer stehen HIER, nicht verteilt in
   zwanzig Regeln. Wer einen Zustand ergänzt, setzt eine Variable.

   Rot ist aus dem laufenden Scan verschwunden. Es war die Vorgabe für
   „suchen" — also für den Normalzustand der ersten Sekunden, in dem der
   Gast noch gar nichts falsch gemacht hat. Ein alarmroter Rahmen, weil die
   Kamera gerade erst offen ist, erzieht zur Unsicherheit; und Bewegung ist
   genau das, was das Sammelfenster wirklich zurücksetzt. Geblieben sind
   drei Töne, die einer Steigerung folgen: neutral (ich suche) → Führung
   (ich sehe etwas, korrigieren Sie noch dies) → Erfolg. */
/* ── Die Huelle behaelt ihr Format, auch unter dem Deckel (V1.0.1281) ─────
   Der Deckel begrenzte nur die HOEHE. Griff er (kurzes Fenster, Querlage),
   blieb die Breite bei 100 % — die Huelle wurde flach, und der Rahmen mit
   seinen 92 % Breite lief unten aus ihr heraus, wo `overflow:hidden` ihn
   abschnitt. Der Gast sah dann einen Rahmen, den es so nicht gab, und die
   Messung rechnete mit einem dritten Mass.

   Die Breite bekommt deshalb denselben Deckel, umgerechnet ueber dasselbe
   Format. Beide Werte stehen als EINE Zahl da: wer den Deckel aendert,
   aendert ihn zwangslaeufig fuer beide Achsen. Auf dem Handy greift keiner
   von beiden (358 x 477 bei 62vh = 523) — das ist eine Reparatur fuer die
   Raender, nicht fuer den Regelfall. */
/* ── `svh` statt `vh` (V1.0.1282) ─────────────────────────────────────────
   Auf iOS Safari meint `vh` die Hoehe OHNE Adressleiste — also mehr, als der
   Gast in dem Moment wirklich sieht. Der Kasten war damit fuer einen
   Bildausschnitt bemessen, den es nicht gab, und lief unten aus dem Bild.
   `svh` ist die kleine Fassung (Leiste sichtbar) und passt deshalb IMMER.
   Nicht `dvh`: das waechst beim Scrollen mit, und ein Kamerakasten, der
   waehrend des Scannens seine Hoehe aendert, verschiebt Rahmen und Messfeld
   mitten im Takt. Die `vh`-Zeile bleibt als Rueckfall fuer Browser ohne
   `svh` stehen — sie war jahrelang gut genug fuer alles ausser iOS.
   `scroll-margin` gehoert dazu: `holeInsBild()` scrollt den Kasten mit
   `block:'nearest'` heran, und ohne diesen Rand klebte er an der Kante. */
.idcap-huelle{position:relative;width:100%;aspect-ratio:3/4;
  --idcap-deckel:62vh;
  --idcap-deckel:62svh;
  scroll-margin-block:14px;
  max-height:var(--idcap-deckel);max-width:calc(var(--idcap-deckel) * 3 / 4);margin-inline:auto;
  border-radius:14px;overflow:hidden;background:#111;display:flex;align-items:center;justify-content:center;
  --idcap-neutral:rgba(255,255,255,.88);
  --idcap-fuehrung:#e0a13c;
  --idcap-erfolg:#4ade80;
  --idcap-band-neutral:rgba(255,255,255,.06);
  --idcap-band-fuehrung:rgba(224,161,60,.16);
  --idcap-band-erfolg:rgba(74,222,128,.15);
  --idcap-weich:.22s ease}
.idcap-video{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}

/* ── Der Rahmen ist die erwartete Dokumentkante ───────────────────────────
   Seitenverhältnis und Breite setzt der CameraController als Inline-Stil je
   Ausweisart (GHOST_PROFILE) — die Werte hier sind nur die Vorgabe, falls
   das Modul einmal ohne Profil montiert. Ein ZWEITES Rechteck um das
   Dokument gibt es bewusst nicht: Der Gast soll eine Kante ausrichten, nicht
   zwei gegeneinander abwägen. */
.idcap-rahmen{position:relative;width:92%;aspect-ratio:1.42;border:2.5px solid var(--idcap-ton,var(--idcap-neutral));border-radius:10px;box-shadow:0 0 0 100vmax rgba(0,0,0,.34);transition:border-color var(--idcap-weich)}
/* Vier Rahmenzustaende (V1.0.1280), gebunden an das, was der Automat WIRKLICH
   weiss:
     suchen    kein Dokument gefunden — neutral, keine Anklage.
     fuehrung  Geometrie hat ein Rechteck (geo.rechteck) oder alle drei Tore
               halten, nur noch nicht oft genug hintereinander (STABILISIERT).
               Bernstein heisst „fast, korrigieren Sie noch dies".
     sammelt   Fenster laeuft — gruen + Ring; bleibt auch im Fehltakt stehen,
               sonst flackert der Rahmen mehrmals je Sekunde.
     gruen     aufgenommen/erkennen (die Aufnahme blitzt zusaetzlich).
   Sammelt und gruen teilen die Farbe BEWUSST — unterscheidbar machen sie
   Ring und Text. */
.idcap-rahmen.idcap-suchen{--idcap-ton:var(--idcap-neutral);--idcap-band:var(--idcap-band-neutral)}
.idcap-rahmen.idcap-fuehrung{--idcap-ton:var(--idcap-fuehrung);--idcap-band:var(--idcap-band-fuehrung)}
.idcap-rahmen.idcap-sammelt{--idcap-ton:var(--idcap-erfolg);--idcap-band:var(--idcap-band-erfolg)}
.idcap-rahmen.idcap-gruen{--idcap-ton:var(--idcap-erfolg);--idcap-band:var(--idcap-band-erfolg)}
.idcap-status{position:absolute;left:10px;right:10px;bottom:10px;margin:0;padding:9px 12px;text-align:center;color:var(--idc-brand-ink);font-size:15px;font-weight:600;line-height:1.35;border-radius:12px;background:rgba(10,12,18,.6);text-shadow:0 1px 3px rgba(0,0,0,.8)}
.idcap-blitz .idcap-video{filter:brightness(2.2)}

/* Ecken (V1.0.1214) — sie tragen den Zustand ein zweites Mal, ueber die
   FORM: im Erfolgsfall wachsen die vier Winkel aus ihrer jeweiligen Ecke
   heraus. Zustand darf nie nur an der Farbe haengen (V1.0.1280); wer
   Bernstein und Gruen nicht unterscheidet, sieht trotzdem, dass etwas
   eingerastet ist — und liest es ausserdem im Statustext. */
.idcap-ecke{position:absolute;width:22px;height:22px;border:3px solid var(--idcap-ton,var(--idcap-neutral));pointer-events:none;transition:border-color var(--idcap-weich),transform var(--idcap-weich)}
.idcap-tl{top:-3px;left:-3px;border-right:0;border-bottom:0;border-radius:10px 0 0 0;transform-origin:top left}
.idcap-tr{top:-3px;right:-3px;border-left:0;border-bottom:0;border-radius:0 10px 0 0;transform-origin:top right}
.idcap-bl{bottom:-3px;left:-3px;border-right:0;border-top:0;border-radius:0 0 0 10px;transform-origin:bottom left}
.idcap-br{bottom:-3px;right:-3px;border-left:0;border-top:0;border-radius:0 0 10px 0;transform-origin:bottom right}
.idcap-rahmen.idcap-sammelt .idcap-ecke,.idcap-rahmen.idcap-gruen .idcap-ecke{transform:scale(1.3)}

/* ── Der Ghost: eine Positionierschablone, kein gemalter Ausweis ──────────
   Bis V1.0.1279 war der Rahmen ein nacktes Rechteck mit einem gestrichelten
   Kasten darin. Er sagte, WIE GROSS das Dokument sein soll, aber nicht, was
   der Scanner darin erwartet — und der Gast vom 11.8.2026 legte prompt die
   Datenseite in den Rahmen und die Codezeilen darunter.

   Der Ghost beantwortet die Frage raeumlich: links das Foto, rechts die
   Angaben, unten die Codezeilen. Er ist durchscheinend (Deckkraft .22), damit
   das ECHTE Dokument durch ihn hindurch sichtbar bleibt, und er tritt
   zurueck, sobald die Geometrie greift — seine Arbeit ist dann getan. */
/* Der Ghost endet, wo das Codezeilen-Band beginnt (V1.0.1282) — `bottom`
   setzt der Controller je Ausweisart, der Wert hier ist der Rueckfall fuer
   den Pass. Vorher lief er ueber die ganze Rahmenhoehe; das ging gut,
   solange das Band nur ein Fuenftel hoch war. Bei der Karte nimmt die Zone
   der Norm fast die halbe Rueckseite ein, und die drei Aufdruckbalken
   staenden mitten darin — zwei Schablonen uebereinander, die einander
   widersprechen. */
.idcap-ghost{position:absolute;top:0;left:0;right:0;bottom:26.93%;padding:6% 6% 0;display:flex;flex-direction:column;opacity:0;transform:scale(.985);pointer-events:none;transition:opacity .5s ease,transform .5s ease}
/* Eingeblendet wird er erst, wenn WIRKLICH ein Bild fliesst (das Modul setzt
   idcap-bild-laeuft nach dem ersten Frame) — nicht schon, waehrend der
   Browser noch nach der Kameraerlaubnis fragt. */
.idcap-bild-laeuft .idcap-ghost{opacity:.22;transform:none}
.idcap-rahmen.idcap-fuehrung .idcap-ghost{opacity:.13}
.idcap-rahmen.idcap-sammelt .idcap-ghost,.idcap-rahmen.idcap-gruen .idcap-ghost{opacity:0}
.idcap-ghost-oben{flex:1;display:flex;gap:5%;min-height:0}
/* Klein und links — ein Passfoto nimmt rund ein Fuenftel der Datenseite ein.
   Ein halbflaechiger Block waere als Schablone unbrauchbar. */
.idcap-portraet{flex:none;width:20%;height:64%;border-radius:3px;background:rgba(255,255,255,.16);position:relative;overflow:hidden}
.idcap-portraet::before{content:'';position:absolute;left:50%;top:14%;width:52%;aspect-ratio:1;transform:translateX(-50%);border-radius:999px;background:rgba(255,255,255,.55)}
.idcap-portraet::after{content:'';position:absolute;left:50%;bottom:0;width:78%;height:32%;transform:translateX(-50%);border-radius:14px 14px 0 0;background:rgba(255,255,255,.55)}
.idcap-felder{flex:1;min-width:0;display:flex;flex-direction:column;justify-content:center;gap:6%}
.idcap-feld{display:block;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:clamp(7px,2.1vw,11px);font-weight:500;letter-spacing:.04em;color:rgba(255,255,255,.92);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* Die Rueckseite einer Karte traegt keine Datenfelder, sondern Aufdruck.
   Sie als Beispieltext zu erfinden hiesse, eine Struktur zu behaupten, die
   dort nicht steht. */
.idcap-balken{height:3px;border-radius:2px;background:rgba(255,255,255,.42)}

/* ── Das Codezeilen-Band ──────────────────────────────────────────────────
   Bis V1.0.1279 stand hier eine freistehende GESTRICHELTE Box mitten im
   Rahmen — ein zweites Rechteck, das mit der Dokumentkante konkurrierte und
   aussah wie ein eigenes Zielfeld. Jetzt ist das Band der untere Streifen
   DERSELBEN Flaeche: buendig in den drei Rahmenkanten (left/right/bottom 0),
   nach oben abgesetzt durch eine einzige durchgezogene Linie. Lesbar wird
   die Zone ueber die FLAECHE (--idcap-band), nicht ueber eine Umrandung.

   Es bleibt stehen, wenn der Ghost verblasst: Es traegt den Zustand des
   dritten Tors weiter, wenn die Schablone ihre Arbeit schon getan hat. */
/* ── Die Lage kommt aus Millimetern, nicht aus dem Textfluss (V1.0.1282) ──
   Bis V1.0.1281 war das Band so hoch, wie sein Inhalt eben wurde: zwei bis
   drei Zeilen plus Beschriftung. Gemessen hiess das 17,9 % der Rahmenhoehe
   bei 390 px und 19,8 % bei 320 px — dieselbe Schablone meinte auf dem
   schmaleren Telefon eine andere Zone. Und sie meinte auf JEDEM Geraet die
   falsche: Die Zeilen lagen bei 83,4 .. 93,5 % der Rahmenhoehe, waehrend die
   echten Zeichenzeilen eines Passes bei 78,7 .. 88,2 % liegen (am Korpus
   gemessen, Herleitung in captureKonfiguration.js/ZEICHENZEILEN).

   `top` und die Zeilenmitten setzt jetzt der CameraController als
   Inline-Stil je Ausweisart. Die Werte hier sind nur der Rueckfall fuer den
   Pass, falls das Modul einmal ohne Profil montiert — und ein Rueckfall,
   der die Norm trifft, ist besser als einer, der huebsch aussieht. */
.idcap-mrz{position:absolute;left:0;right:0;bottom:0;top:73.07%;overflow:hidden;pointer-events:none;border-top:1px solid var(--idcap-ton,var(--idcap-neutral));border-radius:0 0 7px 7px;background:var(--idcap-band,var(--idcap-band-neutral));transition:border-color var(--idcap-weich),background var(--idcap-weich);container-type:inline-size}
/* Jede Zeile sitzt auf IHRER Mitte (`top` vom Controller, dazu die
   Halbierung) — nicht untereinander im Fluss. Nur so behaelt der Abstand
   zweier Zeilen die 6,35 mm der Norm, unabhaengig von Schriftgrad und
   Zeilenabstand des Browsers. */
.idcap-mrz-zeile{position:absolute;left:5%;right:5%;top:32%;transform:translateY(-50%);font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:clamp(6px,var(--idcap-zeilengrad,2.86cqw),15px);letter-spacing:.04em;line-height:1;color:rgba(255,255,255,.82);white-space:nowrap;overflow:hidden;opacity:var(--idcap-zeilen,.6);transition:opacity var(--idcap-weich),color var(--idcap-weich)}
/* Der Schriftgrad folgt der ZEICHENhoehe der Norm (2,5 mm), umgerechnet ueber
   die Versalhoehe der Schreibmaschinenschrift — gesetzt wird er als
   `--idcap-zeilengrad` vom Controller. Bis V1.0.1281 stand hier je Profil
   ein aus der Bandbreite gerechneter Wert (3,2 bzw. 4,7 cqw). Der war nicht
   falsch: Beide Rechnungen liegen weniger als ein Prozent auseinander, weil
   eine normgerechte Zeile das Dokument zu 89 % ausfuellt. Die Groesse war
   nie das Problem — die Lage war es. */
.idcap-bild-laeuft .idcap-mrz-zeile{--idcap-zeilen:.6}
/* Die Beschriftung sitzt im leeren Kopf des Bandes: Die Norm laesst ueber der
   ersten Zeichenzeile eine Zeilenhoehe frei, und genau die ist hier frei. */
.idcap-mrz-marke{position:absolute;left:5%;right:5%;top:3%;font-size:clamp(6px,1.7vw,9px);font-weight:700;letter-spacing:.08em;text-transform:uppercase;color:var(--idcap-ton,var(--idcap-neutral));transition:color var(--idcap-weich)}

/* Profile: EINE Klasse je Ausweisart, gesetzt vom CameraController.
   Reisepass = aufgeschlagene Datenseite mit Foto. Karte (ID-1) = die Seite
   MIT den Codezeilen, und das ist bei TD1 die Rueckseite: ohne Foto. Eine
   Karten-Schablone mit Portrait wuerde den Gast auf die Vorderseite fuehren,
   die unser drittes Tor zwangslaeufig abweist. */
.idcap-profil-id_card .idcap-portraet,.idcap-profil-residence_permit .idcap-portraet{display:none}

/* ── Der Fortschrittsring (V1.0.1232) ─────────────────────────────────────
   An die ECHTE Sammelfensterzeit gebunden (strokeDashoffset aus dem
   Controller), keine Animation. Sichtbar nur waehrend des Sammelns. */
.idcap-ring{position:absolute;top:8px;right:8px;width:56px;height:56px;opacity:0;transition:opacity .25s;pointer-events:none}
.idcap-ring.idcap-ring-aktiv{opacity:1}
.idcap-ring svg{width:100%;height:100%;transform:rotate(-90deg)}
.idcap-ring-spur{fill:rgba(0,0,0,.35);stroke:rgba(255,255,255,.35);stroke-width:5}
.idcap-ring-voll{fill:none;stroke:#4ade80;stroke-width:5;stroke-linecap:round;stroke-dasharray:163.36;stroke-dashoffset:163.36;transition:stroke-dashoffset .12s linear}

/* ── Bewegung (V1.0.1280) ─────────────────────────────────────────────────
   Zwei Animationen, beide nur ueber `transform` und `clip-path` auf kleinen
   Flaechen — ueber dem Livebild rechnet parallel der Erkennungstakt, und der
   Hauptthread traegt ausserdem Ring und Statustext. Nichts hiervon animiert
   Farbe, Schatten oder Layout.

   1. Der Suchlauf laeuft NUR, solange wirklich gesucht wird, und nur ueber
      dem Codezeilen-Band — dort schaut der Scanner hin. 2,8 s je Durchgang
      mit Ruhepause und 14 % Deckkraft: ein Streifen Licht, kein Laserstrahl.
   2. Die Lesemarke laeuft EINMAL, wenn das dritte Tor haelt und das Fenster
      beginnt. Sie behauptet keine gelesenen Zeichen — sie zeigt, dass die
      ZONE sitzt; was der Scanner wirklich weiss, sagt der Statustext. */
@keyframes idcapSuchlauf{0%{transform:translateX(-105%)}55%,100%{transform:translateX(105%)}}
@keyframes idcapZeileGelesen{from{clip-path:inset(0 100% 0 0)}to{clip-path:inset(0 0 0 0)}}
.idcap-mrz::after{content:'';position:absolute;inset:0;pointer-events:none;opacity:0;background:linear-gradient(90deg,transparent,rgba(255,255,255,.14),transparent)}
.idcap-rahmen.idcap-suchen .idcap-mrz::after,.idcap-rahmen.idcap-fuehrung .idcap-mrz::after{opacity:1;animation:idcapSuchlauf 2.8s ease-in-out infinite}
.idcap-rahmen.idcap-sammelt .idcap-mrz-zeile{color:rgba(198,247,214,.95);animation:idcapZeileGelesen .45s ease both}
.idcap-rahmen.idcap-sammelt .idcap-mrz-zeile:nth-of-type(2){animation-delay:.13s}
.idcap-rahmen.idcap-sammelt .idcap-mrz-zeile:nth-of-type(3){animation-delay:.26s}

/* Bei reduzierter Bewegung bleibt JEDER Zustand unterschieden — es entfaellt
   nur der Weg dorthin. Farbwechsel und die wachsenden Ecken sind kein
   „Motion" im Sinne der Vorgabe, sondern die Zustandsanzeige selbst; sie
   springen dann eben, statt zu gleiten. */
@media (prefers-reduced-motion:reduce){
  .idcap-ghost{transition:none}
  .idcap-ecke{transition:border-color var(--idcap-weich)}
  .idcap-rahmen.idcap-suchen .idcap-mrz::after,.idcap-rahmen.idcap-fuehrung .idcap-mrz::after{animation:none;opacity:0}
  .idcap-rahmen.idcap-sammelt .idcap-mrz-zeile{animation:none}
}

/* ══ Der Anleitungsbildschirm vor der Kamera (V1.0.1283) ══════════════════
   Er zeigt DIESELBE Schablone wie das Livebild — gebaut von derselben
   Funktion (`baueSchablone`), aus denselben Millimetern (`ZEICHENZEILEN`).
   Deshalb stehen hier KEINE Masse: keine Bandhoehe, keine Zeilenlage, kein
   Schriftgrad. Alles das setzt der Aufbau als Inline-Stil, genau wie in der
   Kamera. Was hier steht, ist ausschliesslich die HAUT: heller Grund statt
   Livebild.

   Zwei Zahlensaetze fuer dieselbe Sache waeren die Falle, die dieses Haus
   schon mehrfach getroffen hat — die naechste Korrektur an der Bandlage
   erreichte sonst die Kamera und nicht die Anleitung, und der Gast richtete
   sein Dokument nach einem Bild aus, das der Scanner nicht mehr meint. */
/* Die weisse Karte um die Abbildung: kein sichtbarer Rand, nur ein Hauch
   Schatten, und deutlich mehr Luft als bis V1.0.1286 (gemessen an der
   Vorlage: die Abbildung nimmt 73 % der Kartenbreite ein, oben 7 %, unten
   ein knappes Fuenftel — dort steht die Bildunterschrift). Die Abbildung
   sitzt bewusst NICHT mittig: Oben Bild, unten Wort. */
.anl-blatt{margin:0 0 18px;padding:22px 18px 18px;background:var(--idc-surface);
           border:0;border-radius:20px;box-shadow:0 1px 3px rgba(15,23,42,.06);
           display:flex;flex-direction:column;align-items:center;gap:16px}
/* Die Buehne ist NUR der Bezugsrahmen fuer das Deckblatt (V1.0.1285) — sie
   traegt dieselbe Breitengrenze wie der Rahmen darin und ist damit deckungs-
   gleich mit ihm. Absichtlich so herum: `d1RahmenProbe` und
   `tests/checkin/anleitungBildschirm.test.js` bauen den Rahmen OHNE Buehne
   nach und messen ihn aus. Haette ich die Breitengrenze hierher verschoben,
   maessen beide ab sofort einen ungedeckelten Rahmen — also eine Geometrie,
   die es auf der Seite nicht gibt, bei voellig gruenem Lauf. */
.anl-buehne{position:relative;width:78%;max-width:300px;perspective:1000px}
/* Die Breite ist gedeckelt, das Verhaeltnis kommt vom Profil (aspectRatio als
   Inline-Stil) — die Passseite ist flacher als die Karte, und beide sollen
   ihre eigene Form behalten. 300 px passen bei 320 px Fensterbreite mit den
   Innenabstaenden gerade noch ohne Quetschung. */
.anl-rahmen{position:relative;width:100%;max-width:300px;
            border:2px solid var(--idc-line);border-radius:10px;background:#f6f8fc;
            /* Die Tokens, die die geteilten `.idcap-*`-Regeln lesen. Ohne sie
               liefe `var(--idcap-ton)` ins Leere und die Randfarbe fiele auf
               `currentColor` zurueck — die Regeln erwarten sie sonst von
               `.idcap-huelle`, und die gibt es hier nicht. */
            --idcap-ton:#b9761c;--idcap-band:rgba(224,161,60,.18);
            --idcap-zeilen:1;--idcap-weich:.22s ease;
            /* Die Haut des Codezeilenbandes — EINMAL hier, gelesen von der
               Grundregel UND vom Ende der Einfaerbung (V1.0.1287). Vorher
               standen beide Werte doppelt da; das ging gut, bis die Grundfarbe
               sich aenderte: Ohne Bewegung sah der Gast dann das Neue, mit
               Bewegung endete die Kurve weiter im Alten. Ein Unterschied, den
               nur sieht, wer beide Faelle nebeneinander legt. */
            --anl-band:#f7f0e0;--anl-kante:#dcc79b}
/* Der Ghost ist im Livebild durchscheinend und blendet sich mit dem ersten
   Kamerabild ein (`.idcap-bild-laeuft`). Hier gibt es kein Kamerabild und
   nichts durchscheinen zu lassen: Er ist die Abbildung selbst. */
.anl-rahmen .idcap-ghost{opacity:1;transform:none;transition:none}
/* ── Die Datenseite ist hier eine ABBILDUNG, nicht nur eine Schablone ─────
   (V1.0.1287)

   Ueber dem Livebild muss der Ghost sparsam sein: Er liegt zu 22 % deckend
   auf dem echten Dokument, und jedes Zeichen mehr verdeckt genau das, was
   der Scanner lesen soll. Hier liegt er auf hellem Grund und hat den ganzen
   Bildschirm — hier darf er aussehen wie eine Datenseite.

   Die GEOMETRIE bleibt dabei unangetastet: Seitenverhaeltnis, Lage des
   Codezeilenbandes, Zeilenmitten und Schriftgrad kommen weiterhin aus
   `baueSchablone`/`ghostGeometrie`, also aus derselben Rechnung wie in der
   Kamera. Was hier steht, ist Haut: Farbe, Groesse des Portraets, und dass
   ueber jedem Wert sein Name steht. Ein Mass ist nicht dabei — der Waechter
   „das Stylesheet der Anleitung enthaelt KEINE Bandmasse" prueft das. */
.anl-rahmen{background:#fbfcfe}
.anl-rahmen .idcap-ghost{padding:7% 7% 0}
.anl-rahmen .idcap-ghost-oben{gap:7%}
/* Groesser als im Livebild — dort ist das Portraet ein Fleck zum Ausrichten,
   hier das Merkmal, an dem der Gast die richtige Seite erkennt. Aber nicht
   beliebig gross: 22 % der Seitenbreite im Verhaeltnis 35:45 ist ein
   Passbild, ein halbflaechiger Block waere keines mehr. */
.anl-rahmen .idcap-portraet{width:22%;height:auto;aspect-ratio:.78;
                            align-self:flex-start;border-radius:5px;background:#dbe4f2}
.anl-rahmen .idcap-portraet::before,.anl-rahmen .idcap-portraet::after{background:#93a8c6}
.anl-rahmen .idcap-feld{color:#41506b}
.anl-rahmen .idcap-balken{background:#c3cddd}
/* Beschriftung und Wert stehen uebereinander wie auf dem echten Dokument:
   der Name klein, hell und in Versalien, der Wert darunter in Schoenschrift.
   Die Schriftgroessen haengen an der Breite der Abbildung (`cqw`), nicht am
   Fenster — bei 320 px ist die Abbildung fast so breit wie bei 430 px, ein
   `vw`-Mass haette sie dort unnoetig geschrumpft. */
.anl-rahmen .idcap-felder{gap:4%;container-type:inline-size}
.anl-rahmen .idcap-feld{display:flex;flex-direction:column;gap:.15em;font-family:inherit;letter-spacing:0}
.anl-rahmen .idcap-feldname{font-size:clamp(6px,4cqw,9px);font-weight:700;letter-spacing:.07em;
                            text-transform:uppercase;color:#93a3bb;line-height:1.1}
.anl-rahmen .idcap-feldwert{font-size:clamp(8px,6.4cqw,13px);font-weight:700;color:#1f2b45;
                            line-height:1.15;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
/* ── Der Codezeilen-Kasten (V1.0.1287) ───────────────────────────────────
   Im Livebild ist die Zone ein randloses Schauband ueber die volle Breite —
   sie soll dort keine zweite Kante neben den Dokumentrand malen. Auf der
   Anleitung ist sie ein eingerueckter Kasten mit Rand ringsum, in derselben
   Flucht wie der Datenblock darueber: Hier ist sie nicht Messfeld, sondern
   das Merkmal, das der Gast wiedererkennen soll.

   Gezeichnet wird auf `::before`, nicht am Element. Das ist der Punkt: Die
   LAGE des Elements (Oberkante = `bandVon`, Zeilenmitten) kommt aus
   `ghostGeometrie` und darf nicht angefasst werden — ein Innenabstand am
   Element wuerde die absolut gesetzten Zeilen mitverschieben und damit genau
   die gerechnete Geometrie verstellen. Der Schmuckknoten liegt darin und
   verschiebt nichts; die Zeilen zeichnen darueber, weil `::before` vor den
   Kindern gemalt wird. */
.anl-rahmen .idcap-mrz{background:none;border-top:0}
/* Etwas weiter eingerueckt als in der Vorlage (dort 4,4 %): Die zeichnet
   die Codezeilen gekuerzt, wir zeichnen die vollen 44 Zeichen der Norm. Bei
   4,4 % stiessen sie an den Kastenrand. */
.anl-rahmen .idcap-mrz::before{content:'';position:absolute;inset:0 3% 5.5%;
            border:1px solid var(--anl-kante);border-radius:6px;background:var(--anl-band)}
.anl-rahmen .idcap-mrz-zeile{color:#6f5d33}
/* Ueber der ersten Zeichenzeile laesst die Norm eine Zeilenhoehe frei — dort
   sitzt die Beschriftung, im Livebild wie hier. */
/* `text-wrap:balance` verteilt den Satz auf gleich lange Zeilen, statt bei
   320 px ein einzelnes „sein." in die zweite Zeile zu haengen. Wo der Browser
   es nicht kennt, bricht er wie bisher — kein Rueckfall noetig. */
/* Ein Raster mit EINER Zelle: Beide Saetze liegen uebereinander, der laengere
   bestimmt die Hoehe. Wuerden sie sich abloesen, aenderte die Zeile beim
   Wechsel ihre Hoehe und die Abbildung darueber ruckte mit — mitten in einer
   Bewegung, die ruhig wirken soll. */
.anl-marke{margin:0;font-size:14px;font-weight:600;line-height:1.4;
           color:#8a5f14;text-align:center;text-wrap:balance;display:grid}
.anl-sagt{grid-area:1/1}
/* Ohne Bewegung steht der Endzustand da: die Aussage ueber die Seite, die
   der Scanner braucht. Der Hinweis „bitte umdrehen" gehoert zur Bewegung und
   ergaebe ohne sie keinen Sinn — man saehe ja nichts, was sich dreht. */
.anl-sagt--vorn{opacity:0}

/* ── Das Deckblatt: reine Haut, kein einziges Mass (V1.0.1285) ────────────
   Es liegt ueber der Schablone und zeigt, was der Gast VOR dem Scannen in
   der Hand hat: den geschlossenen Passdeckel bzw. die Kartenvorderseite mit
   dem Foto. Dann geht es auf und ist weg.

   Was es bewusst NICHT tut: irgendein Mass mit der Kamera teilen. Der Rahmen
   darunter ist die geteilte Schablone aus `baueSchablone`, gebaut aus den
   Millimetern der Norm. Das Deckblatt ist Zierrat — ein Einband hat im
   Livebild kein Gegenstueck, also kann er auch nicht gegen es driften.

   Im Ruhezustand ist es per `display:none` gar nicht erst da: Ohne Bewegung
   gaebe es nichts, was es wieder wegnimmt, und es verdeckte auf Dauer genau
   die Abbildung, die es erklaeren soll. Sichtbar wird es allein im
   Bewegungsblock unten. (Kein `hidden`-Attribut — auf dieser Seite schlaegt
   jede display-Regel das Attribut, das ist hier schon dreimal passiert.) */
.anl-huelle{position:absolute;inset:0;border-radius:10px;overflow:hidden;display:none}
.anl-huelle-strich{height:6px;border-radius:3px}
/* Der Passdeckel: dunkler Einband, Praegewappen, der Schriftzug — und links
   der Buchruecken, an dem er sich dreht.

   `transform-origin:left center` ist nicht beliebig: Das Scharnier eines
   Hefts sitzt am Bund, und der liegt an der Kante, an der die Datenseite
   beginnt. Dreht der Deckel um die falsche Kante, wandert er ueber die
   Seite hinweg statt sie freizugeben — die Bewegung saehe aus wie ein Blatt,
   das weggeweht wird.

   KEIN `backface-visibility:hidden` (anders als bis V1.0.1286): Der Deckel
   dreht ueber die Senkrechte hinaus und steht dann links neben der Seite,
   so wie ein aufgeschlagenes Heft. Mit versteckter Rueckseite verschwaende
   er genau dort schlagartig — und das las sich nicht als „aufgeklappt",
   sondern als „weg". Die Schrift darauf ist in diesem Moment ein paar
   Bildpunkte breit; dass sie gespiegelt steht, sieht niemand. */
.anl-buehne--aufschlagen .anl-huelle{background:linear-gradient(155deg,#33456b,#1b2740);
            transform-origin:left center;
            flex-direction:column;align-items:center;justify-content:center;gap:9%}
.anl-buehne--aufschlagen .anl-huelle-marke{width:15%;aspect-ratio:1;border-radius:50%;
            border:2px solid rgba(226,232,240,.62)}
.anl-huelle-wort{font-size:clamp(6px,3.2%,11px);font-weight:600;letter-spacing:.34em;
            text-indent:.34em;color:rgba(226,232,240,.62);line-height:1}
/* Die Kartenvorderseite: Foto links, Aufdruck rechts. Sie ist das
   Gegenstueck zur Rueckseite darunter — dort ist kein Foto, dort stehen die
   Codezeilen. Genau diesen Unterschied soll das Wenden zeigen, und deshalb
   traegt die Vorderseite hier ein deutlich sichtbares Foto: Waeren beide
   Seiten gleich hell und gleich leer, waere die Drehung eine Bewegung ohne
   Aussage. */
.anl-buehne--wenden .anl-huelle{background:linear-gradient(155deg,#dae3f0,#c2d0e2);
            border:1px solid #a9b9cf;
            transform-origin:center;grid-template-columns:26% 1fr;
            /* Die Zeilen MUESSEN hier stehen, auch wenn `auto` nach nichts
               aussieht: `grid-row:1/-1` zaehlt gegen das AUSDRUECKLICHE
               Raster. Ohne diese Angabe gibt es keine Linie -1, die Spanne
               des Fotos fiel auf eine einzige Zeile zusammen — und die vier
               Aufdruckstriche rutschten unter das Foto statt daneben. */
            grid-template-rows:repeat(4,auto);
            align-content:center;align-items:center;gap:9% 9%;padding:10%}
.anl-buehne--wenden .anl-huelle-marke{grid-area:1/1/-1/2;aspect-ratio:.75;
            background:#8fa3bd;border-radius:4px}
/* Vier ungleich lange Striche, wie ein echter Aufdruck — gleich lange lesen
   sich als Tabelle, nicht als Ausweisvorderseite. Anteile aus der Vorlage. */
.anl-buehne--wenden .anl-huelle-strich{background:#8fa3bd;width:100%}
.anl-buehne--wenden .anl-huelle-strich:nth-of-type(3){width:71%}
.anl-buehne--wenden .anl-huelle-strich:nth-of-type(4){width:84%}
.anl-buehne--wenden .anl-huelle-strich:nth-of-type(5){width:57%}
.anl-buehne--wenden .anl-rahmen{transform-origin:center;backface-visibility:hidden}
.anl-buehne--wenden .anl-huelle{backface-visibility:hidden}

/* ── Die Bewegung erklaert den Handgriff (V1.0.1285) ──────────────────────
   Bis V1.0.1284 lief hier eine einmalige Hervorhebung: Foto, Felder, Band,
   Beschriftung — nacheinander betont. Sie zeigte die richtige LAGE. Was
   fehlte, war der Handgriff DAVOR, und genau den nennt der Satz ueber dem
   Bild: „Öffnen Sie den Reisepass" bzw. „Zeigen Sie die Rückseite". Das Bild
   zeigte in beiden Faellen dieselbe fertig aufgeschlagene Flaeche — wer den
   Satz nicht liest (und am Telefon liest ihn die Haelfte nicht), sah nie,
   was er tun soll. Jetzt fuehrt die Abbildung genau das vor, was der Satz
   sagt. Ein zweiter Text entsteht dabei nicht.

   Der Ablauf, in der Reihenfolge, in der der Gast es tut:
     1. Das geschlossene Dokument liegt schraeg da.
     2. Es wird aufgeschlagen (Heft) bzw. gewendet (Karte).
     3. Es dreht sich gerade — in die Lage, die der Rahmen der Kamera meint.
     4. Foto, Felder und zuletzt die Codezeilen leuchten nacheinander auf.
        Dort schaut der Scanner hin, dorthin gehoert der letzte Blick.
     5. Ruhe. Ueber ein Drittel des Taktes passiert nichts mehr.

   ── EIN Takt, damit nichts auseinanderlaeuft ────────────────────────────
   Alle Kurven teilen `--anl-takt` und beschreiben ihre Phase in PROZENT
   davon. Vorher trug jede ihre eigene Dauer und Verzoegerung; das ging,
   solange nichts wiederholt wurde. Bei einer Wiederholung waere jede fuer
   sich zurueckgesprungen — ab dem zweiten Durchgang haette sich das Band
   gefaerbt, waehrend der Deckel noch zu war.

   ── Dreimal, nicht endlos ───────────────────────────────────────────────
   Gewuenscht war eine Schleife („ein animiertes GIF"). Endlos ist sie
   trotzdem nicht, denn der Grund von V1.0.1283 gilt weiter: Eine Flaeche,
   die sich ewig bewegt, haelt den Blick auf einem Bildschirm fest, den der
   Gast verlassen soll — und sie steht direkt ueber dem Knopf, den er
   druecken soll. Drei Durchgaenge sind 27 Sekunden: Wer einmal wegsieht,
   bekommt die Bewegung noch zweimal. Danach steht das Bild still, im
   Endzustand, mit hervorgehobenen Codezeilen. `anleitungBildschirm.test.js`
   haelt fest, dass hier nie `infinite` steht.

   ── Ohne Bewegung ───────────────────────────────────────────────────────
   Der Grundzustand oben IST der Endzustand: Band hervorgehoben, Deckblatt
   per `display:none` gar nicht erst da. Der ganze Block steht deshalb unter
   `no-preference` und braucht keine zweite Regel fuer reduzierte Bewegung —
   wer sie abgestellt hat, sieht sofort das aufgeloeste Bild samt
   Hervorhebung, also die Aussage ohne den Weg zu ihr. Ein `animation:none`
   haette den umgekehrten Fehler gemacht: Es haette das Wesentliche
   unmarkiert gelassen und den Deckel liegen lassen.

   Animiert werden ausschliesslich `transform`, `opacity`, `box-shadow` und
   zwei Farben auf kleinen Flaechen — kein Layout, kein Reflow. */
@media (prefers-reduced-motion:no-preference){
  .anl-blatt{--anl-takt:10s;--anl-runden:3}
  .anl-huelle{display:flex}
  /* 1. + 3. Das Blatt kommt schraeg an und dreht sich gerade. Beides in
     EINER Kurve, weil beides denselben `transform` beschreibt: zwei
     Animationen auf einer Eigenschaft ueberschreiben einander, statt sich zu
     ergaenzen. */
  @keyframes anlBlatt{
    0%       {opacity:0}
    4%,100%  {opacity:1}
  }
  /* 2a. Das Heft: der Deckel schwingt um den Buchruecken auf und gibt die
     Datenseite darunter frei — je weiter er sich dreht, desto mehr sieht man.

     Er bleibt bis 19 % voll DECKEND. Ein Deckel, der schon beim Schwingen
     durchsichtig wird, sieht nach einer Ueberblendung aus, nicht nach einer
     Bewegung; erst kurz vor der Senkrechten geht er weg. Weiter als etwa 90
     Grad braucht er dabei nicht zu kommen: Dort ist er ohnehin nur noch eine
     Kante, und `backface-visibility` nimmt ihn danach weg — was frueher
     aussah, als sei er verschwunden statt aufgeklappt. */
  @keyframes anlAufschlagen{
    0%,8%    {transform:rotateY(0);opacity:1}
    14%      {transform:rotateY(-104deg);opacity:1}
    17%,100% {transform:rotateY(-118deg);opacity:0}
  }
  /* 2b. Die Karte: die Vorderseite dreht sich zur Kante weg — die LINKE
     Kante kommt dabei auf den Betrachter zu, also positives `rotateY` um die
     Mitte. Gemessen an der Vorlage: bei 55 Grad ist die linke Bildkante 149
     Bildpunkte hoch, die rechte 129. Die nahe Kante ist die hoehere. */
  @keyframes anlWendenVorn{
    0%,8%      {transform:rotateY(0);opacity:1}
    18%        {transform:rotateY(90deg);opacity:1}
    18.1%,100% {transform:rotateY(90deg);opacity:0}
  }
  /* … und im selben Augenblick kommt die Rueckseite aus der Kante hervor,
     aus derselben Richtung weiterdrehend. Der Wechsel liegt bei 18 %, wo
     beide hochkant stehen und niemand hindurchsehen kann. */
  @keyframes anlWendenHinten{
    0%,18%    {transform:rotateY(-90deg);opacity:0}
    18.1%     {transform:rotateY(-90deg);opacity:1}
    28%,100%  {transform:rotateY(0);opacity:1}
  }
  /* 4. Der Datenblock — Foto UND Angaben zusammen — leuchtet einmal auf und
     tritt wieder zurueck. EINE Flaeche, nicht zwei Hervorhebungen: Bis
     V1.0.1286 bekam das Portraet einen Ring und die Felder eine eigene
     Flaeche, also zwei Signale fuer eine Aussage („das hier ist der
     Datenblock"). In der Vorlage ist es ein Block hinter beidem.

     `anlRing` heisst weiterhin so, weil die Kurve dieselbe Aufgabe hat wie
     vorher — sie sitzt nur an einem anderen Element. `anlFelder` gibt es
     nicht mehr; ein zweiter Takt fuer dieselbe Aussage waere genau das, was
     hier weggefallen ist. */
  @keyframes anlRing{
    0%,34%   {background:rgba(23,41,74,0)}
    40%,48%  {background:rgba(23,41,74,.07)}
    54%,100% {background:rgba(23,41,74,0)}
  }
  /* Der einzige Uebergang, der BLEIBT: das Band faerbt sich ein und behaelt
     die Farbe. Alles davor tritt wieder zurueck. */
  @keyframes anlBand{
    0%,52%   {border-top-color:#cfd6e2;background:rgba(23,41,74,.04)}
    60%,100% {border-top-color:var(--anl-kante);background:var(--anl-band)}
  }
  /* Die Beschriftung im Bandkopf liest ihre Farbe sonst aus `--idcap-ton`,
     und das ist ab dem ersten Bild der Endwert. Sie wuerde also bernstein
     ueber einem noch neutralen Band stehen. Custom Properties lassen sich
     ohne `@property` nicht weich ueberblenden — deshalb hier die Farbe
     direkt, im selben Takt wie das Band darunter. */
  @keyframes anlMarkeFarbe{
    0%,52%   {color:#8a93a5}
    60%,100% {color:#b9761c}
  }
  /* Die Zeilen selbst kommen zuletzt und einzeln — der Punkt, auf dem der
     Blick am Ende stehenbleiben soll. */
  @keyframes anlZeile{
    0%,56%   {opacity:.22}
    64%,100% {opacity:1}
  }
  @keyframes anlMarke{
    0%,64%   {opacity:0;transform:translateY(4px)}
    70%,100% {opacity:1;transform:none}
  }
  /* Der Wechsel der Bildunterschrift liegt AUF dem Wenden: Die Vorderseite
     dreht sich zwischen 8 % und 18 % zur Kante weg, ab 18 % kommt die
     Rueckseite. Der Satz geht kurz davor und kurz danach — so wechselt er
     mit der Seite und nicht daneben. */
  @keyframes anlSagtVorn{
    0%,14%   {opacity:1}
    19%,100% {opacity:0}
  }
  @keyframes anlSagtHinten{
    0%,19%   {opacity:0}
    26%,100% {opacity:1}
  }
  .anl-sagt--vorn{animation:anlSagtVorn var(--anl-takt) ease var(--anl-runden) both}
  .anl-sagt--hinten{animation:anlSagtHinten var(--anl-takt) ease var(--anl-runden) both}
  .anl-buehne{animation:anlBlatt var(--anl-takt) ease var(--anl-runden) both}
  .anl-buehne--aufschlagen .anl-huelle{animation:anlAufschlagen var(--anl-takt) ease-out var(--anl-runden) both}
  .anl-buehne--wenden .anl-huelle{display:grid;animation:anlWendenVorn var(--anl-takt) ease-in var(--anl-runden) both}
  .anl-buehne--wenden .anl-rahmen{animation:anlWendenHinten var(--anl-takt) ease-out var(--anl-runden) both}
  .anl-rahmen .idcap-ghost-oben{animation:anlRing var(--anl-takt) ease var(--anl-runden) both;border-radius:6px}
  .anl-rahmen .idcap-mrz::before{animation:anlBand var(--anl-takt) ease var(--anl-runden) both}
  .anl-rahmen .idcap-mrz-marke{animation:anlMarkeFarbe var(--anl-takt) ease var(--anl-runden) both}
  /* Die Verzoegerung verschiebt die ganze Kurve, nicht nur den Start — die
     Staffelung bleibt damit ueber alle drei Durchgaenge erhalten. */
  .anl-rahmen .idcap-mrz-zeile{animation:anlZeile var(--anl-takt) ease var(--anl-runden) both}
  .anl-rahmen .idcap-mrz-zeile:nth-of-type(2){animation-delay:.2s}
  .anl-rahmen .idcap-mrz-zeile:nth-of-type(3){animation-delay:.4s}
  .anl-marke{animation:anlMarke var(--anl-takt) ease var(--anl-runden) both}
  /* Die EINE Unterschrift blendet sich zum Schluss ein — sie ist das Fazit.
     ZWEI Unterschriften koennen das nicht: Die erste muss von Anfang an
     dastehen, sonst spricht das Bild ueber eine Vorderseite, die niemand
     benannt sieht. Hier traegt deshalb jeder Satz seine eigene Zeit, und die
     Huelle haelt still. */
  .anl-marke--zwei{animation:none}
}

/* ── Ausrufezeichen und Hilfetext am Feld (V1.0.1273) ─────────────────────
   Vorher stand über jedem auffälligen Feld eine gelbe Marke mit dem Satz
   „Bitte prüfen – Erkennung unsicher". Bei drei sauber gelesenen Feldern
   standen drei Warnungen untereinander — das verunsichert den Gast, statt
   ihm zu helfen. Geblieben ist ein kleines Zeichen; der Satz kommt erst auf
   Antippen oder beim Fokus.

   Das Zeichen sitzt oben rechts auf der Beschriftungszeile. Das ist die
   einzige Stelle, an der es bei ALLEN Bausteinen frei ist: im Eingabefeld
   käme es dem Text in die Quere, im Länderfeld dem Aufklapp-Pfeil und im
   nativen Auswahlfeld dem Pfeil des Betriebssystems.

   Der Hilfetext wird NIE aus dem Baum genommen: `aria-describedby` auf ein
   Element mit `display:none` ist für den Screenreader nicht vorhanden — und
   genau diese Verbindung ist der V1.0.1239-Fix. Er ist im Ruhezustand nur
   unsichtbar (dieselbe Technik wie `.sr-only`). Aus demselben Grund steht
   hier auch kein `hidden`: Auf dieser Seite schlägt jede display-Regel das
   Attribut, das ist in diesem Haus schon dreimal zugeschnappt. */
.feld-block{position:relative;margin-bottom:16px}
.feld-block:last-child{margin-bottom:0}
.feld-block>.field{margin-bottom:0}
.feld-zeichen{position:absolute;top:-3px;right:0;width:28px;height:28px;padding:0;
              display:grid;place-items:center;border-radius:50%;cursor:pointer;
              border:1px solid var(--warn-border);background:var(--warn-bg);color:var(--warn-ink);
              /* einzeln, nicht als `font`-Kurzform: `inherit` ist dort kein
                 gueltiger Familienname, und ein „!" in der Schrift des
                 Browsers faellt neben dem Formular auf. */
              font-family:inherit;font-size:15px;font-weight:700;line-height:1}
.feld-zeichen:hover{background:var(--idc-surface)}
.feld-zeichen:focus-visible{outline:2px solid var(--idc-brand);outline-offset:2px}
.feld-hilfe{position:absolute;width:1px;height:1px;margin:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}
.feld-hilfe.ist-offen{position:static;width:auto;height:auto;overflow:visible;clip:auto;white-space:normal;
                      margin:8px 0 0;font-size:13px;line-height:1.5;color:var(--warn-ink)}
.feld-hilfe.ist-fehler.ist-offen{color:var(--err)}

/* V1.0.1600 — „✓ Flug gefunden": Rückmeldung unter der Flugnummer, nie blockierend. */
.flight-check:empty { display: none; }
.flight-check { margin-top: 8px; }
.flight-check-box { display: grid; gap: 2px; padding: 10px 12px; border-radius: 10px; background: var(--surface-2, #f6f7f9); font-size: 0.95rem; }
.flight-check-ok { color: #15803d; font-weight: 600; }
.flight-check-warn { color: #b45309; }
.flight-check-frage { margin-top: 6px; font-weight: 600; }
.flight-check-wahl { justify-self: start; margin-top: 4px; }
.flight-check-wahl[aria-pressed="true"] { outline: 2px solid currentColor; }
