/* ══════════════════════════════════════════════════
   v2 DATENBAUTEILE

   Was dichte Verwaltungsmasken brauchen und der Kundenbereich nicht hat:
   Tabelle, Eingabegruppe, Blaetterung, Hinweisband, Reiter, Aufklapper,
   Kartenabschnitte.

   Herkunft: ms-base.css der Alt-Oberflaeche, nicht neu erfunden — dort waren
   die Bauteile schon ueberwiegend tokenbasiert. Uebernommen sind NUR die
   Selektoren, die v2 fehlen: von den 139 Klassen in ms-base.css kollidieren 43
   mit v2 (.btn-primary, .card, .modal, .badge, .toast, .dropdown …). Die haben
   in v2 ihre eigene, dem Entwurf folgende Fassung und werden hier NICHT
   angefasst.
   ══════════════════════════════════════════════════ */

/* ── Tabelle ──
   Keine Zebrastreifen: bei dreissig Zeilen sind sie Unruhe, keine Hilfe. Die
   Trennlinie unter jeder Zeile reicht, um der Zeile mit dem Auge zu folgen.
   Muster wie .detail-files-table der Bestelldetailseite, nur allgemein. */
.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ms-text-small);
}
.table th,
.table td {
  padding: var(--ms-space-3) var(--ms-space-3);
  text-align: left;
  vertical-align: middle;
  border-bottom: 1px solid var(--ms-border-divider);
}
.table thead th {
  font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-secondary);
  font-size: var(--ms-text-caption);
  letter-spacing: 0.02em;
  text-transform: uppercase;
  /* Dieselbe Linie wie zwischen den Zeilen. Vorher trug der Kopf eine
     1,5px-Linie in --ms-border-control (#86868B) mit der Begruendung, er
     begrenze den Datenbereich — in einer Maske mit vier Tabellen untereinander
     zog sie den Blick jedoch staerker an als die Daten selbst. Der Kopf hebt
     sich ohnehin durch Versalien, Groesse und Farbe ab; er braucht keinen
     Balken dafuer. */
  border-bottom: 1px solid var(--ms-border-divider);
}
.table tbody tr:last-child td { border-bottom: 0; }
/* Geschlossene Tabelle: wenn unter den Daten noch eine Zusammenfassung folgt,
   setzt die letzte Zeile das bestehende Raster fort. Kein separater Trenner —
   dieselbe Breite, Farbe und Zellteilung wie alle Zeilen darueber. */
.table.table--closed tbody tr:last-child td { border-bottom: 1px solid var(--ms-border-divider); }
.table-hover tbody tr:hover { background: var(--ms-bg-secondary); }
.table-sm th,
.table-sm td { padding: var(--ms-space-2) var(--ms-space-3); }
/* Zahlenspalten rechtsbuendig und mit gleich breiten Ziffern: nur so stehen
   Tausenderstellen untereinander und Betraege sind ohne Lesen vergleichbar. */
.table .num { text-align: right; font-variant-numeric: tabular-nums; }
/* ── Summenzeile einer Tabelle ──
   Gehoert in den `tfoot` und nicht als letzte Zeile in den `tbody`: beim
   Stapeln unter 768px (`.table--stack`) saehe sie dort wie ein weiterer
   Datensatz aus, und der Reihenfolgewechsel beim Sortieren wuerde sie
   mitnehmen.

   Der Oberstrich ist doppelt so stark wie die Zeilentrennung — er trennt
   nicht zwei Zeilen, sondern die Zahlen von ihrer Summe. Der graue Grund
   sagt dasselbe noch einmal fuer den, der quer liest.

   Aus `.report-sum` der Auswertungsseite verallgemeinert (9. September
   2026): eine Tabelle mit Summe ist kein Sonderfall einer Seite. */
.table-total td {
  border-top: 2px solid var(--ms-border-divider);
  border-bottom: 0;
  background: var(--ms-bg-secondary);
  font-weight: var(--ms-weight-semibold);
}
/* Dieselbe Linie wie zwischen den Zeilen (2026-10-06, Weisung des
   Betreibers an der Versandkarte der Bestelldetails): in einer kurzen Tabelle
   mit wenigen Zeilen zog der doppelte Strich den Blick staerker an als die
   Zahlen. Der graue Grund und der halbfette Schnitt sagen „Summe" weiterhin.
   Als Zusatz und nicht an der Grundregel — die Auswertung behaelt ihren
   Strich, bis das anders entschieden ist.

   UND EINE LINIE DARUNTER, in derselben Staerke (Weisung des Betreibers,
   2026-10-06). Ohne sie lief der graue Grund unten ins Leere, und der Satz
   „Bei 4 Zeilen fehlen …" darunter klebte an der Summe. Die Grundregel
   setzt `border-bottom: 0`, weil die Auswertung ihre Summe als letzte Zeile
   einer Karte ohne Abschluss zeigt — dort bleibt es so. */
.table-total.table-total--thin td {
  border-top-width: 1px;
  border-bottom: 1px solid var(--ms-border-divider);
}
/* Ein Zusatz in der Summenzeile („Summe aller 296 Zeilen") ist eine
   Erlaeuterung und keine Zahl — er nimmt die Fettung wieder zurueck. */
.table-total .cell-muted { font-weight: var(--ms-weight-regular); margin-left: var(--ms-space-2); }
/* Waagerechtes Rollen NUR fuer die Tabelle, nicht fuer die Seite. Ohne diese
   Huelle schiebt eine breite Tabelle die ganze Maske zur Seite. */
.table-responsive { width: 100%; overflow-x: auto; }

/* ── Kartenabschnitte ──
   v2 gibt `.card` selbst einen Innenabstand; Kopf, Rumpf und Fuss teilen ihn
   deshalb nicht noch einmal zu, sondern gliedern ihn nur. Ein `.card-body`
   OHNE eigenen Abstand ist Absicht — sonst stuende die Polsterung doppelt. */
.card-body { padding: 0; }
.card-header {
  /* Zieht sich um genau den Innenabstand der Karte nach aussen, damit die
     Trennlinie bis an beide Kanten reicht. --ms-card-padding kommt von .card
     selbst (surfaces.css) — deshalb stimmt es auch, wenn der Abstand dort
     einmal anders gesetzt wird. */
  margin: calc(-1 * var(--ms-card-padding)) calc(-1 * var(--ms-card-padding)) var(--ms-space-5);
  padding: var(--ms-space-4) var(--ms-card-padding);
  border-bottom: 1px solid var(--ms-border-divider);
  font-weight: var(--ms-weight-semibold);
  /* DIE GROESSE GEHOERT DEM BAUTEIL, NICHT SEINER UMGEBUNG (2026-09-17,
     Befund des Betreibers an der Bestellvorschau: „deutlich zu gross").

     Hier stand KEINE Groesse, und damit entschied sie das Element, das die
     Klasse zufaellig trug, plus das Raster darum. Gemessen kam dasselbe
     Bauteil dreimal verschieden heraus:

       h3.card-header in .detail-cards   14px  (pages.css rettete es)
       h3.card-header ohne dieses Raster 30-32px (die Vorschau — `h3` erbt
                                         --ms-text-h3, fluid bis 2rem)
       div.card-header                   16px  (erbt den Fliesstext)

     `--ms-text-small` ist der Wert, den die Detailseite laengst setzt und den
     7 der 13 Koepfe schon tragen. Er passt zur Rolle: die Kopfzeile einer
     Karte benennt ein Thema, sie fuehrt keine Seite an. Und er ist FEST — die
     h-Stufen sind fluid und gehoeren auf eine Verkaufsseite, nicht in eine
     Datenmaske. */
  font-size: var(--ms-text-small);
}
/* Mehr als ein Knopf im Kartenkopf.

   Die Kopfzeile einer Angabenkarte verteilt ihre Kinder mit
   `space-between` (pages.css). Bei Titel + ZWEI Knoepfen landet der erste
   Knopf dadurch in der Mitte der Zeile statt am rechten Rand — die Knoepfe
   muessen deshalb als EIN Kind auftreten. */
.card-header-actions {
  display: inline-flex;
  align-items: center;
  gap: var(--ms-space-1);
}
.card-title {
  font-size: var(--ms-text-lg);
  font-weight: var(--ms-weight-semibold);
  margin: 0 0 var(--ms-space-2);
}
.card-text { margin: 0; color: var(--ms-text-secondary); font-size: var(--ms-text-small); }

/* ── Nummerierte Anleitung in einer Karte ──
   Fuer die wenigen Stellen, an denen die Maske eine Handlung AUSSERHALB der
   Anwendung erklaert: den Zugang bei einem fremden Dienst einrichten, eine
   Datei besorgen, etwas im Konto eines Anbieters umstellen.

   WOZU EIN BAUTEIL: die Anleitung stand als `<ol class="form-help">` da —
   `.form-help` ist fuer ABSAETZE gedacht und bringt nur Schriftgroesse, Farbe
   und einen oberen Abstand mit. Der Einzug der Liste blieb damit die Vorgabe
   des BROWSERS (also kein Wert aus dieser Datei), zwischen den Schritten stand
   gar kein Abstand, und oben fielen die 8px des Kartentitels mit den 8px von
   `.form-help` zu EINEM Abstand zusammen — die Liste klebte am Titel
   (gemeldet vom Betreiber, 2026-09-17).

   ALLE DREI MASSE KOMMEN AUS DER LEITER:
     --ms-space-4  Abstand zum Titel darueber (eine Stufe mehr als im Fliesstext,
                   weil eine Liste ein eigener Block ist und kein Folgeabsatz)
     --ms-space-5  Einzug — die Ziffern stehen damit im Innenabstand der Karte,
                   der Text eine Stufe weiter rechts; das ist das klassische
                   Bild einer Anleitung
     --ms-space-2  zwischen zwei Schritten: genug, um sie als einzelne
                   Handlungen zu lesen, zu wenig, um sie auseinanderfallen zu
                   lassen */
.setup-steps {
  margin: var(--ms-space-4) 0 0;
  padding-left: var(--ms-space-5);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}
.setup-steps li + li { margin-top: var(--ms-space-2); }
/* Der Schlusssatz unter der Liste ist ein eigener Gedanke („und der Server muss
   das erreichen koennen"), kein fuenfter Schritt — er bekommt denselben
   Abstand, den die Liste nach oben hat. */
.setup-note { margin: var(--ms-space-4) 0 0; font-size: var(--ms-text-small); color: var(--ms-text-secondary); }

/* ── Eingabegruppe ──
   Feld plus angehaengter Knopf oder Beschriftung als EIN Bedienelement. Die
   Radien der Aussenkanten bleiben, die Innenkanten laufen zusammen — sonst
   sieht die Gruppe aus wie zwei Dinge, die zufaellig nebeneinander liegen. */
.input-group { display: flex; align-items: stretch; width: 100%; }
.input-group > * { border-radius: 0; }
.input-group > *:first-child { border-top-left-radius: var(--ms-radius-sm); border-bottom-left-radius: var(--ms-radius-sm); }
.input-group > *:last-child { border-top-right-radius: var(--ms-radius-sm); border-bottom-right-radius: var(--ms-radius-sm); }
.input-group > .form-input,
.input-group > .form-select { flex: 1 1 auto; width: 1%; min-width: 0; }
/* Der Rahmen liegt nur einmal zwischen zwei Teilen, nicht zweimal
   aneinander. */
.input-group > * + * { margin-left: -1px; }
.input-group > *:focus,
.input-group > *:focus-within { position: relative; z-index: 1; }
.input-group-text {
  display: flex; align-items: center;
  height: var(--ms-control-h);
  padding: 0 var(--ms-space-3);
  background: var(--ms-bg-secondary);
  border: var(--ms-control-border-w) solid var(--ms-border-control);
  color: var(--ms-text-secondary);
  /* Der Vorsatz steht AM Feld und muss mit ihm auf einer Zeile liegen —
     dieselbe Groesse wie das Feld daneben (--ms-control-font). */
  font-size: var(--ms-control-font);
  white-space: nowrap;
}

/* ── Hinweisband ──
   Text auf gedaempfter Flaeche, Farbe traegt die Bedeutung MIT, nicht allein:
   die Ueberschrift benennt sie. Die Textfarben sind die -text-Abwandlungen,
   die einzigen, die auf den Tint-Flaechen AA erreichen (designsystem.md 6). */
.alert {
  padding: var(--ms-space-4) var(--ms-space-5);
  border-radius: var(--ms-radius-md);
  border: 1px solid transparent;
  font-size: var(--ms-text-small);
  margin-bottom: var(--ms-space-4);
}
.alert-heading { font-size: var(--ms-text-md); font-weight: var(--ms-weight-semibold); margin: 0 0 var(--ms-space-2); }
.alert p:last-child { margin-bottom: 0; }
/* UND DIE UEBERSCHRIFT AUCH, wenn ihr nichts mehr folgt (2026-09-17, gemeldet
   vom Betreiber an der Verbindungspruefung der Uebersetzungsmaske).

   Der untere Abstand ist fuer den ABSATZ da, der sonst darunter steht. Ist die
   Ueberschrift der ganze Inhalt — „Die Verbindung … funktioniert." —, dann
   bleiben unten 8px mehr stehen als oben, und der Text sitzt sichtbar zu hoch
   in seinem Band. Die Regel fuer `p` daneben gibt es aus genau demselben Grund
   schon; sie war nur nie auf die Ueberschrift angewandt worden, weil ein Alert
   bis dahin immer beides hatte. */
.alert-heading:last-child { margin-bottom: 0; }
.alert-success { background: var(--ms-success-muted); border-color: rgba(var(--ms-success-rgb), 0.25); color: var(--ms-success-text); }
.alert-danger  { background: var(--ms-error-muted);   border-color: rgba(var(--ms-error-rgb), 0.25);   color: var(--ms-error-text); }
.alert-warning { background: var(--ms-warning-muted); border-color: rgba(var(--ms-warning-rgb), 0.3); color: var(--ms-warning-text); }
.alert-info    { background: rgba(var(--ms-accent-rgb), var(--ms-tint)); border-color: rgba(var(--ms-accent-rgb), 0.25); color: var(--ms-text-primary); }
.alert-secondary { background: var(--ms-bg-secondary); border-color: var(--ms-border-light); color: var(--ms-text-secondary); }

/* ── Pruefteafel der Sammeluebersetzung (v2/admin/translation/review.html.twig) ──

   EINE Karte, darin ein Block je Datensatz, getrennt durch EINE duenne Linie.
   Vorher trug jeder Datensatz seine eigene `.card` und der Knopf darunter noch
   eine — bei drei Artikeln waren das vier Kaesten untereinander, und weil jede
   Karte ihren eigenen Aussenabstand mitbringt, sahen die Abstaende willkuerlich
   aus (gemeldet vom Betreiber, 2026-09-17).

   Das Muster ist das der Rechteordnung (`.perm-row + .perm-row`): gleichartige
   Bloecke in EINER Flaeche, die Gruppierung macht die Karte, die Trennung eine
   Linie. Die Karte traegt `.card--flush`, damit die Linien bis an ihren Rand
   laufen; die Polsterung sitzt an den Bloecken.

   ALLE ABSTAENDE KOMMEN AUS DER LEITER, keiner ist gegriffen:
     --ms-space-5  Innenabstand eines Blocks (wie `.card`)
     --ms-space-4  zwischen Kopfzeile und Feldern, und zwischen zwei Feldern
     --ms-space-2  innerhalb der Kopfzeile */
.review-item { padding: var(--ms-space-5); }
.review-item + .review-item { border-top: 1px solid var(--ms-border-divider); }
.review-head {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--ms-space-2);
  margin-bottom: var(--ms-space-4);
}
.review-name { font-weight: var(--ms-weight-semibold); min-width: 0; }
/* Der Entfernen-Knopf sitzt am rechten Rand seiner Zeile — dort, wo in jeder
   Liste des Hauses die Handlung steht. */
.review-head .icon-action { margin-left: auto; }

/* DREI SPALTEN: Feldname, deutscher Text, englischer Vorschlag. Die beiden
   Textspalten sind GLEICH BREIT — man vergleicht sie, und Ungleiches laesst
   sich schlechter vergleichen. Der Feldname bekommt eine feste Spur, damit die
   Texte aller Felder an derselben Stelle beginnen. */
.review-field {
  display: grid;
  grid-template-columns: var(--ms-review-label-w) minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--ms-space-4);
  align-items: start;
}
.review-field + .review-field { margin-top: var(--ms-space-4); }
.review-label {
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-secondary);
  padding-top: var(--ms-space-2);
}
.review-target > .form-help { margin-top: var(--ms-space-1); }

/* Der Abschluss einer Karte, die ihre Polsterung abgegeben hat.
   -------------------------------------------------------------------------
   `.panel-foot` stand hier zuerst und bringt nur `--ms-space-3` nach oben und
   unten mit. Zwischen Bloecken mit `--ms-space-5` sah der Streifen gedraengt
   aus (gemeldet vom Betreiber, 2026-09-17: „der Bereich oben und unten ist
   noch zu eng"). Ein Modifier an `.panel-foot` haette an vier anderen Stellen
   mitgewirkt, deshalb ein eigenes, benanntes Bauteil.

   DIE POLSTERUNG IST DIESELBE WIE IM BLOCK DARUEBER (`--ms-space-5`), und die
   Linie dieselbe wie zwischen zwei Bloecken: der Fuss ist der letzte Block,
   nur mit einer Handlung statt Inhalt.

   DIE HANDLUNG STEHT RECHTS, wie ueberall im Haus
   (`.form-actions { justify-content: flex-end }`). Was links danebensteht —
   ein Zaehler, ein Erklaersatz — gehoert in `.form-actions-start`, das sich
   mit `margin-right: auto` selbst nach links schiebt. */
.review-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ms-space-3);
  padding: var(--ms-space-5);
  border-top: 1px solid var(--ms-border-divider);
}

/* Unter 900px stapeln die drei Spalten — derselbe Umbruchpunkt, den die
   Abschnittskopfzeile und acht weitere Stellen dieser Datei benutzen. Er ist
   der Haus-Umbruch fuer „drei Spalten passen nicht mehr nebeneinander". Der Feldname steht dann als
   Ueberschrift ueber seinem Paar — ohne das stuenden Beschriftung und Text in
   drei Zeilen ohne erkennbaren Zusammenhang. */
@media (max-width: 900px) {
  .review-field { grid-template-columns: minmax(0, 1fr); gap: var(--ms-space-1); }
  .review-label { padding-top: 0; }
  .review-field + .review-field { margin-top: var(--ms-space-5); }
}

