/* ══════════════════════════════════════════════════
   v2 FORMS
   Destilliert aus Zeile 444-497 des Prototyps.
   ══════════════════════════════════════════════════ */
.form-label {
  display: block;
  font-size: var(--ms-text-small);
  font-weight: var(--ms-weight-medium);
  color: var(--ms-text-secondary);
  margin-bottom: var(--ms-space-s);
}
/* ══════════════════════════════════════════════════
   DAS AUSSEHEN EINES BEDIENELEMENTS — EINE STELLE
   ══════════════════════════════════════════════════
   Rand, Radius, Fuellung und Fokusring JEDES Feldes im System stehen hier und
   nirgends sonst. Die Liste unten ist die vollstaendige Aufzaehlung aller
   Feldbauformen; sie ist der Preis dafuer, dass CSS keine Mixins kennt.

   WARUM ES DIESE REGEL GIBT. Vorher hatte jede Bauform ihre eigene
   Felddefinition, und sie waren auseinandergelaufen:

     .form-input/.form-select/.form-textarea   1.5px   forms.css
     .search-input                             1px     pages.css
     .prod-date-field input                    1px     pages.css
     .wizard-date-input input                  1px     pages.css
     .input-group-text                         1.5px   data.css

   In der Werkzeugleiste der Bestellliste standen drei davon nebeneinander. Bei
   einem Rand aus halbdurchsichtigem Grau (--ms-border-control ist ein Alias von
   --ms-border-divider, 40% Deckkraft) sieht eine andere Staerke nicht wie eine
   andere Staerke aus, sondern wie eine andere FARBE — so wurde es auch gemeldet.

   WAS HIER FESTGELEGT IST: Rand, Radius, Fuellung, Textfarbe, Fokusring — und
   seit dem 2026-09-09 die SCHRIFTGROESSE (--ms-control-font).

   Bis dahin stand hier das Gegenteil: „nicht festgelegt sind Hoehe,
   Innenabstand und Schriftgroesse — ein Filterfeld in einer dichten Leiste
   darf kleiner sein als ein Feld in einer Maske". Die Freiheit wurde neunmal
   genutzt und ergab fuenf verschiedene Groessen; in Schritt 4 des Assistenten
   standen 18px-Textfelder neben einem 14px-Datumsfeld, im eigenen Auswahlfeld
   sogar drei Groessen in einem einzigen Bedienelement. Der Betreiber hat die
   Freiheit am 2026-09-09 zurueckgenommen: „Sobald es Formularfelder sind, soll
   dies identisch sein." Begruendung des Wertes am Token in tokens.css.

   Hoehe und Innenabstand bleiben frei — sie tragen die Dichte des Bereichs.

   NEUE FELDER kommen in diese Aufzaehlung, nicht in eine eigene Regel.
   `bin/design-check` meldet jede Randdefinition mit --ms-border-control
   ausserhalb dieser Datei. Hintergrund: designsystem.md Abschnitt 6. */
/* EIN VERBORGENES FELD BELEGT KEINEN PLATZ.

   Das Formularthema haengt `.form-input` an jedes einfache Feld — auch an
   `type="hidden"` (v2/_form_theme.html.twig; nur `data-validate` bleibt dort
   aussen vor). Mit der gemeinsamen Feldregel darunter bekommt das verborgene
   Feld damit Rand, Polsterung und volle Breite. Sichtbar wird es nicht, aber
   in einem Raster oder einer Flex-Reihe zaehlt es als Kind und verschiebt
   alles danach um eine Spalte. Aufgefallen an der Ablaufmaske (2026-09-08),
   wo Kopfzeile und Zeile dadurch nicht mehr bueendig standen. */
.form-input[type="hidden"] { display: none; }

.form-input, .form-select, .form-textarea,
.search-input,
.prod-date-field input,
.wizard-date-input input,
.v2-datepicker-display,
.detail-notes-area {
  border: var(--ms-control-border-w) solid var(--ms-border-control);
  border-radius: var(--ms-control-radius);
  background: var(--ms-control-bg);
  color: var(--ms-text-primary);
  font-size: var(--ms-control-font);
  outline: none;
  transition: border-color var(--ms-transition-base), box-shadow var(--ms-transition-base);
  box-sizing: border-box;
}

/* Der Fokuszustand traegt die Zusicherung aus WCAG 1.4.11 (designsystem.md,
   Abschnitt 6): der RUHENDE Rand darf dezent sein, weil das bediente Feld den
   Akzent als Rand traegt. Deshalb gehoert er in denselben Regelsatz.

   `:focus-within` neben `:focus`, weil es zwei Bauformen gibt: ein echtes
   Eingabefeld nimmt den Fokus selbst an, der Chip-Behaelter der
   Schlagwortsuche ist ein <span> und kann das nicht — dort sitzt der Fokus im
   Tippfeld darin. Ohne die zweite Bedingung waere die Schlagwortsuche das
   einzige Bedienelement des Systems ohne sichtbaren Fokusring. */
.form-input:focus, .form-select:focus, .form-textarea:focus,
.search-input:focus, .search-input:focus-within,
.prod-date-field input:focus,
.wizard-date-input input:focus,
.v2-datepicker-display:focus,
.detail-notes-area:focus {
  border-color: var(--ms-accent);
  box-shadow: var(--ms-control-focus-ring);
}

.form-input, .form-select, .form-textarea {
  width: 100%;
  padding: var(--ms-space-3) var(--ms-space-4);
  /* `min-width: 0` IST HIER DER KERN UND KEIN BEIWERK (Betreiber, 2026-09-11).
     Ein Feld in einer Rasterspalte (`.md-filterrow`, zwei mal `1fr`) oder in
     einem Flexverbund wird von sich aus NIE schmaler als sein laengster
     unteilbarer Inhalt — das ist die „automatische Mindestgroesse". Bei einer
     Firma wie „KPMG AG Wirtschaftspruefungsgesellschaft" zwang das die Spalte
     auf die Breite des Namens: gemessen 409 px in einer 354 px breiten Spalte,
     das Nachbarfeld wurde aus der Karte geschoben und war nicht mehr zu sehen.
     Mit `min-width: 0` gilt wieder, was das Raster sagt, und gekuerzt wird der
     TEXT (`.select-label` bringt `text-overflow: ellipsis` mit). */
  min-width: 0;
}
/* Einzeilige Felder auf die gemeinsame Bedienhoehe (designsystem.md, Regel 7).
   Vorher bildeten sie ihre Hoehe aus Schrift plus Innenabstand — gemessen 49px
   gegen 46px des Knopfs darunter. Bei uebereinanderliegenden Feldern faellt das
   weniger auf als in einer Reihe, sichtbar ist es trotzdem, und die Regel gilt
   fuer beide Anordnungen.
   Der Mehrzeiler ist ausgenommen: er MUSS wachsen duerfen. */
.form-input, .form-select, .v2-datepicker-display {
  height: var(--ms-control-h);
  padding-top: 0;
  padding-bottom: 0;
}
/* Das Anzeigefeld des Datumsauswaehlers ist ein einzeiliges Textfeld und
   gehoert deshalb in dieselben Regeln wie .form-input — es stand in KEINER
   davon und war damit das einzige Bedienelement des Systems ohne Rand,
   Polsterung und Fokusring. Sichtbar in sechs Masken (Anmeldeprotokoll,
   beide Auswertungen, Produktuebersicht, Bestellschritte 2 und 4).
   Breite und Schriftgroesse kommen aus derselben Quelle wie dort. */
.v2-datepicker-display {
  width: 100%;
  padding-left: var(--ms-space-4);
  /* Kalendersymbol im Feld, wie in myOFFICE (`.mo-dp-icon`). Als Hintergrund
     und nicht als zweites Element: dieselbe Bauform, mit der `.form-select`
     schon seinen Pfeil traegt — kein zusaetzliches Markup, das in sechs
     Vorlagen nachgezogen werden muesste. Der Ton ist --ms-text-tertiary
     (#86868B), derselbe wie beim Auswahlpfeil. */
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2386868B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect width='18' height='18' x='3' y='4' rx='2'/%3E%3Cpath d='M3 10h18M8 2v4M16 2v4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--ms-space-3) center;
  padding-right: calc(var(--ms-space-3) * 2 + 16px);
}
.form-select {
  appearance: none; cursor: pointer;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%2386868B' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right var(--ms-space-3) center;
  /* Platz fuer den Pfeil als Rechnung und nicht als Zahl: Abstand rechts,
     Symbolbreite, Abstand zum Text. Dieselbe Form wie bei
     .v2-datepicker-display. Ergibt dieselben 40px wie vorher. */
  padding-right: calc(var(--ms-space-3) * 2 + 16px);
}
.form-textarea { resize: vertical; min-height: 80px; }

