/**
 * JP FM — plugin-eigene Feinheiten (letzte Schicht).
 *
 * Diese Datei gehört dem Plugin, nicht dem Design-System-Paket: sie wird von
 * keinem Paket geliefert und von keinem überschrieben (siehe
 * FM_Core_Frontend_Assets::enqueue_design_system(), der Enqueue prüft nur, ob
 * die Datei existiert). Hier gehört alles hin, was dauerhaft lokal bleiben soll.
 *
 * Reihenfolge der Schichten:
 * tailadmin → fm-tokens → fm-app → …Modul-CSS… → fm-design-system
 * → fm-design-system-extensions → **fm-overrides**
 *
 * @package Facility Management
 */

/* ============================================================================
   Kompatibilitäts-Aliase (Punkt 8 – Modul-Stylesheet-Bereinigung)

   Mehrere Modul-Layer lesen Farben über Alias-Namen (--fm-card-bg, --fm-color-*,
   --fm-hover, --fm-title, --fm-warning-bg …), die nirgends definiert waren – dort
   griff bisher ein hart kodierter Fallback (u. a. weiße Flächen #fff), der den
   Dunkelmodus (Punkt 7) durchbrach. Diese Zeilen mappen die Aliase auf die
   kanonischen Tokens; damit erben die betroffenen Module Marke UND Dunkelmodus,
   ohne dass jede Moduldatei einzeln angefasst werden muss (gleiche Strategie wie
   der Alias-Block in fm-tokens.css). Nicht gemappt: --fm-surface-sunken (bewusst
   `transparent`) und Layout-Aliase (--fm-letter-*, --fm-help-tour-inset).
   ============================================================================ */
#fm-app {
	--fm-card-bg: var(--fm-surface);
	--fm-title: var(--fm-heading);
	--fm-hover: var(--fm-surface-2);
	--fm-warning-bg: var(--fm-warning-soft);
	--fm-mail-toast-accent: var(--fm-primary);

	/* Parallele --fm-color-*-Namenskonvention einzelner Layer. */
	--fm-color-surface: var(--fm-surface);
	--fm-color-surface-muted: var(--fm-surface-2);
	--fm-color-border: var(--fm-border);
	--fm-color-text-muted: var(--fm-body);
	--fm-color-primary: var(--fm-primary);
	--fm-color-primary-soft: var(--fm-primary-soft);
	--fm-color-danger: var(--fm-danger);
	--fm-color-danger-soft: var(--fm-danger-soft);
	--fm-color-success: var(--fm-success);
	--fm-color-warning: var(--fm-warning);
}

/* --------------------------------------------------------------------------
 * `fm-row-actions`: ein Klassenname, zwei verschiedene Bauteile
 * --------------------------------------------------------------------------
 *
 * `fm-notifications.css` definiert `.fm-row-actions` als **Knopfreihe**
 * (`display:flex; align-items:center`) — und genau so benutzen es die sechs
 * Templates des Moduls `notifications`. Die Datei wird global geladen (die
 * Glocke in der Topbar steht auf jeder App-Seite), die Regel gilt also überall.
 *
 * `contracts/templates/detail-review.php` verwendet denselben Namen für ein
 * **Aufklapp-Element**: `<details class="fm-row-actions">` mit `<summary>` und
 * `.fm-row-actions-body`. Durch das geerbte `display:flex` werden
 * Zusammenfassung und Inhalt zu Flex-Geschwistern — das aufgeklappte Prüf-Panel
 * stand rechts NEBEN dem Knopf statt darunter und war auf seine `min-width`
 * von 16rem gequetscht (gemessen bei 1280 px: Panel links 619 bei Knopfende
 * 614, Breite 256 statt 613; der Knopf mittig daneben).
 *
 * Der Element-Selektor trifft nur den `<details>`-Fall; die Knopfreihen der
 * Benachrichtigungen bleiben unverändert flex. Spezifität (1,1,1) schlägt die
 * (0,1,0) aus `fm-notifications.css`.
 *
 * Sauber wäre, eines der beiden Bauteile umzubenennen — dann kann diese Regel
 * ersatzlos entfallen.
 */
#fm-app details.fm-row-actions {
	display: block;
}

/* --------------------------------------------------------------------------
 * `screen-reader-text`: die WordPress-Klasse ohne WordPress-Stylesheet
 * --------------------------------------------------------------------------
 *
 * Im Plugin gilt `fm-sr-only` (definiert in tailadmin.css:75). An acht Stellen
 * steht stattdessen die WordPress-Klasse `screen-reader-text` — und die trägt
 * in dieser Umgebung KEINE Regel:
 *
 *   1. kein Stylesheet des Plugins definiert sie;
 *   2. das Theme `fm-blank` entfernt die Frontend-Stylesheets, die sie sonst
 *      mitbrächten (`wp-block-library`, `wp-block-library-theme`,
 *      `global-styles`, `classic-theme-styles`);
 *   3. `admin-bar.css` würde sie mitbringen, aber `FM_Core_Login` blendet die
 *      Adminleiste per Vorgabe aus (`show_admin_bar` → false).
 *
 * Der nur für Screenreader gedachte Text stand damit sichtbar auf der Seite.
 * Diese Regel spiegelt `fm-sr-only` eins zu eins.
 *
 * Betroffen: notifications/overview.php:160,
 * notifications/notification-settings.php:65,
 * purchasing/supplier-detail.php:352, time-accounts/cockpit.php:56,63,159,238.
 * Die achte Stelle liegt in der PWA, die diese Datei nicht lädt — dafür steht
 * dieselbe Regel in fm-pwa.css.
 *
 * Ziehen die Templates auf `fm-sr-only` nach, kann die Regel ersatzlos weg.
 */
#fm-app .screen-reader-text {
	position: absolute;
	width: 1px;
	height: 1px;
	padding: 0;
	margin: -1px;
	overflow: hidden;
	clip: rect(0, 0, 0, 0);
	white-space: nowrap;
	border: 0;
}

/* --------------------------------------------------------------------------
 * Zwei Merkmale, die die Systemschicht benutzt, aber niemand definiert
 * --------------------------------------------------------------------------
 *
 * `fm-design-system-extensions.css` greift dreimal auf `--fm-fg` und einmal auf
 * `--fm-fs-base` zu — **ohne Rückfallwert**. Beide Merkmale sind in keiner
 * CSS-Datei des Plugins definiert. Ein `var()` ohne Definition und ohne
 * Rückfall macht die ganze Deklaration ungültig; die Eigenschaft fällt auf
 * `unset` zurück, bei `color` also auf den geerbten Wert.
 *
 * Gemessen (1280 px, Elternfarbe absichtlich rot gesetzt):
 *
 *   .fm-readonly-value  ohne Erweiterungsschicht: rgb(13,27,42)  = --fm-heading
 *                       mit  Erweiterungsschicht: rgb(255,0,0)   = geerbt
 *
 * Das ist eine echte Verschlechterung: `fm-design-system.css:1132` setzt für
 * dieses Element `color: var(--fm-heading)`, und die spätere, ungültige
 * Deklaration hebt sie auf. Bei `.fm-detail-subtitle` (Runde 54) und `.fm-label`
 * (Runde 82) gibt es keine Basisregel — dort bleibt die Farbangabe schlicht
 * wirkungslos.
 *
 * Statt drei Paketregeln lokal zu ändern (das Paket ersetzt die Datei jede
 * Runde), werden hier die fehlenden Merkmale nachgereicht. Custom Properties
 * werden am Element aufgelöst, nicht in Dateireihenfolge — die Definition wirkt
 * deshalb auch auf die früher geladene Erweiterungsschicht.
 *
 * `--fm-fg` erhält `--fm-heading`: alle drei Fundstellen sind kräftiger Text
 * (Zwischenüberschrift, Feldbeschriftung, Antwortwert), und für
 * `.fm-readonly-value` stellt es genau den vorherigen Zustand wieder her.
 * `--fm-fs-base` erhält `--fm-fs-md` (1 rem), die Grundschriftgröße der Skala.
 *
 * Definiert das Paket die Merkmale eines Tages selbst, kann dieser Block weg.
 */
#fm-app {
	--fm-fg: var(--fm-heading);
	--fm-fs-base: var(--fm-fs-md);
}

