/* ══════════════════════════════════════════════════
   v2 PAGES — seitenspezifische Bauteile, ein Abschnitt pro Prototyp-Seite.
   Alles hier ist Zeile-für-Zeile auf 00_Daten/Redesign_Prototyp/index.html
   rückführbar; die Zeilenbereiche stehen an jedem Abschnitt.
   ══════════════════════════════════════════════════ */

/* ── Dashboard (Prototyp Zeile 1703-1749) ──
   Padding-Varianten der Sections sind im Prototyp Inline-Style; hier als
   eigene Modifier-Klassen, damit .section-white/.section-gray selbst generisch
   bleiben (siehe base.css). */
/* `.section-white--hero` (80px oben) und `.section-gray--tiles` (80px unten)
   sind am 2026-09-16 entfallen (Weisung des Betreibers). Sie stammten aus den
   Inline-Stilen des Prototyps und gaben jedem Band eine andere Kante: der Hero
   oben 80 / unten 96, das Kachelband oben 96 / unten 80. Nebeneinander sah man
   das als ungleiche Luft um die Kacheln. Jetzt traegt jedes Band oben wie
   unten `--ms-section-padding` — ein Wert, ein System, keine Ausnahme. */
.dashboard-greeting { font-size: var(--ms-text-body); color: var(--ms-text-secondary); margin-bottom: var(--ms-space-2); }
/* `margin-bottom: 0` ist der Punkt: als `<p>` bringt der Absatz 16px
   Grundabstand mit, und die addierten sich auf die 96px des Bandes — gemessen
   112px unter der Subline gegen 96px ueber den Kacheln im Band darunter
   (2026-09-16). Der Abstand zur Bandkante gehoert dem BAND
   (`--ms-section-padding`) und keinem Absatz darin. */
.dashboard-intro { margin-top: var(--ms-space-3); margin-bottom: 0; max-width: var(--ms-content-narrow); }

.tiles-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--ms-space-5); }

/* ---- Kennzahlen-Band der Startseite (Entwurf .stats-grid/.stat-*) ----
   Mitwachsend wie das Kennzahlen-Raster der Auswertung: vier Kacheln auf
   breiten Schirmen, zwei auf schmalen, ohne eigene Medienabfrage. */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: var(--ms-space-4);
}
/* WIE JEDE ANDERE KARTE: Schatten statt Kante (2026-09-16).

   Hier stand `border: 1px solid var(--ms-border-light)`. Das Token ist laut
   seiner eigenen Beschreibung in tokens.css die KANTE EINER FLAECHE und darf
   zart sein — „weil Schatten und Fuellung die Flaeche ohnehin absetzen"
   (1,24:1 auf Weiss). Genau diese Hilfe fehlte der Kennzahlenkarte: sie hatte
   keinen Schatten, und auf `.section-white` ist ihre Fuellung dieselbe Farbe
   wie der Grund. Weiss auf Weiss mit 1,24:1 Rand — der Betreiber sah die
   Karten kaum.

   Jetzt `--ms-card-shadow`, derselbe wie an `.card`; damit sitzt die Kachel
   auf demselben Bauteilvokabular wie die Kacheln darueber. Die 1px-Kante
   entfaellt: der Schatten bringt seinen eigenen Haarstrich mit
   (`0 0 0 1px`). Oben bleibt Platz fuer die farbige Kante — deshalb
   `border: none` und nicht die Kurzschreibweise weglassen. */
.stat-card {
  padding: var(--ms-space-5);
  border-radius: var(--ms-radius-md);
  border: none;
  box-shadow: var(--ms-card-shadow);
  background: var(--ms-card-bg);
}
/* ── Wenige, GROSSE Kacheln: hoechstens vier je Reihe, Restzeile fuellt aus ──
   (Weisung des Betreibers, 2026-09-16)

   `.stats-grid` waechst mit: bei 180px Mindestbreite passen auf einen breiten
   Schirm fuenf bis sechs Kacheln in EINE Reihe, und sie werden dabei so
   schmal, dass die Beschriftung dreizeilig umbricht. Fuer ein Band aus drei
   oder vier Zahlen ist das richtig; fuer die waehlbare Kundenstartseite, die
   sechs und spaeter mehr fuehrt, nicht.

   FLEX UND NICHT GRID, und das ist der ganze Punkt: bei sechs Kacheln sollen
   die zwei aus der zweiten Reihe die Breite UNTER SICH AUFTEILEN (je die
   Haelfte) statt als zwei Viertel mit einer leeren Haelfte daneben zu stehen.
   Ein Raster teilt seine Spalten ueber ALLE Reihen — eine Restzeile bleibt
   dort so breit wie die Spalten darueber. Flex verteilt je Zeile, und
   `flex-grow` laesst die letzte genau das tun, was der Betreiber wollte.
   Grid koennte es nur mit ausgezaehlten `grid-column: span n` je Restfall —
   und die haengen an einer festen Spaltenzahl, womit das Mitwachsen auf
   schmalen Schirmen verloren waere.

   VIER JE REIHE, ohne Medienabfrage: die Grundbreite ist ein Viertel der
   Flaeche abzueglich der Luecken, die Mindestbreite haelt dagegen. Wird es
   enger, passen drei, dann zwei, dann eine — und die jeweils letzte Zeile
   fuellt immer aus. `min()` bei der Mindestbreite, damit auf einem sehr
   schmalen Schirm keine Kachel breiter wird als ihre Flaeche (sonst
   waagerechter Bildlauf). */
.stats-grid--breit {
  /* 15rem (240px) und nicht mehr — gerechnet gegen den SCHMALSTEN Fall, in dem
     die Viererreihe noch stehen muss: unter 1400px Fensterbreite ist die
     Arbeitsflaeche auf `--ms-content-width` (1140px) begrenzt, abzueglich der
     Polsterung bleiben 1092px, und vier Kacheln mit drei Luecken kommen darin
     auf 261px je Stueck. Eine hoehere Mindestbreite haette die vierte Kachel
     dort in die naechste Zeile geschoben, und die Viererreihe waere auf einem
     gewoehnlichen Notebook nie zu sehen gewesen. Gemessen.

     Ab 1400px traegt die Seite 1320px (tokens.css), und dieselben vier Kacheln
     stehen auf je 306px. Die Zahl hier bleibt trotzdem 240: sie ist die
     Untergrenze, nicht die Zielbreite — die feste Spaltenzahl teilt den Platz,
     der da ist. */
  --stats-kachel-min: 15rem;
  --stats-spalten: 4;
  display: flex;
  flex-wrap: wrap;
}
.stats-grid--breit > * {
  flex: 1 1 calc(
    (100% - (var(--stats-spalten) - 1) * var(--ms-space-4)) / var(--stats-spalten)
  );
  min-width: min(var(--stats-kachel-min), 100%);
}

.stat-label {
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
  margin-bottom: var(--ms-space-2);
}
/* EINE STUFE KLEINER ALS FRUEHER (h3 statt h2, 2026-09-16). Die Zahl stand
   auf 48px, das Datum daneben auf 24px — die Reihe las sich als zwei
   Schriftgrade statt als ein Band (Befund des Betreibers). Beide treffen sich
   jetzt auf h3; die Zahl gibt etwas ab, das Datum bekommt etwas dazu.

   h3 bleibt Grossschrift nach WCAG 1.4.3 (fett, 24-32px im fluiden Profil),
   die Volltonfarben der --ms-figure-Familie gelten also unveraendert. */
.stat-value {
  font-size: var(--ms-text-h3);
  font-weight: var(--ms-weight-bold);
  letter-spacing: -0.02em;
  line-height: 1.15;
  /* Gleiche Zifferbreiten, damit die Zahlen der vier Kacheln auf einer Linie
     stehen und nicht je nach Ziffer wandern. */
  font-variant-numeric: tabular-nums;
}
/* Ein DATUM ist keine Zahl und braucht eine eigene Groesse.
   „06.04.2026" sind zehn Zeichen; bei --ms-text-h2 (bis 48px) ist das rund
   260px breit und lief gemessen aus der Karte heraus. Der Entwurf zeigt dort
   „06. Apr." — kurz genug, aber ohne Jahr, und eine Lieferung im Januar waere
   dann nicht mehr eindeutig. Statt Information wegzulassen wird die Schrift
   kleiner: --ms-text-h4 (20 bis 24px) traegt das vollstaendige Datum. */
/* Dieselbe Stufe wie die Zahl. Der Modifikator bleibt: er hatte seinen Grund
   darin, dass ein langer Text bei h2 aus der Kachel lief — bei h3 laeuft er
   nicht mehr, und was bleibt, ist die etwas ruhigere Sperrung. Er markiert
   weiterhin „das ist keine Zahl". */
.stat-value--datum {
  font-size: var(--ms-text-h3);
  letter-spacing: -0.01em;
}
/* „Kein Termin" ist keine Zahl und soll nicht wie eine aussehen: kleiner und
   gedaempft, damit die echten Zahlen die Reihe fuehren. */
.stat-value--leer {
  font-size: var(--ms-text-body);
  font-weight: var(--ms-weight-medium);
  color: var(--ms-text-secondary);
  letter-spacing: 0;
}
/* Die Unterzeile einer Kennzahl: WORAUS sie besteht (2026-09-16).
   Gebraucht von der Bestandskachel — „7" allein beantwortet nicht, ob nichts
   mehr da ist oder es nur knapp wird, und beides zugleich kommt vor (ein
   Artikel, der in einer Bestellung vollstaendig abgerufen wird, faellt in
   derselben Sekunde unter die Schwelle und auf null).

   KLEIN UND GEDAEMPFT, damit die Zahl die Reihe fuehrt: dieselbe Stufe wie
   `.stat-label` darueber. Der Abstand nach oben ist die kleinste Stufe des
   Systems — die Unterzeile gehoert zur Zahl und nicht neben sie. */
.stat-note {
  margin: var(--ms-space-2) 0 0;
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
}

/* ---- Kennzahlen-Band der Verwaltung ----
   Ergaenzt .stats-grid/.stat-card um genau eine Aussage: die
   DRINGLICHKEITSKANTE oben. Bedienkonzept nach myOFFICE, wo die Kachel des
   Dashboards ihre Farbe als Oberkante traegt und nicht als Flaeche — eine
   flaechig rote Kachel schreit, eine Kante zeigt.

   MUSS NACH DEM BLOCK DARUEBER STEHEN. `.stat-card` dort setzt `border` als
   Kurzschreibweise (seit dem 2026-09-16 `border: none`); sie ueberschreibt
   jede vorher notierte `border-top`. Reihenfolge ist hier die Zusicherung,
   nicht Spezifitaet — beide Selektoren sind eine Klasse.

   DIE ZAHL nimmt die --ms-figure-*-Toene und nicht die Volltonfarben direkt:
   Begruendung und die gemessenen Werte stehen an der Token-Familie in
   tokens.css, geprueft in bin/design-check.

   ABSTAND NACH UNTEN aus dem System (--ms-space-5, 24px): das Band ist ein
   eigener Abschnitt und nicht die erste Karte. Diese Regel war zwischen dem
   2026-09-02 und heute WIRKUNGSLOS — beim Verschieben des Blocks blieb der
   Rumpf eines Kommentars ohne sein `/*` stehen, der Parser las die Zeilen als
   Selektor und verschluckte die Regel dahinter mit. Sichtbar war es nur als
   fehlender Abstand, nicht als Fehler. */
.stats-grid--band { margin-bottom: var(--ms-space-5); }
/* EIN BAND LIEST SICH NUR ALS BAND, WENN SEINE ZAHLEN AUF EINER LINIE STEHEN.
   Sobald eine Beschriftung umbricht, rutscht ihre Zahl um eine Zeilenhoehe
   nach unten und die Reihe wirkt schief. Gemessen am 17. September 2026,
   nachdem die Auswertungskacheln ihren Zeitraum in die Beschriftung bekamen:
   drei Zahlen bei 290px, „davon abgerechnet (laufendes Jahr)" bei 313px.
   Seit dort der Zeitraum SELBST steht, tritt der Fall bei jeder Spanne auf —
   „davon abgerechnet (01.10.2025 – 17.09.2026)".

   AUSDRUECKLICH UND NICHT FUER JEDES BAND (`zweizeilig: true` am Bauteil).
   Die Verwaltungs-Startseite hat einwortige Beschriftungen; dort waeren zwei
   reservierte Zeilen einfach 22px Luft unter jedem Wort — im dichten
   Massprofil das Gegenteil dessen, was es soll. Wer weiss, dass seine
   Beschriftungen umbrechen koennen, sagt es; der Rest bleibt eng.

   `lh` ist die Zeilenhoehe DIESES Elements, also kein handgesetzter Wert und
   keine Annahme ueber die Schriftgroesse. Ein Browser, der die Einheit nicht
   kennt, wirft die Zeile weg und verhaelt sich wie bisher. */
.stats-grid--zweizeilig .stat-label { min-height: 2lh; }
.stat-card {
  /* Die Kante ist immer da, auch neutral — sonst springen die Kacheln in der
     Hoehe, sobald eine Zahl von 0 auf 1 geht. */
  border-top: 3px solid var(--ms-border-light);
}
.stat-card--danger  { border-top-color: var(--ms-error); }
.stat-card--warning { border-top-color: var(--ms-warning); }
.stat-card--success { border-top-color: var(--ms-success); }
.stat-card--info    { border-top-color: var(--ms-accent); }
.stat-card--neutral { border-top-color: var(--ms-text-tertiary); }

/* ── ZWEI MARKENTOENE, DIE NICHTS BEHAUPTEN (2026-09-16) ──
   Die fuenf Stufen darueber bedeuten DRINGLICHKEIT. Ein Umsatz ist keine
   Warnung und eine Bestellmenge kein Erfolg — deshalb standen die vier
   Kacheln der Auswertungen bis heute alle auf `neutral`, und deshalb bleibt
   diese Begruendung gueltig.

   Der Betreiber wollte sie farbig, „nicht bunt, aber mit Akzentfarben".
   Genau dafuer sind diese beiden da: die ENDEN DES HAUSVERLAUFS
   (--ms-gradient-start = --ms-accent, --ms-gradient-end = --ms-teal). Sie
   sagen „diese Kacheln gehoeren zusammen" und nicht „hier stimmt etwas
   nicht". Beide haben im Dunkelmodus eigene Werte.

   WARUM `--accent` NEBEN `--info` STEHT, obwohl beide heute dieselbe Farbe
   tragen: `--info` ist eine SIGNALSTUFE, die zufaellig das Markenblau nutzt.
   Aendert sich die Signalfamilie, soll sie das Kennzahlenband der
   Auswertungen nicht mit umfaerben. Zwei Namen, zwei Absichten.

   DIE ZAHL BLEIBT SCHWARZ. Gefaerbt wird nur die Kante — so hat der Betreiber
   es verlangt („zumindest mit dem oberen Rand"), und eine eingefaerbte Zahl
   waere wieder eine Aussage ueber ihren Wert. Wie bei `--neutral` steht die
   Regel deshalb ausdruecklich da: eine Klasse ohne Regel wirft keinen Fehler,
   sie tut nur nichts. */
.stat-card--accent { border-top-color: var(--ms-accent); }
.stat-card--brand  { border-top-color: var(--ms-teal); }

/* ── SCHRITTWEISER VERLAUF UEBER EIN GANZES BAND (2026-09-16) ──
   Die linke Kachel traegt das Blau, die rechte das Tuerkis, die dazwischen
   die Stufen — fuer JEDE Anzahl Kacheln, ohne eine Zahl je Band.

   ZWEI TEILE, und beide gab es schon:

   1. DIE STUFE kommt aus `--ms-fill`, der Anteilsleiter des Hauses
      (base.css, `.fill-0` … `.fill-100` in Fuenferschritten). Die Leiter ist
      dort ausdruecklich generisch gehalten — „die Klasse setzt nur den Wert;
      ob daraus Breite oder Hoehe wird, entscheidet das Bauteil". Hier wird
      daraus ein MISCHANTEIL. Damit braucht der Verlauf keine eigene Leiter,
      und es steht weiterhin kein Inline-Stil in einer Vorlage.

   2. DIE MISCHUNG macht `color-mix()` aus den beiden Tokens. Keine
      gerechneten Hexwerte im Blatt: wer `--ms-accent` oder `--ms-teal` dreht,
      dreht den ganzen Verlauf mit.

   `in oklab` und nicht `in srgb`: der Weg von Blau nach Tuerkis fuehrt in
   sRGB durch einen sichtbar stumpfen Bereich, in oklab bleiben die Stufen
   gleich hell und gleich gesaettigt.

   DIE ERSTE ZEILE IST DER RUECKFALL und kein Versehen: kennt ein Browser
   `color-mix()` nicht, faellt die zweite Angabe aus und alle Kacheln stehen
   im Blau — eine Farbe statt keiner. */
.stat-card--verlauf {
  border-top-color: var(--ms-accent);
  border-top-color: color-mix(in oklab, var(--ms-teal) var(--ms-fill, 0%), var(--ms-accent));
}
.stat-value--accent,
.stat-value--brand { color: var(--ms-text-primary); }

/* Die Zahl traegt die Volltonfarbe ihrer Stufe (--ms-figure-*), nicht die
   Textstufe: sie ist 24px fett und damit Grossschrift, wo 3:1 genuegt.
   Begruendung und die gemessenen Werte stehen an der Token-Familie in
   tokens.css. Nur fuer .stat-value — an kleinerem Text waeren dieselben
   Toene unter AA. */
.stat-value--danger  { color: var(--ms-figure-danger); }
.stat-value--warning { color: var(--ms-figure-warning); }
.stat-value--success { color: var(--ms-figure-success); }
.stat-value--info    { color: var(--ms-figure-info); }
/* NEUTRAL IST EINE STUFE WIE DIE ANDEREN, nur ohne Signalfarbe: der Wert
   traegt den normalen Textton. Bis zum 8. September 2026 fehlte die Regel —
   `.stat-card--neutral` gab es, `.stat-value--neutral` nicht. Aufgefallen ist
   es nie, weil die Startseite ihre neutralen Kacheln immer mit `wert: 0`
   fuehrt und dann `--leer` greift; erst die Backups-Maske setzt eine neutrale
   Kachel MIT Wert („Nicht aktiv"). Eine Klasse ohne Regel wirft keinen
   Fehler, sie tut nur nichts — deshalb steht sie hier ausdruecklich. */