/* ── Datei-Feld ──
   Die Schaltflaeche IM Feld kommt vom Browser, und sie war damit die einzige
   Bedienflaeche des Systems ohne eine eigene Regel: grauer Kasten, eigene
   Schrift, eigene Hoehe. Sichtbar in der Sicherungszentrale und in der
   Ersteinrichtung (gemeldet vom Betreiber, 2026-09-08: „alle Formularelemente
   auch in unserem Designsystem"). `.form-input` allein reichte nicht — es
   gestaltet den Rahmen um die Schaltflaeche, nicht sie selbst.

   `::file-selector-button` ist die eine Stelle dafuer, und sie traegt die
   Werte des abgesetzten Knopfs: genau das ist sie, ein zweitrangiger Knopf in
   einem Feld. Die Trennlinie ist der STEUERTON und nicht der Flaechenton —
   sie sitzt in einem Bedienelement (designsystem.md, Abschnitt 6).

   Die Polsterung links entfaellt, damit die Schaltflaeche an der Feldkante
   sitzt; ihre eigene bringt sie mit. Ohne das stuende sie 16px eingerueckt in
   einem Feld, dessen Rand sie beruehren soll. */
.form-input[type="file"] { padding-inline-start: 0; cursor: pointer; }
.form-input[type="file"]::file-selector-button {
  height: 100%;
  margin-inline-end: var(--ms-space-3);
  padding: 0 var(--ms-space-4);
  border: 0;
  border-inline-end: var(--ms-control-border-w) solid var(--ms-border-control);
  border-radius: 0;
  background: var(--ms-bg-secondary);
  color: var(--ms-text-primary);
  font: inherit;
  font-weight: var(--ms-weight-medium);
  cursor: pointer;
  transition: background var(--ms-transition-fast);
}
/* Schleierflaeche wie an jeder anderen Hover-Flaeche des Systems (Regel 5) —
   es gibt keinen zweiten Grauton fuer „Flaeche eine Stufe dunkler", und einen
   dafuer zu erfinden waere ein Wert ausserhalb des Systems. */
.form-input[type="file"]:hover::file-selector-button {
  background: rgba(var(--ms-accent-rgb), var(--ms-tint));
}
/* Kompakte Textteile, die mit ihrem Inhalt wachsen. Die Mindesthoehe bleibt
   die des allgemeinen Textfelds (oben, aus dem Tokensystem abgeleitet), und
   `rows` im Formular ist der Rueckfall fuer Browser ohne `field-sizing`.
   Keine Hoehe wird per Skript oder Inline-Stil geschrieben. */
.form-textarea--auto { field-sizing: content; }

/* GESPERRTE FELDER — bis 2026-09-03 gab es dafuer gar keine Regel.
   Ein `disabled`-Feld sah damit aus wie ein leeres zum Ausfuellen: weisser
   Grund, schwarzer Text, nur der Rahmen der Browservorgabe war eine Spur
   blasser. Man klickte hinein und nichts geschah, ohne dass die Maske vorher
   gesagt haette, dass hier nichts zu entscheiden ist.

   Gedaempft wird ueber die FLAECHE und nicht ueber die Textfarbe: der Inhalt
   soll lesbar bleiben (er steht ja da, weil man ihn wissen will), nur die
   Einladung zum Tippen faellt weg. --ms-text-secondary erfuellt AA auf beiden
   Untergruenden, --ms-text-tertiary nicht (Designsystem, Regel 8). */
.form-input:disabled, .form-select:disabled, .form-textarea:disabled,
.form-input[readonly], .form-textarea[readonly] {
  background-color: var(--ms-bg-secondary);
  color: var(--ms-text-secondary);
  border-color: var(--ms-border-light);
  cursor: not-allowed;
}
.form-select:disabled { background-image: none; }

/* Erklaerung unter einem Feld — was aus der Eingabe folgt.
   ------------------------------------------------------------------
   Ein BAUTEIL und keine weitere Utility (Designsystem, Regel 4): die
   Artikel- und die Set-Maske brauchen es an neun Stellen, sonst waere
   an jeder eine eigene Absatzformatierung entstanden.

   Nach Regel 8 gedaempft ueber GROESSE und nicht ueber Helligkeit:
   `--ms-text-secondary` ist die letzte Stufe, die auf beiden
   Untergruenden AA erfuellt (4,66:1 / 5,07:1). `--ms-text-tertiary`
   liegt bei 3,33:1 und ist fuer Text, den man lesen soll, unzulaessig.

   Kein eigener Farb- oder Abstandswert, nur Tokens. */
.form-help {
  margin: var(--ms-space-2) 0 0;
  font-size: var(--ms-text-small);
  font-weight: var(--ms-weight-regular);
  color: var(--ms-text-secondary);
}

.form-help strong { font-weight: var(--ms-weight-semibold); color: var(--ms-text-primary); }
/* Gedaempfte Fassung fuer einen Hinweis, der eine Einordnung gibt und keine
   Anweisung ist („Adressen werden an der Firma gepflegt"). Dieselbe Groesse wie
   die Beizeile einer Tabellenzelle (.cell-meta); die Farbe bleibt
   --ms-text-secondary, weil --ms-text-tertiary fuer Text unzulaessig ist. */
.form-help--small { font-size: var(--ms-text-caption); }

/* ── Rueckmeldung des Autosave (assets/v2-autosave.js) ──
   Klein und ruhig: sie steht neben der Ueberschrift der Karte und soll sagen,
   dass gespeichert ist — nicht darum bitten, gelesen zu werden. Der Fehlerfall
   ist die einzige Stufe mit Signalfarbe, und dort ist sie berechtigt: die
   Aenderung liegt noch nicht auf dem Server.

   Die drei Textfarben sind die im System gepruefteten Paarungen auf Weiss
   (--ms-text-secondary 4,66:1, --ms-success-text 9,36:1, --ms-error-text
   6,54:1; siehe bin/design-check). */
.autosave-status { font-size: var(--ms-text-caption); color: var(--ms-text-secondary); }
.autosave-status.is-saved { color: var(--ms-success-text); }
.autosave-status.is-error { color: var(--ms-error-text); }
/* Fehlerfassung derselben Zeile — fuer Rueckmeldungen, die im Betrieb
   entstehen (Uebersetzungshilfe). Eigene Farbe braucht es nicht: der
   Fehlerton des Systems erfuellt AA auf beiden Untergruenden. */
.form-help.is-error { color: var(--ms-error-text); }

/* ── Optionsfelder (Auswahl aus wenigen festen Werten) ──
   Muster nach der Typ-Zeile in myOFFICE (.mo-radio-row): waagerecht, jede
   Option mit Beschriftung im eigenen <label>, damit auch ein Klick auf den
   Text trifft.

   WARUM ES DAS BRAUCHT: `form_div_layout` gibt eine aufgeklappte Auswahl als
   nackte Optionsfelder aus — im ersten Entwurf der Artikelmaske standen dort
   zwei Punkte OHNE jede Beschriftung. Das Thema (templates/v2/_form_theme)
   zeichnet sie jetzt mit diesem Bauteil.

   Waagerecht und nicht gestapelt: bei zwei bis drei Werten liest sich eine
   Zeile schneller als eine Liste, und die Maske bleibt kurz. */
.radio-row { display: flex; flex-wrap: wrap; gap: var(--ms-space-5); align-items: center; min-height: var(--ms-control-h); }
/* DIE BESCHRIFTUNG EINER OPTION IST EIN WERT, kein Beschriftungstext: sie
   steht fuer das, was im Feld staende, waere die Auswahl zugeklappt. Deshalb
   --ms-control-font wie im Feld und wie in der Liste des eigenen
   Auswahlfeldes (.select-menu .select-option) — und nicht die Groesse des
   Fliesstextes, die sie vorher erbte (18px beim Kunden, 15px in der
   Verwaltung, in beiden Faellen etwas anderes als das Feld daneben). */
.radio-option { display: inline-flex; align-items: center; gap: var(--ms-space-2); cursor: pointer; font-size: var(--ms-control-font); }
.radio-option input { flex: none; cursor: pointer; }

/* Abwandlung: AUSWAHLKARTEN. Wenn die Wahl eine Folge hat, die man kennen
   muss, gehoert die Erklaerung an die Option und nicht als Absatz darunter.
   Bei der Artikelart entscheidet sie ueber Bestandsfuehrung, Verfuegbarkeit
   und Wareneingang — ein Wort allein ("Bestandsartikel") sagt das niemandem,
   der die Anwendung neu bedient.

   Die Karte ist KEIN Knopf: der Radius bleibt deshalb --ms-radius-md und
   nicht die Pille (Regel 6). Angeklickt wird ein Optionsfeld, das sichtbar
   bleibt — die Karte ist seine Beschriftung, nicht sein Ersatz. */
