/* komponenten.css -- die gemeinsame CSS-Ebene des Portals (D-15-11).
   Plan 15-05, Welle 4.

   HIERHIN gehoert, was MEHR ALS EIN Werkzeug benutzt: Klappelemente,
   Abzeichen, Tabellen/Listen, Knoepfe, Formularfelder, Maskenrahmen.

   NICHT hierhin gehoert:
   - das Seitengeruest (Kopfzeile, Seitenleiste, Startseite) -- das bleibt
     in style.css, weil es genau einmal existiert und kein zweites
     Werkzeug es teilt.
   - werkzeugeigene Besonderheiten (z.B. .crm-baum, .pj-balken) -- die
     bleiben blueprintlokal.

   LADEREIHENFOLGE (base.html): komponenten.css -> head_extra
   (Blueprint-CSS) -> Tokenblock -> style.css. Diese Datei ist die
   UNTERSTE Schicht -- eine Blueprint-Datei muss eine gemeinsame Regel bei
   gleicher Spezifitaet noch ueberschreiben koennen. Deshalb aendert diese
   Welle vorlaeufig NICHTS am Bild: die Bestandsregeln in crm.css,
   projekte.css, style.css usw. liegen SPAETER in der Kaskade und gewinnen
   bei gleicher Spezifitaet weiterhin. Sie werden erst in den Plaenen
   15-06/15-08/15-09/15-10/15-11 entfernt, wenn die jeweilige Datei ohnehin
   fuer die Tokenisierung angefasst wird (D-15-06). Bis dahin gilt diese
   Datei nur als Netz, nicht als sichtbare Quelle.

   T-15-HARTWERT (tests/test_gestaltung_struktur.py) gibt dieser Datei
   Budget 0: kein Hex-Wert, kein rgb()/rgba() -- nur var(--...) und reine
   Layoutwerte ohne Skalenbezug. */

/* ---------------------------------------------------------------------
   0. Schriftauslieferung (D-18-02, D-18-07, Plan 18-03)

   Nunito Sans wird ab jetzt selbst ausgeliefert statt von Google geladen --
   kein Aufruf mehr an fonts.googleapis.com/fonts.gstatic.com beim
   Seitenaufbau, die IPs der Nutzer verlassen das Haus nicht (T-18-07).
   Datei: app/static/fonts/NunitoSans-latin.woff2, per fontTools subset aus
   der Google-Herkunftsschrift erzeugt (Plan 18-01).

   HIER und nicht in base.html: die Datei-URL ist eine gewoehnliche
   relative CSS-URL, kein Jinja-Ausdruck. Ein Cache-Busting-Anhaenger ist
   nicht noetig, weil eine Schriftdatei praktisch nie mehr wechselt -- ein
   Wechsel bekaeme einen neuen Dateinamen (z.B. NunitoSans-latin-v2.woff2).

   format('woff2') und nicht format('woff2-variations'): Googles eigene
   ausgelieferte CSS schreibt fuer dieselbe nachweislich variable Datei
   ebenfalls format('woff2') -- Browser werten die fvar-Tabelle selbst aus,
   das Format-Schluesselwort ist nur ein Hinweis, keine Steuerung.

   unicode-range deckt Ä Ö Ü ä ö ü ß, den Gedankenstrich (U+2013, der
   None-Fall des kommenden |euro-Filters) und € (U+20AC) ab -- "latin-ext"
   wird nicht gebraucht. */
