/*
 * DER RAUM — der angemeldete Bereich als bleibende Huelle.
 *
 * EIGENE DATEI, nicht `web/styles.css`: die Marketing-Seite ist eine Buehne
 * mit Ken-Burns; dieser Bereich ist ein ORT, in dem gearbeitet wird.
 * Dieselben Farben, dasselbe Material, andere Dichte.
 *
 * ── DIE BAUART (Kapitaens-Entscheid 22.08.2026, „Weg B — Der Raum") ──────
 *
 * EINE Huelle besitzt das Fenster und bleibt stehen: Materialgrund, Schiene
 * oben, Fassung links und rechts, Buehne in der Mitte, Inspektor rechts. Die
 * vier Bereiche — Sammlung, Deck, Kampf, Bestellen — sind ZONEN in diesem
 * Raum, keine Seiten, die einander ersetzen. Ein Zonenwechsel tauscht den
 * Inhalt der Buehne; Schiene und Inspektor bleiben stehen.
 *
 * Wer eine Schiene, einen Inspektor oder eine Fassung „nur fuer diesen einen
 * Bildschirm" ein zweites Mal baut, hat die Huelle viermal — genau die
 * Fehlerklasse, gegen die `AGENTS.md` § 0.8 geschrieben ist. Das Gegenstueck
 * dazu steht in `js/app.js`: die Huelle wird EINMAL gebaut und nie neu
 * gezeichnet.
 *
 * Die Werte kommen aus `web/assets/tokens.json` (erzeugt aus
 * `frontend/src/theme/{colors,tiers}.ts`) — bei Widerspruch gewinnt der Code,
 * das sagt die Quelle selbst. Nicht nachtippen: bei Aenderung neu abschreiben.
 *
 * Drei Regeln aus `web/AGENTS.md` gelten hier unveraendert:
 *   1. Bernstein #F2A33C ist die EINZIGE Signalfarbe — Interaktion und
 *      Gewinn, nie Dekoration. Material und Motiv tragen die Farbigkeit.
 *   2. Nur `transform` und `opacity` animieren.
 *   3. `prefers-reduced-motion: reduce` macht aus jeder Bewegung eine
 *      150-ms-Blende. Vertrag, keine Option.
 */

/* Dieselbe Anzeigeschrift wie App und Marketingseite (`web/styles.css`) —
   ohne diese Regel FORDERT `h1`/`h2` unten 'Oswald' an, bekommt sie aber nie:
   jede Ueberschrift rendert in `system-ui`. Die Dateien liegen bereits im
   Auslieferstand bei (`gen/dateien/`), erzeugt aus
   `frontend/node_modules/@expo-google-fonts/oswald` — hier nur verdrahtet.

   Die Dateinamen tragen Metros INHALTS-Pruefsumme, und `gen/` wird bei jedem
   Export neu gelegt: aendern sich die Schriftbytes, zeigt der Verweis ins
   Leere, und der Ausfall ist still (Rueckfall auf `system-ui`). Deshalb haelt
   `toteAssetVerweise()` in `web/build.mjs` jedes `url()` gegen die
   Auslieferliste — nach einem Export-Lauf die Namen hier nachziehen. */
@font-face {
  font-family: 'Oswald';
  src: url('gen/dateien/Oswald_600SemiBold.a96a1c662a5d61fee4a0b929b3a504e3.ttf') format('truetype');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Oswald';
  src: url('gen/dateien/Oswald_700Bold.10c64324bb2a7a3fef799c573e0ce19f.ttf') format('truetype');
  font-weight: 700;
  font-display: swap;
}

:root {
  --background: #0c0a08;
  --surface: #2b241c;
  --surface-alt: #3a3126;
  --text-primary: #f3e9d7;
  --text-secondary: #b8a78d;
  --text-muted: #7a6c58;
  --brass: #b08d57;
  --brass-dark: #7a5c33;
  --brass-light: #d3b37c;
  --amber: #f2a33c;
  --danger: #c0392b;
  --success: #7ba05b;

  --tier-platinum: #d8dde3;
  --tier-gold: #d3b37c;
  --tier-silver: #b6bcc2;
  --tier-bronze: #b08d57;
  --tier-gray: #7a6c58;

  --gap: 1rem;
  --radius: 6px;

  /*
   * DIE MASSE DES RAUMS.
   *
   * `--schiene-hoehe` ist FEST und nicht gemessen: der Inspektor klebt darunter
   * (`position: sticky`), und ein gemessener Wert braeuchte einen Beobachter,
   * der bei jeder Textaenderung feuert. Damit die Zahl stimmt, darf die
   * Schiene oberhalb der Inspektor-Schwelle NICHT umbrechen — dafuer sorgt
   * `.schiene__werkzeuge` weiter unten (waagerecht rollen statt umbrechen).
   *
   * `--fassung` ist die Breite des MOEBELS links und rechts (Kapitaens-
   * Entscheid E1: „als Moebel"). Sie waechst mit dem Fenster, bleibt aber
   * gedeckelt — eine Fassung, die mitwaechst, bis sie die Arbeitsflaeche
   * frisst, ist Dekoration und kein Moebel.
   */
  --schiene-hoehe: 4rem;
  --fassung: clamp(0.5rem, 2.4vw, 3.25rem);
  --inspektor-breite: clamp(20rem, 24vw, 27rem);
}

* {
  box-sizing: border-box;
}

/* Der Ueberrollbereich mancher Browser zeigt sonst Weiss neben dem Material. */
html {
  background: #100d0a;
}

/*
 * `background: transparent` IST HIER WICHTIG und keine Nachlaessigkeit: der
 * Materialgrund unten haengt an `body::before` mit negativem `z-index`, und
 * nach der Malreihenfolge kommt der EIGENE Hintergrund von `body` ERST
 * DANACH (Schritt 3: Hintergruende der Bloecke im Fluss). Ein gesetzter
 * Hintergrund auf `body` deckt das Pseudo-Element also zu — gemessen
 * 22.08.2026: 55,5 % des Fensters standen in flachem `#100d0a`, waehrend das
 * Leder darunter lag und niemand es sah. Die Vorfarbe traegt `html`, dort ist
 * sie der Grund der Leinwand und liegt richtig.
 */
body {
  margin: 0;
  background: transparent;
  color: var(--text-primary);
  font-family: system-ui, -apple-system, 'Segoe UI', sans-serif;
  font-size: 16px;
  line-height: 1.5;
}