.radio-row--cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--ms-space-3); align-items: stretch; }
.radio-row--cards .radio-option {
  align-items: flex-start;
  gap: var(--ms-space-3);
  padding: var(--ms-space-4);
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-md);
  background: var(--ms-bg-primary);
  transition: border-color var(--ms-transition-base), background var(--ms-transition-base);
}
.radio-row--cards .radio-option:hover { border-color: var(--ms-accent); }
/* :has() traegt hier die Auswahl, damit die Karte ohne Skript mitschaltet —
   dasselbe Mittel wie an anderen Stellen des Systems, in denen ein Zustand
   aus dem Formular kommt und nicht aus JavaScript. */
.radio-row--cards .radio-option:has(input:checked) {
  border-color: var(--ms-accent);
  background: rgba(var(--ms-accent-rgb), var(--ms-tint));
}
.radio-row--cards .radio-option:has(input:focus-visible) { box-shadow: var(--ms-control-focus-ring); }
.radio-card-text { display: flex; flex-direction: column; gap: var(--ms-space-1); }
.radio-card-title { font-weight: var(--ms-weight-semibold); }
/* Nach Regel 8 ueber die GROESSE gedaempft, nicht ueber Helligkeit. */
.radio-card-hint { font-size: var(--ms-text-small); color: var(--ms-text-secondary); }

/* ── Sprachreiter eines Formulars ──
   Nutzt das vorhandene Reiter-Bauteil (.nav-tabs/.nav-link in data.css) und
   ergaenzt nur, was ein <button> gegenueber einem <a> braucht. Ein eigenes
   Reiter-Bauteil waere die zweite Fassung derselben Gestalt gewesen (Regel 9).

   Reiter und nicht untereinander: die Uebersetzungsfelder standen im ersten
   Entwurf beide gleichzeitig da — Name und Beschreibung zweimal, ohne
   sichtbaren Unterschied, weil die Auszeichnung der Sprache fehlte. */
.nav-tabs .nav-link { background: none; cursor: pointer; font-family: inherit; }
.tab-pane[hidden] { display: none; }

/* ── Bestandszahl im Formular ──
   Der Bestand ist an dieser Stelle eine AUSKUNFT und kein Feld: geaendert wird
   er ueber „Einlagern" und „Korrigieren". Er steht deshalb wie ein Wert und
   nicht wie eine Eingabe — ein Zahlenfeld daneben waere ein zweiter, stiller
   Weg gewesen. Groesse aus der Kennzahlen-Stufe, damit er in der Zeile mit
   Mindestmenge und Einheit die Hauptaussage bleibt. */
.stock-figure {
  margin: 0;
  font-size: var(--ms-text-h4);
  font-weight: var(--ms-weight-bold);
  font-variant-numeric: tabular-nums;
  line-height: var(--ms-control-h);
}
.stock-figure-unit { font-size: var(--ms-text-small); font-weight: var(--ms-weight-regular); color: var(--ms-text-secondary); }

/* Der Beisatz in Klammern — bei der letzten Einlagerung das Datum
   (Betreiber, 2026-09-07). Eine Stufe leiser als die Einheit, damit die
   Leserichtung bleibt: Zahl, Einheit, Datum. `nowrap`, weil ein Umbruch
   zwischen Zahl und Klammer die Zeile wieder zweizeilig machen wuerde — und
   genau das war der Grund, das Datum hier hereinzuholen. */
.stock-figure-note {
  font-size: var(--ms-text-small);
  font-weight: var(--ms-weight-regular);
  color: var(--ms-text-secondary);
  white-space: nowrap;
}

/* ── Die Zeile der Bestandsangaben ──
   KEIN ZWEITER ABSTAND UNTER DEN SPALTEN. `.form-row` bringt
   `margin-bottom: --ms-space-4` mit, damit Felder untereinander Luft haben —
   in EINER Rasterzeile ist dieser Abstand aber der zweite: die Luecke zwischen
   den Spalten setzt schon `g-3`. Er hat die Karte um 16px verlaengert, ohne
   etwas zu trennen (gemessen am 2026-09-07).

   Die Handlungen stehen in derselben Zeile am rechten Rand. `gap` fuer die
   Knoepfe untereinander, weil die Spalte ein Flexverbund ist und
   `.form-actions` hier nicht passt — dessen `margin-top` waere in einer Zeile
   ein Versatz nach unten. */
.stock-row .form-row { margin-bottom: 0; }
.stock-row-actions { gap: var(--ms-space-3); }
/* Das Auswahlfeld der Einheit ist ein SCHMALES Feld, kein Zeilenfeld: sein
   laengster Eintrag ist „Stück". 8rem sind Text, Polsterung und Auswahlpfeil —
   und sie bleiben stehen, wenn die Wahl von „Stück" auf „m" wechselt. Das
   vorhandene `.form-select--auto` waere hier falsch: es misst den GEWAEHLTEN
   Eintrag (die eigene Liste aus v2-select.js zeichnet einen Knopf mit dem
   aktuellen Text), das Feld schrumpfte also beim Umstellen. In einer Filterleiste
   ist das gleichgueltig, in einem Formular ist es eine wandernde Kante. */
.stock-row-unit .form-select { width: 8rem; }

/* Zwei Zustaende derselben Zahl (2026-09-04): der Strich steht, wo es nichts
   zu melden gibt — nicht als fehlender Wert, sondern als Antwort „keine". Er
   traegt deshalb die zweite Textfarbe und nicht die volle. Die Fehlmenge
   dagegen ist eine Aufforderung und wird ausgezeichnet. Beide erben Groesse
   und Ziffernbreite von .stock-figure, damit die Spalten in der Zeile
   uebereinanderstehen. */
.stock-figure--empty { color: var(--ms-text-secondary); font-weight: var(--ms-weight-regular); }
/* Eine Zahl ist Text und braucht 4,5:1. --ms-warning erreicht auf Weiss
   2,20:1, --ms-warning-text 6,80:1. */
.stock-figure--warn { color: var(--ms-warning-text); }

/* ── Bildfeld eines Datensatzes ──
   Vorschau links, Bedienung rechts. Die Vorschau ist QUADRATISCH, weil das
   Bild es ist — ein anderes Verhaeltnis hier waere eine Zusage, die die Datei
   nicht einloest. */
.image-field { display: flex; flex-wrap: wrap; gap: var(--ms-space-4); align-items: flex-start; margin-bottom: var(--ms-space-4); }
.image-field-body { flex: 1 1 16rem; min-width: 0; }
.image-preview {
  flex: none;
  width: 7.5rem; height: 7.5rem;
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-md);
  background: var(--ms-bg-secondary);
  overflow: hidden;
}
.image-preview img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* Platzhalter: dasselbe Mass, damit die Maske beim ersten Bild nicht springt. */
.image-preview--empty {
  display: flex; align-items: center; justify-content: center;
  color: var(--ms-text-tertiary); font-size: var(--ms-text-h3);
}
/* `display: flex` oben SCHLAEGT das `hidden`-Attribut: die UA-Regel
   `[hidden] { display: none }` ist eine Browser-Regel, jede Autorenregel
   gewinnt dagegen. Folge im Betrieb: nach dem Zuschneiden standen ZWEI
   Kaesten da, das neue Bild und der leere Platzhalter daneben. Deshalb hier
   ausdruecklich — und an `.image-preview` allgemein, damit derselbe Fehler
   nicht beim naechsten Zustand wiederkommt. */
.image-preview[hidden] { display: none; }

/* Die Vorschau ist anklickbar: sie oeffnet das Bild in seiner grossen Stufe.
   Ein Knopf und kein Bild mit Klickbehandlung — Tastatur und Vorlesehilfe
   bekommen ihn damit von selbst. */
button.image-preview { padding: 0; cursor: zoom-in; }
button.image-preview:hover { border-color: var(--ms-border-control-hover); }
button.image-preview:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }

/* Bild in seiner grossen Stufe im Dialog. Hoehe begrenzt, damit der Dialog
   auf einem Laptop vollstaendig sichtbar bleibt. */
.image-view { display: block; max-width: 100%; max-height: 70vh; margin: 0 auto; border-radius: var(--ms-radius-md); }

/* Zuschnittflaeche im Dialog. Die Hoehe ist begrenzt, damit der Dialog auf
   einem Laptop vollstaendig sichtbar bleibt — sonst liegen die Knoepfe
   unterhalb des Fensterrands. */
