/* Inventar DA Elektrotechnik -- Stylesheet
 *
 * Mobile zuerst: die App wird im Fahrzeug und auf der Baustelle bedient,
 * der Desktop ist der Sonderfall. Alles ausserhalb einer Media Query gilt
 * fuer das Handy; @media (min-width: 768px) macht es fuer Tablet/Desktop
 * grosszuegiger.
 *
 * Leitgedanke seit dem Umbau am 2026-08-08: Informationsdichte vor
 * Bildern. Vorher war jeder Artikel eine Karte mit 120px hohem Bild --
 * auf dem Handy passten zwei Artikel auf den Schirm. Bei mehreren tausend
 * Artikeln ist das unbenutzbar. Jetzt ist die Liste eine Liste, das Bild
 * ein 48px-Vorschaubild, und es passen zehn Zeilen auf den Schirm.
 */

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

:root {
  --text: #1a1a1a;
  --text-schwach: #6b7280;
  --linie: #e5e7eb;
  --flaeche: #ffffff;
  --hintergrund: #f4f5f7;
  --blau: #2563eb;
  --rot: #dc2626;
  --gruen: #166534;
  --warn: #b45309;
  --dunkel: #1f2937;
  --radius: 10px;
  /* Hoehe der festen Navigation unten, inkl. iPhone-Home-Indikator. */
  --tabbar-hoehe: calc(56px + env(safe-area-inset-bottom, 0px));

  /* Die Zahl ist der Hauptdarsteller dieser App -- Bestand, Menge,
     Laenge. Sie stand bisher in derselben Groesse wie Ort, Kategorie und
     Preis daneben und unterschied sich nur durch die Strichstaerke.
     Eigene Groesse, damit sie beim Ueberfliegen einer Liste zuerst
     gelesen wird und nicht als vierte Angabe einer Zeile. */
  --zahl: 0.87rem;

  /* Die Klemmleiste: eine farbige Kante links an einer Flaeche sagt,
     worum es dort geht. Sie kostet keine Hoehe -- der entscheidende
     Punkt auf einem 812px hohen Schirm -- und ist im Augenwinkel
     lesbar, waehrend man scrollt. Die Bedeutungen sind fest:
       blau  = hier wird gehandelt (Buchungsblock)
       gruen = gefunden, bestaetigt (Treffer, guenstigster Anbieter)
       warn  = will Aufmerksamkeit (unter Mindestbestand)
       linie = gehoert dazu (Kofferinhalt) */
  --leiste: 4px;
}

html { -webkit-text-size-adjust: 100%; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  margin: 0;
  color: var(--text);
  background: var(--hintergrund);
  font-size: 15px;
  line-height: 1.4;
  /* Kein waagerechtes Scrollen -- egal was ein einzelnes Element tut. */
  overflow-x: hidden;
}

h1 { font-size: 1.35rem; margin: 0 0 10px; }
h2 { font-size: 1.05rem; margin: 20px 0 8px; }
h3 { font-size: 0.95rem; margin: 14px 0 6px; }
p { margin: 0 0 10px; }
a { color: var(--blau); }

.container {
  max-width: 900px;
  margin: 0 auto;
  padding: 12px 12px 20px;
}
/* Platz fuer die feste Navigation unten, damit der letzte Listeneintrag
   nicht darunter verschwindet. */
body.has-tabbar .container { padding-bottom: calc(var(--tabbar-hoehe) + 16px); }

/* --- Kopfzeile ------------------------------------------------------ */
/* Auf dem Handy genau eine Zeile: Name links, Scan rechts. Die alte
   Fassung stapelte Marke, sechs Navigationspunkte und das Benutzermenue
   uebereinander und ass rund 120px Hoehe -- ein Drittel des sichtbaren
   Bereichs, bevor der erste Artikel kam. */
.topbar {
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px; background: var(--dunkel); color: white;
  padding-top: calc(10px + env(safe-area-inset-top, 0px));
}
/* Der Name steht als Schild: die Anwendung in Weiss, der Betrieb
   gedaempft dahinter, getrennt durch einen Haarstrich. Das ist die
   Schreibweise, in der in diesem Gewerk ohnehin beschriftet wird -- auf
   einem Zaehlerschrank steht auch nicht ein Satz, sondern ein Feld und
   sein Kennzeichen. Nebenbei loest es ein gemessenes Problem: als ein
   Textstueck war der Name 263 von 375 Pixeln breit, und die Auslassung
   frass den Betriebsnamen. */
.topbar .brand {
  color: white; text-decoration: none;
  flex: 1; min-width: 0;
  display: flex; align-items: baseline; gap: 8px;
  /* Volle Kopfzeilenhoehe als Tippziel, nicht nur die Texthoehe. */
  min-height: 34px; padding: 8px 0; margin: -8px 0;
}
.brand-app {
  font-weight: 600; font-size: 0.95rem; flex: none;
}
/* Der Betrieb weicht zuerst: Kleinbuchstaben mit Sperrung, gedaempft,
   und er ist es, der auf einem schmalen Geraet in die Auslassung geht.
   `min-width: 0` ist die Bedingung dafuer -- ohne sie schrumpft ein
   Flexelement nicht unter seine Textbreite. */
.brand-marke {
  font-size: 0.72rem; font-weight: 500; letter-spacing: 0.06em;
  text-transform: uppercase; color: rgba(255,255,255,0.62);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  /* Der Haarstrich zwischen beiden -- dieselbe Trennung wie zwischen den
     Angaben einer Zeile, nur senkrecht. */
  border-left: 1px solid rgba(255,255,255,0.25); padding-left: 8px;
}
.topnav { display: none; }
/* .topbar-scan ist mit A51 entfernt (14.08.2026) -- der Knopf zeigte auf
   dasselbe Ziel wie der mittlere Reiter unten. */

/* --- Feste Navigation unten ----------------------------------------- */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 50;
  display: flex; background: var(--flaeche);
  border-top: 1px solid var(--linie);
  padding-bottom: env(safe-area-inset-bottom, 0px);
  box-shadow: 0 -1px 4px rgba(0,0,0,0.06);
}
.tabbar a {
  flex: 1; min-width: 0;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 2px; padding: 7px 2px; min-height: 56px;
  color: var(--text-schwach); text-decoration: none;
  font-size: 0.68rem; line-height: 1.1; text-align: center;
}
.tabbar a.aktiv { color: var(--blau); font-weight: 600; }

/* --- Symbole -------------------------------------------------------- */
/* Strichzeichnungen statt Emoji (siehe templates/_symbole.html fuer das
   Warum). `currentColor` ist der Grund fuer den Wechsel: im aktiven
   Reiter faerbt sich jetzt auch das Symbol blau, vorher blieb es bunt
   und der Zustand stand nur an der Beschriftung. */
.sym {
  width: 20px; height: 20px; flex: none;
  display: inline-block; vertical-align: -0.22em;
}
/* Symbol vor Fliesstext: kleiner und auf die Grundlinie gesetzt, damit
   die Zeile nicht auseinanderlaeuft ("Landet in ...", "fehlt"). */
.sym-text { width: 16px; height: 16px; vertical-align: -0.16em; }
/* In einer Plakette (badge) sitzt es noch eine Spur tiefer und kleiner --
   die Plakette ist nur 0.72rem hoch. */
.sym-badge { width: 14px; height: 14px; vertical-align: -0.18em; }
/* In einem Knopf steht das Symbol vor der Beschriftung. */
.button .sym, button .sym { vertical-align: -0.25em; margin-right: 2px; }
/* Im Reiter etwas groesser: dort ist das Symbol die Hauptsache und die
   Beschriftung die Beigabe. Steht NACH `.sym`, sonst gewinnt bei
   gleicher Spezifitaet die frueher notierte Regel -- aus demselben
   Grund stehen die Masse fuer `.menu-icon`, `.ordner-icon` und
   `.scan-gross-icon` in deren eigenen Abschnitten weiter unten und
   nicht hier. */
.tab-icon { width: 22px; height: 22px; vertical-align: 0; }