.stat-value--neutral { color: var(--ms-text-primary); }
/* neutral behaelt die Textfarbe des Rumpfs — kein eigener Ton. */

/* Zweitzeile: was die Zahl genau meint („Lieferdatum vorbei"). Ohne sie
   muesste die Beschriftung den ganzen Satz tragen und umbrechen. */
.stat-foot {
  margin-top: var(--ms-space-1);
  font-size: var(--ms-text-caption);
  color: var(--ms-text-secondary);
}

/* Anklickbare Kachel: fuehrt in die Liste, die die Zahl aufschluesselt. */
.stat-card--link {
  display: block; text-decoration: none; color: inherit;
  transition: border-color var(--ms-transition-fast), box-shadow var(--ms-transition-fast);
}
.stat-card--link:hover { box-shadow: var(--ms-card-shadow-hover); }
.stat-card--link:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }

.tile {
  padding: var(--ms-space-8) var(--ms-space-6); text-align: center;
  color: var(--ms-text-primary); text-decoration: none; display: block;
}
/* ── Symbolplatte einer Startseiten-Kachel ──
   ZWEI WERTE GEAENDERT AM 2026-09-16, beide aus dem Vergleich mit den anderen
   Symbolplatten des Systems:

     Bauteil               Platte  Grund                      Glyphe
     .tile-icon (vorher)   64px    --ms-bg-secondary (grau)   24px  = 37 %
     .success-icon         64px    --ms-success-muted (Tint)  40px  = 62 %
     .confirm-card-badge   56px    --ms-success (Vollton)     28px  = 50 %

   1. DER GRUND WAR NEUTRALGRAU. Die Regel des Hauses steht am Token
      `--ms-tint`: die Flaeche hinter Abzeichen und Symbolplatten ist ein
      SCHLEIER DER EIGENEN FARBE (achtmal so umgesetzt, u. a. `.badge-info`,
      `.alert-info`, `.record-status-opt.is-on`). `.success-icon` haelt sich
      daran, diese Platte hielt sich als einzige nicht daran — das Symbol
      schwamm in einem grauen Kreis, statt in seiner eigenen Farbe zu stehen.

   2. DIE GLYPHE WAR ZU KLEIN FUER IHRE PLATTE. 24px in 64px sind 37 %; die
      Regel des Hauses steht an `.confirm-card-badge` („Das Symbol nimmt die
      halbe Kreisbreite") und wird ueber die SCHRIFTGROESSE gesteuert, weil
      `.ms-ico` in `em` misst. 2rem sind 32px und damit die Haelfte.

   DER KREIS BLEIBT: er ist kein Einzelfall, sondern das Muster (drei von drei
   Symbolplatten haben einen). Ohne ihn stuende das Symbol als einziges im
   System frei auf der Karte. */
.tile-icon {
  width: var(--ms-icon-size-xl); height: var(--ms-icon-size-xl);
  border-radius: 50%;
  background: rgba(var(--ms-icon-color-rgb), var(--ms-tint));
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto var(--ms-space-5); color: var(--ms-icon-color);
  /* Die Glyphe nimmt die halbe Plattenbreite (Designsystem 21.1): 32 von 64px. */
  font-size: var(--ms-icon-size-m);
}
.tile-title {
  font-size: var(--ms-text-h4); font-weight: var(--ms-weight-semibold);
  line-height: 1.25; margin-bottom: var(--ms-space-2);
}
.tile-desc { font-size: var(--ms-text-body); color: var(--ms-text-secondary); line-height: 1.6; }

@media (max-width: 900px) {
  .tiles-grid { grid-template-columns: 1fr; }
}

/* ── Produktübersicht (Prototyp Zeile 1462-1651) ── */
.toolbar { display: flex; flex-wrap: wrap; gap: var(--ms-space-4); align-items: flex-end; }
/* Die 320px sind die Basis fuers UMBRECHEN, nicht die Anzeigebreite: durch
   `flex-grow: 1` nimmt das Suchfeld ohnehin den ganzen restlichen Platz. Mit
   320px blieben neben der Datumsgruppe nur 15px Reserve — jede etwas breitere
   Uebersetzung oder Schriftwiedergabe schob die Gruppe damit in die zweite
   Zeile, und dort wuchs das Suchfeld auf die volle Breite. Mit 240px bleiben
   rund 95px Reserve; an der dargestellten Breite aendert sich nichts. */
.search-box { flex: 1 1 240px; position: relative; }
.search-icon {
  position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  width: 20px; height: 20px; color: var(--ms-text-tertiary); pointer-events: none;
}
/* Rand, Radius, Fuellung und Fokusring kommen aus dem gemeinsamen Regelsatz
   in forms.css — hier stehen nur Groesse und Innenabstand, die diese Bauform
   von den uebrigen unterscheiden: die Hoehe aus dem Token (damit sie dieselbe
   ist wie die der Datumsfelder und des Knopfs daneben) und links Platz fuer
   das Lupensymbol. */
.search-input {
  width: 100%;
  height: var(--ms-control-h);
  /* Links Platz fuer die Lupe: Abstand zum Rand + Symbolbreite + Luft zum
     Text. Als Rechnung und nicht als Zahl, damit der Wert der Lupe folgt,
     wenn sie sich aendert. Ergibt dieselben 44px wie das vorherige 2.75rem. */
  padding: 0 var(--ms-space-4) 0 calc(var(--ms-space-4) + 20px + var(--ms-space-2));
}

/* Das kleine Kreuz, das Chrome und Safari in ein `type="search"` setzen, ist
   hier ueberfluessig: „Zuruecksetzen" nimmt ALLE Filter zurueck, das Kreuz nur
   den Text — zwei Wege mit verschiedener Reichweite nebeneinander. */
.search-input::-webkit-search-cancel-button { appearance: none; }
.prod-dates { display: flex; gap: var(--ms-space-3); align-items: flex-end; flex-wrap: wrap; }
/* Knopf und nachgeordneter Verweis auf die Hoehe der Felder.
   Beim Knopf muss dazu der SENKRECHTE Innenabstand weichen: er addiert sich
   sonst zur Hoehe und der Knopf wird hoeher als die Felder (gemessen 46 gegen
   38px), obwohl sein Hoehen-Token dasselbe sagt. Der waagerechte Abstand
   bleibt, er gibt dem Knopf seine Breite. Muster aus myOFFICE
   (.mo-report-row .mo-btn).

   Der Verweis braucht die Hoehe, damit seine Textzeile auf der Mittellinie der
   Felder sitzt statt auf deren Unterkante — er ist sonst das einzige Element,
   das sichtbar aus der Reihe faellt. */
/* Nur noch fuer den TEXTVERWEIS: er hat keine eigene Hoehe und saesse sonst
   auf der Unterkante der Felder statt auf ihrer Mittellinie. Knoepfe brauchen
   hier nichts mehr — ihre Hoehe stimmt seit `padding-block: 0` in buttons.css
   von selbst mit --ms-control-h ueberein. */
.prod-dates .btn-text {
  height: var(--ms-control-h);
}
.prod-date-field { display: flex; flex-direction: column; gap: var(--ms-space-1); }
.prod-date-field label { font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium); color: var(--ms-text-secondary); }
/* Echte <input type="date"> im Prototyp; unsere Felder sind wegen der
   Mindestdatum-Logik <input type="text"> hinter Flatpickr (siehe
   product-overview_controller.js), das eigene Klassen ("form-control input",
   Flatpickr-Standardwert) auf sein Alt-Input setzt. Deshalb hier ueber den
   Tag, nicht ueber Typ oder Klasse — greift unabhaengig davon, was Flatpickr
   selbst vergibt, kein eigenes Alt-Input-Styling noetig. */
/* Aussehen UND Schriftgroesse aus dem gemeinsamen Regelsatz (forms.css); hier
   nur Hoehe und Innenabstand dieser Bauform. */
.prod-date-field input {
  height: var(--ms-control-h);
  /* Aus dem Abstandssystem statt 0.875rem (14px) — 2px schmaler und damit
     derselbe Wert, den .wizard-date-input rechts verwendet. */
  padding: 0 var(--ms-space-3);
}

.prod-filters { display: flex; flex-wrap: wrap; gap: var(--ms-space-2); padding-bottom: var(--ms-space-2); }
.filter-pill {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  padding: var(--ms-space-2) var(--ms-space-4);
  border-radius: var(--ms-radius-pill);
  border: 1px solid var(--ms-border-light);
  background: var(--ms-bg-primary);
  font-size: var(--ms-text-small);
  font-weight: var(--ms-weight-medium);
  color: var(--ms-text-secondary);
  cursor: pointer;
  transition: all var(--ms-transition-base);
}
.filter-pill:hover { border-color: var(--ms-accent); color: var(--ms-accent); background: rgba(var(--ms-accent-rgb),0.04); }
.filter-pill.active { background: var(--ms-accent); border-color: var(--ms-accent); color: var(--ms-on-accent); }
.filter-pill.active:hover { background: var(--ms-accent-hover); border-color: var(--ms-accent-hover); color: var(--ms-on-accent); }
/* Tint aus dem neutralen Grau statt aus Schwarz: Schwarz mit 8 % ist auf dunklem
   Grund unsichtbar, das Grau bleibt in beiden Fassungen ein Schleier. */
.pill-count { font-size: var(--ms-text-caption); background: rgba(var(--ms-text-tertiary-rgb), 0.2); border-radius: var(--ms-radius-pill); padding: var(--ms-space-05) var(--ms-space-2); font-weight: var(--ms-weight-semibold); }
.filter-pill.active .pill-count { background: rgba(255,255,255,0.25); }

.divider {
  height: 1px;
  background: linear-gradient(to right, transparent, var(--ms-border-light) 15%, var(--ms-border-light) 85%, transparent);
  margin: var(--ms-space-5) 0;
}

.prod-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
  gap: var(--ms-space-5);
}
.prod-grid--equal-height { align-items: stretch; }

/* EINE Karte in der Breite, die sie beim Kunden hat — fuer die Vorschau in der
   Artikelmaske. Die Zahl steht absichtlich HIER neben dem Raster: zwei Werte,
   die zusammengehoeren, sollen sich nicht aus zwei Dateien anschauen.

   408px UND NICHT 320px, obwohl 320 die Mindestspalte des Rasters ist: die
   Kundenseite traegt ab 1400px Fensterbreite 1320px (tokens.css), abzueglich
   der Polsterung bleiben 1272px, und darin fuellt `auto-fill` DREI Spalten —
   (1272 - 48) / 3 = 408. Das ist die Breite, in der ein Kunde die Karte auf
   einem gewoehnlichen Schirm sieht, und eine Vorschau soll den Regelfall
   zeigen, nicht den Grenzfall.

   Die Zahl stand bis zum 2026-09-17 auf 384 und war schon damals gegen eine
   Flaeche gerechnet, die es nicht gab (1200px statt der wirklichen 1092px).
   Wer die Seitenbreite wieder aendert, rechnet hier nach — die beiden Werte
   gehoeren zusammen, und darum steht die Zahl HIER neben dem Raster und nicht
   in einer zweiten Datei. Enger als 320 wird es nie, breiter nur auf sehr
   weiten Schirmen. */
.prod-preview {
  display: grid;
  grid-template-columns: minmax(0, 408px);
  justify-content: center;
  /* Etwas Luft nach oben zum Hinweis darueber: die Karte ist ein Bild in einem
     Text, kein weiterer Absatz. */
  padding-block: var(--ms-space-2);
}
.prod-card {
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  overflow: hidden;
  transition: box-shadow var(--ms-transition-slow), transform var(--ms-transition-slow);
  /* Spalte, damit die Aktionszeile am unteren Kartenrand sitzt. Das Raster
     zieht alle Karten einer Reihe auf dieselbe Hoehe; ohne diese Spalte
     endet der Karteninhalt aber dort, wo sein Text aufhoert, und die
     Knoepfe stehen von Karte zu Karte auf anderer Hoehe. */
  display: flex;
  flex-direction: column;
}
.prod-card:hover { box-shadow: var(--ms-card-shadow-hover); transform: translateY(-2px); }
.prod-card.clickable { cursor: pointer; }
/* NICHT VERFUEGBAR HEISST GEKENNZEICHNET, NICHT AUSGEGRAUT (2026-09-18).
   Dasselbe Gelb, in dem der Buchungskalender belegte Tage zeigt — eine
   Sprache fuer denselben Sachverhalt. Die Karte bleibt lesbar und klickbar:
   wer „nicht verfuegbar" liest, will als Naechstes wissen, wann wieder, und
   das steht im Detailfenster. Gesetzt wird die Klasse erst, wenn die
   Verfuegbarkeit im gewaehlten Zeitraum NULL ist (product-availability). */
/* `outline` UND NICHT `border-color` (korrigiert 2026-09-18): `.prod-card` hat
   gar keinen Rahmen, nur einen Schatten — die Regel war wirkungslos, der
   Betreiber sah nur den farbigen Satz. `outline` zeichnet ausserhalb des
   Kastens und verschiebt deshalb nichts; dieselbe Bauart wie der Fokusring der
   Knoepfe (buttons.css).

   DER TON IST --ms-warning (#FF9500), der Vollton (korrigiert 2026-09-18).

   Hier stand zuvor --ms-figure-warning (#C27100) mit der Begruendung, der
   Vollton verfehle die 3:1 aus WCAG 1.4.11. Die Rechnung stimmte, die
   Schlussfolgerung war falsch — aus zwei Gruenden.

   ERSTENS bindet 1.4.11 nur ein grafisches Element, das zum VERSTEHEN
   ERFORDERLICH ist. Seit die Beschriftung darunter ein `badge-warning` ist,
   steht die Aussage in Worten UND als getoente Flaeche auf der Karte; die
   Umrandung ist Verstaerkung und nicht mehr Traeger der Information. Faellt
   sie weg, geht nichts verloren.

   ZWEITENS war das Ergebnis das Gegenteil des Gemeinten: #C27100 auf Weiss
   liest sich als brauner Strich und nicht als Warnung (Weisung des
   Betreibers, 2026-09-18). Ein Kontrastwert, der eine Farbe ihre Bedeutung
   kostet, hat die Barriere nicht abgebaut, sondern verschoben.

   Im Dunkelmodus folgt der Vollton der zweiten Tokenfassung (#FF9F0A). */
.prod-card.is-unavailable {
  outline: 2px solid var(--ms-warning);
  outline-offset: -2px;
}
/* KEINE EIGENE SCHRIFTFARBE MEHR. Die Zeile trug --ms-warning-text (#8A4B00)
   auf dem weissen Kartengrund — also genau den Ton, der ausschliesslich dafuer
   da ist, AUF dem Warn-Tint lesbar zu sein. Ohne seine Flaeche ist dunkles
   Orange Braun, und die Signalfarbe kam auf der Karte gar nicht mehr vor.
   Die Beschriftung wird jetzt zum Abzeichen (product-availability_controller),
   und damit gilt die Paarung des Systems: Flaeche --ms-warning-muted,
   Schrift --ms-warning-text, 6,10:1. Dieselbe Paarung wie ein belegter Tag im
   Buchungskalender.

   `min-width: 0` ist die einzige eigene Regel, die es braucht: in der
   Artikelfassung steht die Beschriftung als Flex-Element neben dem
   Bestandsabzeichen, und „Im gewaehlten Zeitraum nicht verfuegbar" ist
   deutlich laenger als die Mengenangabe, die dort sonst steht. Ohne die
   Zeile schrumpft ein Flex-Element nicht unter seine Inhaltsbreite und
   schoebe das Abzeichen daneben aus der Karte. */
.prod-card-availability-label { min-width: 0; }
/* Die Flaeche hinter dem Bild ist die Kartenflaeche selbst, nicht das Grau des
   Seitenhintergrunds. Weil die Bilder `contain` sind, bleiben bei nicht-4:3
   Motiven Raender frei — mit Grau zerfiel die Karte dadurch sichtbar in zwei
   Haelften. Der Prototyp setzt hier Grau, hat aber ausnahmslos formatfuellende
   Motive und zeigt den Bruch deshalb nicht. `--ms-card-bg` und nicht einfach
   Weiss: so folgt die Flaeche der Karte, wenn deren Farbe sich je aendert. */
/* QUADRATISCH, wie jedes Bildfenster des Systems (Weisung des Betreibers,
   2026-09-09: „wir setzen ausschliesslich quadratische Bilder ein"). Die
   Bildkette legt jedes Bild quadratisch ab (ImagePipeline); ein 4:3-Fenster
   davor hiess, dass JEDES Bild oben und unten einen Streifen Leerraum bekam —
   bei `contain` sichtbar, bei `cover` als abgeschnittener Rand. */
.prod-card-img {
  position: relative;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  margin: var(--ms-space-4) var(--ms-space-4) 0;
  border-radius: var(--ms-radius-md);
  background: var(--ms-card-bg);
}
/* `contain` statt `cover`: dieselbe Entscheidung wie bei .product-thumb in der
   Artikelauswahl, und aus demselben Grund — die Fotos sind teils hochkant
   (Rollups), teils quer (Messestaende), teils sind es Schaubilder mit Text.
   `cover` schneidet davon je Artikel etwas anderes ab; bei den Paketbildern
   fehlte dadurch die untere Zeile der Inhaltsliste.

   Der Prototyp nutzt hier `cover`, weil seine Beispielbilder ausnahmslos quere
   Studiofotos sind — mit den echten Daten trifft die Annahme nicht zu. Das
   feste 4:3-Fenster darueber bleibt: es haelt die Karten gleich hoch, und die
   graue Flaeche dahinter faengt die Raender hochkanter Bilder auf. */
.prod-card-img img { width: 100%; height: 100%; object-fit: contain; transition: transform var(--ms-transition-slow); }
.prod-card:hover .prod-card-img img { transform: scale(1.05); }

/* DER BILDPLATZHALTER — ein Datensatz ohne Foto (seit 2026-10-08, Variante A
   des Entwurfs, vom Betreiber gewaehlt; Teilstueck `v2/_bild_platzhalter`).

   DIE FLAECHE EINES FOTOS: quadratisch und so breit wie das Bildfenster, also
   genau das, was ein Bild dort einnaehme. Kein eigener Grund — den gibt das
   Fenster (`--ms-card-bg`), wie beim Foto. Vorher stand hier ein 4:3-Ersatzbild,
   das im quadratischen Fenster kleiner wirkte als die Bilder daneben.

   Die Symbolgroesse geht ueber `--ms-ico` am Behaelter und nicht ueber Breite
   und Hoehe am Symbol (Regel in icons.css). Kein Zoom beim Ueberfahren: die
   Regel darueber trifft nur `img`, und ein Platzhalter, der sich wie ein Bild
   bewegt, gaebe sich als eines aus. */