/* ── Die Bilder eines Artikels ──
   Eine Reihe quadratischer Felder, umbrechend. Feste Kante (`--ms-image-tile`)
   und nicht anteilige Breite: die Felder sollen bei einem Bild genauso
   aussehen wie bei sechs, und ein Raster haette das erste Bild ueber die halbe
   Maske gezogen.

   `image-gallery-*` UND NICHT `image-tile-*`: der Name war schon vergeben.
   Die Set-Maske fuehrt in admin.css eine eigene `.image-tile` mit anderem
   Rahmen, und weil sie spaeter greift, nahm sie dem Produktbild hier seinen
   Akzentrahmen — im Browser nachgemessen am 2026-09-03. Zwei Bedeutungen unter
   einem Klassennamen sind kein Zufall, der sich von selbst aufloest. */
.image-gallery {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ms-space-3);
  align-items: flex-start;
  width: 100%;
}

.image-gallery-item {
  position: relative;
  flex: none;
  width: var(--ms-image-tile);
  margin: 0;
  border: var(--ms-control-border-w) solid var(--ms-border-light);
  border-radius: var(--ms-radius-md);
  background: var(--ms-bg-secondary);
  overflow: hidden;
}

/* DAS PRODUKTBILD, sichtbar ohne es zu lesen: Akzentrahmen und Beschriftung.
   Der Rahmen ist die Aussage, deshalb liegt er auf dem Feld und nicht auf dem
   Bild darin. `--ms-accent` und nicht eine eigene Farbe — es ist dieselbe
   Auszeichnung wie beim aktiven Listeneintrag. */
.image-gallery-item--main { border-color: var(--ms-accent); }

.image-gallery-view {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
/* DIE HOEHE FOLGT DER BREITE, sie wird nicht gesetzt.
   Vorher stand hier `height: var(--ms-image-tile)` — dieselbe Zahl wie die
   Kachelbreite. Mit `box-sizing: border-box` ist die INNENbreite der Kachel
   aber um die Raender kleiner (117 statt 120), der Kasten also 117x120 und
   nicht quadratisch. Ein quadratisches Bild stand darin mit `contain`
   mittig, und oben wie unten blieben 1,5 px Kachelgrund frei — im Hellmodus
   ein weisser Streifen unter der Umrandung (gemeldet am 2026-09-03).
   Nebenwirkung derselben Ursache: die Bildkacheln waren 123 px hoch, das
   Feld „Bild hinzufuegen" 120 — die Reihe stand ungleich.

   `object-fit: contain` bleibt: die abgelegten Bilder SIND quadratisch, und
   bei einem, das es einmal nicht ist, soll nichts abgeschnitten werden. */
.image-gallery-view img {
  display: block;
  width: 100%;
  /* `height: auto` ist Pflicht, nicht Zierde: das Bild traegt im Markup
     `width` und `height` als Attribute (gegen den Sprung beim Laden), und die
     wirken als Stilvorgabe. Ohne `auto` bleibt die Hoehe bei 160 px stehen und
     `aspect-ratio` kommt gar nicht zum Zuge. */
  height: auto;
  aspect-ratio: 1;
  object-fit: contain;
}
.image-gallery-view:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: -2px; }

/* Die Beschriftung liegt AUF dem Bild und nicht darunter: darunter waere das
   Feld je nach Beschriftung unterschiedlich hoch, und die Reihe stuende
   ungleich. */
.image-gallery-mark {
  position: absolute;
  inset: auto 0 0 0;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ms-space-1);
  padding: var(--ms-space-1) var(--ms-space-2);
  background: var(--ms-accent);
  color: var(--ms-on-accent);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-semibold);
}

.image-gallery-actions {
  position: absolute;
  inset: var(--ms-space-1) var(--ms-space-1) auto auto;
  display: flex;
  gap: var(--ms-space-1);
}
/* Die Knoepfe liegen auf dem Bild — ohne eigenen Grund waeren sie auf einem
   dunklen Foto nicht zu sehen. */
.image-gallery-actions .icon-action {
  background: var(--ms-bg-primary);
  box-shadow: var(--ms-shadow-sm);
}

/* Das Feld zum Hinzufuegen hat dasselbe Mass wie ein Bildfeld, damit die Reihe
   nicht springt, sobald ein Bild dazukommt. */
.image-gallery-add {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ms-space-2);
  width: var(--ms-image-tile);
  height: var(--ms-image-tile);
  border: var(--ms-control-border-w) dashed var(--ms-border-control);
  border-radius: var(--ms-radius-md);
  background: none;
  color: var(--ms-text-secondary);
  font-size: var(--ms-text-small);
  text-align: center;
  cursor: pointer;
}
.image-gallery-add:hover { border-color: var(--ms-accent); color: var(--ms-accent); }
.image-gallery-add:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 3px; }

/* ── Eine Auskunft in der Fusszeile eines Dialogs ──
   `margin-right: auto` schiebt sie an den linken Rand, die Knoepfe bleiben
   rechts (die Fusszeile ist eine Flex-Zeile mit `justify-content: flex-end`).
   Kein eigener Aussenabstand senkrecht: die Zeile soll auf der Hoehe der
   Knoepfe stehen, nicht darunter. */
.modal-footer-note { margin: 0 auto 0 0; }

/* DER TEXT WEICHT, NICHT DER KNOPF.
   In einer Flex-Zeile schrumpfen ab Werk ALLE Kinder. Der laengere Hinweis
   nahm sich seinen Platz, und „Zuschneiden und uebernehmen" brach auf zwei
   Zeilen um (gemeldet am 2026-09-03 mit Bild). Die Knoepfe behalten deshalb
   ihre Inhaltsbreite; der Umbruch gehoert in den Satz, nicht in die
   Beschriftung eines Knopfes.

   Nur in Fusszeilen MIT Hinweis: ohne ihn ist Platz genug, und eine Regel
   fuer jeden Dialog der Anwendung waere mehr Aenderung als Anlass. */
.modal-footer:has(> .modal-footer-note) > button { flex: none; }

/* ── Eine Erklaerung an der Spitze eines Dialogs ──
   Sie gehoert weiter weg von dem, was sie erklaert — sonst liest sie sich als
   Fussnote der Trennlinie statt als Einleitung des Bereichs darunter (gemeldet
   am 2026-09-02 zum Zuschnitt-Dialog).

   NUR DER UNTERE ABSTAND steht hier. Den oberen loest inzwischen die Huelle
   selbst: `.modal-body` traegt senkrecht dasselbe Mass wie Kopf und Fuss, und
   `.modal-body > :first-child` nimmt dem ersten Kind seinen Aussenabstand
   (overlays.css). Eine eigene Ausnahme daneben wuerde die Trennlinie wieder
   aus der Mitte schieben — nur in die andere Richtung. */
.modal-body > .form-help:first-child { margin-bottom: var(--ms-space-5); }

/* Die Flaeche gibt den PLATZ vor; article-image_controller.js schreibt daraus
   die tatsaechliche Groesse in der Form des Bildes (deshalb `margin-inline`:
   eine schmalere Flaeche steht sonst links statt in der Mitte). */
.crop-canvas {
  display: block;
  width: 100%;
  height: 60vh;
  max-height: 32rem;
  margin-inline: auto;
  background: var(--ms-bg-secondary);
}

/* ── Anfasser der Zuschnittauswahl ──
   cropperjs zeichnet sie ab Werk als 5-px-Punkt in halbdurchsichtigem Blau,
   in einem 15-px-Kasten ohne Fuellung. Auf einem Foto sieht man davon
   praktisch nichts (nachgefragt am 2026-09-02).

   GESTALTET WIRD DER HOST, nicht der Punkt darin: der steckt im Schatten-DOM
   des Elements und ist von aussen nicht erreichbar. Das Wirtselement dagegen
   gehoert dem Dokument — Autorenregeln gewinnen dort gegen die `:host`-Regeln
   der Bibliothek. Der eingebaute Punkt bleibt, verschwindet aber in der
   Fuellung.

   Groesse 16 px bei einem Versatz von -8 px (aus der Bibliothek): damit sitzt
   die Mitte des Anfassers genau auf der Ecke.

   Akzent gefuellt mit weissem Rand und Schatten: das ist auf hellen UND auf
   dunklen Bildbereichen zu erkennen. Ein Rand allein verschwindet auf
   gemustertem Grund. */
.crop-canvas cropper-handle[action$="-resize"] {
  width: 16px; height: 16px;
  background: var(--ms-accent);
  border: 2px solid var(--ms-bg-primary);
  border-radius: var(--ms-radius-sm);
  box-shadow: var(--ms-shadow-sm);
}
.crop-canvas cropper-handle[action$="-resize"]:hover { background: var(--ms-accent-hover); }