@font-face {
  font-family: 'Nunito Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/NunitoSans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* ---------------------------------------------------------------------
   1. Werkzeugmaske (D-15-11, Fortsetzung von c162527)
   --------------------------------------------------------------------- */

/* Heute sechsfach verteilt (.crm, .pj, .mon, .mkt, .accountant je
   blueprintlokal; .adm-wrap/.admin-wrap/.fb-page hatten ihn schon).
   `.werkzeug-maske` ist der kanonische Name fuer NEUES Markup, die acht
   Bestandsnamen stehen gleichberechtigt daneben (Sammelselektor statt
   Vorlagenaenderung, s. 15-05-PLAN.md „Sammelselektor").

   BEWUSSTE, BENANNTE AENDERUNG: `.pj` stand bisher auf max-width: 1180px,
   `.mon` und `.mkt` auf 1100px. Hier gehen alle neun auf 1280px -- der
   Vertrag gibt eine gemeinsame Breite vor, damit das Portal wie EIN
   Programm wirkt (D-15-14). Das ist sichtbar und gewollt, sobald die
   jeweilige Bestandsregel entfaellt -- kein Versehen, nicht zurueckdrehen. */
.werkzeug-maske, .crm, .pj, .mon, .mkt, .accountant, .adm-wrap,
.admin-wrap, .fb-page {
  padding: var(--space-lg);
  box-sizing: border-box;
  max-width: 1280px;
}

/* ---------------------------------------------------------------------
   1a. Seitenkopf und Betragskomponente (D-18-01, D-18-03, D-18-05,
       Plan 18-05, Welle 3)
   --------------------------------------------------------------------- */

/* `.werkzeug-kopf` ist die gemeinsame Kopfklasse fuer alle neun Werkzeuge,
   Name nach der bestehenden Reihe `.werkzeug-maske`/`.werkzeug-liste`/
   `.werkzeug-klapp` -- keine neue Namenskonvention. Sie steht HIER, nicht
   in style.css und nicht blueprintlokal, weil komponenten.css auf JEDER
   Seite geladen wird, noch vor der Blueprint-CSS-Datei -- `aufgaben` und
   `einstellungen` haben keine eigene CSS-Datei und haetten sonst gar
   keine Regel (an genau dieser Stelle ist Plan 15-13 schon einmal
   gestolpert).

   KEINE @media-Abfrage noetig: bei flex-wrap: wrap rutscht
   .werkzeug-kopf-aktionen von selbst in eine eigene Zeile und bleibt
   durch margin-left: auto am rechten Rand SEINER Zeile (Flexbox wendet
   margin: auto je Zeile an -- Spezifikationsverhalten, kein Sonderfall);
   der Zeilenabstand kommt aus dem gap am Elternelement.

   Die Unterzeile steht FEST auf 400, damit gar nicht erst die Versuchung
   entsteht, sie mit font-weight: 500 vom Fliesstext abzuheben.
   GEWICHT500_BUDGETS steht fuer alle acht Dateien auf exakt 0 und bleibt
   dort. */
.werkzeug-kopf {
  display: flex; align-items: flex-start; flex-wrap: wrap;
  gap: var(--space-md);
  margin-bottom: var(--space-lg);
}
/* Nachtrag Plan 18-06: das Zielmarkup aus 18-UI-SPEC.md gruppiert h1 und
   Unterzeile in einem eigenen `.werkzeug-kopf-text`-Behaelter (damit beide
   als EIN Flex-Kind neben `.werkzeug-kopf-aktionen` stehen, statt einzeln
   zu umbrechen) -- Plan 18-05 hat vier Regeln fuer diesen Abschnitt
   geliefert, aber keine fuer diesen Wrapper; ohne Regel faellt die Klasse
   durch test_jede_klasse_hat_eine_ladende_css_datei (T-15-KLASSE), weil sie
   in keiner geladenen CSS-Datei vorkommt. min-width: 0 ist der uebliche
   Flex-Fix, damit ein langer Werkzeugname/Unterzeilentext das Flex-Kind
   nicht ueber seine Content-Breite hinaus zwingt und `.werkzeug-kopf-aktionen`
   verdraengt. */
.werkzeug-kopf-text {
  min-width: 0;
}
.werkzeug-kopf h1 {
  font-size: var(--font-size-2xl); line-height: 1.15; font-weight: 600;
  margin: 0 0 var(--space-2xs); letter-spacing: -0.02em;
}
.werkzeug-kopf-unterzeile {
  margin: 0; color: var(--color-text-muted); font-size: var(--font-size-md);
  font-weight: 400;
}
.werkzeug-kopf-aktionen {
  margin-left: auto; display: flex; gap: var(--space-xs); flex-wrap: wrap;
}

/* Betragskomponente (D-18-01 "Betrag gross mit Anzahl klein darunter"),
   geteilte Klasse statt blueprintlokaler Dopplung (D-15-11-Muster).
   font-variant-numeric: tabular-nums ist die konkrete Umsetzung von
   "Ziffern in Tabellenbreite" (ROADMAP-Kriterium 4) -- ohne Tabellenziffern
   verschieben unterschiedlich breite Ziffern die Dezimalstellen sichtbar
   gegeneinander, und eine rechtsbuendige Spalte wirkt trotz
   text-align: right uneben. */
.betrag {
  display: block; font-variant-numeric: tabular-nums; font-weight: 600;
}
.betrag-anzahl {
  display: block; color: var(--color-text-faint); font-size: var(--font-size-xs);
}

/* ---------------------------------------------------------------------
   2. Listen- und Tabellenvertrag (D-15-15, D-15-16, D-15-17)
   --------------------------------------------------------------------- */

/* Die neun Bestandstabellenklassen, ermittelt ueber
   `grep -rn "border-collapse" app/static/css app/blueprints/*/static/*.css`
   (2026-09-18): .crm-table, .crm-pos-tabelle, .pj-table, .pj-pos,
   .mon-table, .acc-table, .mkt-table, .fb-table, .admin-table.
   `.werkzeug-liste` ist der kanonische Name fuer NEUES Markup. */

/* Dichte (D-15-15): 8 (--space-xs, padding-block) + 20 (line-height) + 8
   (--space-xs) = 36px Zeilenhoehe bei 13px Grundschrift. */
.werkzeug-liste th, .werkzeug-liste td,
.crm-table th, .crm-table td,
.crm-pos-tabelle th, .crm-pos-tabelle td,
.pj-table th, .pj-table td,
.pj-pos th, .pj-pos td,
.mon-table th, .mon-table td,
.acc-table th, .acc-table td,
.mkt-table th, .mkt-table td,
.fb-table th, .fb-table td,
.admin-table th, .admin-table td {
  padding: var(--space-xs) var(--space-sm);
  line-height: 20px;
}

/* Trennlinie (D-15-16): die neue Haarlinie. KEINE Zebrastreifen -- eine
   zweite Flaechenfarbe wuerde mit den sechs Statusrollen um Aufmerksamkeit
   konkurrieren (D-15-16). Nicht "ergaenzen". */
.werkzeug-liste td,
.crm-table td,
.crm-pos-tabelle td,
.pj-table td,
.pj-pos td,
.mon-table td,
.acc-table td,
.mkt-table td,
.fb-table td,
.admin-table td {
  border-bottom: 1px solid var(--color-border-tertiary);
}

/* Hervorhebung unter dem Zeiger (D-15-16 -> D-19-01, Plan 19-03, Welle 3):
   die vormals handgemischte Vierprozent-Tinte gegen die tertiaere
   Hintergrundflaeche ist abgeloest -- die Zeile bezieht ihren
   Hover-Hintergrund jetzt aus demselben Token wie der Knopf
   (.btn:hover, weiter unten in dieser Datei), nur leiser: sie bekommt
   keine Erhebung und keinen Druckpunkt (Variante C gibt ihr keinen).

   Rolle Zeile (D-18-05, Plan 18-05, Welle 3): --shadow-sm stuetzt den
   Hover zusaetzlich zum Hintergrundwechsel. KEIN Dunkelzweig noetig --
   --shadow-sm loest sich im Dunkeln ueber den Tokensatz auf `none` auf,
   und --color-primary-soft traegt den Hover dort allein. Es existiert
   keine konkurrierende Dunkelregel auf tbody tr -- das ist im UI-SPEC
   (19-UI-SPEC.md, "Wo die Regel NICHT gilt") ausdruecklich gegengeprueft:
   reine Custom-Property-Aufloesung, kein Kaskadenkonflikt, deshalb bleibt
   diese Regel ohne eigenen html[data-thema="dunkel"]-Zweig. */
.werkzeug-liste tbody tr:hover,
.crm-table tbody tr:hover,
.crm-pos-tabelle tbody tr:hover,
.pj-table tbody tr:hover,
.pj-pos tbody tr:hover,
.mon-table tbody tr:hover,
.acc-table tbody tr:hover,
.mkt-table tbody tr:hover,
.fb-table tbody tr:hover,
.admin-table tbody tr:hover {
  background: var(--color-primary-soft);
  box-shadow: var(--shadow-sm);
}

/* Mitlaufender Kopf (D-15-17). box-shadow statt border-bottom, und das
   ist kein Geschmack: alle neun Tabellen nutzen border-collapse: collapse;
   Firefox verschluckt unter einem mitlaufenden (sticky) Kopf den
   kollabierten Kopfrahmen (Mozilla-Fehler, in 15-RESEARCH.md belegt),
   Chromium nicht. Die Kopfflaeche muss deckend sein (kein transparent,
   kein color-mix() mit Transparenzanteil), sonst scheint durchlaufender
   Zeileninhalt durch. */
.werkzeug-liste thead th,
.crm-table thead th,
.crm-pos-tabelle thead th,
.pj-table thead th,
.pj-pos thead th,
.mon-table thead th,
.acc-table thead th,
.mkt-table thead th,
.fb-table thead th,
.admin-table thead th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--color-background-secondary);
  box-shadow: inset 0 -1px 0 var(--color-border-secondary);
  font-weight: 600;
}