/* --------------------------------------------------------------------------
 * `fm-field-error` neben dem `hidden`-Attribut  (aus Runde 153 hierher gezogen)
 * --------------------------------------------------------------------------
 *
 * `#fm-app .fm-field-error` setzt in `fm-design-system.css` `display: flex`. Eine
 * Autorenregel schlägt die Browser-Vorgabe `[hidden] { display: none }` unabhängig
 * von der Spezifität — ein Element mit der Klasse UND dem `hidden`-Attribut bleibt
 * also sichtbar. Getroffen hat es die Überschreitungswarnung der Splittbuchung
 * (`data-fm-split-warn`), die `fm-banking.js` allein über `warnEl.hidden` schaltet:
 * im Browser gemessen `display: flex` und 111 px Höhe trotz `hidden`, die Warnung
 * stand also dauerhaft da. Dieselbe Bremse liegt bereits als Einzelregel in
 * `fm-mail-center.css` (Zeilen 336, 845) — hier an der Klasse selbst, damit die
 * Falle nicht bei jeder weiteren Verwendung neu zuschlägt.
 *
 * Stand bis 29.07.2026 in `fm-design-system-extensions.css`. Die Regel gehört
 * dem Plugin, nicht dem Paket: der Paketfassung fehlt sie, ein wörtlich
 * übernommener Paketstand hätte sie also gelöscht und den Fehler zurückgebracht.
 */
#fm-app .fm-field-error[hidden] {
	display: none;
}

/* --------------------------------------------------------------------------
 * `fm-code-inline`: lange technische Werte dürfen umbrechen
 * --------------------------------------------------------------------------
 *
 * Die Klasse kam mit den Runden 211–218 und hatte bis hierhin keine einzige
 * Regel. Sie ist aber NICHT wirkungslos gewesen: sie sitzt in allen acht
 * Fundstellen auf einem `<code>`-Element, und `#fm-app code`
 * (`fm-design-system.css:70`) gestaltet das bereits — Monospace, getönter
 * Hintergrund, Radius. Die Klasse war also schlicht überflüssig.
 *
 * Ein echtes Problem bleibt trotzdem: die Werte dahinter sind Message-IDs,
 * Prüfsummen, SMTP-Hosts und maskierte Lizenzschlüssel — Zeichenketten ohne
 * Leerzeichen, die nirgends umbrechen dürfen und deshalb aus ihrer Zelle
 * laufen. Gemessen an einer Message-ID in einem 182 px breiten Container:
 *
 *   ohne Regel            Code-Element 350 px  →  168 px Überlauf
 *   mit overflow-wrap     Code-Element 175 px  →  bricht auf zwei Zeilen
 *
 * Genau das macht die Klasse jetzt — und nur an den Stellen, die sie tragen;
 * kurze `<code>`-Schnipsel im Fließtext bleiben unangetastet.
 */
#fm-app .fm-code-inline {
	overflow-wrap: anywhere;
}

/* --------------------------------------------------------------------------
 * `mfr-migration`: die Klassen des Assistenten, für die es keine Komponente gibt
 * --------------------------------------------------------------------------
 *
 * Das Modul wurde nie gestaltet — sein Markup trug 20 Klassen ohne jede Regel.
 * Der größere Teil ist am Template gelöst (auf bestehende Komponenten abgebildet:
 * fm-form-row→fm-field, fm-report-list→fm-detail-list, die Kacheln in
 * step-check auf components/card-kpi.php, fm-visually-hidden→fm-sr-only,
 * fm-table-scroll→fm-table-wrap). Was hier steht, hat kein Gegenstück im System:
 * es sind Bauteile, die es nur in diesem Assistenten gibt.
 *
 * Warum in fm-overrides.css und nicht in der Erweiterungsschicht: die gehört dem
 * Design-System-Paket (CLAUDE.md §7.1), und dieses Modul liefert es nicht mit.
 */

/* Quellenauswahl (Schritt 1): Karten nebeneinander, auf dem Telefon gestapelt. */
#fm-app .fm-source-grid {
	display: grid;
	grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
	gap: var(--fm-s-4);
	margin: 0;
	padding: 0;
	list-style: none;
}

#fm-app .fm-source-card {
	display: flex;
	flex-direction: column;
	align-items: flex-start;
	gap: var(--fm-s-3);
	padding: var(--fm-s-4);
	border: 1px solid var(--fm-stroke);
	border-radius: var(--fm-r-lg);
	background: var(--fm-surface);
}

#fm-app .fm-source-card.is-selected {
	border-color: var(--fm-primary-500);
	box-shadow: inset 0 0 0 1px var(--fm-primary-500);
}

#fm-app .fm-source-name {
	font-weight: 600;
	color: var(--fm-heading);
}

/* Verbindungstest (Schritt 2): je Prüfpunkt Bezeichnung, Ergebniswort, Meldung.
   fm-check-list/-item/-label liegen in der Systemschicht; hier fehlen nur das
   Ergebnis-Abzeichen, die Meldung und die drei Zustandsfarben. */
#fm-app .fm-check-badge {
	padding: 0.1rem 0.5rem;
	border-radius: 999px;
	background: var(--fm-muted-soft);
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-xs);
	font-weight: 600;
}

#fm-app .fm-check {
	flex-wrap: wrap;
}

#fm-app .fm-check-msg {
	flex: 1 1 100%;
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-sm);
}

#fm-app .fm-check-ok .fm-check-badge {
	background: var(--fm-success-soft);
	color: var(--fm-success-text);
}

/* Der dritte Zustand heisst `skip` (Pruefung nicht durchgefuehrt), nicht
   `warn` — die Zustaende kommen aus step-connection.php:88. */
#fm-app .fm-check-skip .fm-check-badge {
	background: var(--fm-warning-soft);
	color: var(--fm-warning-text);
}

#fm-app .fm-check-fail .fm-check-badge {
	background: var(--fm-danger-soft);
	color: var(--fm-danger-text);
}

/* Bereichsauswahl (Schritt 3): der Text im Kästchen ist der dehnbare Teil,
   damit die Zeile bei langen Bereichsnamen sauber umbricht. */
#fm-app .fm-checkbox-label {
	flex: 1 1 auto;
}

/* Pflichtfeldstern. Rein visuell — die Vorlesbarkeit hängt an `required` und
   `aria-required`, deshalb trägt der Stern im Markup `aria-hidden`. */
#fm-app .fm-req {
	color: var(--fm-danger-text);
	font-weight: 600;
}

/* Lauf (Schritt 6): Fortschrittstext über dem Balken, darunter je Bereich eine
   Zeile mit Bereichsname links und den drei Zahlen rechts. */
#fm-app .fm-progress-text {
	margin: 0 0 var(--fm-s-2);
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-sm);
}

#fm-app .fm-count-list {
	display: grid;
	gap: var(--fm-s-2);
	margin: var(--fm-s-4) 0 0;
	padding: 0;
	list-style: none;
}

#fm-app .fm-count-list > li {
	display: flex;
	flex-wrap: wrap;
	align-items: baseline;
	justify-content: space-between;
	gap: var(--fm-s-2);
	padding-bottom: var(--fm-s-2);
	border-bottom: 1px solid var(--fm-stroke-soft);
}

#fm-app .fm-count-area {
	font-weight: 500;
	color: var(--fm-heading);
}

#fm-app .fm-count-nums {
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-sm);
	font-variant-numeric: tabular-nums;
}

/* Assistentenrahmen: Abstand zwischen Schrittleiste und Schrittinhalt, und die
   Beschriftung in der Schrittleiste (fm-wizard-steps/-step/-step-num sind
   definiert, -label fehlte). */
#fm-app .fm-wizard-body {
	margin-top: var(--fm-s-5);
}

#fm-app .fm-wizard-step-label {
	font-size: var(--fm-fs-sm);
	line-height: 1.3;
}