/* Die Kante der Auswahl selbst — ohne sie schwebt der Bereich konturlos im
   Bild. Zusaetzlich zum `outlined`-Merkmal der Bibliothek, weil deren Linie
   ihre Farbe aus einer Eigenschaft des Elements zieht und nicht aus unseren
   Tokens. */
.crop-canvas cropper-selection { outline: 2px solid var(--ms-accent); outline-offset: -1px; }

.form-grid { display: grid; gap: var(--ms-space-4); }
/* `minmax(0, 1fr)` und nicht `1fr` — dieselbe Falle wie bei `.form-grid-3`
   (dort am 2026-09-06 behoben): eine Rasterspalte ist von Haus aus mindestens
   so breit wie ihr Inhalt. „Corning Optical Communications GmbH & Co. KG" zog
   die erste Spalte auf 420px, waehrend die zweite auf 301px blieb — zwei
   gleichrangige Felder in zwei Breiten. Aufgefallen, als die Zuordnung des
   Katalogs ohne Kundengruppenfeld auf zwei Spalten fiel (2026-09-09). */
.form-grid-2 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
/* Drei Felder in einer Zeile — fuer Angaben, die zusammengehoeren und einzeln
   schmal sind (Katalog: Firma, Kundengruppen, Ablauf). Unter 900px, der
   Bruchstelle des Systems, untereinander: drei Auswahlfelder in einer
   Spaltenbreite waeren nicht mehr lesbar. */
/* `minmax(0, 1fr)` und nicht `1fr`: eine Rasterspalte ist von Haus aus
   mindestens so breit wie ihr Inhalt. Ein langer Firmenname zog die erste
   Spalte deshalb auf 268px, waehrend die beiden daneben auf 180px blieben —
   drei gleichrangige Felder in drei verschiedenen Breiten. Mit der Null als
   Mindestmass teilen sich die drei die Zeile wirklich zu Dritteln, und die
   Kuerzung im Feld greift statt die Spalte zu dehnen. */
.form-grid-3 { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1fr); }
/* AUCH EINSPALTIG BRAUCHT ES `minmax(0, …)`, und das war bis zum 9. September
   2026 uebersehen: die Regel darueber wurde korrigiert, diese hier nicht.
   `1fr` ist die Kurzform von `minmax(auto, 1fr)`, und `auto` heisst als
   Mindestmass „so breit wie der Inhalt mindestens sein will". Ein
   Auswahlfeld will so breit sein wie seine laengste Option — gemessen am
   Baukasten der Auswertung auf einem 390px-Schirm: die Spalte wurde 517px
   breit, obwohl das Formular 294px hatte. Sichtbar war es NICHT als
   Ueberlauf der Seite, weil `.card` mit `overflow: hidden` abschneidet: die
   rechte Haelfte der Felder war schlicht weg. */
@media (max-width: 900px) { .form-grid-3 { grid-template-columns: minmax(0, 1fr); } }
.form-grid-full { grid-column: 1 / -1; }
/* Gleiche Ueberlegung wie beim Optionsfeld: der Satz neben dem Kaestchen ist
   das, was angekreuzt wird, und damit ein Wert. Die gedaempfte FARBE bleibt —
   geaendert wurde am 2026-09-09 nur die Groesse. */
.form-checkbox {
  display: flex; align-items: center; gap: var(--ms-space-2);
  font-size: var(--ms-control-font); color: var(--ms-text-secondary); cursor: pointer;
}
.form-checkbox input { accent-color: var(--ms-accent); width: 16px; height: 16px; }
/* Ankreuz- und Optionsfeld tragen NICHT `.form-input` (siehe Formularthema):
   sie sind keine Eingabefelder mit Bedienhoehe und Rahmen. Ihre Groesse steht
   hier einmal, statt sie ueber einen Elternselektor zurueckzuholen. */
/* `flex: none` IST HIER KEINE FEINHEIT, SONDERN DIE GROESSE (Befund des
   Betreibers, 2026-09-16). `.form-checkbox` ist ein Flex-Behaelter, und ein
   Flex-Element schrumpft von sich aus, wenn der Text daneben nicht in eine
   Zeile passt: gemessen im Kachel-Dialog standen sechs Kaestchen mit 16,0 /
   14,0 / 13,6 / 16,0 / 13,0 / 16,0 Pixel Breite nebeneinander — jedes genau
   dort schmaler, wo die Beschriftung umbrach. Die HOEHE blieb bei 16, also
   wurden aus Quadraten Ovale. Ein Ankreuzfeld hat eine Groesse, und die haengt
   nicht an der Laenge seines Satzes. */
.form-check-input,
.form-radio-input { accent-color: var(--ms-accent); width: 16px; height: 16px; margin: 0; flex: none; }
/* ── Ankreuzfeld mit MEHRZEILIGER Beschriftung ──
   Groesserer Abstand zwischen Kaestchen und Beschriftung, und das Kaestchen
   steht an der ERSTEN Zeile statt in der Mitte des Ganzen.

   WARUM DIE MITTE FALSCH IST (gemeldet vom Betreiber, 2026-09-14). `.form-checkbox`
   zentriert senkrecht, und das ist richtig, solange die Beschriftung eine Zeile
   hat. Hier hat sie je Eintrag eine andere Hoehe — Name plus Erklaersatz, der
   bei „Externe Partner" ueber zwei Zeilen laeuft. Das Kaestchen rutschte damit
   je nach Textlaenge nach unten, und die Abstaende zwischen den Kaestchen einer
   Liste waren ungleich, obwohl die Eintraege gleich weit auseinanderstehen. Wer
   die Liste abhakt, zielt aber auf die Kaestchen und nicht auf die Saetze.

   DER VERSATZ IST GEMESSEN: die erste Zeile ist 1,6 x 16px = 25,6px hoch
   (Zeilenhoehe aus reset.css, Schriftgroesse `--ms-control-font`), das Kaestchen
   16px — es gehoert also 4,8px unter die Oberkante, damit es auf der Mitte
   DIESER Zeile sitzt. `--ms-space-1` ist 4px und damit die Stufe des Systems,
   die dem am naechsten kommt; eine eigene Zahl waere hier ein individuelles
   Mass fuer nichts. Im Browser nachgemessen (Nutzermaske, Eintraege von einer,
   zwei und drei Zeilen): das Kaestchen steht in ALLEN dreien 0,7px ueber der
   Mitte der ersten Textzeile — derselbe Versatz, unabhaengig von der Texthoehe.
   Genau das war das Ziel. */
.form-checkbox--wide { align-items: flex-start; gap: var(--ms-space-3); }
.form-checkbox--wide .form-check-input { margin-top: var(--ms-space-1); }
/* Eine SPALTE aus solchen Ankreuzfeldern — die Liste, die man abhakt.
   Steht hier und nicht in data.css, weil sie zum Ankreuzfeld gehoert und nicht
   zu einer Maske. `.perm-members` (data.css) ist dasselbe Bauteil unter einem
   engeren Namen; es bleibt unangetastet, weil an ihm die Verwaltungsmasken
   haengen. Wer dort das naechste Mal arbeitet, kann es hierher zusammenlegen. */
.choice-list {
  display: flex;
  flex-direction: column;
  gap: var(--ms-space-3);
}
/* Der Erklaersatz gehoert UNTER den Namen und nicht dahinter. `.form-help` ist
   fuer einen Absatz gedacht (er bringt seinen Aussenabstand mit); als `<span>`
   im Beschriftungstext fliesst er sonst in dieselbe Zeile, und aus „Bestand
   beachten" plus Erklaersatz wird ein einziger Fliesstext. Die Regel steht
   BEWUSST an dieser Liste und nicht an `.form-help--small`: an dem Bauteil
   haengen die Verwaltungsmasken, und ihr Aussehen ist hier nicht die Frage. */
.choice-list .form-help {
  display: block;
  margin-top: var(--ms-space-1);
}

/* Auswahlfeld nur so breit wie sein Inhalt, statt die Zeile zu fuellen —
   fuer Filter, die neben anderen Bedienelementen in einer Leiste stehen. */
.form-select--auto { width: auto; }