.bild-platzhalter {
  --ms-ico: var(--ms-icon-size-xl);
  width: 100%;
  aspect-ratio: 1 / 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--ms-space-3);
  color: var(--ms-text-secondary);
}
/* Das SYMBOL im gedaempften Ton, wie im Entwurf; der SATZ darunter in
   `--ms-text-secondary` — Regel 8 des Designsystems laesst `-tertiary` nur
   Symbolen und Platzhaltern, nicht lesbarem Text (3,62:1 auf Weiss).

   DER STRICH IST FEINER ALS SONST (Weisung des Betreibers, 2026-10-08): die
   Staerke rechnet in der 24er-Zeichenflaeche des Symbols, 1,5 waere bei 64 px
   ein rund 4 px dicker Strich. 0,75 ergibt rund 2 px. */
.bild-platzhalter .ms-ico { color: var(--ms-text-tertiary); stroke-width: 0.75; }
/* Im Vorschaubildchen (32 px) reicht die gewoehnliche Staerke. */
.bild-platzhalter--klein .ms-ico { stroke-width: 1.5; }
.bild-platzhalter__text { font-size: var(--ms-text-small); }
/* Im Vorschaubildchen (Bestellpositionen, Zusammenfassung): nur das Symbol, in
   der Zeile ausgerichtet wie das Bild, das dort stuende. */
.bild-platzhalter--klein {
  --ms-ico: var(--ms-icon-size-m);
  display: inline-flex;
  vertical-align: middle;
}

/* DIE VORSCHAU REAGIERT NICHT AUF DIE MAUS.
   In der Uebersicht des Kunden ist die Karte ein Bedienelement — sie hebt sich
   und das Motiv zoomt leicht, damit man sieht, dass hier etwas anzuklicken ist.
   In der Vorschau der Artikelmaske ist sie ein BILD: die Knoepfe darin sind
   ohne Wirkung, und eine Karte, die auf die Maus antwortet, ohne etwas zu tun,
   verspricht etwas, das sie nicht halten kann (gemeldet am 2026-09-03).

   Beide Reaktionen werden zurueckgenommen, nicht nur das Heben — der Zoom des
   Motivs ist dieselbe Einladung mit anderen Mitteln. Der Schatten bleibt der
   ruhende (`--ms-card-shadow`), damit die Karte beim Zeigen nicht flach wird. */
.prod-preview .prod-card:hover { box-shadow: var(--ms-card-shadow); transform: none; }
.prod-preview .prod-card:hover .prod-card-img img { transform: none; }

/* Steht eine Auskunft UNTER der Vorschau, braucht sie Abstand zur Karte: die
   Meldungsbaender des Systems tragen nur einen unteren Aussenabstand, und die
   Polsterung der Vorschau allein (8px) klebte das Band an die Karte. */
.prod-preview + .alert { margin-top: var(--ms-space-3); }
.prod-card-body {
  padding: var(--ms-space-4) var(--ms-space-5) var(--ms-space-5);
  /* Fuellt die Karte aus, damit `margin-top: auto` auf der Aktionszeile
     wirken kann. */
  flex: 1;
  display: flex;
  flex-direction: column;
}
/* Kein !important noetig: v2s Basisregeln fuer p/h4 tragen selbst schon
   keins, eine Bauteilklasse gewinnt also ohnehin. */
/* Nur der Titel traegt .text-clamp (base.css) und reserviert eine Zeile. Die
   Beschreibung bleibt vollstaendig sichtbar, weil sie die Auswahl begruendet. */
.prod-card-title {
  --ms-clamp: var(--ms-clamp-card-title);
  --ms-clamp-lh: 1.3;
  font-size: var(--ms-text-lg);
  font-weight: var(--ms-weight-semibold);
  margin: 0;
  letter-spacing: -0.01em;
}
.prod-card-title-row {
  padding-bottom: var(--ms-space-3);
  border-bottom: 1px solid var(--ms-border-divider);
}
.prod-card-desc {
  font-size: var(--ms-text-small);
  line-height: 1.5;
  color: var(--ms-text-secondary);
  margin: 0;
}
.prod-card-desc-row { padding-block: var(--ms-space-3); }
.prod-card-desc-row.is-empty { padding-block: 0; }
.prod-card-availability {
  margin-top: auto;
  padding-top: var(--ms-space-3);
  border-top: 1px solid var(--ms-border-divider);
  font-size: var(--ms-text-caption);
  color: var(--ms-text-secondary);
  font-weight: var(--ms-weight-medium);
}
/* Artikelkarte: Bestand links, Warnabzeichen rechts. Die Set-Fassung steht
   rechtsbuendig (.text-end) — dort ist die Zahl das einzige Element. Hier
   traegt die Zeile zwei Dinge, und ein Abzeichen am rechten Rand mit der Zahl
   davor liest sich als Zusammenhang; beide rechts gedraengt taeten das nicht. */
.prod-card-availability--stock {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--ms-space-2);
}

/* Alte Balkenklassen bleiben fuer andere Ansichten, die einen belastbaren
   Bezugswert besitzen. Die Kundenkarte nutzt bewusst keinen Balken mehr. */
.prod-stock-bar {
  height: 4px;
  background: var(--ms-bg-secondary);
  border-radius: var(--ms-radius-pill);
  overflow: hidden;
  margin-bottom: var(--ms-space-1);
}
.prod-stock-fill {
  height: 100%;
  width: var(--ms-fill, 0%);
  border-radius: var(--ms-radius-pill);
  transition: width var(--ms-transition-slow);
}
/* Zustand des Fuellstands. Der Prototyp setzt die Farbe inline und kennt nur
   zwei Stufen (Warnung bis etwa zur Haelfte, sonst Erfolg); die dritte ist
   ergaenzt, weil die Anwendung sehr wohl Artikel mit Bestand 0 fuehrt — die
   Alt-Ansicht hat sie rot ausgewiesen, und das bleibt so. */
.prod-stock-fill--none { background: var(--ms-error); }
.prod-stock-fill--low  { background: var(--ms-warning); }
.prod-stock-fill--ok   { background: var(--ms-success); }

.prod-part-list__qty.is-low { color: var(--ms-error-text); }

.prod-card-actions {
  display: flex;
  align-items: center;
  gap: var(--ms-space-4);
  margin-top: var(--ms-space-4);
  /* Die Zeile wird nach unten gedrueckt, damit „Mehr erfahren" bei allen
     Karten einer Reihe auf derselben Hoehe steht — auch wenn die
     Beschreibungen verschieden lang sind (Weisung des Betreibers,
     2026-09-18: Knopf unten rechts). `.prod-card-body` ist dafuer eine
     Spalte, siehe dort. */
  margin-top: auto;
  padding-top: var(--ms-space-4);
}
/* Der Bestand der Zeile darueber traegt den Abstand schon; ohne das stuenden
   zwei Abstaende uebereinander. */
.pdetail-stock { display: flex; align-items: center; gap: var(--ms-space-2); margin: 0; }
.pdetail-stock-qty { font-size: var(--ms-text-md); color: var(--ms-text-primary); }

/* ── Bestellliste (Prototyp Zeile 928-1057) ── */
/* Abwandlung fuer Suchfelder, die allein in ihrer Zeile stehen (Bestellliste,
   Adressbuch): sie sollen wachsen, aber nicht ueber die halbe Bildschirmbreite
   laufen. `flex: 1 1 auto` und NICHT `flex: none` — mit `none` schrumpft der
   Behaelter in einer Leiste auf seine Inhaltsbreite (gemessen 264px statt der
   verfuegbaren 640px). Steht er ausserhalb einer Leiste, ist die
   Flex-Angabe wirkungslos und `max-width` allein zaehlt. */
.search-box--wide { flex: 1 1 auto; max-width: 640px; }
/* Fuer Werkzeugleisten, in denen die Suche NEBEN mehreren Filtern steht. Die
   Masse stehen bei `.card-toolbar .search-box--filter` (data.css) — dort, wo
   auch die Regel steht, die sie ueberstimmen muss. */
.search-box--filter { flex: 1 1 260px; }
/* Kein Vorbild im Prototyp: der Rueckstell-Knopf braucht die Schlagwort-Chip-
   Suche, die der Prototyp nicht hat. */
.search-clear {
  position: absolute; right: var(--ms-space-3); top: 50%; transform: translateY(-50%);
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem;
  border: 0; border-radius: 50%; background: transparent;
  color: var(--ms-text-tertiary); cursor: pointer;
}
.search-clear:hover { background: var(--ms-bg-secondary); color: var(--ms-text-primary); }
.search-clear:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }
/* Dieselbe Falle wie bei `.dropdown-item` (navigation.css): `display:
   inline-flex` schlaegt `[hidden]`, und der Knopf staende auch bei leerem Feld
   da — im eigenen Screenshot aufgefallen. Wer ein Bauteil mit eigener
   `display`-Angabe baut, das jemand verbergen koennen soll, braucht diese
   Zeile. */
.search-clear[hidden] { display: none; }
/* Steht der Knopf im Feld, macht das Feld rechts Platz — sonst laeuft ein
   langer Suchbegriff unter ihn. `--ms-space-8` (48px) deckt den Knopf (28px)
   und seinen Abstand zum Rand (12px) mit Luft dazwischen. */
.search-box:has(.search-clear) .form-input { padding-right: var(--ms-space-8); }

/* ── Zustand einer Bestellung ──
   Ein Abzeichen mit getoentem Grund. Die fuenf Zuordnungen kommen aus
   App\Service\OrderStatusPresenter; getoent und nicht als volle Farbflaeche,
   weil weisse Schrift auf --ms-warning nur 2,15:1 erreicht.

   Der farbige PUNKT vor der Bestellnummer ist am 2026-09-02 entfallen: er trug
   dieselbe Aussage wie dieses Abzeichen, nur ohne Beschriftung. In der
   frueheren Kartenliste war er der einzige Farbtraeger, in einer Tabelle mit
   fester Statusspalte war er eine zweite Stimme fuer dasselbe.

   Hier stand bis zum 2026-09-02 auch das Spaltenraster der Bestellliste
   (.order-row/.order-header, sechs Pixelspalten in ZWEI Regeln, die
   voneinander wussten) samt Sortierpillen und aufklappbarer Schnellansicht.
   Die Liste ist jetzt eine Tabelle wie die Firmen- und die Kontaktliste; die
   Breiten stehen als Anteile in data.css, die Schnellansicht ist ein Dialog.
   Punkt und Abzeichen sind das Einzige, was davon gebraucht wird — sie stehen
   auch im Bestelldetail und in der Vorschau. */
.order-badge {
  display: inline-flex; align-items: center;
  padding: var(--ms-space-1) var(--ms-space-3);
  border-radius: var(--ms-radius-pill);
  font-size: var(--ms-text-caption);
  font-weight: var(--ms-weight-semibold);
  white-space: nowrap;
}
.order-badge.status-open { background: rgba(var(--ms-error-rgb),0.08); color: var(--ms-error-text); }
/* ERFUELLT IST GRUEN, nicht tuerkis (9. September 2026). Hier stand der
   Markenton --ms-icon-color — ein ZWEITER Name fuer --ms-teal (#00B4A0),
   weshalb beim Lesen niemand merkte, dass das Abzeichen die Markenfarbe
   traegt. Zwei Gruende, es zu tauschen:

   LESBARKEIT: #00B4A0 auf seinem eigenen 10%-Tint ergibt 2,37:1. Es war
   das EINZIGE der fuenf Statusabzeichen, das durchfiel — die anderen vier
   liegen bei 4,57 bis 6,28:1, weil sie ihren `-text`-Ton nehmen. Mit
   --ms-success-text sind es 8,24:1.

   BEDEUTUNG: der Zustand heisst im Code `success`
   (OrderStatusPresenter::color), der Fortschrittsbalken DERSELBEN Zeile
   ist bereits --ms-success, und „erledigt" ist im Haus sonst ueberall
   gruen (.aside-action.is-done, StockLevel). Nebeneinander lasen sich
   Balken und Abzeichen als zwei verschiedene Gruentoene fuer dieselbe
   Aussage. Der Markenton faerbt Flaechen, er benennt keinen Zustand. */
.order-badge.status-confirmed { background: rgba(var(--ms-success-rgb),0.1); color: var(--ms-success-text); }
.order-badge.status-progress { background: rgba(var(--ms-warning-rgb),0.1); color: var(--ms-warning-text); }
.order-badge.status-done { background: rgba(var(--ms-text-tertiary-rgb),0.1); color: var(--ms-text-secondary); }
.order-badge.status-invoiced { background: rgba(var(--ms-accent-rgb),.10); color: var(--ms-accent-active); }


/* ── Bestelldetail (Prototyp Zeile 726-927 CSS, 2391-2542 HTML) ── */
.detail-back {
  display: inline-flex; align-items: center; gap: var(--ms-space-2);
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium);
  color: var(--ms-text-secondary); cursor: pointer;
  margin-bottom: var(--ms-space-4);
  background: none; border: none;
  transition: color var(--ms-transition-fast);
  text-decoration: none;
}
.detail-back:hover { color: var(--ms-accent); }
.detail-back .ms-ico { font-size: var(--ms-ico-plus); }

.detail-header-card {
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  padding: var(--ms-space-5) var(--ms-space-6);
  margin-bottom: var(--ms-space-5);
}
/* Name und Statusabzeichen sind EINE Einheit links, die Handlungen stehen
   rechts. Vorher lag das Abzeichen bei den Handlungen und sah dort aus wie ein
   weiterer Knopf. */
/* DER TITEL WEICHT, DIE HANDLUNGEN BLEIBEN (2026-09-17).
   Veranstaltungsnamen sind beliebig lang („Goethe Gymnasium goes KPMG
   Duesseldorf - L.Querforth"). Die Zeile stand auf `flex-wrap: wrap`, und der
   Name drueckte damit Abzeichen und Aufklappmenue in eine zweite Zeile — das
   Menue landete UNTER dem Titel, wo niemand es sucht.

   Jetzt bricht die Zeile nicht mehr um (`nowrap`): der Namensblock nimmt den
   uebrigen Platz und gibt ihn wieder her (`flex: 1 1 auto` + `min-width: 0`,
   ohne das schrumpft ein Flex-Kind nie unter seinen Inhalt), die Handlungen
   behalten ihre Breite (`flex: none`). Der Name selbst kuerzt mit `.text-truncate`
   und zeigt sich vollstaendig in der Sprechblase — gesetzt vom
   text-overflow-Controller und NUR, wenn wirklich gekuerzt wurde. */
/* SEIT DEM 2026-10-06 STEHT DAS ABZEICHEN WIEDER IM TITELBLOCK (Weisung des
   Betreibers). Deshalb `nowrap`: mit `wrap` rutschte es bei einem langen Namen
   unter den Titel — genau der Befund, der es am 2026-09-17 auf die Kennzeile
   vertrieben hatte. Jetzt kuerzt der Name (`min-width: 0` am h1), und das
   Abzeichen bleibt daneben stehen. */
.detail-title { display: flex; align-items: center; gap: var(--ms-space-3); flex-wrap: nowrap; min-width: 0; flex: 1 1 auto; }
/* EINZEILIG, UND DESHALB LIEGT DAS SYMBOL AUF DER SCHRIFT. Der Block traegt
   Namen und Abzeichen in EINER Zeile, und keiner von beiden bricht um. Damit
   deckt sich die Mitte des linken Blocks mit der Mitte der Titelzeile, und
   `align-items: center` der Zeile setzt den Druckknopf genau auf diese Hoehe.
   Wer hier ein Element hineinstellt, das umbricht, nimmt diese Deckung weg. */
.detail-title-row {
  display: flex; align-items: center; justify-content: space-between; flex-wrap: nowrap; gap: var(--ms-space-2);
  margin-bottom: var(--ms-space-3);
}
/* `margin: 0` ist nicht Zierde: seit der Titel ein h1 ist, brachte er den
   Aussenabstand von h1 (--ms-space-5) mit und schob das Abzeichen daneben
   aus der Zeile.

   `min-width: 0` ist die zweite Haelfte der Kuerzung: `.text-truncate` bringt
   `overflow: hidden` mit, greift in einer Flex-Zeile aber erst, wenn das Kind
   unter seine Inhaltsbreite schrumpfen darf. */
.detail-event-name { font-size: var(--ms-text-h3); font-weight: var(--ms-weight-bold); line-height: 1.2; letter-spacing: -0.01em; margin: 0; min-width: 0; }
/* Das Abzeichen gibt nichts ab — es traegt zwei Worte, und gekuerzt saehe es
   aus wie ein Fehler. Gekuerzt wird der Name. */
.detail-title > .order-badge { flex: none; }
/* Kennzeile: Bestell- und Projektnummer ueber dem Fortschrittsbalken. Das
   Abzeichen stand hier vom 2026-09-17 bis zum 2026-10-06 und steht jetzt
   wieder neben dem Titel. */
.detail-ids {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--ms-space-2) var(--ms-space-3);
  font-size: var(--ms-text-small); color: var(--ms-text-secondary); margin-bottom: var(--ms-space-4);
}
.detail-progress { display: flex; align-items: center; gap: var(--ms-space-4); }
.detail-progress-bar { flex: 1; height: 6px; background: var(--ms-bg-secondary); border-radius: var(--ms-radius-pill); overflow: hidden; }
.detail-progress-fill { height: 100%; width: var(--ms-fill, 0%); border-radius: var(--ms-radius-pill); background: var(--ms-gradient); transition: width var(--ms-transition-slow); }
/* ── Ablaufkette ──
   Die Schritte einer Bestellung als Reihe: erledigt, als naechstes dran, noch
   offen. Sie ist Anzeige UND Bedienung — deshalb sieht ein Schritt aus wie
   etwas, das man druecken kann, und traegt unter seinem Namen, was der Klick
   verlangt.

   Die Kette bricht um statt zu rollen: ein Ablauf hat drei bis sechs Schritte,
   und eine waagerechte Rollleiste fuer sechs Kacheln waere mehr Bedienung als
   Nutzen. */