/* --------------------------------------------------------------------------
 * `fm-timeline-time`: der Zeitstempel im Verlauf
 * --------------------------------------------------------------------------
 *
 * Die Klasse sitzt im Verlaufs-Panel des Klärfalls auf dem `<span>` mit der
 * Uhrzeit, direkt vor dem `<strong>` mit der Aktion. Sie hatte keine Regel —
 * der Zeitstempel erschien damit in Größe und Farbe wie die Aktion selbst,
 * obwohl er die nachrangige Angabe ist. Das Geschwister-Element für den
 * Benutzernamen trägt dafür bereits `fm-muted`.
 *
 * Zwei Dinge stellt die Regel her:
 *   - Zurücknahme (kleiner, gedämpft) — die Aktion bleibt die Hauptinformation.
 *   - `font-variant-numeric: tabular-nums` — Ziffern gleicher Breite, damit die
 *     Aktionen untereinander an derselben Stelle beginnen. Ohne das wandert der
 *     Aktionstext je nach Uhrzeit um mehrere Pixel hin und her (proportionale
 *     Ziffern: „11:11" ist schmaler als „08:44").
 *
 * Bewusst KEINE feste Breite: die Zeitformate der Installationen unterscheiden
 * sich (12h/24h, mit und ohne Datum), eine feste Spalte würde abschneiden.
 */
#fm-app .fm-timeline-time {
	font-size: var(--fm-fs-sm);
	color: var(--fm-body);
	font-variant-numeric: tabular-nums;
	margin-right: 0.5rem;
}

/* ==========================================================================
   Auslastungsmatrix (Disposition → Auslastung): Ampel-Zellen mit Prozentbalken.
   Grün bis 80 %, gelb bis 100 %, rot darüber; grau bei nicht verfügbarer Ressource.
   ========================================================================== */
#fm-app .fm-util-matrix th.fm-util-col {
	text-align: center;
	white-space: nowrap;
	font-size: var(--fm-fs-sm);
}

#fm-app .fm-util-cell {
	text-align: center;
	vertical-align: middle;
	min-width: 60px;
}

#fm-app .fm-util-num {
	display: block;
	font-weight: 600;
	font-variant-numeric: tabular-nums;
}

#fm-app .fm-util-bar-track {
	height: 4px;
	margin-top: 4px;
	border-radius: 2px;
	background: var(--fm-muted-bg);
	overflow: hidden;
}

#fm-app .fm-util-bar {
	display: block;
	height: 100%;
	background: currentColor;
}

#fm-app .fm-util-ok {
	color: var(--fm-success);
}

#fm-app .fm-util-high {
	color: var(--fm-warning);
}

#fm-app .fm-util-over {
	color: var(--fm-danger);
}

#fm-app .fm-util-off {
	color: var(--fm-muted);
}

/* Preisblatt-Reihenfolge: sortierbare Liste (Drag-and-Drop + Nummerneingabe) — #19.
   Ohne JavaScript bleibt sie als Nummernliste voll bedienbar; der Griff wird erst
   per JS ziehbar (progressive enhancement). */
#fm-app .fm-sort-list {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
}
#fm-app .fm-sort-item {
	display: flex;
	align-items: center;
	gap: var(--fm-s-3);
	padding: var(--fm-s-2) var(--fm-s-3);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-surface);
}
#fm-app .fm-sort-item.is-dragging {
	opacity: 0.5;
}
#fm-app .fm-sort-handle {
	color: var(--fm-muted);
	font-size: 1.15rem;
	line-height: 1;
	cursor: grab;
	touch-action: none;
	-webkit-user-select: none;
	user-select: none;
	/* Touch-Ziel ≥ 44 px (Außendienst, CLAUDE.md §7). */
	min-width: 44px;
	min-height: 44px;
	display: inline-flex;
	align-items: center;
	justify-content: center;
}
#fm-app .fm-sort-handle[draggable="true"]:active {
	cursor: grabbing;
}
#fm-app .fm-sort-label {
	flex: 1 1 auto;
}
#fm-app .fm-sort-number {
	flex: 0 0 4.5rem;
	width: 4.5rem;
}

/* ============================================================================
   Dunkles Farbschema (Punkt 7)

   Aktivierung über das Attribut #fm-app[data-fm-theme="dark"] (gesetzt server-
   seitig aus dem Cookie fm_theme für FOUC-Freiheit, danach vom Umschalter in
   public/js/fm-app.js). Die Systemschicht liest Farben ausschließlich über die
   --fm-*-Tokens; deshalb genügt es, hier die Neutral-/Flächentoken und die harten
   Soft-Flächen umzudefinieren — Karten, Tabellen, Formulare, Badges usw. erben den
   Dunkelmodus automatisch (docs/INTEGRATION.md §5b).

   Neutral-Achse: niedrige Nummern = dunkle Flächen (n-0 bleibt die HELLSTE Fläche,
   damit Karten sich vom Hintergrund abheben), hohe Nummern = heller Text. Marke
   (--fm-primary*) bleibt unverändert (Aktion/Fokus tragen auf hell wie dunkel).
   ============================================================================ */
#fm-app[data-fm-theme="dark"] {
	/* Neutral – Flächen (0 = Karte, 100 = App-Hintergrund) bis Text (900). */
	--fm-n-0: #1b2635;
	--fm-n-50: #151f2c;
	--fm-n-100: #0f1824;
	--fm-n-150: #202d3d;
	--fm-n-200: #2b3a4d;
	--fm-n-300: #3a4b60;
	--fm-n-400: #6b7c93;
	--fm-n-500: #94a3b8;
	--fm-n-700: #cbd5e1;
	--fm-n-900: #f1f5f9;

	/* „white" wird hier als Fläche gelesen (Karten), nicht als reine Farbe. */
	--fm-white: var(--fm-n-0);

	/* Weiche Blau-/Akzentflächen: hell (#e8f1ff) würde auf dunkel blenden.
	   Die Basisstufen 50/100 werden mit umdefiniert — sie werden im System
	   ausschließlich als FLÄCHE benutzt (aktive/ausgewählte Zustände wie
	   `fm-choice:has(input:checked)`, aktive Subnav, markierte Tabellenzeilen).
	   Ohne das bleibt dort die helle Fläche stehen, während der Text hell
	   bleibt → unlesbar. */
	--fm-primary-50: #16283f;
	--fm-primary-100: #1d3453;
	--fm-primary-soft: #16283f;
	--fm-accent-soft: #16283f;
	--fm-accent-border: #24405f;

	/* Status: dunkle, entsättigte Flächen + heller Text/Rahmen (Kontrast ≥ 4.5:1). */
	--fm-success-soft: #14301f;
	--fm-success-line: #1c4a30;
	--fm-success-text: #4ade80;
	--fm-warning-soft: #38290c;
	--fm-warning-line: #57411a;
	--fm-warning-text: #fbbf24;
	--fm-danger-soft: #3a1719;
	--fm-danger-line: #5c2428;
	--fm-danger-text: #f87171;
	--fm-info-soft: #16283f;
	--fm-info-line: #24405f;
	--fm-info-text: #93b4f6;
	--fm-neutral-soft: var(--fm-n-150);

	/* Elevation: auf dunklem Grund tragen nur klar schwarze Schatten. */
	--fm-e-1: 0 1px 2px rgba(0, 0, 0, 0.4);
	--fm-e-2: 0 4px 12px rgba(0, 0, 0, 0.5);
	--fm-e-3: 0 14px 34px rgba(0, 0, 0, 0.6);
	--fm-shadow: var(--fm-e-1);
	--fm-shadow-lg: var(--fm-e-2);

	/* Fokusring eine Spur kräftiger, damit er auf dunkel gleich gut sitzt. */
	--fm-focus-ring: 0 0 0 3px rgba(59, 130, 246, 0.45);

	color-scheme: dark;
}