/* Bekannte Ausnahme, hier nur benannt, in Plan 15-11 behoben: .fb-table
   steckt in .fb-table-wrap { overflow-x: auto } (fb.css). Sobald eine
   Achse nicht "visible" ist, hebt die CSS-Spezifikation die andere von
   "visible" auf "auto" -- der Wrapper wird selbst zum Scroll-Behaelter und
   der Kopf positioniert sich relativ zu ihm statt zu .appview. Wirkung:
   der Kopf laeuft im Fahrtenbuch NICHT mit, ohne Fehlermeldung, ohne
   Lintbefund. */

/* ---------------------------------------------------------------------
   3. Klappelement (D-15-11) -- einmal statt dreimal
   --------------------------------------------------------------------- */

/* Drei wortgleiche/fast wortgleiche Fassungen zusammengefuehrt:
   - .crm-klapp    app/blueprints/crm/static/crm.css:212-224
   - .pj-klapp     app/blueprints/projekte/static/projekte.css:166-178
   - .aufg-klapp   app/static/css/style.css:517-529,546

   Abweichung aufgeloest: das Aufklappzeichen war einmal "＋"/"−" in Gold
   (crm-klapp/pj-klapp), einmal "+"/"-" in var(--color-primary)
   (aufg-klapp). Hier gilt die aufg-klapp-Fassung: Gold besteht im hellen
   Modus 2.42:1 gegen Weiss und darf nach dem Vertrag nie alleiniger
   Bedeutungstraeger sein -- ein Aufklappzeichen ist genau das.

   Zwei weitere Abweichungen, in der Zusammenfuehrung ueber die
   Mehrheitsfassung (crm-klapp/pj-klapp, wortgleich) entschieden:
   - Hintergrund: crm-klapp/pj-klapp nutzen einen mit "#fff" rueckfallenden
     var(--color-surface), aufg-klapp ein durchscheinendes color-mix(var(--color-bg2) 40%,
     transparent). Hier: var(--color-surface) -- deckend, tokenisiert,
     Mehrheitsfassung.
   - Eckradius: alle drei Fassungen nutzen 5px hart; das mappt nach
     D-15-18/UI-SPEC auf --radius-md (6px) -- winzige, gewollte
     Verschiebung, deckungsgleich mit der bereits vollzogenen
     Radien-Verkleinerung vom 2026-09-17 (5fd0784).
   Beides bleibt wirkungslos, solange die Bestandsregeln unten in der
   Kaskade stehen (s.o.) -- wird erst bei deren Entfernung sichtbar.

   Die drei Bestandsfassungen werden HIER NICHT geloescht -- sie gehoeren
   den Plaenen 15-06 (style.css), 15-08 (crm.css) und 15-09
   (projekte.css) und verschwinden dort im selben Durchgang wie die
   Tokenisierung. Margen (crm-klapp/pj-klapp: margin-bottom; aufg-klapp:
   margin-top) bleiben bewusst lokal -- sie sind Platzierung im jeweiligen
   Seitenkontext, kein gemeinsames Komponentenmerkmal, und werden beim
   Entfernen der Bestandsfassungen neu betrachtet. */
.werkzeug-klapp, .crm-klapp, .pj-klapp, .aufg-klapp {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  padding: 0 14px;
}
.werkzeug-klapp > summary, .crm-klapp > summary, .pj-klapp > summary,
.aufg-klapp > summary {
  cursor: pointer;
  padding: 11px 0;
  font-size: var(--font-size-base);
  font-weight: 600;
  color: var(--color-text);
  list-style: none;
}
.werkzeug-klapp > summary::-webkit-details-marker,
.crm-klapp > summary::-webkit-details-marker,
.pj-klapp > summary::-webkit-details-marker,
.aufg-klapp > summary::-webkit-details-marker {
  display: none;
}
.werkzeug-klapp > summary::before, .crm-klapp > summary::before,
.pj-klapp > summary::before, .aufg-klapp > summary::before {
  content: "+ ";
  color: var(--color-primary);
}
.werkzeug-klapp[open] > summary::before, .crm-klapp[open] > summary::before,
.pj-klapp[open] > summary::before, .aufg-klapp[open] > summary::before {
  content: "\2212 ";
}
.werkzeug-klapp[open] > summary, .crm-klapp[open] > summary,
.pj-klapp[open] > summary, .aufg-klapp[open] > summary {
  border-bottom: 1px solid var(--color-border);
}
.werkzeug-klapp-inhalt, .crm-klapp-inhalt, .pj-klapp-inhalt,
.aufg-klapp-inhalt {
  padding: 14px 0;
}

/* Abstand zwischen gestapelten Klappelementen (Plan 15-08): .crm-klapp
   verliert seine lokale margin-bottom in crm.css vollstaendig (Abnahme-
   kriterium des Plans verlangt dort exakt 0 Fundstellen des Klassennamens,
   ausnahmslos) -- mehrere Werkzeuge stapeln crm-klapp aber ohne eigenen
   Abstand im Markup (z. B. eingang.html: drei Klappelemente direkt
   hintereinander). Ohne Ersatz kleben sie aneinander. 14px liegt genau
   mittig zwischen --space-sm (12px) und --space-md (16px); bei echtem
   Gleichstand gilt die Konsolidierungsrichtung der Phase (kleinerer Wert,
   siehe 15-06-SUMMARY.md). .pj-klapp bekommt denselben Wert schon jetzt,
   bleibt aber wirkungslos, bis Plan 15-09 seine eigene lokale Regel in
   projekte.css entfernt (dieselbe Kaskadenlogik wie beim Maskenselektor). */
.crm-klapp, .pj-klapp {
  margin-bottom: var(--space-sm);
}

/* ---------------------------------------------------------------------
   4. Knoepfe (D-15-11, D-15-18) -- Plan 15-06, Welle 5.
   Verschoben aus style.css:271-283 (.btn/.btn-primary/.btn-ghost/
   .btn-block) und style.css:387-389 (.btn-sm/.btn-danger). Gehoert in die
   gemeinsame Ebene, weil Knoepfe von jedem der acht Werkzeuge benutzt
   werden, nicht nur von Admin/Anmeldung, wo sie bisher lagen.
   D-15-18: Gewicht 500 entfaellt vollstaendig, 600 bleibt.

   Zustandssprache (D-19-01) -- Plan 19-02, Welle 2. `.btn` und
   `.btn:hover` stehen ZWINGEND vor allen Variantenregeln (.btn-primary,
   .btn-ghost, .btn-danger und ihre :hover-Fassungen): `.btn:hover` und
   z. B. `.btn-primary:hover` haben dieselbe Spezifitaet (0,2,0), bei
   Gleichstand gewinnt die spaetere Regel im Quelltext. Jede Variante
   behauptet deshalb jede Eigenschaft, die `.btn:hover` neu setzt, fuer
   sich selbst erneut -- sonst erbt sie versehentlich die Sekundaerfarbe
   (Bauvorschrift, `19-UI-SPEC.md`, Abschnitt "Die Dunkelzweig-
   Bauvorschrift", auf den Hellzweig verallgemeinert). NICHT nach oben
   verschieben. */
.btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--space-2xs);
  font-family: var(--font-body); font-size: var(--font-size-base);
  font-weight: 600;
  padding: var(--space-xs) var(--space-md);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border-tertiary);
  background: var(--color-surface); color: var(--color-text);
  box-shadow: var(--shadow-sm);
  cursor: pointer;
  transition: background .12s ease-out, border-color .12s ease-out,
              color .12s ease-out, box-shadow .12s ease-out,
              transform .05s ease-out;
}
/* :not(:disabled) an ALLEN Knopf-Zustandsregeln (Befund CR-04 des
   Code-Reviews zu Phase 19). .btn wird nachweislich gesperrt
   ausgeliefert: crm/eingang.html:52 und :83 setzen
   {% if not ki_aktiv %}disabled{% endif %} an einem
   class="btn btn-primary btn-sm". Ist die KI-Anbindung abgeschaltet, hob
   sich der gesperrte "Auslesen"-Knopf beim Ueberfahren um 2px, wechselte
   auf --color-primary-hover und nahm den --shadow-md an -- dieselbe
   "Versprechen ohne Handlung"-Situation, fuer die Plan 19-03 bei
   .acc-del und .fb-btn eigens eine Ausnahme geschrieben hat. An der
   Portalgrundklasse, die auf jeder Maske vorkommt, fehlte sie ganz.

   Die Aussparung haengt an ALLEN FUENF Zustandsregeln je Zweig, nicht
   nur an .btn:hover/.btn:active. Das ist Pflicht, nicht Kosmetik:
   :not(:disabled) hebt die Spezifitaet um eine Klassenstufe.
   .btn:hover:not(:disabled) allein waere (0,3,0) und haette
   .btn-primary:hover (0,2,0) geschlagen -- der Primaerknopf haette beim
   Ueberfahren den Grund des Sekundaerknopfs bekommen. Mit der Aussparung
   an allen Gliedern bleiben die Verhaeltnisse innerhalb jedes Zweigs
   exakt wie vorher, nur gesperrte Knoepfe fallen heraus. Dieselbe
   Rechnung noch einmal in beiden Dunkelzweigen, wo alle Regeln von
   (0,3,1) auf (0,4,1) wandern.

   Gegengeprueft, dass keine blueprintlokale Regel dadurch verliert:
   admin.css:57 (.btn-primary:hover) setzt filter, admin.css:29
   (.btn-copy:hover) setzt background -- beides Eigenschaften, die die
   Regeln hier nicht anfassen. Keine Ueberschneidung, keine Wirkung.

   Ausdruecklich NICHT mitentschieden: .btn:disabled traegt portalweit
   weiterhin keine eigene Gestaltung (kein cursor: not-allowed, keine
   opacity). Der Bericht nennt das als Alternative; es waere eine neue
   sichtbare Gestaltungsentscheidung und gehoert nicht in einen
   Blocker-Fix. */
.btn:hover:not(:disabled) {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
  color: var(--color-primary);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
/* Druckpunkt (D-19-01): der Kern dieser Phase, nicht die Erhebung. Bleibt
   ausserhalb jedes prefers-reduced-motion-Blocks -- ein durch
   `transition: none` von jeder Interpolation befreiter Versatz ist ein
   Zustandswechsel, keine Animation (Praezedenzfall seit Plan 15-06). */
.btn:active:not(:disabled) { transform: translateY(1px); box-shadow: var(--shadow-press); }
.btn-primary {
  background: var(--color-primary); color: var(--color-on-primary);
  border-color: var(--color-primary);
}
.btn-primary:hover:not(:disabled) {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-on-primary);
}
.btn-ghost {
  background: transparent;
  color: var(--color-text-muted); border-color: var(--color-border);
}
/* Ersetzt das bisherige color-mix() mit einem hart aufgehellten Weiss --
   ein harter Wert aus der Dunkelmodus-Vergangenheit. var(--color-surface-hover)
   traegt im Dunkelmodus den richtigen Wert von selbst. */
.btn-ghost:hover:not(:disabled) {
  background: var(--color-surface-hover); color: var(--color-text);
  border-color: var(--color-border-strong);
}
.btn-block { width: 100%; }
.btn-sm { padding: var(--space-2xs) var(--space-sm); font-size: var(--font-size-sm); }
/* .btn-danger: Weiss-auf-Rot-Text braucht denselben Kontrastmechanismus
   wie ein Primaerknopf -- var(--color-on-primary) ist in jedem Tokensatz
   genau die Farbe, die auf einer gesaettigten Flaeche des jeweiligen
   Modus lesbar bleibt (hell weiss auf dunkel, dunkel auf hell). Deshalb
   hier wiederverwendet statt eines eigenen Hex-Werts. */
.btn-danger {
  background: var(--color-danger); color: var(--color-on-primary);
  border-color: var(--color-danger);
}
/* filter bleibt die Ruecknahme fuer den Ruhezustand; die drei neuen
   Zeilen sind noetig, weil .btn:hover sonst einen Loeschknopf beim
   Ueberfahren blassblau faerbte (--color-primary-soft/--color-primary
   erben, siehe Bauvorschrift oben). */
.btn-danger:hover:not(:disabled) {
  filter: brightness(1.08);
  background: var(--color-danger); border-color: var(--color-danger);
  color: var(--color-on-primary);
}

/* ---------------------------------------------------------------------
   4a. Nebenknoepfe (D-19-01) -- Plan 19-03, Welle 3.

   Zehn Knopfarten, die aus historischen Gruenden nicht .btn tragen,
   bekommen hier denselben Druckpunkt wie .btn -- bewusst HIER und nicht
   in den fuenf betroffenen Blueprint-Dateien: komponenten.css wird auf
   JEDER Seite und VOR jeder Blueprint-CSS geladen, eine Selektorliste
   erreicht alle zehn Knopfarten aus einer Hand, und die Wildwuchsratsche
   bekommt zwei statt zwanzig neue Deklarationen zu verwalten.

   Die Liste, wortgleich in allen drei Regeln: .acc-btn, .acc-pill,
   .acc-del, .btn-copy, .icon-btn, .pmode-btn, .crm-pos-weg, .pj-pos-weg
   und die beiden Fahrtenbuch-Primaerklassen (Selektorliste unten).

   Die zweite Fahrtenbuch-Klasse steht ausdruecklich MIT in der Liste: es
   sind die vier Primaerknoepfe des Fahrtenbuchs (fahrtenbuch/index.html:40,
   detail.html:159/191/210) und sie tragen an keiner der vier Stellen
   zusaetzlich die erste Fahrtenbuch-Klasse -- eine eigenstaendige
   Knopfart, kein Zusatz. NICHT aufgenommen: .acc-btn-ghost (laeuft im
   Markup immer mit .acc-btn mit, accountant/index.html:39 --
   gegengeprueft) und .btn-primary (traegt die Portalklasse, mit Plan
   19-02 erledigt).

   Ausschliesslich box-shadow und transform werden gesetzt (Regel 2/3):
   jede der zehn Knopfarten hat bereits eine eigene Hover-Farbe in ihrer
   Blueprint-Datei, die NACH komponenten.css geladen wird -- box-shadow
   und transform setzt keine davon, also entsteht kein Kaskadenkampf und
   keine lokale Gestaltung wird ueberfahren.

   .acc-del bekommt in Hover und Druck eine :not([disabled])-Ausnahme,
   damit eine gesperrte Loeschschaltflaeche nicht so tut, als liesse sie
   sich bedienen (T-19-05). Aus demselben Grund tragen die beiden
   Fahrtenbuch-Glieder :not(:disabled): fb.css:121/128 fuehren eigene
   :disabled-Regeln, und detail.html:210 ("Als Fahrt uebernehmen") wird
   ausgeliefert, WAEHREND es gesperrt ist -- ein gesperrter Knopf,
   der sich beim Ueberfahren hebt, verspricht eine Handlung, die es nicht
   gibt.

   Kein Dunkelzweig noetig: keine der zehn Klassen traegt heute eine
   html[data-thema=...]-Regel (gegengeprueft, siehe 19-03-SUMMARY.md), und
   box-shadow/transform werden von keiner bestehenden Regel bestritten. */