/*
 * DER MATERIALGRUND (Sprintplan S5) — Leder mit Radialverlauf, Blendmodus und
 * Innenschatten statt des flachen `#0c0a08`.
 *
 * Rezept und Material sind nicht neu: `web/styles.css`s `.band--deep` macht
 * dasselbe auf der Marketingseite, und `assets/img/plate-leather.webp` liegt
 * seit dem 15.08.2026 im Repo. Hier wird es montiert, nicht erfunden.
 *
 * ALS FESTES PSEUDO-ELEMENT, nicht als `background-attachment: fixed` auf
 * `body`: der feste Anhang ist auf iOS-Safari seit Jahren fehlerhaft (der
 * Grund springt beim Scrollen), ein festes ELEMENT ist es nicht. Der negative
 * `z-index` haelt es hinter dem Inhalt: nach der Malreihenfolge kommen
 * negative Stapelebenen VOR den Bloecken im Fluss.
 */
body::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: #14100b;
  background-image:
    radial-gradient(120% 90% at 50% 0%, rgba(176, 141, 87, 0.14), transparent 62%),
    url('../assets/img/plate-leather.webp');
  background-size: cover;
  background-position: center;
  background-blend-mode: overlay;
  /* Der Innenschatten dunkelt die Raender, ohne die Koernung zu erschlagen:
     `overlay` gegen eine sehr dunkle Grundfarbe staucht das Leder ohnehin auf
     wenige Stufen, und ein zu dichter Schatten macht daraus wieder eine
     Flaeche. */
  box-shadow: inset 0 0 18rem 2rem rgba(6, 5, 4, 0.62);
}

/*
 * DIE HUELLE. Sie nimmt die ganze Fensterbreite — das ist S9. Sie ist damit
 * ausdruecklich NICHT mehr der Anker der Breiten-Messung: `width: 100%` macht
 * eine Messung an `#app` tautologisch (sie meldete strukturell ~100 %, auch
 * mit gedeckelter Buehne). Gemessen wird an den zwei tragenden Spalten —
 * `INHALTS_ANKER` = `.buehne` + `.inspektor` in `qa/lib/messgrundlage.mjs`,
 * und zwar die von ihnen BELEGTE Breite.
 *
 * Bis zum 22.08.2026 stand hier `max-width: 60rem`: 960 px, unabhaengig von
 * der Fensterbreite. Bei 1920 px waren das 50 % genutzte Breite (noch am alten
 * `#app`-Anker gemessen, nicht am heutigen) gegen 97–100 % bei jeder von sechs
 * Referenzen (Recherche § 1.2). Die Spaltenrechnung der
 * Sammlung (`js/collection.js`, `spaltenBreite`) folgte der Breite schon
 * immer — sie war nur gedeckelt.
 */
#app {
  width: 100%;
  min-height: 100vh;
}

/*
 * DIE ANMELDEFLAECHE ALS ORT (Sprintplan S13 — „Ortsangabe durch
 * Schluesselbild").
 *
 * Ohne Sitzung gibt es keinen Raum, nur die Anmeldung — eine eigene Flaeche
 * (Sprintplan, Bauauftrag B6). Bis zum 22.08.2026 bekam sie hier nichts als
 * ihren Rand: die Tafel klebte oben, darunter zwei Drittel flaches
 * `#0c0a08`, und der erste Bildschirm ueberhaupt trug NULL Bildquellen
 * (Recherche § 1.3/§ 1.6). Jetzt traegt sie den Schauplatz — die Schwelle
 * eines Sammlerhauses bei Nacht.
 *
 * DER SCHAUPLATZ IST EIN EIGENES FESTES ELEMENT, nicht der Grund von
 * `body`: `body::before` traegt bereits das Leder des Raums, und ein zweiter
 * Hintergrund an derselben Stelle hiesse, den einen gegen den anderen zu
 * tauschen, sobald jemand sich abmeldet. Zwei Ebenen, jede mit einem
 * Besitzer — dieselbe Trennung wie zwischen Huelle und Zone.
 *
 * Der Verlauf DARUEBER ist kein Schmuck, sondern die Bedingung dafuer, dass
 * die Tafel lesbar bleibt: das Bild ist auf Mitte 34 von 255 geregelt
 * (`scripts/process-assets.mjs`, STYLEGUIDE § 7), aber die Lichtquelle im
 * Motiv ist es nicht, und die Tafel steht mittig darauf.
 */
.anmeldeflaeche {
  padding: var(--gap);
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
}

.anmeldeflaeche::before {
  content: '';
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background-color: #0f0c08;
  background-image:
    radial-gradient(85% 75% at 50% 45%, rgba(10, 7, 4, 0.28), rgba(10, 7, 4, 0.72) 82%),
    url('../assets/img/scene-web-anmeldung.webp');
  background-size: cover;
  background-position: center;
}

/* Die Tafel steht in der MITTE des Fensters, nicht an seinem oberen Rand:
   `.login-panel` war nur waagerecht zentriert, das Panel begann bei y ~ 8 px
   eines 1080-px-Fensters und darunter standen 66 % leer (Recherche § 1.6,
   Befund 2). Am Verfahren aendert das nichts — es ist dieselbe Tafel. */
.anmeldeflaeche__mitte {
  width: 100%;
  max-width: 32rem;
}

/*
 * DIE WORTMARKE (Sprintplan S13). Dieselbe Bildmarke wie App und
 * Marketingseite, geklont aus der Vorlage in `app.html` — s. `js/marke.js`.
 * Hier steht nur, wie gross sie sitzt.
 */
.wortmarke {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin: 0 0 1.25rem;
}

.wortmarke .marke-iris {
  width: 2rem;
  height: 2rem;
  flex: 0 0 auto;
}

.wortmarke__schrift {
  font-family: 'Oswald', system-ui, sans-serif;
  font-weight: 700;
  font-size: 1.5rem;
  letter-spacing: 0.28em;
  /* Der Schriftzug steht auf einem BILD, nicht auf einer Tafel — ohne
     eigenen Schatten liefe er an einer hellen Stelle des Motivs aus. */
  color: var(--brass-light);
  text-shadow: 0 2px 10px rgba(0, 0, 0, 0.85);
}

/* Dieselbe Marke in der Schiene: kleiner, ohne Aussenabstand, in der Zeile. */
.wortmarke--schiene {
  margin: 0;
  gap: 0.4rem;
}