/* ── Blaetterung ── */
.pagination { display: flex; flex-wrap: wrap; gap: var(--ms-space-1); list-style: none; margin: var(--ms-space-5) 0 0; padding: 0; }
.page-link {
  display: flex; align-items: center; justify-content: center;
  min-width: var(--ms-control-h); height: var(--ms-control-h);
  padding: 0 var(--ms-space-3);
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-sm);
  color: var(--ms-text-primary);
  font-size: var(--ms-text-small);
  text-decoration: none;
  transition: background-color var(--ms-transition-base), border-color var(--ms-transition-base);
}
.page-link:hover { background: var(--ms-bg-secondary); border-color: var(--ms-border-control); }
.page-item.active .page-link { background: var(--ms-accent); border-color: var(--ms-accent); color: var(--ms-on-accent); }
.page-item.disabled .page-link { color: var(--ms-text-tertiary); pointer-events: none; }

/* ── Reiter ── */
.nav-tabs { display: flex; flex-wrap: wrap; gap: var(--ms-space-1); list-style: none; margin: 0 0 var(--ms-space-5); padding: 0; border-bottom: 1px solid var(--ms-border-light); }
.nav-tabs .nav-link {
  padding: var(--ms-space-3) var(--ms-space-4);
  border: 0;
  /* Der aktive Reiter traegt eine 2px-Linie; die inaktiven halten denselben
     Platz mit einer durchsichtigen, sonst springt die Zeile beim Wechsel. */
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  color: var(--ms-text-secondary);
  font-size: var(--ms-text-small);
  font-weight: var(--ms-weight-medium);
}
.nav-tabs .nav-link:hover { color: var(--ms-text-primary); }
.nav-tabs .nav-link.active { color: var(--ms-text-primary); border-bottom-color: var(--ms-accent); }

/* ── Aufklapper ── */
.accordion-item { border: 1px solid var(--ms-border-light); border-radius: var(--ms-radius-md); margin-bottom: var(--ms-space-3); overflow: hidden; }
.accordion-button {
  display: flex; align-items: center; justify-content: space-between;
  width: 100%; padding: var(--ms-space-4) var(--ms-space-5);
  background: var(--ms-bg-primary); border: 0; cursor: pointer;
  font-size: var(--ms-text-body); font-weight: var(--ms-weight-medium);
  text-align: left; color: var(--ms-text-primary);
}
.accordion-button:hover { background: var(--ms-bg-secondary); }
.accordion-button::after {
  content: '';
  width: 8px; height: 8px;
  border-right: 2px solid var(--ms-text-tertiary);
  border-bottom: 2px solid var(--ms-text-tertiary);
  transform: rotate(45deg);
  transition: transform var(--ms-transition-base);
}
.accordion-button.collapsed::after { transform: rotate(-45deg); }
.accordion-body { padding: var(--ms-space-5); border-top: 1px solid var(--ms-border-light); }

/* ── Ueberschrift eines Maskenabschnitts ──
   Altname `.ms-section-title`, 34 Vorkommen. Gefuehrt statt umbenannt: der
   Name sagt, was er ist. Das `ms-`-Praefix faellt beim Umbau der Bereiche. */
.section-title,
.ms-section-title {
  font-size: var(--ms-text-h4);
  font-weight: var(--ms-weight-semibold);
  letter-spacing: -0.01em;
  margin: 0 0 var(--ms-space-4);
}

/* ── Liste ohne Aufzaehlungszeichen ──
   Fuer Aufzaehlungen in Karten, bei denen die Karte selbst schon sagt, dass
   es eine Liste ist. Ersetzt Stellen, an denen <li> ganz ohne <ul> stand. */
/* ── Getrennte Aufzaehlung (heute: die Positionen in der Bestellvorschau) ──

   DIESELBE STUFE WIE DER WERT EINER PAARLISTE (2026-09-17, Befund des
   Betreibers: „Es scheint, als ob die Angabe des Artikelnamens eine andere
   Farbe hat wie die Angaben fuer Lieferadresse und die Datumsangaben.").

   Die FARBE war es nicht — gemessen tragen beide `--ms-text-primary`. Es war
   die Groesse: die Aufzaehlung stand auf `--ms-text-small` (14px), die Werte
   der Karte daneben auf `--ms-text-md` (16px). Zwei gleich gebaute Karten
   nebeneinander, beide zeigen den Inhalt der Bestellung, in zwei Registern —
   und kleinerer Text auf gleichem Grund liest sich als blasser.

   16px ist die richtige Seite des Vergleichs, aus dem Grund, der schon an
   `.pair-list--stacked .pair dd` steht: das ist die Stelle, an der man Angaben
   tatsaechlich liest. Das GEWICHT bleibt regulaer — `medium` trennt dort Wert
   von Beschriftung, und diese Unterscheidung gibt es in einer Aufzaehlung
   nicht; die Betonung traegt hier das `<strong>` an der Menge. */
.plain-list { list-style: none; margin: 0; padding: 0; font-size: var(--ms-text-md); }
.plain-list li { padding: var(--ms-space-1) 0; }
.plain-list li + li { border-top: 1px solid var(--ms-border-light); }

/* ── Zeile einer Formularsammlung ──
   Huelle, die der Controller `form-collection` beim Hinzufuegen erzeugt und
   beim Entfernen sucht. Der Altname war `item` — ein Klassenname ohne Aussage,
   der zudem an mehreren Stellen mit anderer Bedeutung vorkam. */
.collection-row {
  display: flex;
  align-items: flex-end;
  gap: var(--ms-space-2);
  margin-bottom: var(--ms-space-3);
}
/* Das Eingabefeld nimmt den freien Platz, eine Auswahl davor bleibt bei ihrer
   Breite: in „Art | Nummer | Muelleimer" soll die Nummer wachsen, nicht die
   Art. `min-width: 0` gehoert dazu, sonst schrumpft ein Feld nicht unter seine
   Inhaltsbreite und die Zeile laeuft aus der Karte.

   DIE 9rem SIND GERECHNET, nicht gewaehlt: `flex: 0 0 auto` allein half nicht,
   weil `.form-select` `width: 100%` traegt — die Auswahl nahm die ganze Zeile
   und schob die Nummer aus der Karte. Die laengste Art („Dienstlich") braucht
   bei --ms-text-body rund 80px, dazu der Innenabstand links (16px) und der
   Platz fuer den Chevron rechts (40px): 136px. 9rem sind 144px und damit die
   naechste runde Groesse darueber — fest, damit alle Zeilen dieselbe Kante
   haben, statt mit der Laenge der gewaehlten Art zu springen. */
.collection-row > .form-input { flex: 1 1 auto; min-width: 0; }
.collection-row > .form-select { flex: 0 0 9rem; width: 9rem; }

/* ── Schrittfolge ──
   Eine Reihe benannter Schritte in ihrer Reihenfolge. Vorher gleich aussehende
   graue Abzeichen ohne Nummern — die Reihenfolge war da, aber nicht lesbar.

   Nummern statt Pfeilen: ein Pfeil zwischen den Marken muesste in der Luecke
   sitzen und braucht dafuer entweder ein eigenes Element je Zwischenraum oder
   absolute Positionierung, die beim Zeilenumbruch bricht. Die Nummer sagt
   dasselbe und steht immer am richtigen Platz. */
.step-chain {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-2);
  list-style: none; margin: 0; padding: 0;
  counter-reset: schritt;
}
.step-chain li {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  padding: var(--ms-space-1) var(--ms-space-3);
  background: var(--ms-bg-secondary);
  border-radius: var(--ms-radius-pill);
  font-size: var(--ms-text-caption);
  color: var(--ms-text-secondary);
  counter-increment: schritt;
}
.step-chain li::before {
  content: counter(schritt);
  font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-primary);
  font-variant-numeric: tabular-nums;
}

/* ── Auswahl auf einen Klick ──
   Fuer Werte, die im Ganzen kopiert werden (API-Schluessel, Kennungen). Ohne
   das trifft man beim Ziehen ueber vierzig Zeichen leicht daneben. */
.select-all { user-select: all; }
/* Maschinennahe Werte (Praefixe, Kennungen, IP-Adressen): gleich breite
   Zeichen, damit gleich lange Werte gleich lang AUSSEHEN. */
code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--ms-text-code);
}

/* ── Liste einmalig gezeigter Codes ──
   Die zehn Reserve-Codes der Zwei-Faktor-Anmeldung. Sie werden ABGESCHRIEBEN
   oder in einen Passwortmanager uebertragen, und man muss dabei die Zeile
   halten koennen — deshalb zwei Spalten statt zehn Zeilen, gleich breite
   Zeichen und deutlicher Zeilenabstand.

   `auto-fill` mit einer Mindestbreite und nicht zwei feste Spalten: auf dem
   Telefon ist eine Spalte richtig, und die Umschaltung soll an der
   verfuegbaren Breite haengen, nicht an einer geratenen Geraetegrenze. */
.code-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
  gap: var(--ms-space-2) var(--ms-space-5);
  margin: var(--ms-space-4) 0;
  padding: 0;
  list-style: none;
}
.code-grid li { margin: 0; }
.code-grid code { font-size: var(--ms-text-md); }

/* Zwischenueberschrift in einer Tabelle: gliedert Zeilen zu Gruppen, ohne die
   Spalten zu verlassen (Wareneingangspruefung, Pruefung je Paket). */
.table .table-group th {
  background: var(--ms-bg-secondary);
  text-transform: none;
  font-size: var(--ms-text-small);
  color: var(--ms-text-primary);
  border-bottom: 1px solid var(--ms-border-light);
}

/* Quadratische Bildkachel in der Bildverwaltung der Baugruppen. Ersetzt drei
   `style="aspect-ratio: 1/1"` und ein `style="z-index:9"` in
   ProductAssemblyForm — die Verhaeltnisangabe gehoert zum Bauteil, nicht ans
   einzelne Bild. */
.tile-square { aspect-ratio: 1 / 1; }
/* Die Ueberblendung liegt ueber dem Bild, aber unter Dialogen. */
.tile-overlay { z-index: 1; }
/* Bild, das seine Spalte fuellt (Vorschau in der Produktkonfiguration). */
.img-full { width: 100%; height: auto; }

/* ══════════════════════════════════════════════════
   SAMMELABSCHNITT, UMSCHALTER, BROTKRUME

   Was eine Stammdatenmaske ausmacht, die nicht nur ein Formular ist: der
   Datensatz oben, darunter seine Sammlungen — Adressen, Kontakte,
   Bestellungen — je als aufklappbarer Abschnitt mit Zaehler, Suche und einer
   Handlung. Bedienkonzept nach myOFFICE (mo-accordion), Werte aus tokens.css.

   Warum <details> und kein eigenes Bauteil mit JS: Auf- und Zuklappen kann der
   Browser, samt Tastatur und Vorlesehilfe. Das Suchfeld im Kopf filtert die
   Zeilen darunter (Controller `table-search`).
   ══════════════════════════════════════════════════ */

/* ── Brotkrume ──
   Nur dort, wo eine Maske wirklich in einer Liste steckt (Firma, Kunde). Der
   Ruecksprung `.detail-back` bleibt fuer Masken ohne echte Hierarchie. */
.breadcrumb {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-2);
  margin-bottom: var(--ms-space-3);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}
/* Die Verweise der Brotkrume sind .text-link (base.css) — hier stand
   dieselbe Regel ein zweites Mal, Wort fuer Wort. Zwei Definitionen einer
   Optik laufen auseinander, sobald jemand nur eine davon anfasst. */
.breadcrumb-sep { color: var(--ms-text-tertiary); }

/* ── Umschalter ──
   Ein Ankreuzfeld in Schalterform, fuer Zustaende, die sofort wirken
   („Inaktive anzeigen"), nicht fuer Formularfelder — dort bleibt das
   Ankreuzfeld (.form-checkbox), weil es zu einem Formular gehoert, das
   abgeschickt wird. */
.switch {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  cursor: pointer; user-select: none;
  font-size: var(--ms-text-small); color: var(--ms-text-primary);
}
.switch > input { position: absolute; opacity: 0; width: 1px; height: 1px; }
.switch-track {
  position: relative; flex: none;
  width: 38px; height: 22px;
  border-radius: var(--ms-radius-pill);
  background: var(--ms-border-control);
  transition: background var(--ms-transition-fast);
}
.switch-track::after {
  content: '';
  position: absolute; top: 2px; left: 2px;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--ms-bg-primary);
  box-shadow: var(--ms-shadow-sm);
  transition: transform var(--ms-transition-fast);
}
.switch > input:checked + .switch-track { background: var(--ms-accent); }
.switch > input:checked + .switch-track::after { transform: translateX(16px); }
.switch > input:focus-visible + .switch-track { outline: 2px solid var(--ms-accent); outline-offset: 2px; }
.switch > input:disabled + .switch-track { opacity: 0.5; }
.switch:has(> input:disabled) { cursor: not-allowed; }

/* ── Sammelabschnitt ── */
.panel {
  background: var(--ms-bg-primary);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  overflow: hidden;
}
/* Abstand ZWISCHEN Abschnitten — und ebenso zwischen dem Datensatz und dem
   ersten Abschnitt darunter. Die Karte bringt keinen Aussenabstand mit, und in
   der Artikelmaske klebte der Abschnitt „Verwendung" deshalb unmittelbar am
   Formular. Derselbe Wert wie zwischen zwei Abschnitten, damit die Maske EINEN
   Rhythmus hat und nicht zwei. */
.panel + .panel,
.card + .panel,
/* … UND NACH EINEM KARTENSTAPEL. Die Set- und Artikelmaske reihen ihre
   Abschnitte als Karten in einem `.record-stack` und haengen die Abschnitte
   DAHINTER an — das vorherige Geschwister ist dann der Stapel und nicht die
   Karte, und `.card + .panel` griff nicht: der Abschnitt klebte an der
   Notizkarte (gemeldet vom Betreiber am 2026-09-09, gemessen margin-top 0).
   Dieselbe Stufe wie oben, damit der Abstand im Stapel und dahinter derselbe
   ist. */
.record-stack + .panel { margin-top: var(--ms-space-4); }
.panel > summary {
  display: flex; align-items: center; gap: var(--ms-space-3);
  padding: var(--ms-space-2) var(--ms-space-4);
  min-height: 56px;
  background: var(--ms-panel-head);
  color: var(--ms-panel-head-on);
  font-weight: var(--ms-weight-semibold);
  cursor: pointer;
  list-style: none;
}
.panel > summary::-webkit-details-marker { display: none; }
.panel > summary:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: -2px; }
.panel-chevron { flex: none; color: var(--ms-panel-head-muted); transition: transform var(--ms-transition-base); }
.panel[open] > summary .panel-chevron { transform: rotate(180deg); }
/* Fester Kopfblock: so beginnen die Suchfelder aller Abschnitte auf derselben
   Hoehe — sonst wandern sie mit der Laenge des Titels. */
.panel-lead { display: inline-flex; align-items: center; gap: var(--ms-space-2); min-width: 260px; flex: none; }
/* `tabular-nums`: „Reihe 1" und „Reihe 7" sind damit gleich breit, und die
   Zahlen dahinter beginnen in allen Reihen an derselben Stelle — mit
   Proportionalziffern waren es drei Pixel Versatz je Zeile, und bei acht
   Reihen sieht man den Versatz. Titel ohne Ziffern merken nichts davon. */
.panel-title { display: inline-flex; align-items: center; gap: var(--ms-space-2); min-width: 0; font-variant-numeric: tabular-nums; }
.panel-count {
  /* SENKRECHT KEINE POLSTERUNG. Mit `1px` oben und unten war das Abzeichen
     21,2px hoch, die Zeilenbox des Titels daneben 19,2px — es stand also zwei
     Pixel ueber die Zeile hinaus und las sich als eigenes Ding statt als Teil
     der Zeile (gemeldet vom Betreiber, 2026-09-07: „dass die Badges in der
     gleichen Hoehe stehen wie die daneben liegende Zeile"). Ohne die Polsterung
     ist das Kaestchen genau die geerbte Zeilenhoehe hoch, und beide Mitten
     liegen aufeinander. Die Breite kommt weiter aus dem Abstandssystem. */
  padding: 0 var(--ms-space-2);
  border-radius: var(--ms-radius-pill);
  background: var(--ms-panel-head-overlay);
  /* Voller Titel-Ton, nicht der gedaempfte: auf dem aufgehellten Schleier
     erreicht der gedaempfte nur 3,32:1. */
  color: var(--ms-panel-head-on);
  font-size: var(--ms-text-caption); font-weight: var(--ms-weight-semibold);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
/* Der Kopfblock ohne reservierte Breite — fuer Abschnitte mit Nebenzahlen
   statt Suchfeld. Die 260px oben halten die Suchfelder auf einer Linie; wo
   keines steht, schoben sie die Zahlen 170px hinter den Titel ins Leere. */
.panel-lead--auto { min-width: 0; }

/* Die Zahlen eines Abschnitts als Text: „7 Faecher · 21 Plaetze · 18 Artikel".
   EIN Ton fuer alle drei — ein Abzeichen fuer die erste und gedaempfter Text
   fuer die anderen machte aus gleichrangigen Angaben zwei Raenge (Weisung des
   Betreibers, 2026-09-07). Gedaempft gegen den Titel, auf der Kopfzeile mit
   5,13:1 geprueft.

   Der Abstand zum Titel kommt HIER dazu und nicht aus dem `gap` der Kopfzeile:
   12px liessen die Zahlen am Titel kleben, und der Abstand soll gewollt
   aussehen. Unter 900px weg — dort braucht der Titel die Breite. */
.panel-meta {
  margin-left: var(--ms-space-4);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-regular);
  color: var(--ms-panel-head-muted);
  white-space: nowrap;
}
@media (max-width: 900px) { .panel-meta { display: none; } }
.panel-spacer { flex: 1 1 auto; }
.panel-switch { color: var(--ms-panel-head-on); white-space: nowrap; }
.panel-switch .switch-track { background: var(--ms-panel-head-overlay); }
.panel-search {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  padding: 0 var(--ms-space-3);
  height: 2rem;
  border-radius: var(--ms-radius-sm);
  background: var(--ms-panel-head-overlay);
  color: var(--ms-panel-head-muted);
}
.panel-search input {
  width: 150px;
  border: 0; background: none; outline: none;
  color: var(--ms-panel-head-on);
  font: inherit; font-size: var(--ms-control-font); font-weight: var(--ms-weight-regular);
}
.panel-search input::placeholder { color: var(--ms-panel-head-muted); }
.panel-search input::-webkit-search-cancel-button { appearance: none; }
/* Symbolknopf in der dunklen Kopfzeile — die gleiche Trefferflaeche wie
   .icon-action, nur in den Farben des Kopfes. */