.acc-btn, .acc-pill, .acc-del, .btn-copy, .icon-btn, .pmode-btn,
.crm-pos-weg, .pj-pos-weg, .fb-btn, .fb-btn-primary {
  transition: background .12s ease-out, border-color .12s ease-out,
              color .12s ease-out, box-shadow .12s ease-out,
              transform .05s ease-out;
}
/* Sperr-Aussparung an JEDEM Glied, nicht nur an dreien (Befund CR-03 des
   Code-Reviews zu Phase 19). Vorher trug nur .acc-del/.fb-btn/
   .fb-btn-primary eine Aussparung -- folgenlos: accountant.js:387 erzeugt
   die gesperrte Loeschschaltflaeche als
   <button class="acc-btn acc-del" disabled>, sie traegt also BEIDE
   Klassen, und das Geschwister .acc-btn:hover derselben Selektorliste
   traf dasselbe Element ohne jede Bedingung. :hover greift auf gesperrten
   <button>-Elementen in allen gaengigen Browsern. Der Kreis ist groesser
   als .acc-del: accountant.js setzt disabled zur Laufzeit auch an
   #acc-import und #acc-abgleich -- alle drei tragen class="acc-btn".
   (Stand Plan 20-05, gemessen mit
   `grep -n "disabled = true" app/blueprints/accountant/static/accountant.js`:
   frueher fuenf, davon sind die drei ehemaligen DATEV-Workflow-Knoepfe
   (Sammel-Upload, Download, Verbindung trennen) mit den Plaenen
   20-04/20-05 entfallen.)

   :not(:disabled) statt :not([disabled]) auch an .acc-del: die Sperrung
   wird im Accountant per JS-Property gesetzt (btn.disabled = true);
   :disabled ist der robustere Selektor fuer Formularelemente. Beide haben
   dieselbe Spezifitaet (0,1,0), die Umstellung verschiebt also nichts.

   Alle zehn Glieder bekommen dieselbe Bedingung, nicht nur die heute
   nachweislich betroffenen: eine Selektorliste, in der ein Glied die
   Aussparung eines anderen aushebelt, ist genau die Falle, die hier
   zugeschlagen hat. Uniform ist sie pruefbar, selektiv nicht.

   Zeichengleich im prefers-reduced-motion-Gegenstueck weiter unten
   nachgezogen, sonst findet die Ratsche das Paar nicht mehr. */