/* --- Meldungen ------------------------------------------------------ */
.flashes { list-style: none; padding: 0; margin: 0 0 12px; }
.flash { padding: 9px 12px; border-radius: 8px; margin-bottom: 6px; font-size: 0.88rem; }
.flash-success { background: #dcfce7; color: var(--gruen); }
.flash-error { background: #fee2e2; color: #991b1b; }
.flash-info { background: #dbeafe; color: #1e40af; }
.flash-warn { background: #fef3c7; color: #92400e; }

/* --- Schaltflaechen ------------------------------------------------- */
.button, button {
  background: var(--blau); color: white; border: none;
  padding: 9px 14px; border-radius: 8px; cursor: pointer;
  text-decoration: none; display: inline-block;
  font-size: 0.88rem; font-family: inherit; min-height: 40px;
}
.button-danger, button.button-danger { background: var(--rot); }
.button-still, button.button-still {
  background: var(--flaeche); color: var(--text); border: 1px solid var(--linie);
}
/* 40px statt 34: „Bearbeiten", „Fertig", „Kataloge verwalten" sind echte
   Aktionen und werden mit dem Daumen getroffen, nicht mit der Maus. */
.button-klein, button.button-klein { padding: 6px 12px; min-height: 40px; font-size: 0.8rem; }

.page-header {
  display: flex; justify-content: space-between; align-items: center;
  gap: 10px; flex-wrap: wrap; margin-bottom: 10px;
}
.page-header h1 { margin: 0; min-width: 0; overflow-wrap: anywhere; }
.header-actions { display: flex; gap: 8px; flex-wrap: wrap; }

.hint { color: var(--text-schwach); font-size: 0.82rem; }

/* Nur auf Tablet/Desktop sichtbar.
   Die Regel dahinter (2026-08-09): die Ueberschrift entfaellt auf dem
   Handy genau dann, wenn der aktive Reiter unten sie schon nennt --
   Artikel, Scannen, Wo ist was, Mehr. Jede dieser Zeilen kostet 44px,
   also eine halbe Artikelzeile, ohne etwas zu sagen. Seiten HINTER
   "Mehr" (Kategorien, Kataloge, Katalogsuche, Kupfernotierung ...)
   behalten ihren Titel: dort sagt nichts sonst, wo man gelandet ist. */
.nur-desktop { display: none; }
/* Bleibt die Aktion einer Kopfzeile ohne sichtbare Ueberschrift uebrig,
   soll sie trotzdem rechts stehen. */
.page-header .header-actions { margin-left: auto; }

/* --- Filterleiste --------------------------------------------------- */
.suchzeile { display: flex; gap: 8px; margin-bottom: 8px; }
.suchzeile input[type="search"] {
  flex: 1; min-width: 0; padding: 10px 12px; font-size: 16px; /* 16px: iOS zoomt sonst beim Fokus */
  border: 1px solid var(--linie); border-radius: 8px; background: var(--flaeche);
}
.neu-knopf {
  flex: none; width: 40px; padding: 0; font-size: 1.3rem; line-height: 1;
  display: inline-flex; align-items: center; justify-content: center;
}

/* Waagerecht scrollbare Filterreihe -- vier Filter passen auf einem
   360px-Geraet nicht nebeneinander, sollen aber auch nicht dreizeilig
   umbrechen und den halben Schirm fressen. */
.filterreihe {
  display: flex; gap: 6px; overflow-x: auto; padding-bottom: 6px;
  margin-bottom: 8px; -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.filterreihe::-webkit-scrollbar { display: none; }
.filterreihe select, .chip {
  flex: none; font-size: 0.8rem; font-family: inherit;
  padding: 7px 12px; min-height: 44px;
  border: 1px solid var(--linie); border-radius: 999px;
  background: var(--flaeche); color: var(--text);
  text-decoration: none; display: inline-flex; align-items: center;
  max-width: 60vw;
}
.filterreihe select.gesetzt, .chip.aktiv {
  border-color: var(--blau); background: #eff6ff; color: #1e40af; font-weight: 600;
}
.chip-fav.aktiv { border-color: #f59e0b; background: #fffbeb; color: #92400e; }
.filter-status {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 8px; font-size: 0.78rem; color: var(--text-schwach); margin-bottom: 6px;
}
.filter-status a { color: var(--text-schwach); }

/* --- Verdichtete Zeilenliste ---------------------------------------- */
/* Traegt Artikel, Orte und Ortsinhalte. Eine Zeile ist rund 76px hoch,
   damit auf einem 390px-Geraet ohne Scrollen etwa zehn Stueck sichtbar
   sind. */
.zeilen {
  list-style: none; margin: 0; padding: 0;
  background: var(--flaeche); border: 1px solid var(--linie);
  border-radius: var(--radius); overflow: hidden;
}
.zeile {
  position: relative;
  display: flex; align-items: center; gap: 10px;
  padding: 10px 12px;
  border-bottom: 1px solid var(--linie);
}
.zeile:last-child { border-bottom: none; }
.zeile:active { background: #f9fafb; }

/* Unter Mindestbestand: eine Kante links an der Zeile. Die rote Menge
   allein sagt es erst, wenn man die Zeile liest -- gesucht wird aber
   andersherum, naemlich "welcher von 26 Posten ist knapp". Eine Kante
   ist im Augenwinkel sichtbar, waehrend man scrollt, und kostet im
   Gegensatz zu einer Plakette keine Zeilenhoehe.

   `inset`-Schatten statt `border-left`: ein echter Rahmen verschoebe den
   Inhalt jeder markierten Zeile um 4px gegen ihre Nachbarn. */
.zeile-knapp { box-shadow: inset var(--leiste) 0 0 var(--warn); }

/* Die ganze Zeile ist anklickbar: der Link legt sich unsichtbar ueber
   alles. Elemente, die trotzdem eigene Klicks brauchen (der Stern),
   heben sich mit z-index darueber. */
.zeile-link { position: static; color: inherit; text-decoration: none; flex: 1; min-width: 0; }
.zeile-link::after { content: ""; position: absolute; inset: 0; }

/* Einzeilig mit Auslassung. Zwei Zeilen kosteten 18px je Eintrag -- bei
   DATANORM-Namen wie "Leitungsschutzschalter ABB B16A 3p 400V 3TE 50Hz
   S203-B16 6kA IP20" laufen praktisch ALLE Titel zweizeilig, und aus
   acht sichtbaren Artikeln wurden fuenf. Der vollstaendige Name steht
   eine Berührung weiter auf der Artikelseite. */
.zeile-titel {
  font-weight: 600; font-size: 0.92rem; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zeile-unter, .zeile-meta {
  font-size: 0.78rem; line-height: 1.3;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zeile-unter { color: var(--text-schwach); }
.zeile-meta { color: var(--text); margin-top: 1px; }
/* Der Bestand ist die wichtigste Zahl der Zeile -- wichtiger als der
   Preis, der bewusst unbetont hinten steht.
   Seit 2026-08-10 auch GROESSER und nicht nur fetter: gemessen standen
   Menge, Ort und Preis alle drei bei 12,48px, und die eine Angabe, nach
   der entschieden wird, unterschied sich nur durch die Strichstaerke.
   Die Zeilenhoehe bleibt gleich -- .zeile-meta gibt sie mit 16,2px vor,
   und 13,9px Schrift passen darin. */
/* Feste Zeilenhoehe trotz groesserer Schrift: `.zeile-meta` gibt
   `line-height: 1.3` als ZAHL vor, und eine Zahl vererbt sich als Faktor
   -- die groessere Menge haette ihre Zeile damit um 2px aufgeblaeht, und
   das mal 26 Posten. 1.15 x 13.92px = 16px, genau die Hoehe der uebrigen
   Angaben daneben. */
.zeile-meta .menge { font-weight: 700; font-size: var(--zahl); line-height: 1.15; }
.zeile-meta .menge.knapp { color: var(--rot); }
.zeile-meta .ort { color: var(--text-schwach); }
.zeile-meta .preis { color: var(--text-schwach); }
.zeile-meta .trenner { color: #d1d5db; margin: 0 3px; }

.zeile-thumb {
  flex: none; width: 48px; height: 48px; object-fit: cover;
  border-radius: 6px; background: #f3f4f6; z-index: 1; position: relative;
}
.zeile-chevron { flex: none; color: #c7ccd4; font-size: 1.2rem; line-height: 1; }

/* Favoritenstern. Ohne Bild kein Platzhalter -- fehlt das Bild, ruecken
   die Texte einfach nach rechts durch. */
.fav-form { flex: none; margin: 0; position: relative; z-index: 1; }
/* 44x44 statt 30x40: der Stern wird im Fahrzeug mit dem Daumen getroffen,
   und daneben liegt der Link auf die ganze Zeile -- ein Fehlgriff oeffnet
   also den Artikel, statt zu merken. */
.fav-btn {
  background: none; border: none; padding: 0; min-height: 44px; width: 44px;
  font-size: 1.25rem; line-height: 1; cursor: pointer;
  color: #cbd5e1;
}
.fav-btn[aria-pressed="true"] { color: #f59e0b; }

/* --- Nachladen ------------------------------------------------------ */
.mehr-laden { display: block; width: 100%; margin: 12px 0 0; text-align: center; }

/* Blaettern in der Katalogsuche. Dort waere ein "Weitere anzeigen" wie in
   der Artikelliste irrefuehrend: die Trefferzahl ist unbekannt (ueber vier
   Millionen Katalogzeilen zaehlt niemand fuer eine Ueberschrift), es geht
   nur vor und zurueck. */
.blaettern { display: flex; gap: 8px; margin-top: 12px; }
.blaettern .button { flex: 1; text-align: center; }

/* --- Menueliste (Seite "Mehr") -------------------------------------- */
.menu-list {
  background: var(--flaeche); border: 1px solid var(--linie);
  border-radius: var(--radius); overflow: hidden;
}
.menu-row {
  display: flex; align-items: center; gap: 12px;
  padding: 12px; border-bottom: 1px solid var(--linie);
  color: inherit; text-decoration: none; min-height: 56px;
}
.menu-row:last-child { border-bottom: none; }
/* Breite UND Hoehe: mit nur einer der beiden Angaben zieht sich das
   Symbol in die Laenge -- auf dem Desktop stand es als 24x22 da. */
.menu-icon { flex: none; width: 22px; height: 22px; }
.menu-text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.menu-text small { color: var(--text-schwach); font-size: 0.76rem; }
.menu-chevron { color: #c7ccd4; font-size: 1.2rem; }
.menu-row-danger strong { color: var(--rot); }

/* --- Karten / Abschnitte -------------------------------------------- */
.karte {
  background: var(--flaeche); border: 1px solid var(--linie);
  border-radius: var(--radius); padding: 12px; margin-bottom: 12px;
}
.karte > h2:first-child, .karte > h3:first-child { margin-top: 0; }

/* Aufklappbarer Abschnitt -- Historie, QR und Verwaltung sind auf dem
   Handy fast nie gefragt und blaehten die Artikelseite vorher auf
   Bildschirmlaenge auf. */
.klapp {
  background: var(--flaeche); border: 1px solid var(--linie);
  border-radius: var(--radius); margin-bottom: 10px; overflow: hidden;
}
.klapp > summary {
  padding: 12px; cursor: pointer; font-weight: 600; font-size: 0.9rem;
  min-height: 48px; display: flex; align-items: center; gap: 8px;
  list-style: none;
}
.klapp > summary::-webkit-details-marker { display: none; }
.klapp > summary::after { content: "▾"; margin-left: auto; color: var(--text-schwach); }
.klapp[open] > summary::after { content: "▴"; }
.klapp > summary + * { padding: 0 12px; }
.klapp > *:last-child { padding-bottom: 12px; }

/* --- Artikeldetail -------------------------------------------------- */
.detail-kopf { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px; }
.detail-kopf h1 { flex: 1; min-width: 0; font-size: 1.2rem; overflow-wrap: anywhere; }
.detail-kopf .fav-btn { font-size: 1.5rem; width: 40px; }

.detail-bild {
  width: 100%; max-height: 240px; object-fit: contain;
  background: var(--flaeche); border: 1px solid var(--linie);
  border-radius: var(--radius); margin-bottom: 12px; display: block;
}

/* Stammdaten als zweispaltiges Raster statt als Absatzwueste. */
.datenliste { display: grid; grid-template-columns: auto 1fr; gap: 4px 12px; font-size: 0.86rem; }
.datenliste dt { color: var(--text-schwach); }
.datenliste dd { margin: 0; overflow-wrap: anywhere; }

.bestand-gross { font-size: 1.5rem; font-weight: 700; line-height: 1.1; }
.bestand-gross.knapp { color: var(--rot); }
.bestand-zeile {
  display: flex; justify-content: space-between; gap: 10px;
  padding: 7px 0; border-bottom: 1px solid var(--linie); font-size: 0.86rem;
}
.bestand-zeile:last-child { border-bottom: none; }

/* Die Gebinde einer Leitung, direkt unter der Menge ihres Ortes.

   Eingerückt und ohne Trennlinie, weil sie keine eigene Zeile sind,
   sondern die Aufschlüsselung der Zeile darüber: 1970 m können zehn
   volle Ringe oder eine halb abgewickelte Trommel sein.

   `flex-wrap` statt einer Tabelle: auf 375 px Breite stehen drei
   Plaketten nebeneinander, auf dem Schreibtisch acht. Eine Tabelle
   müsste dafür scrollen. */
.gebinde-zeile {
  display: flex; flex-wrap: wrap; gap: 4px;
  padding: 0 0 7px 12px; margin-top: -4px;
}
/* Ein volles Gebinde ist etwas Gutes -- grün wie der Preis, nicht grau
   wie eine Angabe. Angebrochen ist keine Warnung (der Rest ist
   brauchbar), aber eine Unterscheidung: gelb, nicht rot. */
.badge-gebinde { background: #dcfce7; color: #166534; }
.badge-gebinde.badge-angebrochen { background: #fef3c7; color: #92400e; }

/* Schnellaktionen: eine Zeile pro Aktion, Eingabe und Knopf nebeneinander
   statt untereinander. */
.aktion { border-bottom: 1px solid var(--linie); padding: 10px 0; }
.aktion:last-child { border-bottom: none; }
.aktion-titel { font-size: 0.8rem; color: var(--text-schwach); margin-bottom: 6px; }
.aktion-felder { display: flex; gap: 6px; flex-wrap: wrap; align-items: stretch; }
/* min-height wie ueberall: ohne sie kam das Feld auf 39px -- ein Pixel
   unter dem Mass, aber gemessen ist gemessen, und es steht neben
   Knoepfen, die 40 haben. */
.aktion-felder select, .aktion-felder input {
  flex: 1 1 100px; min-width: 0; padding: 8px 10px; font-size: 16px;
  min-height: 40px;
  border: 1px solid var(--linie); border-radius: 8px; background: var(--flaeche);
  font-family: inherit;
}
.aktion-felder button { flex: none; }

.badge {
  display: inline-block; background: #eef0f3; border-radius: 5px;
  padding: 2px 7px; font-size: 0.72rem; color: #4b5563; white-space: nowrap;
}
.badge-low { background: #fee2e2; color: #991b1b; }
.badge-archived { background: #f3f4f6; color: var(--text-schwach); }
.badge-fav { background: #fffbeb; color: #92400e; }
.qty-low { color: var(--rot); font-weight: 700; }
.price { color: var(--gruen); font-weight: 600; }

/* --- Bezugsquellen --------------------------------------------------- */
/* Die Artikelnummer des Lieferanten wird abgetippt und vorgelesen ("S54547"
   gegen "854547") -- deshalb Tabellenschrift, wo 0 und O sich unterscheiden. */
.bezugsquellen .nummer {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem; white-space: nowrap;
}
/* Der günstigste Anbieter soll ins Auge fallen, ohne die Zeile zu schreien:
   ein grüner Balken links statt einer farbigen Fläche — dieselbe Kante
   wie an der Trefferkarte, dieselbe Bedeutung. */
.bezugsquellen tr.guenstigster td:first-child { box-shadow: inset var(--leiste) 0 0 var(--gruen); }
.bezugsquellen td { vertical-align: top; }

/* --- Formulare ------------------------------------------------------ */
.item-form label, .inline-form label { display: block; margin-bottom: 10px; font-size: 0.84rem; color: var(--text-schwach); }
.item-form input, .item-form select, .item-form textarea,
.inline-form input, .inline-form select {
  width: 100%; padding: 9px 10px; margin-top: 4px; font-size: 16px;
  border: 1px solid var(--linie); border-radius: 8px; font-family: inherit;
  color: var(--text); background: var(--flaeche);
}
.item-form textarea { min-height: 70px; }
/* Eine Checkbox soll eine Checkbox bleiben -- ohne diese Ausnahme zoege sie
   sich `.item-form input`s `width:100%` auf Formularbreite, z.B. beim
   An-/Abwaehlen eines Retourenbelegs (`.beleg-schalter`). */
.item-form input[type="checkbox"] { width: auto; padding: 0; margin: 4px 6px 0 0; }
.inline-form { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin: 10px 0; }
.inline-form input, .inline-form select { width: auto; flex: 1 1 140px; min-width: 0; }

/* --- Retoure: Kopier-Knoepfe klein und neben dem Wert, nicht als grosser
   blauer Block mitten im Text (Deniz, 28.09.2026: "die anzeige gefällt mir
   auch nicht so"). --- */
.kopierbar { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.kopieren, button.kopieren {
  background: none; color: var(--blau); border: 1px solid var(--linie);
  padding: 2px 8px; min-height: 0; font-size: 0.72rem; border-radius: 6px;
  flex: none; margin-left: auto;
}

/* --- Tabellen ------------------------------------------------------- */
/* Auf dem Handy bricht keine Tabelle sinnvoll um -- sie bekommt deshalb
   einen eigenen waagerechten Scrollbereich, statt die Seite breiter zu
   machen. */
.tabelle-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 8px 0; }
.table {
  width: 100%; border-collapse: collapse; background: var(--flaeche);
  font-size: 0.82rem;
}
.table th, .table td {
  padding: 7px 8px; border-bottom: 1px solid var(--linie);
  text-align: left; white-space: nowrap;
}
.table th { color: var(--text-schwach); font-weight: 600; }
.table tr:last-child td { border-bottom: none; }
/* Betragsspalten rechtsbuendig: nur so stehen Euro und Cent
   untereinander, und genau dafuer sind die Spalten der Auswertung da. */
.table .zahl { text-align: right; }
.table tfoot th { border-top: 2px solid var(--linie); border-bottom: none; }

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; margin: 12px 0; }
.tile { background: var(--flaeche); border: 1px solid var(--linie); border-radius: var(--radius); padding: 12px; }
.tile-label { display: block; color: var(--text-schwach); font-size: 0.76rem; }
.tile-number { display: block; font-size: 1.5rem; font-weight: 700; }
.tile-hint { display: block; color: var(--warn); font-size: 0.75rem; }
.tile-warning .tile-number { color: var(--warn); }

/* --- Bilder --------------------------------------------------------- */
.image-gallery { display: flex; gap: 10px; flex-wrap: wrap; margin: 8px 0; }
.image-gallery img { width: 68px; height: 68px; object-fit: cover; border-radius: 6px; cursor: zoom-in; }

/* Bild, Kennzeichnung und die zwei Handlungen als eine Kachel
   (2026-08-09). Die Knoepfe stehen UNTER dem Bild und nicht als Symbole
   darauf: 68px Vorschau minus ein Ueberlagerungskreuz laesst kein
   Tippziel uebrig, das man auf der Baustelle sicher trifft -- und
   danebengegriffen wird hier eine Datei geloescht.

   Feste Kachelbreite, obwohl das Bild nur 68px braucht: die Beschriftung
   "Als Hauptbild" bestimmt die Breite, und Kacheln, die je nach Zustand
   verschieden breit sind, ergaeben eine unruhige Reihe. Bei 375px passen
   drei nebeneinander. */
.bild-kachel {
  margin: 0; display: flex; flex-direction: column;
  align-items: center; gap: 4px; width: 100px;
}
/* Die Kennzeichnung sitzt an derselben Stelle wie der Knopf, der sie
   holt, und ist ebenso hoch -- sonst stuenden die Entfernen-Knoepfe je
   nachdem, welches das Hauptbild ist, auf verschiedenen Hoehen. */
.bild-marke {
  display: flex; align-items: center; justify-content: center;
  min-height: 40px; width: 100%;
  font-size: 0.7rem; line-height: 1.1; text-align: center;
  color: var(--text-schwach);
}
/* Das Formular um den Knopf muss mitziehen, sonst bleibt der Knopf auf
   seiner Textbreite stehen und die Kachel sieht ausgefranst aus --
   "Entfernen" waere 62px breit, "Als Hauptbild" 81px, beide in einer
   100px-Kachel. */
.bild-kachel form { width: 100%; }
/* 40px, nicht 36: einer der beiden Knoepfe LOESCHT eine Datei, und
   daneben steht der andere. Die Kachel waechst dadurch um 8px, der
   Fehlgriff kostet mehr. */
.bild-knopf {
  min-height: 40px; padding: 4px 6px; font-size: 0.7rem; width: 100%;
}
.lightbox {
  position: fixed; inset: 0; background: rgba(0,0,0,0.9);
  display: flex; align-items: center; justify-content: center; z-index: 100;
}
.lightbox.hidden { display: none; }
.lightbox img { max-width: 92%; max-height: 92%; }

.qr-image { width: 160px; height: 160px; }
/* Das Bandetikett steht in Originalgroesse des Druckkopfs -- ein Pixel
   ist ein Druckpunkt. Auf dem Handy ist es dafuer zu breit, deshalb die
   Begrenzung; `pixelated` haelt die QR-Module beim Verkleinern scharf
   statt sie zu Grau zu verwaschen. Langes Tippen sichert weiterhin das
   Original, nicht die verkleinerte Anzeige. */
.etikett-bild {
  max-width: 100%; border: 1px solid var(--linie); background: #fff;
  border-radius: 4px; image-rendering: pixelated; display: block;
}

/* Das Werkzeugetikett ist mit 320 px schmaler als das Band und wuerde in
   Originalgroesse verloren auf der Seite stehen. Es soll gross sein: die
   Vorschau ist das, woran man vor dem Drucken erkennt, ob der Name ganz
   drauf steht. Nach oben begrenzt, damit es auf dem Desktop nicht ins
   Plakathafte laeuft. */
/* `height: auto` ist hier nicht kosmetisch: das <img> traegt width- und
   height-Attribute, damit beim Laden nichts springt. Ohne diese Zeile
   bleibt die Hoehe auf 240 px stehen, waehrend die Breite mitwaechst --
   die Vorschau zeigt dann ein leicht verzerrtes Etikett, und verzerrt ist
   genau das, was man auf dieser Seite pruefen will. */
.etikett-bild-werkzeug { width: 100%; max-width: 480px; height: auto; }

/* Schrittfolge, die man beim ersten Mal mitliest und danach nie wieder.
   Nummeriert und nicht als Fliesstext: es sind Handgriffe am Geraet, und
   wer bei Schritt 3 unterbrochen wird, muss die Stelle wiederfinden. */
.anleitung { margin: 0 0 10px; padding-left: 22px; font-size: 0.85rem;
             line-height: 1.45; color: var(--text); }
.anleitung li { margin-bottom: 5px; }

.login-box { max-width: 340px; margin: 40px auto; }
.login-box label { display: block; margin-bottom: 12px; }
.login-box input { width: 100%; padding: 10px; margin-top: 4px; font-size: 16px; border: 1px solid var(--linie); border-radius: 8px; }

/* --- Katalogsuche --------------------------------------------------- */
.catalog-search { margin: 10px 0; }
.catalog-search-input { width: 100%; padding: 11px 10px; min-height: 44px; font-size: 16px; border: 1px solid var(--linie); border-radius: 8px; }
.catalog-result {
  display: flex; justify-content: space-between; align-items: center;
  background: var(--flaeche); padding: 8px; border-radius: 8px; margin-top: 6px;
  gap: 8px; font-size: 0.84rem; border: 1px solid var(--linie);
}
.catalog-result form { margin: 0; }
/* Ergebnis eines Scans oder einer Katalogsuche, steht ganz oben auf der
   Seite. Gruene Kante: gefunden.
   (`.catalog-suggestion` stand hier daneben -- eine Karte mit BLAUER
   Kante fuer denselben Sachverhalt, ohne einen einzigen Verwender in
   Template, JavaScript oder Test. Entfernt, weil zwei Kantenfarben fuer
   "gefunden" die Bedeutung der Kante aufheben.) */
.treffer-karte { border-left: var(--leiste) solid var(--gruen); }
.treffer-titel { font-weight: 600; overflow-wrap: anywhere; margin-bottom: 4px; }

/* --- Scanner -------------------------------------------------------- */
.scanner { margin: 10px 0; }
.scanner-video {
  width: 100%; max-width: 480px; aspect-ratio: 4 / 3;
  background: #111; border-radius: var(--radius); object-fit: cover; display: block;
}
.scanner-status { margin: 8px 0; min-height: 1.2em; font-size: 0.88rem; }
.scanner-status-error { color: #b91c1c; }
.scanner-status-ok { color: #15803d; }

/* --- Kategorienverwaltung ------------------------------------------- */
.kat-zeile {
  display: flex; align-items: center; gap: 8px;
  padding: 9px 12px; border-bottom: 1px solid var(--linie);
}
.kat-zeile:last-child { border-bottom: none; }
.kat-name { flex: 1; min-width: 0; }
.kat-name input {
  width: 100%; padding: 9px; min-height: 40px; font-size: 16px; border: 1px solid transparent;
  border-radius: 6px; background: transparent; font-family: inherit; color: var(--text);
}
.kat-name input:focus { border-color: var(--linie); background: var(--flaeche); outline: none; }
.kat-anzahl { flex: none; font-size: 0.76rem; color: var(--text-schwach); white-space: nowrap; }
/* Vier Knoepfe nebeneinander, einer davon loescht: bei 34x34 und 4px
   Abstand ist der Griffbereich kleiner als eine Fingerkuppe, und der
   Fehlgriff trifft ausgerechnet den Papierkorb. 40px und mehr Abstand. */
.kat-aktionen { flex: none; display: flex; gap: 6px; }
.kat-aktionen button, .kat-aktionen .button {
  flex: none;  /* ohne das schrumpfen sie im Flexrahmen auf 33px zurueck */
  min-height: 40px; width: 40px; padding: 0; font-size: 0.9rem;
  display: inline-flex; align-items: center; justify-content: center;
}
/* Auf der Profilseite steht in derselben Reihe ein Auswahlfeld -- ohne
   eigene Regel bekam es die Vorgabehoehe des Browsers. */
.kat-aktionen select {
  min-height: 40px; padding: 0 8px; font-size: 16px; font-family: inherit;
  border: 1px solid var(--linie); border-radius: 8px;
  background: var(--flaeche); color: var(--text); max-width: 45vw;
}
/* Der Name ist der einzige Weg zum Mitarbeiter bzw. zur Kategorie -- als
   Textzeile war er 20px hoch. Derselbe Kniff wie in .zeile-titel. */
.kat-name a { display: inline-block; padding: 11px 0; margin: -11px 0; }
/* Die Pfeilknoepfe stecken je in einem eigenen Formular -- ohne diese
   Zeile sind die FORMULARE die Flexelemente und schrumpfen, waehrend das
   `flex: none` am Knopf darin wirkungslos bleibt. */
.kat-aktionen > form { flex: none; margin: 0; }

/* Auf dem Handy passen Name, Anzahl und vier Knoepfe nicht in eine
   375px-Zeile, ohne dass etwas darunter leidet: entweder wird das
   Namensfeld auf 100px gequetscht oder die Knoepfe auf 33px -- und einer
   der vier loescht. Deshalb bricht die Zeile hier um: das Feld bekommt
   die volle Breite, die Aktionen ruecken darunter nach rechts.
   Kategorien und Profile sind Bueroarbeit, ein paar Pixel Hoehe kosten
   hier nichts. */
@media (max-width: 520px) {
  .kat-zeile { flex-wrap: wrap; row-gap: 6px; }
  .kat-name { flex: 1 1 100%; }
  .kat-aktionen { margin-left: auto; }
}

/* --- Ortsuebersicht ------------------------------------------------- */
/* Der Loeschknopf schwebt nicht mehr dauerhaft ueber jeder Karte: er
   erscheint erst im Bearbeitungsmodus. */
.zeile-loeschen { flex: none; position: relative; z-index: 1; margin: 0; }
.zeile-loeschen button {
  background: none; border: 1px solid var(--linie); color: var(--rot);
  border-radius: 8px; width: 40px; min-height: 40px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
}
/* Dieselbe Position, aber ein Knopf MIT Beschriftung (Katalogseite:
   „Stilllegen"): das quadratische Mass oben ist fuer das ✕ gedacht und
   quetschte den Text in 36 Pixel Breite. */
.zeile-loeschen button.button-klein {
  width: auto; padding: 6px 12px; color: var(--text); white-space: nowrap;
}
/* Zwei Aktionen am Ende derselben Zeile: "Mitzaehlen" und Entfernen.
   Ohne diese Regel stehen sie auf 375px untereinander und schieben die
   Zeile auf die doppelte Hoehe -- ausgerechnet im Bearbeitungsmodus,
   wo man mehrere Orte nacheinander durchgeht. */
.zeile-aktionen { display: flex; align-items: center; gap: 6px; flex: none; }

/* ==================================================================== */
/* Tablet und Desktop                                                    */
/* ==================================================================== */
@media (min-width: 768px) {
  body { font-size: 16px; }
  body.has-tabbar .container { padding-bottom: 24px; }

  .container { padding: 20px 16px 32px; }
  h1 { font-size: 1.6rem; }

  /* Waagerechte Navigation zurueck, feste Leiste unten weg -- mit Maus
     ist der Weg nach oben kurz, und der Platz ist da. */
  .topnav { display: flex; gap: 14px; flex-wrap: wrap; align-items: center; margin-left: auto; }
  .topnav a { color: #d1d5db; text-decoration: none; font-size: 0.9rem; }
  .topnav a:hover { color: white; }
  .tabbar { display: none; }
  .nur-desktop { display: flex; }
  .neu-knopf { display: none; }

  .zeile { padding: 11px 14px; gap: 14px; }
  .zeile-titel { font-size: 1rem; }
  .zeile-unter, .zeile-meta { font-size: 0.85rem; }
  .zeile-thumb { width: 56px; height: 56px; }

  .filterreihe { flex-wrap: wrap; overflow: visible; }
  .filterreihe select, .chip { max-width: none; font-size: 0.85rem; }

  .detail-spalten { display: flex; gap: 20px; align-items: flex-start; }
  .detail-haupt { flex: 2; min-width: 0; }
  .detail-neben { flex: 1; min-width: 220px; }
  .detail-bild { max-height: 320px; }

  .table th, .table td { padding: 9px 10px; }
}

@media (min-width: 1024px) {
  .container { max-width: 1000px; }
}

/* Sehr schmale Geraete (360px und darunter): der Preis ist das erste,
   was weichen darf -- Bestand und Ort sind wichtiger. Der Gesamtwert
   haengt am Preis und ist noch entbehrlicher als dieser selbst. */
@media (max-width: 360px) {
  .zeile { padding: 8px 10px; gap: 8px; }
  .zeile-thumb { width: 42px; height: 42px; }
  .zeile-meta .preis, .zeile-meta .wert { display: none; }
}

/* --- Katalogtreffer (2026-08-09) ------------------------------------ */
/* Die Metazeile einer Katalogzeile bricht um, statt zu klemmen: sie
   traegt Lieferant, Artikelnummer, Hersteller, EAN, Preis und VPE, und
   auf 375px Breite fiel davon mit `nowrap` ausgerechnet der PREIS hinten
   raus -- die Angabe, wegen der man vier Haendler nebeneinander stellt.
   Aus demselben Grund gilt hier auch die 360px-Regel oben nicht. */
.katalog-meta {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}
.katalog-meta .preis { display: inline !important; color: var(--text); font-weight: 600; }

/* --- Mitarbeiterausstattung (2026-08-08) ---------------------------- */
/* "persoenlich ausgegeben" hebt sich von den Lagerorten ab, ohne wie
   eine Warnung auszusehen -- es ist ein normaler, gewollter Zustand. */
.badge-persoenlich { background: #ede9fe; color: #5b21b6; }
.badge-ok { background: #dcfce7; color: var(--gruen); }
.status-ok { color: var(--gruen); }

.abschnitt-titel {
  font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-schwach); margin: 16px 0 6px;
}

/* Zeilen mit mehreren Vorkommen (Suche) oder eingebetteten Formularen
   (Mitarbeiterseite) duerfen hoeher werden als die 70-90px der
   Artikelliste -- der Inhalt bestimmt die Hoehe, nicht umgekehrt. */
.zeile-hoch { align-items: flex-start; }
/* Steht im Titel ein eigener Link, dann ist die Zeile KEIN Vollflaechen-
   Link (dort traegt .zeile-link::after die ganze Flaeche) -- in diesen
   Zeilen steckt ein Formular, und der Name ist der einzige Weg zum
   Artikel. Als Textzeile war er 20px hoch; die negativen Aussenabstaende
   machen daraus ein 40px hohes Tippziel, ohne die Zeile
   auseinanderzuziehen.

   Der Selektor haengt bewusst am Aufbau (`.zeile-titel > a`) und nicht an
   der Klasse `.zeile-mit-formular`: die setzt die Mitarbeiterseite nur,
   wenn etwas FEHLT -- eine vollstaendige Position hatte deshalb wieder
   einen 20px-Link. */
.zeile-titel > a {
  color: inherit; text-decoration: none;
  display: inline-block; padding: 11px 0; margin: -11px 0;
}

/* Flache Klapp-Box innerhalb einer Zeile (Ruecknahme-Aktionen): ohne
   eigenen Rahmen, damit sie nicht wie eine Karte in der Karte wirkt. */
.klapp-flach { border: none; background: transparent; margin: 4px 0 0; }
.klapp-flach > summary { padding: 8px 0; font-size: 0.82rem; color: var(--blau); min-height: 40px; }
.klapp-flach > summary + * { padding: 6px 0 0; }
.klapp-flach > *:last-child { padding-bottom: 4px; }

/* --- Inventur (2026-08-08) ------------------------------------------ */
/* Beim Zaehlen ist der Scanknopf die Seite. Er darf gross sein -- man
   bedient ihn mit Arbeitshandschuhen, im Halbdunkel, mit einer Hand,
   waehrend die andere die Klemmen haelt. */
.scan-gross {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 6px; padding: 22px 12px; margin-bottom: 4px;
  background: var(--blau); color: #fff; text-decoration: none;
  border-radius: var(--radius); font-size: 1.05rem; font-weight: 600;
  min-height: 96px;
}
.scan-gross:active { background: #1d4ed8; }
.scan-gross-icon { width: 32px; height: 32px; }

/* Der Artikelname auf der Zaehlseite darf umbrechen -- hier ist er die
   einzige Information, die zaehlt, und ein abgeschnittener Name fuehrt
   zur Zaehlung am falschen Artikel. */
.zaehl-name { font-size: 1.15rem; font-weight: 700; line-height: 1.25; margin-bottom: 4px; }
.zaehl-kopf { margin-bottom: 10px; }

/* Mengeneingabe: grosse Tippziele links und rechts, die Zahl in der
   Mitte gross genug, um sie im Vorbeigehen zu lesen. */
.menge-eingabe { display: flex; align-items: stretch; gap: 8px; margin-top: 4px; }
.menge-eingabe input {
  flex: 1; min-width: 0; text-align: center;
  font-size: 1.6rem; font-weight: 700; padding: 8px;
  border: 1px solid var(--linie); border-radius: 8px; font-family: inherit;
}
.menge-schritt {
  flex: none; width: 56px; font-size: 1.5rem; line-height: 1;
  background: var(--flaeche); color: var(--text); border: 1px solid var(--linie);
}
.menge-schritt:active { background: #eef2ff; }

/* Zustandswahl beim Werkzeug: der Chip ist das Tippziel, nicht der
   winzige Radiobutton darin. */
.filterreihe label.chip { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }

/* ==================================================================== */
/* Buchen (2026-08-09)                                                   */
/* ==================================================================== */
/* Diese App hat genau einen Vorgang, der oft passiert: etwas rausnehmen
   oder reinlegen. Vorher lag er als vierter von fuenf gestapelten
   Formularen bei y=937 -- auf einem 812px-Schirm also eine volle
   Bildschirmlaenge unter dem Namen, hinter Stammdaten, Bestand und Preis.
   Jetzt steht er direkt unter dem Artikelnamen und ist die einzige
   Flaeche der Seite mit eigenem Untergrund. */
.buchen {
  background: #f8fafc; border: 1px solid var(--linie);
  border-left: var(--leiste) solid var(--blau);
  border-radius: var(--radius); padding: 12px; margin-bottom: 12px;
}
.buchen-kopf {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 10px; margin-bottom: 10px;
}
.buchen-kopf .bestand-gross { line-height: 1; }
/* Kofferinhalt: eingerueckt und abgesetzt, damit auf einen Blick zu
   sehen ist, dass die Teile ZU diesem Exemplar gehoeren und nicht
   daneben liegen. Die linke Kante ist die Kofferwand. */
.koffer {
  margin: 8px 0 0; padding: 8px 0 2px 10px;
  border-left: var(--leiste) solid var(--linie);
}
.koffer h4 { margin: 0 0 6px; font-size: 0.85rem; }
.koffer-inhalt { list-style: none; margin: 0; padding: 0; }
.koffer-inhalt > li {
  padding: 6px 0; border-bottom: 1px solid var(--linie);
}
.koffer-inhalt > li:last-child { border-bottom: none; }
/* Je Teil stehen zwei Wege nebeneinander (umlegen, herausnehmen), und
   jeder steckt in einem eigenen Formular. Ohne diese Zeilen sind die
   FORMULARE die Flexelemente: sie schrumpfen nicht, das Auswahlfeld
   darin bleibt 260px breit, und die Seite war auf einem 375px-Gerät
   579px breit -- das ganze Layout scrollte seitlich. */
.koffer-inhalt .buchen-zeile { flex-wrap: wrap; }
.koffer-inhalt .buchen-zeile > form { flex: 1 1 200px; min-width: 0; margin: 0; }
.koffer-inhalt .buchen-zeile > form > button { flex: none; }

.buchen-zeile { display: flex; gap: 6px; margin-bottom: 8px; }
.buchen-zeile > select, .buchen-zeile > input {
  flex: 1 1 100px; min-width: 0; padding: 10px; font-size: 16px;
  border: 1px solid var(--linie); border-radius: 8px;
  background: var(--flaeche); color: var(--text); font-family: inherit;
}
/* Die beiden Richtungen gleich gross nebeneinander: entnehmen ist der
   haeufigere Fall und traegt deshalb die Vollfarbe, auffuellen steht als
   Umriss daneben. Rot waere falsch -- eine Entnahme ist der Normalfall
   und keine Gefahr. */
.buchen-knoepfe { display: flex; gap: 8px; }
.buchen-knoepfe button {
  flex: 1 1 50%; min-height: 52px; font-size: 0.95rem; font-weight: 600;
}
.buchen-weitere { margin-top: 4px; }

/* Was der gescannte Code bedeutet: Gebinde, Fabrikat, Haendler. Steht
   ueber dem Formular, weil sonst niemand weiss, warum im Mengenfeld
   schon eine Zahl steht. */
.gescannt {
  margin: 0 0 8px; font-size: 0.82rem; color: var(--text);
  overflow-wrap: anywhere;
}

/* Kandidatenliste beim Zusammensuchen der Bezugsquellen einer Leitung.
   Die Ankreuzflaeche ist die ganze Zeile -- ein 13px grosses Kaestchen
   trifft im Fahrzeug niemand. */
.kandidat {
  display: flex; align-items: flex-start; gap: 12px;
  flex: 1; min-width: 0; cursor: pointer; padding: 2px 0;
}
.kandidat input[type="checkbox"] {
  flex: none; width: 22px; height: 22px; margin: 2px 0 0;
}
.kandidat-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.kandidat input:disabled + .kandidat-text { opacity: 0.55; }

/* Mengeneingabe mit Steppern -- dieselbe Komponente wie beim Zaehlen in
   der Inventur, damit man sie nur einmal lernt. */
.menge-eingabe input { font-variant-numeric: tabular-nums; }

/* Zahlen stehen in dieser App untereinander und werden verglichen:
   Bestaende, Mengen, Preise, Artikelnummern. Mit Proportionalziffern
   springen die Spalten, und "0,2402" gegen "0,2469" liest sich schlechter
   als noetig. */
.menge, .preis, .price, .wert, .bestand-gross, .tile-number,
.table td, .datenliste dd, .bezugsquellen .nummer {
  font-variant-numeric: tabular-nums;
}

/* Zurueck-Zeile: vorher ein 99x17px grosser Textlink in .hint -- auf dem
   Handy praktisch nicht zu treffen. Jetzt eine eigene Zeile mit voller
   Daumenhoehe. */
.zurueck {
  display: inline-flex; align-items: center; gap: 4px;
  min-height: 44px; padding: 0 10px 0 0; margin: 0 0 4px -4px;
  color: var(--text-schwach); text-decoration: none; font-size: 0.85rem;
}
.zurueck:active { color: var(--blau); }

/* Kopfzeile der Artikel- und Schnellaktionsseite: Stern links, Name
   daneben, beides auf einer Linie mit dem Zurueck-Weg darueber. */
.detail-kopf .fav-btn { width: 44px; min-height: 44px; }

/* "Weiter scannen": nach einer Buchung aus dem Scan heraus geht es fast
   immer mit dem naechsten Artikel weiter. Ohne diesen Weg fuehrt jede
   Buchung ueber Tabbar -> Scannen -> Kamera starten. */
.weiter-scannen {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  min-height: 52px; margin-bottom: 12px;
  background: var(--dunkel); color: #fff; text-decoration: none;
  border-radius: var(--radius); font-weight: 600; font-size: 0.95rem;
}
.weiter-scannen:active { background: #111827; }

/* Chips, die auf Zahlen zeigen (Favoriten, Zuletzt) stehen VOR den
   Auswahlfeldern: die Filterreihe scrollt waagerecht, und was hinten
   liegt, existiert auf einem 375px-Geraet praktisch nicht. Der
   Favoritenchip lag vorher bei x=658. */
.chip-zuletzt.aktiv { border-color: var(--dunkel); background: #eef2ff; color: #312e81; }

/* Fokus sichtbar halten -- die App wird auch am Buero-Rechner mit
   Tastatur bedient, und der Standardring verschwindet auf farbigen
   Flaechen. */
:focus-visible {
  outline: 2px solid var(--blau); outline-offset: 2px; border-radius: 4px;
}

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

@media (min-width: 768px) {
  /* Am Schreibtisch bleibt der Block links stehen, statt sich ueber die
     ganze Fensterbreite zu ziehen -- ein 800px breites Mengenfeld fuer
     eine zweistellige Zahl sieht nach Formular aus, nicht nach Buchung. */
  .buchen-zeile { flex-wrap: wrap; }
  .buchen-zeile > select, .buchen-zeile > input { flex: 0 1 260px; }
  .buchen .menge-eingabe { max-width: 420px; }
  .buchen-knoepfe button { flex: 0 1 200px; }
}

/* --- Trennung Material / Werkzeug ----------------------------------- */
/* Fuenf Reiter statt vier. Auf einem 375px-Geraet sind das 75px je
   Reiter; die Beschriftung zu kuerzen kam nie in Frage, "Wo ist" waere
   kein Reitername mehr, sondern ein Raetsel.
   0.72rem statt 0.63 seit 2026-08-10: die alte Annahme war "0.68rem
   braucht rund 60px" -- nachgemessen sind es 51,7px, und bei 0.72rem
   54,7px. Selbst auf einem 360px-Geraet (72px je Reiter) bleiben damit
   15px Reserve. Es waren die kleinsten Beschriftungen der ganzen App
   (10,08px) an ihrem meistbenutzten Bedienelement. */
.tabbar-fuenf a { padding: 7px 1px; font-size: 0.72rem; letter-spacing: -0.01em; }

/* Der Querverweis in den anderen Bereich. Er steht am Ende der Treffer
   und nicht oben: er ist die Antwort auf "hier ist nichts", und die
   liest man erst, wenn man das Nichts gesehen hat. */
.bereich-hinweis { margin-top: 10px; }
.bereich-hinweis a { color: var(--blau); }

/* --- Kategoriebaum -------------------------------------------------- */
/* Der Weg als Zeile ueber der Liste. Er scrollt waagerecht statt
   umzubrechen: bei vier Stufen ("Schalterprogramm > Gira > Standard 55 >
   reinweiss glaenzend") braeuchte ein Umbruch auf dem Handy drei Zeilen,
   und die Liste ruecke jedes Mal um 40px nach unten. */
.krumen {
  display: flex; align-items: center; gap: 4px;
  overflow-x: auto; white-space: nowrap;
  font-size: 0.8rem; margin-bottom: 8px; padding-bottom: 2px;
  scrollbar-width: none;
}
.krumen::-webkit-scrollbar { display: none; }
/* Als reine Textzeile waren die Stufen 16px hoch -- auf einem Handy mit
   Arbeitshandschuhen nicht zu treffen, und ein Fehlgriff fuehrt eine
   Ebene zu weit nach oben. Die negativen Aussenabstaende machen daraus
   ein 36px hohes Ziel, ohne die Zeile auseinanderzuziehen. */
.krumen a, .krumen-hier {
  display: inline-block; padding: 10px 2px; margin: -10px 0;
}
.krumen a { color: var(--blau); text-decoration: none; }
/* Die letzte Stufe ist der Ort, an dem man steht -- kein Link, aber die
   auffaelligste Angabe der Zeile. */
.krumen-hier { font-weight: 600; color: var(--text); }
.krumen-pfeil { color: #c7ccd4; flex: none; }

/* Kategoriezeilen sehen aus wie Artikelzeilen und verhalten sich auch
   so -- der Unterschied ist das Symbol vorn und die Zahl statt eines
   Bestands. Eine eigene Optik haette den Eindruck erweckt, das seien
   zwei verschiedene Listen. */
.zeilen-ordner { margin-bottom: 10px; }
.ordner-icon { flex: none; width: 20px; height: 20px; color: var(--text-schwach); }

/* Anzahl RECHTS statt in einer zweiten Textzeile (2026-08-09).
   ---------------------------------------------------------------------
   Vorher trug jede Ordnerzeile den Namen oben und "84 Artikel" darunter:
   zwei Textzeilen und rund 56px fuer eine Angabe, die aus einer Zahl
   besteht. Bei achtzehn Materialkategorien sind das tausend Pixel
   Scrollweg fuer eine Liste aus Namen.

   Nebeneinander bleiben 48px -- weiterhin ein volles Tippziel -- und die
   Zahlen bilden am rechten Rand eine Spalte. Untereinander gelesen sagt
   sie in einem Blick, wo der Bestand liegt; dieselben Zahlen unter je
   einem Namen sagen das nicht. Tabellenziffern, damit die Spalte auch
   wirklich eine ist und nicht bei jeder Zeile um zwei Pixel wandert. */
.zeile-ordner { min-height: 48px; }
/* Der Name darf umbrechen wuerde die Zeilenhoehe wieder sprengen --
   lange Namen bekommen deshalb die Auslassung wie in der Artikelliste. */
.zeile-ordner .zeile-titel { font-size: 0.95rem; }
.ordner-zahl {
  flex: none;
  font-size: 0.8rem; line-height: 1;
  font-variant-numeric: tabular-nums;
  color: var(--text-schwach);
  white-space: nowrap;
}
.ordner-zahl b { font-weight: 700; color: var(--text); }

/* --- Zuordnung pruefen (2026-08-09) --------------------------------- */
/* Eine Karte je Fall statt einer Tabelle: die Zeile traegt Name, Grund,
   zwei Pfade und drei Knoepfe -- als Tabellenzeile waere davon auf 375px
   nichts mehr lesbar. Buerotaetigkeit, ein paar Pixel Hoehe kosten hier
   nichts. */
.pruef-zeile {
  padding: 12px; border-bottom: 1px solid var(--linie);
}
.pruef-zeile:last-child { border-bottom: none; }
.pruef-kopf { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pruef-name { font-weight: 600; color: inherit; text-decoration: none; min-width: 0; }
.pruef-grund { font-size: 0.8rem; color: var(--text-schwach); margin: 4px 0 8px; }

/* Zwei Zeilen, zwei Zustaende: wo der Artikel jetzt liegt und wo er
   liegen soll. Als Beschreibungsliste, weil es genau das ist. */
.pruef-wege {
  display: grid; grid-template-columns: auto 1fr; gap: 2px 10px;
  margin: 0 0 10px; font-size: 0.82rem;
}
.pruef-wege dt { color: var(--text-schwach); }
.pruef-wege dd { margin: 0; overflow-wrap: anywhere; }
.pruef-wege .pruef-ziel { font-weight: 600; }

.pruef-aktionen { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* Zwischenueberschrift zwischen Kategorien und den Artikeln der Ebene.
   Der untere Abstand ist 12px und nicht 6: traegt die Ueberschrift einen
   Verweis (`.abschnitt-link`), ragt dessen Tippflaeche 12px unter die
   Textzeile -- bei 6px lag sie ueber der Oberkante der ersten Zeile, und
   ein Fehlgriff oeffnete den ersten Posten statt der Liste. */
.abschnitt {
  font-size: 0.82rem; font-weight: 600; color: var(--text-schwach);
  text-transform: uppercase; letter-spacing: 0.03em;
  margin: 14px 0 12px; display: flex; align-items: baseline; gap: 6px;
}
.abschnitt-zahl { font-weight: 400; text-transform: none; letter-spacing: 0; }

/* Umhaengen in der Kategorieverwaltung: volle Breite unter der Zeile,
   damit der ausgeschriebene Pfad ("Schalterprogramm > Gira > ...")
   lesbar bleibt. Neben vier Knoepfen waere er auf 80px gequetscht. */
.kat-umhaengen { flex: 1 1 100%; margin: 0; }
.kat-umhaengen select {
  /* 40px wie jedes andere Ziel der App. Bei 36 stand ausgerechnet das
     Feld, das eine Kategorie samt Inhalt woandershin haengt, unter dem
     eigenen Mass -- und davon 76 Stueck untereinander. */
  width: 100%; min-height: 40px; padding: 0 8px;
  font-size: 0.78rem; font-family: inherit;
  border: 1px solid var(--linie); border-radius: 6px;
  background: var(--flaeche); color: var(--text-schwach);
}

/* --- Schnellerfassung ----------------------------------------------- */
/* Menge und Einheit stehen rechts in der Zeile, der Name links -- beim
   Durchgehen der Liste vor dem Buchen wandert der Blick eine Spalte
   herunter und nicht im Zickzack. */
.schnell-zeile { align-items: center; }
.schnell-menge {
  flex: none; width: 84px; text-align: right;
  padding: 8px; min-height: 40px; font-size: 16px; /* 16px: iOS zoomt sonst */
  border: 1px solid var(--linie); border-radius: 6px;
  background: var(--flaeche); font-family: inherit; color: var(--text);
}
.schnell-einheit {
  flex: none; font-size: 0.78rem; color: var(--text-schwach);
  min-width: 34px;
}
/* Die Entfernen-Knoepfe liegen bewusst UNTER der Buchungsliste und nicht
   als rotes Kreuz in jeder Zeile: gebucht wird viel oefter als
   entfernt, und ein Loeschknopf neben dem Mengenfeld ist auf einem
   Handy der haeufigste Fehlgriff. */
.schnell-fuss {
  display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px;
  padding-top: 10px; border-top: 1px solid var(--linie);
}
.schnell-fuss .inline-form { margin: 0; }

/* --- Wareneingang ---------------------------------------------------- */
/* Die Kontrollansicht vor dem Buchen. Aufbau wie die Schnellerfassung --
   Haken links, Name in der Mitte, Menge rechts -- mit einem Unterschied:
   eine Zeile darf hier WACHSEN. Wo die Zuordnung unsicher ist, steht der
   Grund darunter, und den abzuschneiden hiesse, die eine Information zu
   verstecken, wegen der die Zeile ueberhaupt auffaellt. */
.we-zeile { align-items: flex-start; }
.we-text { flex: 1; min-width: 0; }
/* 44px Tippziel wie beim Favoritenstern: daneben liegt das Mengenfeld,
   und ein Fehlgriff waere hier eine Position, die ungewollt mitbucht. */
.we-haken {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  width: 40px; min-height: 40px; margin: -8px 0 -8px -4px; cursor: pointer;
}
.we-haken input { width: 22px; height: 22px; margin: 0; accent-color: var(--blau); }
.we-haken-leer { color: var(--warn); cursor: default; }
.we-haken-leer .sym { width: 20px; height: 20px; }

/* Die Rueckfrage "kommt trotzdem ins Hauptlager". Sie steht an der
   Ladekante auf einem 375px-Schirm und verhindert eine Fehlbuchung --
   also dieselben 40px Tippziel wie die Positionshaken und die ganze
   Zeile anfassbar, nicht nur das Kaestchen. */
.we-bestaetigung {
  display: flex; align-items: center; gap: 10px;
  min-height: 40px; margin-top: 2px; cursor: pointer;
  font-size: 0.85rem;
}
.we-bestaetigung input {
  flex: none; width: 22px; height: 22px; margin: 0; accent-color: var(--blau);
}

/* Der Grund darf umbrechen -- .zeile-meta daneben tut es ausdruecklich
   nicht. „Lieferschein zaehlt in Versandeinheit, der Artikel in STK" ist
   der Satz, der die Fehlbuchung um Faktor 24 verhindert. */
.we-grund {
  margin: 4px 0 0; font-size: 0.78rem; line-height: 1.35;
  color: #92400e; white-space: normal;
}
/* Der Gebindevorschlag bei Leitungen: eine eigene Zeile über die ganze
   Breite der Position.

   `flex-basis: 100%` bricht sie im Positionsraster um, statt sie als
   vierte Spalte neben Haken, Text und Menge zu quetschen -- auf 375 px
   wären das vier Ziele in einer Zeile, und getroffen würde das falsche.
   Die Längenangabe darf wachsen, Art und Einheit bleiben schmal. */
.we-gebinde {
  flex: 1 1 100%; display: flex; flex-wrap: wrap; gap: 6px;
  align-items: center; margin-top: 6px;
}
.we-gebinde label { flex: none; }
.we-gebinde select { flex: 0 1 100px; }
.we-gebinde input {
  flex: 1 1 120px; min-width: 0; padding: 8px 10px; font-size: 16px;
  min-height: 40px; border: 1px solid var(--linie); border-radius: 8px;
  background: var(--flaeche); font-family: inherit;
}
.we-gebinde select {
  padding: 8px 10px; font-size: 16px; min-height: 40px;
  border: 1px solid var(--linie); border-radius: 8px;
  background: var(--flaeche); font-family: inherit;
}

.we-klaeren { box-shadow: inset var(--leiste) 0 0 var(--warn); }
/* Keine eigene Hoehe hier: `.button-klein` bringt 40px mit, und das ist
   das Mass der App. Ein erster Versuch mit 36px sparte Platz und stand
   damit unter der Grenze, gegen die am 10.08.2026 78 Bedienelemente
   nachgebessert wurden. */
.we-aktionen { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }

/* Sichtbar nur fuer Tastatur und Vorlesehilfe: die erste Schaltflaeche
   eines Formulars ist die, die ein Enter im Eingabefeld ausloest. Sie
   muss deshalb die HAUPTaktion sein und nicht die erste Zeilenaktion --
   sonst legt ein Enter im Mengenfeld einen Artikel an. */
.nur-tastatur {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%);
  white-space: nowrap; border: 0; min-height: 0;
}

/* --- Arbeitsort (2026-08-10) ---------------------------------------- */
/* Der ausdrueckliche Ortskontext. Er entscheidet, gegen welchen Ort
   gebucht wird, und muss deshalb SICHTBAR sein: eine Vorauswahl, die
   niemand sieht, war genau der Fehler -- wer am Crafter stand, buchte
   still das Hauptlager ab. */
.ort-kontext {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
  margin: 0 0 10px;
}
.ort-kontext .hint { margin: 0; }
/* "Ort lösen" war ein 53x18px grosser Textlink -- und zwar ausgerechnet
   der, der den Arbeitsort wieder aufhebt. Ein Fehlgriff daneben laesst
   ihn stehen, und dann bucht wieder jemand gegen den falschen Ort: genau
   der Fall, gegen den diese Zeile ueberhaupt eingebaut wurde (F53).
   Derselbe Kniff wie in `.zeile-titel > a` -- 40px Tippziel, ohne die
   Zeile auseinanderzuziehen. Fliesstext ist das hier nicht: es ist ein
   Bedienelement in einer Bedienzeile. */
.ort-kontext a.hint {
  display: inline-block; padding: 11px 4px; margin: -11px 0;
  text-decoration: underline;
}
.badge-ort { background: #dbeafe; color: #1e40af; }

/* Die Aktionsreihe der Ortsseite. "Hier scannen" ist die Primaeraktion:
   wer die Seite oeffnet, steht meist vor dem Fahrzeug. Seit 2026-08-10
   steht sie allein -- die zwei Verweise in die gefilterte Liste sitzen
   jetzt an ihrer Zwischenueberschrift. Drei gleich aussehende Knoepfe
   nebeneinander brachen auf 375px in zwei Reihen um (88px) und liessen
   offen, welcher der gemeinte ist. */
.ort-aktionen {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px;
}
.ort-aktionen .button { flex: 1 1 auto; text-align: center; }

/* Die Kennzahlenzeile unter einer Ueberschrift. Zuerst fuer die
   Ortsseite gebaut (vorher drei Plaketten in einer eigenen Zeile plus
   eine 110px hohe Wertkachel darunter -- 132px fuer vier Zahlen, die
   niemand wegen ihrer selbst aufschlaegt), inzwischen auch auf der
   Mitarbeiterseite und am Artikel.
   Hiess bis zum 14.08.2026 `.ort-kennzahlen` -- der Name stimmte schon
   beim zweiten Verwender nicht mehr. */
.kennzahlen {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0 5px;
  margin: -6px 0 6px; font-size: 0.82rem; color: var(--text-schwach);
}
.kennzahlen .price { font-size: 0.88rem; }
/* Die Herstellernummer ist zum Abtippen und Vergleichen da -- in einer
   Proportionalschrift verschwimmen 0/O und 1/l genau dann, wenn es
   darauf ankommt. */
.kennzahlen .nummer { font-family: ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace; }
.ort-wert-details { margin: -4px 0 10px; }

/* Der Sprung in die gefilterte Liste steht an der Zwischenueberschrift
   des jeweiligen Bereichs -- dort heisst er etwas ("dieselben Posten,
   aber mit Suche und Sortierung") und nicht bloss "Material hier"
   neben "Hier scannen". */
.abschnitt-link {
  margin-left: auto; font-weight: 400; text-transform: none;
  letter-spacing: 0; font-size: 0.8rem; white-space: nowrap;
  /* Als reine Textzeile 17px hoch. Derselbe Kniff wie in .zeile-titel:
     ein volles Tippziel (41px), ohne die Ueberschrift auseinanderzuziehen.
     Die 12px Ueberhang nach unten sind der Grund fuer den 12px-Abstand an
     `.abschnitt` -- sonst reicht die Tippflaeche in die erste Zeile. */
  display: inline-block; padding: 12px 0 12px 8px; margin: -12px 0;
}

/* Ortsseite: Zeilen tragen unter dem Text noch eine Aktion und sind
   deshalb hoeher als in der Artikelliste. `.zeile-ort-text` ersetzt ein
   `style="flex:1; min-width:0"` an drei Stellen im Template. */
.zeile-ort-text { flex: 1; min-width: 0; }

/* Buchen direkt in der Zeile. Bleibt eingeklappt, damit die Liste eine
   Liste bleibt -- aufgeklappt steht die Menge neben dem Knopf.
 *
 * `position: relative; z-index: 1` ist hier keine Kosmetik, sondern die
 * Bedingung dafuer, dass die Knoepfe ueberhaupt zu treffen sind: der Link
 * auf die ganze Zeile liegt als `.zeile-link::after` mit `inset: 0` ueber
 * dem gesamten Eintrag. Ohne das Anheben landet jeder Tipp auf
 * "Entnehmen" oder "Ich nehme es mit" auf der Artikelseite -- geprueft
 * mit elementFromPoint, nicht geraten. Derselbe Grund wie beim
 * Favoritenstern weiter oben. */
.zeile-aktion { margin-top: 2px; position: relative; z-index: 1; }
.zeile-aktion.aktion-felder { margin-top: 6px; }
.zeile-aktion .aktion-felder { margin-top: 0; }
.zeile-aktion > summary { display: list-item; }

/* Zustand melden steht im Buchungsblock, aber nicht neben den taeglichen
   Griffen: ein defektes Geraet meldet man selten, und "Verloren" darf
   kein Nachbar von "Ich nehme es mit" sein. */
.buchen-zustand > summary { color: var(--warn); }

/* Ein abgeschicktes Formular. Die Sperre gegen den zweiten Tipp sitzt in
   liste.js; hier steht nur, dass man ihr ansieht, dass etwas laeuft. */
form.sendet { opacity: 0.6; }
form.sendet button { cursor: default; }

/* --- Technische Uebernahme (2026-08-10) ----------------------------- */
/* Zeilen, die keine Taetigkeit von jemandem sind, sondern eine einmalige
   Umstellung des Datenbestands. Sie stehen bewusst da -- die Historie ist
   append-only -- aber sie sollen sich nicht wie eine Neuanschaffung von
   heute lesen. */
.tx-uebernahme { color: var(--text-schwach); }
.tx-uebernahme .badge { background: #f3f4f6; color: var(--text-schwach); }
/* Die Notizspalte traegt lange Texte und darf die Tabelle nicht sprengen. */
.tx-notiz { color: var(--text-schwach); font-size: 0.82rem; max-width: 22em; }

/* --- Grundzustand der Desktop-Bausteine ----------------------------- */
/* Diese drei Regeln stehen VOR der Media Query, und das ist keine
   Formsache: Media Queries erhoehen die Spezifitaet nicht: bei gleicher
   Spezifitaet gewinnt die SPAETER notierte Regel. Stuenden sie hinter dem
   Block, haetten sie ihn wieder aufgehoben, und zwar lautlos -- die
   Tabelle waere auf keiner Breite je sichtbar geworden.

   `.nav-gruppe` loest sich hier auf: `display: contents` nimmt den
   Behaelter aus dem Layout, seine Links liegen damit direkt in der
   waagerechten Reihe der `.topnav`. Genau so sah die Navigation vor dem
   16.08.2026 aus, und genau so sieht sie unterhalb 1200px weiterhin aus
   -- die Gruppierung im HTML aendert fuer Handy und Tablet nichts. */
.nur-breit { display: none; }
.nav-gruppe { display: contents; }
.nav-titel { display: none; }

/* Nur fuer Screenreader: eine Spaltenueberschrift, die sichtbar leer
   bleiben muss (die Favoritenspalte traegt nur Sterne), braucht trotzdem
   ein Wort -- sonst liest der Screenreader "Spalte 1". */
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}

/* ==================================================================== */
/* DER ARBEITSPLATZ -- ab 1200px (2026-08-16)                            */
/* ==================================================================== */
/*
 * Warum es diesen Abschnitt gibt (gemessen am 16.08.2026 an der
 * laufenden App, beide Rollen, 1280/1440/1920 gegen 375):
 *
 * Bis hierher endete der Desktop bei `@media (min-width: 1024px)`, und
 * dieser Block bestand aus EINER Zeile -- `max-width: 1000px`. Oberhalb
 * 1024px bekamen 1280, 1440 und 1920 damit buchstaeblich dasselbe
 * Layout. Auf 1920x1080 blieben 920px ungenutzt (48 %), waehrend die
 * Ortsseite trotzdem 4,1 Bildschirme brauchte -- weil ihre Zeilen die
 * Hoehe behalten, die sie auf 375px brauchen. Der Satz, an dem die
 * Sache kippte: auf 1280x720 zeigte die Ortsseite VIER Bestandsposten,
 * das Handy zeigt FUENF. Mehr Bildschirm fuehrte zu weniger Uebersicht.
 *
 * DIE GRENZE IST 1200 UND NICHT 768. Der vorhandene 768er-Block gilt
 * fuer Tablet UND Desktop gemeinsam, und das ist zu grob: ein Tablet
 * quer ist nahe am Handy, ein 27-Zoll-Schirm ist eine andere Gattung.
 * Deshalb greift hier NICHTS unterhalb von 1200px -- was auf dem Handy
 * und auf dem Tablet heute funktioniert, kann durch diesen Abschnitt
 * nicht kaputtgehen, weil die Regeln dort schlicht nicht existieren.
 *
 * Und deshalb ist der Umbau ADDITIV: die Sidebar tritt neben die
 * `.tabbar` statt an ihre Stelle, die Datentabelle neben `.zeilen`
 * statt an deren Stelle. Die Zeilenhoehe der Ortsseite wird ausdruecklich
 * NICHT angefasst -- sie ist auf dem Handy A50 und kostet dort entweder
 * das ausgeschriebene "Entnehmen" oder 17 % der Namensbreite.
 */
@media (min-width: 1200px) {

  /* --- Block 1: das Geruest ----------------------------------------- */
  /* Die Kopfleiste wird zur festen Zeile und bekommt ein bekanntes Mass,
     denn die Seitenleiste darunter haengt daran. Vorher ergab sich ihre
     Hoehe aus Polsterung plus Textzeile (gemessen 41px) -- ein Wert, den
     man nicht ausrechnen, sondern nur nachmessen konnte. */
  .topbar {
    position: fixed; top: 0; left: 0; right: 0; z-index: 60;
    height: 48px; padding: 0 18px;
  }

  /* Der Inhalt rueckt unter die Kopfleiste und neben die Seitenleiste.
     Die Obergrenze ist bewusst NICHT aufgehoben, sondern von 1000 auf
     1400px angehoben: eine Tabellenzeile, die ueber die volle Breite
     eines 27-Zoll-Schirms laeuft, ist mit dem Auge nicht mehr bis zur
     letzten Spalte zu verfolgen. */
  .container {
    max-width: 1400px;
    margin-left: 240px;
    margin-right: auto;
    /* Die Kopfleiste ist hier `fixed` und damit aus dem Fluss -- ohne
       diesen Abstand liegt die erste Ueberschrift der Seite darunter.
       Beim ersten Durchgang genau so passiert: "Material" stand zur
       Haelfte hinter der dunklen Leiste, und im Quelltext war nichts
       davon zu sehen. Fixed heisst: der Platz muss von Hand zurueckgegeben
       werden. */
    margin-top: 48px;
    padding: 24px 28px 40px;
  }
  /* `has-tabbar` reserviert unten Platz fuer die feste Navigation --
     die gibt es hier nicht, die Reserve waere leerer Rand. */
  body.has-tabbar .container { padding-bottom: 40px; }

  /* --- Block 2: die Seitenleiste ------------------------------------ */
  /* Aus der waagerechten Reihe wird eine Spalte. Vorher standen hier 14
     Links gleichgewichtet nebeneinander in 14,4px Schrift -- Tagesgeschaeft
     neben Verwaltung, "Abmelden" neben "Katalogsuche", ohne Gruppierung.
     Die Gruppen stehen im HTML (base.html) und sind unterhalb 1200px
     unsichtbar: dort loest `display: contents` sie auf, und die Links
     liegen wie bisher direkt in der Reihe. */
  .topnav {
    position: fixed; top: 48px; left: 0; bottom: 0; width: 240px;
    margin: 0; z-index: 55;
    display: flex; flex-direction: column; align-items: stretch;
    gap: 1px; padding: 14px 0 24px;
    background: var(--dunkel);
    overflow-y: auto; overscroll-behavior: contain;
  }
  .nav-gruppe { display: flex; flex-direction: column; margin-bottom: 14px; }
  .nav-titel {
    display: block;
    font-size: 0.66rem; letter-spacing: 0.09em; text-transform: uppercase;
    color: rgba(255,255,255,0.38);
    padding: 6px 18px 4px;
  }
  .topnav a {
    display: block; padding: 9px 18px;
    font-size: 0.88rem; color: #d1d5db; text-decoration: none;
    border-left: 3px solid transparent;
  }
  .topnav a:hover { background: rgba(255,255,255,0.06); color: #fff; }
  /* Die Klemmleiste als Ortsangabe: dieselbe Kante links, die in der
     Liste "hier ist etwas knapp" sagt, sagt hier "hier stehst du". */
  .topnav a.aktiv {
    color: #fff; font-weight: 600;
    border-left-color: var(--blau);
    background: rgba(255,255,255,0.08);
  }
  /* Ein Link, der die App verlaesst, darf nicht wie die zwoelf davor
     aussehen. Das Passwort liegt in der Lern-App auf einer anderen
     Domain -- bisher unterschied es sich in nichts. */
  .topnav a.nav-extern::after {
    content: " ↗"; color: rgba(255,255,255,0.45); font-size: 0.8em;
  }
  /* Abmelden gehoert ans Ende und nicht in eine Arbeitsgruppe. */
  .nav-gruppe-schluss { margin-top: auto; margin-bottom: 0; }

  /* --- Block 3: die Datentabelle ------------------------------------ */
  /* Der Baustein fuer alles, was VERGLICHEN wird: Artikellisten,
     Ortsbestaende, Katalogtreffer. Die gestapelte Zeile (`.zeilen`)
     bleibt daneben bestehen und traegt weiterhin Handy und Tablet.
     Gemessen war die Stapelung der Grund fuer 80px Zeilenhoehe bei 692px
     Leerraum -- Name, Kategorie und Bestand standen untereinander, weil
     auf 375px die Breite fehlt. Hier fehlt sie nicht. */
  .datentabelle {
    width: 100%; border-collapse: collapse;
    background: var(--flaeche);
    border: 1px solid var(--linie); border-radius: var(--radius);
    overflow: hidden;
    font-size: 0.88rem;
  }
  .datentabelle th {
    text-align: left; font-weight: 600; font-size: 0.72rem;
    letter-spacing: 0.06em; text-transform: uppercase;
    color: var(--text-schwach); background: #fafbfc;
    padding: 8px 12px; border-bottom: 1px solid var(--linie);
    white-space: nowrap;
  }
  .datentabelle td {
    padding: 7px 12px; border-bottom: 1px solid var(--linie);
    vertical-align: middle;
  }
  .datentabelle tbody tr:last-child td { border-bottom: none; }
  /* Mit der Maus wird gezeigt, bevor geklickt wird -- auf dem Handy gibt
     es diesen Zustand nicht, deshalb steht er hier und nicht oben. */
  .datentabelle tbody tr:hover { background: #f7f9fc; }
  /* Zahlen, die untereinander verglichen werden, brauchen gleiche
     Ziffernbreite und rechten Anschlag. Genau dafuer ist diese Tabelle
     ueberhaupt da. */
  .datentabelle .spalte-zahl { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
  .datentabelle .spalte-schmal { width: 1%; white-space: nowrap; }
  .datentabelle .zt-name { font-weight: 600; color: inherit; text-decoration: none; }
  .datentabelle .zt-name:hover { color: var(--blau); }
  .datentabelle .zt-schwach { color: var(--text-schwach); }
  .datentabelle .zt-bild {
    width: 32px; height: 32px; object-fit: cover; border-radius: 4px;
    background: #f3f4f6; display: block;
  }
  /* Dieselbe Bedeutung wie `.zeile-knapp` in der Liste: eine Kante links
     heisst "unter Mindestbestand". Wieder als inset-Schatten, damit die
     markierte Zeile nicht um 4px gegen ihre Nachbarn verspringt. */
  .datentabelle tr.zt-knapp td:first-child { box-shadow: inset var(--leiste) 0 0 var(--warn); }
  .datentabelle .zt-menge { font-weight: 700; }
  .datentabelle .zt-menge.knapp { color: var(--rot); }
  /* Der Stern sitzt in der ersten Spalte und ist dort das einzige
     Bedienelement -- er braucht kein 40px-Ziel mehr, hier wird geklickt
     und nicht getippt. */
  .datentabelle .fav-form button { min-height: 0; padding: 2px 4px; }

  /* --- Die Umschaltung ---------------------------------------------- */
  /* Zwei Darstellungen desselben Bestands. Serverseitig ist die Frage
     nicht zu entscheiden -- der Server kennt die Fensterbreite nicht --,
     also rendert das Template beide und hier faellt die Wahl. Der Preis
     ist doppeltes Markup fuer eine Seite; er ist vertretbar, weil eine
     Seite hoechstens 50 Artikel traegt (Paginierung) und weil die
     Alternative waere, die Handy-Zeile umzudeuten. Genau das soll dieser
     Umbau nicht tun. */
  .nur-breit { display: block; }
  .nur-schmal { display: none; }
}

/* --- Nachbestellliste ------------------------------------------------ */
/* Additiv, wie der Rest dieser Datei -- keine bestehende Regel wird hier
   veraendert. */
.meldungsliste .zeile { align-items: flex-start; flex-direction: column; }
.meldung-inhalt { width: 100%; }
.meldung-text { margin: 6px 0 0; font-size: 0.9rem; }

.medien-fotos { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
.medien-foto {
  width: 84px; height: 84px; object-fit: cover; border-radius: 8px;
  background: #f3f4f6; border: 1px solid var(--linie);
}
.medien-audio { display: block; width: 100%; margin-top: 8px; }

.artikel-suche { margin: 10px 0; }
.artikel-suche-ergebnisse { display: flex; flex-direction: column; gap: 2px; margin-top: 4px; }
.artikel-suche-treffer {
  text-align: left; padding: 9px 10px; border: 1px solid var(--linie);
  border-radius: 8px; background: var(--flaeche); color: var(--text);
  font-family: inherit; font-size: 0.9rem; cursor: pointer;
}
.artikel-suche-treffer:active { background: #f3f4f6; }

.sprachaufnahme { margin: 10px 0 16px; padding: 10px; border: 1px solid var(--linie); border-radius: 8px; }

/* --- KI-Chat -------------------------------------------------------- */

.ki-chat { display: flex; flex-direction: column; height: calc(100vh - 180px); min-height: 420px; }
.ki-chat-kopf { display: flex; align-items: baseline; justify-content: space-between; flex-wrap: wrap; gap: 4px; }
.ki-chat-kopf h1 { margin: 0; }
.ki-chat-status { display: flex; align-items: center; gap: 6px; margin: 0; font-size: 0.82rem; color: var(--text-schwach); }
.ki-status-punkt { width: 9px; height: 9px; border-radius: 50%; display: inline-block; }
.ki-status-punkt.online { background: var(--gruen); }
.ki-status-punkt.offline { background: var(--text-schwach); }

.ki-worker-control {
  display: flex; flex-direction: column; gap: 4px; width: 100%;
  margin-top: 6px; padding: 8px 10px; border: 1px solid var(--linie);
  border-radius: var(--radius); background: var(--flaeche); font-size: 0.82rem;
}
.ki-worker-control-zeile { display: flex; align-items: center; gap: 6px; margin: 0; color: var(--text-schwach); }
.ki-worker-control-fehler { margin: 0; color: var(--rot, #b23b3b); }
.ki-worker-control-aktionen { display: flex; gap: 8px; margin-top: 2px; }

.ki-chat-verlauf {
  flex: 1; overflow-y: auto; display: flex; flex-direction: column; gap: 10px;
  padding: 10px 4px; margin: 10px 0; border: 1px solid var(--linie);
  border-radius: var(--radius); background: var(--flaeche);
}
.ki-nachricht {
  max-width: 85%; padding: 8px 12px; border-radius: 12px; background: var(--hintergrund);
}
.ki-nachricht p { margin: 0 0 4px; white-space: pre-wrap; word-break: break-word; }
.ki-nachricht-user { align-self: flex-end; background: #dbeafe; }
.ki-nachricht-assistant { align-self: flex-start; }
.ki-nachricht-system { align-self: center; background: #ecfdf5; font-size: 0.85rem; text-align: center; }
.ki-nachricht-tool { display: none; }
.ki-nachricht-zeit { font-size: 0.72rem; color: var(--text-schwach); }
.ki-nachricht-foto { max-width: 100%; border-radius: 8px; display: block; margin-bottom: 6px; }
.ki-nachricht-audio { display: block; margin-bottom: 6px; max-width: 100%; }
.ki-nachricht-gesperrt { color: var(--text-schwach); font-style: italic; }
.ki-nachricht-kontext { font-size: 0.72rem; color: var(--text-schwach); margin: 2px 0 0; }

.ki-nachricht:has(.ki-quellen) { max-width: 100%; }
.ki-quellen { margin-top: 6px; display: flex; flex-direction: column; gap: 10px; }
.ki-quelle-block { border-top: 1px dashed var(--linie); padding-top: 6px; }
.ki-quelle-kopf { margin: 0 0 4px; font-size: 0.82rem; font-weight: 600; }
.ki-quelle-pfad { font-weight: 400; color: var(--text-schwach); }
.ki-quelle-fehler { color: var(--rot, #b91c1c); font-size: 0.85rem; }
.ki-quelle-tabelle { border-collapse: collapse; font-size: 0.82rem; max-width: 100%; margin-bottom: 4px; }
.ki-quelle-tabelle th, .ki-quelle-tabelle td { border: 1px solid var(--linie); padding: 3px 6px; text-align: left; vertical-align: top; }
.ki-quelle-tabelle.ki-quelle-felder th { color: var(--text-schwach); font-weight: 500; white-space: nowrap; }

.ki-pending {
  align-self: center; max-width: 95%; border: 1px solid var(--warn); border-radius: var(--radius);
  padding: 10px 12px; background: #fffbeb;
}
.ki-pending-vorschau { margin: 0 0 8px; }
.ki-pending-aktionen { display: flex; gap: 8px; }

.ki-chat-form { display: flex; flex-direction: column; gap: 8px; }
.ki-chat-anhang { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ki-chat-anhang-name { font-size: 0.8rem; color: var(--text-schwach); }
.ki-chat-denken { display: flex; align-items: center; gap: 4px; font-size: 0.82rem; color: var(--text-schwach); cursor: pointer; }

/* --- Hinweisband einer Test-Kopie (INSTANZ_HINWEIS) ------------------- */
/* Eine feste, schlichte Leiste ganz oben. Fest (fixed), weil sie auch beim
   Scrollen stehen soll -- wer in einer Kopie bucht, muss es sehen, bevor er
   bucht, nicht erst, wenn er zufaellig nach oben scrollt. Neutral gehalten:
   gedaempftes Gelb, keine Warnfarbe, denn nichts ist kaputt.

   Die Hoehe steht in einer Variable, weil zwei andere Dinge davon abhaengen:
   der Koerper rueckt darunter, und ab 1200px sind Kopfleiste und Seitenleiste
   selbst `fixed` und muessen um dieselbe Hoehe nach unten. Auf dem Handy
   (schmal) darf der Text auf zwei Zeilen umbrechen. */
:root { --hinweis-hoehe: 0px; }
body.hat-instanz-hinweis { --hinweis-hoehe: 30px; padding-top: var(--hinweis-hoehe); }
.instanz-hinweis {
  position: fixed; top: 0; left: 0; right: 0; z-index: 90;
  box-sizing: border-box; height: var(--hinweis-hoehe);
  display: flex; align-items: center; justify-content: center;
  padding: 0 12px; text-align: center;
  background: #fef3c7; color: #78350f; border-bottom: 1px solid #f0d58a;
  font-size: 0.8rem; font-weight: 600; line-height: 1.2;
  overflow: hidden;
}
@media (max-width: 640px) {
  body.hat-instanz-hinweis { --hinweis-hoehe: 40px; }
  .instanz-hinweis { font-size: 0.74rem; padding: 0 10px; }
}
@media (min-width: 1200px) {
  body.hat-instanz-hinweis .topbar { top: var(--hinweis-hoehe); }
  body.hat-instanz-hinweis .topnav { top: calc(48px + var(--hinweis-hoehe)); }
}

/* Stornierte Buchung in der Auftragsansicht: durchgestrichen und blass --
   sie steht da (die Historie ist vollstaendig), zaehlt aber nicht. */
.tx-storniert td { color: var(--text-schwach); }
.tx-storniert td a, .tx-storniert td.zahl { text-decoration: line-through; }
.tx-auftrag, .unterzeile { display: block; color: var(--text-schwach); font-size: 0.78rem; }
.table-buchungen td { white-space: normal; vertical-align: top; }
.table-buchungen td:first-child { white-space: nowrap; }
.table-buchungen td.zahl { white-space: nowrap; }