.wortmarke--schiene .marke-iris {
  width: 1.25rem;
  height: 1.25rem;
}

.wortmarke--schiene .wortmarke__schrift {
  font-size: 1.05rem;
  letter-spacing: 0.2em;
  text-shadow: none;
}

h1,
h2 {
  font-family: 'Oswald', system-ui, sans-serif;
  letter-spacing: 0.02em;
  margin: 0 0 0.5rem;
}

h1 {
  font-size: 1.75rem;
}

h2 {
  font-size: 1.25rem;
}

.panel {
  /* Material statt Flachfarbe: derselbe Gedanke wie beim Seitengrund, eine
     Ebene hoeher. Der Verlauf ist die Tiefe, der Innenstrich die Kante. */
  background-color: var(--surface);
  background-image: linear-gradient(180deg, rgba(243, 233, 215, 0.055), rgba(0, 0, 0, 0.22));
  border: 1px solid var(--brass-dark);
  border-radius: var(--radius);
  box-shadow:
    inset 0 1px 0 rgba(211, 179, 124, 0.16),
    0 1.25rem 2.5rem rgba(0, 0, 0, 0.42);
  padding: calc(var(--gap) * 1.25);
  margin-bottom: var(--gap);
}

.panel.error {
  border-color: var(--danger);
}

.lead,
.muted {
  color: var(--text-secondary);
}

.muted,
small {
  font-size: 0.875rem;
}

/* ── Der Raum ─────────────────────────────────────────────────────────── */

.raum {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

/*
 * DAS MESSING-REZEPT AN EINER STELLE.
 *
 * Materialplatte, Verlauf darueber, `overlay` als Mischung — dieselben fuenf
 * Zeilen trugen die Schiene, die Fassung des Raums und die Fassung des
 * QR-Codes je einmal ab. Sie stehen jetzt EINMAL; was die drei wirklich
 * unterscheidet, ist der Grundton und der Winkel des Verlaufs, und genau das
 * setzt jede von ihnen unten als eigene Eigenschaft.
 */
.schiene,
.fassung,
.qr-fassung {
  background-color: var(--messing-grund);
  background-image:
    var(--messing-verlauf),
    url('../assets/img/plate-brass.webp');
  background-size: cover;
  background-position: center;
  background-blend-mode: overlay, normal;
}

/*
 * DIE SCHIENE (Sprintplan S7) — EIN vollbreites Band, klebend beim Scrollen.
 *
 * Sie traegt alles, was in jeder Zone gilt: die Marke, die vier Zonen, die
 * Werkzeuge der laufenden Zone (die Kategorie-Filter der Sammlung), die
 * sichtbare Sprachwahl und das Abmelden. Vorher waren `.nav` und `.filters`
 * zwei Zeilen an zwei Orten, und die Filter gehoerten der Ansicht.
 */
.schiene {
  position: sticky;
  top: 0;
  z-index: 40;
  display: flex;
  align-items: center;
  gap: 1rem;
  min-height: var(--schiene-hoehe);
  padding: 0.5rem clamp(0.75rem, 1.6vw, 1.5rem);
  --messing-grund: #241c11;
  --messing-verlauf: linear-gradient(180deg, rgba(243, 233, 215, 0.12), rgba(0, 0, 0, 0.5));
  border-bottom: 1px solid var(--brass-dark);
  box-shadow:
    inset 0 1px 0 rgba(211, 179, 124, 0.3),
    0 0.6rem 1.4rem rgba(0, 0, 0, 0.55);
}

/* Die Marke des Raums ist die Wortmarke (Sprintplan S13) — dieselbe wie auf
   der Anmeldung, nur klein. Sie steht in `.wortmarke--schiene` weiter unten;
   hier bleibt nur, dass sie in der Schiene nicht mitschrumpft. */
.schiene > .wortmarke {
  flex: 0 0 auto;
}

.schiene__rechts {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  flex: 0 0 auto;
}

/*
 * Die Werkzeuge der laufenden Zone. WAAGERECHT ROLLEND statt umbrechend:
 * die Schiene muss oberhalb der Inspektor-Schwelle ihre Hoehe halten, weil
 * der Inspektor darunter klebt (`--schiene-hoehe`). Ein Umbruch verschoebe
 * ihn lautlos.
 */
.schiene__werkzeuge {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-width: 0;
  overflow-x: auto;
  scrollbar-width: thin;
}

.schiene__werkzeuge:empty {
  display: none;
}

.schiene__werkzeuge .filters {
  margin: 0;
  flex-wrap: nowrap;
}

.nav {
  display: flex;
  align-items: center;
  gap: 0.35rem;
  flex: 0 0 auto;
}

.nav a {
  color: var(--text-secondary);
  text-decoration: none;
  padding: 0.35rem 0.75rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
}

.nav a:hover {
  color: var(--text-primary);
  border-color: rgba(176, 141, 87, 0.4);
}

.nav a.active {
  color: #1a1206;
  background: var(--amber);
  border-color: var(--amber);
}

/*
 * DIE SICHTBARE SPRACHWAHL (Sprintplan S7, Kapitaens-Entscheid E3).
 *
 * Bis zum 22.08.2026 kam die Sprache allein aus `navigator.language`: ein
 * deutscher Nutzer mit englischem Browser bekam die englische Oberflaeche
 * ohne Ausweg. Die Mechanik (`js/strings.js`) war da, es fehlte das
 * Bedienelement. Zwei Knoepfe statt eines Umschalters, damit der aktuelle
 * Zustand ABLESBAR ist und nicht erraten werden muss.
 */
.sprache {
  display: flex;
  align-items: center;
  border: 1px solid var(--brass-dark);
  border-radius: var(--radius);
  overflow: hidden;
}

.sprache button {
  border: none;
  border-radius: 0;
  background: transparent;
  color: var(--text-secondary);
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 0.3rem 0.55rem;
}

.sprache button + button {
  border-left: 1px solid var(--brass-dark);
}

.sprache button[aria-pressed='true'] {
  background: var(--amber);
  color: #1a1206;
}

/*
 * DIE FLAECHE: Fassung — Buehne — (Inspektor) — Fassung.
 *
 * Die Spaltenzahl steht in genau EINER Regel je Zustand. Der Inspektor
 * bekommt seine Spalte erst ab 80rem (1280 px); darunter faellt er weg und
 * die Sammlung nimmt wieder ihre Ueberlagerung (`.karten-detail`).
 */
.raum__flaeche {
  display: grid;
  grid-template-columns: var(--fassung) minmax(0, 1fr) var(--fassung);
  flex: 1 1 auto;
  min-height: calc(100vh - var(--schiene-hoehe));
}

/*
 * DIE MESSINGFASSUNG ALS MOEBEL (Sprintplan S6, Kapitaens-Entscheid E1).
 *
 * Sie ist ein sichtbares Stueck mit eigener Kante, kein auslaufender
 * Farbverlauf: innen ein heller Messingstrich, aussen eine dunkle Naht, dazu
 * die Materialplatte dazwischen. Das ist der Unterschied, den der Entscheid
 * benennt — bei 1920 px muss man sehen, WO das Moebel aufhoert.
 */
.fassung {
  --messing-grund: #2a2114;
  --messing-verlauf: linear-gradient(
    90deg,
    rgba(0, 0, 0, 0.65),
    rgba(243, 233, 215, 0.14) 45%,
    rgba(0, 0, 0, 0.6)
  );
}

.fassung--links {
  box-shadow:
    inset -1px 0 0 rgba(211, 179, 124, 0.45),
    inset -2px 0 0 rgba(0, 0, 0, 0.75),
    1px 0 1.5rem rgba(0, 0, 0, 0.5);
}

.fassung--rechts {
  box-shadow:
    inset 1px 0 0 rgba(211, 179, 124, 0.45),
    inset 2px 0 0 rgba(0, 0, 0, 0.75),
    -1px 0 1.5rem rgba(0, 0, 0, 0.5);
}

.buehne {
  min-width: 0;
  padding: var(--gap) clamp(0.75rem, 1.6vw, 1.75rem);
}

/*
 * DER INSPEKTOR (Sprintplan S8) — ab 1280 px eine rechte SPALTE statt einer
 * Ueberlagerung ueber der Sammlung.
 *
 * Er ist Teil des Raums und nicht der Sammlung: er steht auch dann, wenn
 * gerade keine Karte gewaehlt ist (dann mit benanntem Leer-Zustand,
 * `AGENTS.md` § 0.8 — kein Ersatzbild), und er ueberlebt einen Zonenwechsel.
 *
 * Was er zeigt, ist die EINE Kartendarstellung (`app/gen/kartenrenderer.js`).
 * Fundort, Stufe und Lore stehen auf der ZERTIFIKATS-RUECKSEITE dieser Karte
 * und werden hier NICHT ein zweites Mal als Text gesetzt — das waere die
 * zweite Kartendarstellung, gegen die `AGENTS.md` § 0.8 steht und die
 * `app/tests/sammlung.test.mjs` verbietet.
 */
.inspektor {
  display: none;
}

@media (min-width: 80rem) {
  .raum__flaeche {
    grid-template-columns: var(--fassung) minmax(0, 1fr) var(--inspektor-breite) var(--fassung);
  }

  .inspektor {
    display: block;
    align-self: start;
    position: sticky;
    top: calc(var(--schiene-hoehe) + var(--gap));
    margin: var(--gap) clamp(0.75rem, 1.6vw, 1.75rem) var(--gap) 0;
    padding: calc(var(--gap) * 1.1);
    border: 1px solid var(--brass-dark);
    border-radius: var(--radius);
    background-color: var(--surface);
    background-image: linear-gradient(180deg, rgba(243, 233, 215, 0.05), rgba(0, 0, 0, 0.26));
    box-shadow:
      inset 0 1px 0 rgba(211, 179, 124, 0.16),
      0 1.25rem 2.5rem rgba(0, 0, 0, 0.42);
  }
}

.inspektor__kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

.inspektor__kopf h2 {
  font-size: 1.05rem;
  margin: 0;
}

.inspektor__buehne {
  margin-top: 0.75rem;
  display: flex;
  justify-content: center;
}

.inspektor__hinweis {
  margin: 0.75rem 0 0;
  font-size: 0.85rem;
  color: var(--text-secondary);
}

/* ── Knoepfe ── */

button {
  font: inherit;
  cursor: pointer;
  border-radius: var(--radius);
  border: 1px solid var(--brass-dark);
  padding: 0.5rem 1rem;
  background-color: var(--surface-alt);
  background-image: linear-gradient(180deg, rgba(243, 233, 215, 0.07), rgba(0, 0, 0, 0.2));
  color: var(--text-primary);
}

button:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

button.primary {
  background-color: var(--amber);
  background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.22), rgba(0, 0, 0, 0.12));
  border-color: var(--amber);
  color: #1a1206;
  font-weight: 600;
}