.workflow { display: flex; flex-wrap: wrap; align-items: flex-start; gap: var(--ms-space-4); margin-top: var(--ms-space-4); padding-top: var(--ms-space-4); border-top: 1px solid var(--ms-border-divider); }
/* ALLE KACHELN GLEICH GROSS — deshalb ein Raster und keine Flex-Zeile. Bei
   Flex bestimmt der laengste Text die Breite seiner Kachel, und die Kette sah
   aus wie eine Reihe verschieden grosser Knoepfe („Bestaetigt" schmal,
   „Projekt abgeschlossen / Rechnungsinformationen eingeben" doppelt so breit).
   `auto-fit` mit gleicher Mindestbreite gibt jeder Spur dieselbe Breite und
   bricht von selbst um, wenn der Platz nicht reicht.

   Der Pfeil zwischen den Kacheln ist dabei ENTFALLEN: er sass als `::before`
   im Kaestchen und nahm der ersten Kachel nichts, allen anderen aber vierzehn
   Pixel — womit sie eben nicht mehr gleich gross waren. Die Reihenfolge steht
   ohnehin in der Leserichtung, im Haken und im Akzentrahmen. */
.workflow-chain {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: var(--ms-space-2);
  list-style: none; margin: 0; padding: 0;
  flex: 1 1 auto;
}
.workflow-step { display: flex; align-items: stretch; position: relative; }
.workflow-step > form { display: contents; }
.workflow-step .workflow-step-body { flex: 1 1 auto; }
/* Der Weg zurueck sitzt IN der Kachel, oben rechts — nicht daneben. Als
   Nachbar im Kaestchen sah er aus wie ein Trenner zwischen zwei Schritten und
   nicht wie eine Handlung an diesem einen. Absolut gesetzt, damit er die
   Kachelbreite nicht mitbestimmt: die Kacheln sollen gleich gross sein, auch
   die ohne Rueckweg.

   Das Formular darum ist `display: contents` (siehe oben), sonst saesse ein
   Block zwischen Kachel und Knopf. */
.workflow-step-undo { display: contents; }
.workflow-step-undo .icon-action {
  position: absolute; top: 2px; right: 2px;
  color: var(--ms-text-tertiary);
}
/* Platz fuer den Pfeil — nur dort, wo einer steht. */
.workflow-step:has(.workflow-step-undo) .workflow-step-body { padding-right: var(--ms-space-6); }
.workflow-step-body {
  display: flex; align-items: center; gap: var(--ms-space-2);
  padding: var(--ms-space-2) var(--ms-space-3);
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-sm);
  background: var(--ms-bg-primary);
  color: var(--ms-text-primary);
  text-align: left;
  cursor: pointer;
  transition: border-color var(--ms-transition-fast), background var(--ms-transition-fast);
}
/* Die erste Phase ist ein <span>: sie wird vom Absenden gesetzt, es gibt
   nichts anzuklicken. */
span.workflow-step-body { cursor: default; }
button.workflow-step-body:hover:not(:disabled) { border-color: var(--ms-border-control); }
.workflow-step-body:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 2px; }
.workflow-step-body:disabled { cursor: not-allowed; opacity: 0.6; }
.workflow-step-mark { display: inline-flex; flex: none; color: var(--ms-text-tertiary); }
.workflow-step-text { display: flex; flex-direction: column; min-width: 0; }
.workflow-step-label { font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium); }
.workflow-step-hint {
  font-size: var(--ms-text-caption); color: var(--ms-text-secondary);
  font-variant-numeric: tabular-nums;
}
/* Erledigt: zurueckgenommen, aber weiter anklickbar — ein Schritt laesst sich
   widerrufen, und eine Projektnummer laesst sich aendern. */
.workflow-step.is-done .workflow-step-body { border-style: dashed; background: transparent; }
.workflow-step.is-done .workflow-step-mark { color: var(--ms-success-text); }
.workflow-step.is-done .workflow-step-label { color: var(--ms-text-secondary); }
/* Als naechstes dran: der einzige Schritt mit Akzent — sonst waere keiner betont. */
.workflow-step.is-next .workflow-step-body { border-color: var(--ms-accent); border-width: 1.5px; }
.workflow-step.is-next .workflow-step-mark,
.workflow-step.is-next .workflow-step-label { color: var(--ms-accent); }
.workflow-step.is-next .workflow-step-label { font-weight: var(--ms-weight-semibold); }
/* `.workflow-extra` ist am 2026-09-17 entfallen: der einzige Bewohner war die
   Wareneingangskontrolle, und sie steht jetzt in der Produktkarte. Die drei
   Zeilen waren ohnehin nur Flickwerk — sie bogen `.aside-action`, ein Bauteil
   der frueheren SEITENSPALTE (shell.css, senkrechte Liste), fuer eine
   waagerechte Reihe zurecht. Genau daher kam der fremde Eindruck. */

/* ── Angaben als einzelne Karten ──
   Je Thema eine Karte statt aller Themen in einer. Die Spaltenzahl richtet
   sich nach dem Platz: auf einem breiten Schirm drei bis vier nebeneinander,
   auf einem Laptop zwei, auf dem Telefon eine. 300px ist die Untergrenze, ab
   der eine Anschrift ohne Zwangsumbruch steht.

   `align-items: start` verhindert, dass alle Karten einer Reihe auf die Hoehe
   der laengsten wachsen — „Kategorien" mit einer Zeile saehe sonst aus wie ein
   halbleerer Kasten neben „Lieferdetails" mit acht. */
.detail-cards {
  display: grid;
  /* DREI SPALTEN, feste Zahl — und nicht `auto-fit`. Mit `auto-fit` bestimmte
     die Fensterbreite, wie viele Karten in eine Zeile passen; auf einem
     1730px-Schirm waren es vier, und die gewollte Aufteilung (oben die drei
     grossen Angabenkarten, darunter die drei kurzen) zerfiel. Die Reihenfolge
     im Markup ist die Reihenfolge auf dem Schirm, siehe order/_facts.

     `minmax(0, 1fr)` und nicht `1fr`: eine Grid-Spur ist standardmaessig
     mindestens so breit wie ihr laengstes unumbrechbares Wort. Eine lange
     E-Mail-Adresse haette die Spalte sonst aufgezogen und die beiden anderen
     zusammengedrueckt — die drei sollen sich das Fenster gleich teilen. */
  grid-template-columns: repeat(3, minmax(0, 1fr));
  /* GLEICHE HOEHE je Zeile — der Standardwert `stretch`. Hier stand `start`,
     damit eine kurze Karte nicht auf die Hoehe der laengsten wuchs; mit drei
     bewusst gruppierten Zeilen ist die gemeinsame Unterkante aber das, was die
     Zeile als Zeile lesbar macht. */
  gap: var(--ms-space-4);
  margin-bottom: var(--ms-space-4);
}
/* Zwei Spalten, sobald drei zu schmal wuerden: unter rund 1100px bleiben je
   Karte keine 340px, und darunter steht eine Anschrift nicht mehr ohne
   Zwangsumbruch. */
@media (max-width: 1100px) {
  .detail-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (max-width: 767px) {
  .detail-cards { grid-template-columns: 1fr; }
}
/* Die Kopfzeile einer Angabenkarte ist leiser als die einer Maske: sie
   benennt ein Thema, sie fuehrt keine Seite an.

   GLEICHE HOEHE MIT UND OHNE KNOPF. Die Hoehe kam vorher aus dem Inhalt: eine
   Kopfzeile mit Symbolknopf war so hoch wie der Knopf, eine ohne so hoch wie
   ihre Textzeile — und die Koepfe der sechs Karten sprangen. Die Mindesthoehe
   ist deshalb der Knopf selbst plus die Polsterung, die die Kopfzeile ohnehin
   traegt. Ein fester Pixelwert waere hier falsch: er muesste jedes Mal
   nachgerechnet werden, wenn sich die Knopfgroesse aendert. */
.detail-cards .card-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--ms-space-2);
  min-height: calc(var(--ms-icon-btn-h) + 2 * var(--ms-space-4));
  padding-top: 0; padding-bottom: 0;
  /* Die SCHRIFTGROESSE stand hier und ist am 2026-09-17 an das Bauteil selbst
     gewandert (data.css, `.card-header`). Sie war nie eine Eigenheit dieses
     Rasters — sie war nur hier aufgeschrieben, und ausserhalb davon stand
     derselbe Kopf doppelt so gross da. Was hier bleibt, ist wirklich Layout:
     die Zeile verteilt Titel und Knopf und haelt ihre Hoehe mit und ohne
     Knopf gleich. */
}
.detail-cards .card-header .icon-action { flex: none; }

.detail-section {
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  padding: var(--ms-space-5) var(--ms-space-6);
  margin-bottom: var(--ms-space-4);
}
.detail-section-title {
  /* `--ms-text-lg` (18px) statt des handgesetzten `1.1rem` (17,6px), das die
     Skala nicht kennt — 2026-09-17. Der Unterschied ist unsichtbar, die
     Herkunft nicht: der Wert kommt jetzt aus dem Tokensystem. Und er steht
     wieder UEBER dem Fliesstext der Karte (16px), was bei 17,6 gegen 18 nicht
     mehr der Fall war. */
  font-size: var(--ms-text-lg); font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-primary);
  margin-bottom: var(--ms-space-4);
  display: flex; align-items: center; justify-content: space-between; gap: var(--ms-space-2);
}
/* HIER STAND `.detail-section-title .ms-ico { color: var(--ms-icon-color) }`
   (entfernt 2026-09-17). Gedacht war die Regel fuer ein schmueckendes Symbol
   neben einer Abschnittsueberschrift — ein solches gibt es in keiner der sechs
   Ueberschriften. Getroffen hat sie deshalb genau ein Element: den
   Ansichtswechsler in der Produktkarte, der dadurch als EINZIGER Symbolknopf
   der Anwendung tuerkis war statt grau (Befund des Betreibers). Ein Knopf
   nimmt seine Farbe aus `.icon-action`, nicht aus der Ueberschrift, in der er
   zufaellig steht. */

/* Handlungen rechts in einer Abschnittsueberschrift. `.detail-section-title`
   verteilt mit `space-between`; ab der zweiten Handlung braucht es eine
   Klammer, sonst zoege die Verteilung die Knoepfe auseinander. */
.detail-section-actions { display: inline-flex; align-items: center; gap: var(--ms-space-2); }

/* RASTER UND NICHT FLEXVERBUND (2026-09-17). Befund des Betreibers am eigenen
   Bildschirm: „warum haben die Karten unterschiedliche Breiten? Kann es sein,
   dass sich diese nach der Laenge der Zeichenketten richten?" — ja, genau das.

   Der Verbund stand auf `display: flex` und die Karte trug nur `min-width`,
   also galt die Voreinstellung `flex: 0 1 auto`: jede Karte war so breit wie
   ihr Inhalt und wuchs nicht mit. Der breiteste Inhalt ist die Namenszeile mit
   dem Abzeichen daneben — „Notizbuch - Nachhaltige Ideen" ergab 540px,
   „Veggie Bag" 358px. Und weil nichts wuchs, blieb die Zeile rechts offen.

   Diese Reihe war die EINZIGE Kachelflaeche des Kundenbereichs ohne Raster;
   `.prod-grid`, `.product-grid`, `.addr-grid` und `.summary-items` sind alle
   `auto-fill` mit gleichen Spalten. Es war kein Entwurf, es war ein Ausreisser.

   220px Mindestspalte: vier Karten auf der 1140er-Seite (je 264px), fuenf auf
   der 1320er (je 243px), und beide mit Luft zum Umschlag (4,69 und 5,46). Die
   Kachel traegt ein 90px-Bild, den Namen und eine Menge — sie braucht weniger
   Breite als eine Katalogkarte und darf enger stehen.

   `min-width` an der Karte ist entfallen: die Spur des Rasters setzt die
   Mindestbreite, und zwei Stellen fuer dieselbe Zahl waeren eine zu viel. */
.detail-products {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: var(--ms-space-4);
}
/* Die Kachel einer Position (`.detail-product-item` samt Bild, Namen und
   Menge) ist am 2026-10-06 entfallen: die bestellten Produkte stehen nur noch
   als Tabelle da, aufgebaut wie der Ausdruck (Weisung des Betreibers). */

/* Wiederverwendet fuer die Dateiliste UND, in der Listenansicht, fuer die
   bestellten Produkte — gleiche Tabellenoptik, kein zweites Bauteil noetig. */
.detail-files-table { width: 100%; border-collapse: separate; border-spacing: 0; }
.detail-files-table thead th {
  font-size: var(--ms-text-caption); font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-secondary); text-align: left;
  padding: 0 var(--ms-space-3) var(--ms-space-2);
  border-bottom: 1px solid var(--ms-border-light);
}
.detail-files-table tbody td { font-size: var(--ms-text-small); padding: var(--ms-space-3); border-bottom: 1px solid var(--ms-border-light); vertical-align: middle; }
.detail-files-table tbody tr:last-child td { border-bottom: none; }
.detail-files-table tbody tr:hover { background: rgba(var(--ms-accent-rgb),0.02); }
/* Rechtsbuendige Spalte: die Grundregel oben setzt `text-align: left` mit
   0-1-2 und schlaegt damit `.text-end` (0-1-0) — der Kopf „Menge" blieb
   links, die Zahlen standen rechts (Befund des Betreibers, 2026-10-05). Die
   Gegenregel muss die Spezifitaet der Grundregel uebertreffen. */
.detail-files-table thead th.text-end { text-align: right; }
/* Anklickbare Zeile (bestellte Produkte): der Zeiger sagt es, und wer mit
   der Tastatur kommt, sieht, wo er steht. */
.detail-files-table tbody tr.row-link { cursor: pointer; }
.detail-files-table tbody tr.row-link:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: -2px; }
/* DIE TABELLE DER BESTELLTEN PRODUKTE IST DIE PACKLISTE (2026-10-06,
   Weisung des Betreibers: „Orientiere dich an dem Ausdruck"). Deshalb steht
   die Beschreibung VOLLSTAENDIG unter dem Namen, ungekuerzt und mit ihren
   Zeilenumbruechen — beim Messepaket ist sie die Aufzaehlung dessen, was in
   den Karton gehoert. Der Name halbfett wie im Ausdruck, die Beschreibung in
   der Groesse der Zelle; leiser gesetzt waere sie die Nebensache, und hier ist
   sie die Hauptsache. Zellen oben ausgerichtet: eine lange Aufzaehlung neben
   einer mittig stehenden Menge laesst nicht mehr erkennen, wozu die Zahl
   gehoert. */
.detail-products-table tbody td { vertical-align: top; }
.detail-product-title { font-weight: var(--ms-weight-semibold); }
.detail-product-content {
  margin: var(--ms-space-2) 0 0;
  padding: 0;
  list-style: none;
}
.detail-file-actions { display: flex; gap: var(--ms-action-gap); justify-content: flex-end; }
/* Die Handlungen einer Dateizeile stehen als AKTIONS-SYMBOLE da (.icon-action,
   buttons.css) — kein Rahmen, kein gefuellter Grund. Bis zum 2026-09-10 trug
   `.detail-file-btn` hier eine eigene Gestaltung mit beidem; sie ist mit dem
   Bauteil entfallen. Der Abstand kommt aus --ms-action-gap. */

/* Aussehen aus dem gemeinsamen Regelsatz (forms.css). Der groessere Radius
   bleibt: das Notizfeld ist eine Flaeche und kein Zeilenfeld. */
.detail-notes-area {
  width: 100%; min-height: 100px;
  border-radius: var(--ms-radius-md);
  padding: var(--ms-space-3) var(--ms-space-4);
  line-height: 1.6; color: var(--ms-text-primary);
  resize: vertical; outline: none;
  transition: border-color var(--ms-transition-base);
}
.detail-notes-area:focus { border-color: var(--ms-accent); box-shadow: 0 0 0 3px rgba(var(--ms-accent-rgb),0.12); }
.detail-notes-actions { display: flex; justify-content: flex-end; margin-top: var(--ms-space-3); }

/* ── Angabengruppen einer Bestellung ──
   Hier stand `.detail-info-grid` mit VIER FESTEN Spalten und `.detail-info-block`,
   das ganze Anschriften in `--ms-text-caption` (12px) setzte — der kleinsten
   Stufe des Systems, gedacht fuer Beizeilen und Zaehler. Vier Spalten auf einer
   1320px-Seite lassen je Spalte rund 280px; eine Anschrift bricht darin
   mehrfach um, und zwoelf Pixel liest man dann nicht mehr, man entziffert sie.

   Jetzt richtet sich die Spaltenzahl nach dem Platz statt umgekehrt, und der
   Inhalt sind Paarlisten (`.pair-list`, data.css) in `--ms-text-small`. 260px
   ist die Untergrenze, unter der eine Beschriftung neben ihrem Wert nicht mehr
   sinnvoll steht; darunter stapelt die Paarliste selbst. */
.detail-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: var(--ms-space-5) var(--ms-space-6);
}
/* Zwei Spalten fest — fuer den Protokollbereich der Adminsicht, dessen zwei
   Bloecke im Anteilsraster je nach Fensterbreite sonst untereinander landen. */
.detail-facts--halves { grid-template-columns: 1fr 1fr; }
.detail-facts-title {
  display: flex; align-items: center; justify-content: space-between; gap: var(--ms-space-2);
  /* Knopfhoehe plus Innenabstand: so ist eine Ueberschrift MIT Symbolknopf
     genauso hoch wie eine ohne, und die Gruppen beginnen auf einer Linie. */
  min-height: calc(2.25rem + var(--ms-space-2));
  margin: 0 0 var(--ms-space-3);
  padding-bottom: var(--ms-space-2);
  border-bottom: 2px solid var(--ms-accent);
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-primary);
}
.detail-facts-title .icon-action { flex: none; }
/* ── Karte einer Aenderungsanfrage ──
   Ihr Koerper ist EINE Paarliste (Beschriftung 12px, Wert 16px), darunter je
   nach Recht das Entscheidungsformular oder der Knopf zum Zuruecknehmen. Die
   Paarliste bringt keinen Abstand nach unten mit — sie ist sonst das letzte
   Element einer Karte. Hier folgt etwas, also braucht es den Abstand hier. */
/* ── Eine Anfrage in der Karte „Aenderungsanfragen" (2026-09-17) ──
   Die Anfragen standen bis dahin in einem eigenen Abschnitt ueber die volle
   Breite, jede in einer eigenen Karte. Der Betreiber fand das zu dominant, und
   das war es: eine Anfrage ist eine Randnotiz zu einer Angabe nebenan, keine
   zweite Hauptsache. Jetzt teilen sich alle EINE Karte im Raster der
   Angabenkarten, getrennt durch dieselbe Linie, die auch sonst gliedert.

   `.change-request-card` ist damit entfallen — die Klasse bezeichnete die
   einzelne Karte, die es nicht mehr gibt. */