.acc-btn:hover:not(:disabled), .acc-pill:hover:not(:disabled),
.acc-del:hover:not(:disabled), .btn-copy:hover:not(:disabled),
.icon-btn:hover:not(:disabled), .pmode-btn:hover:not(:disabled),
.crm-pos-weg:hover:not(:disabled), .pj-pos-weg:hover:not(:disabled),
.fb-btn:hover:not(:disabled), .fb-btn-primary:hover:not(:disabled) {
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
/* Druckpunkt: wortgleich zu .btn:active, bleibt ausserhalb jedes
   prefers-reduced-motion-Blocks (derselbe Praezedenzfall seit Plan
   15-06/19-02: ein Zustandswechsel, keine Animation). */
.acc-btn:active:not(:disabled), .acc-pill:active:not(:disabled),
.acc-del:active:not(:disabled), .btn-copy:active:not(:disabled),
.icon-btn:active:not(:disabled), .pmode-btn:active:not(:disabled),
.crm-pos-weg:active:not(:disabled), .pj-pos-weg:active:not(:disabled),
.fb-btn:active:not(:disabled), .fb-btn-primary:active:not(:disabled) {
  transform: translateY(1px);
  box-shadow: var(--shadow-press);
}

/* ---------------------------------------------------------------------
   5. Formularfelder (D-15-11) -- Plan 15-06, Welle 5.
   Verschoben aus style.css:319-333 (.field/.field-label/.field input/
   .field input:focus/.hint). Die Felder gelten portalweit, nicht nur auf
   der Anmeldemaske -- D-15-11 nennt "Formularfelder" ausdruecklich. */
.field { display: flex; flex-direction: column; gap: var(--space-xs); }
.field-label { font-size: var(--font-size-sm); color: var(--color-text-muted); }
.field input {
  background: color-mix(in srgb, var(--color-bg2) 60%, transparent);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: var(--space-sm) var(--space-md); color: var(--color-text);
  font-size: var(--font-size-md); font-family: var(--font-body);
}
/* Fokusring auf das bestehende Muster gebracht (vgl. .start-kachel:
   focus-visible, .thema-schalter a:focus-visible) statt des bisherigen
   box-shadow-Glows. */
.field input:focus {
  outline: 2px solid var(--color-primary); outline-offset: 2px;
  border-color: var(--color-primary);
}
.hint {
  color: var(--color-text-muted); font-size: var(--font-size-sm);
  margin-top: calc(var(--space-2xs) * -1);
}

/* ---------------------------------------------------------------------
   6. Abzeichen (D-15-11) -- Plan 15-06, Welle 5.
   Verschoben aus style.css:397-404 (.actions, .badge und die vier
   Varianten). Die vier Varianten benutzen bereits color-mix() mit Token --
   das bleibt unveraendert, nur Radius/Schriftgroesse/Innenabstand kommen
   auf die Skala. Pillen (999px) bleiben von der Radienskala ausgenommen
   (D-15-18). */
.actions { display: flex; gap: var(--space-xs); flex-wrap: wrap; }
.actions form { display: inline; }

.badge {
  font-size: var(--font-size-xs); padding: var(--space-2xs) var(--space-xs);
  border-radius: 999px; border: 1px solid var(--color-border);
  color: var(--color-text-muted);
}
.badge-ok { color: var(--color-success); border-color: color-mix(in srgb, var(--color-success) 50%, transparent); }
.badge-locked { color: var(--color-danger); border-color: color-mix(in srgb, var(--color-danger) 50%, transparent); }
.badge-pending { color: var(--color-primary); border-color: color-mix(in srgb, var(--color-primary) 50%, transparent); }
.badge-admin { color: var(--color-primary); border-color: color-mix(in srgb, var(--color-primary) 50%, transparent); margin-left: var(--space-xs); }

/* ---------------------------------------------------------------------
   7. Bedeutungsrollen (D-15-06, Erfolgskriterium 4) -- Plan 15-06,
   Welle 5. Sechs Klassen fuer NEUES Markup.

   Die Statusformel wird NICHT zentral auf die Bestandsklassen der
   Blueprints angewandt (Entscheidung im Kopf von 15-06-PLAN.md): jeder
   Blueprint-Plan der Welle 6 (15-08 bis 15-11) schreibt seine eigenen
   Statusklassen (.crm-status-*, .fb-pill-imported, .acc-monat-offen usw.)
   in seiner EIGENEN Datei nach DERSELBEN Formel um. Sechs Zeilen
   Wiederholung je Datei gegen null Kopplung ueber Plangrenzen.

   FORMEL (woertlich, fuer alle Wellen-6-Plaene, siehe 15-06-SUMMARY.md):
     Hell:   background: color-mix(in srgb, var(--color-<rolle>) 6%, var(--color-surface));
             color: var(--color-<rolle>);
     Dunkel: background: color-mix(in srgb, var(--color-<rolle>) 10%, var(--color-background-secondary));
             color: var(--color-<rolle>);
   Ausnahme Hervorhebung/Marke: siehe .rolle-marke unten.

   Ein siebter Status wird nicht erfunden -- wer eine neue Bedeutung
   braucht, ordnet sie einer der sechs zu oder fuehrt die Erweiterung als
   eigene Entscheidung, nicht als freihaendiges Hex. */
.rolle-erfolg {
  background: color-mix(in srgb, var(--color-success) 6%, var(--color-surface));
  color: var(--color-success);
}
.rolle-fehler {
  background: color-mix(in srgb, var(--color-danger) 6%, var(--color-surface));
  color: var(--color-danger);
}
.rolle-info {
  background: color-mix(in srgb, var(--color-primary) 6%, var(--color-surface));
  color: var(--color-primary);
}
/* Hervorhebung/Marke: Gold (accent-gold) erreicht im hellen Modus 2.42:1
   gegen Weiss und besteht damit nicht einmal die 3:1-Schwelle fuer
   Raender -- im Hellen deshalb NIE alleinige Textfarbe. Die Flaeche
   bleibt die uebliche 6%-Tinte, die Schrift faellt auf var(--color-text)
   zurueck; ein optionaler 3px-Randstreifen in vollem
   var(--color-accent-gold) darf ergaenzend gesetzt werden
   (border-left: 3px solid var(--color-accent-gold)), ist aber nicht Teil
   dieser Klasse -- keine siebte Klasse dafuer. Im dunklen Modus besteht
   Gold 4.63:1 und darf dort direkt als Text stehen. */
.rolle-marke {
  background: color-mix(in srgb, var(--color-accent-gold) 6%, var(--color-surface));
  color: var(--color-text);
}
.rolle-neutral {
  background: color-mix(in srgb, var(--color-text-muted) 6%, var(--color-surface));
  color: var(--color-text-muted);
}
.rolle-warten {
  background: color-mix(in srgb, var(--color-warning) 6%, var(--color-surface));
  color: var(--color-warning);
}

html[data-thema="dunkel"] .rolle-erfolg {
  background: color-mix(in srgb, var(--color-success) 10%, var(--color-background-secondary));
  color: var(--color-success);
}
html[data-thema="dunkel"] .rolle-fehler {
  background: color-mix(in srgb, var(--color-danger) 10%, var(--color-background-secondary));
  color: var(--color-danger);
}
html[data-thema="dunkel"] .rolle-info {
  background: color-mix(in srgb, var(--color-primary) 10%, var(--color-background-secondary));
  color: var(--color-primary);
}
html[data-thema="dunkel"] .rolle-marke {
  background: color-mix(in srgb, var(--color-accent-gold) 10%, var(--color-background-secondary));
  color: var(--color-accent-gold);
}
html[data-thema="dunkel"] .rolle-neutral {
  background: color-mix(in srgb, var(--color-text-muted) 10%, var(--color-background-secondary));
  color: var(--color-text-muted);
}
html[data-thema="dunkel"] .rolle-warten {
  background: color-mix(in srgb, var(--color-warning) 10%, var(--color-background-secondary));
  color: var(--color-warning);
}

/* Knopf-Dunkelzweig (D-19-01) -- Plan 19-02, Welle 2. Acht Regeln, jeder
   Zustand einzeln ausgeschrieben, KEIN Kaskadenverlass: der Dunkelzweig-
   Selektor auf der Grundklasse hat die Spezifitaet (0,2,1) und schlaegt
   sowohl den Hoverzweig (0,2,0) als auch die Primaervariante (0,1,0) --
   ohne eigene Dunkelfassung bliebe der Hover-Grund bei
   --color-surface-hover stehen und der Primaerknopf verloere seine
   Primaerflaeche (19-UI-SPEC.md, Belege 1+2). Keine der acht Regeln
   benutzt die border-color-Kurzform, nicht weil sie hier gefaehrlich
   waere (keiner der vier Knoepfe hat eine bedeutungstragende
   Einzelseite, anders als .crm-kpi/.aufg-kpi), sondern damit diese Zeile
   nicht spaeter versehentlich auf die Kacheln uebertragen wird. */
html[data-thema="dunkel"] .btn {
  background: var(--color-surface-hover);
  border-color: var(--color-border-strong);
}
html[data-thema="dunkel"] .btn:hover:not(:disabled) {
  background: var(--color-primary-soft);
  border-color: var(--color-primary);
  color: var(--color-primary);
  box-shadow: var(--shadow-md);
}
html[data-thema="dunkel"] .btn-primary {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
}
html[data-thema="dunkel"] .btn-primary:hover:not(:disabled) {
  background: var(--color-primary-hover);
  border-color: var(--color-primary-hover);
  color: var(--color-on-primary);
}
html[data-thema="dunkel"] .btn-ghost {
  background: transparent;
  border-color: var(--color-border);
  color: var(--color-text-muted);
}
html[data-thema="dunkel"] .btn-ghost:hover:not(:disabled) {
  background: var(--color-surface-hover);
  border-color: var(--color-border-strong);
  color: var(--color-text);
}
html[data-thema="dunkel"] .btn-danger {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: var(--color-on-primary);
}
html[data-thema="dunkel"] .btn-danger:hover:not(:disabled) {
  background: var(--color-danger);
  border-color: var(--color-danger);
  color: var(--color-on-primary);
}
/* Druckpunkt im Dunkelzweig (D-19-01) -- Nachtrag aus dem Code-Review
   (CR-01). Ohne diese Regel gewinnt html[data-thema="dunkel"] .btn:hover
   (0,3,1) gegen .btn:active (0,2,0) ueber die Spezifitaet: beim Druecken
   mit der Maus ist der Knopf immer gleichzeitig :hover, der eingelassene
   --shadow-press wurde deshalb im Dunkeln NIE sichtbar. Die Regel steht
   bewusst NACH .btn:hover derselben Spezifitaetsklasse, sonst entscheidet
   wieder die Quelltextfolge gegen sie.

   Bewusst NUR box-shadow: transform: translateY(1px) aus .btn:active wird
   im Dunkelzweig von keiner Regel bestritten (keine der acht
   Dunkelregeln setzt transform) und gilt dort unveraendert weiter. Ein
   Wiederholen haette den Versatz auf (0,3,1) gehoben und ihn damit aus
   der Reichweite des prefers-reduced-motion-Blocks (.btn:hover, 0,2,0)
   genommen -- das waere eine stille Verhaltensaenderung in Dunkel gegen
   Hell und gehoert nicht zu diesem Befund. */
html[data-thema="dunkel"] .btn:active:not(:disabled) {
  box-shadow: var(--shadow-press);
}

@media (prefers-color-scheme: dark) {
  html[data-thema="system"] .rolle-erfolg {
    background: color-mix(in srgb, var(--color-success) 10%, var(--color-background-secondary));
    color: var(--color-success);
  }
  html[data-thema="system"] .rolle-fehler {
    background: color-mix(in srgb, var(--color-danger) 10%, var(--color-background-secondary));
    color: var(--color-danger);
  }
  html[data-thema="system"] .rolle-info {
    background: color-mix(in srgb, var(--color-primary) 10%, var(--color-background-secondary));
    color: var(--color-primary);
  }
  html[data-thema="system"] .rolle-marke {
    background: color-mix(in srgb, var(--color-accent-gold) 10%, var(--color-background-secondary));
    color: var(--color-accent-gold);
  }
  html[data-thema="system"] .rolle-neutral {
    background: color-mix(in srgb, var(--color-text-muted) 10%, var(--color-background-secondary));
    color: var(--color-text-muted);
  }
  html[data-thema="system"] .rolle-warten {
    background: color-mix(in srgb, var(--color-warning) 10%, var(--color-background-secondary));
    color: var(--color-warning);
  }

  /* Systemzweig-Gegenstueck der acht Knopfregeln oben -- wortgleich,
     siehe Kommentar dort. */
  html[data-thema="system"] .btn {
    background: var(--color-surface-hover);
    border-color: var(--color-border-strong);
  }
  html[data-thema="system"] .btn:hover:not(:disabled) {
    background: var(--color-primary-soft);
    border-color: var(--color-primary);
    color: var(--color-primary);
    box-shadow: var(--shadow-md);
  }
  html[data-thema="system"] .btn-primary {
    background: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-on-primary);
  }
  html[data-thema="system"] .btn-primary:hover:not(:disabled) {
    background: var(--color-primary-hover);
    border-color: var(--color-primary-hover);
    color: var(--color-on-primary);
  }
  html[data-thema="system"] .btn-ghost {
    background: transparent;
    border-color: var(--color-border);
    color: var(--color-text-muted);
  }
  html[data-thema="system"] .btn-ghost:hover:not(:disabled) {
    background: var(--color-surface-hover);
    border-color: var(--color-border-strong);
    color: var(--color-text);
  }
  html[data-thema="system"] .btn-danger {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: var(--color-on-primary);
  }
  html[data-thema="system"] .btn-danger:hover:not(:disabled) {
    background: var(--color-danger);
    border-color: var(--color-danger);
    color: var(--color-on-primary);
  }

  /* Systemzweig-Gegenstueck des Druckpunkts -- wortgleich, siehe
     Kommentar beim dunkel-Zweig oben. Der dritte Zweig ist hier genauso
     Pflicht wie bei den acht Regeln davor: html[data-thema="system"]
     .btn:hover (0,3,1) schlaegt .btn:active (0,2,0) im system-dunklen
     Modus mit derselben Rechnung. */
  html[data-thema="system"] .btn:active:not(:disabled) {
    box-shadow: var(--shadow-press);
  }
}