.panel-action {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--ms-icon-btn-h-dense); height: var(--ms-icon-btn-h-dense); flex: none;
  border: 0; border-radius: var(--ms-radius-sm);
  background: none; color: var(--ms-panel-head-muted);
  cursor: pointer; text-decoration: none;
  transition: background var(--ms-transition-fast), color var(--ms-transition-fast);
}
.panel-action:hover { background: var(--ms-panel-head-overlay); color: var(--ms-panel-head-on); }
.panel-action:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }
/* Haelt den Platz einer fehlenden Handlung frei, damit die Suchfelder aller
   Abschnitte an derselben Stelle enden. */
.panel-actionslot { width: var(--ms-icon-btn-h-dense); height: var(--ms-icon-btn-h-dense); flex: none; }
.panel-body { border-top: 1px solid var(--ms-border-divider); }
/* Ein Abschnitt, dessen Inhalt ein FORMULAR ist statt einer Tabelle.

   `.panel-body` polstert absichtlich nicht: die Zellen einer Tabelle bringen
   ihren Abstand selbst mit, und eine zweite Polsterung um sie herum liesse die
   Tabelle in der Karte schwimmen. Ein Formular bringt keinen mit — seine
   Beschriftungen und Felder lagen deshalb an der Kante der Karte (gemeldet vom
   Betreiber, 2026-09-08: „achte darauf, dass alle Elemente entsprechenden
   Abstand zu den Aussenbereichen haben").

   Wie `.panel-empty` und `.panel-foot` polstert der Inhalt sich also selbst,
   und mit demselben Mass wie eine gewoehnliche Karte.

   NICHT `var(--ms-card-padding)`: dieses Token ist IN `.card` deklariert
   (surfaces.css) und damit nur dort sichtbar. Ein Sammelabschnitt ist keine
   Karte — ausserhalb loest die Angabe zu nichts auf, und die Polsterung war
   still 0. Genau daran ist der erste Versuch dieser Regel gescheitert. */
.panel-form { padding: var(--ms-space-5); }
/* Der Kopf der Tabelle IM Abschnitt bekommt den hellen Grund. Kein `>` vor
   `.table-responsive` mehr: die Kontaktliste der Firmenmaske liegt seit dem
   Sammelumzug in einem <form>, und mit dem direkten Kindselektor waere ihr
   Tabellenkopf als einziger dunkel geblieben. */
.panel-body .table-responsive > .table > thead th { background: var(--ms-bg-primary); }
.panel-empty {
  margin: 0;
  padding: var(--ms-space-4) var(--ms-space-5);
  font-size: var(--ms-text-small); color: var(--ms-text-secondary);
}
.panel-foot {
  padding: var(--ms-space-3) var(--ms-space-5);
  border-top: 1px solid var(--ms-border-divider);
}
/* Fusszeile als Bedienleiste: Auskunft links, Handlung rechts. Gebraucht fuer
   den Sammelumzug der Kontakte in der Firmenmaske — die Leiste erscheint erst
   mit der Auswahl (`hidden`), und `[hidden]` schlaegt hier das `display`. */
.panel-foot--bar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-3);
}
/* Das `display: flex` oben ist spezifischer als die Browservorgabe fuer
   `[hidden]` und wuerde sie aushebeln — dieselbe Falle wie bei `.badge`. */
.panel-foot--bar[hidden] { display: none; }
/* Leise Stufe am Fuss eines Sammelabschnitts.
   Die Bestandswarnung der Startseite trennt zwei Aussagen: was Nachschub
   braucht (Tabelle im Abschnitt) und was nur keinen Puffer mehr hat. Letzteres
   trifft gemessen 78 Artikel — inline wuerden sie die acht negativen Bestaende
   zudecken. Sie stehen deshalb hinter dieser Zeile.
   Eigenes Bauteil und kein zweiter .panel: ein Abschnitt IM Abschnitt haette
   eine zweite dunkle Kopfzeile und laese sich als gleichrangig. */
.stock-quiet { border-top: 1px solid var(--ms-border-divider); }
.stock-quiet > summary {
  display: flex; align-items: center; gap: var(--ms-space-2);
  padding: var(--ms-space-3) var(--ms-space-5);
  font-size: var(--ms-text-small); color: var(--ms-text-secondary);
  cursor: pointer; list-style: none;
}
.stock-quiet > summary::-webkit-details-marker { display: none; }
.stock-quiet > summary:hover { color: var(--ms-text-primary); background: var(--ms-bg-secondary); }
.stock-quiet > summary:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: -2px; }
.stock-quiet[open] > summary { border-bottom: 1px solid var(--ms-border-divider); }
.stock-quiet > summary .panel-chevron { transition: transform var(--ms-transition-base); }
.stock-quiet:not([open]) > summary .panel-chevron { transform: rotate(-90deg); }
@media (max-width: 767px) {
  .panel-lead { min-width: 0; }
  .panel-switch span:not(.switch-track) { display: none; }
  .panel-search input { width: 90px; }
}

/* ── Karte ohne eigene Polsterung ──
   Fuer Karten, die nur eine Werkzeugleiste und eine Tabelle enthalten: beide
   bringen ihre Abstaende selbst mit, die Karte waere sonst doppelt gepolstert. */
.card--flush { padding: 0; }
.card-toolbar {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-3);
  padding: var(--ms-space-3) var(--ms-space-4);
  border-bottom: 1px solid var(--ms-border-divider);
}
.card-toolbar .search-box { flex: 1 1 320px; max-width: 640px; }

/* ── EINE WERKZEUGLEISTE, DIE EIN FORMULAR IST (2026-09-16) ──
   Die Polsterung oben (12/16px) ist die der LISTE: darunter folgt eine
   Tabelle, deren Zellen auf denselben 16px sitzen, und die Leiste richtet
   sich an ihr aus. Wo keine Tabelle folgt — die Karte „Auswertung speichern"
   besteht nur aus zwei Leisten —, fehlt dieser Bezug, und die Felder standen
   sichtbar zu dicht an der Kante (Befund des Betreibers).

   Hier gilt deshalb das Polster der KARTE, und zwar ueber ihr eigenes Token:
   `--ms-card-padding`. Nicht `--ms-space-5` abgeschrieben — aendert jemand
   das Kartenmass, geht diese Leiste mit.

   DER SENDEKNOPF STEHT RECHTS (Weisung des Betreibers). `margin-left: auto`
   und keine zweite Flex-Gruppe: bricht die Leiste auf schmalen Schirmen um,
   bleibt der Knopf auf seiner Zeile trotzdem rechts, statt in einer starren
   Gruppe mitzuwandern. */
.card-toolbar--form {
  padding: var(--ms-card-padding);
  gap: var(--ms-space-4);
}
.card-toolbar--form > [type="submit"] { margin-left: auto; }
/* Werkzeugleiste mit MEHREREN Filtern.

   Der GRUNDWERT entscheidet ueber den Umbruch, nicht die Endbreite: eine
   Flex-Zeile bricht anhand der `flex-basis` um. Mit `1 1 320px` (dem Wert der
   einfachen Suchleiste) ueberschritt die Summe der Grundwerte die Zeile, und
   „Zuruecksetzen" rutschte darunter, obwohl gewachsen alles gepasst haette.
   260px als Grundwert, und was uebrig bleibt, bekommt die Suche bis 460px —
   damit ist sie auf einem Arbeitsplatzschirm die breiteste Angabe der Leiste
   und faellt auf schmalen nicht aus der Zeile.

   Die Regel steht hier und nicht bei `.search-box--filter` in pages.css:
   `.card-toolbar .search-box` ist spezifischer und haette die Abwandlung
   sonst still ueberstimmt. */
.card-toolbar .search-box--filter { flex: 1 1 260px; max-width: 460px; }
/* EIN TEXTFELD IN EINER WERKZEUGLEISTE, das keine Suche ist — der Name, unter
   dem eine Auswertung gespeichert wird. `.form-input` ist `width: 100%`; als
   Flexkind heisst das Grundwert 100 %, und die Leiste bricht hinter dem Feld
   um, egal wie viel Platz noch da ist. Gemessen am 9. September 2026: Haekchen
   und Speichern-Knopf standen dadurch in einer eigenen Zeile unter einem
   bildschirmbreiten Namensfeld.

   Dieselbe Bauform wie bei `.search-box` daroben: ein Grundwert, der den
   Umbruch entscheidet, und eine Obergrenze, damit das Feld nicht die ganze
   Leiste einnimmt. Schmaler als die Suche, weil ein Name kuerzer ist als eine
   Suchanfrage. */
.card-toolbar > .form-input { flex: 1 1 220px; width: auto; max-width: 24rem; }

/* ── Zweitzeile in einer Tabellenzelle ──
   Die Kundennummer unter dem Firmennamen, die Postleitzahl unter der Strasse:
   eine Angabe, die zur Zeile gehoert, aber keine eigene Spalte verdient. */
.cell-meta { font-size: var(--ms-text-caption); color: var(--ms-text-secondary); }
/* DIE AUSSAGE EINER MEHRZEILIGEN ZELLE (2026-09-22). `.cell-meta` ist 12px in
   --ms-text-secondary und damit die ANMERKUNG neben einer Hauptaussage. Wo eine
   Zelle mehrere Zeilen stapelt, trug bisher jede diese Stufe — im Bestandsjournal
   standen Ortswechsel und Notiz so leise da wie der Name dessen, der gebucht hat
   (Weisung des Betreibers: „nicht so klein und so hell").

   Diese Klasse setzt die Zeile auf die Tabellenschrift zurueck: 14px erbt sie
   von `.table`, die Textfarbe steht hier. Keine eigene Groesse — eine dritte
   Stufe neben 14 und 12 waere eine Stufe zu viel. */
.cell-note { color: var(--ms-text-primary); }
/* Eine Kennung in einer Zelle: Artikelnummer, Bestellnummer, Projektnummer.
   Gleiche Zifferbreiten, damit die Nummern einer Spalte untereinander stehen,
   und kein Umbruch — eine halbe Nummer ist keine Nummer. Ruhiger als der
   Fliesstext, weil daneben der NAME die Hauptaussage traegt. */
.cell-id {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--ms-text-secondary);
}
/* Handlungsspalte am Zeilenende: so schmal wie ihr Inhalt, rechts. Damit
   stehen die Stiftsymbole zweier Tabellen im selben Abschnitt auf einer
   Linie, auch wenn die Tabellen ihre uebrigen Spalten unabhaengig rechnen. */
.cell-action { width: 1%; white-space: nowrap; text-align: right; }
.cell-main { display: flex; align-items: center; gap: var(--ms-space-2); min-width: 0; }
/* ── Spaltenraster einer Uebersicht ──
   `.table` allein rechnet die Spaltenbreiten aus dem Inhalt (`table-layout:
   auto`). Das ist fuer eine Tabelle mit drei Spalten richtig und fuer eine
   Uebersicht falsch: der ueberschuessige Platz geht vollstaendig an die Spalte
   mit dem breitesten Inhalt, also an den Namen. Auf 1600px hatte der Name 835
   von 1520px — mehr als die Haelfte — waehrend die Betraege in schmalen
   Spalten am Rand klebten.

   `.table--fixed` dreht das um: die Anteile stehen an den Kopfzellen, der
   Rest gehoert der Textspalte. Anteile und keine Pixel, damit sie MIT dem
   Fenster wachsen — genau darum ging es beim Schritt auf die volle Breite.
   Vorbild: .mo-table--fixed in myOFFICE.

   Die Klassen sind nach dem benannt, WAS in der Spalte steht, nicht nach ihrer
   Nummer: eine `nth-child`-Kette waere beim naechsten Einschub still falsch
   geworden. `overflow: hidden` gehoert zum festen Raster — ohne es waechst
   eine Zelle wieder ueber ihren Anteil hinaus. */
.table--fixed { table-layout: fixed; }
.table--fixed th,
.table--fixed td { overflow: hidden; }
/* Anzahl (Kontakte, Positionen): eine bis drei Stellen, mehr braucht es nie. */
.table--fixed th.col-count { width: 9%; }
/* Betrag oder eine Zahl mit langer Ueberschrift („Umsatz lfd. Jahr"). */
.table--fixed th.col-money { width: 15%; }
/* Kontaktdaten: E-Mail und Telefonnummer untereinander. Die E-Mail ist das
   breiteste Element der Zelle und wird gekuerzt — 26% reichen auf 1280px fuer
   rund 34 Zeichen, was die meisten Firmenadressen ungekuerzt zeigt. */
.table--fixed th.col-contactdata { width: 26%; }
/* Zuordnung eines Kontakts: Firma mit der Kundengruppe als Beizeile. */
.table--fixed th.col-company { width: 22%; }
/* ── Anteile der Tag-Liste (Artikel-Tags einer Firma) ──
   DIESE TABELLE HAT MEHR BREITE ALS INHALT: ein Name, zwei zweistellige
   Zahlen, ein Abzeichen und drei Knoepfe sind zusammen rund 340px — die Zeile
   ist 1700 breit. Die restlichen 1350px Weissraum muessen irgendwo stehen; die
   Anteile entscheiden nur, WO.

   UND DIE AUSRICHTUNG ENTSCHEIDET MIT. Eine Zahl sitzt am RECHTEN Rand ihrer
   Spalte, ein Abzeichen am LINKEN Rand der naechsten — genau dort stossen die
   beiden aneinander, waehrend links davor die ganze Spaltenbreite leer bleibt.
   Mit den vorigen Anteilen (38/18/18/17) waren die vier Abstaende zwischen den
   Inhalten deshalb 820 / 286 / 24 / 270px: eine Leere nach dem Namen, ein
   Stoss zwischen „41" und „Aktiv" (gemeldet vom Betreiber, 2026-09-10: „warum
   ist so viel Platz zwischen der ersten und der zweiten Spalte, und warum
   kleben die dritte und die vierte so dicht beieinander?").

   DESHALB SIND DIE ANTEILE HIER AUS DEN INHALTS-POSITIONEN ZURUECKGERECHNET
   und nicht aus den Inhaltsbreiten: gesucht war, wo die Zahl STEHEN soll, und
   daraus folgt, wo die Spalte ENDEN muss. Dass „Artikel" 11 % und
   „Artikel-Sets" 20,5 % bekommt, ist deshalb kein Widerspruch — die zweite
   Spalte traegt den Abstand, den ihre Zahl nach links braucht. Genauso
   arbeitet myOFFICE: die Belegliste fuehrt ihre elf Spalten als `<colgroup>`
   mit von Hand gesetzten Prozenten („alle Spalten wachsen proportional →
   gleichmaessige Verteilung"); bei uns stehen sie als Klassen an den
   Kopfzellen, weil das Designsystem keine Inline-Stile erlaubt.

   Gemessen mit den acht Tags der Firma KPMG:
     1697px Tabelle   Abstaende 383 / 330 / 337 / 350
     1217px Tabelle   Abstaende 239 / 231 / 229 / 221
   Die Summe der Anteile ist 73 % — der Rest gehoert der Handlungsspalte
   (`col-actions--rest`), deren Knoepfe rechtsbuendig stehen und damit an der
   Kante der Zeile bleiben, egal wie breit sie wird. */
.table--fixed th.col-tagname { width: 19%; }
.table--fixed th.col-tagproducts { width: 11%; }
.table--fixed th.col-tagassemblies { width: 20.5%; }
.table--fixed th.col-tagstate { width: 22.5%; }
/* Das Abzeichen rechtsbuendig wie die Zahlen davor: linksbuendig klebte es an
   der Zahl, und der Abstand seiner Spalte lag hinter ihm statt davor. */
.table th.col-tagstate,
.table td.col-tagstate { text-align: right; }
/* Handlungsspalte OHNE eigene Breite: im festen Raster faellt der Rest immer
   an die Spalten ohne Prozentangabe, und hier soll er genau dorthin. Sonst
   wie `.col-actions` — 32px Abstand nach links zwischen Lesen und Bedienen. */
.table th.col-actions--rest,
.table td.col-actions--rest {
  padding-left: var(--ms-space-6);
  white-space: nowrap;
  text-align: right;
}
/* ── Anteile der Kundengruppen-Liste (Abschnitt in der Firmenmaske) ──
   Dieselbe Lage wie bei der Tag-Liste darueber und dieselbe Rechnung: Name,
   drei Zahlen, ein Abzeichen und ein Knopf sind zusammen rund 320px in einer
   1700px breiten Zeile, und weil Zahlen und Abzeichen am RECHTEN Rand ihrer
   Spalte stehen, entscheidet der Anteil, wie weit sie von ihrem linken
   Nachbarn wegruecken.

   Gesucht war wieder die POSITION, nicht die Breite: fuenf gleiche Abstaende
   zwischen sechs Inhalten. `col-count` (9 %) reicht fuer eine dreistellige
   Zahl unter „Kontakte"; die beiden folgenden Zahlenspalten tragen den
   Abstand, den ihre Zahl nach links braucht, und der Rest gehoert der
   Handlungsspalte (`col-actions--rest`), deren Knopf rechtsbuendig an der
   Zeilenkante bleibt.

   Der Name wird gekuerzt statt die Spalte zu weiten — mit Sprechblase, und
   nur wenn wirklich gekuerzt wurde (text-overflow_controller.js). */
.table--groups th.col-name { width: 18%; }
.table--groups th.col-addresses { width: 17.5%; }
.table--groups th.col-assemblies { width: 17.5%; }
.table--groups th.col-state { width: 19.5%; }
.table--groups th.col-state,
.table--groups td.col-state { text-align: right; }
/* ── Anteile der Bestellkategorien-Liste ──
   Hier liegt der Fall anders als bei Tags und Kundengruppen: ZWEI Spalten
   tragen variabel langen Text (die Kategorien einer Frage und die
   Kundengruppen, die sie sehen). Deren Inhalt beginnt am linken Rand seiner
   Spalte und fuellt sie, statt an der rechten Kante zu kleben — die grossen
   Luecken der Zahlentabellen entstehen hier gar nicht.

   Gemessen (Tabelle 1697px): im automatischen Raster standen 218 / 831 / 410 /
   78 / 160px — die Statusspalte war auf 78px geschrumpft und ihr Abzeichen
   klebte am Zugang, waehrend die Kategorien 831px bekamen, weil das
   automatische Raster den freien Platz nach Inhaltsbreite verteilt. Mit
   Anteilen sind es 238 / 645 / 373 / 204 / 238px.

   DER PREIS IST EINE KUERZUNG: auf 1217px reicht der Anteil fuer die laengste
   Kategorienliste („Audit, Tax, Consulting, DA, FS, Strategy, CS, ITS, Law,
   uebergreifend") nicht mehr. Sie bricht dort nicht um, sondern endet mit
   Auslassungspunkten und traegt den vollen Text als Sprechblase — dasselbe
   Verhalten wie bei Firmennamen und Adressen in den Uebersichten. Ohne
   Anteile war nichts gekuerzt, aber die Statusspalte blieb ein 78px-Rest. */