/* ── Eigene Liste fuer Auswahlfelder (assets/v2-select.js) ──
   Das `<select>` bleibt im DOM und behaelt Name, Wert und Pflichtangabe; an
   seine Stelle tritt ein Knopf mit DENSELBEN Klassen, damit jede vorhandene
   Layoutregel weiter greift. Hier stehen nur die Unterschiede, die ein Knopf
   gegenueber einem Auswahlfeld braucht.

   Die Liste selbst leiht das Aussehen des Aufklappmenues (`.dropdown-menu`,
   `.dropdown-item` in navigation.css) — dasselbe Bauteil fuer dieselbe Sache.
   Sie haengt am `<body>` und ist deshalb `fixed`: in einer Karte oder
   Tabellenzelle mit `overflow` waere sie abgeschnitten. */
.select-control { text-align: left; }
.select-label { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Ein Platzhalter ist keine Angabe, sondern die Abwesenheit einer. */
.select-label--placeholder { color: var(--ms-text-secondary); }
/* ZWEI Klassen im Selektor, nicht eine: `.dropdown-menu` setzt
   `position: absolute`, und navigation.css wird NACH forms.css geladen — bei
   gleicher Spezifitaet gewaenne dort `absolute`. Die Liste haengt aber am
   `<body>` und wird aus Ansichtskoordinaten platziert
   (`getBoundingClientRect`); auf einer gerollten Seite stuende sie damit um
   die Rollhoehe versetzt. Ueber die Spezifitaet geloest und nicht mit
   `!important`. */
.dropdown-menu.select-menu {
  position: fixed;
  top: 0; left: 0;
  /* Ueber dem DIALOG und nicht nur ueber der Seite: die Liste haengt am
     <body> und steht damit ausserhalb des Stapels ihres Feldes — auf der
     Stufe der Aufklappmenues (200) verschwand sie in einem Dialog (310)
     dahinter. Begruendung am Token --ms-z-detached. */
  z-index: var(--ms-z-detached);
  /* Der Kopf mit dem Suchfeld bleibt stehen, nur die Liste rollt — deshalb
     rollt nicht mehr das Menue selbst, sondern `.select-list` darin. 22rem
     statt 16: die 16 waren die Liste, der Kopf kommt darueber. */
  max-height: 22rem;
}
.dropdown-menu.select-menu.show { display: flex; flex-direction: column; }
.select-list { flex: 1 1 auto; overflow-y: auto; min-height: 0; }
/* ── Feste Hoehe, damit das Suchen das Fenster nicht springen laesst ──
   (Weisung des Betreibers, 2026-09-16)

   Im Adressdialog stand die Liste auf ihrer natuerlichen Hoehe: vierzehn
   Adressen machten ein hohes Fenster, ein getipptes „essen" ein winziges. Wer
   sucht, tippt aber weiter — und das Fenster sprang unter der Hand.

   Die Hoehe steht deshalb FEST, und zwar auf so vielen Zeilen, wie die Liste
   UNGEFILTERT hat, hoechstens zehn. Sie wird beim Oeffnen gesetzt und aendert
   sich beim Filtern nicht mehr; was nicht hineinpasst, rollt.

   DIE ZEILENHOEHE IST GEMESSEN und nicht gerechnet: 3,6rem fuer eine Zeile aus
   Polsterung und zwei Textzeilen (`.dropdown-item--stacked`). Eine Rechnung
   aus den Tokens traefe es nicht, weil die Zeilenhoehe der beiden Textzeilen
   nicht aus dem Abstandssystem kommt. Bricht ein langer Name um, wird die
   zehnte Zeile angeschnitten — das ist kein Mangel, sondern der Hinweis, dass
   es weitergeht. */
/* `flex: 0 1 auto` und nicht `0 0 auto`: waechst sie nicht, DARF sie aber
   schrumpfen. Auf einem kurzen Fenster passen zehn Zeilen nicht mehr in den
   Dialog, und eine Liste, die nicht nachgibt, waere unten abgeschnitten —
   samt der Eintraege, an die man dann nicht mehr herankommt. Mit `1` gibt sie
   nach und rollt in sich weiter; wo Platz ist, bleibt die Hoehe die
   gerechnete. */
.select-list--fest { flex: 0 1 auto; height: calc(var(--ms-rows, 10) * 3.6rem); }
/* Stufen wie bei der Anteilsleiter `.fill-*` (base.css): die Klasse setzt nur
   den Wert, die Hoehe daraus macht das Bauteil. Zehn genuegen — mehr Zeilen
   zeigt die Liste nicht. */
.rows-1  { --ms-rows:  1; }
.rows-2  { --ms-rows:  2; }
.rows-3  { --ms-rows:  3; }
.rows-4  { --ms-rows:  4; }
.rows-5  { --ms-rows:  5; }
.rows-6  { --ms-rows:  6; }
.rows-7  { --ms-rows:  7; }
.rows-8  { --ms-rows:  8; }
.rows-9  { --ms-rows:  9; }
.rows-10 { --ms-rows: 10; }
/* Die Regel dazu, dass `display: flex` das `[hidden]` schlaegt, steht seit dem
   2026-09-16 an `.dropdown-item` selbst (navigation.css) — sie gilt damit auch
   fuer jede andere Liste, die sich das Bauteil leiht. Hier stand sie an der
   engeren Klasse und fehlte dem Adressdialog. */
/* Ueberschrift einer Gruppe in der Auswahlliste (aus <optgroup>). Dieselbe
   Anmutung wie ein Tabellenkopf: Versalien, Kaptionsgroesse, gedaempft — sie
   gliedert und darf nicht wie ein waehlbarer Eintrag aussehen. Ein
   Trennstrich oben statt einer Linie ueber die ganze Breite: die erste
   Gruppe steht direkt unter dem Suchfeld und braucht keinen. */
.select-group {
  padding: var(--ms-space-3) var(--ms-space-4) var(--ms-space-1);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-semibold);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--ms-text-secondary);
}
.select-group + .select-option { margin-top: 0; }
.select-group[hidden] { display: none; }
/* Ab der zweiten Gruppe eine Linie: sie trennt, was die Ueberschrift benennt. */
.select-option + .select-group {
  margin-top: var(--ms-space-2);
  border-top: 1px solid var(--ms-border-divider);
}

/* ── Suchkopf einer langen Liste (ab acht Eintraegen, v2-select.js) ──
   Bedienkonzept nach der `.mo-combo` von myOFFICE: Feld links, Trefferzahl
   rechts, darunter die Liste. Die Zahl ist kein Beiwerk — bei 140 Kontakten
   ist „7 von 140" die Rueckmeldung, dass die Suche etwas getan hat. */
.select-search {
  flex: 0 0 auto;
  display: flex; align-items: center; gap: var(--ms-space-3);
  padding: var(--ms-space-2) var(--ms-space-3);
  border-bottom: 1px solid var(--ms-border-light);
}
.select-search-input { flex: 1 1 auto; min-width: 0; }
.select-search-count {
  flex: 0 0 auto;
  font-size: var(--ms-text-caption);
  color: var(--ms-text-secondary);
  font-variant-numeric: tabular-nums;
}
.select-empty {
  padding: var(--ms-space-4);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}
/* Die Eintraege sind die WERTE DES FELDES und nicht die Punkte eines Menues:
   sie stehen genauso gross da wie der gewaehlte Wert im Feld darueber. Ohne
   diese Zeile erben sie `.dropdown-item` (--ms-text-small) — im Kundenbereich
   14px in einer Liste, die an einem 16px-Feld haengt. */
.select-option { justify-content: space-between; }
/* Zwei Klassen und nicht eine: `.dropdown-item` (navigation.css) setzt
   --ms-text-small und steht in der Ladereihenfolge SPAETER — bei gleicher
   Spezifitaet gewinnt die spaetere Regel, und der Eintrag blieb bei 14px.
   Gemessen. */
.select-menu .select-option { font-size: var(--ms-control-font); }
/* Der hervorgehobene Eintrag — Tastatur wie Maus fuehren denselben Zustand. */
.select-option.is-active { background: var(--ms-bg-secondary); }
/* Der gewaehlte Eintrag traegt ein Haekchen. Nicht die Akzentfarbe: in einer
   Liste aus fuenf Zeilen ist eine gefaerbte genauso schnell zu finden wie ein
   Zeichen, aber sie sieht aus wie ein Knopf. */
.select-option[aria-selected="true"]::after {
  content: '\2713';
  color: var(--ms-accent);
  font-weight: var(--ms-weight-semibold);
}
/* MEHRFACHAUSWAHL: ein Kaestchen vorn statt eines Haekchens hinten, und die
   Liste bleibt beim Ankreuzen offen (v2-select.js). Das Kaestchen ist ein
   echtes `<input type=checkbox>` und traegt deshalb dieselbe Akzentfarbe und
   Groesse wie jedes andere im System (siehe `.form-checkbox`); hier steht nur,
   dass der Haken hinten dann entfaellt. */