/* ---------------------------------------------------------------------
   8. Rueckweg-Verweis (D-15-11, D-15-14) -- Plan 15-13, Welle 7.
   --------------------------------------------------------------------- */

/* Von ZWEI Vorlagen benutzt: einstellungen/index.html und
   admin/users.html -- genau das Kriterium aus D-15-11 fuer die gemeinsame
   Ebene. Kein Elternklassen-Selektor davor: eigenstaendig, damit er
   ueberall greift, wo die Klasse steht.

   Stand seit Plan 18-09 (Welle 4): in BEIDEN Vorlagen steht der Verweis
   jetzt in `.werkzeug-kopf > .werkzeug-kopf-text`, nicht mehr unter
   `.admin-head` bzw. `.adm-head`. Die frueher hier vermerkte Warnung, die
   zusammengesetzte Regel `.adm-head .back-link` in admin.css nicht zu
   loeschen, ist damit gegenstandslos -- diese Regel existiert nicht mehr,
   und die Klasse haengt an keiner Elternklasse, die sie ueberstimmen
   koennte. (Befund IN-01 aus 18-REVIEW.md, nachgezogen am 2026-09-20.)

   Die Lehre von damals bleibt trotzdem gueltig und ist der Grund, warum
   diese Regel hier und nicht in admin.css steht: /einstellungen laedt
   admin.css gar nicht (kein head_extra-Block), und selbst wenn -- der
   damalige Selektor verlangte `.adm-head` als Elternklasse, waehrend
   /einstellungen `.admin-head` trug (anderer Name). Die Klasse stand dort
   also OHNE JEDE Regel da. Das war der eigentliche Befund aus
   15-13-PLAN.md, Blocking Anti-Pattern 1. */
.back-link {
  color: var(--color-text-muted);
  font-size: var(--font-size-sm);
  text-decoration: none;
}
.back-link:hover {
  color: var(--color-primary);
}

/* ROADMAP-Erfolgskriterium 6, Plan 18-13: Bestandsbefund aus Phase 15
   (BEFUND_BESTAND_BEWEGUNG) -- kein neuer Effekt, die bestehende
   .btn-transition wird nur abschaltbar gemacht. */