.table--questions th.col-name { width: 14%; }
.table--questions th.col-categories { width: 38%; }
.table--questions th.col-access { width: 22%; }
.table--questions th.col-state { width: 12%; }
.table--questions th.col-state,
.table--questions td.col-state { text-align: right; }
/* ── Anteile der Nutzerliste (Verwaltungskonten in den Einstellungen) ──
   ALLE SECHS ANGABEN STEHEN LINKSBUENDIG — anders als bei Tags und
   Kundengruppen, wo Zahlen und Abzeichen an der rechten Kante ihrer Spalte
   kleben. Der Abstand zwischen zwei Inhalten ist hier deshalb schlicht
   „Spaltenbreite minus Inhaltsbreite", und gleichmaessig wird er, wenn jede
   Spalte ihren laengsten Wert plus denselben Zuschlag bekommt.

   OHNE ANTEILE WAR ES SCHIEF, und zwar systematisch: `.table` rechnet die
   Breiten aus dem Inhalt und gibt den ueberschuessigen Platz den breitesten
   Spalten. Name, E-Mail und Rollen bekamen ihn, Telefon, zweiter Faktor und
   Zustand standen auf ihrer Inhaltsbreite — die Telefonnummer endete
   unmittelbar vor dem Rollenabzeichen, waehrend links davon Leere blieb
   (gemeldet vom Betreiber, 2026-09-14: „Telefon und Rollen sind relativ eng
   zusammengerueckt").

   Die Anteile sind an den laengsten Werten des Bestands gemessen:
     Name           „Philipp Schwerdfeger"        ~155px
     E-Mail         „julius.nissle@getready-…"    ~245px
     Telefon        „+49 152 22784722"            ~125px
     Rollen         Abzeichen „Super-Admin"        ~95px
     Zweiter Faktor „TOTP · E-Mail"               ~100px
     Zustand        Abzeichen „Aktiv"              ~55px
   Die Summe ist 93 %; der Rest gehoert der Handlungsspalte
   (`col-actions--rest`), deren Stift rechtsbuendig an der Zeilenkante bleibt.
   Name und E-Mail kuerzen mit Sprechblase, wenn der Anteil nicht reicht
   (text-overflow_controller.js) — dieselbe Loesung wie in den uebrigen
   Uebersichten. */
.table--adminusers th.col-name { width: 17%; }
.table--adminusers th.col-email { width: 23%; }
.table--adminusers th.col-phone { width: 15%; }
.table--adminusers th.col-roles { width: 13%; }
.table--adminusers th.col-twofactor { width: 14%; }
.table--adminusers th.col-state { width: 11%; }
/* ── Anteile einer BEARBEITUNGStabelle (Set-Inhalt) ──
   Eine Uebersicht wird gelesen, diese Tabelle wird ausgefuellt: in der
   Mengenspalte steht ein Eingabefeld.

   DIE BREITE IST DIE DES FELDES PLUS ZELLABSTAND und kein Anteil. Ein Anteil
   war der Versuch davor (20%, dann 15%) und ging in beide Richtungen schief:
   auf 1512px blieben rechts vom Feld 46px Leere, auf 1400px war die Spalte
   96px schmal und BESCHNITT das 104px breite Feld — das feste Raster weitet
   eine Spalte nicht, es kappt sie (im Browser nachgemessen, client 96 zu
   scroll 128). Mit der Rechnung passt das Feld auf jeder Breite genau, und
   der Rest gehoert der Artikelspalte, in der jetzt auch der Anzeigetext
   steht.

   DIESELBE EINHEIT WIE DAS FELD (`rem`, siehe .form-input--num in forms.css):
   `ch` haette hier 14px als Grundlage und dort 15px, und die Spalte waere
   siebzehn Pixel zu schmal geblieben. */
.table--fixed th.col-qty { width: calc(6.5rem + 2 * var(--ms-space-3)); }
/* Die Zustandsauswahl der Wareneingangskontrolle. Eine feste Breite, damit
   die drei Auswahlfelder untereinander stehen und die Artikelspalte den Rest
   bekommt — sie traegt die langen Namen. `rem` wie bei `.col-qty`: die Zelle
   rechnet sonst mit einer anderen Schriftgroesse als das Feld darin. */
.table--fixed th.col-condition { width: 14rem; }
/* Ein Textfeld NEBEN den festen Angaben: der Anzeigetext, mit dem eine Zeile
   des Katalogtextes individualisiert wird. 24% sind an den Werten gemessen —
   38% in drei Schritten auf Wunsch des Betreibers (16 → 22 → 30 → 38): auf
   2000px Fensterbreite sind das rund 470px — Platz fuer eine ausformulierte
   Bezeichnung wie „25 Paar Socken (6x Gr. 35-38, 6x Gr. 39-42)". Der Name
   daneben bleibt dabei lesbar, weil er die verbleibende Breite bekommt; auf
   1512px kuerzt er mit Sprechblase, denn sechs Spalten in einer 800px-Karte
   gehen nicht anders. */
.table--fixed th.col-label { width: 38%; }

/* SPRACHABHAENGIGE STELLE AUSSERHALB DER REITER (2026-09-17).

   Name, Einleitung und Nachsatz eines Sets stehen je Sprache in einem eigenen
   Bereich unter den Reitern. Der ANZEIGETEXT einer Set-Zeile kann das nicht:
   er gehoert in seine Tabellenzelle, und die steht ueber den Reitern.

   Am 2026-09-16 stand deshalb das englische Feld als ZWEITES unter dem
   deutschen — zwei Eingabefelder auf verschiedenen Hoehen in einer Zelle,
   genau die Etage, die der Betreiber am 2026-09-04 abbestellt hatte („eine
   Zeile, eine Linie"). Jetzt zeigt die Zelle das Feld des gewaehlten Reiters;
   geschaltet wird ueber `data-sprache` am Maskenrahmen
   (assets/controllers/tabs_controller.js).

   ALS REGEL AM VORFAHREN und nicht als `hidden` je Feld: die Tabelle bekommt
   nach dem Aufbau weitere Zeilen (assembly-parts). Ein `hidden`, das beim
   Reiterwechsel gesetzt wird, fehlte an jeder Zeile, die danach dazukommt. */
.lang-pane { display: block; }
[data-sprache="de"] .lang-pane[data-lang="en"],
[data-sprache="en"] .lang-pane[data-lang="de"] { display: none; }
/* Eigene `display`-Angabe heisst: eigene `[hidden]`-Regel (bin/design-check,
   pruefe_verborgene_bauteile). Sonst bliebe ein mit `hidden` verborgenes
   Feld sichtbar. */
.lang-pane[hidden] { display: none; }
/* „max. Sets": eine bis vierstellige Zahl mit einer langen Ueberschrift. 12%
   statt der 16% von col-stock — die Zahl braucht sie nicht, und der Platz
   gehoert dem Artikelnamen. Weniger als 12% liesse die Ueberschrift auf
   schmalen Schirmen umbrechen und die Kopfzeile zwei Zeilen hoch werden. */
.table--fixed th.col-sets { width: 10%; }
/* Zahl MIT EINHEIT in einer Zeile („1.517 Stueck"): rund 80px Inhalt, und die
   Ueberschrift „Bestand" bleibt damit einzeilig. 12% statt 16% — die
   Differenz gehoert dem Artikelnamen, der in derselben Zeile steht. */
.table--fixed th.col-stock { width: 12%; }
/* ── Die drei Spalten der Lagerauskunft ──
   Name 50 %, verfuegbare Menge 20 %, Lagerorte 30 %. Die Anteile sind KEIN
   Gefuehl: die Tabelle hatte vier Spalten, davon zwei Zahlenspalten zu je
   12 %, die aneinanderklebten und rechts daneben eine Luecke liessen. Seit
   „eingelagert" entfallen ist (es war die Summe der Spalte daneben), tragen
   drei Spalten die Breite — und ihre Kanten liegen bei 50, 70 und 100 %,
   also gleichmaessig verteilt (Weisung des Betreibers, 2026-09-17).

   Eigene Klasse und nicht `col-stock`: das sind 12 % und gelten auch in der
   Set-Maske, wo eine schmale Zahlenspalte richtig ist. Benannt nach dem, WAS
   drinsteht — wie alle Anteile hier. */
.table--fixed th.col-available { width: 20%; }
/* Lagerorte einer Kundenzeile: „R01-F02-E01  500" plus bei einer Kiste noch
   „steht auf R01-F03-E02", und bei mehreren Orten die Summe darunter. Mit
   fester Breite geht der ueberschuessige Platz an den ARTIKELNAMEN — ohne sie
   teilten Name und Lagerort den Rest gleichmaessig auf, und „Bauzaunbanner
   Wirtschaftswelt rocken" brach um, waehrend neben dem Lagerort Platz frei
   blieb (gemeldet vom Betreiber, 2026-09-09). */
.table--fixed th.col-storage { width: 30%; }
/* RECHTSBUENDIG (Weisung des Betreibers, 2026-09-17). Die Spalte ist die
   letzte, und ihr Inhalt ist kurz: „Kein Lagerort hinterlegt" endete gemessen
   bei 1512px rund 190 Pixel vor der Kartenkante und liess einen Streifen
   Leere stehen, der wie ein Fehler aussah. Am rechten Rand schliesst die Zeile
   ab — dieselbe Kante wie die Zahlen darueber in der Werkzeugleiste.

   Die Ortszeile ist eine Flex-Zeile (`.cell-main`); Textausrichtung allein
   bewegt sie nicht, ihre Elemente muessen mitwandern. */
.table--fixed th.col-storage,
.table--fixed td.col-storage { text-align: right; }
.table--fixed td.col-storage .cell-main { justify-content: flex-end; }
/* Handlungsspalte mit EINEM Knopf: 44px sind 32px Knopf, 8px Innenabstand
   links und 4px Rest. Der linke Abstand steht hier ausdruecklich, weil das
   feste Raster (`.table--fixed`) die Spalte auf ihre Breite BESCHNEIDET: mit
   den regulaeren 12px blieben dem 32px-Knopf 28px, und er stand ueber dem
   Rand. Die grosse Schwester (.col-actions, 148px) rechnet mit drei Knoepfen
   und liess hier eine leere Flaeche stehen. */
.table--fixed th.col-action-single,
.table--fixed td.col-action-single {
  /* Trefferflaeche plus der Innenabstand links — aus Tokens, seit die
     Aktions-Symbole eine eigene Groesse haben (2026-09-10). Vorher standen
     hier 44px von Hand, die Knopfhoehe des Kundenbereichs. */
  width: calc(var(--ms-action-hit) + var(--ms-space-2));
  padding-left: var(--ms-space-2);
  white-space: nowrap;
  text-align: right;
}
/* Handlungsspalte einer BEARBEITUNGSZEILE: hoch, runter, entfernen.

   DIE BREITE IST GERECHNET UND NICHT GERATEN: drei Aktions-Symbole in ihrer
   Trefferflaeche, zwei Luecken und der Innenabstand links. Als feste
   108px stand sie falsch, sobald das kompakte Massprofil griff — und das
   feste Raster (.table--fixed) BESCHNEIDET die Spalte, statt sie zu weiten:
   der Papierkorb war halb weg (im Browser nachgemessen, tdScroll 124 zu
   clientWidth 108). Seit dem 2026-09-10 rechnet sie mit --ms-action-hit; die
   Trefferflaeche eines Aktions-Symbols haengt nicht mehr an der Knopfhoehe
   und ist damit in beiden Massprofilen dieselbe.

   Die 148px von .col-actions rechnen zusaetzlich mit 32px Abstand zur letzten
   Zahlenspalte, den es hier nicht braucht: rechts von der Zeile steht nichts
   mehr, was man lesen wuerde. */
.table--fixed th.col-action-row,
.table--fixed td.col-action-row {
  width: calc(3 * var(--ms-action-hit) + 2 * var(--ms-action-gap) + var(--ms-space-2));
  padding-left: var(--ms-space-2);
  white-space: nowrap;
  text-align: right;
}

/* ── Tabelle BUENDIG unter ihrer Werkzeugleiste ──
   Eine Uebersicht steht in einer `.card--flush`: oben die Werkzeugleiste mit
   Suchfeld und Filtern, darunter die Tabelle. Beide beginnen an derselben
   Kante — aber nicht am selben Wert: `.card-toolbar` polstert waagerecht mit
   --ms-space-4 (16px), eine Tabellenzelle mit --ms-space-3 (12px). Die
   Zeichenketten der ersten Spalte standen dadurch vier Pixel weiter links als
   das Suchfeld darueber, und die letzte Spalte endete vier Pixel weiter
   rechts. Gemessen bei 1512px in der Lagerauskunft UND in der Kontaktliste —
   es betraf jede Uebersicht des Hauses, gemeldet am 2026-09-17.

   Beide Werte kommen aus dem Tokensystem; falsch war nicht der Wert, sondern
   dass zwei verschiedene an derselben Kante stehen. Angeglichen wird die
   AEUSSERE Spalte, nicht der Abstand zwischen den Spalten.

   `:not(.table--flush)`, weil die naechste Regel das Gegenteil will: eine
   Tabelle MITTEN IN EINEM FORMULAR soll gar keinen aeusseren Abstand haben.
   Heute treffen sich die beiden Klassen in keiner Vorlage — die Ausnahme steht
   trotzdem hier, damit es auch morgen stimmt.

   AUSGENOMMEN SIND DIE HANDLUNGSSPALTEN (`col-action*`): ihre Breite ist aus
   der Trefferflaeche GERECHNET und nicht geraten, und `.table--fixed`
   BESCHNEIDET eine Spalte, statt sie zu weiten. Vier Pixel mehr Innenabstand
   waeren dort vier Pixel weniger Knopf. */
.card--flush .table:not(.table--flush) > thead > tr > th:first-child,
.card--flush .table:not(.table--flush) > tbody > tr > td:first-child { padding-left: var(--ms-space-4); }
.card--flush .table:not(.table--flush) > thead > tr > th:last-child:not([class*="col-action"]),
.card--flush .table:not(.table--flush) > tbody > tr > td:last-child:not([class*="col-action"]) { padding-right: var(--ms-space-4); }

/* ── Tabelle BUENDIG im Formularabschnitt ──
   Eine Tabelle in einer Uebersicht steht in einer Karte und darf ihren
   Innenabstand haben. Eine Tabelle MITTEN IN EINEM FORMULAR steht neben
   Feldern und Beschriftungen, und die beginnen am Innenabstand der Karte. Die
   12px Zellabstand aussen ruecken sie dagegen ein: die Ueberschrift ARTIKEL
   stand rechts vom Abschnittstitel, die letzte Zahl endete vor dem Rand
   (gemeldet am 2026-09-03). `--flush` nimmt nur die AEUSSEREN Abstaende weg;
   zwischen den Spalten bleiben sie. Vorbild und Name: `.card--flush`.

   Zweiter Teil: gesperrt ist die Handlungsspalte leer
   (`[data-record-lock-action] { display: none }`), und 40px Weiss am rechten
   Rand sind dann kein Raster, sondern eine Luecke. Sie faellt zusammen, die
   vorletzte Spalte wird buendig — und beim Entsperren ruecken die Zahlen um
   die Knopfbreite nach links, weil dort jetzt ein Knopf steht. Das ist der
   Zustandswechsel, den der Nutzer selbst ausgeloest hat; still danebenstehende
   Leere waere der schlechtere Tausch. */
.table--flush > thead > tr > th:first-child,
.table--flush > tbody > tr > td:first-child { padding-left: 0; }
.table--flush > thead > tr > th:last-child,
.table--flush > tbody > tr > td:last-child { padding-right: 0; }
.record-lock.is-locked .table--flush th.col-action-single,
.record-lock.is-locked .table--flush td.col-action-single,
.record-lock.is-locked .table--flush th.col-action-row,
.record-lock.is-locked .table--flush td.col-action-row { width: 0; padding: 0; }
.record-lock.is-locked .table--flush > thead > tr > th:nth-last-child(2),
.record-lock.is-locked .table--flush > tbody > tr > td:nth-last-child(2) { padding-right: 0; }
/* Die Ueberschriften der Zahlenspalten duerfen umbrechen — sie sind laenger
   als ihre Werte, und ein Anteilsraster kann sich nicht nach ihnen richten. */
.table--fixed thead th { white-space: normal; }

/* ── Kurze Spalten schrumpfen, Text bekommt den Rest ──
   DIE VOREINSTELLUNG FUER JEDE TABELLE, ohne Anteile und ohne festes Raster.

   Sie wurde noetig, als die Verwaltungsmaske ihre Obergrenze verlor
   (tokens-dense.css): das automatische Spaltenraster verteilt den freien Platz
   im Verhaeltnis zum Inhalt — auf einem 2200px-Schirm bekam damit eine Spalte
   mit einer vierstelligen Kennung mehrere hundert Pixel, nur weil daneben ein
   noch breiterer Text stand. Vier Zeichen in dreihundert Pixeln sehen nicht
   grosszuegig aus, sondern nach einem Fehler.

   `width: 1%` heisst fuer eine Tabelle „so schmal wie moeglich": der Browser
   kann den Wunsch nicht erfuellen und nimmt die Inhaltsbreite. Zusammen mit
   `nowrap` ist das die Breite des laengsten Werts oder der Ueberschrift, je
   nachdem, was breiter ist — mehr braucht eine kurze Angabe nie. Der ganze
   Rest gehoert damit den Textspalten, die ohne Breitenangabe stehen.

   WANN STATTDESSEN ANTEILE: wenn die Zahlenspalten nebeneinander VERGLEICHBAR
   sein sollen (Firmen-, Kontakt-, Bestellliste). Dann `.table--fixed` mit
   Anteilen an den Kopfzellen — siehe weiter unten. Zwei Betriebsarten, eine
   Voreinstellung, und beide benennen die Spalten nach ihrem INHALT.

   `:not(.table--fixed)` ist nicht Vorsicht, sondern noetig: die Anteile stehen
   WEITER OBEN in dieser Datei und sind gleich spezifisch
   (`.table--fixed th.col-count` gegen `.table th.num` — beide 0,2,1). Ohne die
   Ausnahme haette die spaetere Zeile gewonnen und jede Zahlenspalte der
   Firmen-, Kontakt- und Bestelltabelle auf ihre Inhaltsbreite gezogen. So
   koennen sich die beiden Betriebsarten gar nicht begegnen. */