/* Gewöhnliche Textlinks im Dunkelmodus.

   Die Systemschicht setzt `#fm-app a { color: var(--fm-primary-600) }` (#2563eb).
   Da die Marke oben bewusst NICHT umdefiniert wird, stand dieses Blau auch auf
   der dunklen Kartenfläche (#1b2635) — gemessen 2,95:1 und damit unter dem
   WCAG-AA-Minimum von 4,5:1 für Fließtext (Hellmodus: 5,17:1). --fm-primary-300
   (#93b4f6) hebt das auf 7,34:1, ohne die Marke anzufassen.

   Warum `:where()` und nicht `#fm-app[data-fm-theme="dark"] a`:
   Der Attributselektor zählt als Klasse, die Regel läge also bei (1,1,1) und
   würde — aus dieser Datei als letzter Quelle — JEDE Komponente überstimmen, die
   ihre Linkfarbe selbst setzt. Gemessen wären das 22 Bausteine, darunter
   `.fm-btn-primary` (weiß auf Blau, 5,17:1 → 2,49:1), `.fm-btn-danger`
   (4,83:1 → 2,32:1), die Sidebar (`--fm-sidebar-text` → Blau im Navy),
   `.fm-table a` (neutral, Blau erst bei Hover), `.fm-card-kpi-link`
   (`color: inherit`) sowie Subnav, Chips, Breadcrumb, Portal-Nav und
   Benutzermenü. `:where()` steuert 0 zur Spezifität bei: die Regel landet auf
   (1,0,1) — exakt der Position von `#fm-app a`, das sie ersetzen soll — und
   gewinnt allein über die spätere Quellposition. Alles, was seine Farbe über
   eine Klasse (≥ 1,1,0) selbst bestimmt, bleibt unberührt. Beim Ändern diese
   Konstruktion bitte erhalten; die vermeintliche Vereinfachung auf den reinen
   Attributselektor bricht die genannten Bausteine.

   Der Hover-Zustand steht weiter unten im Block mit den weichen Blauflächen. */
#fm-app:where([data-fm-theme="dark"]) a {
	color: var(--fm-primary-300);
}

/* Text auf weichen Blauflächen.

   Die Systemschicht paart `--fm-primary-50/100` (Fläche) fest mit
   `--fm-primary-700/900` (Text) — im Hellmodus helle Fläche + dunkle Schrift.
   Im Dunkelmodus sind die Flächen jetzt dunkel; die Textstufen dürfen aber
   NICHT global umdefiniert werden, weil `--fm-primary-700` an anderer Stelle
   als Fläche dient (`fm-btn-primary:hover`, `--fm-accent-strong` in
   fm-lexoffice-migration.css). Deshalb wird die Schrift hier komponentenweise
   aufgehellt. */
#fm-app[data-fm-theme="dark"] code,
#fm-app[data-fm-theme="dark"] .fm-code,
#fm-app[data-fm-theme="dark"] .fm-avatar,
#fm-app[data-fm-theme="dark"] .fm-btn-subtle,
#fm-app[data-fm-theme="dark"] a.fm-chip-active,
#fm-app[data-fm-theme="dark"] .fm-chip-active,
#fm-app[data-fm-theme="dark"] .fm-btn-link,
#fm-app[data-fm-theme="dark"] .fm-kpi-icon,
#fm-app[data-fm-theme="dark"] a:hover {
	color: var(--fm-primary-300);
}

#fm-app[data-fm-theme="dark"] .fm-btn-subtle:hover,
#fm-app[data-fm-theme="dark"] .fm-btn-link:hover {
	color: #c7dbff;
}

/* Umschalter für das Farbschema (Topbar). Touch-Ziel ≥ 40 px, tokenbasiert. */
#fm-app .fm-theme-toggle {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	width: 40px;
	height: 40px;
	min-width: 40px;
	padding: 0;
	border: 1px solid var(--fm-stroke);
	border-radius: var(--fm-r-md);
	background: var(--fm-surface);
	color: var(--fm-text);
	cursor: pointer;
	transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
#fm-app .fm-theme-toggle:hover {
	background: var(--fm-surface-2);
	color: var(--fm-heading);
}
#fm-app .fm-theme-toggle:focus-visible {
	outline: none;
	box-shadow: var(--fm-focus-ring);
}
#fm-app .fm-theme-toggle .fm-icon {
	width: 20px;
	height: 20px;
}
/* Hell zeigt den Mond (klick → dunkel), Dunkel zeigt die Sonne (klick → hell). */
#fm-app .fm-theme-icon-to-light {
	display: none;
}
#fm-app[data-fm-theme="dark"] .fm-theme-icon-to-dark {
	display: none;
}
#fm-app[data-fm-theme="dark"] .fm-theme-icon-to-light {
	display: inline-flex;
}

/* ---------------------------------------------------------------------------
   Kombinierte Mehr-Routen-Ansicht (Disposition → „Touren nebeneinander").
   Routen eines Tages als nebeneinanderliegende Lanes; der Container scrollt
   horizontal in sich (der Seitenkörper nie). Nur Tokens, keine festen Werte.
   --------------------------------------------------------------------------- */
#fm-app .fm-routes-hint {
	margin: 0 0 var(--fm-s-3);
	font-size: 0.9rem;
}

#fm-app .fm-routes-lanes {
	display: flex;
	gap: var(--fm-s-4);
	align-items: flex-start;
	overflow-x: auto;
	padding-bottom: var(--fm-s-3);
	-webkit-overflow-scrolling: touch;
}

#fm-app .fm-route-lane {
	flex: 0 0 20rem;
	max-width: 20rem;
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
	padding: var(--fm-s-3);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-surface);
}

#fm-app .fm-route-lane-head {
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-1);
	padding-bottom: var(--fm-s-2);
	border-bottom: 1px solid var(--fm-border);
}

#fm-app .fm-route-lane-title {
	margin: 0;
	font-size: 1rem;
	line-height: 1.3;
}

#fm-app .fm-route-lane-badges {
	display: flex;
	flex-wrap: wrap;
	gap: var(--fm-s-1);
}

#fm-app .fm-route-lane-time {
	margin: 0;
	font-size: 0.85rem;
	color: var(--fm-muted);
	font-variant-numeric: tabular-nums;
}

#fm-app .fm-route-lane-conflicts {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-1);
}

#fm-app .fm-lane-stops {
	list-style: none;
	margin: 0;
	padding: 0;
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
	counter-reset: fm-lane-stop;
}

#fm-app .fm-lane-stop {
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
	padding: var(--fm-s-2);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-bg);
}

#fm-app .fm-lane-stop-empty {
	border-style: dashed;
	background: var(--fm-muted-bg);
}

#fm-app .fm-lane-stop-main {
	display: flex;
	flex-direction: column;
	gap: 2px;
	min-width: 0;
}

#fm-app .fm-lane-stop-name {
	font-weight: 600;
	word-break: break-word;
}

#fm-app .fm-lane-stop-name::before {
	counter-increment: fm-lane-stop;
	content: counter(fm-lane-stop) ". ";
	color: var(--fm-muted);
}

#fm-app .fm-lane-stop-meta {
	font-size: 0.82rem;
	color: var(--fm-muted);
}

#fm-app .fm-lane-move {
	display: flex;
	flex-wrap: wrap;
	gap: var(--fm-s-2);
	align-items: center;
}

#fm-app .fm-lane-move-target {
	flex: 1 1 8rem;
	min-width: 0;
	min-height: 44px;
}

#fm-app .fm-lane-move .fm-btn {
	min-height: 44px;
}

@media ( max-width: 640px ) {
	#fm-app .fm-route-lane {
		flex-basis: 85vw;
		max-width: 85vw;
	}
}

/* ---------------------------------------------------------------------------
   Standardtour-Vorlagen: Editor der geordneten Objekt-Stopps (Objekt / Leistung
   / Notiz je Zeile) + kompaktes Inline-Datumsfeld beim Ausrollen. Nur Tokens.
   --------------------------------------------------------------------------- */
#fm-app .fm-template-stops {
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
}

#fm-app .fm-template-stop-row {
	display: grid;
	grid-template-columns: 2fr 1.5fr 2fr;
	gap: var(--fm-s-2);
	align-items: end;
}

#fm-app .fm-template-stop-row .fm-field {
	margin: 0;
}

#fm-app .fm-input-inline {
	width: auto;
	max-width: 11rem;
	min-height: 44px;
}

@media ( max-width: 640px ) {
	#fm-app .fm-template-stop-row {
		grid-template-columns: 1fr;
	}
}

/* Barcode-/QR-Scanner (Einkauf/Lager „Scannen"). Eigene Komponente → hier, nicht im
   Design-System-Paket (CLAUDE.md §7). Nur Tokens, keine festen Farbwerte. */
#fm-app .fm-input-group {
	display: flex;
	gap: var(--fm-s-2);
	align-items: stretch;
}

#fm-app .fm-input-group .fm-input {
	flex: 1 1 auto;
	min-width: 0;
}

#fm-app .fm-scan-viewport {
	position: relative;
	width: 100%;
	max-width: 32rem;
	margin: 0 auto var(--fm-s-3);
	border-radius: var(--fm-radius);
	overflow: hidden;
	background: var(--fm-muted-bg);
	aspect-ratio: 4 / 3;
}