button.link {
  background: none;
  border: none;
  color: var(--amber);
  padding: 0.25rem 0.5rem;
}

.chip {
  padding: 0.3rem 0.7rem;
  font-size: 0.9rem;
  white-space: nowrap;
}

.chip.active {
  background-color: var(--amber);
  border-color: var(--amber);
  color: #1a1206;
}

.filters,
.tabs,
.actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: var(--gap) 0;
}

/* ── Anmeldung ── */

.login-panel {
  max-width: 32rem;
  margin: 0 auto;
  text-align: center;
}

/*
 * DIE MESSINGFASSUNG DES QR-CODES (Sprintplan S14, Kapitaens-Entscheid E4).
 *
 * Vorher: `.qr { background: #fff }` — ein weisses Rechteck ohne Fassung,
 * bewusst roh (Recherche § 1.4 b, Befund 2). Der Kapitaen hat die FASSUNG
 * gewaehlt: dasselbe Messing-Rezept wie `.fassung`/`.schiene`
 * (Materialplatte + Verlauf, `background-blend-mode: overlay`), ein heller
 * Innenstrich und eine dunkle Aussenkante wie bei `.panel`, dazu die Blende
 * als Marke — dieselbe wie in der Wortmarke oben, geklont ueber `blende()`
 * (`js/marke.js`), keine zweite Zeichnung davon (`AGENTS.md` § 0.8).
 *
 * DIE MARKE SITZT AUF DEM RAHMEN, NICHT AUF DEM CODE. Sie ist ein Abzeichen
 * an der oberen Kante, zur Haelfte ueber den Rahmen hinausragend
 * (`translate(-50%, -50%)`), und beruehrt `.qr` an keiner Stelle — jede
 * Ueberlappung mit den Modulen selbst waere genau das Risiko, gegen das die
 * Abnahme misst (`qa/web-anmeldung-qr-shots.mjs`): der Code traegt seine
 * eigene Ruhezone (`qr.js`, `quietZone`), und die Fassung packt aussen
 * darum, ohne sie zu verkleinern. `.qr` selbst bleibt UNVERAENDERT das
 * Element, dem `login.js` die Klasse `entwertet` ansetzt — die Fassung liegt
 * eine Ebene darueber und liest an keiner Pruefung mit.
 */