.table:not(.table--fixed) th.num,
.table:not(.table--fixed) th.col-code,
.table:not(.table--fixed) th.col-date,
.table:not(.table--fixed) th.col-datetime,
.table:not(.table--fixed) th.col-flag,
.table:not(.table--fixed) th.col-person,
.table:not(.table--fixed) th.col-state { width: 1%; white-space: nowrap; }
/* Datum und Kennung auch in der ZELLE einzeilig: „14.08.2026 09:12" oder eine
   gekuerzte IP ueber zwei Zeilen zu brechen macht die Spalte nicht schmaler,
   nur die Zeile hoeher. Abzeichen (col-flag, col-state) sind ausgenommen —
   die duerfen umbrechen, ihre Beschriftung ist frei gewaehlt. */
.table td.col-date,
.table td.col-datetime,
.table td.col-code,
/* EIN NAME BRICHT NICHT (2026-09-22). Die Mitarbeiterspalte des
   Bestandsjournals ist so breit wie ihr laengster Name; „L. Gruhl" ueber
   zwei Zeilen zu brechen macht die Spalte nicht schmaler, nur die Zeile
   hoeher — und die Notiz daneben soll den Rest bekommen. */
.table td.col-person { white-space: nowrap; }

/* ── Handlungsspalte am Zeilenende ──
   Die Breite ist gerechnet, nicht gewaehlt: drei Aktions-Symbole in ihrer
   Trefferflaeche, zwei Luecken, der Innenabstand rechts und der GROSSE Abstand
   links. Der linke Abstand ist der eigentliche Punkt — ohne ihn endet der
   Betrag der letzten Zahlenspalte zwoelf Pixel vor dem ersten Symbol, und
   Lesen und Bedienen fliessen ineinander.

   AUS TOKENS SEIT DEM 2026-09-10: vorher standen hier 148px als feste Zahl,
   gerechnet aus 32px-Knoepfen. Wer jetzt --ms-action-hit dreht, bekommt die
   passende Spalte mit, ohne diese Zeile zu suchen.

   `Aktionen` steht seither auch als Beschriftung darueber (Muster aus
   myOFFICE, `mo-col-actions`) — deshalb `text-align: right` an BEIDEN,
   damit Kopf und Symbole auf derselben Kante enden. */
.table th.col-actions,
.table td.col-actions {
  /* DIE ZAHL DER KNOEPFE STEHT JETZT IN EINER EIGENSCHAFT (2026-09-17) und
     nicht mehr als `3` in der Rechnung. Vorher war die Breite auf DREI Knoepfe
     festgeschrieben; eine Zeile mit vieren lief 28px aus ihrer Zelle heraus,
     ohne dass irgendetwas darauf hinwies — gemessen an der Bestellliste, als
     sie vier trug. Wer eine Spalte mit einer anderen Zahl baut, setzt die
     Eigenschaft und muss die Rechnung nicht nachvollziehen. */
  --ms-actions-count: 3;
  width: calc(var(--ms-actions-count) * var(--ms-action-hit)
              + (var(--ms-actions-count) - 1) * var(--ms-action-gap)
              + var(--ms-space-3) + var(--ms-space-6));
  padding-left: var(--ms-space-6);
  white-space: nowrap;
  text-align: right;
}
/* Eine Spalte mit anderer Knopfzahl setzt `--ms-actions-count` neben
   `.col-actions`. Die Bestellliste braucht das seit dem 2026-09-21: zu
   Schnellinfo, Oeffnen und „erneut bestellen" ist das Briefsymbol fuer die
   Notizen gekommen.

   DIE KLASSE GEHOERT AN DIE KOPFZELLE. `table-layout: fixed` nimmt die
   Spaltenbreiten aus der ersten Zeile — das ist die Kopfzeile; an der
   Datenzelle allein bliebe sie wirkungslos. `_row.html.twig` setzt sie
   trotzdem mit, damit `.table--stack` (gestapelte Karten unter 768px, ohne
   Kopfzeile) dieselbe Zelle beschreibt.

   DIE SPEZIFITAET MUSS DIE DER GRUNDREGEL TREFFEN. Die steht als
   `.table th.col-actions, .table td.col-actions` da (0,2,1); eine
   Zusatzklasse allein (0,1,0) verliert gegen sie, EGAL wie weit unten sie
   steht — der Wert blieb dann bei 3, und die Spalte war 40px zu schmal.
   Gemessen im Browser, nicht erschlossen. */
.table th.col-actions--vier,
.table td.col-actions--vier { --ms-actions-count: 4; }

/* ── Namensspalte ──
   Die Breite kommt vom Spaltenraster, nicht von hier — `max-width: 100%`
   heisst nur „nicht breiter als die Zelle". `min-width: 0` braucht es, weil
   `.cell-main` eine Flex-Zeile ist und ein Flex-Kind sonst nicht unter seine
   Inhaltsbreite schrumpft; erst damit greift `.text-truncate` (grid.css)
   ueberhaupt. Der volle Name steht im `title`.

   Hier stand zuvor `clamp(200px, 34vw, 720px)` nach dem Vorbild von
   .mo-cell-name aus myOFFICE. Das war eine Schaetzung der Spaltenbreite an
   der Stelle, an der die Spaltenbreite selbst nicht festgelegt war — sobald
   sie es ist (.table--fixed), ist die Schaetzung ueberfluessig und im
   Widerspruch dazu: sie kuerzte bei 544px, obwohl die Spalte 551px bot. */
.cell-name { min-width: 0; max-width: 100%; }

/* ── Zeile mit Symbol in einer Tabellenzelle ──
   E-Mail und Telefonnummer eines Kontakts stehen untereinander in EINER Zelle,
   je mit ihrem Symbol davor. Das Symbol nimmt den gedaempften Ton: es sagt,
   um welche Art von Angabe es geht, und ist nicht selbst die Angabe.

   `min-width: 0` am Verweis, damit `.text-truncate` in der Flex-Zeile greift
   (dieselbe Falle wie bei `.cell-name`). `flex: none` am Symbol, damit es beim
   Kuerzen nicht mitschrumpft. */
.cell-line { display: flex; align-items: center; gap: var(--ms-space-2); min-width: 0; }
.cell-line + .cell-line { margin-top: var(--ms-space-1); }
.cell-line > .ms-ico { color: var(--ms-text-secondary); }
/* Ein Verweis in einer Tabellenzelle liest sich als TEXT, nicht als Verweis:
   die Zelle ist eine Angabe, und eine Angabe in Blau und unterstrichen macht
   aus jeder Zeile ein Feld von Blickfaengen. Dass es ein Verweis ist, sagt der
   Zeiger und die Farbe beim Ueberfahren — dieselbe Regel wie in der
   Verweisliste der Arbeitsspalte (`.aside-links a`, shell.css).

   Die globale Linkregel ist im System gestrichen; ohne diese Zeilen greift der
   Browserstandard. `min-width: 0` gehoert zur Flex-Zeile, damit `.text-truncate`
   ueberhaupt kuerzen kann. */
.cell-line > a { min-width: 0; color: inherit; text-decoration: none; }
.cell-line > a:hover { color: var(--ms-accent); }

/* Anklickbare Zeile: der Zeiger sagt es, die Handlungen am Ende bleiben
   eigenstaendig anklickbar (sie stoppen das Ereignis selbst). */
.table tbody tr.row-link { cursor: pointer; }

/* ── Verweis in einer Datenzelle ──
   Ein <a> ohne eigene Klasse bekommt die Browserfarbe: Blau mit Unterstrich.
   In einer Uebersicht ist das falsch, gleich doppelt. Erstens ist es KEINE
   Systemfarbe — das System hat mit --ms-link einen eigenen Blauton, und der
   Browser kennt ihn nicht. Zweitens ist der Text in einer Tabellenzelle die
   ANGABE und nicht ein Angebot: die Veranstaltung heisst so, und ganz nebenbei
   fuehrt sie auch zur Bestellung. Wenn jede zweite Zelle blau unterstrichen
   dasteht, liest man die Spalte nicht mehr, man scannt Verweise.

   Deshalb: im Ruhezustand normaler Text, beim Zeigen der Akzent mit
   Unterstrich. Die Zeile selbst ist ohnehin anklickbar (.row-link) und zeigt
   das ueber den Zeiger; der Unterstrich beim Zeigen sagt zusaetzlich, dass
   dieses Wort ein eigenes Ziel hat. Muster: die Titelspalte der Projektliste
   in myOFFICE.

   Eine eigene Klasse und keine Regel auf `.table a`: die Handlungsspalte
   enthaelt ebenfalls <a>, und eine Regel ueber den Tag haette deren
   Aktions-Symbole (.icon-action) mit uebernommen — genau der Wettlauf um
   Spezifitaet, den base.css mit der gestrichenen globalen Linkregel vermeidet. */
.cell-link {
  color: var(--ms-text-primary);
  text-decoration: none;
  transition: color var(--ms-transition-fast);
}
.cell-link:hover,
.cell-link:focus-visible { color: var(--ms-accent); text-decoration: underline; }
/* Gedaempft, wo der Verweis eine Nebenangabe ist (E-Mail, Telefon, Firma in
   der Beizeile) — dieselbe Mechanik, eine Stufe leiser. */
.cell-link--muted { color: var(--ms-text-secondary); }
/* MIT SYMBOL DAHINTER („oeffnet in einem neuen Reiter"). Der Abstand war ein
   getipptes Leerzeichen — also die Wortluecke der Schrift und kein Mass aus
   dem System (gemeldet vom Betreiber, 2026-09-17). `inline-flex` setzt den
   Abstand auf --ms-space-2 und haelt Zeichen und Symbol zugleich auf einer
   Grundlinie; der Umbruch innerhalb des Verweises entfaellt dabei, was hier
   richtig ist: eine Bestellnummer und ihr Symbol gehoeren zusammen. */
.cell-link--extern {
  display: inline-flex;
  align-items: center;
  gap: var(--ms-space-2);
}

/* ── Datensatz sperren und entsperren ──
   Eine Stammdatenkarte startet GESPERRT: die Felder sind lesbar, aber nicht
   aenderbar, bis jemand ausdruecklich entsperrt. Grund ist nicht Sicherheit,
   sondern die Fehleingabe — eine Firmenmaske wird meist geoeffnet, um etwas
   NACHZUSEHEN, und ein Klick daneben aendert dabei einen Stammdatensatz, den
   Bestellungen, Adressen und Kontakte tragen.

   Bedienkonzept nach myOFFICE (`[data-lock-form]`, Stammdaten-Detailseiten);
   Verhalten in assets/controllers/record-lock_controller.js.

   Die Felder tragen im gesperrten Zustand `readonly`, nicht `disabled`. Der
   Unterschied ist nicht kosmetisch: ein `disabled`-Feld wird vom Browser NICHT
   mitgesendet — beim Speichern haette das Formular die gesperrten Felder leer
   uebertragen und die Stammdaten geloescht. `readonly` bleibt ausserdem
   fokussierbar und fuer Screenreader lesbar, und genau das ist gemeint: lesen
   ja, aendern nein. */
.record-lock-head {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-3);
  /* Zieht sich um den Innenabstand der Karte nach aussen wie .card-header,
     damit die Trennlinie bis an beide Kanten reicht. */
  margin: calc(-1 * var(--ms-card-padding)) calc(-1 * var(--ms-card-padding)) var(--ms-space-5);
  padding: var(--ms-space-3) var(--ms-card-padding);
  border-bottom: 1px solid var(--ms-border-divider);
}
/* DER NAME IST DER TITEL DER MASKE und war die KLEINSTE Titelgroesse der
   Seite: er hatte gar keine Angabe und erbte damit den Fliesstext (16-18px),
   waehrend die Rubriken darunter mit --ms-text-h4 (20-24px) standen und die
   Neuanlage-Ueberschrift mit --ms-text-h3. Die Rangfolge war verdreht
   (nachgefragt am 2026-09-03).

   `--ms-text-h3` — GEMESSEN, nicht gerechnet. Die Verwaltungshuelle staucht
   die fluiden Stufen (tokens-dense.css); im Browser rendert die Skala hier als
   caption 12, small 14, body 15, md 16, h4 17, lg 18, h3 20. `h4` waere also
   genau so gross wie die Rubriken darunter, und `lg` ein Pixel darueber — beides
   kein Schritt, den man sieht. `h3` ist die Groesse des Seitentitels; fett
   statt halbfett haelt beide auseinander. */
.record-lock-title {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  font-size: var(--ms-text-h3);
  font-weight: var(--ms-weight-bold);
}
/* Die Abzeichen daneben bleiben klein — sie sollen den Namen begleiten, nicht
   mit ihm wachsen. */
.record-lock-title .badge { font-size: var(--ms-text-caption); font-weight: var(--ms-weight-semibold); }

/* ── ZWEITE ZEILE DES KOPFES ──
   Links Kennung und Zustand, rechts die Handlung. `margin-left: auto` an der
   Handlung statt `justify-content: space-between`: so bleiben Kennung und
   Abzeichen beieinander, auch wenn eines von beiden fehlt. */
.record-lock-sub {
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--ms-space-3);
  margin-top: calc(-1 * var(--ms-space-2));
}
.record-lock-ident { font-size: var(--ms-text-small); color: var(--ms-text-secondary); }
.record-lock-sub-action { margin-left: auto; }
/* Schiebt den Schalter an die rechte Kante — wie in myOFFICE, wo der
   Sperrzustand dem Titel gegenuebersteht. */
.record-lock-control { display: inline-flex; align-items: center; gap: var(--ms-space-3); margin-left: auto; }

/* MIT EINER ANGABE IN DER KOPFZEILE (Artikel-Sets: „10 Sets lieferbar").

   EINE ZEILE: Name links, Angabe in der Mitte, Schalter rechts.

   DREI SPALTEN, und die Untergrenze ist `min-content` — beides mit Grund, und
   beides erst nach drei Versuchen:

     `1fr auto 1fr`: die rechte Spalte war 253px breit und ihr Inhalt
     (Statusschalter plus Sperrschalter) 394px. Er lief nach links aus der
     Spalte und lag ueber der Angabe.

     Flexverbund mit `margin-inline: auto` an der Angabe: kein Ueberlaufen, aber
     ein Flexverbund BRICHT lieber um, als seine Kinder zu schrumpfen — die
     Schalter rutschten in eine zweite Zeile. Mit `nowrap` dazu stand die Zeile,
     die Angabe sass aber bei JEDER Breite links der Mitte, weil die beiden
     Seiten ungleich breit sind.

     Jetzt: die Angabe steht in ihrer eigenen Spalte und ist damit exakt
     mittig, sobald der Platz reicht (gemessen an einer 1105px breiten Karte).
     Die Untergrenze `min-content` haelt die Schalter zusammen; wird es enger,
     gibt der NAME nach und wird gekuerzt (`.record-lock-name`), nicht die
     Zeile. Der volle Name steht zwei Zeilen darunter im Feld „Name".

   Kein `min-content` fuer die linke Spalte trotz gleicher Schreibweise: mit
   `overflow: hidden` am Namen ist dessen Mindestbeitrag null — genau das macht
   ihn zum nachgebenden Teil. */
.record-lock-head--centered {
  display: grid;
  grid-template-columns: minmax(min-content, 1fr) auto minmax(min-content, 1fr);
}
/* `overflow: hidden` AM RASTERFELD und nicht nur am Namen darin: der
   Mindestbeitrag eines Rasterfeldes ist die Breite seines Inhalts, solange es
   ueberlaufen darf — mit dem vollen Namen darin blieb die linke Spalte auf
   222px stehen und die Schalter liefen 24px aus der Karte (im Browser
   nachgemessen). Erst mit `hidden` wird der Beitrag null, und die Spalte gibt
   nach. Geclippt wird dabei nichts: nachgeben tut der Name, der dafuer seine
   eigene Huelle hat. */