.select-option--multi { justify-content: flex-start; }
.select-option--multi::after { content: none; }
.select-option--multi input[type="checkbox"] { accent-color: var(--ms-accent); width: 16px; height: 16px; margin: 0; }

/* Zeile und Fehlerausgabe des v2-Formularthemas
   (templates/v2/_form_theme.html.twig). */
.form-row { margin-bottom: var(--ms-space-4); }
/* Der Datumsauswaehler ist von Haus aus `inline-block` — er steht sonst in
   Werkzeugleisten neben anderen Feldern. In einer Formularzeile soll er die
   Breite nehmen wie `.form-input` darueber und darunter; ein schmales
   Datumsfeld zwischen zwei ganzbreiten sieht aus wie ein Fehler.

   NUR DIE HUELLE. Das Anzeigefeld darin bringt Breite, Polsterung, Hoehe und
   Rand schon mit (siehe `.v2-datepicker-display` weiter oben) — und es
   bekommt KEIN `.form-input`, weil das die Daempfung fuer `[readonly]`
   anzoege. */
.form-row > .v2-datepicker { display: block; }
/* Der Abstand liegt OBEN, nicht unten: die Meldung steht seit dem 2026-09-09
   unter dem Feld (siehe _form_theme.html.twig) und braucht Luft zum Feld,
   nicht zur naechsten Zeile — die bringt `.form-row` selbst mit. */
.form-error {
  list-style: none; margin: var(--ms-space-1) 0 0; padding: 0;
  font-size: var(--ms-text-small); color: var(--ms-error-text);
}
.form-row.has-error .form-input,
.form-row.has-error .form-select,
.form-row.has-error .form-textarea { border-color: var(--ms-error); }

/* Aktionszeile am Ende eines Formulars — Knopf rechts, mit Luft nach oben. */
/* `gap` gehoert in die GRUNDFORM und nicht nur in ihre Abwandlungen. Sie
   hatte keine: `--tight`, `--gated` und `.form-actions-end` brachten je eine
   mit, die nackte Zeile nicht — und dort, wo sie allein steht (die beiden
   Bestandsknoepfe der Artikelmaske), klebten die Knoepfe aneinander (gemeldet
   am 2026-09-03). Der Wert ist derselbe wie in `--tight`, damit eine Zeile
   nicht anders aussieht, sobald jemand die Abwandlung dazunimmt. */
/* `flex-wrap: wrap` ist nicht Kosmetik: ohne es SCHRUMPFEN die Knoepfe, wenn
   die Leiste zu schmal wird, und die Beschriftung bricht IM Knopf. Gemeldet
   vom Betreiber am 2026-09-07 aus dem Fach-Dialog (560px breit): „Ebene 4
   hinzufuegen" und „Alle Ebenen ausser Betrieb nehmen" standen beide
   zweizeilig, jeder Knopf las sich als zwei. Mit `wrap` rutscht der zweite
   Knopf ganz in die naechste Zeile und behaelt seine Breite — ein Knopf ist
   ein Wort, keine Absatzflaeche. */
.form-actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--ms-space-3); margin-top: var(--ms-space-5); }

/* ── Rueckmeldung beim Tippen (assets/v2-validate.js) ──
   Drei Zustaende je Feld: FEHLT (Pflichtfeld leer oder Inhalt unbrauchbar),
   GUELTIG, NEUTRAL (optionales Feld, leer). Die Klassen setzt das Skript
   innerhalb eines Formulars mit data-validate-scope; welche Felder Pflicht
   sind, sagt das Formular selbst ueber data-required (aus der `required`-
   Option des Symfony-Feldes, gesetzt im Formularthema).

   Fehlend: Rand in --ms-invalid plus Tint-Flaeche — beim Aufschlagen der Maske
   schon sichtbar, damit man sieht, was zu tun ist, bevor man anfaengt.
   Gueltig: NUR der Rand wird gruen, keine Flaeche. Ein gruen geflutetes Feld
   liest sich wie eine Meldung; die Bestaetigung am Rand genuegt.
   Der Server bleibt die Schranke — hier steht Bequemlichkeit, keine Sicherung. */
/* `.v2-datepicker-display` STEHT HIER MIT IN DER AUFZAEHLUNG (2026-09-16).
   Es ist die vierte Bauform des gemeinsamen Feld-Regelsatzes weiter oben
   (Rahmen, Hoehe, Schriftgroesse) — nur bei der Rueckmeldung fehlte es. Das
   Datum war dadurch das einzige Feld des Bestellassistenten, das nie rot oder
   gruen wurde: seinen Wert traegt ein verborgenes Feld, gezeigt wird dieses
   hier, und es bekommt den Zustand ueber `data-validate-mirror`
   (assets/v2-validate.js) gereicht. */
.form-input.is-invalid, .form-select.is-invalid, .form-textarea.is-invalid,
.v2-datepicker-display.is-invalid {
  border-color: var(--ms-invalid);
  background-color: rgba(var(--ms-invalid-rgb), var(--ms-tint));
}
.form-input.is-valid, .form-select.is-valid, .form-textarea.is-valid,
.v2-datepicker-display.is-valid { border-color: var(--ms-valid); }
.form-input.is-invalid:focus, .form-select.is-invalid:focus, .form-textarea.is-invalid:focus,
.v2-datepicker-display.is-invalid:focus {
  border-color: var(--ms-invalid);
  box-shadow: 0 0 0 3px rgba(var(--ms-invalid-rgb), 0.15);
}
.form-input.is-valid:focus, .form-select.is-valid:focus, .form-textarea.is-valid:focus,
.v2-datepicker-display.is-valid:focus {
  border-color: var(--ms-valid);
  box-shadow: 0 0 0 3px rgba(var(--ms-valid-rgb), 0.15);
}
/* Waehrend eine Antwort des Servers aussteht (Eindeutigkeit der E-Mail):
   weder rot noch gruen, der Rand bleibt neutral, der Knopf gesperrt. */
.form-input.is-pending { border-color: var(--ms-border-control); }
/* Pflichtmarke an der Beschriftung. Farbe der Rueckmeldung, damit Marke und
   Feldzustand dasselbe sagen; nach dem Ausfuellen bleibt sie stehen — sie
   beschreibt die Regel, nicht den Zustand. */
.form-required { color: var(--ms-invalid-text); font-weight: var(--ms-weight-semibold); margin-left: var(--ms-space-05); }
/* Hinweis unter einem Feld, das inhaltlich nicht passt (E-Mail vergeben …).
   Vom Skript gefuellt; leer nimmt er keinen Platz. */
.form-feedback { display: none; margin-top: var(--ms-space-1); font-size: var(--ms-text-caption); color: var(--ms-invalid-text); }
.form-feedback:not(:empty) { display: block; }

/* Fusszeile eines Formulars mit Fortschritt: links „4 von 6 Pflichtfeldern",
   rechts der Knopf. Der Balken nutzt den Fuellgrad-Satz aus base.css
   (.fill-<n>), gesetzt vom Skript — kein Inline-Wert. */
/* Handlungen links, Zustand rechts — wie in den Stammdatenmasken von
   myOFFICE. Hier stand links der Pflichtfeld-Fortschritt (.form-progress mit
   Balken); er ist am 2026-09-02 gegangen, samt seiner Klassen: bei fuenf
   Feldern zaehlt niemand mit, welche noch fehlen, und das rot umrandete Feld
   sagt es an der Stelle, an der man es ausfuellt. */
/* Handlungszeile ohne den Abstand einer Formularfusszeile — fuer Knoepfe,
   die MITTEN im Formular stehen und zu dem Feld darunter gehoeren. */
.form-actions--tight { margin-bottom: var(--ms-space-3); gap: var(--ms-space-3); align-items: center; }
/* Knopfleiste mit Trennlinie darueber — fuer den Abschluss eines
   Dialogrumpfes, dessen Handlung dem GANZEN Dialog gehoert und nicht dem
   Abschnitt direkt darueber. Dieselbe Linie wie `.modal-body .section-title`
   (overlays.css), damit ein Dialog nur eine Art Trennlinie kennt. */