.qr-fassung {
  position: relative;
  width: fit-content;
  margin: calc(var(--gap) + 0.9rem) auto var(--gap);
  padding: 1.7rem 0.85rem 0.85rem;
  border-radius: calc(var(--radius) + 0.4rem);
  --messing-grund: #2a2114;
  --messing-verlauf: linear-gradient(180deg, rgba(243, 233, 215, 0.18), rgba(0, 0, 0, 0.4));
  box-shadow:
    inset 0 1px 0 rgba(211, 179, 124, 0.45),
    inset 0 -2px 0 rgba(0, 0, 0, 0.6),
    0 0.75rem 1.75rem rgba(0, 0, 0, 0.5);
}

.qr-fassung__marke {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  background: radial-gradient(circle at 35% 30%, rgba(243, 233, 215, 0.4), rgba(10, 7, 4, 0.85) 75%);
  box-shadow:
    0 0 0 2px var(--brass-dark),
    0 0.35rem 0.6rem rgba(0, 0, 0, 0.55);
  /* Rein dekorativ — `aria-hidden` in login.js, hier zusaetzlich aus jeder
     Zeigerinteraktion genommen, falls sie je eine Kante ueberlappt. */
  pointer-events: none;
}

.qr-fassung__marke .marke-iris {
  width: 1.5rem;
  height: 1.5rem;
}

.qr {
  width: min(16rem, 70vw);
  margin: 0;
  background: #fff;
  padding: 0.5rem;
  border-radius: var(--radius);
}

.qr svg {
  display: block;
  width: 100%;
  height: auto;
}

/* Ein abgelaufener Code muss im BILD tot aussehen, nicht nur in der Beschriftung
   darunter — sonst steht ein weiterhin scharf gezeichneter Code neben „Der
   Code ist abgelaufen". Das Verfahren bleibt davon unberuehrt: `login.js` setzt
   allein die Klasse `entwertet` (dort begruendet, `entwerteBild()`), an keiner
   Frist und keiner Pruefung haengt sie.
   Der Daempfungs-Filter liegt auf dem SVG, nicht auf `.qr` selbst: auf `.qr`
   daempfte er auch dessen eigenes `::after` mit, und der Entwertungs-Strich in
   var(--danger) kaeme als dunkler Haarstrich statt als sichtbares Rot an. Der
   weisse Rand von `.qr` wird deshalb direkt gesetzt (#fff unter
   brightness(0.35) ergibt #595959), nicht gefiltert. */
.login-panel .qr.entwertet {
  position: relative;
  background: #595959;
}

.login-panel .qr.entwertet svg {
  filter: brightness(0.35) grayscale(0.7);
}

.login-panel .qr.entwertet::after {
  content: '';
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom right,
    transparent calc(50% - 2px),
    var(--danger) calc(50% - 2px),
    var(--danger) calc(50% + 2px),
    transparent calc(50% + 2px)
  );
  pointer-events: none;
}

/* Das Ziel ist die Angabe, gegen die der Nutzer prueft, was sein Telefon
   gleich anzeigt — deshalb so ausgezeichnet und nicht als graue Zeile. */
.target {
  font-size: 1.05rem;
}

.target strong {
  color: var(--amber);
  overflow-wrap: anywhere;
}