.change-request-entry + .change-request-entry {
  margin-top: var(--ms-space-5);
  padding-top: var(--ms-space-4);
  border-top: 1px solid var(--ms-border-divider);
}
/* Die Art der Anfrage und ihr Stand in einer Zeile — dieselbe Bauform wie eine
   Kartenkopfzeile, nur eine Stufe leiser: sie fuehrt einen Eintrag an, keine
   Karte. */
.change-request-kind {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--ms-space-2);
  margin: 0 0 var(--ms-space-3);
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold);
}
.change-request-entry .pair-list + form { margin-top: var(--ms-space-4); }

/* Kategorien und Freigaben stehen als Abzeichen nebeneinander. */
.detail-tags { display: flex; flex-wrap: wrap; gap: var(--ms-space-2); }

/* Statusabzeichen und Aktionsmenue rechts neben dem Veranstaltungsnamen.
   `flex: none` haelt die Knopfreihe oben rechts, egal wie lang der Name ist. */
.detail-status-row { display: flex; align-items: center; gap: var(--ms-space-2); flex: none; }
/* „Erneut bestellen" ist ein POST und braucht deshalb ein <form> um seinen
   Symbolknopf. `display: contents` nimmt dem Formular seinen eigenen Kasten —
   der Knopf wird direktes Kind der Reihe und steht damit genau so wie die
   Knoepfe daneben. Ohne das saesse zwischen ihnen ein Block mit eigener Hoehe.
   Das Formular bleibt im Dokument und wird unveraendert abgeschickt. */
.detail-status-row > form { display: contents; }

/* Platzhalter, solange der Turbo-Frame der Dateiliste noch laedt. Die feste
   Hoehe haelt die Seite ruhig: ohne sie springt der Inhalt darunter nach oben,
   sobald die Liste eintrifft. */
.detail-frame-loading {
  height: 150px;
  display: flex; align-items: center; justify-content: center;
}

/* Dateiname in der Dateiliste: einzeilig mit Auslassungspunkten. Die feste
   Hoechstbreite haelt die Aktionsspalte an ihrem Platz — Namen aus Uploads sind
   beliebig lang („Angebot_Messestand_Halle4_Rev3_final_final.pdf"). Der volle
   Name steht im `title`; das ist hier bewusst die Sprechblase des Browsers und
   nicht die eigene, weil sie nur bei tatsaechlich gekuerzten Namen etwas
   hinzufuegt und sonst nur im Weg waere. */
.detail-file-name {
  display: block;
  max-width: 320px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Produktbild in der Positionstabelle — anklickbar, oeffnet die Grossansicht. */
/* Das Bild der Position: quadratisch wie jedes Bildfenster des Hauses, in der
   Groesse der frueheren Kachel (90px) — klein genug fuer eine Tabellenzeile,
   gross genug, um beim Packen den Artikel zu erkennen. */
.detail-product-thumb {
  width: 90px; height: 90px;
  object-fit: contain;
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-sm);
}
/* ── Bestellassistent (Prototyp Zeile 531-673 CSS, 1754-2024 HTML) ──
   Wichtig: der Prototyp ist EINE Seite mit 7 clientseitig animierten Karten.
   Die echte App hat 5 echte Symfony-Schritte (eigene Seitenaufrufe mit
   Formular-Validierung, Sitzungsdaten, Verfuegbarkeitspruefung). Uebernommen
   wird deshalb die Optik, nicht der Zustandsautomat — abgestimmt, siehe
   /Users/martin/.claude/plans/velvet-riding-wadler.md. */

/* Kategorie-/Konfigurationsauswahl (Schritt 1) */
.categories-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--ms-space-5); max-width: 900px; margin: 0 auto; }
/* EIN FESTES BILDFENSTER, wie bei `.prod-card-img` und `.product-thumb` — die
   Kachel in Schritt 1 war die einzige Bildkachel des Kundenbereichs ohne eines
   (behoben 2026-09-09).

   Vorher hatte `.category-thumb` weder Hoehe noch Seitenverhaeltnis. Das
   `height: 100%` am Bild hatte damit keinen Bezugswert, faellt auf `auto`
   zurueck, und `object-fit: cover` bekam nie etwas zu beschneiden: die Hoehe
   der Kachel war schlicht die Hoehe der HOCHGELADENEN DATEI, auf Kartenbreite
   skaliert. Jedes Motiv in einem anderen Format hiess: jede Ueberschrift auf
   einer anderen Hoehe. Genau so gemeldet.

   Und das Format kann beliebig sein: das Katalogbild ist ein gewoehnliches
   `FileType` ueber Vich (ProductConfigurationType) und wird abgelegt, wie es
   ankommt. Die Bildkette mit ihren drei quadratischen Stufen
   (App\Service\Image\ImagePipeline) gilt fuer Artikel und Artikel-Sets, nicht
   fuer den Katalog. Die Kachel darf sich darauf also nicht verlassen.

   4:3 und `contain` sind NICHT neu entschieden, sondern von `.prod-card-img`
   uebernommen — dieselbe Sache, dieselbe Antwort: die Fotos sind teils
   hochkant (Rollups), teils quer (Messestaende), `cover` schnitte je Motiv
   etwas anderes ab. Die Flaeche dahinter ist die Kartenflaeche und nicht das
   Grau des Seitengrunds, sonst zerfaellt die Karte bei nicht-4:3-Motiven
   sichtbar in zwei Haelften. */
.category-thumb {
  display: flex; align-items: center; justify-content: center;
  overflow: hidden;
  aspect-ratio: 1 / 1;
  background: var(--ms-card-bg);
}
.category-thumb img { width: 100%; height: 100%; object-fit: contain; }
.category-icon {
  width: 56px; height: 56px; border-radius: var(--ms-radius-md);
  display: flex; align-items: center; justify-content: center;
  color: var(--ms-on-accent);
}
.category-body { padding: var(--ms-space-5); }
.category-name { font-size: var(--ms-text-h4); font-weight: var(--ms-weight-semibold); line-height: 1.25; margin-bottom: var(--ms-space-1); }
.category-desc { font-size: var(--ms-text-small); color: var(--ms-text-secondary); }
/* Die Kachel ist ein echter Verweis (tastaturbedienbar, in neuem Tab
   oeffenbar). v2 faerbt Links bewusst nicht global (siehe base.css), also
   braucht die Kachel ihre Textfarbe hier — sonst greift das Browser-Blau. */
a.card { display: block; color: var(--ms-text-primary); text-decoration: none; }

/* Fortschritt: im Prototyp rein per JS, hier serverseitig je Seite gesetzt
   (siehe templates/v2/order/wizard/_progress.html.twig). */
.wizard-progress { display: flex; align-items: center; gap: 0; margin-bottom: var(--ms-space-6); padding: 0 var(--ms-space-1); flex-wrap: wrap; }
/* ZIELGROESSE, getrennt von der Darstellung (WCAG 2.5.8, Stufe AA): der
   sichtbare Punkt bleibt 10px, das anklickbare Ziel ist 24px. Gezeichnet wird
   deshalb ein ::before, das Element selbst ist nur noch die Trefferflaeche.
   Der Abstand steckt jetzt IN dieser Flaeche und nicht mehr im `gap` — mit
   `gap` daneben ueberlappten sich die Ziele, und ein Klick zwischen zwei
   Punkten traefe einen beliebigen davon.
   Vorher: 10x10px bei 8px Luecke, Raster 18px. Damit war weder die Zielgroesse
   erfuellt noch die Ausnahme fuer ausreichenden Abstand (die einen 24px-Kreis
   um das Ziel verlangt, der kein Nachbarziel schneidet). */
.wizard-dot {
  width: var(--ms-space-5); height: var(--ms-space-5);
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; text-decoration: none;
}
.wizard-dot::before {
  content: ''; display: block;
  width: 10px; height: 10px; border-radius: 50%;
  background: var(--ms-border-light);
  transition: all var(--ms-transition-slow);
}
.wizard-dot.done::before { background: var(--ms-icon-color); }
/* Die aktive Pille ist 32px breit; ihre Trefferflaeche waechst mit, damit
   zwischen ihr und den Nachbarpunkten dieselbe Luft bleibt. */
.wizard-dot.active { width: 40px; }
.wizard-dot.active::before { background: var(--ms-accent); width: 32px; border-radius: var(--ms-radius-pill); }
a.wizard-dot { cursor: pointer; }
a.wizard-dot:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: 3px; }
.wizard-step-label { font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium); color: var(--ms-text-secondary); margin-left: var(--ms-space-2); }
.wizard-step-count { font-size: var(--ms-text-small); color: var(--ms-text-secondary); margin-left: auto; }

.wizard-card-container { position: relative; }
.wizard-card-inner {
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-xl);
  box-shadow: var(--ms-card-shadow);
  padding: var(--ms-space-8);
}
.wizard-card-header { margin-bottom: var(--ms-space-6); }
.wizard-card-title { font-size: var(--ms-text-h3); font-weight: var(--ms-weight-bold); letter-spacing: -0.02em; line-height: 1.2; margin-bottom: var(--ms-space-2); }
.wizard-card-subtitle { font-size: var(--ms-text-body); color: var(--ms-text-secondary); line-height: 1.6; margin: 0; }
/* DIE KOPFSYMBOLE DER ASSISTENTENKARTEN SIND ENTFALLEN (2026-09-16, Weisung
   des Betreibers: „diese benoetigen wir nicht, sie rauben nur Platz"). Sie
   standen auf jeder der sieben Karten ueber der Ueberschrift — ein farbiges
   Quadrat, das nichts sagte, was die Ueberschrift nicht schon sagt, und im
   Assistenten oberhalb des Falzes rund 80px kostete. Mit ihnen fallen
   `.wizard-card-icon` und seine drei Farbfassungen. */
.wizard-nav { display: flex; justify-content: space-between; align-items: center; gap: var(--ms-space-3); margin-top: var(--ms-space-6); }

/* Datumsfelder im Assistenten: Flatpickr haengt hier (Wrap-Modus) eigene
   Klassen an, deshalb ueber den Tag im eigenen Wrapper stylen — gleiches
   Muster wie .prod-date-field auf der Produktuebersicht. */
.wizard-date-row { display: flex; gap: var(--ms-space-3); flex-wrap: wrap; }
.wizard-date-field { display: flex; flex-direction: column; gap: var(--ms-space-1); flex: 1 1 160px; }
.wizard-date-field > label { font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium); color: var(--ms-text-secondary); }
.wizard-date-input { display: flex; align-items: stretch; }
/* Rand, Fuellung und Fokusring kommen aus dem gemeinsamen Regelsatz
   (forms.css). Hier steht nur, was diese Bauform ausmacht: das Feld sitzt
   links an einem Kalenderschalter, also ist es rechts offen. */
.wizard-date-input input {
  flex: 1 1 auto; min-width: 0;
  /* NUR waagerecht und aus dem Abstandssystem. Vorher stand hier
     `padding: 0.6rem 0.875rem` — zwei harte Werte, die zugleich das
     `padding-top/bottom: 0` des gemeinsamen Regelsatzes ueberstimmten. Das
     Feld hat eine feste Hoehe (--ms-control-h); senkrechte Polsterung darin
     quetscht nur den Textkasten. Gemessen ergab das 9,6px/14px und damit die
     dritte Polsterung desselben Bauteils. */
  padding-left: var(--ms-space-4);
  padding-right: var(--ms-space-3);
  border-radius: var(--ms-control-radius) 0 0 var(--ms-control-radius);
  border-right: none;
}
/* Das Kalendersymbol traegt in dieser Bauform der Schalter DANEBEN. Ohne diese
   Regel zeichnet `.v2-datepicker-display` sein eigenes zusaetzlich in den
   Hintergrund — zwei Kalender an einem Feld, das zweite halb unter dem Text.
   `:not(:only-child)` ist die Bedingung und nicht Zierde: steht das Feld
   allein (Regel unten), ist der Hintergrund das EINZIGE Symbol. */
.wizard-date-input input:not(:only-child) { background-image: none; }
/* Ohne danebenliegenden Schalter (Uhrzeitfeld) ist das Feld rundum
   geschlossen — sonst fehlte ihm rechts der Rand. */
.wizard-date-input input:only-child {
  border-radius: var(--ms-control-radius);
  border-right: var(--ms-control-border-w) solid var(--ms-border-control);
}
.wizard-date-input input:disabled { background: var(--ms-bg-secondary); color: var(--ms-text-tertiary); cursor: not-allowed; }
/* Der Schalter sitzt AM Feld und traegt deshalb dessen Rand — sonst haette
   dasselbe Bedienelement zwei verschiedene Kanten. */
.wizard-date-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 0 var(--ms-space-3);
  border: var(--ms-control-border-w) solid var(--ms-border-control);
  border-radius: 0 var(--ms-control-radius) var(--ms-control-radius) 0;
  background: var(--ms-bg-secondary);
  color: var(--ms-text-secondary);
  cursor: pointer;
}

/* Hinweisbanner fuer kritische Wunschtermine (Wochenende/kurzfristig) —
   eigene Ergaenzung, im Prototyp nicht vorhanden. */
.wizard-warning {
  display: flex; align-items: flex-start; gap: var(--ms-space-3);
  padding: var(--ms-space-4) var(--ms-space-5);
  margin-bottom: var(--ms-space-5);
  border: 1px solid var(--ms-warning);
  border-radius: var(--ms-radius-md);
  background: var(--ms-warning-muted);
  font-size: var(--ms-text-small);
}
/* Das Symbol sagt „Achtung" und traegt damit Bedeutung — WCAG 1.4.11, 3:1
   gegen seinen Untergrund. --ms-warning auf --ms-warning-muted erreicht nur
   1,97:1; --ms-warning-text liegt bei 6,10:1. Dieselbe Rechnung wie bei
   .is-error ein paar Zeilen weiter unten. */
/* NUR DAS EIGENE SYMBOL, deshalb `>` (2026-09-09). Ohne den Kindselektor traf
   die Regel jedes Symbol IM Band — auch das im Knopf „Fortsetzen". Dort war es
   dann 20px gross und in --ms-accent eingefaerbt, also blau auf blauem Grund:
   unsichtbar, aber Platz beanspruchend. Gemeldet wurde es als „der Knopf ist
   nach links ungewoehnlich erweitert". */
.wizard-warning > .ms-ico { color: var(--ms-warning-text); font-size: var(--ms-glyph-lg); line-height: 1; }
.wizard-warning h5 { font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold); margin: 0 0 var(--ms-space-1); }
.wizard-warning p { font-size: var(--ms-text-small); margin: 0; }

/* Zwei Abwandlungen desselben Banners. Sie standen zuvor als Inline-Styles an
   sechs Stellen in den Schritt-Vorlagen — inhaltlich sind es aber genau zwei
   Zustaende, und als Klasse gesagt sind sie auch benannt:

   .is-error  Pflichtfelder fehlen oder sind ungueltig (Schritte 4, 5, 6, 7 und
              das Bearbeiten-Band). Rot, weil es den Absender blockiert.
   .is-info   sachlicher Hinweis ohne Fehler — bislang nur das Band in Schritt 1,
              das eine unterbrochene Bestellung zum Fortsetzen anbietet. */
.wizard-warning.is-error {
  border-color: var(--ms-error);
  background: var(--ms-error-muted);
}
/* Das Symbol traegt hier Bedeutung (es sagt „Fehler") und faellt damit unter
   WCAG 1.4.11: 3:1 gegen seinen Untergrund. --ms-error auf --ms-error-muted
   erreicht nur 2,97:1 — knapp darunter, aber darunter. --ms-error-text liegt
   bei 5,47:1. */
.wizard-warning.is-error > .ms-ico { color: var(--ms-error-text); }

.wizard-warning.is-info {
  border-color: var(--ms-accent);
  background: rgba(var(--ms-accent-rgb), var(--ms-tint));
}
.wizard-warning.is-info > .ms-ico { color: var(--ms-accent); }

/* Abschluss nach dem Absenden (Prototyp #wizard-success, Zeile 664-672 / 2014-2023). */
.success-page {
  display: flex; align-items: center; justify-content: center;
  min-height: calc(100vh - 56px); text-align: center;
  padding: var(--ms-space-8) var(--ms-space-4);
}
/* Begrenzt die Textbreite der Abschlussmeldung. Ohne Deckel laufen die Zeilen
   auf breiten Schirmen ueber die gesamte Fensterbreite und werden unlesbar. */
.success-box { max-width: 480px; }
.success-icon {
  width: var(--ms-icon-size-xl); height: var(--ms-icon-size-xl);
  border-radius: 50%; background: var(--ms-success-muted);
  display: flex; align-items: center; justify-content: center;
  margin: 0 auto var(--ms-space-5);
  color: var(--ms-success);
  font-size: var(--ms-glyph-2xl);
}

/* Produktauswahl (Schritt 3). Der Prototyp kennt nur eine einfache
   Klick-Auswahl; die echte Auswahl hat Pflicht-Ebenen, Mengenfelder,
   Verfuegbarkeitspruefung und ein Info-Popup je Karte — die Zusatzteile
   sitzen zusaetzlich in derselben Kartenoptik. */
/* DIE MINDESTSPALTE STEHT IN tokens.css und wandert dort mit der Seitenbreite
   (2026-09-17): 220px auf der 1140px-Seite, 260px auf der 1320px-Seite. Grund
   ist, dass `auto-fill` eine breitere Flaeche voreingestellt in MEHR und
   SCHMALERE Spalten umsetzt — ohne das Mitwandern waere jede Karte von 255 auf
   235px geschrumpft, weil eine fuenfte Spalte hineinpasst. So bleiben es vier
   Spalten, die von 255 auf 300px wachsen.

   Warum nicht EINE Zahl fuer beide Breiten: sie muesste zwischen 236 und 255
   liegen, und 250px kaeme auf der schmalen Seite auf 4,07 Spalten heraus —
   sieben Hundertstel ueber dem lautlosen Umschlag auf drei. Die Begruendung
   und beide Rechnungen stehen bei den Token.

   Gilt auch fuer den Katalog-Editor (`.stage-grid` erbt dieses Raster). Dort
   greift die Sprosse nicht (die Verwaltung hat keine Seitenbreite, sondern
   100%), der Editor bleibt also bei 220px — unveraendert zu vorher. */