.form-actions--divided {
  padding-top: var(--ms-space-4);
  border-top: 1px solid var(--ms-border-divider);
}
.form-actions--gated { justify-content: space-between; align-items: center; gap: var(--ms-space-4); flex-wrap: wrap; }
.form-actions-end { display: inline-flex; align-items: center; gap: var(--ms-space-3); margin-left: auto; }
/* DAS GEGENSTUECK, das bisher fehlte: eine Gruppe am LINKEN Rand der
   Knopfzeile, die den Rest nach rechts schiebt (`margin-right: auto`).

   Wofuer: ein Knopf mit einem Erklaersatz daneben, waehrend die eigentliche
   Handlung rechts bleibt — „Verbindung pruefen · Fragt nur den Verbrauch ab"
   links, „Speichern" rechts. Ohne die Gruppe stuenden Knopf, Satz und
   Speichern als drei gleichrangige Elemente in einer Reihe, und bei
   `space-between` landete der Satz in der Mitte statt neben seinem Knopf.

   `--ms-space-4` statt `--ms-space-3` wie rechts: der Satz ist Fliesstext und
   kein Bedienelement, er braucht mehr Luft zum Knopf, um nicht wie seine
   Beschriftung zu wirken (gemeldet vom Betreiber, 2026-09-17). */
.form-actions-start { display: inline-flex; align-items: center; gap: var(--ms-space-4); margin-right: auto; }
/* EIN FORMULAR IN DER KNOPFZEILE SOLL NICHT SICHTBAR SEIN, nur sein Knopf.
   Dieselbe Regel wie `.aside-actions form` in shell.css, und aus demselben
   Anlass: zwei Handlungen gehoeren nebeneinander, duerfen aber nicht in
   DASSELBE Formular — die eine speichert, die andere nicht. `display: contents`
   loest die Box auf; der Knopf wird direktes Flex-Item und richtet sich mit
   den anderen aus. Die Formularfunktion bleibt unberuehrt. */
.form-actions form { display: contents; }

/* ── Suchfeld einer Werkzeugleiste ──
   In `.form-actions--gated` (eines links, eines rechts) soll die Suche den
   freien Platz nehmen und der Knopf seine Inhaltsbreite behalten. Ohne das
   waren beide gleich schmal, und das Suchfeld sah aus wie ein zweiter Knopf.
   Die Obergrenze ist eine Lesbarkeitsgrenze: ein Suchfeld ueber die ganze
   Breite einer 1300px-Karte laedt zu einem Satz ein, wo eine Artikelnummer
   hingehoert. */
.parts-search { flex: 1 1 auto; max-width: 28rem; }
/* Suchfeld der Tag-Auswahl (ab fuenfzehn Tags). Dieselbe Lesebreite wie oben:
   die Auswahl steht in einer Zeile ueber die ganze Kartenbreite, und ein
   Suchfeld darueber, das diese Breite mitnimmt, sieht wie ein Balken aus. */
.tag-search { max-width: 28rem; }

/* ── Zahlenfeld in einer Datentabelle ──
   Ein `.form-input` ist 100% breit. In einer Tabellenzelle heisst das je nach
   Spaltenraster „so breit wie die halbe Maske" oder „auf einen Streifen
   zusammengefallen"; beides war im Set-Inhalt zu sehen.

   WARUM NICHT `size`: das naheliegende Merkmal wirkt bei `type=number` nicht
   — die Browser ignorieren es (nachgemessen: 206px breite Felder).

   WARUM `rem` UND NICHT `ch`: 6.5rem sind 104px, und genau diese Zahl braucht
   auch die Spalte um das Feld (`.col-qty` in data.css). `ch` haengt an der
   Schriftgroesse des ELEMENTS — die Tabelle setzt 14px, das Eingabefeld 15px,
   und dieselbe Angabe `11ch` ergab dort 87px und hier 104px. Die Spalte
   beschnitt das Feld dann um 17px (client 111 zu scroll 128). Mit `rem`
   rechnen beide mit derselben Einheit.

   104px sind gerechnet: „1.000,00" braucht rund 65px, dazu zweimal
   --ms-space-4 Polster (die Breite gilt im Rahmenmodell fuer die ganze
   Schachtel) und Platz fuer die Pfeiltasten, die Safari bei `type=number`
   daneben setzt. */
.form-input--num { width: 6.5rem; }

/* ── Ablagefeld fuer eine Datei (ohne Fremdbibliothek) ──
   Die Sicherungsmaske nimmt ein Archiv per Ziehen ODER per Klick an; sie
   traegt dafuer kein Dropzone-Objekt, sondern ein verborgenes `input[type=file]`
   und drei Ereignisse am Feld selbst. Bis zum 8. September 2026 stand dort
   `.backup-dropzone` — eine Klasse, die es in keinem Stylesheet gab. Folge:
   das Feld hatte keinen Rahmen, und sein Symbol hing frei ueber der Zeile.

   GLEICHE GESTALT WIE `.dropzone--compact` in vendor.css, absichtlich: das ist
   die Form, fuer die sich das System bei Dateiablagen entschieden hat (eine
   Zeile, kein Kasten). Was dort mit zwei Klassen gegen die Bibliotheks-Stile
   ankaempfen muss, braucht hier nur eine.

   Der Name sagt, was es ist, und nicht, wo es zuerst gebraucht wurde
   (Regel 9): `.dropzone` selbst war vergeben — die Bibliothek setzt sie an
   ihr eigenes Markup. */
.filedrop {
  display: flex; align-items: center; justify-content: center;
  gap: var(--ms-space-3);
  flex-wrap: wrap;
  padding: var(--ms-space-3) var(--ms-space-4);
  background: var(--ms-bg-secondary);
  /* Gestrichelt und 2px: die Flaeche ist kein Eingabefeld, sondern ein Ziel.
     --ms-border-control und nicht --ms-border-light, weil ein Bedienelement
     mit Umriss 3:1 halten muss (WCAG 1.4.11, Designsystem Abschnitt 6). */
  border: 2px dashed var(--ms-border-control);
  border-radius: var(--ms-radius-md);
  text-align: center;
  cursor: pointer;
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
  transition: border-color var(--ms-transition-base), background-color var(--ms-transition-base);
}
.filedrop:hover,
.filedrop.is-dragover {
  border-color: var(--ms-accent);
  background: rgba(var(--ms-accent-rgb), var(--ms-tint));
}
.filedrop:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }
/* Das Symbol so gross wie die Textzeile daneben: in einer Zeile hat es keine
   eigene, in der es gross sein koennte. */
.filedrop > svg {
  width: var(--ms-icon-size-s); height: var(--ms-icon-size-s);
  color: var(--ms-text-tertiary);
  flex: 0 0 auto;
}
.filedrop-title { font-weight: var(--ms-weight-semibold); color: var(--ms-text-primary); }

/* ── Einladung zum Speichern ──
   Beim ANLEGEN eines Datensatzes. Muster aus myOFFICE: dort steht am unteren
   Bildrand ein Band, das zum Speichern auffordert, und erst danach ist der
   Rest des Datensatzes bearbeitbar.

   ES STEHT PERMANENT DA (Betreiber, 2026-09-09). Bis dahin erschien es erst,
   wenn kein Pflichtfeld mehr fehlte — es sagte damit erst etwas, wenn man es
   nicht mehr brauchte. Jetzt wechselt nur der Satz darin
   (`[data-validate-open]` / `[data-validate-ready]`, v2-validate.js), und der
   Knopf ist gesperrt, solange etwas fehlt.

   Gruen wie `.stage-check.is-ok`: dieselbe Aussage („es ist alles da"),
   deshalb dieselbe Flaeche und dieselbe Schriftfarbe. */
.save-invite {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-3);
  margin-top: var(--ms-space-4);
  padding: var(--ms-space-3) var(--ms-space-4);
  border-radius: var(--ms-radius-md);
  background: var(--ms-success-muted);
  color: var(--ms-success-text);
  font-size: var(--ms-text-small);
}
/* KLEBEND AM UNTEREN BILDRAND, solange die Karte im Bild ist. Kein `fixed`:
   das laege ueber der Navigationsspalte und muesste ihre Breite kennen — ein
   zweites Mass neben `--ms-nav-w`, das beim naechsten Umbau falsch wird.
   Der Rand nach unten ist der Abstand zum Fensterrand, der Schatten hebt das
   Band von dem ab, was darunter durchlaeuft. */
.save-invite--sticky {
  position: sticky;
  bottom: var(--ms-space-4);
  z-index: 1;
  box-shadow: var(--ms-card-shadow);
}
/* Der Satz nimmt den Platz, der Knopf rueckt an das Ende der Zeile — und in
   einer engen Spalte darunter, weil die Zeile umbrechen darf. */
.save-invite-text { display: flex; align-items: center; gap: var(--ms-space-2); flex: 1 1 auto; min-width: 0; }
/* Die Knoepfe stehen am Ende der Zeile — auch dann, wenn sie in einer engen
   Spalte unter den Satz rutschen. */
.save-invite .button-row { margin-left: auto; }
.save-invite-text > svg { width: var(--ms-icon-size-s); height: var(--ms-icon-size-s); flex: 0 0 auto; }