.countdown {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

.status {
  min-height: 1.5em;
  color: var(--text-secondary);
}

/* Der Grund, warum jemand wieder auf dem Anmeldebildschirm steht („Die
   Sitzung ist abgelaufen"). Er steht ueber dem Code und traegt den Rahmen, den
   sonst nur Fehler tragen — ohne ihn wirkt der Rueckwurf wie ein Aussetzer der
   Seite. `hidden` blendet ihn aus, solange es nichts zu sagen gibt. */
.notice {
  border: 1px solid var(--brass-dark);
  border-left: 3px solid var(--amber);
  padding: 0.6rem 0.8rem;
  margin-bottom: var(--gap);
  text-align: left;
  color: var(--text-secondary);
}

.notice[hidden] {
  display: none;
}

.security {
  text-align: left;
  margin-top: var(--gap);
  border-top: 1px solid var(--brass-dark);
  padding-top: 0.75rem;
}

.security summary {
  cursor: pointer;
  color: var(--text-secondary);
}

.device {
  margin-top: var(--gap);
  color: var(--text-muted);
  font-size: 0.85rem;
  font-variant-numeric: tabular-nums;
}

/* ── Die gravierte Zierlinie (Sprintplan S12, Kapitaens-Entscheid E2) ─────
 *
 * EIN Element fuer JEDE Abschnittsgliederung des Bereichs — die Sammlung
 * gliedert damit nach Kategorie, der Deckbau trennt damit seinen Vorrat ab.
 * Der Baustein steht in `js/zierlinie.js`, und dort steht auch, warum sie
 * gezeichneter Stil ist und kein Bild (LC-691).
 *
 * DIE GRAVUR ist die Schrift-Fassung dessen, was `frontend/src/theme/engrave.ts`
 * als `dark` beschreibt („heller GUSS — Metall-Glyphe, Lichtkante oben,
 * Kernschatten unten"): auf unserem dunklen Material liegt die Kante oben,
 * der Kernschatten unten. Kein Bernstein — die Zierlinie ist Gliederung, und
 * Bernstein bleibt der Interaktion vorbehalten (`web/AGENTS.md`).
 *
 * DIE FLUEGEL sind ein Verlauf und keine Linie: eine durchgezogene 1-px-Regel
 * endet hart an der Schrift, ein zur Mitte hin heller werdender Strich
 * laeuft in sie hinein. Zwei Ebenen uebereinander — der helle Grat und der
 * Schatten darunter — machen daraus eine eingelassene Rille statt eines
 * aufgeklebten Balkens. */

.zierlinie {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin: calc(var(--gap) * 1.1) 0 0.6rem;
}

.zierlinie__fluegel {
  flex: 1 1 0;
  height: 3px;
  min-width: 1.5rem;
  background-image:
    linear-gradient(90deg, transparent, rgba(211, 179, 124, 0.55)),
    linear-gradient(90deg, transparent, rgba(0, 0, 0, 0.75));
  background-size: 100% 1px, 100% 1px;
  background-position: 0 1px, 0 2px;
  background-repeat: no-repeat;
}

/* Der rechte Fluegel spiegelt den linken, damit der helle Grat zur Mitte hin
   zulaeuft und nicht von ihr weg. */
.zierlinie__fluegel:last-child {
  transform: scaleX(-1);
}

.zierlinie__wort {
  flex: 0 1 auto;
  margin: 0;
  font-family: 'Oswald', system-ui, sans-serif;
  font-weight: 600;
  font-size: 0.95rem;
  line-height: 1.2;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  color: var(--brass-light);
  text-shadow:
    0 -1px 0 rgba(255, 246, 224, 0.22),
    0 1px 1px rgba(0, 0, 0, 0.75);
}

/* ── Die Abschnitte der Sammlung (Sprintplan S12) ── */

.sammlung-abschnitte {
  margin-top: 0.25rem;
}

/* Der ERSTE Abschnitt braucht oben keinen Abstand — darueber steht schon die
   Kartenzahl der Sammlung. */
.sammlung-abschnitt:first-child .zierlinie {
  margin-top: 0.4rem;
}

/* ── Karten ──────────────────────────────────────────────────────────────
 *
 * HIER STAND BIS ZUM 22.08.2026 DIE KARTE ALS TEXTZEILE: `.cards`, `.card`,
 * die vier `.card.tier-*`-Streifen, `.attrs` — und darunter `.cards.selectable`
 * mit dem Ankreuz-Deckbau. Alles davon ist mit S10 (Bauauftrag B3) gefallen,
 * weil die letzte Ansicht, die es benutzt hat, jetzt die EINE
 * Kartendarstellung rendert (`js/deckView.js`).
 *
 * Die Regeln STEHEN ZU LASSEN waere kein Nullzustand gewesen: eine fertige
 * Stilvorlage fuer eine Kartenzeile ist die Einladung, die Zeile beim
 * naechsten Mal wieder zu bauen — genau die Bauart, gegen die `AGENTS.md`
 * § 0.8 steht und die LC-690 abgeraeumt hat. Was eine Karte zeigt,
 * entscheidet der Renderer; hier steht nur noch, WO er arbeitet. */

/* ── Die ECHTEN Karten (LC-007) ── */

/*
 * Der Renderer (`app/gen/kartenrenderer.js`) bringt seine eigenen Masse mit —
 * hier steht nur, WO er arbeitet. Eine Kartenfarbe, ein Rahmen oder ein
 * Schatten gehoerte in die Karte selbst, nicht daneben (AGENTS.md § 0.8).
 */
.karten {
  margin-top: 0.75rem;
}

/*
 * DIE UEBERLAGERUNG bleibt — aber nur noch UNTERHALB von 1280 px. Ab dort
 * oeffnet die Sammlung in den Inspektor, und dort verdeckt sie das Raster
 * nicht mehr (Sprintplan S8). Welcher der beiden Wege gilt, entscheidet
 * `js/app.js` (`raum.inspektor.breit()`), nicht diese Datei — sonst laegen
 * zwei Schwellen an zwei Orten.
 */
.karten-detail {
  position: fixed;
  inset: 0;
  z-index: 60;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  padding: 1.5rem;
  overflow-y: auto;
  background: rgba(12, 10, 8, 0.92);
}

.karten-detail-schliessen {
  /* Oben rechts AM BILDSCHIRM, nicht in der Mittelachse der Karte: in einer
     senkrecht zentrierten Spalte landete `align-self: flex-end` auf halber
     Hoehe neben dem Nichts. */
  position: absolute;
  top: 1.5rem;
  right: 1.5rem;
  /* Eigene Flaeche statt durchsichtig: auf einem Telefon liegt der Knopf
     ueber der abgedunkelten Navigationsleiste, und zwei Beschriftungen
     uebereinander lesen sich als Fehler. */
  background: var(--surface);
  border: 1px solid var(--brass-dark);
  border-radius: var(--radius);
  color: var(--text-secondary);
  cursor: pointer;
  font: inherit;
  padding: 0.35rem 0.8rem;
}

.karten-detail-schliessen:hover {
  border-color: var(--brass);
  color: var(--text-primary);
}

.karten-detail-hinweis {
  font-size: 0.85rem;
  text-align: center;
}

/* ── Deckbau (Sprintplan S10) ────────────────────────────────────────────
 *
 * Die Auswahl selbst zeichnet der Renderer: eine gewaehlte Karte HEBT sich
 * (`frontend/src/webkarte/Ansicht.tsx`), eine gesperrte ist gedaempft. Hier
 * steht nur der Vorrat um sie herum — und ausdruecklich keine Kartenfarbe,
 * kein Rahmen, kein Stufenstreifen (`AGENTS.md` § 0.8). */

.deck-vorrat {
  margin-top: calc(var(--gap) * 1.25);
}

.summary {
  font-variant-numeric: tabular-nums;
  color: var(--text-secondary);
}

.summary.ok {
  color: var(--success);
}

.problems {
  color: var(--danger);
  margin: 0.5rem 0;
  padding-left: 1.25rem;
}

.note.ok {
  color: var(--success);
}

.note.error {
  color: var(--danger);
}

.empty {
  color: var(--text-muted);
}

/*
 * ── DER ERSTLAUF: NULL KARTEN (Sprintplan S15) ─────────────────────────
 *
 * Der zweite Bildschirm eines Erstbesuchers. Er beantwortet dieselben drei
 * Fragen wie der Kampfeinstieg — WAS ist das, WARUM will ich es, WAS tue ich
 * als naechstes — und wird nach demselben Mass abgenommen: die Unterkanten
 * von `.erstlauf-warum`, dem letzten `.erstlauf-schritt` und `.erstlauf-tat`
 * muessen ueber der Falz liegen (`qa/web-erstlauf-shots.mjs`).
 *
 * DESHALB IST HIER NICHTS ZENTRIERT UND NICHTS AUSEINANDERGEZOGEN: jede
 * Zeile Luft, die hier grosszuegig aussaehe, schiebt die dritte Antwort unter
 * den Fensterrand. Die Bauteile sind bewusst dieselben wie beim
 * Kampfeinstieg (Zahl in Messing, Kasten je Schritt) — ein zweites
 * Einstiegs-Aussehen fuer dieselbe Sorte Bildschirm waere die Fehlerfamilie
 * aus `AGENTS.md` § 0.8, eine Ebene ueber der Karte.
 *
 * Das Grundbild ist das Kabinettzimmer (`scene-web-sammlung.webp`, Serie 11)
 * — 89,7 % flaches Schwarz standen hier vorher. Es liegt auf DIESEM Kasten
 * und nicht auf der Buehne: die Buehne gehoert der Huelle und traegt in jeder
 * anderen Zone das Leder.
 */
.erstlauf {
  position: relative;
  overflow: hidden;
  border: 1px solid var(--brass-dark);
  border-radius: var(--radius);
  padding: clamp(1rem, 2.4vw, 2rem);
  /*
   * VOLLFLAECHIG, nicht als Kachel im oberen Drittel (Sprintplan S13
   * woertlich: „ein vollflaechiges Szenenbild"). Ohne diese Zeile stand das
   * Kabinettzimmer in einem halbhohen Kasten und darunter wieder die leere
   * Flaeche, gegen die der ganze Auftrag geschrieben ist.
   *
   * Gerechnet statt `height: 100%`: die Kette Buehne -> Ansicht -> Kasten
   * traegt keine durchgehende Hoehe, und eine Hoehe je Glied nachzureichen
   * hiesse, die Huelle fuer eine Zone umzubauen. Die zwei Groessen, die
   * abgezogen werden, sind die der Huelle selbst und stehen als Variablen
   * schon da.
   */
  min-height: calc(100vh - var(--schiene-hoehe) - 2 * var(--gap));
  box-shadow:
    inset 0 1px 0 rgba(211, 179, 124, 0.16),
    0 1.25rem 2.5rem rgba(0, 0, 0, 0.42);
  background-color: #100d09;
  /* Der Schleier ist OBEN am dichtesten und laeuft nach unten aus: oben steht
     der Text, unten das Motiv. Andersherum — und so stand es zuerst — deckt
     er ausgerechnet das Kabinettzimmer zu und laesst den Text auf der
     hellsten Stelle stehen. */
  background-image:
    linear-gradient(180deg, rgba(10, 7, 4, 0.86) 0%, rgba(10, 7, 4, 0.7) 42%, rgba(10, 7, 4, 0.3) 100%),
    url('../assets/img/scene-web-sammlung.webp');
  background-size: cover;
  background-position: center;
}

.erstlauf h1 {
  margin-bottom: 0.25rem;
}

.erstlauf .lead {
  margin: 0 0 0.6rem;
  max-width: 46rem;
}

.erstlauf-warum {
  color: var(--text-primary);
  margin: 0 0 1rem;
  max-width: 46rem;
}

.erstlauf-schritte {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
  /* Wie beim Kampfeinstieg: nebeneinander, wo Platz ist, sonst
     untereinander — am Telefon eine Spalte, und dann muessen die drei
     Zeilen trotzdem ueber der Falz stehen. */
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.erstlauf-schritt {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgba(176, 141, 87, 0.28);
  border-radius: var(--radius);
  background-color: rgba(58, 49, 38, 0.72);
  background-image: linear-gradient(180deg, rgba(243, 233, 215, 0.05), rgba(0, 0, 0, 0.18));
}

.erstlauf-schritt__n {
  font-family: 'Oswald', system-ui, sans-serif;
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--brass);
  flex: 0 0 auto;
  min-width: 1ch;
}

.erstlauf-schritt p {
  margin: 0;
}

/*
 * DIE TAT — die Antwort auf „was tue ich als naechstes".
 *
 * Bernstein als Kante, nicht als Flaeche: die Signalfarbe markiert
 * Interaktion (`web/AGENTS.md`), und dies ist keine — der Browser kann nicht
 * scannen. Ein voller Bernstein-Kasten saehe aus wie ein Knopf und waere die
 * Luege, die `js/collection.js` an dieser Stelle ausdruecklich vermeidet.
 */
.erstlauf-tat {
  border: 1px solid var(--brass-dark);
  border-left: 3px solid var(--amber);
  border-radius: var(--radius);
  padding: 0.7rem 0.9rem;
  background: rgba(10, 7, 4, 0.55);
  margin-bottom: 0.6rem;
}

.erstlauf-tat__satz {
  margin: 0 0 0.25rem;
  font-weight: 600;
  color: var(--text-primary);
}

.erstlauf-tat .muted {
  margin: 0;
}

.erstlauf-haus {
  margin: 0;
}

.erstlauf-haus a {
  color: var(--amber);
}

/* ── Bestellstrecke ── */

/* Der Riegel steht oben und ist nicht zu uebersehen — eine Strecke, die erst
   auf der letzten Seite zugibt, dass sie nicht liefert, hat den Besteller
   bis dahin Daten eingeben lassen. */
.warning {
  background: rgba(192, 57, 43, 0.15);
  border: 1px solid var(--danger);
  border-radius: var(--radius);
  padding: 0.75rem;
  color: var(--text-primary);
}

.form {
  display: grid;
  gap: 0.65rem;
  max-width: 28rem;
}

.field {
  display: grid;
  gap: 0.2rem;
}

.field span {
  font-size: 0.85rem;
  color: var(--text-secondary);
}

input {
  font: inherit;
  padding: 0.45rem 0.6rem;
  border-radius: var(--radius);
  border: 1px solid var(--brass-dark);
  background: rgba(12, 10, 8, 0.7);
  color: var(--text-primary);
}


.quote table {
  border-collapse: collapse;
  margin-top: var(--gap);
  min-width: 18rem;
}

.quote th {
  text-align: left;
  font-weight: 400;
  color: var(--text-secondary);
  padding: 0.2rem 1rem 0.2rem 0;
}

.quote td {
  text-align: right;
  font-variant-numeric: tabular-nums;
}

.quote tr.total th,
.quote tr.total td {
  border-top: 1px solid var(--brass-dark);
  font-weight: 600;
  color: var(--text-primary);
}

.estimate {
  color: var(--text-muted);
  font-size: 0.85rem;
}

.orders ul {
  list-style: none;
  padding: 0;
}

.orders li {
  border-top: 1px solid var(--brass-dark);
  padding: 0.4rem 0;
  font-size: 0.9rem;
}

.orders .id {
  font-family: ui-monospace, monospace;
  color: var(--text-secondary);
}

/* ── Bewegung ── */

@media (prefers-reduced-motion: reduce) {
  * {
    animation-duration: 150ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 150ms !important;
  }
}

/* ── Der Uebungskampf (W3, LC-654) ──────────────────────────────────────
 *
 * Die Buehne selbst zeichnet der erzeugte Renderer (`app/gen/`), nicht diese
 * Datei — hier steht nur, was um sie herum liegt: der Einstieg, der die drei
 * Fragen beantwortet (WAS · WAS TUN · WARUM), und der Rahmen waehrend des
 * Kampfes.
 *
 * Die Schrittkarten sind die DICHTE Fassung von `.step`/`.step__n` der
 * Marketingseite (`web/styles.css`): dieselbe Messing-Ziffer, dieselbe
 * Kontur — aber waagerecht statt hochkant, weil dieser Bereich ein
 * Arbeitsplatz ist und die drei Zeilen ueber dem Knopf stehen muessen, ohne
 * dass jemand scrollt.
 */

.kampf-panel h1 {
  margin-bottom: 0.25rem;
}

.kampf-warum {
  color: var(--text-primary);
  margin: 0 0 1rem;
}

.kampf-schritte {
  list-style: none;
  margin: 0 0 1.1rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
  /* Wie `.steps` der Marketingseite: nebeneinander, wo Platz ist, sonst
     untereinander. Am Telefon bleibt es eine Spalte — die drei Zeilen
     muessen ueber der Falz stehen. */
  grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
}

.kampf-schritt {
  display: flex;
  align-items: baseline;
  gap: 0.7rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid rgba(176, 141, 87, 0.28);
  border-radius: var(--radius);
  background-color: var(--surface-alt);
  background-image: linear-gradient(180deg, rgba(243, 233, 215, 0.05), rgba(0, 0, 0, 0.18));
}

.kampf-schritt__n {
  font-family: 'Oswald', system-ui, sans-serif;
  font-weight: 700;
  font-size: 1.5rem;
  line-height: 1;
  color: var(--brass);
  flex: 0 0 auto;
  min-width: 1ch;
}

.kampf-schritt p {
  margin: 0;
}

.kampf-start {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}

/* Der EINE Bernstein-Knopf dieses Bildschirms — die Signalfarbe markiert
   Interaktion, nie Dekoration (`web/AGENTS.md`). */
.kampf-los {
  font-size: 1.05rem;
  padding: 0.7rem 1.4rem;
}

.kampf-aufstellung {
  margin: 0 0 0.75rem;
}

.kampf-aufgebot {
  /* BEWUSST KEIN `display: flex` — das Raster legt der Renderer selbst
     (`webkarte/Ansicht.tsx`s `styles.raster`). Ein Flex-Container hier machte
     seinen Wurzelknoten zum Flex-ITEM: der schrumpft dann auf seinen Inhalt
     statt auf die Tafel, und die Reihe lief auf einem 390-px-Telefon
     gemessene 180 px seitlich hinaus, ohne umzubrechen. */
  margin-top: 0.25rem;
}

/*
 * Auf einem schmalen Telefon bricht die Schiene um — dort gibt es weder
 * Fassung noch Inspektor, und ihre Hoehe haengt an nichts mehr.
 */
@media (max-width: 48rem) {
  /* Und die Fassung faellt weg: acht Pixel Messing links und rechts sind auf
     einem Telefon kein Moebel mehr, sondern vier Prozent weniger Karte. */
  :root {
    --fassung: 0px;
  }

  .schiene {
    flex-wrap: wrap;
    gap: 0.5rem 0.75rem;
  }

  .schiene__werkzeuge {
    order: 3;
    width: 100%;
  }

  .nav a {
    padding: 0.35rem 0.5rem;
  }
}

@media (max-width: 30rem) {
  .wortmarke--schiene .wortmarke__schrift {
    font-size: 0.9rem;
    letter-spacing: 0.14em;
  }

  .wortmarke--schiene .marke-iris {
    width: 1.05rem;
    height: 1.05rem;
  }
}

.kampf-kopf {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

/*
 * DIE KAMPFBUEHNE FUELLT DEN RAUM (Sprintplan S11, Bauauftrag B3).
 *
 * Hier stand bis zum 22.08.2026 `max-width: 34rem` mit der Begruendung „ein
 * Brett, kein Feld": ohne Obergrenze staenden zwei Karten verloren in der
 * Mitte einer leeren Flaeche. Die Begruendung war richtig und ist erledigt —
 * die Flaeche ist nicht mehr leer. Der gemalte Kategorie-Schauplatz und die
 * Buehnenvignette fuhren schon im Buendel mit und waren in diese 544 px
 * eingesperrt, waehrend 1376 px des Fensters schwarz blieben (Recherche
 * § 1.4 c). Montiert wird beides im Buendel
 * (`frontend/src/webkarte/kampf/KampfBuehne.tsx`); hier faellt nur die Kappe.
 *
 * Die KARTEN wachsen dabei NICHT mit: ihre Obergrenze steht in
 * `frontend/src/webkarte/kampf/rundenbild.ts` (`BUEHNEN_KARTE_MAX`) und ist
 * dort mit der Leserichtung begruendet — das Vergleichsfeld steht unter den
 * Karten, und was ueber die Fensterhoehe hinausragt, sieht niemand. Die
 * Buehne wird breiter, die Karten bleiben lesbar.
 */
.kampf-buehne {
  margin: 0;
}

/*
 * DIE LAUFENDE KAMPFTAFEL HAT KEINEN INNENABSTAND. Der Schauplatz IST ihre
 * Oberflaeche und reicht bis an die Messingkante; ein Innenabstand liesse
 * ringsum einen Streifen Tafelfarbe stehen, und genau der macht aus einer
 * Buehne wieder einen Kasten. Kopf und Fuss bringen ihren Abstand deshalb
 * selbst mit. `overflow: hidden` haelt den Schauplatz in der Rundung.
 */
.kampf-panel--laeuft {
  padding: 0;
  overflow: hidden;
}

.kampf-panel--laeuft .kampf-kopf,
.kampf-panel--laeuft .kampf-fuss {
  padding: calc(var(--gap) * 0.75) calc(var(--gap) * 1.25);
}

.kampf-fuss {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}