.product-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--ms-product-card-min), 1fr)); gap: var(--ms-space-5); }
/* Kein `cursor: pointer` auf der Karte selbst: bei Mengenauswahl ist sie kein
   Schalter, nur der Rahmen um Stepper und Loeschen-Zeile. Die Einfachauswahl
   bekommt den Zeiger ueber `.interactive`, das `checkbox-card` setzt. */
.product-card {
  /* Rahmenbreite konstant 2px: der Prototyp springt bei Auswahl von 1.5px auf
     2px, was jede gewaehlte Karte um einen Punkt hoeher macht als ihre
     Nachbarn. Die Auswahl aendert deshalb nur die Farbe. */
  border: 2px solid var(--ms-border-light); border-radius: var(--ms-radius-lg);
  transition: all var(--ms-transition-base);
  overflow: hidden; position: relative;
  background: var(--ms-card-bg);
}
.product-card:hover { transform: translateY(-2px); box-shadow: var(--ms-card-shadow); }
/* AUSGEWAEHLT IST GRUEN — derselbe Ton wie ein geprueftes Feld (2026-09-16).
   Nachgefragt hat es der Betreiber: „warum verwenden wir hier ueberhaupt
   andere Gruentoene?"

   Bis dahin war es Tuerkis, mit dieser Begruendung: „Blau ist die Farbe der
   Bedienelemente (Knoepfe, Links, Fortschritt), Tuerkis die Symbolfarbe der
   Marke — die Auswahl bleibt so vom Klickbaren unterscheidbar." Der erste
   Teil gilt weiter, der zweite war eine Farbe zu viel: das System hatte
   damit ZWEI gruenliche Toene fuer nahe verwandte Aussagen — #007E70 fuer
   „ausgewaehlt" und #198754 fuer „geprueft und in Ordnung", 21 Grad Farbton
   auseinander. Wer beide nacheinander sieht, liest zweimal dasselbe Gruen und
   sucht den Unterschied.

   Jetzt ist es EIN Ton: --ms-success, derselbe Wert, auf den --ms-valid
   zeigt. „Gesetzt und in Ordnung" ist dieselbe Aussage, ob sie an einem Feld
   oder an einer Karte steht. 4,53:1 auf Weiss und 4,16:1 auf dem grauen Band
   — deutlich ueber den 3:1, die ein bedeutungstragendes Grafikelement
   braucht (WCAG 1.4.11).

   Tuerkis bleibt die Marke: der Abschlussknopf (.btn-teal), die Symbolfarbe
   und der Verlauf. Es benennt nur keinen Zustand mehr. */
.product-card.selected { border-color: var(--ms-success); }

/* ── DIE GEWAEHLTE KARTE TRITT HERVOR (2026-09-16) ──
   Betreiber: „mit einem leichten Schatten anheben, sodass diese optisch
   hervortreten." `--ms-card-shadow` ist der Schatten, den eine Karte des
   Systems traegt — derselbe, den hier bisher nur das Ueberfahren gab.

   OHNE `translateY`, anders als beim Ueberfahren. Die Verschiebung um 2px ist
   dort richtig, weil sie kommt und geht; als DAUERZUSTAND stuende eine
   gewaehlte Karte zwei Pixel hoeher als ihre Nachbarn — und genau das
   Gegenteil war die Weisung von vorgestern („alle Bedienelemente auf der
   identischen Hoehe"). Der Schatten allein hebt sie optisch; die Zeile bleibt
   eine Zeile.

   `--ms-card-shadow-hover` UND NICHT `--ms-card-shadow`, obwohl „leicht"
   gefragt war: nebeneinander gestellt und angesehen war der leichtere auf der
   weissen Karteninnenflaeche von einer nicht gewaehlten Karte kaum zu
   unterscheiden — er traegt dort, wo eine Karte auf dem grauen Grund der Seite
   liegt, nicht auf Weiss. Der zweite Wert ist immer noch ein weicher Schatten
   und kein Rahmen; er hebt die Karte sichtbar ab.

   UEBERFAHREN BLEIBT unterscheidbar: `.product-card:hover` gibt zusaetzlich
   `translateY(-2px)`, und das ueberschreibt der Zustand nicht. Eine gewaehlte
   Karte hebt sich unter dem Zeiger also weiter an — nur eben aus einer schon
   erhoehten Lage. */
.product-card.checked,
.product-card.selected { box-shadow: var(--ms-card-shadow-hover); }
.product-card.disabled { cursor: not-allowed; }
.product-card.disabled:hover { transform: none; box-shadow: none; }
/* Die Deckkraft liegt auf dem Karteninhalt, NICHT auf der ganzen Karte —
   sonst blasst sie die daruebergelegte Meldung „Nicht verfuegbar“ mit aus und
   macht sie unlesbar. Aktuell abgeschaltet: die Meldung legt ohnehin einen
   weissen Schleier ueber die Karte, zweimal Ausblassen war zu viel.
   .product-card.disabled > .card { opacity: .45; } */
/* Nicht verfuegbare Artikel sind nicht bedienbar: weder Kartenklick
   (Einfachauswahl) noch Stepper oder Loeschen-Zeile (Mengenauswahl). Der
   Info-Knopf liegt ausserhalb von `.card` und bleibt erreichbar — die Details
   darf man auch dann ansehen, wenn gerade nichts verfuegbar ist. */
.product-card.disabled > .card { pointer-events: none; }
/* `.product-check` ist entfallen (2026-09-16): das runde Haekchen des
   Prototyps, 28x28 hart gesetzt und in --ms-teal — ohne Verwender in
   templates/ und assets/. Es war der Vorlaeufer des `.checkmark`, den die
   Controller erzeugten und der am selben Tag mit weggefallen ist. */
/* `contain` statt `cover`: die Artikel sollen vollstaendig zu sehen sein. Die
   Fotos sind teils hochkant (Rollups), teils quer (Messestaende) — beschneiden
   wuerde je nach Artikel etwas anderes abschneiden. */
/* Bildflaeche = Kartenflaeche, dieselbe Regel wie bei .prod-card-img: derselbe
   Artikel darf in der Artikelauswahl nicht auf Grau und in der Uebersicht auf
   Weiss stehen. */
/* Feste 200px waren dasselbe in anderer Schreibweise: bei einer Karte um die
   260px ergab das rund 4:3. Jetzt das Quadrat der Bildkette. */
.product-thumb { aspect-ratio: 1 / 1; display: flex; align-items: center; justify-content: center; overflow: hidden; background: var(--ms-card-bg); }
.product-thumb img { width: 100%; height: 100%; object-fit: contain; }
.product-body { padding: var(--ms-space-3) var(--ms-space-5) var(--ms-space-2); }
/* Zwei Zeilen fest reserviert und laengere Namen abgeschnitten: sonst waeren
   Karten mit einzeiligem Namen eine Zeile flacher als ihre Nachbarn. */
.product-name {
  font-size: var(--ms-text-body); font-weight: var(--ms-weight-semibold);
  letter-spacing: -0.01em; line-height: 1.35; min-height: 2.7em;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.product-desc { font-size: var(--ms-text-small); color: var(--ms-text-secondary); }
.product-count {
  margin-top: var(--ms-space-5); padding: var(--ms-space-3) var(--ms-space-5);
  border-radius: var(--ms-radius-md);
  background: rgba(var(--ms-accent-rgb), 0.06);
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-medium); color: var(--ms-accent);
  display: flex; align-items: center; gap: var(--ms-space-2);
}
/* Ebenen-Ueberschrift der Produktauswahl (Pflicht/optional je Ebene) —
   eigene Ergaenzung, im Prototyp nicht vorhanden. */
/* ── Eine Artikelgruppe in Schritt 3 ──
   SICHTBAR GETRENNT (2026-09-16, Weisung des Betreibers: die Gruppen sollen
   „wirklich wahrnehmbar optisch strukturiert" sein). Vorher trennte sie nur
   ein Abstand nach unten, und zwei Dinge gingen dabei schief:

   1. `font-size: 1.05rem` war HANDGESETZT — ein Wert, den die Skala nicht
      kennt. Jetzt --ms-text-h4, die Stufe fuer eine Abschnittsueberschrift.
   2. DER ABSTAND ZU DEN KARTEN HING AM HINWEIS. Er stand an
      `.product-level-hint`; eine Gruppe ohne Hinweis („Wandkonstruktion")
      hatte damit vier Pixel zwischen Ueberschrift und Kartenreihe — die
      Ueberschrift klebte an der Karte. So gemeldet. Der Abstand gehoert an
      den KOPF der Gruppe, der immer da ist.

   Die Linie ist `--ms-border-divider`: sie gliedert, sie rahmt nicht. Die
   erste Gruppe bekommt keine — ueber ihr steht die Kartenueberschrift. */
.product-level { margin-bottom: var(--ms-space-6); }
.product-level + .product-level {
  padding-top: var(--ms-space-6);
  border-top: 1px solid var(--ms-border-divider);
}
.product-level-head { margin-bottom: var(--ms-space-5); }
.product-level-title { font-size: var(--ms-text-h4); font-weight: var(--ms-weight-semibold); margin: 0; }
.product-level-hint { font-size: var(--ms-text-small); color: var(--ms-text-secondary); margin: var(--ms-space-1) 0 0; }
.product-level-hint.is-required { color: var(--ms-error-text); font-weight: var(--ms-weight-medium); }

/* --- Vorgaben der Auswahl-Controller ---
   `select-card_controller.js` und `checkbox-card_controller.js` erzeugen
   .checkmark / .disabled-overlay selbst und schalten .checked / .disabled /
   .interactive am Wrapper um. Die Klassennamen sind damit fest verdrahtet —
   der Wrapper traegt deshalb ZUSAETZLICH .product-card, damit er die
   Prototyp-Optik bekommt, ohne dass die Controller geaendert werden muessen.
   `select-card` braucht ausserdem ein Kind mit .card als Klickflaeche; dessen
   eigene Kartenoptik wird hier neutralisiert, sonst laege eine Karte in der
   Karte. */
.product-card.card-wrapper { position: relative; }
.product-card.interactive { cursor: pointer; }
.product-card.disabled { cursor: not-allowed; }
.product-card > .card {
  background: none; border: 0; border-radius: 0; box-shadow: none;
  /* Auch den Innenabstand zuruecknehmen: die aeussere .product-card polstert
     bereits, sonst stuende die Polsterung doppelt. */
  padding: 0;
  height: 100%; display: flex; flex-direction: column;
}
/* Derselbe Ton wie oben und wie am geprueften Feld — Begruendung an
   `.product-card.selected`. Die Umrandung traegt die Auswahl allein, seit das
   Abzeichen entfallen ist; daneben steht als farbunabhaengiges Merkmal die
   Loeschen-Zeile, die erst bei einer Auswahl erscheint (WCAG 1.4.1). */
.product-card.checked { border-color: var(--ms-success); }
.product-card > .disabled-overlay { display: none; position: absolute; inset: 0; pointer-events: none; }
.product-card.disabled > .disabled-overlay {
  display: flex; align-items: center; justify-content: center;
  padding: var(--ms-space-5);
  background: rgba(var(--ms-surface-rgb), .75);
  border-radius: var(--ms-radius-lg);
}
/* GELB STATT ROT (2026-09-18, Weisung des Betreibers). „Nicht verfuegbar" ist
   kein Fehler des Kunden und keine Stoerung — es ist eine Auskunft. Rot stand
   hier seit jeher; es passt nicht mehr zu der Sprache, die der Buchungskalender
   spricht (dort ist Gelb „belegt", und Rot kommt bewusst nicht vor). */
.product-card.disabled > .disabled-overlay h3 {
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold);
  color: var(--ms-warning-text); text-align: center; margin: 0;
}
/* DAS GRUENE KREISSYMBOL OBEN RECHTS IST ENTFALLEN (2026-09-16).
   Es stand in keiner Regel des Systems: 28x28 hart gesetzt, Flaeche
   --ms-teal mit weissem Zeichen darauf. Der Markenton ist in tokens.css
   ausdruecklich als FLAECHE hinter heller Schrift zugelassen (siehe
   .btn-teal) — ein Symbol, das Bedeutung traegt, faellt aber unter
   WCAG 1.4.11 und braucht 3:1; Weiss auf #00B4A0 liegt darunter.

   Die Auswahl sagen jetzt zwei Dinge, die es ohnehin gab: der Rahmen der
   Karte und die Fusszeile — bei der Einfachauswahl ein Abzeichen
   („Ausgewaehlt"), bei der Mengenauswahl die Zahl selbst plus die
   Loeschen-Zeile. Damit traegt nicht die Farbe allein die Aussage.
   Die Controller erzeugen das Element nicht mehr. */
/* Der Info-Knopf liegt ueber der Karte (Controller-Markup). */
.product-card > .info-button {
  position: absolute; z-index: 4;
  width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; border-radius: 50%;
  font-size: var(--ms-glyph-xs); cursor: pointer;
}
.product-card > .info-button { top: var(--ms-space-2); left: var(--ms-space-2); display: none; background: var(--ms-bg-dark); color: var(--ms-text-primary-dark); }
.product-card:hover > .info-button { display: inline-flex; }
@media (hover: none) { .product-card > .info-button { display: inline-flex; } }
/* Mengenauswahl unten in der Karte (nur bei Auswahl mit Menge).
   Statt eines Dialogs mit langer Zahlenliste steht die Menge direkt in der
   Karte: minus / Zahl / plus, darunter Verpackungseinheit und Verfuegbarkeit
   sowie eine Loeschen-Zeile. */
/* Beide Auswahlarten teilen sich dieselbe Fusszeilenhoehe: die Mengenauswahl
   braucht Stepper, Hinweis und Loeschen-Zeile, die Einfachauswahl nur einen
   Satz. Ohne die Mindesthoehe waeren die Karten der beiden Ebenen
   unterschiedlich hoch. */
.product-card .card-footer {
  margin-top: auto;
  padding: 0 var(--ms-space-5) var(--ms-space-4);
  min-height: 110px;
  display: flex; flex-direction: column; justify-content: center;
}

/* ── ALLE BEDIENELEMENTE EINER REIHE AUF EINER HOEHE (2026-09-16) ──
   Gemeldet: die Mengenfelder lagen unterschiedlich hoch. Sie waren gleich
   GROSS, standen aber verschieden weit oben — und das lag an der Fusszeile.

   Die Karten einer Reihe sind gleich hoch (Raster), die Fusszeile haengt
   unten (`margin-top: auto`) und zentriert ihren Inhalt in mindestens 110px.
   Ihr Inhalt ist aber verschieden hoch: der Hinweis ist ein- oder zweizeilig
   (mit Verpackungseinheit), und die Loeschen-Zeile steht nur bei gewaehlten
   Karten sichtbar da. Zwei Zeilen Hinweis schoben den Stepper um eine
   Zeilenhoehe nach oben — genau der Versatz im Bild.

   `subgrid` loest das an der Wurzel: die Karte spannt fuenf Zeilen des
   Rasters, und jede Zeile — Bild, Name, Menge, Hinweis, Loeschen — ist in
   der GANZEN Rasterzeile gleich hoch. Der Stepper steht damit in jeder Karte
   auf demselben Pixel, egal wie lang der Hinweis daneben ist. Feste Hoehen
   haetten dasselbe versprochen und beim dritten Hinweiszeile gebrochen.

   NUR AUF DER KUNDENSEITE (`.product-grid > .product-card`): im Editor liegt
   zwischen Raster und Karte die `.stage-cell`, die ihr eigenes `subgrid`
   fuehrt (siehe `_area.html.twig`).

   Leere Zeilen kosten nichts: bei der Einfachauswahl bleibt die fuenfte Zeile
   in der ganzen Gruppe leer und faellt auf null zusammen. */
/* `row-gap: 0` AN JEDER STUFE. Ein `subgrid` erbt die Spuren des Rasters,
   benutzt aber seinen EIGENEN Abstand — ohne Angabe also den des Elternteils.
   Das sind hier 24px zwischen den Karten, und die standen damit ploetzlich
   auch zwischen Bild, Name, Menge, Hinweis und Loeschen: vier zusaetzliche
   Luecken, 96px je Karte (gemessen 558px Kartenhoehe). Die Abstaende INNERHALB
   der Karte bringen die Bauteile selbst mit (Polsterung des Rumpfes, Rand an
   Hinweis und Loeschen-Zeile) — das Raster soll sie nicht verdoppeln.
   Zwischen den KartenREIHEN bleiben die 24px: die stehen an `.product-grid`
   selbst und werden davon nicht beruehrt. */
.product-grid > .product-card {
  grid-row: span 5;
  display: grid;
  grid-template-rows: subgrid;
  row-gap: 0;
}
.product-grid > .product-card > .card {
  grid-row: 1 / -1;
  display: grid;
  grid-template-rows: subgrid;
  row-gap: 0;
  height: auto;
}
.product-grid > .product-card > .card > .card-footer {
  grid-row: 3 / -1;
  display: grid;
  grid-template-rows: subgrid;
  row-gap: 0;
  align-items: center;
  margin-top: 0;
  min-height: 0;
}
/* DIE MENGE IST EIN BEDIENELEMENT, also sieht sie aus wie eines (2026-09-16).
   Vorher: 1px Rahmen in --ms-border-light (die Kante einer FLAECHE, nicht
   eines Feldes), Knoepfe ohne Grund und ohne Kante, 32x34px und `.7rem` —
   alles handgesetzt. Neben einer Karte, die man anklickt, las sich das als
   Anzeige und nicht als Eingabe; genau so wurde es gemeldet.
   Jetzt die Masse des Systems: Bedienhoehe, Feldrahmen, Feldradius,
   Feldschrift — und die beiden Knoepfe mit eigener Flaeche und Trennkante,
   damit sichtbar ist, dass man sie druecken kann. */