.record-lock-head--centered .record-lock-title { min-width: 0; overflow: hidden; }
.record-lock-head--centered .record-lock-name {
  min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.record-lock-head--centered .record-lock-control { margin-left: 0; justify-self: end; }
.record-lock-mid { justify-self: center; }

/* UNTER 1600px GESTAPELT. Die Angabe (126px) und die beiden Schalter (394px)
   brauchen zusammen rund 570px, dazu der Name — eine Zeile traegt das erst ab
   etwa 1600px Fensterbreite, weil links Navigationsspalte und Auswahlliste
   stehen (gemessen: 1900px Fenster → 1105px Karte, eine Zeile; 1500px → 705px,
   der Sperrschalter ragte 24px aus der Karte).

   Gestapelt statt gequetscht: der Name bekommt die erste Zeile ganz, die
   Angabe bleibt mittig, die Schalter kommen darunter. Auf schmalen Schirmen
   ist Hoehe billiger als Breite — ein auf „G…" gekuerzter Name ist keine
   Auskunft mehr. */
/* UNTER 1600px ZWEI ZEILEN: Name und Angabe oben, die Schalter darunter.

   Drei Zeilen waren der erste Versuch (Name, Angabe zentriert, Schalter) — auf
   einem 14-Zoll-MacBook stand die Angabe damit allein auf einer Zeile in der
   Mitte, und der Kopf war hoeher als die ersten beiden Felder der Maske
   zusammen (gemeldet am 2026-09-03). Die Angabe gehoert neben den Namen: sie
   sagt etwas UEBER ihn.

   Zentriert wird hier nicht mehr — auf schmalen Schirmen ist die Zeile ohnehin
   voll, und eine Mitte, die es nicht gibt, vorzutaeuschen macht sie nur
   loecheriger. */
/* Die zwei Gruppen rechts: die Handlung (Vorschau) und der Schalterblock
   (Status, Sperrzustand, Schalter). Auf breiten Schirmen stehen sie
   nebeneinander in EINEM Rasterfeld — deshalb die Huellen. */
.record-lock-action,
.record-lock-switch { display: inline-flex; align-items: center; gap: var(--ms-space-3); }

/* UNTER 1600px: Name und Angabe links, der Schalterblock rechts, die Handlung
   in einer eigenen Zeile unter dem Namen (so gewuenscht am 2026-09-03).

   `display: contents` an der Gruppe ist der Kern: damit werden ihre beiden
   Huellen selbst zu Rasterfeldern und lassen sich einzeln setzen — ohne einen
   zweiten Knopf im Markup. Es sind genau ZWEI Kinder fuer ZWEI Felder; mit
   drei losen Kindern laege eins auf dem anderen. */
@media (max-width: 1600px) {
  .record-lock-head--centered {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "titel mitte schalter"
      "aktion aktion aktion";
    row-gap: var(--ms-space-2);
  }
  .record-lock-head--centered .record-lock-title { grid-area: titel; overflow: visible; }
  .record-lock-head--centered .record-lock-name { white-space: normal; }
  .record-lock-head--centered .record-lock-control { display: contents; }
  .record-lock-head--centered .record-lock-action { grid-area: aktion; justify-self: end; }
  .record-lock-head--centered .record-lock-switch { grid-area: schalter; justify-self: end; }
  .record-lock-mid { grid-area: mitte; justify-self: start; align-self: center; }
}

/* Das Abzeichen mit einer ZAHL — und KEINE eigene Groesse.

   Hier stand bis zum 2026-09-03 eine Stufe mehr (--ms-text-small statt
   --ms-text-caption) mit der Begruendung, die Zahl solle Vorrang haben. Der
   Betreiber hat das verworfen, und die Regel ist die bessere: ABZEICHEN SIND IN
   DER GANZEN ANWENDUNG GLEICH GROSS. Ein Etikett, das aus der Reihe faellt,
   verschiebt das Gewicht einer Zeile — im Kartenkopf sah die Angabe damit
   wichtiger aus als der Name des Datensatzes.

   Was bleibt, ist keine Groesse, sondern Form: gleich breite Ziffern, und EINE
   Zeile — ohne das brach „4 Sets lieferbar" um und zog die Kopfzeile auf 77px. */
.badge-figure {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* Gesperrtes Feld: gedaempfte Flaeche, ruhiger Rand, gedaempfter Text, Zeiger
   sagt „hier nicht".

   HIER STAND, der Text bleibe in der primaeren Farbe, weil er der Inhalt sei,
   den man lesen soll. Das Ergebnis war ein Formular, das gesperrt genauso
   aussah wie entsperrt: --ms-bg-secondary ist #F5F5F7 und auf einer weissen
   Karte kaum zu sehen, und die Schrift war dieselbe. Wer die Karte
   aufschlaegt, sieht ein normales Formular, tippt hinein und wundert sich.

   --ms-text-secondary ist der richtige Ton dafuer, und zwar OHNE die
   Lesbarkeit zu opfern: es ist die letzte Stufe, die auf beiden Untergruenden
   AA erfuellt (4,66:1 / 5,07:1 — dieselbe Begruendung wie bei .form-help in
   forms.css). --ms-text-tertiary waere unzulaessig. Lesbar bleibt also alles;
   nur sagt es jetzt auch, dass es nicht angefasst werden will. */
.record-lock.is-locked .form-input,
.record-lock.is-locked .form-select,
.record-lock.is-locked .form-textarea {
  background: var(--ms-bg-secondary);
  border-color: var(--ms-border-divider);
  color: var(--ms-text-secondary);
  cursor: default;
}
/* Die Beschriftungen mit: ein Feldname in vollem Schwarz ueber einem
   gedaempften Feld sieht aus wie ein Fehler in der Darstellung. */
.record-lock.is-locked .form-label,
.record-lock.is-locked .form-checkbox { color: var(--ms-text-secondary); }
/* Der Fokusrahmen gehoert zum Bearbeiten. Ein gesperrtes Feld ist weiter
   erreichbar (Tastatur, Screenreader), soll aber nicht aussehen, als warte es
   auf eine Eingabe. */
.record-lock.is-locked .form-input:focus,
.record-lock.is-locked .form-select:focus,
.record-lock.is-locked .form-textarea:focus {
  border-color: var(--ms-accent);
  box-shadow: none;
}
/* Handlungen der Karte (Speichern, Fortschritt der Pflichtfelder) sind
   verborgen, solange gesperrt ist.

   myOFFICE laesst sie sichtbar und setzt sie auf `disabled`. Das geht hier
   nicht: der Speichern-Knopf gehoert der Feldrueckmeldung (v2-validate.js),
   die ihn nach jeder Pruefung selbst wieder freischaltet — zwei Stellen um
   dasselbe `disabled` haetten sich abgewechselt. Und `pointer-events: none`
   allein blockt nur die Maus, nicht die Leertaste auf dem fokussierten Knopf.
   Verbergen ist die eine Fassung, die beides loest, und es kostet nichts:
   der Schalter daneben heisst „Entsperren" und sagt, wie man hinkommt. */
.record-lock.is-locked [data-record-lock-action] { display: none; }

/* GESPERRT HEISST AUCH: KEIN FOKUS. `readonly` haelt ein Feld
   fokussierbar — ein Klick setzte den Fokusrahmen, und die Karte sah
   bearbeitbar aus, obwohl sie es nicht war (nachgefragt am 2026-09-02).
   Zusammen mit `tabIndex = -1` im Controller ist das Feld damit weder mit
   der Maus noch mit der Tastatur erreichbar.

   DER PREIS, bewusst in Kauf genommen: markieren und kopieren geht im
   gesperrten Zustand nicht mehr. Wer eine Artikelnummer herausholen will,
   entsperrt — dieselbe Entscheidung wie beim Aendern, und myOFFICE verhaelt
   sich mit `disabled` genauso. */
.record-lock.is-locked .form-input,
.record-lock.is-locked .form-select,
.record-lock.is-locked .form-textarea,
.record-lock.is-locked .radio-option,
.record-lock.is-locked .form-checkbox { pointer-events: none; }

/* ── Aktiv/Inaktiv eines Datensatzes ──
   Zwei benannte Knoepfe statt eines Schalters: beide Zustaende stehen da, und
   der aktuelle ist hervorgehoben. Ein Schalter verlangte zu wissen, was „an"
   bedeutet. Markup: templates/v2/admin/_record_status.html.twig, Verhalten:
   assets/controllers/record-status_controller.js.

   Der Akzent markiert den AKTUELLEN Zustand, nicht den guten — deshalb traegt
   ihn auch „Inaktiv", wenn der Datensatz stillgelegt ist.

   Die aktuelle Seite ist HELL HINTERLEGT und nicht voll gefuellt: dieselbe
   Paarung wie `.badge-info` (Akzent als Schleier, --ms-link als Schrift,
   data-display.css) und dieselbe Optik wie das Statussegment in myOFFICE. Ein
   vollflaechiger Akzentknopf in der Fusszeile stand neben dem echten
   Speichern-Knopf und sah aus wie ein zweiter — er ist aber eine Anzeige, die
   man umschalten kann, keine Handlung. */
.record-status { display: inline-flex; align-items: center; gap: var(--ms-space-2); }
.record-status-label { font-size: var(--ms-text-caption); color: var(--ms-text-secondary); }
.record-status-seg {
  display: inline-flex;
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-sm);
  overflow: hidden;
}
.record-status-opt {
  background: none; border: 0;
  padding: var(--ms-space-1) var(--ms-space-3);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-medium);
  color: var(--ms-text-secondary);
  cursor: pointer;
}
.record-status-opt + .record-status-opt { border-left: 1px solid var(--ms-border-light); }
.record-status-opt:hover:not(.is-on):not(:disabled) { background: var(--ms-bg-secondary); color: var(--ms-text-primary); }
.record-status-opt.is-on { background: rgba(var(--ms-accent-rgb), var(--ms-tint)); color: var(--ms-link); }
/* Fachlich gesperrte Seite (der letzte aktive Kontakt einer Firma) und der
   Moment, in dem die Antwort des Servers aussteht. Beides wie die abgeschaltete
   Seitenzahl der Blaetterung: gedaempft und nicht anklickbar. */
.record-status-opt:disabled { color: var(--ms-text-tertiary); cursor: default; }
.record-status-seg.is-busy { pointer-events: none; }
/* Solange die Karte gesperrt ist, sagt das Segment nur, wo man steht. Der
   Zustand aendert sich ueber dieselbe Entscheidung wie die Felder: erst
   entsperren. */
.record-lock.is-locked .record-status-seg { pointer-events: none; }
.record-lock.is-locked .record-status-opt:not(.is-on) { color: var(--ms-text-tertiary); }

/* ── Spalten der Bestelltabelle ──
   Anteile und keine Pixel, aus demselben Grund wie bei den Firmen: sie wachsen
   mit dem Fenster mit. Die Summe liegt bei 62 Prozent — der Rest gehoert der
   Veranstaltungsspalte, dem einzigen Feld mit unbekannter Laenge. Die
   Handlungsspalte kommt als feste Breite (.col-actions) obendrauf.

   In der Kundensicht entfaellt „Kunde"; die Veranstaltung bekommt die 14
   Prozent dazu. Deshalb steht an der Textspalte KEINE Breite: sie ist in
   beiden Faellen der Rest. */
/* Die Bestellnummer als feste Breite und nicht als Anteil: sie ist IMMER zwölf
   Ziffern lang (App\Service\OrderIdGenerator), also weder laenger noch kuerzer
   zu erwarten. Ein Anteil machte sie auf schmalen Arbeitsspalten zu „2026…" —
   ausgerechnet die Kennung, an der man die Zeile wiedererkennt. */
.table--fixed th.col-orderno { width: 140px; }
/* Datumsspalten ebenfalls fest: der Wert ist immer zehn Zeichen breit, und die
   Ueberschrift „Anfahrt/Anlieferung" ist EIN Wort — sie kann nicht umbrechen,
   wenn der Anteil nicht reicht, sondern wird abgeschnitten.

   136px UND NICHT 108 (2026-09-17). Die 108 fassten den WERT (ein Datum misst
   74px, mit Zellpolster 98), aber nicht die beiden laengeren Inhalte derselben
   Spalte — nachgemessen im Browser:
     „Keine Abholung"   105px + 24px Polster = 129px
     Kopf „ANLIEFERUNG"  87px + Sortierpfeil + Polster ≈ 129px
   Beides brach deshalb zweizeilig um, und eine Zeile mit „Keine / Abholung"
   sieht aus wie ein Fehler. Der Pfeil zaehlt mit, obwohl er unsichtbar ist:
   `.th-sort-icon` steht auf `opacity: 0` und belegt seinen Platz weiter. */
.table--fixed th.col-date { width: 136px; }
/* MITTIG UNTER DER UEBERSCHRIFT (Weisung des Betreibers, 2026-09-17): „ich
   moechte, dass hier die Spalte Anlieferung/Abholung so ausgefuehrt wird, dass
   die Datumsangaben mittig unter der Spaltenbeschriftung liegen."

   Vorher standen die Werte rechtsbuendig (`.num`) und die Ueberschrift links —
   in einer 136px breiten Spalte liegen dazwischen gut vierzig Pixel, und die
   Zahl stand sichtbar neben ihrer eigenen Beschriftung statt darunter.
   Rechtsbuendig ist die Regel fuer Betraege, die man untereinander vergleicht;
   ein Datum hat immer dieselbe Breite und gewinnt dadurch nichts.

   BEIDE mittig, Kopf und Zelle — sonst verschiebt sich das Problem nur. Die
   gleichen Zifferbreiten aus `.num` bleiben: die Zelle traegt beide Klassen,
   und diese Regel ist die spezifischere (0,2,1 gegen 0,2,0). */
.table--fixed th.col-date,
.table--fixed td.col-date { text-align: center; }
/* DER UNSICHTBARE PFEIL ZAEHLT MIT (2026-09-17). `text-align: center` allein
   reichte nicht: zentriert wird der KASTEN aus Beschriftung UND Sortierpfeil,
   und der Pfeil steht dauerhaft im Fluss (`.th-sort-icon` wechselt nur die
   Deckkraft, damit die Ueberschriften beim Ueberfahren nicht springen). Die
   sichtbare Beschriftung sass dadurch sieben Pixel links der Spaltenmitte —
   gemessen, und genau das hat der Betreiber gesehen.

   Ein Polster links in der Groesse von Pfeil plus Luecke schiebt den Kasten so
   weit nach rechts, dass die BESCHRIFTUNG mittig steht und der Pfeil rechts
   daneben haengt. Beide Werte kommen aus dem Tokensystem und wachsen mit der
   Schrift mit; eine feste Zahl waere bei jeder Schriftaenderung wieder falsch. */
.table--fixed th.col-date .th-sort { padding-left: calc(var(--ms-ico-sm) + var(--ms-space-1)); }
/* DATUM MIT UHRZEIT braucht eine eigene Breite: „08.09.2026 14:45" sind
   sechzehn Zeichen, und in den 108px von `.col-date` wird daraus „08.09.2026
   14" — die Uhrzeit fehlt genau dort, wo sie gebraucht wird (in den Backups
   entstehen mehrere Sicherungen am selben Tag; nachgemessen am 8. September
   2026: Zelle 108px, Inhalt 141px).

   Das Jahr weglassen wie im Anmeldeprotokoll (`d.m. H:i`) ist hier keine
   Loesung: dort sind es Sperren der letzten Minuten, ein Archiv reicht Monate
   zurueck. 150px fassen den Wert samt Zellpolster. */
.table--fixed th.col-datetime { width: 150px; }
.table--fixed th.col-customer { width: 13%; }
/* „Fortschritt" ist ebenfalls ein Wort, das nicht umbrechen kann, und der
   Balken braucht daneben eine Mindestbreite, sonst ist er kein Balken mehr.

   150px UND NICHT 120 (2026-09-17, Weisung des Betreibers: die vier Spalten
   rechts „ein klein wenig entzerren"). Von den 120px gingen das Polster und
   der Zaehler „1/4" ab — dem Balken selbst blieben rund 60px, und auf 60px
   ist ein Viertel zwoelf Pixel lang. */
.table--fixed th.col-progress { width: 176px; }
/* Der Zustand traegt die Beschriftung des aktuellen SCHRITTS, und die ist frei
   gewaehlt („Anfrage gesendet", „Stand aufgebaut", was ein Ablauf sonst noch
   vorsieht). Eine Breite, die JEDE denkbare Beschriftung fasst, gibt es nicht —
   deshalb grosszuegig UND umbruchfaehig (siehe .table .order-badge weiter
   unten). Ein abgeschnittenes Abzeichen sagt nichts.

   16 PROZENT UND NICHT 13 (2026-09-17): seit eine Zeile zusaetzlich „Aenderung
   angefragt" tragen kann, stehen hier zwei Abzeichen untereinander, und das
   zweite brach in 13 Prozent selbst noch einmal um („Aenderung / angefragt").
   Die drei Prozent kommen aus der Veranstaltungsspalte — sie ist der Rest, und
   ihr Text kuerzt ohnehin mit Auslassungspunkten. */
.table--fixed th.col-status { width: 14%; }

/* In einer Tabellenzelle darf das Zustandsabzeichen umbrechen: die
   Beschriftung kommt aus dem Ablauf und ist frei gewaehlt. Ausserhalb (Kopf des
   Bestelldetails, Vorschau) bleibt es einzeilig. */
.table .order-badge { white-space: normal; }
/* AUSNAHME: ein Abzeichen mit FESTER Beschriftung bricht nicht um. Der Umbruch
   oben ist fuer frei gewaehlte Ablaufschritte gedacht, deren Laenge niemand
   kennt. „Aenderung angefragt" steht dagegen in den Uebersetzungsdateien und
   ist zweisprachig bekannt — zweizeilig sah es aus wie ein Textfehler. */
.table .order-badge--fixed { white-space: nowrap; }

/* ── Sortierbarer Spaltenkopf ──
   Der Pfeil steht bei der aktiven Spalte fest da und erscheint sonst erst beim
   Zeigen: eine Kopfzeile mit acht dauerhaften Pfeilen ist unruhiger, als sie
   nuetzlich waere. Er bleibt aber IMMER im Fluss (nur die Deckkraft wechselt),
   sonst springen die Ueberschriften beim Ueberfahren. */
.th-sort {
  display: inline-flex; align-items: center; gap: var(--ms-space-1);
  color: inherit; text-decoration: none;
}
.th-sort:hover { color: var(--ms-text-primary); }
/* Die GROESSE kommt aus der Skala (`ms-ico--sm` an der Aufrufstelle in
   v2/_sort_th.html.twig), hier steht nur noch das Verhalten. Vorher
   schrumpfte ein `font-size: 0.75em` den Pfeil in einer 12px-Kopfzeile
   auf 9px — kleiner als jede andere Symbolgroesse der Anwendung. */
.th-sort-icon { opacity: 0; transition: opacity var(--ms-transition-base); }
.th-sort:hover .th-sort-icon { opacity: 0.45; }
.th-sort.is-active { color: var(--ms-text-primary); }
.th-sort.is-active .th-sort-icon { opacity: 1; color: var(--ms-accent); }
.th-sort:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; border-radius: var(--ms-radius-sm); }

/* ── Fortschritt in einer Zeile ──
   Balken und Zahlen NEBENEINANDER, nicht uebereinander: im Balken waeren die
   Zahlen unlesbar, sobald die Fuellung darunter durchlaeuft — dieselbe
   Erfahrung wie beim Balken der Detailansicht. Der Fuellgrad kommt als Klasse
   `.fill-<n>` (base.css) ueber --ms-fill, nicht als Inline-Stil.

   Gruen und nicht die Statusfarbe: ausdrueckliche Festlegung,
   docs/architecture/auftragsablaeufe.md Abschnitt 8. */