#fm-app .fm-scan-video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

#fm-app .fm-scan-frame {
	position: absolute;
	inset: 18% 12%;
	border: 3px solid var(--fm-primary);
	border-radius: var(--fm-radius);
	pointer-events: none;
	box-shadow: 0 0 0 100vmax rgba(0, 0, 0, 0.28);
}

/* ------------------------------------------------------------------ */
/* Kontrollpunkt-QR-Etiketten (Objektmodul) + Scan-Viewport.          */
/* Eigene Komponente (§7.1): nur #fm-app-Präfix, Tokens, kein !important. */
/* ------------------------------------------------------------------ */

#fm-app .fm-qr-labels {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(11rem, 1fr));
	gap: var(--fm-s-3);
	margin-top: var(--fm-s-3);
}

#fm-app .fm-qr-label {
	display: flex;
	flex-direction: column;
	align-items: center;
	gap: var(--fm-s-2);
	padding: var(--fm-s-3);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-surface);
	text-align: center;
	break-inside: avoid;
}

#fm-app .fm-qr-label-img svg {
	display: block;
	width: 100%;
	height: auto;
	max-width: 9rem;
}

#fm-app .fm-qr-label-meta {
	display: grid;
	gap: 0.15rem;
	word-break: break-word;
}

#fm-app .fm-qr-label-name {
	font-weight: 600;
}

#fm-app .fm-qr-label-object {
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-sm);
}

#fm-app .fm-qr-label-code {
	font-family: var(--fm-font-mono, ui-monospace, monospace);
	font-size: var(--fm-fs-sm);
	letter-spacing: 0.05em;
}

#fm-app .fm-qr-scan-viewport {
	position: relative;
	width: 100%;
	max-width: 28rem;
	margin: 0 auto var(--fm-s-3);
	border-radius: var(--fm-radius);
	overflow: hidden;
	background: var(--fm-muted-bg);
	aspect-ratio: 4 / 3;
}

#fm-app .fm-qr-scan-video {
	display: block;
	width: 100%;
	height: 100%;
	object-fit: cover;
}

@media print {
	#fm-app .fm-no-print {
		display: none;
	}

	#fm-app .fm-qr-labels {
		grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
	}

	#fm-app .fm-qr-label {
		border-color: #000;
	}
}

/* ============================================================================
   Handlungsleiste in einem Hinweis (`fm-notice-actions`)

   Ein Hinweis, der sagt „legen Sie zuerst X an", ist ohne Weg dorthin eine
   Sackgasse (LV-Import-Landing ohne Kunden). Ein Knopf im Hinweis brauchte eine
   eigene Zeile mit Abstand — `fm-notice` definiert für seine Kindelemente keine.
   Bewusst nur Abstand + Umbruch, keine Farben: der Knopf behält seine Variante,
   der Hinweis seinen Akzent.
   ========================================================================== */

#fm-app .fm-notice-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--fm-s-2);
	margin: var(--fm-s-3) 0 0;
}

/* ============================================================================
   Folgeentscheidung innerhalb eines Feldes (`fm-choice` im `fm-field`)

   Ein Kästchen, das sich auf das Feld darüber bezieht (LV-Import: „Hinterlegten
   Schlüssel entfernen" unter dem OpenAI-Schlüssel), stieß ohne Abstand an das
   Eingabefeld bzw. den Hilfetext: `fm-choice` bringt als eigenständige Kachel
   keinen oberen Rand mit, `fm-field-hint` endet ohne unteren. Als eigenes
   `fm-field` daneben wäre die Zuordnung verloren gegangen (im `fm-grid-2` sogar
   in der Nachbarspalte gelandet). Bewusst nur Abstand — Rahmen, Farbe und
   Trefferfläche der Kachel bleiben unverändert.
   ========================================================================== */

#fm-app .fm-field > .fm-input + .fm-choice,
#fm-app .fm-field > .fm-field-hint + .fm-choice {
	margin-top: var(--fm-s-3);
}

/* ============================================================================
   Spaltensortierung in Übersichtstabellen (`public/js/fm-table-sort.js`)

   Das Skript hängt in jede sortierbare Überschrift einen echten `<button>` —
   damit ist die Bedienung mit Tastatur und Screenreader ohne Zusatzarbeit
   richtig (Enter/Leertaste, Zustand über `aria-sort` an der Zelle).

   Der Knopf übernimmt die Innenabstände der Zelle und füllt sie vollständig
   aus; die Zelle selbst gibt ihre Polsterung dafür ab. Sonst wäre nur die
   Beschriftung anklickbar und das Touch-Ziel deutlich unter 44 px.

   Schriftbild wird durchgehend geerbt (`font: inherit` u. a.), damit die
   Versalien-/Laufweiten-Einstellung aus `fm-design-system.css` erhalten bleibt
   und ein Kopf mit Knopf neben einem ohne nicht auffällt.
   ========================================================================== */

#fm-app .fm-table th.fm-th-sortable {
	padding: 0;
}

#fm-app .fm-table th.fm-th-sortable .fm-th-sort {
	display: flex;
	align-items: center;
	justify-content: flex-start;
	gap: var(--fm-s-1);
	width: 100%;
	min-height: 44px; /* Touch-Ziel (CLAUDE.md §7.2), wie in fm-design-system.css. */
	margin: 0;
	padding: 0.55rem var(--fm-cell-x);
	border: 0;
	border-radius: 0;
	background: none;
	font: inherit;
	letter-spacing: inherit;
	text-transform: inherit;
	color: inherit;
	text-align: inherit;
	cursor: pointer;
}

/* Zahlenspalten sind rechtsbündig – der Knopfinhalt folgt. */
#fm-app .fm-table th.fm-num .fm-th-sort,
#fm-app .fm-table th.fm-text-right .fm-th-sort {
	justify-content: flex-end;
}

#fm-app .fm-table th.fm-th-sortable .fm-th-sort:hover {
	background: var(--fm-n-150);
	color: var(--fm-primary-600);
}

/* Der globale Fokusring steht 2 px AUSSERHALB – in einer randlosen, klebenden
   Kopfzelle wird er dort abgeschnitten. Deshalb nach innen gelegt. */
#fm-app .fm-table th.fm-th-sortable .fm-th-sort:focus-visible {
	outline: 2px solid var(--fm-focus-color);
	outline-offset: -2px;
}

#fm-app .fm-table th.fm-th-sortable .fm-th-sort-icon {
	flex: none;
	font-size: var(--fm-fs-sm);
	line-height: 1;
	opacity: 0.35;
}

#fm-app .fm-table th.fm-th-sortable .fm-th-sort-icon::before {
	content: "↕";
}

#fm-app .fm-table th.fm-th-sortable:hover .fm-th-sort-icon {
	opacity: 0.7;
}

#fm-app .fm-table th[aria-sort="ascending"] .fm-th-sort-icon,
#fm-app .fm-table th[aria-sort="descending"] .fm-th-sort-icon {
	opacity: 1;
	color: var(--fm-primary-600);
}

#fm-app .fm-table th[aria-sort="ascending"] .fm-th-sort-icon::before {
	content: "↑";
}

#fm-app .fm-table th[aria-sort="descending"] .fm-th-sort-icon::before {
	content: "↓";
}

/* Hinweis unter einer Tabelle mit Seitennavigation: sortiert wird die Seite,
   nicht der Gesamtbestand. Erscheint erst nach dem ersten Sortieren. */
#fm-app .fm-table-sort-hint {
	margin: var(--fm-s-2) 0 0;
}

/* Auswahlleiste für die mobile Kartenansicht.

   Standardmäßig AUS: solange der Tabellenkopf sichtbar ist, ist er die
   Bedienung — eine zweite daneben wäre Redundanz. Sichtbar wird die Leiste
   ausschließlich in demselben Fenster, in dem `fm-mobile-usability.css` die
   `.fm-table-cards`-Tabelle zur Kartenliste macht und ihren `<thead>`
   ausblendet. Deshalb ist die Grenze hier bewusst identisch: **640 px**, nicht
   768 px — der 768er-Block derselben Datei vergrößert nur Touch-Ziele, der
   Kartenumbruch steht im 640er-Block. Eine Abweichung ließe entweder beide
   Bedienungen gleichzeitig stehen (641–768 px) oder keine.
   Bewacht von `tests/table-sort-smoke.php` (Abschnitt 6): der Test liest beide
   Grenzen aus den Dateien und vergleicht sie. */