.product-card .qty-stepper {
  display: flex; align-items: stretch;
  height: var(--ms-control-h);
  border: var(--ms-control-border-w) solid var(--ms-border-control);
  border-radius: var(--ms-control-radius);
  background: var(--ms-control-bg); overflow: hidden;
}
.product-card .qty-stepper input {
  flex: 1 1 auto; width: 100%; min-width: 0;
  padding: 0;
  border: none; outline: none; background: none;
  font-size: var(--ms-control-font); font-weight: var(--ms-weight-medium);
  text-align: center; color: var(--ms-text-primary);
}
.product-card .qty-step {
  flex: 0 0 auto; width: var(--ms-control-h); padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: var(--ms-bg-secondary); cursor: pointer;
  color: var(--ms-text-primary); font-size: var(--ms-control-font); line-height: 1;
  transition: background var(--ms-transition-fast), color var(--ms-transition-fast);
}
/* Die Kante zwischen Knopf und Zahl — dieselbe Linie, die auch Tabellenzeilen
   trennt. Ohne sie verschwimmt der Knopf mit dem Feld. */
.product-card .qty-step:first-child { border-right: 1px solid var(--ms-border-divider); }
.product-card .qty-step:last-child { border-left: 1px solid var(--ms-border-divider); }
.product-card .qty-step:hover:not(:disabled) { background: var(--ms-border-light); }
.product-card .qty-step:focus-visible { outline: 2px solid var(--ms-accent); outline-offset: -2px; }
/* Ausgegraut statt ausgeblendet: ein verschwindender Knopf wuerde die beiden
   anderen Bedienelemente bei jedem Schritt verschieben. */
.product-card .qty-step:disabled { opacity: .3; cursor: default; }
.product-card .qty-hint {
  margin: var(--ms-space-1) 0 0;
  font-size: var(--ms-text-caption); color: var(--ms-text-secondary); text-align: center;
  min-height: 1.2em;
}
/* Die Loeschen-Zeile bleibt auch ohne Auswahl im Layout stehen (nur
   unsichtbar) — sonst waeren die Karten einer Reihe unterschiedlich hoch,
   sobald eine davon eine Menge traegt. */
.product-card .qty-remove {
  visibility: hidden;
  display: flex; align-items: center; justify-content: center; gap: var(--ms-space-2);
  width: 100%; margin-top: var(--ms-space-1); padding: var(--ms-space-1) 0;
  border: none; background: none; cursor: pointer;
  color: var(--ms-error-text); font-size: var(--ms-text-caption);
}
.product-card .qty-remove:hover { text-decoration: underline; }
.product-card.checked .qty-remove { visibility: visible; }
/* Einfachauswahl: dieselbe Fusszeile, aber statt Mengenfeld ein Satz, der den
   Zustand benennt. */
/* EIN KASTEN FUER BEIDE ZUSTAENDE. „Zum Auswaehlen anklicken" ist blosser
   Text, „Ausgewaehlt" ein Abzeichen mit Polsterung — das Abzeichen ist damit
   ein paar Pixel hoeher, und die Zeile sass in einer gewaehlten Karte anders
   als in ihren Nachbarn. Eine feste Zeilenhoehe aus dem Massystem
   (--ms-btn-xs-height) plus mittige Ausrichtung haelt beide auf derselben
   Linie. */
.product-select-hint {
  margin: 0; text-align: center;
  font-size: var(--ms-text-small); color: var(--ms-text-secondary);
  display: flex; align-items: center; justify-content: center;
  min-height: var(--ms-btn-xs-height);
}
/* Bei einer gewaehlten Karte faellt die Aufforderung weg — „Zum Auswaehlen
   anklicken" waere dort falsch. Den Zustand sagen Umrandung und
   Loeschen-Zeile. */
.product-card.checked .product-select-hint .hint-idle { display: none; }
.ms-dropdown-item { cursor: pointer; }
.ms-dropdown-item.selected { background: rgba(var(--ms-accent-rgb),.08); color: var(--ms-accent); font-weight: var(--ms-weight-medium); }

/* Zusammenfassung (Schritt 5) */
/* ── Zusammenfassung (Assistent, Schritt 8) ──
   RUBRIKEN UNTEREINANDER, jede mit Trennlinie (2026-09-16, Weisung des
   Betreibers). `.summary-grid` stellte Lieferumfang, Veranstaltung und
   Lieferung als drei gleich breite Spalten nebeneinander: der Lieferumfang
   wuchs mit jeder Position nach unten und liess neben sich zwei halbleere
   Spalten stehen. Die Klasse ist entfallen, `.summary-row`/-label/-value mit
   ihr — die Paare zeichnet jetzt `.pair-list--stacked` (data.css), das Bauteil
   des Systems fuer genau diesen Fall (Designsystem 6c).

   DIE LINIE IST `--ms-border-divider`: sie gliedert, sie umrandet nicht
   (designsystem.md, Begruendung am Token). Der erste Abschnitt bekommt keine —
   darueber steht schon die Kartenueberschrift. */
.summary-sections { display: flex; flex-direction: column; }
.summary-section + .summary-section {
  margin-top: var(--ms-space-6);
  padding-top: var(--ms-space-6);
  border-top: 1px solid var(--ms-border-divider);
}
.summary-block-title { font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold); color: var(--ms-text-secondary); margin: 0 0 var(--ms-space-4); }
/* Der Lieferumfang bekommt die volle Breite und fuellt sie mit Kacheln —
   dieselbe Rasterregel wie die Artikelauswahl in Schritt 3
   (`.product-grid`), damit eine Bestellung mit zwoelf Positionen nicht
   zwoelf Zeilen lang wird. */
.summary-items {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: var(--ms-space-3);
}
/* Die Paare eines Abschnitts NEBENEINANDER, solange Platz ist. `.pair-list`
   ist eine Spalte (data.css) — in einem Abschnitt ueber die volle Breite
   stuenden Name, Datum und Kontakt sonst dreizeilig untereinander, mit sehr
   viel Weiss rechts. `auto-fit` statt drei festen Spalten: eine Rubrik hat
   mal drei Paare, mal zwei, und auf dem Telefon eines.

   HIER UND NICHT ueber `.form-grid` am Element: `.pair-list` steht in data.css
   und damit SPAETER als forms.css — bei gleicher Spezifitaet haette seine
   Spaltenrichtung das Raster ueberstimmt, und die Paare waeren doch wieder
   untereinander gestanden. Gemessen. */
.summary-section .pair-list {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--ms-space-5);
}
.summary-notes { font-size: var(--ms-text-small); margin: 0; }
.summary-product {
  display: flex; align-items: center; gap: var(--ms-space-4);
  padding: var(--ms-space-4); border-radius: var(--ms-radius-md);
  border: 1px solid var(--ms-border-light);
}
.summary-product-icon {
  width: 56px; height: 56px; border-radius: var(--ms-radius-sm);
  background: rgba(var(--ms-accent-rgb), 0.06);
  display: flex; align-items: center; justify-content: center;
  color: var(--ms-accent); flex-shrink: 0; overflow: hidden;
}
.summary-product-icon img { width: 100%; height: 100%; object-fit: contain; }
.summary-product-name { font-weight: var(--ms-weight-semibold); font-size: var(--ms-text-small); }
.summary-product-meta { font-size: var(--ms-text-caption); color: var(--ms-text-secondary); }

@media (max-width: 900px) {
  .categories-grid { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 620px) {
  .categories-grid { grid-template-columns: 1fr; }
  .wizard-card-inner { padding: var(--ms-space-5); }
}

/* ==========================================================================
   Produktdetail-Fenster (templates/v2/productAssembly/detail.html.twig)
   ========================================================================== */
.pdetail-gallery { display: flex; gap: var(--ms-space-4); align-items: flex-start; }
/* Miniaturen als schmale Spalte links, wie im alten Fenster. Sie scrollen
   mit, wenn ein Produkt viele Bilder hat, statt das Hauptbild zu quetschen. */
.pdetail-thumbs {
  flex: 0 0 76px; display: flex; flex-direction: column; gap: var(--ms-space-2);
  /* Dieselbe Hoehe wie das Hauptbild daneben — sonst endet die Spalte der
     Miniaturen frueher und die Reihe sieht unausgerichtet aus. Die Zahl folgt
     deshalb der `max-height` von `.main-image` und wird mit ihr geaendert
     (2026-09-18: 420 -> 512). */
  max-height: 512px; overflow-y: auto;
}
.pdetail-thumbs .img-thumbnail {
  width: 100%; aspect-ratio: 1; object-fit: contain;
  padding: var(--ms-space-1); cursor: pointer;
  background: var(--ms-bg-primary);
  border: 1px solid var(--ms-border-light); border-radius: var(--ms-radius-sm);
  transition: border-color var(--ms-transition-fast);
}
.pdetail-thumbs .img-thumbnail:hover { border-color: var(--ms-accent); }
.pdetail-main {
  flex: 1 1 auto; min-width: 0;
  display: flex; align-items: center; justify-content: center;
  /* Wie .prod-card-img und .product-thumb: die Flaeche hinter dem Bild ist die
     des Dialogs (.modal-content nutzt denselben Token). */
  background: var(--ms-card-bg); border-radius: var(--ms-radius-md);
  padding: var(--ms-space-3);
}
/* DAS BILD FUELLT SEINE FLAECHE (2026-09-18). Vorher stand es mit
   `max-height: 320px` mittig in einem viel breiteren Feld — zwischen den
   Miniaturen und dem Bild klaffte dadurch eine Luecke, die niemand gesetzt
   hatte (gemeldet vom Betreiber). Jetzt nimmt es die Breite, die da ist, und
   die Hoehe ist grosszuegiger. `object-fit: contain` haelt das
   Seitenverhaeltnis; ein Hochformat wird also nicht gestreckt.

   512 UND NICHT MEHR (2026-09-18, nachgerechnet auf Nachfrage des Betreibers:
   „koennte man die ggf. noch etwas groesser darstellen?"). Vorher 420 — die
   Zahl war gegriffen, und sie liess Platz liegen:

       800 Dialog (.modal-dialog-lg)
     −  64 Innenabstand des Rumpfs (2 x --ms-space-6)
     −  76 Miniaturenspalte
     −  16 Spaltenabstand (--ms-space-4)
     −  24 Innenabstand von .pdetail-main (2 x --ms-space-3)
     = 620 verfuegbar

   Die Bilder sind quadratisch (ImagePipeline), `contain` zieht die Breite
   also mit der Hoehe: bei 420 stand ein 420x420-Bild in einem 620 breiten
   Feld, mit je 100 px Leerraum daneben.

   DIE GRENZE IST NICHT DER PLATZ, SONDERN DIE DATEI. Ausgeliefert wird die
   grosse Stufe mit 1024 px. Ein Bildschirm doppelter Punktdichte braucht
   2 x 512 = 1024 echte Punkte — bei 512 sitzt die Darstellung also GENAU auf
   der gespeicherten Aufloesung. Bei 620 waeren es 1240 gegen 1024 vorhandene,
   und die Galerie waere auf Retina wieder weich. Ein breiterer Dialog hilft
   dagegen nicht; die Zahl haengt an der Datei.

   Umgekehrt haengt die Mindestkante des Uploads an DIESER Zahl —
   ImagePipeline::MIN_EDGE ist ihr Doppeltes, und dort steht die Regel. Wer
   hier etwas aendert, liest sie zuerst. */
.pdetail-main .main-image { width: 100%; max-height: 512px; object-fit: contain; }
/* Der Platzhalter nimmt dieselbe Flaeche ein wie ein Foto hier: quadratisch,
   hoechstens so gross wie das Bild darf (Begruendung der 512 darueber). */
.pdetail-main .bild-platzhalter { max-width: 512px; }
/* SCHRIFTGROESSEN DES PRODUKTFENSTERS (Weisung des Betreibers, 2026-10-08).
   Bis dahin stand der INHALT des Fensters — Beschreibung, Bestand,
   Set-Inhalt — in 14px Grau (`--ms-text-small`, `--ms-text-secondary`) und
   die Abschnittstitel in 12px (`--ms-text-caption`). Damit war der Inhalt
   kleiner als Felder und Knoepfe im selben Fenster (16px,
   `--ms-control-font`) und doppelt gedaempft — kleiner UND heller —, obwohl
   er nichts Nachgeordnetes ist (Designsystem, Regel 8).

   Jetzt: Inhalt `--ms-text-md` (16px FEST) in der Textfarbe, Titel
   `--ms-text-small` (14px). FEST und nicht `--ms-text-body`: das waechst bis
   18px mit, und dann stuende der Text im Fenster groesser als die Knoepfe
   darunter. Bestellzeilen und Hinweise bleiben 14px — Datenzeilen und
   Hilfetexte, fuer die das System diese Stufe vorsieht.

   NUR TOKENS (Weisung des Betreibers): wer hier eine Groesse aendert, waehlt
   eine andere Stufe der Skala in tokens.css und setzt keinen Wert von Hand. */
.pdetail-description {
  margin-top: var(--ms-space-5);
  font-size: var(--ms-text-md); color: var(--ms-text-primary); line-height: 1.6;
}
.pdetail-description ul { margin: var(--ms-space-2) 0 0; padding-left: var(--ms-space-5); }
.pdetail-section-title {
  margin: var(--ms-space-6) 0 var(--ms-space-2);
  padding-top: var(--ms-space-4);
  border-top: 1px solid var(--ms-border-light);
  font-size: var(--ms-text-small); font-weight: var(--ms-weight-semibold);
  letter-spacing: 0.04em; color: var(--ms-text-secondary);
}
/* Stueckliste des Pakets: welcher Artikel in welcher Menge vorhanden ist.
   Stand vorher auf der Produktkarte. Dort ergab sie je Paket eine andere
   Laenge und machte die Karten unterschiedlich hoch; die Karte fuehrt jetzt
   den Verfuegbarkeitsbalken, der dieselbe Aussage in fester Hoehe trifft.
   Die Einzelmengen sind damit aber nicht verschwunden, sondern hierher
   gewandert — die Klassennamen sind bewusst dieselben geblieben. */
.prod-part-list {
  margin-top: var(--ms-space-2);
  font-size: var(--ms-text-md);
}
.prod-part-list__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--ms-space-3);
  padding: var(--ms-space-2) 0;
  border-bottom: 1px solid var(--ms-border-light);
}
.prod-part-list__row:last-child { border-bottom: 0; }
.prod-part-list__name {
  color: var(--ms-text-secondary);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.prod-part-list__qty {
  /* Gleiche Zifferbreiten, damit die Mengen untereinander fluchten. */
  font-variant-numeric: tabular-nums;
  font-weight: var(--ms-weight-medium);
  flex: none;
}
/* Bestand unter der hinterlegten Mindestmenge. */
.prod-part-list__qty.is-low { color: var(--ms-error-text); }

/* Vier Spalten: Termin, Veranstaltung, Besteller, Menge. Die Veranstaltung
   nimmt den Rest — sie ist das einzige Feld mit unbekannter Laenge. */
.pdetail-order {
  display: grid; grid-template-columns: auto minmax(0, 1fr) auto 3rem;
  gap: var(--ms-space-3); align-items: baseline;
  padding: var(--ms-space-2) 0;
  border-bottom: 1px solid var(--ms-border-light);
  font-size: var(--ms-text-small); color: var(--ms-text-secondary);
}
.pdetail-order:last-child { border-bottom: none; }
/* Farbe, Gewicht und Unterstreichung kommen aus `.text-link` (base.css) — hier
   stand eine eigene Fassung mit DAUERHAFTER Unterstreichung, die es im
   Designsystem nicht gibt. Geblieben ist nur, was diese Zelle betrifft. */
.pdetail-order-event { min-width: 0; }
/* NICHT FETT: in einer Liste aus lauter Mengen hebt ein halbfetter Schnitt
   nichts hervor (dieselbe Regel wie in der Schnellinfo, 2026-09-17). */
.pdetail-order-qty { text-align: right; color: var(--ms-text-secondary); }
@media (max-width: 640px) {
  .pdetail-gallery { flex-direction: column; }
  .pdetail-thumbs { flex: 0 0 auto; flex-direction: row; max-height: none; width: 100%; }
  .pdetail-thumbs .img-thumbnail { width: 64px; }
  .pdetail-order { grid-template-columns: 1fr auto; }
}

/* ==========================================================================
   Auswertung (templates/v2/reporting/*)

   HIER STEHT NICHTS MEHR — und das ist der Punkt (9. September 2026).

   Die Seite fuehrte bis heute rund 280 Zeilen eigenes CSS: ein zweites
   Kennzahlenband (.report-kpi-*), eine eigene Feldreihe mit nummerierten
   Kreisen (.report-controls/.report-field/.report-step), eigene Chips,
   eigene Symbolknoepfe, eigene Reiter, eine eigene Speicherleiste. Jedes
   dieser Bauteile gab es im System bereits; die Auswertung hatte sie nur
   unter anderem Namen nachgebaut und lief damit bei jeder Aenderung am
   System aus dem Tritt.

   Was wohin gewandert ist:
     .report-kpi-*        v2/admin/_kennzahlen.html.twig (+ `zahl`, `trend`)
     .report-controls/-field/-step
                          .form-grid .form-grid-3 + .form-label + .form-select
     .report-scope        .card-toolbar
     .report-chip         .badge .badge-link / .badge-neutral
     .report-note         .cell-meta
     .report-saved-*      .badge-link + .icon-action
     .report-icon-btn     .icon-action (+ --active/--danger, buttons.css)
     .report-dropped      .alert .alert-warning
     .report-empty        .cell-muted
     .report-chart-tabs   .nav-tabs .nav-link
     .report-chart-bars   .chart-* (data-display.css) — allgemeines Bauteil
     .report-sum          .table-total (data.css) — allgemeines Bauteil

   Zwei Regeln bleiben, weil sie ALLGEMEIN sind und nichts mit dieser Seite
   zu tun haben: der Abstand des Fortschrittsbalkens in einer Tabellenzelle
   und `.empty-note`.
   ========================================================================== */


/* Der Balken der Ergebniszeile steht UNTER der Beschriftung und nicht daneben:
   die erste Spalte traegt bei der Gruppierung nach Veranstaltung lange Namen,
   und ein Balken daneben haette sie auf ein Drittel gedrueckt. */
.table .progress-track { margin-top: var(--ms-space-1); }


/* Hinweis an der Stelle, an der sonst eine Liste stuende. Bewusst leise:
   „keine Eintraege" ist kein Fehler, den man rot melden muesste. Lag bis zum
   Adressbuch unbenutzt im Stylesheet (als .report-empty), obwohl mehrere
   Seiten den Fall haben — deshalb hier verallgemeinert. */
.empty-note { padding: var(--ms-space-5) 0; font-size: var(--ms-text-small); color: var(--ms-text-secondary); text-align: center; }

/* ==========================================================================
   Anmeldung, zweiter Faktor, Passwort-Ruecksetzung (templates/v2/login_layout)

   Kein Vorbild im Entwurf — er hat keine Anmeldeseite. Aufgebaut aus den
   Bauteilen des angemeldeten Bereichs, damit die Anmeldung schon die
   Oberflaeche zeigt, in die sie fuehrt.
   ========================================================================== */
.auth-page {
  min-height: 100vh;
  /* --ms-auth-bg ist hell identisch mit --ms-bg-secondary und dunkel eine
     eigene, kuehle Flaeche, auf der der Lichthof sichtbar wird. */
  background: var(--ms-auth-bg);
  display: flex; align-items: center; justify-content: center;
  padding: var(--ms-space-6) var(--ms-space-4);
}
/* Das Motiv steht neben dem Formular, nicht dahinter: die Person sitzt
   bildmittig, hinter der Karte waere ihr Gesicht verdeckt. Unter 960px bleibt
   die Seite die reine Karte — auf einem Telefon waere ein angeschnittenes
   Grossbild weder erkennbar noch die Ladezeit wert. Weil die Regel erst im
   Media-Query steht, laedt der Browser die Datei dort auch nicht. */
.auth-visual { display: none; }

@media (min-width: 960px) {
  .auth-page {
    display: grid;
    /* Untergrenze traegt die 420px-Karte plus Luft, Obergrenze verhindert,
       dass die Spalte auf sehr breiten Schirmen das Motiv erdrueckt. */
    grid-template-columns: clamp(520px, 46%, 680px) 1fr;
    align-items: stretch;
    padding: 0;
  }
  .auth-page > .auth-col { margin: auto; }
  .auth-visual {
    display: block;
    background-image: url("../../login_backdrop-zi9L-kd.jpg");
    background-size: cover;
    /* center haelt die Person im Bild, egal wie schmal die Spalte wird. */
    background-position: center;
    background-repeat: no-repeat;
  }
}

/* ── Lichthof auf der Formularseite, nur im Dunkelmodus ──
   Uebernommen aus myOFFICE (mo-login.css), aber deutlich zurueckhaltender:
   dort ist die Aurora die einzige Stimmung der Seite, hier steht daneben ein
   Foto. Sie darf deshalb nur Tiefe andeuten, nicht selbst auftreten.

   Nur dunkel, weil ein Lichthof auf hellem Grund nichts ergibt — hell bleibt
   die Formularseite die ruhige graue Flaeche. Und bewusst OHNE Animation:
   myOFFICE laesst den Hauptschein atmen, das ist dort das einzige bewegte
   Element. Neben einem Foto waere es eine Bewegung zu viel, und es erspart
   die Ausnahme fuer prefers-reduced-motion.

   Die Farbe steht als fester Wert und nicht als --ms-accent: der Akzent ist
   gesaettigtes Signalblau fuer Knoepfe. Als Lichthof wirkt er wie ein
   eingeschaltetes Licht statt wie Tiefe. */
@media (min-width: 960px) {
  :root[data-theme="dark"] .auth-page,
  :root:not([data-theme="light"]) .auth-page {
    position: relative;
    overflow: hidden;
  }
  :root[data-theme="dark"] .auth-page::before,
  :root[data-theme="dark"] .auth-page::after,
  :root:not([data-theme="light"]) .auth-page::before,
  :root:not([data-theme="light"]) .auth-page::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    pointer-events: none;
  }
  /* Hauptschein hinter der Karte. */
  :root[data-theme="dark"] .auth-page::before,
  :root:not([data-theme="light"]) .auth-page::before {
    top: 48%; left: 22%;
    width: 58rem; height: 58rem;
    transform: translate(-50%, -50%);
    background: radial-gradient(circle,
      rgba(var(--ms-auth-glow-rgb), 0.16) 0%,
      rgba(var(--ms-auth-glow-rgb), 0.06) 38%,
      transparent 66%);
    filter: blur(64px);
  }
  /* Nebenakzent oben links, noch schwaecher. */
  :root[data-theme="dark"] .auth-page::after,
  :root:not([data-theme="light"]) .auth-page::after {
    top: -9rem; left: -7rem;
    width: 26rem; height: 26rem;
    background: radial-gradient(circle,
      rgba(var(--ms-auth-glow-rgb), 0.14) 0%,
      transparent 70%);
    filter: blur(70px);
  }
  /* Inhalt und Foto ueber den Deko-Ebenen. */
  .auth-page > .auth-col,
  .auth-page > .auth-visual { position: relative; z-index: 1; }
}
.auth-card {
  width: 100%;
  /* Enger als der Textbereich der Seiten: ein Anmeldeformular hat zwei Felder,
     eine breite Karte laesst sie verloren wirken. */
  max-width: 420px;
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  padding: var(--ms-space-8) var(--ms-space-6);
}
/* Markenzeile ueber der Karte: Logo und Produktname. Ersetzt das Logo IN der
   Karte — dort stand es ohne Namen, und „wo bin ich" beantwortet ein Logo
   allein nicht zuverlaessig. */