.progress-cell { display: flex; align-items: center; gap: var(--ms-space-2); }
.progress-track {
  flex: 1 1 auto; min-width: 32px; height: 6px;
  background: var(--ms-bg-secondary);
  border-radius: var(--ms-radius-pill);
  overflow: hidden;
}
.progress-fill {
  display: block; height: 100%; width: var(--ms-fill, 0%);
  background: var(--ms-success);
  border-radius: var(--ms-radius-pill);
}
.progress-label {
  flex: none;
  font-size: var(--ms-text-caption); color: var(--ms-text-secondary);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Gedaempfter Platzhalter in einer Zelle („—", „Keine Abholung"). */
.cell-muted { color: var(--ms-text-secondary); }
/* Zwei Abzeichen in einer Zelle — Bestellstatus und „Aenderung angefragt".
   Sie brechen um statt die Spalte zu sprengen: die Statusspalte ist ein
   Anteil an `.table--fixed` und kann nicht breiter werden, und ein Abzeichen
   traegt `white-space: nowrap`. */
.cell-badges { display: flex; flex-wrap: wrap; align-items: center; gap: var(--ms-space-1); }
/* Symbol neben dem Namen in einer Zelle (Freigaben an einer Bestellung). */
.cell-icon { flex: none; color: var(--ms-text-tertiary); }

/* ── DER BLAUE STREIFEN AM ZEILENANFANG IST ENTFALLEN (2026-09-21) ──
   `.unseen-marker` hing an `admin_unseen_changes` und behauptete „neue
   Nachricht", meinte aber fast immer einen Datei-Upload: 1.126 Marker im
   Bestand, 1.113 davon vom Typ `notes`, bei vier existierenden Notizen — der
   aelteste vom 26.11.2024. Eine Kante ohne Beschriftung, die Vorlesegeraete
   ausserdem gar nicht wahrnehmen.

   An seiner Stelle steht das Briefsymbol der Handlungsspalte
   (`.icon-action--notiz`, buttons.css). Es sagt dasselbe mit einem Wort in
   der Sprechblase, haengt am Lesestand (`order_seen`) und meint genau eine
   Sache: ungelesene Eintraege im Notizverlauf. */

/* ── Zeitraumfeld in einer Werkzeugleiste ── */
.toolbar-daterange {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  font-size: var(--ms-text-caption); color: var(--ms-text-secondary);
  white-space: nowrap;
}
/* DIE FALLE, DIE DAS HAUS SCHON DREIMAL KENNT (`.badge[hidden]`,
   `.panel-foot--bar[hidden]`, `.scan-stage[hidden]`): eine `display`-Angabe im
   Autorenstil schlaegt die Browser-Regel fuer `[hidden]` — Autorenstile gewinnen
   gegen den Browser, unabhaengig von der Spezifitaet. Ohne diese Zeile stehen
   die Datumsfelder des festen Zeitraums in der Auswertung IMMER da, auch wenn
   ein relativer Zeitraum gewaehlt ist. */
.toolbar-daterange[hidden] { display: none; }
/* Die Beschriftung steht in der Leiste NEBEN dem Feld, nicht darueber —
   `.form-label` bringt `display:block` und einen Abstand nach unten mit, die
   hier beide falsch sind. Und das Feld nimmt seine Inhaltsbreite, damit die
   Leiste nicht von zwei Datumsfeldern gesprengt wird. */
.toolbar-daterange .form-label { margin: 0; }
/* Der Gedankenstrich zwischen Anfang und Ende des Zeitraums — er ersetzt das
   Wort „bis" und braucht zwoelf statt neunzig Pixel. Fuer Vorlesegeraete steht
   das Wort weiter da, als verborgene Beschriftung des zweiten Feldes. */
.toolbar-daterange-sep { color: var(--ms-text-tertiary); }
.toolbar-daterange .form-input,
.toolbar-daterange .v2-datepicker,
.toolbar-daterange .v2-datepicker-display { width: auto; }
/* 10rem, nicht 8,5: „TT.MM.JJJJ" braucht rund 90px, und das Feld verliert
   links 16px Polsterung und rechts 40px an das Kalendersymbol. Bei 8,5rem war
   der Platzhalter abgeschnitten („TT.MM.JJ."). myOFFICE kommt mit 140px aus,
   weil sein Symbol schmaler polstert. */
.toolbar-daterange .v2-datepicker-display { width: 10rem; }


/* ── Blaetterung unter einer Tabelle ── */
.table-pagination {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--ms-space-3);
  padding: var(--ms-space-3) var(--ms-space-4);
  border-top: 1px solid var(--ms-border-divider);
}
.table-pagination-info { font-size: var(--ms-text-caption); color: var(--ms-text-secondary); }
.table-pagination .pagination { margin: 0; }

/* ── Schnellinfo im Dialog ──
   SIE HAT KEINE EIGENEN BAUTEILE MEHR (2026-09-17, Weisung des Betreibers:
   „bitte nutze hierfuer ausschliesslich Elemente aus unserem Token-System").
   Entfallen sind `.preview-head`, `.preview-ids`, `.preview-grid`,
   `.preview-title`, `.preview-address` und `.preview-foot` — sechs Klassen,
   die nachbauten, was das System bereits hat: das Anteilsraster der
   Angabengruppen (`.detail-facts`), ihre Ueberschrift (`.detail-facts-title`),
   die gestapelte Paarliste und der Dialogfuss (`.modal-footer`).

   Geblieben ist `.preview-note` weiter unten: den Lade- und Fehlerhinweis
   setzt der Controller, er hat kein Gegenstueck im System. */
/* ── Paarliste: Beschriftung und Wert ──
   Das Bauteil fuer „Angabe: Wert" ausserhalb einer Tabelle — Schnellinfo der
   Bestellliste, Angabengruppen des Bestelldetails. Beschriftung links
   gedaempft, Wert rechts in Mittelstaerke; wird es eng, steht der Wert unter
   seiner Beschriftung.

   EIGENES BAUTEIL und nicht `.aside-pairs`: die traegt `overflow-wrap:
   anywhere`, weil sie fuer die schmale Arbeitsspalte einer Stammdatenmaske
   gebaut ist. Im Dialog zerlegte das ein Datum in „04.0 / 9.20 / 26".

   `--ms-text-small` (14px) und NICHT `--ms-text-caption` (12px): das hier ist
   Text, den man liest, keine Beizeile. Die Angaben des Bestelldetails standen
   bis zum 2026-09-02 in 12px und waren dadurch kaum zu entziffern. */
/* Der Abstand ZWISCHEN zwei Angaben ist --ms-space-3 (12px) und nicht
   --ms-space-2 (8px): bei acht Pixeln stehen „Firma" und „Ansprechpartner"
   dichter beieinander als die Beschriftung an ihrem eigenen Wert, und die
   Gruppe liest sich als ein Block statt als eine Aufzaehlung. Zwoelf ist die
   naechste Stufe der Skala — kein Zwischenwert. */
.pair-list { margin: 0; display: flex; flex-direction: column; gap: var(--ms-space-3); }
.pair {
  display: flex; justify-content: space-between; gap: var(--ms-space-4);
  font-size: var(--ms-text-small); line-height: 1.5;
}
.pair dt { color: var(--ms-text-secondary); flex: none; }
.pair dd { margin: 0; font-weight: var(--ms-weight-medium); text-align: right; min-width: 0; }

/* GESTAPELT: Beschriftung ueber dem Wert, beides linksbuendig.
   Fuer schmale Spalten — im Datenblatt einer Bestellung stehen bis zu vier
   Gruppen nebeneinander, jede rund 230px breit. Nebeneinander blieben dem Wert
   davon 140px, und eine Anschrift stand darin vierzeilig und rechtsbuendig:
   technisch richtig, lesbar nicht. Gestapelt hat der Wert die ganze Breite.

   Dieselbe Paarliste und nicht ein zweites Bauteil: es ist dieselbe Aussage in
   einer anderen Spaltenbreite. Die nebeneinanderstehende Fassung bleibt fuer
   die Schnellinfo, deren zwei Spalten breit genug sind. */
.pair-list--stacked .pair { flex-direction: column; gap: 0; }
.pair-list--stacked .pair dt { font-size: var(--ms-text-caption); }
/* Der WERT eine Stufe groesser als die Beschriftung — 16px gegen 12px. In zwei
   breiten Spalten ist dafuer Platz, und das Datenblatt ist die Stelle, an der
   man Angaben tatsaechlich liest (Anschrift abtippen, Nummer vergleichen). */
.pair-list--stacked .pair dd { text-align: left; font-size: var(--ms-text-md); }
.preview-note { color: var(--ms-text-secondary); font-size: var(--ms-text-small); margin: 0; }

/* ── Uebersichtstabelle auf schmalen Schirmen ──
   Die Tabelle wird zum Stapel: je Zeile eine Karte, je Zelle eine Zeile mit
   ihrer Ueberschrift davor (aus `data-label`). Sie BLENDET NICHTS AUS — genau
   das war der Fehler der alten Bestellliste, die unter 700px Datum, Zustand
   und den Verweis ins Detail wegnahm und eine reine Namensliste uebrig liess.

   `data-label` steht an den Zellen der Bestelltabelle; Tabellen ohne die
   Angabe stapeln trotzdem sauber, nur ohne Beschriftung. */
@media (max-width: 767px) {
  .table--stack thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }
  .table--stack,
  .table--stack tbody,
  /* `tfoot` MUSS MIT. Ohne ihn blieb die Fussgruppe eine Tabellengruppe,
     waehrend ihre Zeile schon ein Block war — der Browser legt dann eine
     anonyme Tabellenbox darum, und die schrumpft auf ihren Inhalt: die
     Summenzeile der Auswertung war auf einem 390px-Schirm gemessene 206px
     breit statt 342px und stand als grauer Klotz mitten in der Karte
     (9. September 2026). Vorher fiel es nicht auf, weil keine gestapelte
     Tabelle des Hauses einen Fuss hatte. */
  .table--stack tfoot,
  .table--stack tr,
  .table--stack td { display: block; width: auto; }
  .table--stack.table--fixed { table-layout: auto; }
  .table--stack tbody tr {
    padding: var(--ms-space-3) 0;
    border-bottom: 1px solid var(--ms-border-divider);
  }
  .table--stack tbody tr:last-child { border-bottom: 0; }
  /* GESTAPELT IST DIE SUMME EIN BLOCK und keine Zeile aus Zellen: Strich und
     Grund gehoeren deshalb an die ZEILE. An jeder einzelnen Zelle waeren es
     fuenf Oberstriche untereinander, und die Polsterung der Zeile liesse
     ueber und unter dem grauen Feld je einen weissen Streifen. */
  .table--stack tfoot tr {
    padding: var(--ms-space-3) 0;
    border-top: 2px solid var(--ms-border-divider);
    background: var(--ms-bg-secondary);
  }
  /* `border-bottom` GEHOERT DAZU, seit die duenne Summe unten einen Strich
     traegt (2026-10-06): gestapelt ist jede Zelle eine eigene Zeile, und der
     Strich stand dann unter JEDER — auch unter den leeren, die zu einem
     Doppelstrich zusammenfielen. Gestapelt traegt die ZEILE die Linien. */
  .table--stack .table-total td { border-top: 0; border-bottom: 0; background: none; }
  /* Gestapelt dieselbe Ordnung: Strich oben UND unten, beide 1px — an der
     ZEILE, aus dem Grund im Kommentar darueber. */
  .table--stack tfoot tr.table-total--thin {
    border-top-width: 1px;
    border-bottom: 1px solid var(--ms-border-divider);
  }
  .table--stack td {
    border: 0;
    padding: var(--ms-space-1) var(--ms-space-4);
    overflow: visible;
    text-align: left;
  }
  .table--stack td.num { text-align: left; }
  /* Gestapelt steht jede Zelle als Wertepaar unter ihrer Beschriftung — dort
     waere rechtsbuendig gegen die Leserichtung. Dieselbe Ausnahme wie bei den
     Zahlen eine Zeile darueber. */
  .table--stack td.col-storage { text-align: left; }
  .table--stack td.col-storage .cell-main { justify-content: flex-start; }

  /* Beschriftung links, Wert rechts — als RASTER und nicht als Flex-Zeile.
     Eine Zelle kann zwei Angaben tragen (Firmenname und Besteller,
     Veranstaltung und Ort); im Raster stehen sie beide in der zweiten Spalte
     untereinander. In einer Flex-Zeile teilten sie sich die Restbreite und
     waeren auf einem Telefon beide gekuerzt — also beide unlesbar.

     Zellen OHNE Beschriftung (die erste und die Handlungen) bleiben Bloecke,
     sonst stuenden sie eingerueckt unter einer leeren Spalte. */
  .table--stack td[data-label] {
    display: grid;
    grid-template-columns: 38% 1fr;
    column-gap: var(--ms-space-3);
    align-items: baseline;
  }
  .table--stack td[data-label]::before {
    content: attr(data-label);
    grid-column: 1; grid-row: 1;
    font-size: var(--ms-text-caption); color: var(--ms-text-secondary);
  }
  .table--stack td[data-label] > * { grid-column: 2; min-width: 0; }
  /* Ein Abzeichen umschliesst seinen Text; als Rasterkind wuerde es sonst auf
     die volle Spaltenbreite gedehnt und saehe aus wie eine Flaeche. */
  .table--stack td[data-label] > .order-badge { justify-self: start; }

  /* Die erste Zelle ist die Ueberschrift der Karte: Bestellnummer und
     Projektnummer nebeneinander wie eine Zeile, nicht als Wertepaar. */
  .table--stack td:first-child {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: var(--ms-space-2);
    padding-bottom: var(--ms-space-2);
  }
  .table--stack td.col-actions {
    width: auto;
    padding-left: var(--ms-space-4);
    padding-top: var(--ms-space-2);
  }
  .table--stack .admin-row-actions { justify-content: flex-start; }
  /* Auswahlfelder der Werkzeugleiste duerfen die Karte nicht sprengen: ihre
     Breite kommt sonst vom laengsten Firmennamen. */
  .card-toolbar .form-select { max-width: 100%; }

  .pair { flex-direction: column; gap: 0; }
  .pair dd { text-align: left; }
}

/* ── Zwei Beschriftungen, eine Breite ──
   Der Sperrschalter tauschte seine Texte aus („Gesperrt" gegen „Entsperrt",
   „Entsperren" gegen „Sperren"), und weil die Texte verschieden lang sind,
   sprang die ganze Gruppe bei jedem Klick hin und her.

   Beide Fassungen liegen jetzt im SELBEN Rasterfeld: die Breite ist die der
   laengeren, unabhaengig davon, welche zu sehen ist. Kein `min-width` in `ch` —
   das waere geraten und in der englischen Fassung falsch („Locked"/„Unlocked",
   „Unlock"/„Lock" haben andere Verhaeltnisse).

   `visibility: hidden` und nicht `display: none`: verborgen soll sie sein,
   aber weiter Platz belegen — sonst waere die Breite wieder die der sichtbaren
   Haelfte. Aus der Vorlesehilfe nimmt sie der Controller mit `aria-hidden`. */
.record-lock-swap { display: inline-grid; justify-items: center; }
.record-lock-swap > * { grid-area: 1 / 1; }
.record-lock-swap > [aria-hidden="true"] { visibility: hidden; }
/* KEIN UMBRUCH. Der Zweck des Rasterfeldes ist, dass die Gruppe die Breite der
   laengeren Beschriftung behaelt und beim Umschalten nichts wandert. Mit
   Umbruch loeste sich genau das auf: „Speichern und sperren" brach auf drei
   Zeilen (gemessen 72px Breite bei 72px Inhaltsbreite, statt der noetigen
   ~140px), die Zeile wuchs in die HOEHE, und der Schalter sprang senkrecht
   statt waagerecht. Sichtbar geworden, als das Zuruecksperren am 2026-09-07
   wieder das Speichern uebernahm und die Beschriftung das sagen musste. */
.record-lock-swap > * { white-space: nowrap; }
/* Die Beschriftung am Schalter beginnt links: mittig gesetzt wanderte ihr
   Anfang beim Wechsel, und genau das Wandern soll weg. Beim Abzeichen ist
   mittig richtig — die Pille hat eine feste Breite, innen darf der Text
   stehen, wo er am besten sitzt. */
.record-lock-swap--start { justify-items: start; }
/* GLEICHE ABSTAENDE ringsum: Abzeichen — Schalter — Beschriftung. Der
   Schalter brachte von `.switch` einen engeren eigenen Abstand mit
   (space-2 gegen space-3 der Gruppe), wodurch die Beschriftung naeher am
   Schalter klebte als der Schalter am Abzeichen. */
.record-lock-control .switch { gap: var(--ms-space-3); }
/* „Gespeichert 07.09.2026, 14:32" — der Zeitpunkt des letzten Speicherns
   (App\Record\LastSaved, Muster aus myOFFICE).

   ER STEHT NEBEN DEM NAMEN und nicht beim Sperrschalter (Betreiber,
   2026-09-07): dort sass er zwischen Statusschalter und Sperrschalter und las
   sich wie ein weiteres Bedienelement. Er ist ein HINWEIS, also die kleinste
   Schriftgroesse des Systems — dieselbe wie „Status" (`.record-status-label`)
   — und gedaempft.

   Der Abstand ist bewusst groesser als die Luecke der Kopfzeile: direkt neben
   dem Abzeichen gelesen wirkte er wie ein zweites Etikett am Namen. `nowrap`,
   damit das Datum die Zeile nicht bricht. */
.record-lock-saved {
  margin-left: var(--ms-space-4);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-regular);
  color: var(--ms-text-secondary);
  white-space: nowrap;
}
/* Leer traegt er keinen Abstand: sonst stuende in der Kopfzeile eines noch
   nie gespeicherten Datensatzes eine Luecke ohne Inhalt. */
.record-lock-saved:empty { margin-left: 0; }

/* ── EIN WEG ZUM SPEICHERN (2026-09-07) ────────────────────────────────────
   In einer sperrbaren Karte SPEICHERT DER SCHALTER („Speichern und sperren",
   record-lock_controller.js). Der Knopf in der Fusszeile war danach der zweite
   Weg zur selben Handlung — zwei Knoepfe fuer eine Sache, und der Betreiber
   hat genau diese Art Dopplung an mehreren Stellen gemeldet. Er bleibt IM
   MARKUP stehen, weil der Schalter ihn druecken laesst: damit gelten
   unveraendert Pflichtfeldpruefung, LiveComponent-Aktion und CSRF. Nur sehen
   soll man ihn nicht mehr.

   „Abbrechen" daneben bleibt sichtbar — es ist die einzige Ruecknahme. */
.record-lock [data-record-lock-action] [data-validate-submit] { display: none; }

/* Der Ersatz fuer den gesperrten Knopf: der Grund, warum gerade nichts
   gespeichert werden kann. Ausserhalb einer Sperre steht der Knopf sichtbar da
   und erklaert sich selbst — dort bleibt der Satz weg. */
.save-blocked { display: none; font-size: var(--ms-text-small); color: var(--ms-error-text); }
.record-lock .form-actions--gated:has([data-validate-submit]:disabled) .save-blocked { display: inline; }

/* ── Bestandswarnungen: Empfaengerlisten (2026-09-04) ──────────────────────
   Zwei Stufen in einem Sammelabschnitt (templates/v2/admin/_stock_alert_recipients.html.twig).
   Die Stufen sind durch Abstand und Linie getrennt und nicht durch zwei
   Abschnitte: es ist EINE Einstellung mit zwei Adressaten, und zwei
   Klappabschnitte haetten die Frage „wer bekommt das" auf zwei Klicks
   verteilt. */
.alert-stage + .alert-stage {
  margin-top: var(--ms-space-6);
  padding-top: var(--ms-space-6);
  border-top: 1px solid var(--ms-border-light);
}
.alert-stage__title {
  display: flex;
  align-items: center;
  gap: var(--ms-space-2);
  margin: 0 0 var(--ms-space-1);
  font-size: var(--ms-text-body);
  font-weight: var(--ms-weight-semibold);
}