#fm-app .fm-table-sort-bar {
	display: none;
}

@media ( max-width: 640px ) {
	#fm-app .fm-table-sort-bar {
		display: flex;
		flex-wrap: wrap;
		align-items: center;
		gap: var(--fm-s-2);
		margin-bottom: var(--fm-s-3);
	}

	/* Beschriftung auf eigener Zeile, damit Auswahlfeld und Richtungsknopf
	   nebeneinander die volle Breite bekommen. */
	#fm-app .fm-table-sort-bar .fm-table-sort-label {
		flex: 1 0 100%;
		margin-bottom: 0;
	}

	#fm-app .fm-table-sort-bar .fm-table-sort-select {
		flex: 1 1 10rem;
		min-width: 0;
		min-height: 44px;
	}

	#fm-app .fm-table-sort-bar .fm-table-sort-dir {
		flex: 0 0 auto;
		min-height: 44px;
	}
}

@media print {
	#fm-app .fm-table th.fm-th-sortable .fm-th-sort-icon,
	#fm-app .fm-table-sort-hint,
	#fm-app .fm-table-sort-bar {
		display: none;
	}

	#fm-app .fm-table th.fm-th-sortable .fm-th-sort {
		min-height: 0;
		padding: 0;
	}
}

/* ==========================================================================
   .fm-form — Formular-Grundgerüst (fehlende Komponente, §7.1)

   `fm-form` steht 68× in 50 Dateien (66× am <form>), hatte aber projektweit
   KEINE Regel: die Klasse wurde still ungestylt ausgeliefert. `.fm-form-actions`
   gibt es, `.fm-form` gab es nicht. Aufgefallen über die CSS-Abdeckungsprüfung
   in tests/time-accounts-smoke.php.

   BEWUSST KEIN Abstand zwischen den Feldern: den bringt `.fm-field` schon selbst
   mit (`margin-bottom: var(--fm-s-5)` in fm-design-system.css). Ein `gap` oder
   ein Kind-Rand hier würde die Rhythmik verdoppeln — genau die Falle, wegen der
   die Komponente nicht einfach „nachgereicht" werden konnte.

   Bleibt der eine Fall, den das Feld nicht selbst lösen kann: endet ein Formular
   mit einem Feld statt mit `.fm-form-actions`, hängt dessen unterer Rand als
   Leerraum am Formularende (Filterleisten, Schnellmasken). `.fm-form-actions`
   bleibt unangetastet — seine negativen Ränder lassen die Aktionsleiste bewusst
   bis an die Kartenkante laufen.
   ========================================================================== */

#fm-app .fm-form > .fm-field:last-child {
	margin-bottom: 0;
}

/* ---------------------------------------------------------------------------
   Abhängige Auswahl im Rahmenvertragsformular (fm-contract-form-scope.js)
   ---------------------------------------------------------------------------
   Das Skript blendet nicht passende Objektzeilen und – wenn keine übrig bleibt –
   das ganze Auswahlgitter über das `hidden`-Attribut aus. Gegen die Regel
   `#fm-app .fm-checkbox-grid { display: grid }` (fm-design-system-extensions.css)
   verliert das UA-Stylesheet, das leere Gitter bliebe als Lücke stehen. Die
   Zeilenregel steht vorsorglich daneben, falls ein Paketstand `fm-checkbox-row`
   später ein eigenes `display` gibt. */
#fm-app .fm-checkbox-grid[hidden],
#fm-app .fm-checkbox-row[hidden] {
	display: none;
}

/* ==========================================================================
   .fm-modal — Rückfrage-Dialog (fehlende Komponente, §7.1)

   Basis ist das native <dialog>: Backdrop, Fokusfalle, Escape-zum-Schließen und
   die Top-Layer-Stapelung liefert der Browser. Nachgebaut wird davon nichts —
   das Skript public/js/fm-modal.js ruft nur showModal()/close().

   ZWEI Darstellungen, bewusst:
   - `:modal` (mit JS, showModal) → mittiger Dialog über abgedunkeltem Hintergrund.
   - `[open]:not(:modal)` (OHNE JS, `open` serverseitig gesetzt) → normale Karte im
     Seitenfluss. Ein <dialog open> steht sonst als position:absolute-Kasten über
     dem Inhalt; ohne diese Regel wäre der No-JS-Weg unbenutzbar.

   Farben ausschließlich über --fm-*-Tokens ⇒ Dunkelmodus erbt automatisch. Der
   Backdrop trägt bewusst einen festen Schleier: ::backdrop liegt im Top-Layer und
   soll auf hell wie dunkel gleich abdunkeln.
   ========================================================================== */

#fm-app dialog.fm-modal {
	/* 36rem: gerade breit genug, dass die drei Entscheidungen (springen / neu /
	   abbrechen) am Schreibtisch in EINE Zeile passen. Schmaler bricht „Abbrechen"
	   allein um und liest sich wie eine nachrangige vierte Möglichkeit. */
	max-width: min(36rem, calc(100vw - var(--fm-s-8)));
	width: 100%;
	padding: 0;
	border: 0;
	border-radius: var(--fm-r-lg);
	background: transparent;
	color: var(--fm-text);
}

#fm-app dialog.fm-modal::backdrop {
	background: rgba(13, 27, 42, 0.55);
}

#fm-app dialog.fm-modal .fm-modal-panel {
	padding: var(--fm-s-6);
	border: 1px solid var(--fm-stroke);
	border-radius: var(--fm-r-lg);
	background: var(--fm-surface);
	box-shadow: var(--fm-e-3);
}

#fm-app dialog.fm-modal .fm-modal-head {
	display: flex;
	align-items: flex-start;
	justify-content: space-between;
	gap: var(--fm-s-3);
	margin-bottom: var(--fm-s-3);
}

#fm-app dialog.fm-modal .fm-modal-title {
	margin: 0;
	font-size: var(--fm-fs-lg);
	color: var(--fm-heading);
}

#fm-app dialog.fm-modal .fm-modal-close {
	flex: 0 0 auto;
	display: inline-flex;
	align-items: center;
	justify-content: center;
	min-width: 44px;
	min-height: 44px;
	margin: calc(var(--fm-s-2) * -1) calc(var(--fm-s-2) * -1) 0 0;
	border-radius: var(--fm-r-md);
	color: var(--fm-muted-fg);
	font-size: var(--fm-fs-xl);
	line-height: 1;
	text-decoration: none;
}

#fm-app dialog.fm-modal .fm-modal-close:hover {
	background: var(--fm-surface-2);
	color: var(--fm-heading);
}

#fm-app dialog.fm-modal .fm-modal-list {
	margin: var(--fm-s-4) 0 0;
	padding: 0;
	list-style: none;
	display: flex;
	flex-direction: column;
	gap: var(--fm-s-2);
}

#fm-app dialog.fm-modal .fm-modal-list li {
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--fm-s-2);
	padding: var(--fm-s-3);
	border: 1px solid var(--fm-stroke-soft);
	border-radius: var(--fm-r-md);
	background: var(--fm-surface-2);
}

#fm-app dialog.fm-modal .fm-modal-actions {
	display: flex;
	flex-wrap: wrap;
	gap: var(--fm-s-2);
	margin-top: var(--fm-s-6);
}

/* Ohne JS: als Karte im Fluss statt als schwebender Kasten. */
#fm-app dialog.fm-modal[open]:not(:modal) {
	position: static;
	max-width: none;
	margin: var(--fm-s-5) 0;
}

@media (max-width: 640px) {
	#fm-app dialog.fm-modal .fm-modal-actions .fm-btn {
		flex: 1 1 100%;
		justify-content: center;
	}
}

/* --------------------------------------------------------------------------
   Dashboard-Kachelauswahl (Bearbeitungsmodus ?fm_dash_edit=1)
   Neue Plugin-Komponente (kein Paket-Bestandteil), daher hier in den
   Overrides: Im Auswahlmodus wird jede KPI-Kachel zum <label> um die eigene
   Checkbox (templates/components/card-kpi.php, 'select_mode') — die ganze
   Kachel ist Touch-Ziel. Abgewählte Kacheln werden gedimmt (progressive
   Enhancement über :has(); ohne Support bleibt die Checkbox maßgeblich).
   -------------------------------------------------------------------------- */