@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:hover:not(:disabled) { transform: none; }
  /* Nebenknoepfe (Plan 19-03, Welle 3) -- Mitglieder ZEICHENGLEICH zu den
     beiden betroffenen Regeln oben, sonst findet die Vergleichslogik der
     Ratsche (normalisierte Selektor-Mitglieder) das Paar nicht. */
  .acc-btn, .acc-pill, .acc-del, .btn-copy, .icon-btn, .pmode-btn,
  .crm-pos-weg, .pj-pos-weg, .fb-btn, .fb-btn-primary { transition: none; }
  .acc-btn:hover:not(:disabled), .acc-pill:hover:not(:disabled),
  .acc-del:hover:not(:disabled), .btn-copy:hover:not(:disabled),
  .icon-btn:hover:not(:disabled), .pmode-btn:hover:not(:disabled),
  .crm-pos-weg:hover:not(:disabled), .pj-pos-weg:hover:not(:disabled),
  .fb-btn:hover:not(:disabled),
  .fb-btn-primary:hover:not(:disabled) { transform: none; }
}

/* ---------------------------------------------------------------------
   9. Fusszeile mit dem Stand des Systems (2026-09-20).

   Beantwortet auf jeder angemeldeten Maske die Frage "welcher Stand ist
   produktiv?", ohne dass jemand danach suchen muss. Bewusst zurueckhaltend:
   `--color-text-faint` ist die leiseste Textfarbe des Vertrags. Nachgerechnet
   gegen den Seitengrund (`--color-background`): 4,50:1 hell und 6,63:1
   dunkel; gegen `--color-surface` 4,91:1 und 5,99:1. Beide Modi bestehen
   damit sogar die 4,5:1-Schwelle fuer Fliesstext -- leise heisst hier
   zurueckhaltend, nicht schlecht lesbar.

   Keine Bewegung, kein Hover, kein Verweis: es ist eine Angabe, kein
   Bedienelement. Damit faellt sie auch nicht unter die
   reduced-motion-Zusicherung.
   --------------------------------------------------------------------- */
.portal-fusszeile {
  padding: var(--space-sm) var(--space-md);
  color: var(--color-text-faint);
  font-size: var(--font-size-xs);
  line-height: var(--line-height-xs);
  text-align: right;
  user-select: text;
}

/* ---------------------------------------------------------------------
   Kalenderkarte (_kalender_karte.html, kalender_karte.js) -- oben links
   auf der CRM-Kontaktseite und der Projektseite (2026-10-08). Gemeinsam,
   weil zwei Werkzeuge sie benutzen. .kal-oben ist die Kopfzeile, die die
   Karte links neben den bisherigen Seitenkopf stellt.
   --------------------------------------------------------------------- */
.kal-oben {
  display: grid; gap: var(--space-md); align-items: start;
  grid-template-columns: minmax(300px, 380px) minmax(0, 1fr);
  margin-bottom: var(--space-md);
}
.kal-oben > * { min-width: 0; }
@media (max-width: 1100px) {
  .kal-oben { grid-template-columns: minmax(0, 1fr); }
}
.kal-karte {
  background: var(--color-surface);
  border: 1px solid var(--color-border-tertiary);
  border-top: 3px solid var(--color-primary);
  border-radius: var(--radius-lg);
  padding: var(--space-sm) var(--space-md) var(--space-md);
  display: flex; flex-direction: column; gap: var(--space-sm);
}
.kal-kopf { display: flex; align-items: center; gap: var(--space-xs); }
.kal-kopf > .ti { font-size: var(--font-size-xl); color: var(--color-primary); }
.kal-titel { margin: 0; font-size: var(--font-size-md); line-height: var(--line-height-md); }
.kal-mit { display: block; font-size: var(--font-size-sm); color: var(--color-text-muted); }
.kal-hinweis { margin: 0; font-size: var(--font-size-sm); line-height: var(--line-height-sm); color: var(--color-text-muted); }

.kal-agenda {
  max-height: 260px; overflow-y: auto;
  border: 1px solid var(--color-border-tertiary); border-radius: var(--radius-md);
  background: var(--color-bg);
}
.kal-tag {
  display: grid; grid-template-columns: 78px minmax(0, 1fr); gap: var(--space-xs);
  padding: var(--space-2xs) var(--space-xs);
  border-bottom: 1px solid var(--color-border-tertiary);
}
.kal-tag:last-child { border-bottom: 0; }
.kal-tag--wochenende { background: var(--color-surface-hover); }
.kal-tag-kopf {
  align-self: start; text-align: left; cursor: pointer;
  background: none; border: 0; padding: var(--space-2xs) 0;
  font: inherit; font-size: var(--font-size-sm); font-weight: 600;
  color: var(--color-text);
  text-decoration: underline dotted; text-underline-offset: 3px;
}
.kal-tag--heute .kal-tag-kopf { color: var(--color-primary); }
.kal-tag-kopf:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
.kal-tag-liste { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.kal-frei { font-size: var(--font-size-sm); color: var(--color-text-muted); padding: var(--space-2xs) 0; }
.kal-eintrag {
  display: flex; gap: var(--space-xs); align-items: baseline;
  font-size: var(--font-size-sm); line-height: var(--line-height-sm);
  padding: 2px var(--space-2xs); border-radius: var(--radius-sm);
  border-left: 3px solid var(--color-primary);
  background: var(--color-primary-soft);
}
.kal-eintrag--frei { border-left-style: dashed; background: transparent; }
.kal-eintrag--kontakt { border-left-width: 6px; font-weight: 600; }
.kal-zeit { flex: none; color: var(--color-text-muted); font-variant-numeric: tabular-nums; }
.kal-eintrag-titel { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--color-text); }

.kal-form { display: flex; flex-direction: column; gap: var(--space-xs); margin: 0; }
.kal-reihe { display: flex; gap: var(--space-xs); flex-wrap: wrap; }
.kal-feld { display: flex; flex-direction: column; gap: 2px; font-size: var(--font-size-sm); color: var(--color-text-muted); }
.kal-feld-breit { flex: 1 1 180px; }
.kal-feld input, .kal-feld select, .kal-notiz textarea {
  font: inherit; font-size: var(--font-size-base); color: var(--color-text);
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-md); padding: var(--space-2xs) var(--space-xs);
  width: 100%; box-sizing: border-box;
}
.kal-feld input:focus, .kal-feld select:focus, .kal-notiz textarea:focus {
  outline: 2px solid color-mix(in srgb, var(--color-primary) 40%, transparent);
  outline-offset: 1px; border-color: var(--color-primary);
}
.kal-notiz summary { cursor: pointer; font-size: var(--font-size-sm); color: var(--color-text-muted); }
.kal-notiz textarea { margin-top: var(--space-2xs); resize: vertical; }
.kal-konflikt {
  margin: 0; font-size: var(--font-size-sm); color: var(--color-warning);
  border-left: 3px solid var(--color-warning); padding-left: var(--space-xs);
}
.kal-aktionen { display: flex; gap: var(--space-xs); flex-wrap: wrap; }