.auth-col { width: 100%; max-width: 420px; }
.auth-brand {
  display: flex; align-items: center; gap: var(--ms-space-3);
  margin-bottom: var(--ms-space-5);
  text-decoration: none;
}
.auth-brand img { height: 32px; width: auto; display: block; }
.auth-brand-name {
  /* h3 und nicht h2: der Name soll die Ueberschrift der Karte anfuehren,
     nicht mit ihr konkurrieren. */
  font-size: var(--ms-text-h3);
  font-weight: var(--ms-weight-semibold);
  letter-spacing: -0.02em;
  color: var(--ms-text-primary);
  line-height: 1.1;
}
.auth-title {
  /* Nicht --ms-text-h1: die fluide Skala geht bis 3,75rem und waere in einer
     420px breiten Karte ueberdimensioniert. */
  font-size: var(--ms-text-h4);
  font-weight: var(--ms-weight-semibold);
  letter-spacing: -0.01em;
  margin-bottom: var(--ms-space-2);
}
.auth-lead {
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
  line-height: 1.5;
  margin-bottom: var(--ms-space-5);
}
.auth-form { display: flex; flex-direction: column; gap: var(--ms-space-4); }
/* Der Hauptknopf fuellt die Karte: er ist die einzige Aktion, und eine
   angeschnittene Breite waere hier eine willkuerliche Entscheidung.
   Hoehe wie die Felder darueber (designsystem.md, Regel 7) — der senkrechte
   Innenabstand muss dafuer weichen, sonst addiert er sich. */
.auth-form .btn-primary {
  width: 100%;
  height: var(--ms-control-h);
  padding-top: 0;
  padding-bottom: 0;
}
.auth-links {
  display: flex; flex-wrap: wrap; gap: var(--ms-space-4);
  margin-top: var(--ms-space-5);
  padding-top: var(--ms-space-4);
  border-top: 1px solid var(--ms-border-light);
}
/* Hinweis nach einer Aktion (Mail versandt, Sperre aktiv). Neutral gehalten:
   die Meldungen sind teils gute, teils schlechte Nachrichten. */
.auth-note {
  margin-bottom: var(--ms-space-4);
  padding: var(--ms-space-3) var(--ms-space-4);
  border-radius: var(--ms-radius-sm);
  background: var(--ms-bg-secondary);
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
  line-height: 1.5;
}
.auth-note--error {
  background: var(--ms-error-muted);
  color: var(--ms-error-text);
}
/* Einmalkennzahl: mittig, weit gesperrt, gleiche Zifferbreiten — sechs Ziffern
   liest man als Gruppe, nicht als Wort. */
.auth-code {
  text-align: center;
  letter-spacing: 0.4em;
  font-size: var(--ms-text-h4);
  font-variant-numeric: tabular-nums;
}

/* ── Adressbuch ──
   Der Entwurf hat keine Adressverwaltung; das Bauteil folgt deshalb der
   Kartenform der Produktuebersicht, nur ohne Bild. Eine Adresse ist ein kurzer,
   fester Block — Karten stellen sie nebeneinander vergleichbar dar, waehrend
   eine Tabelle fuer fuenf Zeilen je Eintrag zu viele Spalten braeuchte. */
.addr-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: var(--ms-space-5); }
.addr-card {
  background: var(--ms-card-bg);
  border-radius: var(--ms-radius-lg);
  box-shadow: var(--ms-card-shadow);
  padding: var(--ms-space-5);
  /* Spalte mit `margin-top: auto` auf der Aktionszeile: das Raster zieht alle
     Karten einer Reihe gleich hoch, und ohne die Spalte stehen die Knoepfe je
     nach Zeilenzahl der Adresse auf anderer Hoehe. Gleiche Ueberlegung wie bei
     .prod-card. */
  display: flex;
  flex-direction: column;
}
.addr-company {
  /* Gekuerzt und auf zwei Zeilen reserviert, wie der Kartentitel der
     Produktuebersicht — und aus demselben Token. Firmennamen von
     Veranstaltungsorten werden lang („Messe Frankfurt Venue GmbH — Halle 4.2,
     Aufbautor Ost"); ohne Kuerzung waechst die Karte, mit fester Hoehe stehen
     die Anschriften aller Karten einer Reihe auf einer Linie. Den vollen Namen
     haengt der text-overflow-Controller als Sprechblase an, aber nur dort, wo
     wirklich gekuerzt wurde. */
  --ms-clamp: var(--ms-clamp-card-title);
  --ms-clamp-lh: 1.3;
  /* `--ms-text-md` (16px) statt des handgesetzten `1.05rem` (16,8px) —
     2026-09-17. Genau der Wert, den der Kommentar an `.product-level-head`
     schon einmal als „HANDGESETZT — ein Wert, den die Skala nicht kennt"
     beanstandet und dort ersetzt hat; hier war er stehen geblieben. */
  font-size: var(--ms-text-md);
  font-weight: var(--ms-weight-semibold);
  color: var(--ms-text-primary);
  margin-bottom: var(--ms-space-2);
  letter-spacing: -0.01em;
}
/* Anschrift als Adressblock: eigene Zeilen, kein Fliesstext. Der etwas
   groessere Zeilenabstand trennt die Zeilen deutlich genug, dass Strasse,
   Postleitzahl und Ort auch ohne Beschriftungen lesbar bleiben. */
.addr-lines {
  font-size: var(--ms-text-small);
  color: var(--ms-text-secondary);
  line-height: 1.7;
  font-style: normal;
}
.addr-tags { display: flex; flex-wrap: wrap; gap: var(--ms-space-2); margin-top: var(--ms-space-3); }
.addr-actions {
  display: flex;
  align-items: center;
  gap: var(--ms-space-3);
  margin-top: auto;
  padding-top: var(--ms-space-4);
}

/* ── Notizverlauf der Bestellung (OrderNotesThread) ──
   Jeder Eintrag eine Karte in der Karte: Kopfzeile mit Verfasser, Zeit und
   den zwei Symbolknoepfen aus der Skala, darunter der Text. Eintraege der
   Verwaltung tragen einen Akzentrand links, die des Kunden keinen — so sieht
   man beim Ueberfliegen, wer spricht, ohne jeden Kopf zu lesen. Die
   angeheftete Anmerkung aus dem Assistenten ist gedaempft hinterlegt: sie
   gehoert zur Bestellung, nicht zum Gespraech. */
.notes-thread { display: flex; flex-direction: column; gap: var(--ms-space-3); }
.note {
  padding: var(--ms-space-4);
  border: 1px solid var(--ms-border-light);
  border-radius: var(--ms-radius-md);
  background: var(--ms-bg-primary);
}
/* `.note--admin` IST ENTFALLEN (2026-09-17). Der Akzentrand links sagte „von
   einem Verwaltungsnutzer" — allein ueber Farbe, ohne ein Wort irgendwo auf
   der Seite. An seiner Stelle steht jetzt ein Abzeichen, das etwas sagt, was
   der Name daneben NICHT sagt: dass der Eintrag neu ist. */
.note--pinned { background: var(--ms-bg-secondary); }
/* ── Eine ungelesene Notiz (2026-09-17) ──
   Weisung des Betreibers: „durch einen hellblauen Fond, also einen ganz zarten
   Farbton, farblich markieren."

   `rgba(--ms-accent-rgb, --ms-tint)` ist im System DIE getoente Akzentflaeche.
   Sie traegt schon `.alert-info`, `.badge-info`, den aktiven Eintrag der
   Schnellsuche und den gesetzten Statusschalter — es ist also keine neue
   Farbe, sondern die vorhandene an einer weiteren Stelle.

   UND SIE IST DUNKELMODUSFEST, was ein fester Hexwert nicht waere: `--ms-tint`
   springt von 0,08 auf 0,20, weil acht Prozent Farbschleier ueber `#1C1C1E`
   praktisch unsichtbar sind (tokens.css, dunkle Fassung). Ein zarter Ton ueber
   Weiss und ein erkennbarer ueber Dunkelgrau sind nicht derselbe Zahlenwert.

   DIE FARBE IST NICHT DAS EINZIGE MITTEL — daneben steht das Abzeichen „Neu".
   Genau daran ist der frueher hier stehende blaue Rand gescheitert (WCAG
   1.4.1): er sagte etwas, das man nur sehen und nirgends lesen konnte. */
.note.is-new { background: rgba(var(--ms-accent-rgb), var(--ms-tint)); }
.note-head {
  display: flex; align-items: center; gap: var(--ms-space-3); flex-wrap: wrap;
  margin-bottom: var(--ms-space-2);
  font-size: var(--ms-text-small);
}
.note-author { font-weight: var(--ms-weight-semibold); display: inline-flex; align-items: center; gap: var(--ms-space-2); }
.note-meta { color: var(--ms-text-secondary); }
/* Die Groesse der beiden Symbole kommt aus `--ms-action-hit`; die eigene
   Zeile dafuer ist mit `.icon-btn` entfallen (2026-09-10). */
.note-actions { margin-left: auto; display: inline-flex; gap: var(--ms-action-gap); }
/* DIE LESEBREITE STEHT AM TEXT (2026-09-17). Der Notizverlauf steht in einem
   `.detail-section` ueber die volle Seitenbreite; bei 1320px Seite laeuft eine
   Zeile auf rund 144 Zeichen, empfohlen sind 70 bis 80. Das Problem gab es
   schon bei 1140px (rund 122) — die breitere Seite macht es nur sichtbarer,
   sie verursacht es nicht.

   AM TEXT UND NICHT AN DER KARTE, dieselbe Regel wie `.card:has(.form-group)`
   in surfaces.css: Kopfzeile, Verfasser, Zeitpunkt und die beiden Symbole
   behalten die volle Breite und bleiben rechts aussen. Begrenzt wird, was
   gelesen wird. */
/* 16px UND NICHT DIE FLIESSTEXTGROESSE (Befund des Betreibers, 2026-09-17:
   „warum ist hier bei den Notizen die Schrift so gross?").

   Der Notiztext trug gar keine eigene Groesse, er erbte `--ms-text-body` —
   und das ist FLUID (clamp 16-18px, unveraendert von mkpi.de uebernommen).
   Gemessen bei 1500px Fensterbreite: 18px. Zum Vergleich in derselben Karte:
   Kopfzeile der Notiz 14px, Dateitabelle 14px, Hinweiszeile 14px, Menge 12px —
   und die UEBERSCHRIFT der Karte 17,6px. Der Fliesstext war damit groesser als
   seine eigene Ueberschrift.

   `--ms-text-md` ist die FESTE 16px-Stufe, die das System dafuer schon hat.
   Sie ist derselbe Wert wie `--ms-control-font` und `--ms-btn-s-font`: 16px
   ist die Betriebsgroesse dieser Anwendung, fluide 16-18px sind die einer
   Verkaufsseite. Eine Bestelldetailseite ist das zweite nicht. */
.note-body {
  white-space: normal;
  overflow-wrap: anywhere;
  max-width: var(--ms-content-narrow);
  font-size: var(--ms-text-md);
}
.note-edit, .note-compose { display: flex; flex-direction: column; gap: var(--ms-space-3); }
.note-edit-actions { display: flex; justify-content: flex-end; gap: var(--ms-space-3); }
.note-confirm { display: flex; align-items: center; justify-content: space-between; gap: var(--ms-space-3); flex-wrap: wrap; margin: var(--ms-space-3) 0 0; }
.note-compose { margin-top: var(--ms-space-2); }

/* ── Hinweis „Bearbeitungsmodus" (Assistent, alle Karten) ──
   Layout und sonst nichts: Flaeche, Rahmen, Schriftgroesse und Textfarbe
   kommen aus `.alert.alert-secondary` (data.css), der Verweis aus
   `.text-link` (base.css). Hier steht nur, dass die drei Teile in EINER
   Zeile stehen und der Abbruch rechts aussen sitzt.

   Enger als ein gewoehnliches Hinweisband: der Satz ist kurz, und das Band
   wiederholt sich auf jeder der sieben Karten. Begruendung im Teilstueck
   `v2/order/wizard/_edit_banner.html.twig`. */
.edit-banner {
  display: flex; align-items: center; gap: var(--ms-space-3);
  padding: var(--ms-space-2) var(--ms-space-4);
  margin-bottom: var(--ms-space-5);
}
.edit-banner .ms-ico { flex: none; color: var(--ms-text-tertiary); }
.edit-banner-text { flex: 1 1 auto; min-width: 0; }

/* ── Die Wahl mit oder ohne Abholung (Schritt 1 des Assistenten) ──
   Zwei Optionsfelder nebeneinander, zwischen den beiden Terminbloecken. Sie
   entscheiden, ob der Block darunter ueberhaupt auszufuellen ist, und stehen
   deshalb davor. Umbruch untereinander, sobald es eng wird — die beiden
   Saetze sind laenger als ein Wort. */
.pickup-choice { display: flex; flex-direction: column; gap: var(--ms-space-2); }
.pickup-choice .form-label { margin: 0; }
/* Die Reihe selbst ist das Bauteil `.radio-row` aus forms.css (das
   Formularthema zeichnet jede aufgeklappte Auswahl damit) — Ausrichtung,
   Abstand und Schriftgroesse stehen dort und werden hier NICHT wiederholt.
   Uebrig bleibt, was diese eine Stelle unterscheidet: keine Bedienhoehe (die
   Reihe steht zwischen zwei Terminbloecken und soll nicht so hoch bauen wie
   ein Eingabefeld) und ein engerer Umbruchabstand, weil die beiden Saetze
   laenger sind als ein Wort. */
.pickup-choice .radio-row {
  min-height: 0;
  gap: var(--ms-space-2) var(--ms-space-5);
}