#fm-app .fm-kpi-select {
	position: relative;
	margin: 0;
	cursor: pointer;
}

#fm-app .fm-kpi-select .fm-kpi-select-box {
	position: absolute;
	top: var(--fm-s-3);
	right: var(--fm-s-3);
	width: 1.25rem;
	height: 1.25rem;
	margin: 0;
	accent-color: var(--fm-primary-600);
	cursor: pointer;
}

/* Tastatur-Fokus der überlagerten Checkbox an der Kachel sichtbar machen. */
#fm-app .fm-kpi-select:focus-within {
	border-color: var(--fm-primary-300);
	box-shadow: var(--fm-e-2);
}

/* Abgewählte Kachel dimmen — reine Nettigkeit, Funktion hängt nicht daran. */
#fm-app .fm-kpi-select:has(.fm-kpi-select-box:not(:checked)) {
	opacity: 0.55;
	border-style: dashed;
}

/* ---------------------------------------------------------------------------
   Seitenkopf mit Aktionen: mobiler Abstand (`fm-page-head-row`)
   ---------------------------------------------------------------------------
   Zur Einordnung, weil die Klasse leicht als kaputt gelesen wird: Das
   Flex-Layout stammt aus der Basisschicht `fm-tailadmin`
   (public/vendor/tailadmin/tailadmin.css — `display: flex`, `flex-wrap`,
   `justify-content: space-between`, `align-items: flex-start`). In
   fm-design-system.css:302 steht nur noch `gap`, was das Layout NICHT aufhebt:
   nicht erneut deklarierte Eigenschaften bleiben aus der früheren Schicht
   gültig. Titelblock und Aktionsknopf stehen also nebeneinander — hier ist
   bewusst kein `display: flex` nachgetragen, das wäre eine zweite Quelle der
   Wahrheit für eine bereits wirksame Regel.

   Kaputt war allein der mobile Abstand: fm-mobile-usability.css setzt unter
   `@media ( max-width: 640px )` ein `gap: 0.5rem`, doch fm-design-system.css:302
   setzt danach unbedingt `gap: var(--fm-s-4)` (1rem). Media Queries erhöhen die
   Spezifität nicht, und die Design-System-Schicht lädt später (eigener Hook mit
   Prio 100 gegen Prio 10 der Modul-Stylesheets) — der engere mobile Abstand war
   damit wirkungslos (gemessen bei 375px: 16px statt der gewollten 8px).

   Diese Schicht lädt als letzte und stellt die Absicht wieder her. Bewusst nur
   `gap`: `flex-direction: column` und `align-items: flex-start` aus
   fm-mobile-usability.css greifen weiterhin, weil das Design-System sie nicht
   überschreibt. */
@media ( max-width: 640px ) {
	#fm-app .fm-page-head-row {
		gap: var(--fm-s-2);
	}
}

/* Angebotsformular: aufklappbare interne Kalkulation je Positionszeile (A3,
   v1.72.165). Neue Komponente — im Design-System-Paket gibt es keinen
   Positions-Kalkblock, deshalb gehört sie laut §7.1 in diese Schicht.
   Natives <details> = No-JS-fähig; das JS spiegelt nur live. */
#fm-app .fm-offer-item-calc {
	margin: var(--fm-s-1) 0 var(--fm-s-2);
	padding: var(--fm-s-2);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-surface-2);
}

#fm-app .fm-offer-item-calc > summary {
	cursor: pointer;
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
	display: flex;
	align-items: center;
	gap: var(--fm-s-2);
	min-height: 44px; /* Touch-Ziel (Außendienst) */
}

#fm-app .fm-offer-calc-grid {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 150px, 1fr ) );
	gap: var(--fm-s-2);
	margin-top: var(--fm-s-2);
}

#fm-app .fm-offer-calc-field .fm-field-label {
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
}

#fm-app .fm-offer-calc-derived {
	margin: var(--fm-s-2) 0 0;
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
	display: flex;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--fm-s-1) var(--fm-s-2);
}

/* --------------------------------------------------------------------------
 * Navigations-Favoriten „Mein Arbeitsbereich" (Sidebar-Stern + Favoriten-Block).
 * Warum hier: eigene Komponente des Plugins, das Design-System-Paket kennt sie
 * nicht. Das Toggle-Formular umschließt die ganze Gruppenliste und muss
 * layoutneutral bleiben; der Stern braucht ein 44-px-Touch-Ziel, ohne die
 * schlanke Nav-Zeile aufzublähen (negative Ränder gleichen das aus).
 * ----------------------------------------------------------------------- */
#fm-app .fm-nav-fav-scope {
	display: contents;
}

#fm-app .fm-nav-row {
	display: flex;
	align-items: center;
}

#fm-app .fm-nav-row .fm-nav-link {
	flex: 1 1 auto;
	min-width: 0;
}

#fm-app .fm-nav-fav {
	min-width: 44px;
	min-height: 44px;
	margin: -4px 0;
	padding: 0;
	background: none;
	border: 0;
	border-radius: var(--fm-r-sm);
	color: var(--fm-sidebar-muted, var(--fm-muted-fg));
	display: grid;
	place-items: center;
	cursor: pointer;
	opacity: 0;
}

#fm-app .fm-nav-fav svg {
	width: 16px;
	height: 16px;
}

#fm-app .fm-nav-row:hover .fm-nav-fav,
#fm-app .fm-nav-row:focus-within .fm-nav-fav,
#fm-app .fm-nav-fav[aria-pressed="true"] {
	opacity: 1;
}

#fm-app .fm-nav-fav[aria-pressed="true"] svg path {
	fill: currentColor;
}

#fm-app .fm-nav-fav:focus-visible {
	outline: 2px solid currentColor;
	outline-offset: -2px;
}

/* Touch-Geräte haben kein Hover: Stern immer zeigen. */
@media (hover: none) {
	#fm-app .fm-nav-fav {
		opacity: 1;
	}
}

/* „Bearbeiten …"-Zeile am Ende des Favoriten-Blocks: bewusst gedämpft. */
#fm-app .fm-nav-link-edit {
	font-size: var(--fm-fs-sm);
	opacity: 0.75;
}

/* A6 (v1.72.166): Turnus-Block je Positionszeile teilt sich die Grid-Optik mit
   dem Kalkblock; Wochentags-Häkchen laufen inline. */
#fm-app .fm-offer-item-turnus {
	margin: var(--fm-s-1) 0 var(--fm-s-2);
	padding: var(--fm-s-2);
	border: 1px solid var(--fm-border);
	border-radius: var(--fm-radius);
	background: var(--fm-surface-2);
}

#fm-app .fm-offer-item-turnus > summary {
	cursor: pointer;
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
	min-height: 44px;
	display: flex;
	align-items: center;
}

#fm-app .fm-choice-inline {
	display: inline-flex;
	align-items: center;
	gap: 4px;
	margin-right: var(--fm-s-2);
	min-height: 44px;
}

/* Bearbeitungsansicht: Menüpunkt-Zeile (Icon außerhalb der Nav braucht ein Maß). */
#fm-app .fm-nav-fav-entry {
	display: inline-flex;
	align-items: center;
	gap: var(--fm-s-2);
}

#fm-app .fm-nav-fav-entry .fm-icon {
	width: 18px;
	height: 18px;
	flex: 0 0 auto;
}

/* Vertrags-Leitstand (Paket V2, v1.72.171): Phasen-Stepper + offene Punkte auf
   der Vertragsakte. Generalisierte Fassung des Lexoffice-Wizard-Musters
   (.fm-lex-steps lädt nur auf der Migrationsansicht — bewusst eigene, hier
   global verfügbare Klassen; §7.1: neue Komponenten in diese Schicht). */
#fm-app .fm-guidance-steps {
	display: flex;
	gap: var(--fm-s-2);
	overflow-x: auto;
	list-style: none;
	margin: 0 0 var(--fm-s-3);
	padding: 0;
}

#fm-app .fm-guidance-step {
	display: flex;
	align-items: center;
	gap: var(--fm-s-1);
	white-space: nowrap;
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
	min-height: 44px;
}