/* Eine Empfaengerzeile: Art, Empfaenger, Entfernen.
   KEINE TABELLE UND KEINE ABZEICHEN (Betreiber, 2026-09-07). Das Abzeichen an
   der Art las sich wie ein Zustand, war aber nur eine Einordnung; und zwei
   Angaben mit einem Knopf sind eine Zeile, kein Raster mit Spaltenkoepfen. Die
   Art steht deshalb als gedaempfter Text mit Symbol davor.

   EIN RASTER UEBER ALLE ZEILEN, OHNE FESTE BREITE (2026-10-08, Weisung des
   Betreibers: nur Werte aus dem Tokensystem). Vorher hielt `min-width: 12rem`
   an der Art die Empfaenger auf einer Linie. Jetzt teilen sich die Zeilen die
   Spalten der Liste (`subgrid`): die Spalte der Art ist so breit wie ihre
   laengste Beschriftung, der Empfaenger nimmt den Rest, der Papierkorb seine
   eigene Breite. Jede Zeile bleibt ein eigenes Element — Linie und Abstand
   dazwischen bleiben erhalten. */
.alert-rows {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr) auto;
  column-gap: var(--ms-space-3);
  list-style: none;
  margin: 0;
  padding: 0;
}
.alert-row {
  display: grid;
  grid-column: 1 / -1;
  grid-template-columns: subgrid;
  /* AN DER ERSTEN ZEILE AUSGERICHTET (2026-10-07): eine Gruppenzeile ist mit
     ihren Personen mehrzeilig; mittig stand die Art („Nutzergruppe") neben
     der dritten Person statt neben dem Gruppennamen. `baseline` und nicht
     `flex-start`: Art und Name haben verschiedene Schriftgroessen und
     stuenden sonst oben buendig, aber nicht auf einer Linie. */
  align-items: baseline;
  padding: var(--ms-space-2) 0;
}
.alert-row + .alert-row { border-top: 1px solid var(--ms-border-light); }
.alert-row__kind {
  display: inline-flex;
  align-items: center;
  gap: var(--ms-space-2);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}
/* `min-width: 0` ist Pflicht: eine Rasterzelle schrumpft sonst nicht unter ihre
   Inhaltsbreite, und eine lange Adresse schoebe den Entfernen-Knopf hinaus. */
.alert-row__label { min-width: 0; overflow-wrap: anywhere; }
/* Wer eine Gruppen- oder Rollenzeile gerade trifft (2026-10-07): unter der
   Beschriftung, gedaempft und kleiner. EINE PERSON JE ZEILE, Name und
   Adresse in zwei Spalten (Betreiber: „tabellarisch, untereinander") — die
   Namen stehen buendig, die Adressen auch. `max-content` fuer die
   Namensspalte, damit sie nur so breit ist wie der laengste Name. */
.alert-row__note,
.alert-members {
  margin-top: var(--ms-space-1);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}
.alert-row__note { display: block; }
.alert-members {
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  column-gap: var(--ms-space-4);
  row-gap: var(--ms-space-05);
}
.alert-members--gruppen { grid-template-columns: max-content minmax(0, 1fr) max-content; }
.alert-members__name { color: var(--ms-text-primary); }
.alert-members__email { min-width: 0; overflow-wrap: anywhere; }
.alert-row__note--lead { margin-top: 0; margin-bottom: var(--ms-space-2); color: inherit; }

/* Hinzufuegen: ZWEI ZEILEN UNTEREINANDER, jede mit eigenem Knopf
   (Betreiber, 2026-09-07). Vorher standen Auswahl und Adressfeld nebeneinander
   an einem Knopf — an dem nicht zu sehen war, welche der beiden Eingaben er
   nimmt. Feld und Knopf bilden je Zeile ein Paar; auf schmalen Schirmen
   rutscht der Knopf unter das Feld. */
.alert-add { margin-top: var(--ms-space-4); }
/* `--ms-space-4` und nicht `-3` (2026-09-10): innerhalb einer Zeile stehen
   Beschriftung und Feld 8px auseinander, zwischen den beiden Zeilen waren es
   12px — fast dasselbe Mass, und damit war nicht zu sehen, was zusammengehoert.
   Jetzt ist die Staffelung 8 / 16 / 32: im Paar, zwischen den Paaren, zwischen
   den Stufen. Alle drei Werte aus dem Abstandssystem. */
.alert-add__row + .alert-add__row { margin-top: var(--ms-space-4); }
.alert-add__control {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ms-space-2);
  /* LESEBREITE, WO GETIPPT WIRD. Der Abschnitt steht in voller Fensterbreite,
     und das Auswahlfeld war damit 1386px breit (gemessen auf 1600px) — fuer
     einen Namen oder eine Adresse. Dieselbe Grenze wie fuer Formularkarten
     (`.card:has(.form-group)`, surfaces.css), nur hier von Hand: ein
     Sammelabschnitt ist keine Karte. `--ms-content-medium` steht in `:root`
     und loest auch ausserhalb einer Karte auf — anders als
     `--ms-card-padding`, das in `.card` deklariert ist und still zu nichts
     wird (die Falle, an der `.panel-form` einmal gescheitert ist). */
  max-width: var(--ms-content-medium);
}
/* KEINE FESTE BREITE (2026-10-08): das Feld nimmt den Rest der Zeile neben
   seinem Knopf; passt beides nicht mehr nebeneinander, rutscht der Knopf
   darunter. Vorher standen hier `18rem`/`12rem`. */
.alert-add__control .form-select,
.alert-add__control .form-input { flex: 1 1 auto; width: auto; min-width: 0; }
.alert-add__control .btn-outline { flex: 0 0 auto; }

/* Die Auswahl „Liste fuer" ueber den Listen der Einstellungsseite
   (2026-10-07): sie sagt, WESSEN Liste darunter steht, und ist deshalb durch
   eine Linie von ihr getrennt — dieselbe Staffelung wie zwischen den Stufen. */
/* Einstellungsseite (2026-10-07): Firma, Interne und Externe Warnmeldung
   als eigene Karten untereinander, darunter die Speicherzeile. Der Abstand
   steht am Behaelter (Raster mit `gap`), nicht als Nachbarabstand — dieselbe
   Bauart wie `.record-stack`. Das Formular der Listen umschliesst die
   Stufenkarten und die Speicherzeile und traegt deshalb dasselbe Raster. */
.alert-cards,
.alert-cards form[action$="/entwurf"] { display: grid; gap: var(--ms-space-4); }
/* EINE BREITE FUER ALLE KARTEN (2026-10-07): die Lesebreite griff nur an
   Karten mit Eingabezeilen (`.card:has(.alert-add)`, surfaces.css) — die
   Karte „Firma" lief ueber die ganze Spalte, die Stufenkarten endeten bei
   800px. Die Grenze steht jetzt am Behaelter, die Karten fuellen ihn. */
.alert-cards { max-width: var(--ms-content-medium); }
.alert-scope .form-label { display: block; margin-bottom: var(--ms-space-2); }
.alert-scope .form-help { margin: var(--ms-space-2) 0 0; }
.alert-save--plain { margin-top: 0; padding-top: 0; border-top: 0; }

/* Spalte „Gemeldet" der Bestandstabelle auf der Startseite (2026-10-07):
   je Stufe ein kurzer Eintrag, nebeneinander, ohne Umbruch innerhalb eines
   Eintrags. Gesendet in normaler Schrift, vorgemerkt und offen gedaempft —
   die Farbe traegt die Aussage „hier ist nichts hinausgegangen". */
.alert-marks { display: inline-flex; flex-wrap: wrap; gap: var(--ms-space-1) var(--ms-space-3); }
.alert-mark {
  display: inline-flex;
  align-items: center;
  gap: var(--ms-space-1);
  white-space: nowrap;
  font-size: var(--ms-text-small);
}
.alert-mark--none,
.alert-mark--reserved { color: var(--ms-text-secondary); }

/* Die Speicherzeile — der einzige Schreibvorgang des Dialogs. Sie sitzt am
   Ende des Formulars und traegt den Zustand, den der Server mitgibt:
   „nicht gespeicherte Aenderungen" oder „alles gespeichert". */
.alert-save {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--ms-space-3);
  margin-top: var(--ms-space-6);
  padding-top: var(--ms-space-4);
  border-top: 1px solid var(--ms-border-light);
}
.alert-save__note {
  display: inline-flex;
  align-items: center;
  gap: var(--ms-space-2);
  margin: 0;
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}

/* Titelzeile eines Abschnitts MIT Beiwerk rechts — in der Artikelmaske die
   Zahl der Empfaenger neben „Warnmeldungen" (Betreiber, 2026-09-07): sie soll
   ohne Klick und ohne Blick in die Karte zu sehen sein. `.section-title`
   bringt seinen Abstand nach unten selbst mit; hier traegt ihn die Zeile. */
.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ms-space-3);
  margin-bottom: var(--ms-space-4);
}
.section-head .section-title,
.section-head .ms-section-title { margin-bottom: 0; }
.section-head__aside {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ms-space-2);
}
/* Titel links, Beiwerk GANZ rechts — fuer eine Zeile, in der das Beiwerk eine
   Handlung ist und kein Zusatz zum Titel (das Zahnrad ueber dem Kennzahlenband
   der Kundenstartseite). Ohne den Modifikator klebt es am Titel, und wer eine
   Handlung sucht, sucht sie am Rand. */
.section-head--split { justify-content: space-between; }

/* Die Abzeichen in der Artikelmaske („intern: 3 · Kunde: 2"). */
.alert-recipients {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ms-space-2);
  margin: 0 0 var(--ms-space-1);
}

/* Die Probemeldungen stehen als zwei eigene Formulare nebeneinander (ein
   <form> darf nicht in einem <form> stehen) — .form-actions ordnet sonst
   Knoepfe, hier zwei Formulare. */

/* Ein Satz zwischen Abschnittskopf und Inhalt — der Geltungsbereich der
   Empfaengerlisten (2026-09-07). Er steht AUSSERHALB von `.panel-body`, damit
   das nachgeladene Teilstueck ihn nicht mit sich tauscht; den Innenabstand
   der Karte bringt er deshalb selbst mit. */
/* `--ms-space-5` und nicht `var(--ms-card-padding)`: das Token ist in `.card`
   deklariert und ausserhalb leer — die Zeile stand dadurch ohne jeden
   Seitenabstand an der Kante des Abschnitts (gefunden am 2026-09-08 beim
   Polstern der Formulare, gemessen: 0px). Der Wert ist derselbe, den das
   Token gemeint hat. */
/* Unten dasselbe Mass wie oben (2026-09-10): ohne es sass der Satz 16px unter
   der Kopfzeile und klebte an der Trennlinie des Rumpfes darunter. Jetzt ist
   der Streifen symmetrisch, und die Linie hat auf beiden Seiten Luft. */
.panel-note { margin: 0; padding: var(--ms-space-4) var(--ms-space-5); }

/* ── Rechte-Editor der Benutzergruppen ──
   Eine Zeile je Bereich: links die Beschriftung (mit Erklärsatz darunter),
   rechts die Stufe. Ein Raster und keine Tabelle, weil die Zeile zwei
   ungleiche Hälften hat — eine, die umbrechen darf, und eine, die ihre Breite
   behält.

   Warum kein Segment wie `.record-status-seg`: das Bauteil ist ein SCHALTER,
   der beim Klick einen Endpunkt ruft (record-status_controller.js). Hier
   stehen sechzehn Werte in EINEM Formular, das erst beim Speichern schreibt —
   das ist ein Auswahlfeld. Ein zweites Segment-Bauteil nur für diese Maske
   wäre ein Bauteil für einen Ort.

   Abwechselnd getönte Zeilen: bei sechzehn Zeilen mit gleich langen
   Auswahlfeldern verliert das Auge sonst die Zuordnung zwischen Beschriftung
   links und Feld rechts. */
/* RAHMEN UND TRENNLINIEN IM TABELLENTON (Betreiber, 2026-09-11, in drei
   Schritten eingestellt: „viel zu hell" — „immer noch zu hell" — „zu viel,
   eine Stufe zurueck").

   Das System hat auf heller Flaeche drei Linientoene:

     --ms-border-light         #E5E7EB                        1,24:1  Kante einer FLAECHE
     --ms-border-divider       rgba(134,134,139,0.40) ~#CFCFD1  1,56:1  Tabellenlinie, Feldkante
     --ms-border-control-hover rgba(134,134,139,0.70) ~#A8A8AC  2,22:1  die staerkere Kante

   Gewaehlt ist die MITTLERE: derselbe Ton, den jede Tabelle des Hauses zwischen
   ihren Zeilen traegt (`.table td`) und den die Auswahlfelder IN dieser Liste
   als Kante haben (`--ms-border-control` ist ein Alias darauf) — Liste und
   Felder darin sind damit aus einer Linie gezeichnet. Der Flaechenton der
   ersten Zeile war hier von Anfang an falsch angewandt.

   DIE DRITTE STUFE WAR ZU VIEL, ausprobiert und verworfen: zusammen mit der
   Randstaerke der Bedienelemente am Rahmen (1,5px) trat das Raster vor die
   Daten. Es gibt im System keine Stufe zwischen 0,40 und 0,70; wer eine
   braucht, legt sie als Token an und nicht als Zahl an dieser Regel.

   RAHMEN UND TRENNER TRAGEN DENSELBEN TON UND DIESELBE STAERKE — 1px,
   `--ms-border-divider`, beide. Das ist keine Bequemlichkeit, sondern die
   Regel aus designsystem.md Abschnitt 6: bei einem HALBDURCHSICHTIGEN Grau
   liest sich eine andere Randstaerke nicht als andere Staerke, sondern als
   andere FARBE. Genau so ist es gemeldet worden („die Trennlinien und die
   Umrandung sollen die gleiche Farbe haben"), als der Rahmen kurzzeitig 1,5px
   trug und die Trenner 1px — bei gleichem Token. Wer hier die Staerke des
   Rahmens anhebt, aendert sichtbar seine Farbe. */
.perm-list {
  border: 1px solid var(--ms-border-divider);
  border-radius: var(--ms-radius-md);
  overflow: hidden;
  margin-bottom: var(--ms-space-4);
}
/* JEDES STUFENFELD IST GLEICH BREIT, unabhaengig von seinem Inhalt (Weisung
   des Betreibers, 2026-09-11). Vorher stand in der zweiten Spalte `auto` und
   am Feld `.form-select--auto` — das misst den GEWAEHLTEN Eintrag, und „Kein
   Zugriff" ist laenger als „Bearbeiten". Sechzehn Zeilen hatten dadurch
   sechzehn verschiedene rechte Kanten, und ein Wechsel der Stufe verschob die
   Kante der Zeile. Genau dieselbe Falle und dieselbe Loesung wie beim
   Einheitenfeld der Artikelmaske (`.stock-row-unit .form-select`, forms.css).

   DIE BREITE STEHT AN DER SPALTE, nicht am Feld: so ist sie auch dann
   dieselbe, wenn die Zeile spaeter etwas anderes in die zweite Spalte stellt
   (ein Abzeichen an einer gesperrten Zeile zum Beispiel).

   Das Mass steht als `--ms-perm-level-w` im Tokensystem, nicht als Zahl hier —
   die Herleitung der 160px ebenfalls dort (tokens.css). */
.perm-row {
  display: grid;
  grid-template-columns: minmax(0, 1fr) var(--ms-perm-level-w);
  align-items: center;
  gap: var(--ms-space-4);
  padding: var(--ms-space-2) var(--ms-space-3);
}
/* Das Feld fuellt seine Spalte — UND SEIN ERSATZ. `assets/v2-select.js` baut
   jedem `select.form-select` einen Knopf daneben und versteckt das Feld
   selbst; der Knopf uebernimmt dabei die KLASSEN des Feldes, seine Breite
   haengt also an `.form-select { width: 100% }` in forms.css. Diese Zeile
   nennt beide ausdruecklich, damit die gleiche Breite nicht davon abhaengt,
   dass die Klassen mitkopiert werden. */
.perm-row select,
.perm-row .select-control { width: 100%; }
/* EINE DUENNE TRENNLINIE GENUEGT (Weisung des Betreibers, 2026-09-11).
   Vorher trug jede zweite Zeile zusaetzlich `--ms-bg-secondary` — bei
   Rubriken von einer bis vier Zeilen ordnete das Zebra nichts, es gab der
   ersten Karte einen grauen Streifen und der naechsten nicht. Seit die
   Rubriken eigene Karten tragen, ist die Gruppierung ohnehin die Karte. */
.perm-row + .perm-row { border-top: 1px solid var(--ms-border-divider); }
.perm-row-label { min-width: 0; }
/* Ein gesperrtes Feld sagt „gibt es, aber nicht für dich". Der Erklärsatz
   daneben sagt warum — deshalb bleibt die Zeile sichtbar statt zu fehlen. */
.perm-row select:disabled { opacity: .55; }

/* Mitglieder und Fähigkeiten: eine Spalte aus Ankreuzfeldern mit Zweitzeile.
   Kein eigenes Raster — `.form-checkbox--wide` bringt die Zweitzeile schon
   mit, hier steht nur der Abstand zwischen den Zeilen.

   DER ABSTAND IST EINE STUFE GEWACHSEN (4 → 12px, 2026-09-14). Mit 4px lagen
   die Einträge dichter beieinander als die Zeilen INNERHALB eines Eintrags:
   ein Erklärsatz, der umbricht, verschmolz mit dem nächsten Eintrag zu einem
   Textblock, und wo ein Eintrag endet, war nicht mehr zu sehen. Jetzt ist der
   Abstand zwischen zwei Einträgen größer als der zwischen zwei Zeilen eines
   Eintrags — erst damit liest sich die Spalte als Liste. */
.perm-members {
  display: flex;
  flex-direction: column;
  gap: var(--ms-space-3);
  margin-bottom: var(--ms-space-4);
}

/* Die Auskunft „Was darf dieses Konto?" in der Nutzermaske: Abzeichen in
   Reihen, je Stufe eine. Gerechnet und nicht bearbeitbar — deshalb Abzeichen
   und keine Felder. */
.effect-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ms-space-1);
  margin-bottom: var(--ms-space-3);
}

/* Spalten der Benutzergruppen-Liste. Anteile und keine Pixel, aus demselben
   Grund wie bei den Firmen: sie wachsen mit dem Fenster mit. Der Name bekommt
   den groessten Anteil — unter ihm steht die Beschreibung, und die ist der
   Satz, an dem man die Gruppe wiedererkennt. */
.table--fixed th.col-groupname { width: 26%; }
.table--fixed th.col-areas { width: 30%; }