#fm-app .fm-guidance-step-dot {
	width: 10px;
	height: 10px;
	border-radius: 50%;
	background: var(--fm-border);
	flex: none;
}

#fm-app .fm-guidance-step.is-done .fm-guidance-step-dot { background: var(--fm-success, #16a34a); }
#fm-app .fm-guidance-step.is-current .fm-guidance-step-dot,
#fm-app .fm-guidance-step.is-blocked .fm-guidance-step-dot { background: var(--fm-primary); }
#fm-app .fm-guidance-step.is-blocked .fm-guidance-step-dot { background: var(--fm-warning, #d97706); }
#fm-app .fm-guidance-step.is-current .fm-guidance-step-label,
#fm-app .fm-guidance-step.is-blocked .fm-guidance-step-label { color: var(--fm-heading); font-weight: 600; }

#fm-app .fm-guidance-next {
	display: flex;
	align-items: center;
	gap: var(--fm-s-2);
	flex-wrap: wrap;
	margin-bottom: var(--fm-s-2);
}

#fm-app .fm-guidance-open > summary {
	cursor: pointer;
	font-size: var(--fm-fs-sm);
	color: var(--fm-muted-fg);
	min-height: 44px;
	display: flex;
	align-items: center;
}

#fm-app .fm-guidance-items {
	list-style: none;
	margin: var(--fm-s-1) 0 0;
	padding: 0;
	display: grid;
	gap: var(--fm-s-1);
}

#fm-app .fm-guidance-item {
	display: flex;
	align-items: center;
	gap: var(--fm-s-2);
	flex-wrap: wrap;
}

#fm-app .fm-guidance-settled .fm-card-body {
	display: flex;
	align-items: center;
	gap: var(--fm-s-2);
}

/* V4 (v1.72.173): erklärte Statusaktionen — Button + Kurzbeschreibung als
   Raster, damit die Erklärzeile ihrem Knopf zugeordnet bleibt. */
#fm-app .fm-status-actions {
	display: grid;
	grid-template-columns: repeat( auto-fill, minmax( 220px, 1fr ) );
	gap: var(--fm-s-2);
	margin-bottom: var(--fm-s-2);
}

#fm-app .fm-status-action .fm-field-hint {
	margin-top: 2px;
}

/* Ein-/Austritts-Checklisten in der Personalakte (#27): das Designsystem hat
   keine Checklisten-Komponente — Zeile mit Inline-Toggle-Formular links und
   durchgestrichenem Text für erledigte Punkte. */
#fm-app .fm-checklist-item {
	display: flex;
	align-items: baseline;
	gap: var(--fm-space-2, 8px);
	padding: var(--fm-space-1, 4px) 0;
}

#fm-app .fm-checklist-item .fm-inline-form {
	flex: 0 0 auto;
}

#fm-app .fm-checklist-done {
	text-decoration: line-through;
	color: var(--fm-color-text-muted, #6b7280);
}

/* Objekt-Terminal (Kiosk, #38): großes Stempel-Formular für geteilte Tablets —
   Touch-Ziele weit über 44 px, eine Spalte, maximale Lesbarkeit. */
#fm-app .fm-kiosk { max-width: 560px; margin: 0 auto; }
#fm-app .fm-kiosk-input { font-size: 1.6rem; padding: 18px 16px; text-align: center; letter-spacing: .12em; }
#fm-app .fm-kiosk-submit { width: 100%; font-size: 1.3rem; padding: 20px; margin-top: 8px; }
#fm-app .fm-kiosk-result { font-size: 1.2rem; }

/* API & Webhooks (#49): Einmal-Anzeige frischer Geheimnisse gut kopierbar. */
#fm-app .fm-api-secret { display: block; margin-top: 6px; padding: 10px 12px; font-size: 1.05rem; word-break: break-all; user-select: all; background: var(--fm-surface-2); border-radius: 4px; }

/* ============================================================================
   Touch-Ziele ≥ 44 px (Befund B-022, CLAUDE.md §7.2)

   Die Regel „Touch-Ziele ≥ 44 px" stand bisher nur in CLAUDE.md und war
   technisch nirgends verankert. Live gemessen am 21.08.2026 in einem
   375-px-Rahmen auf fm_view=time: 14 von 58 Bedienelementen im Formular und
   7 von 50 in der Wochenansicht lagen darunter — betroffen waren gemeinsame
   Komponentenklassen, der Mangel galt also für ALLE mobilen Ansichten:

     .fm-btn-sm      34 px   (Wochennavigation — das Hauptbedienelement mobil)
     .fm-btn-xs      28 px
     .fm-chip        34 px   (Statusfilter)
     .fm-btn-text    34 px
     .fm-burger      40 px   .fm-search-toggle 40 px   .fm-theme-toggle 40 px
     .fm-btn         42 px   .fm-btn-icon 42 px
     .fm-input/.fm-select/.fm-textarea 42 px

   Gelöst im Design-System statt je Modul, und bewusst NUR dort, wo mit dem
   Finger bedient wird: am Zeigergerät (pointer: coarse) sowie auf schmalen
   Rahmen — Letzteres, damit die Messung im Desktop-Browser bei 375 px
   reproduzierbar bleibt (dort meldet der Browser `pointer: fine`). Am
   Arbeitsplatz mit Maus bleibt die kompakte Dichte erhalten.

   Kein `!important` nötig: gleiche Spezifität wie die Systemregeln, diese
   Datei lädt zuletzt. Eine @media-Bedingung erhöht die Spezifität NICHT —
   sie gewinnt hier allein über die Quellreihenfolge.
   ============================================================================ */
#fm-app {
	--fm-touch-min: 44px;
}

@media (pointer: coarse), (max-width: 768px) {

	#fm-app .fm-btn,
	#fm-app .fm-btn-sm,
	#fm-app .fm-btn-xs,
	#fm-app .fm-btn-text,
	#fm-app a.fm-chip,
	#fm-app .fm-chip,
	#fm-app .fm-input,
	#fm-app .fm-select,
	#fm-app .fm-textarea {
		min-height: var(--fm-touch-min);
	}

	/* Quadratische Icon-Flächen: auch in der Breite, sonst bleibt das Ziel schmal. */
	#fm-app .fm-btn-icon,
	#fm-app .fm-burger,
	#fm-app .fm-search-toggle,
	#fm-app .fm-theme-toggle {
		width: var(--fm-touch-min);
		height: var(--fm-touch-min);
		min-width: var(--fm-touch-min);
		min-height: var(--fm-touch-min);
	}

	/* Kästchen und Auswahlknöpfe: Ziel ist das Label, nicht das 18-px-Kästchen. */
	#fm-app .fm-choice {
		min-height: var(--fm-touch-min);
		align-items: center;
	}

	/* Navigation und Menüs (Nachtrag B-028, live gemessen 22.08.2026).
	   Diese Klassen setzen KEINE Höhe, ihre 38 px entstehen aus dem Padding —
	   der Prüfstand konnte sie deshalb nicht sehen, und die erste Runde ließ sie
	   aus. Aufgefallen sind sie erst beim Nachmessen mit GEÖFFNETER Schublade:
	   allein die Seitenleiste stellte 43 Ziele unter 44 px, und sie ist mobil das
	   Hauptnavigationselement. `align-items: center` hält den Text mittig, wenn
	   die Mindesthöhe über die Zeilenhöhe hinausgeht.

	   `a.fm-user-menu-item` steht bewusst ZUSÄTZLICH da und ist keine Dopplung:
	   fm-design-system.css setzt dort `min-height: 2.5rem` über eine Selektorliste,
	   die mit `#fm-app a.fm-user-menu-item` beginnt (Zeile 1465). Der Typ-Selektor
	   hebt die Spezifität an, und die Quellreihenfolge allein reicht dann nicht mehr —
	   ohne diese Zeile bleibt der Eintrag bei 40 px. Nachgemessen, nicht vermutet. */
	#fm-app .fm-nav-link,
	#fm-app .fm-nav-group-title,
	#fm-app .fm-cat-link,
	#fm-app .fm-subnav-link,
	#fm-app .fm-user-menu-item,
	#fm-app a.fm-user-menu-item,
	#fm-app .fm-bell-toggle {
		display: flex;
		align-items: center;
		min-height: var(--fm-touch-min);
	}
}
